1. 程式人生 > >二維碼識別之Android完整編譯Zbar

二維碼識別之Android完整編譯Zbar

大概剛做Android開發的時候就做過二維碼掃描,那時候懂的東西少,就搜出來了ZXing和Zbar兩個庫。ZXing是純Java程式碼實現的,適用於Android平臺;Zbar是C實現的,可以供很多語言和平臺使用,比如Java、iOS平臺、Android平臺,Python等等。很明顯Zbar的識別率和速度都是明顯快於ZXing的,但是無奈那時候不會編譯Zbar,只好下載了ZXing,但是由於當時技術能力不足,對於ZXing自定義剪下框也做不出來,只好下載了別人編譯好的Zbar,可能由於別人修改了程式碼或者編譯的不是很完整,後期bug層出,廢了好大勁才完善好。

後來一直沒有機會學習二維碼掃描,直到前幾天需要給我們平臺的APP加上了二維碼掃描功能,我決定使用ZBar,於是我完整的編譯了一次,今天把這個過程記錄下來,希望可以幫助到需要的同學。

比如微信使用的是ZXing,但是我肯定的說他們修改了不少原始碼,而且有很多地方應該改成了jni實現,所以微信的識別速率和準確率是相當高的,不過今天我編譯後的封裝也是秒秒鐘就可以識別。

因為Zbar是基於LGPL-2.1開源的,因此我基於LGPL-2.1協議,我把一個完整的專案原始碼和sample放到Github上了,提供直接呼叫zbar的識別byte[]資料的功能和呼叫相機識別二維碼的功能:
https://github.com/yanzhenjie/android-zbar-sdk

特別宣告:本文已經修復了zbar識別中文亂碼的問題!!!

編譯Zbar

在正式編譯之前要注意:

編譯Zbar需要先編譯libiconv,編譯libiconv需要linux環境,需要用到gcc。如果你沒有linux環境也沒有關係,我已經提供了編譯好的libiconv。

所以我就拋棄了提供的編譯包,自己編譯了,下面是步驟。

對於libiconv我是下載的在2017-02-02時釋出的最新版1.15。

一、編譯libiconv

如果你沒有linux環境編譯libiconv,那麼你可以在這裡下載我已經編譯好的libiconv1.15
http://download.csdn.net/detail/yanzhenjie1003/9833225,下好好檔案後,你就可以直接跳過這一節,看下面Zbar和libiconv一起編譯

了。

如果你有linux環境可以編譯libiconv,那麼繼續往下看。
下載好libiconv後,進入libiconv資料夾,如果報許可權錯誤進不去的話執行sudo chmod 777 -R libiconv就可以了:
libiconv

進來後先執行:./configure,如果提示沒許可權那麼執行:sudo chmod 777 configure,然後重新執行/.configure即可。

./configure執行完後再執行make命令即可完成編譯

編譯時可能遇到以下錯誤:
1、configure: error: no acceptable C compiler found in $PATH
這個是說你沒有安裝gcc,安裝gcc後再次執行未完成命令即可。

二、Zbar和libiconv一起編譯

libiconv編譯完成了,接下來把Zbar和libiconv放到一起,編譯出我們需要的so檔案。

  1. 把剛才編譯好的libiconv放入我們專案的jni資料夾。
  2. 解壓剛才下載好的Zbar,首先把Zbar的標頭檔案所在資料夾zbar/include放入我們專案的jni資料夾下。
  3. 把Zbar對java的介面檔案zbarjni.c放入我們專案的jni資料夾,zbrjni.c在zbar/java資料夾下。
  4. 把Zbar的核心庫檔案所在的資料夾zbar/zbar放到我們專案的jni資料夾下。
  5. 把Zbar編譯時需要的Android.mkApplicaiton.mkconfig.hzbar\android\jni下拷貝到我們專案的jni資料夾下。

此時我們專案的jni資料夾是這樣的:
zbar

理論上現在可以開始編譯了吧,但是呢因為我們改動了zbar的資料夾結構,所以我們要對Android.mk進行改動,主要改的是資料夾路徑和檔案路徑,修改後的Android.mk的內容如下:

MY_LOCAL_PATH := $(call my-dir)

# libiconv
include $(CLEAR_VARS)
LOCAL_PATH := $(MY_LOCAL_PATH)
LOCAL_MODULE := libiconv
LOCAL_CFLAGS := \
    -Wno-multichar \
    -D_ANDROID \
    -DLIBDIR="c" \
    -DBUILDING_LIBICONV \
    -DBUILDING_LIBCHARSET \
    -DIN_LIBRARY

