前言

这篇文章是我们在新发布的  礼物说 的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. AVCaptureDeviceInput *input = [AVCaptureDeviceInput deviceInputWithDevice:captureDevice error:&error];
  6. if  (!input) {
  7. NSLog(@  "%@" , [error localizedDescription]);
  8. return  NO;
  9. }
  10. _captureSession = [[AVCaptureSession alloc] init];
  11. // Set the input device on the capture session.
  12. [_captureSession addInput:input];
  13. AVCaptureMetadataOutput *captureMetadataOutput = [[AVCaptureMetadataOutput alloc] init];
  14. [_captureSession addOutput:captureMetadataOutput];
  15. // Create a new serial dispatch queue.
  16. dispatch_queue_t dispatchQueue;
  17. dispatchQueue = dispatch_queue_create(  "myQueue" , NULL);
  18. [captureMetadataOutput setMetadataObjectsDelegate:self queue:dispatchQueue];
  19. if  (self.qrcodeFlag)
  20. [captureMetadataOutput setMetadataObjectTypes:[NSArray arrayWithObject:AVMetadataObjectTypeQRCode]];
  21. else
  22. [captureMetadataOutput setMetadataObjectTypes:[NSArray arrayWithObjects:AVMetadataObjectTypeEAN13Code, AVMetadataObjectTypeEAN8Code, AVMetadataObjectTypeCode128Code, AVMetadataObjectTypeQRCode, nil]];
  23. _videoPreviewLayer = [[AVCaptureVideoPreviewLayer alloc] initWithSession:_captureSession];
  24. [_videoPreviewLayer setVideoGravity:AVLayerVideoGravityResizeAspectFill];
  25. [_videoPreviewLayer setFrame:self.view.layer.bounds];
  26. [self.view.layer addSublayer:_videoPreviewLayer];
  27. [_captureSession startRunning];
  28. return  YES;
  29. }
  30. -(  void )stopReading{
  31. [_captureSession stopRunning];
  32. _captureSession = nil;
  33. [_videoPreviewLayer removeFromSuperlayer];
  34. }
  35. -(  void )captureOutput:(AVCaptureOutput *)captureOutput didOutputMetadataObjects:(NSArray *)metadataObjects
  36. fromConnection:(AVCaptureConnection *)connection
  37. {
  38. if  (!_isReading)  return ;
  39. if  (metadataObjects != nil && [metadataObjects count] > 0) {
  40. AVMetadataMachineReadableCodeObject *metadataObj = [metadataObjects objectAtIndex:0];
  41. Do Something....
  42. }
  43. }

这个代码也不需要加什么注释,挺简单易懂的。

阐述问题

我们上面说过了:当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引导用户错误的使用了扫描仪。

关于iOS原生条形码扫描,你需要注意的两三事相关推荐

  1. iOS原生条形码扫描

    转自txx's blog 前言 这篇文章是我们在新发布的礼物说的iOS端开发过程中遇到的一些关于条形码的问题总结而来. 本文记录的问题是:当AVFoundation使用多译码器扫描的时候.二维码是秒杀 ...

  2. ios实现条形码扫描功能

    免费的条形码是zbar,其使用地址是:http://zbar.sourceforge.net/iphone/sdkdoc/install.html 下面是实现的效果:   利用条形码,可以简单的得到二 ...

  3. ios ZXing 二维码、条形码扫描

    转自:http://finalshares.com/read-6901?jike-236 扫描多条: https://github.com/TheLevelUp/ZXingObjC/pull/235 ...

  4. iOS原生二维码扫描(一)

    首先搭建一个最初步的能识别出二维码信息的最基本框架: @interface ScanCodeViewController ()<AVCaptureMetadataOutputObjectsDel ...

  5. iOS 原生二维码扫描和生成

    代码地址如下: http://www.demodashi.com/demo/12551.html 一.效果预览: 功能描述:WSLNativeScanTool是在利用原生API的条件下封装的二维码扫描 ...

  6. iOS原生实现二维码扫描

    iOS原生实现二维码扫描 最近项目上需要开发扫描二维码进行签到的功能,主要用于开会签到的场景,所以为了避免作弊,我们再开发时只采用直接扫描的方式,并且要屏蔽从相册读取图片,此外还在二维码扫描成功签到时 ...

  7. IOS条形码扫描技术实现

    在我们开发ios应用时,尤其是电子商务类应用,时常遇到条形码扫描的业务需求,幸运的已经有开源的SDK供我们使用──条形码的SDK for ios,下面介绍一下这个开源的SDK的使用方法: 免费的条形码 ...

  8. 不借助 Fiori client,直接在手机浏览器里调用 SAP UI5 BarcodeScanner 实现条形码扫描的可能性?

    这个 StackOverflow 讨论 说不可行. sap.ndc.BarcodeScanner 在命名空间 sap.ndc 下声明. 这里的"ndc"代表 Native Devi ...

  9. SAP UI5 应用开发教程之四十八 - 如何在 SAP UI5 应用里开发条形码扫描功能试读版

    一套适合 SAP UI5 初学者循序渐进的学习教程 教程目录 SAP UI5 本地开发环境的搭建 SAP UI5 应用开发教程之一:Hello World SAP UI5 应用开发教程之二:SAP U ...

最新文章

  1. display会影响canvas吗_多动症会影响智商吗?
  2. Python01——初识python
  3. 瑞士军刀——Pandoc
  4. 社区O2O的发展与未来
  5. 简单的函数(也叫方法)
  6. 【Android 电量优化】JobScheduler 相关源码分析 ( JobSchedulerService 源码分析 | 任务检查 | 任务执行 )
  7. 【AWSL】之Linux进程和计划任务管理(ps、top、pgrep、pstree、pkill、at、crontab)
  8. Opencv——几何空间变换(仿射变换和投影变换)
  9. Java Calendar add()方法与示例
  10. raspberry pi_使用Raspberry Pi配置业余无线电网关
  11. 图片热点的使用,html area 的用法
  12. STL之stack容器
  13. 如何检查数组是否有重复值
  14. java 读取pdf、word、Excel文件
  15. android 移除泛型中元素_最新(2020)Android高级面试知识点干货分享(二)
  16. 银江股份:全面布局持续并购打造智慧城市生态圈
  17. Windows 7下IE 11的F12控制台不能使用的解决
  18. les有学计算机的吗,拷问LES大涡模拟
  19. matlab读不出数据,xlsread为什么读不出数据
  20. [深度学习] 自然语言处理 ----- Attention机制中的Q,K,V介绍

热门文章

  1. 一文看尽 JVM GC 调优
  2. 工程师软技能4:找出你的短板
  3. Mybatis如何调用oracle存储过程?入参为日期类型
  4. html文档包包含几个基本标记,HTML中包含哪些基本的标记?
  5. ONVIF协议实现1:Server端Discovery的实现详解
  6. MQ如何防止消息丢失
  7. rabbitmq实现秒杀中订单流量削峰
  8. LCFinder 0.3.0 Beta 发布,图像标注与目标检测工具
  9. Yii2语言国际化配置
  10. [APUE]进程控制(中)