iOS關二維碼那些事

在 iOS7 之前,在iOS中實現二維碼和條形碼掃描,咱們所知的有,兩大開源組件ZBar與ZXing. 這兩大組件咱們都有用過,這裏總結下各自的缺點:java

  • ZBarc++

ZBar在掃描的靈敏度上,和內存的使用上相對於ZXing上都是較優的,可是對於 「圓角二維碼」 的掃描確很困難。如:網絡

021.png

  • 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. 實際的狀況可能就是下圖中的效果:

06.png

上 圖中下面的表明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的詳解,但願對看到的人有幫助。
相關文章
相關標籤/搜索