Zxing Zbar 結合真正的極速秒掃
目前APP開發基本都用到二維碼掃描的功能,可能這個大家一看就覺得。論壇Demo一大把,隨便copy一個就可以使用了!起初我也是這麼認為,網上確實有很多,隨便找了一個改改確實也能用!!!用過一段時間來發現Bug太多,比如某些手機預覽的時候有變形,圖像有拉申,有些手機識別速度非常慢等問題,經過客戶的幾次吐槽後索性自己來做吧。
網上有許多人用zxing 做相機預覽,通過zbar進行解碼, 速度是有提升, 但是也有幾個問題:
- zxing 支持更多條碼類型,例如Data Matrix 、 Axtec、 PDF417等類型的條碼。
- zbar 並不支持以上舉例的條碼。
- zbar 對二維碼有中文會亂碼。
於是我就在想, 自己把這兩個庫結合起來,讓用戶自己選擇用哪個解碼庫不就完了, 說干就干。下載了zxing運行了Demo 默認是橫屏掃描的, 論壇有很多大神改豎屏的方案,本人挨個嘗試後,發現改成豎屏後識別速度明顯慢了許多!!!
其中最主要的原因是相機捕獲的數據始終是橫向的,即使你把預覽框改成了豎向,但是數據依然是橫向,因此需要將數據進行旋轉,而zxing既然不支持橫向掃條碼(二維碼就可以從四面去掃描),這個不得不吐槽zxing了,而旋轉數據的代碼在一台RedMi 3手機上測試發現既然用了600ms左右, 既然看了所有豎屏方案的文章都沒有提及,知道慢的原因了就好辦了看下面的步驟吧。
(一) 編譯zxing
我相信很多人下載了以後編譯都會出現問題,zxing是用maven構建的項目, 那麼編譯需要用到maven。關於maven的下載安裝配置環境變數等這裡就不說了, 實在不會的就聯繫我吧,也可以直接用我編譯好的包哈。
- 本人於2017-08-02下載Zxing的demo 3.3.1版本,發現項目是用maven構造的,編譯花了一下午,報了幾個錯最後是修改了根目錄下的pom.xml文件, 最終編譯成功, 生成了apk文件及兩個jar包。
- 編譯就是直接解壓進入zxing-master目錄執行 mvn install 其中可能會報 Failed to execute goal org.apache.maven.plugins:maven-enforcer-plugin:1.4.1 Failed to execute goal org.apache.rat:apache-rat-plugin:0.12 這兩個錯誤,我是簡單粗暴的方式進行了修改pom.xml文件:
再執行mvn install 一段時間後就OK了。
這時候在android-core 及core 目錄下都會有 創建target目錄並且會生成 android-core-3.3.1-SNAPSHOT.jar 及 core-3.3.1-SNAPSHOT.jar 兩個jar包。
3. 直接用android studio 打開項目選擇zxing-master目錄下的android即可,android studio會自動幫我們把相關的文件整合成as項目,此時會報錯,因為沒有jar包,只需要把上面兩個jar包複製到zxing-master/android/app/libs即可。
(二) 優化Zxing
默認是橫屏的, 這個實在是不能接受,不符合大家的習慣, 於是在zxing的lssuse中找到挺多大神給的方案,也在網上搜索半天,挨個試試以後,總結了一下,豎屏的方案主要修改地方為:
- 修改activity CaptureActivity 中 android:screenOrientation="portrait" 固定為豎屏。
- 修改CameraManager類 的getFramingRectInPreview方法,把
rect.left = rect.left * cameraResolution.x / screenResolution.x; rect.right = rect.right * cameraResolution.x / screenResolution.x; rect.top = rect.top * cameraResolution.y / screenResolution.y;rect.bottom = rect.bottom * cameraResolution.y / screenResolution.y; 改為:rect.left = rect.left * cameraResolution.y / screenResolution.x; rect.right = rect.right * cameraResolution.y / screenResolution.x; rect.top = rect.top * cameraResolution.x / screenResolution.y;rect.bottom = rect.bottom * cameraResolution.x / screenResolution.y;
3. 此時會發現掃描框並不是正方形,非常的高,於是再修改了
CameraManager類的getFramingRect()方法, 在 int leftOffset = (screenResolution.x - width) / 2;
這行代碼前面增加了
width = width + 50;
height = width;
4. 到目前為此,豎屏顯示沒問題了, 但是解碼失敗,掃半天都不行,原因上面講了需要將相機數據進行旋轉:
在DecodeHandler類的 decode方法中
PlanarYUVLuminanceSource source = activity.getCameraManager().buildLuminanceSource(data, width, height);
前增加
byte[] rotatedData = new byte[data.length]; for (int y = 0; y < height; y++) { for (int x = 0; x < width; x++) rotatedData[x * height + height - y - 1] = data[x + y * width];} int tmp = width; width = height; height = tmp;data = rotatedData;
到目前為止,掃碼是沒有問題了, 但是速度非常慢,可能在好一點的手機上沒有感覺,但是本人在一台在紅米3手機上測試,每次執行完decode方法都是在1300ms左右。
解碼速度的優化從decode方法著手 !
(三)編譯Zbar
- Zbar主頁 點開android提示需要依賴libiconv這個庫 http://www.gnu.org/software/libiconv 於是百度半天后參考了別人的貼子,下載了 libiconv-1.15庫,然後Zbar與libiconv一起編譯。
- 將Zbar-master目錄下的 include zbar 及下載的 libiconv-1.15 一起複制到jni目錄,這個jni目錄可以隨意在任何地方新建一文件夾,只要叫jni即可。
- 將zbar-master/android/jni目錄下的 三個文件Android.mk、Application.mk config.h 複製到 jni目錄下。
- 將Zbar-master/java/zbarjni.c也複製到jni目錄下。
- 由於修改了目錄結構,所以需要修改Android.mk文件指定編譯的c代碼目錄。
- 如果有ndk環境,直接在jni目錄下執行ndk-build大概10來分鐘後會在jni目錄下生成libs目錄, 下面會有一堆so庫。
- 將Zbar-master/java目錄下的java代碼複製到項目中即可使用了。
- 修改net.sourceforge.zbar下的 Image.java、ImageScanner.java、SymbolSet.java 在System.loadLibrary("zbarjni");前面把 libiconv庫給載入進去 System.loadLibrary("iconv"); 注意這些名字要跟實際生成的so庫名字一樣哈。
Zbar解碼代碼為:
byte[] imageData = ...; //相機捕獲的數據或圖片數據Image barcode = new Image(size.width, size.height, "Y800");barcode.setData(imageData);// 指定二維碼在圖片中的區域,也可以不指定,識別全圖。// barcode.setCrop(startX, startY, width, height);String qrCodeString = null;int result = mImageScanner.scanImage(barcode);if (result != 0) {SymbolSet symSet = mImageScanner.getResults();for (Symbol sym : symSet) qrCodeString = sym.getData();}if (!TextUtils.isEmpty(qrCodeString)) { // 成功識別二維碼,qrCodeString就是數據。}
(四)Zxing與Zbar的整合
- 知道zbar的解碼過程以後就好辦了, 直接到zxing的 DecodeHandler.java decode()方法中增加代碼:
Image barcode = new Image(width, height, "Y800"); barcode.setData(data); Rect rect = activity.getCameraManager().getFramingRectInPreview(); if (rect != null) { /* zbar 解碼庫,不需要將數據進行旋轉,因此設置裁剪區域是的x為 top, y為left 設置了裁剪區域,解碼速度快了近5倍左右 */ barcode.setCrop(rect.top, rect.left, rect.width(), rect.height()); // 設置截取區域,也就是你的掃描框在圖片上的區域. } ImageScanner mImageScanner = new ImageScanner(); int result = mImageScanner.scanImage(barcode); if (result != 0) { SymbolSet symSet = mImageScanner.getResults(); for (Symbol sym : symSet) resultQRcode = sym.getData(); }
2. 但是本人還是想保留zxing本身的解碼能力,例如PDF417 zbar並不支持。
3. 持續優化decode方法,根據decode方法中日誌,發現數組的轉換大概花了 600ms (測試機 RedMI 3)因此本人用c語言寫了個數組轉換的方法,編譯成so庫,發現調用c代碼進行數組的轉換,只需要30ms左右,快了接近20倍。具體代碼在DecodeHandlerJni.c 文件中。
4. 在 PlanarYUVLuminanceSource source = activity.getCameraManager().buildLuminanceSource(data, width, height);
這行代碼前面增加:
data = DecodeHandlerJni.dataHandler(data, data.length, width, height); int tmp = width;width = height;height = tmp;
那麼現在豎屏狀態下的解碼速度提升了一倍以上了。
為了兼容zxing與zbar 本人在設置界面增加了一個選擇:
兼容模式就是用Zxing來解碼, 而高速模式是用Zbar來解碼。
由於本人對Zxing源代碼改動較少,已知有幾個問題可定製修改:
- 連續掃描的時候,兩個界面的數據傳遞可以使用廣播進行。
- 掃碼後播放聲音默認是使用MediaPlayer,建議使用SoundPool, 因為連續掃碼過快的時候,MediaPlayer播放不及時。
- 掃碼界面未增加閃光燈及連接掃描按鈕。
- 掃碼框沒有美化,例如增加四個角等等。
最後送上源代碼下載地址: XieZhiFa/ZxingZbar
推薦閱讀:
TAG:二維碼掃描 |