iOS原生條形碼掃描

VFoundation 掃碼的簡單使用html

這裏說一下,咱們禮物說是和passbook同樣,同時能夠掃描二維碼和條形碼,真是由於這個特性,致使了我寫這篇總結。 先粘一下掃碼實現部份,以下。ios

- (BOOL)startReading {    
 _isReading = YES;     NSError *error;     AVCaptureDevice *captureDevice = [AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeVideo];      AVCaptureDeviceInput *input = [AVCaptureDeviceInput deviceInputWithDevice:captureDevice error:&error];      if (!input) {         NSLog(@"%@", [error localizedDescription]);         return NO;     }      _captureSession = [[AVCaptureSession alloc] init];     // Set the input device on the capture session.     [_captureSession addInput:input];      AVCaptureMetadataOutput *captureMetadataOutput = [[AVCaptureMetadataOutput alloc] init];     [_captureSession addOutput:captureMetadataOutput];      // Create a new serial dispatch queue.     dispatch_queue_t dispatchQueue;     dispatchQueue = dispatch_queue_create("myQueue", NULL);     [captureMetadataOutput setMetadataObjectsDelegate:self queue:dispatchQueue];      if (self.qrcodeFlag)         [captureMetadataOutput setMetadataObjectTypes:[NSArray arrayWithObject:AVMetadataObjectTypeQRCode]];     else         [captureMetadataOutput setMetadataObjectTypes:[NSArray arrayWithObjects:AVMetadataObjectTypeEAN13Code, AVMetadataObjectTypeEAN8Code, AVMetadataObjectTypeCode128Code, AVMetadataObjectTypeQRCode, nil]];      _videoPreviewLayer = [[AVCaptureVideoPreviewLayer alloc] initWithSession:_captureSession];     [_videoPreviewLayer setVideoGravity:AVLayerVideoGravityResizeAspectFill];     [_videoPreviewLayer setFrame:self.view.layer.bounds];     [self.view.layer addSublayer:_videoPreviewLayer];      [_captureSession startRunning];      return YES; }   -(void)stopReading{     [_captureSession stopRunning];     _captureSession = nil;     [_videoPreviewLayer removeFromSuperlayer]; }  -(void)captureOutput:(AVCaptureOutput *)captureOutput didOutputMetadataObjects:(NSArray *)metadataObjects       fromConnection:(AVCaptureConnection *)connection {     if (!_isReading) return;      if (metadataObjects != nil && [metadataObjects count] > 0) {         AVMetadataMachineReadableCodeObject *metadataObj = [metadataObjects objectAtIndex:0];          Do Something....     } } 
 這個代碼也不須要加什麼註釋,挺簡單易懂的。

 

闡述問題算法

咱們上面說過了:當AVFoundation使用多譯碼器掃描的時候。二維碼是秒殺,可是條形碼卻常常掃不上。若是去掉二維碼的話,條形碼掃描又秒殺的問題。session

 

但有趣的事情是,若是我寫了個demo,用上述代碼的話。卻又能夠秒殺掃描。這個問題困擾了我一下午,仔細對比了項目中的每一行代碼和我demo中的所有。除了demo沒有畫一個提示框在屏幕上之外,其餘地方全都如出一轍。架構

 

那麼爲何致使項目中掃描效率如此之慢呢?app

 

猜測1: UI以及後臺線程佔用大量CPU時間ide

結果在 instrument下,不攻自破,cpu佔用,內存佔用很是很是低。優化

 

猜測2:系統架構問題ui

由於添加了QRCode才致使掃描變慢的,那麼就應該是和算法效率有關。多引入了一個每一幀都要工做的譯碼器,致使條形碼掃描效率降低。個人Demo是arm64 v7s v7 系統全支持,而項目是ArmV7。spa

這個想法挺異想天開的。以爲多是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引導用戶錯誤的使用了掃描儀。

相關文章
相關標籤/搜索