zoukankan      html  css  js  c++  java
  • 关于iOS原生条形码扫描,你需要注意的两三事

     
    前言
    这篇文章是我们在新发布的礼物说的iOS端开发过程中遇到的一些关于条形码的问题总结而来。
     
    本文记录的问题是:当AVFoundation使用多译码器扫描的时候。二维码是秒杀,但是条形码却经常扫不上。如果去掉二维码的话,条形码扫描又秒杀的问题。
     
    为什么我们没有选用ZXing而是用AVfoundation呢,是因为我说服了老板,iOS7开发,而不再去兼容iOS5/6。所以我们终于可以抛弃效率低下的ZXing,而选择AVFoundation。为什么说ZXing效率低下,我们这里可以说上几句。

    ZXing
    ZXing 是 Google Code上的一个开源的条形码扫描库,是用java设计的,连Google Glass 都在使用的。但有人为了追求更高效率以及可移植性,出现了c++ port. Github上的Objectivc-C port,其实就是用OC代码封装了一下而已,而且已经停止维护。
     
    ZXing扫描,是拿到摄像头的每一帧,然后对其根据如下公式做灰度化
    1. f(i,j)=0.30R(i,j)+0.59G(i,j)+0.11B(i,j))  
    之后做全局直方图二值化的方法,最后按照 ISO/IEC 18004 规范进行解析。
     
    这样效率非常低,在instrument下面可以看到CPU占用远远高于 AVFoundation。而且全局直方图二值化导致精准度并不高。这个库还会带来一大堆C++的东西,在纯iOS7的工程下,不推荐使用。
     
    AVFoundation 扫码的简单使用
    这里说一下,我们礼物说是和passbook一样,同时可以扫描二维码和条形码,真是因为这个特性,导致了我写这篇总结。 先粘一下扫码实现部份,如下。
    1. - (BOOL)startReading { 
    2.     _isReading = YES; 
    3.     NSError *error; 
    4.     AVCaptureDevice *captureDevice = [AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeVideo]; 
    5.  
    6.     AVCaptureDeviceInput *input = [AVCaptureDeviceInput deviceInputWithDevice:captureDevice error:&error]; 
    7.  
    8.     if (!input) { 
    9.         NSLog(@"%@", [error localizedDescription]); 
    10.         return NO; 
    11.     } 
    12.  
    13.     _captureSession = [[AVCaptureSession alloc] init]; 
    14.     // Set the input device on the capture session. 
    15.     [_captureSession addInput:input]; 
    16.  
    17.     AVCaptureMetadataOutput *captureMetadataOutput = [[AVCaptureMetadataOutput alloc] init]; 
    18.     [_captureSession addOutput:captureMetadataOutput]; 
    19.  
    20.     // Create a new serial dispatch queue. 
    21.     dispatch_queue_t dispatchQueue; 
    22.     dispatchQueue = dispatch_queue_create("myQueue", NULL); 
    23.     [captureMetadataOutput setMetadataObjectsDelegate:self queue:dispatchQueue]; 
    24.  
    25.     if (self.qrcodeFlag) 
    26.         [captureMetadataOutput setMetadataObjectTypes:[NSArray arrayWithObject:AVMetadataObjectTypeQRCode]]; 
    27.     else 
    28.         [captureMetadataOutput setMetadataObjectTypes:[NSArray arrayWithObjects:AVMetadataObjectTypeEAN13Code, AVMetadataObjectTypeEAN8Code, AVMetadataObjectTypeCode128Code, AVMetadataObjectTypeQRCode, nil]]; 
    29.  
    30.     _videoPreviewLayer = [[AVCaptureVideoPreviewLayer alloc] initWithSession:_captureSession]; 
    31.     [_videoPreviewLayer setVideoGravity:AVLayerVideoGravityResizeAspectFill]; 
    32.     [_videoPreviewLayer setFrame:self.view.layer.bounds]; 
    33.     [self.view.layer addSublayer:_videoPreviewLayer]; 
    34.  
    35.     [_captureSession startRunning]; 
    36.  
    37.     return YES; 
    38.  
    39.  
    40. -(void)stopReading{ 
    41.     [_captureSession stopRunning]; 
    42.     _captureSession = nil; 
    43.     [_videoPreviewLayer removeFromSuperlayer]; 
    44.  
    45. -(void)captureOutput:(AVCaptureOutput *)captureOutput didOutputMetadataObjects:(NSArray *)metadataObjects 
    46.       fromConnection:(AVCaptureConnection *)connection 
    47.     if (!_isReading) return
    48.  
    49.     if (metadataObjects != nil && [metadataObjects count] > 0) { 
    50.         AVMetadataMachineReadableCodeObject *metadataObj = [metadataObjects objectAtIndex:0]; 
    51.  
    52.         Do Something.... 
    53.     } 
     这个代码也不需要加什么注释,挺简单易懂的。
     
    阐述问题
    我们上面说过了:当AVFoundation使用多译码器扫描的时候。二维码是秒杀,但是条形码却经常扫不上。如果去掉二维码的话,条形码扫描又秒杀的问题。
     
    但有趣的事情是,如果我写了个demo,用上述代码的话。却又可以秒杀扫描。这个问题困扰了我一下午,仔细对比了项目中的每一行代码和我demo中的全部。除了demo没有画一个提示框在屏幕上以外,其他地方全都一模一样。
     
    那么为什么导致项目中扫描效率如此之慢呢?
     
    猜想1: UI以及后台线程占用大量CPU时间
    结果在 instrument下,不攻自破,cpu占用,内存占用非常非常低。
     
    猜想2:系统架构问题
    因为添加了QRCode才导致扫描变慢的,那么就应该是和算法效率有关。多引入了一个每一帧都要工作的译码器,导致条形码扫描效率下降。我的Demo是arm64 v7s v7 系统全支持,而项目是ArmV7。
    这个想法挺异想天开的。觉得可能是Arm64的指令集效率比armv7快得多导致的。我还去问巧哥,armv7和arm64在密集运算的时候效率差多少,会不会比较明显。
     
    但重新配置了一下,还是错误的。
     
    插曲
    我发现把屏幕横过来扫描效率比竖过来高多了。于是怀疑是不是 Capture 的方向问题。
     
    猜想3: 摄像头方向问题导致解碼效率低
    这个猜想,我没有去证实,因为太麻烦了。要给Session 添加一个新的output 来输出每一帧,而且还是个CMBuffer,还要手动转码。不过后面证实这个也是错的。
     
    猜想4:摄像头参数问题
    当初看AVCam 写拍照模块的时候,记得摄像头有很多参数,ZXing 也有一个文件位叫做精确解碼,牺牲效率换精确度。于是就在想会不会苹果家的也要设置参数。
     
    于是就坏怀这个问题去看文档去了,结果歪打正着的发现了正确原因。 这是记录在苹果的FAQ中的,并没在AVFoundation 的 Reference 中。具体编号为:Technical Note TN2325
     
    正确原因
    就是描述问题里面说到的,demo和工程里面的唯一区别,多了个surfaceLayer。如下图:
    为了正确解释这个有趣的问题,我们要解释一下条形码扫描原理。
     
    上面有提过二维码是通过全局直方图二值化后,按照ISO标准解碼,实际上是,按照1:1:3:1:1去寻找那三个寻像图形,就是标志性的大方块。然后圈出二维码大小再去解碼的。也就是说,再没设定边界的情况下全屏都可以。

    而条形码完全不同,他是在Detect Center那个点,画一个无限延伸的米字型,然后去判断每一条在线能否解析出条形码所需要的0101010序列。而iOS默认的Center是 Layer 的 Center。
     
    我们再回过头来看工程中的 SurfaceLayer,其实他提示给用户的那个框,已经远离了Center。所以我们竖着扫描的时候,那条水平的扫描线是没有贯穿条形码的,所以扫不上他。
     
    于是乎要根据设备,iPhone4 iPhone5 通过AVCaptureDeviceFormat和AVCaptureSessionPreset 重新设置一下AVCaptureMetadataOutput rectOfInterest,结果问题就解决了。
     
    为什么去掉二维码就没事了呢?
     
    还在那篇FAQ中,有那么一个表格。
    可见,当我们没有二维码的时候,他会有个additional存在。用更加优秀且稍微耗时的算法去优化扫描精准度。
     
    总结
    1.当我们遇到问题的时候,不光要记得看 苹果的 guide 和 reference,还要记得看以下 sample code,tech note, FAQ。
     
    2.说不好有意外收获 为什么条形码扫描仪上往往会有一条红线,这并不是为了拟物化,而是告诉用户一定要用这条线对准条形码,否则会有扫不上的可能性。
     
    3.正如福尔摩斯所说:抛开所有不可能的,剩下的,不管多么令人匪夷所思,那都是事实。两套代码仅有UI不一样,效果不同,其实就是UI引导用户错误的使用了扫描仪。
     
  • 相关阅读:
    js自动小轮播
    js字符串
    工资
    可是姑娘,你为什么要编程呢?
    程序猿媳妇儿注意事项
    js勾选时显示相应内容
    js点击显示隐藏
    js选项卡
    js数组
    js旋转V字俄罗斯方块
  • 原文地址:https://www.cnblogs.com/allen123/p/4519188.html
Copyright © 2011-2022 走看看