二維碼掃描優化


zxing是一款跨平臺的基於Java實現的處理一維或二維條碼的庫。支持多種格式,一維條碼支持UPC-A,UPC-E,EAN-8,Code 39,Code 93等格式,二維條碼支持QR Code,Data Matrix,PDF 417,MaxiCode等格式。數組

注:上述的二維條碼指的是較寬泛的二維條碼,而不是QR Code表示的二維碼。性能優化


前言
性能

本來Lark直接集成了zxing實現掃一掃功能。因爲Lark的特殊業務需求,所以並不須要支持到這麼多格式,只須要支持QR Code,所以咱們對zxing內部進行定製,使得zxing只支持QR Code。這樣既能夠減小zxing庫的大小,也能夠加快zxing處理一幀數據的速度。優化主要包含兩方面:(1)掃描性能(2)交互體驗。測試

掃描性能優化包括:優化

  • 去除zxing額外支持的格式。線程

  • 刪除zxing冗餘代碼。code

  • 將處理相機幀從串行改成並行。orm

交互體驗優化包括:cdn

  • 自動放大。blog

  • 雙擊放大。

  • 重力傳感器聚焦。

  • 手勢調整焦距。


去除zxing額外支持的格式

MultiFormatReader的decodeWithState( )是使用方的入口方法,內部調用了decodeInternal( ),輸入是相機的一幀數據,若是拋了NotFoundException,則表示沒找到二維碼;若是返回了Result,則表示找到了二維碼,並解析完成。代碼以下:

其中,readers變量是一個數組,數組的大小表示支持的條碼格式個數,zxing本來由於支持不少格式,所以這個數組長度比較長。當拿到相機的一幀數據後,須要去檢測是不是全部支持格式的某一個格式,每一種格式的檢測都須要花費一些時間,所以這個遍歷對於Lark是沒必要要的。若是將zxing內部定製成只支持QR Code格式,那麼就免去了額外的格式檢測。


刪除zxing冗餘代碼

咱們主要從幾方面刪除冗餘代碼:

  • 刪除zxing除了二維碼以外的格式的相關代碼,zxing對每種格式的相關代碼都放在各自的目錄中,所以咱們只須要把這些格式對應的目錄刪除便可,好比aztec、maxicode等。

  • 刪除二維碼的encode相關代碼,即"qrcode/encoder"目錄。

  • 刪除decode後文本的解析相關類(好比地址、通信錄、郵件等解析類),只保留URI、URL、Text。

經過以上方式,zxing文件數量從263個縮減到67個,庫大小從1.8M縮減到451K,效果很是明顯。


將處理相機幀從串行改成並行

本來Lark掃一掃的邏輯是串行的,以下圖:

每次從onPreviewFrame()中獲取一幀數據,而後調用zxing的decode解析二維碼,若是成功,則返回;若是失敗,則調用setOneShotPreviewCallback( )從新調用一次onPreviewFrame( )。

缺點是若是處理一幀數據時間很長,會阻礙下一幀的處理,好比上一幀是沒有二維碼的,而下一幀是有二維碼的,若是上一幀處理時間較長,那麼雖然用戶對準了二維碼,可是實際處理的仍是上一幀,所以不太合理。

咱們將串行處理改爲並行處理,一旦從onPreviewFrame( )獲取一幀數據,將decode任務丟進線程池,並當即調用setOneShotPreviewCallback( )獲取下一幀數據。一旦某個任務檢測到二維碼,當即將isSuccess變量置爲true,忽略其餘任務。這樣可以大大加快二維碼檢測的速度。


自動放大

當二維碼很小很遠時,自動放大能大大加快檢測二維碼的速度。QRCodeReader的decode( ) 是二維碼檢測的主方法,分爲兩步:(1)大體判斷是否存在二維碼;(2)解碼。

第一步只是檢測是否存在二維碼,好比去尋找是否存在Position Detection Pattern,Timing Pattern,Alignment Pattern。若是檢測到了,則返回DetectorResult,內部包含了定位點的位置信息;若是沒檢測到,則拋出NotFoundException。若是二維碼很小,即便第一步檢測存在二維碼,可是第二步解碼也可能會失敗。因爲咱們在第一步已經可以知道二維碼的大小,所以根據DetectorResult返回的二維碼定位點信息計算出二維碼的大體寬度,而後判斷二維碼大小在掃碼框中是否足夠小,若是足夠小,則放大必定焦距:若是小於十分之一,則放大到最大焦距;若是小於等於六分之一,則放大到最大焦距的一半。

具體二維碼的原理參見:二維碼的生成細節和原理(見原文連接)

咱們實現了zoomCamera( ),若是判斷須要放大,則返回true,若是不須要放大,則返回false。代碼以下:

咱們在第一步和第二步中間插入該方法,若是須要放大,則不執行第二步;若是二維碼已經足夠大,則執行第二步。代碼以下:


雙擊放大

本來Lark的二維碼掃描中沒有調整焦距的功能,這個對於一些特定場景下會不太方便,所以這裏加入了雙擊放大的功能可以對焦距進行粗略的調整。利用GestureDetector的onDoubleTap()回調捕捉用戶雙擊事件,並在CameraPreview中的onTouchEvent()中添加mGestureDetector.onTouchEvent()。實現以下:


重力傳感器聚焦

重力傳感器可以捕捉用戶手機的運動狀態,當檢測到用戶手機中止時,觸發對焦邏輯。咱們經過實現SensorEventListener接口,並重寫onSensorChanged()監聽手機的運動狀態。


手勢調整焦距

爲了更精細化的讓用戶調整焦距,咱們提供了手勢來縮放焦距。經過在onTouchEvent()中獲取用戶兩個手指的距離是愈來愈近仍是愈來愈遠來調整焦距。代碼以下:

優化結果

通過上述優化,不只增長了用戶體驗,並且還大幅增長了二維碼掃描速度。測試手機:堅果Pro,4G內存,Android 7.1.1。


上圖表示了從打開相機到二維碼解碼成功的耗時,能夠看出,總體時間提高了300%+ 。

上圖表示檢測失敗時的耗時指的是當相機幀中沒有二維碼時檢測的時間,檢測失敗耗時的減小有助於更快地處理相機幀數據,當包含二維碼的幀出現時更快地處理它;上圖中看出,耗時減小300%+ 。

上圖表示檢測成功時的耗時指的是當相機幀中有二維碼時檢測+解碼的時間;上圖中看出,耗時減小150%+ 。

相關文章
相關標籤/搜索