在 iOS7 之前,在iOS中实现二维码和条形码扫描,咱们所知的有,两大开源组件ZBar与ZXing. 这两大组件咱们都有用过,这里总结下各自的缺点:java
ZBarc++
ZBar在扫描的灵敏度上,和内存的使用上相对于ZXing上都是较优的,可是对于 “圆角二维码” 的扫描确很困难。如:网络
ZXingsession
ZXing 是 Google Code上的一个开源的条形码扫描库,是用java设计的,连Google Glass 都在使用的。但有人为了追求更高效率以及可移植性,出现了c++ port. Github上的Objectivc-C port,其实就是用OC代码封装了一下而已,并且已经中止维护。这样效率很是低,在instrument下面能够看到CPU和内存疯涨,在内存小的机器 上很容易崩溃。ide
AVFoundation性能
AVFoundation不管在扫描灵敏度和性能上来讲都是最优的,因此毫无疑问咱们应该切换到AVFoundation,须要兼容iOS 6或以前的版本能够用zbar或zxing代替。测试
下面介绍本文的重点,不管你是用以上哪种或其余的解决方案,都须要知道下面两点。spa
1. 图片很小的二维码设计
以 前测试提了一个bug,说有二维码扫不了,拿到二维码一看,是个很小的二维码,边长不到1cm,因而就修改了 sessionPreset 为 1080p 的,当时用的是ZXing, 当把图片质量改清楚时,也形成了性能的降低,基本打开扫描界面就会报memoryWarning,可是也确实解决了小二维码扫描的问题。rest
AVCaptureSession 能够设置 sessionPreset 属性,这个决定了视频输入每一帧图像质量的大小。
AVCaptureSessionPreset320x240
AVCaptureSessionPreset352x288
AVCaptureSessionPreset640x480
AVCaptureSessionPreset960x540
AVCaptureSessionPreset1280x720
AVCaptureSessionPreset1920x1080
以 上列举了部分的属性值,分别表明输入图片质量大小,通常来讲AVCaptureSessionPreset640x480就够使用,可是若是要保证较小的 二维码图片能快速扫描,最好设置高些,如AVCaptureSessionPreset1920x1080(就是咱们常说的1080p).
2. scanCrop
另外一个提高扫描速度和性能的就是设置解析的范围,在zbar和zxing中就是scanCrop, AVFoundation中设置 AVCaptureMetadataOutput 的 rectOfInterest 属性来配置解析范围。
最开始我按照文档说的按照比例值来设置这个属性,以下:
CGSize size = self.view.bounds.size; CGRect cropRect = CGRectMake(40, 100, 240, 240); captureOutput.rectOfInterest = CGRectMake(cropRect.origin.x/size.width, cropRect.origin.y/size.height, cropRect.size.width/size.width, cropRect.size.height/size.height);
可是发现, Ops, 好像不对啊,扫不到了,明显不正确呢,因而猜测: AVCapture输出的图片大小都是横着的,而iPhone的屏幕是竖着的,那么我把它旋转90°呢:
CGSize size = self.view.bounds.size; CGRect cropRect = CGRectMake(40, 100, 240, 240); captureOutput.rectOfInterest = CGRectMake(cropRect.origin.y/size.height, cropRect.origin.x/size.width, cropRect.size.height/size.height, cropRect.size.width/size.width);
OK, 貌似对了,在iPhone5上一切工做良好,可是在4s上,或者换了sessionPreset的大小以后,这个框貌似就不那么准确了, 可能发现超出框上下一些也是能够扫描出来的。 再次猜测: 图片的长宽比和手机屏幕不是同样的,这个rectOfInterest是相对于图片大小的比例。好比iPhone4s屏幕大小是 640x960, 而图片输出大小是 1920x1080. 实际的状况可能就是下图中的效果:
上 图中下面的表明iPhone4s屏幕,大小640x960, 上面表明AVCaptureVideoPreviewLayer中预览到的图片位置,在图片输入为1920x1080大小时,实际大小上下会被截取一点 的,由于咱们AVCaptureVideoPreviewLayer设置的videoGravity是 AVLayerVideoGravityResizeAspectFill, 相似于UIView的UIViewContentModeScaleAspectFill效果。
因而我对大小作了一下修正:
CGSize size = self.view.bounds.size; CGRect cropRect = CGRectMake(40, 100, 240, 240); CGFloat p1 = size.height/size.width; CGFloat p2 = 1920./1080.; //使用了1080p的图像输出 if (p1 < p2) { CGFloat fixHeight = bounds.size.width * 1920. / 1080.; CGFloat fixPadding = (fixHeight - size.height)/2; captureOutput.rectOfInterest = CGRectMake((cropRect.origin.y + fixPadding)/fixHeight, cropRect.origin.x/size.width, cropRect.size.height/fixHeight, cropRect.size.width/size.width); } else { CGFloat fixWidth = bounds.size.height * 1080. / 1920.; CGFloat fixPadding = (fixWidth - size.width)/2; captureOutput.rectOfInterest = CGRectMake(cropRect.origin.y/size.height, (cropRect.origin.x + fixPadding)/fixWidth, cropRect.size.height/size.height, cropRect.size.width/fixWidth); }
通过上面的验证,证明了猜测rectOfInterest是基于图像的大小裁剪的。
3. 小结
scanCrop对于扫描来讲是比较重要的,试想图片截小点来解析是否是理论上就会更快了呢。网络上貌似很难搜到关于scanCrop的详解,但愿对看到的人有帮助。