二維碼掃描優化

10 人贊了文章

前言

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冗餘代碼。
  • 將處理相機幀從串列改為並行。

交互體驗優化包括:

  • 自動放大。
  • 雙擊放大。
  • 重力感測器聚焦。
  • 手勢調整焦距。

1. 去除zxing額外支持的格式

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

其中,readers變數是一個數組,數組的大小表示支持的條碼格式個數,zxing原本因為支持很多格式,因此這個數組長度比較長。當拿到相機的一幀數據後,需要去檢測是否是所有支持格式的某一個格式,每一種格式的檢測都需要花費一些時間,因此這個遍歷對於Lark是不必要的。如果將zxing內部定製成只支持QR Code格式,那麼就免去了額外的格式檢測。

2. 刪除zxing冗餘代碼

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

  • 刪除zxing除了二維碼之外的格式的相關代碼,zxing對每種格式的相關代碼都放在各自的目錄中,因此我們只需要把這些格式對應的目錄刪除即可,比如aztec、maxicode等。
  • 刪除二維碼的encode相關代碼,即"qrcode/encoder"目錄。
  • 刪除decode後文本的解析相關類(比如地址、通訊錄、郵件等解析類),只保留URI、URL、Text。

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

3. 將處理相機幀從串列改為並行

原本Lark掃一掃的邏輯是串列的,如下圖:

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

缺點是如果處理一幀數據時間很長,會阻礙下一幀的處理,比如上一幀是沒有二維碼的,而下一幀是有二維碼的,如果上一幀處理時間較長,那麼雖然用戶對準了二維碼,但是實際處理的還是上一幀,因此不太合理。

我們將串列處理改成並行處理,一旦從onPreviewFrame()獲取一幀數據,將decode任務丟進線程池,並立即調用setOneShotPreviewCallback()獲取下一幀數據。一旦某個任務檢測到二維碼,立即將isSuccess變數置為true,忽略其他任務。這樣能夠大大加快二維碼檢測的速度。

4. 自動放大

當二維碼很小很遠時,自動放大能大大加快檢測二維碼的速度。

QRCodeReader的decode()是二維碼檢測的主方法,分為兩步:(1)大致判斷是否存在二維碼;(2)解碼。

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

具體二維碼的原理參見:二維碼的生成細節和原理。

我們實現了zoomCamera(),如果判斷需要放大,則返回true,如果不需要放大,則返回false。代碼如下:

我們在第一步和第二步中間插入該方法,如果需要放大,則不執行第二步;如果二維碼已經足夠大,則執行第二步。代碼如下:

5. 雙擊放大

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

6. 重力感測器聚焦

重力感測器能夠捕捉用戶手機的運動狀態,當檢測到用戶手機停止時,觸發對焦邏輯。我們通過實現SensorEventListener介面,並重寫onSensorChanged()監聽手機的運動狀態。

7. 手勢調整焦距

為了更精細化的讓用戶調整焦距,我們提供了手勢來縮放焦距。通過在onTouchEvent()中獲取用戶兩個手指的距離是越來越近還是越來越遠來調整焦距。代碼如下:

優化結果

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

上圖表示了從打開相機到二維碼解碼成功的耗時,可以看出,整體時間提升了300%+。

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

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


推薦閱讀:
查看原文 >>
相关文章