LOCAL_SRC_FILES := \
    libiconv-1.15/lib/iconv.c \
    libiconv-1.15/libcharset/lib/localcharset.c \
    libiconv-1.15/lib/relocatable.c

LOCAL_C_INCLUDES := \
    $(LOCAL_PATH)/libiconv-1.15/include \
    $(LOCAL_PATH)/libiconv-1.15/libcharset \
    $(LOCAL_PATH)/libiconv-1.15/libcharset/include

include $(BUILD_SHARED_LIBRARY)

LOCAL_LDLIBS := -llog -lcharset

# -----------------------------------------------------

# libzbar
include $(CLEAR_VARS)
LOCAL_PATH := $(MY_LOCAL_PATH)
LOCAL_MODULE := zbar
LOCAL_SRC_FILES := \
            zbarjni.c \
            zbar/img_scanner.c \
            zbar/decoder.c \
            zbar/image.c \
            zbar/symbol.c \
            zbar/convert.c \
            zbar/config.c \
            zbar/scanner.c \
            zbar/error.c \
            zbar/refcnt.c \
            zbar/video.c \
            zbar/video/null.c \
            zbar/decoder/code128.c \
            zbar/decoder/code39.c \
            zbar/decoder/code93.c \
            zbar/decoder/codabar.c \
            zbar/decoder/databar.c \
            zbar/decoder/ean.c \
            zbar/decoder/i25.c \
            zbar/decoder/qr_finder.c \
            zbar/qrcode/bch15_5.c \
            zbar/qrcode/binarize.c \
            zbar/qrcode/isaac.c \
            zbar/qrcode/qrdec.c \
            zbar/qrcode/qrdectxt.c \
            zbar/qrcode/rs.c \
            zbar/qrcode/util.c

LOCAL_C_INCLUDES := \
            $(LOCAL_PATH)/include \
            $(LOCAL_PATH)/zbar \
            $(LOCAL_PATH)/libiconv-1.15/include

LOCAL_SHARED_LIBRARIES := libiconv

include $(BUILD_SHARED_LIBRARY)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71

然後在Application.mk中填寫你要編譯的平臺,如果想全部編譯:

APP_ABI := all
  • 1

如果要指定編譯某幾個平臺,把平臺名稱依次空格隔開寫上即可:

APP_ABI := armeabi armeabi-v7a x86 x86_64 mips mips_64 arm64_v8a
  • 1

此時我們用命令列進入專案的jni資料夾的父母路,比如一般jni情況下jni資料夾位於ProjectName/ModuleName/src/main/jni,那麼我們就進入這個main,然後此時執行ndk-build進行編譯。

如果提示沒有ndk-build這個命令,那麼你需要從http://developer.android.com下載ndk並且在電腦上配置PATH。

ndk-build執行完後會在libs下生成所有平臺的so資料夾,資料夾裡面是需要的libiconvzbar的so檔案。

編譯Zbar和libiconv時遇到的錯誤解決

編譯過程中可能發現如下錯誤,請按照修改方案修改即可。

1、libiconv-1.15/jni/libcharset/lib/localcharset.c:51:24: error: langinfo.h: No such file or directory
開啟libiconv-1.15/libcharset/config.h檔案,搜尋#define HAVE_LANGINFO_CODESET,大概在14行,把這行註釋了即可:

/* #define HAVE_LANGINFO_CODESET 1 */
  • 1

2、…c undeclaired…
開啟libiconv-1.15/libcharset/lib/localcharset.c,搜尋到函式get_charset_aliases(),大概在124行。

大概在195行左右,有一個int c;(沒有的話你可以搜尋int c;),把這個一行程式碼移動到get_charset_aliases()開頭:
移動之前:
移動之前

移動之後:
移動之後

zbar的jar包

現在so檔案有了,剩下的就是怎麼呼叫so中的函式來識別條碼/二維碼了,首先把zbar/java下在net.sourceforge.zbar包和裡邊的java檔案拷貝到你的專案的java目錄下,大概結構如下:
這裡寫圖片描述

當然你也像這樣使用原始碼,也可以把這幾個類打包成jar包。

呼叫Zbar識別二維碼

現在全部都編譯好了,jar檔案也有了,我們可以呼叫jar中封裝的方法來識別二維碼了:

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就是資料。
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19

山高水遠,江湖再見!