先來看JNI調OpenCV的三種方式。很多人會吃驚腫麼JNI調OpenCV還會有3種方式,長久以來大量網上教程都說在Android上只有Java和JNI兩種方式使用OpenCV,怎麼又冒出來3種使用JNI的方式。經本人研究,確實有3種調JNI的方式,就連官網指導文檔都模棱兩可,所以讓很多人不知所措。這三種方式分別是:
1、使用靜態的OpenCV庫的方式;
2、使用動態的OpenCV庫的方式;
3、同時使用Java的API又使用JNI的接口的方式,此時編譯時一般使用的是動態鏈接OpenCV庫的方式。
要說明的是,這三種方式均無需安裝OpenCVManager,區別在於mk文件的不同。個人最推崇的就是第一種方式,第一種方式也是和OpenCV2.3.1在JNI調OpenCV使用完全吻合的一種方式。本文是以windows平臺最新的OpenCV-2.4.9-android-sdk爲基礎,使用2.4.9的OpenCV4Android需要使用NDK版本爲r9,本人使用的是android-ndk-r9d的版本。之所以昨晚搗騰到2點,就是因爲之前使用的ndk r7的版本,怎麼編都編不過,因少東西報上千行錯誤。android-ndk-r9d安裝十分簡單,只需要解壓縮配置一個環境變量即可。
一、Android以JNI調OpenCV的第一種配置方法:
Application.mk文件裏的內容如下:
APP_STL:=gnustl_static
APP_CPPFLAGS:=-frtti -fexceptions
APP_ABI:= armeabi-v7a
這三種方式的Application.mk都一樣,所以往後不說了。在Application.mk裏還可以配置APP_PLATFORM=17類似這種,當然不配置完全可以。
Android.mk內容如下:
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
OpenCV_INSTALL_MODULES:=on
OPENCV_CAMERA_MODULES:=off
OPENCV_LIB_TYPE:=STATIC
ifeq ("$(wildcard $(OPENCV_MK_PATH))","")
include D:\ProgramFile\OpenCV-2.4.9-android-sdk\sdk\native\jni\OpenCV.mk
else
include $(OPENCV_MK_PATH)
endif
LOCAL_MODULE := ProcessImg
LOCAL_SRC_FILES := DetectFace_JNI.cpp \
src/copyToAssets.cpp \
src/detectFace.cpp
LOCAL_LDLIBS += -lm -llog
include $(BUILD_SHARED_LIBRARY)
逐一解釋下,OpenCV_INSTALL_MODULES:=on的意思是自動將依賴的OpenCV的so庫拷貝到libs目錄下,但很遺憾的是,這個命令只對OPENCV_CAMERA_MODULES有效。只有當OPENCV_CAMERA_MODULES:=on時,可以看到他會自動將裏面的帶camera的so拷貝至工程下的libs文件夾下。include D:\ProgramFile\OpenCV-2.4.9-android-sdk\sdk\native\jni\OpenCV.mk這句話比較關鍵,這是我安裝OpenCV-2.4.9-android-sdk的地方,我將其安裝到了D盤。而我的工作空間在E盤也是ok的。而不用像OpenCV2.3.1使用時,限制這個解壓縮包的位置了。LOCAL_MODULE 是要生成的庫的名字,LOCAL_SRC_FILES 是jni文件夾下的cpp文件,其中的src說明我的jni下還有個子文件夾名字是“src”,這塊替換成自己的源碼文件就ok了。
爲了測試的嚴謹性,在工程裏將libs文件夾的東西,和obj文件夾下的東西全刪了。用cygwin進到工程,輸入ndk-build,看到如下信息:
- <span style='font-family: "Comic Sans MS";'><span style="font-size: 18px;">Administrator@yanzi /cygdrive/e/WorkSpaces/OpenCV4Android/FaceDetectLiu2
- $ ndk-build
- Android NDK: WARNING: APP_PLATFORM android-19 is larger than android:minSdkVersion 16 in ./AndroidManifest.xml
- [armeabi-v7a] Cygwin : Generating dependency file converter script
- [armeabi-v7a] Compile++ thumb: ProcessImg <= DetectFace_JNI.cpp
- jni/DetectFace_JNI.cpp: In function '_jstring* Java_org_yan_processlib_LibProcessImg_processIplImg(JNIEnv*, jobject, jintArray, int, int)':
- jni/DetectFace_JNI.cpp:99:44: warning: converting 'false' to pointer type for argument 2 of 'jint* _JNIEnv::GetIntArrayElements(jintArray, jboolean*)' [-Wconversion-null]
- jni/DetectFace_JNI.cpp: In function '_jstring* Java_org_yan_processlib_LibProcessImg_processStaticImg(JNIEnv*, jobject, jintArray, int, int)':
- jni/DetectFace_JNI.cpp:133:44: warning: converting 'false' to pointer type for argument 2 of 'jint* _JNIEnv::GetIntArrayElements(jintArray, jboolean*)' [-Wconversion-null]
- [armeabi-v7a] Compile++ thumb: ProcessImg <= copyToAssets.cpp
- [armeabi-v7a] Compile++ thumb: ProcessImg <= detectFace.cpp
- [armeabi-v7a] SharedLibrary : libProcessImg.so
- [armeabi-v7a] Install : libProcessImg.so => libs/armeabi-v7a/libProcessImg.so
- </span></span>
<span style="font-family: 'Comic Sans MS';"><span style="font-size: 18px;">Administrator@yanzi /cygdrive/e/WorkSpaces/OpenCV4Android/FaceDetectLiu2
$ ndk-build
Android NDK: WARNING: APP_PLATFORM android-19 is larger than android:minSdkVersion 16 in ./AndroidManifest.xml
[armeabi-v7a] Cygwin : Generating dependency file converter script
[armeabi-v7a] Compile++ thumb: ProcessImg <= DetectFace_JNI.cpp
jni/DetectFace_JNI.cpp: In function '_jstring* Java_org_yan_processlib_LibProcessImg_processIplImg(JNIEnv*, jobject, jintArray, int, int)':
jni/DetectFace_JNI.cpp:99:44: warning: converting 'false' to pointer type for argument 2 of 'jint* _JNIEnv::GetIntArrayElements(jintArray, jboolean*)' [-Wconversion-null]
jni/DetectFace_JNI.cpp: In function '_jstring* Java_org_yan_processlib_LibProcessImg_processStaticImg(JNIEnv*, jobject, jintArray, int, int)':
jni/DetectFace_JNI.cpp:133:44: warning: converting 'false' to pointer type for argument 2 of 'jint* _JNIEnv::GetIntArrayElements(jintArray, jboolean*)' [-Wconversion-null]
[armeabi-v7a] Compile++ thumb: ProcessImg <= copyToAssets.cpp
[armeabi-v7a] Compile++ thumb: ProcessImg <= detectFace.cpp
[armeabi-v7a] SharedLibrary : libProcessImg.so
[armeabi-v7a] Install : libProcessImg.so => libs/armeabi-v7a/libProcessImg.so
</span></span>
上面兩個警告麼有關係,編譯成功。生成的libProcessImg.so的大小爲4M,整個apk大小爲1.99M。
注意,如果將mk裏的LOCAL_LDLIBS += -lm -llog這一句錯誤的寫爲:LOCAL_LDLIBS := -lm -llog,即將“+=”錯寫成了“:=”將會看到如下大量錯誤:
- <span style='font-family: "Comic Sans MS";'><span style="font-size: 18px;">$ ndk-build
- Android NDK: WARNING: APP_PLATFORM android-19 is larger than android:minSdkVersion 16 in ./AndroidManifest.xml
- [armeabi-v7a] Compile++ thumb: ProcessImg <= DetectFace_JNI.cpp
- jni/DetectFace_JNI.cpp: In function '_jstring* Java_org_yan_processlib_LibProcessImg_processIplImg(JNIEnv*, jobject, jintArray, int, int)':
- jni/DetectFace_JNI.cpp:99:44: warning: converting 'false' to pointer type for argument 2 of 'jint* _JNIEnv::GetIntArrayElements(jintArray, jboolean*)' [-Wconversion-null]
- jni/DetectFace_JNI.cpp: In function '_jstring* Java_org_yan_processlib_LibProcessImg_processStaticImg(JNIEnv*, jobject, jintArray, int, int)':
- jni/DetectFace_JNI.cpp:133:44: warning: converting 'false' to pointer type for argument 2 of 'jint* _JNIEnv::GetIntArrayElements(jintArray, jboolean*)' [-Wconversion-null]
- [armeabi-v7a] Compile++ thumb: ProcessImg <= copyToAssets.cpp
- [armeabi-v7a] Compile++ thumb: ProcessImg <= detectFace.cpp
- [armeabi-v7a] SharedLibrary : libProcessImg.so
- D:/ProgramFile/android-ndk-r9d/toolchains/arm-linux-androideabi-4.6/prebuilt/windows/bin/../lib/gcc/arm-linux-androideabi/4.6/../../../../arm-linux-androideabi/bin/ld.exe: D:/ProgramFile\OpenCV-2.4.9-android-sdk\sdk\native\jni\/../libs/armeabi-v7a/libopencv_core.a(persistence.cpp.o): in function icvGets(CvFileStorage*, char*, int):persistence.cpp(.text._ZL7icvGetsP13CvFileStoragePci+0x7e): error: undefined reference to 'gzgets'
- D:/ProgramFile/android-ndk-r9d/toolchains/arm-linux-androideabi-4.6/prebuilt/windows/bin/../lib/gcc/arm-linux-androideabi/4.6/../../../../arm-linux-androideabi/bin/ld.exe: D:/ProgramFile\OpenCV-2.4.9-android-sdk\sdk\native\jni\/../libs/armeabi-v7a/libopencv_core.a(persistence.cpp.o): in function icvXMLSkipSpaces(CvFileStorage*, char*, int):persistence.cpp(.text._ZL16icvXMLSkipSpacesP13CvFileStoragePci+0x1c4): error: undefined reference to 'gzeof'
- D:/ProgramFile/android-ndk-r9d/toolchains/arm-linux-androideabi-4.6/prebuilt/windows/bin/../lib/gcc/arm-linux-androideabi/4.6/../../../../arm-linux-androideabi/bin/ld.exe: D:/ProgramFile\OpenCV-2.4.9-android-sdk\sdk\native\jni\/../libs/armeabi-v7a/libopencv_core.a(persistence.cpp.o): in function icvYMLSkipSpaces(CvFileStorage*, char*, int, int) [clone .constprop.65]:persistence.cpp(.text._ZL16icvYMLSkipSpacesP13CvFileStoragePcii.constprop.65+0x122): error: undefined reference to 'gzeof'
- D:/ProgramFile/android-ndk-r9d/toolchains/arm-linux-androideabi-4.6/prebuilt/windows/bin/../lib/gcc/arm-linux-androideabi/4.6/../../../../arm-linux-androideabi/bin/ld.exe: D:/ProgramFile\OpenCV-2.4.9-android-sdk\sdk\native\jni\/../libs/armeabi-v7a/libopencv_core.a(persistence.cpp.o): in function icvPuts(CvFileStorage*, char const*):persistence.cpp(.text._ZL7icvPutsP13CvFileStoragePKc+0x32): error: undefined reference to 'gzputs'
- D:/ProgramFile/android-ndk-r9d/toolchains/arm-linux-androideabi-4.6/prebuilt/windows/bin/../lib/gcc/arm-linux-androideabi/4.6/../../../../arm-linux-androideabi/bin/ld.exe: D:/ProgramFile\OpenCV-2.4.9-android-sdk\sdk\native\jni\/../libs/armeabi-v7a/libopencv_core.a(persistence.cpp.o): in function icvClose(CvFileStorage*, std::string*):persistence.cpp(.text._ZL8icvCloseP13CvFileStoragePSs+0x132): error: undefined reference to 'gzclose'
- D:/ProgramFile/android-ndk-r9d/toolchains/arm-linux-androideabi-4.6/prebuilt/windows/bin/../lib/gcc/arm-linux-androideabi/4.6/../../../../arm-linux-androideabi/bin/ld.exe: D:/ProgramFile\OpenCV-2.4.9-android-sdk\sdk\native\jni\/../libs/armeabi-v7a/libopencv_core.a(persistence.cpp.o): in function cvOpenFileStorage:persistence.cpp(.text.cvOpenFileStorage+0x1ac): error: undefined reference to 'gzrewind'
- D:/ProgramFile/android-ndk-r9d/toolchains/arm-linux-androideabi-4.6/prebuilt/windows/bin/../lib/gcc/arm-linux-androideabi/4.6/../../../../arm-linux-androideabi/bin/ld.exe: D:/ProgramFile\OpenCV-2.4.9-android-sdk\sdk\native\jni\/../libs/armeabi-v7a/libopencv_core.a(persistence.cpp.o): in function cvOpenFileStorage:persistence.cpp(.text.cvOpenFileStorage+0x6d4): error: undefined reference to 'gzclose'
- D:/ProgramFile/android-ndk-r9d/toolchains/arm-linux-androideabi-4.6/prebuilt/windows/bin/../lib/gcc/arm-linux-androideabi/4.6/../../../../arm-linux-androideabi/bin/ld.exe: D:/ProgramFile\OpenCV-2.4.9-android-sdk\sdk\native\jni\/../libs/armeabi-v7a/libopencv_core.a(persistence.cpp.o): in function cvOpenFileStorage:persistence.cpp(.text.cvOpenFileStorage+0x75a): error: undefined reference to 'gzopen'
- D:/ProgramFile/android-ndk-r9d/toolchains/arm-linux-androideabi-4.6/prebuilt/windows/bin/../lib/gcc/arm-linux-androideabi/4.6/../../../../arm-linux-androideabi/bin/ld.exe: D:/ProgramFile\OpenCV-2.4.9-android-sdk\sdk\native\jni\/../libs/armeabi-v7a/libopencv_core.a(persistence.cpp.o): in function cvOpenFileStorage:persistence.cpp(.text.cvOpenFileStorage+0xd80): error: undefined reference to 'gzclose'
- D:/ProgramFile/android-ndk-r9d/toolchains/arm-linux-androideabi-4.6/prebuilt/windows/bin/../lib/gcc/arm-linux-androideabi/4.6/../../../../arm-linux-androideabi/bin/ld.exe: D:/ProgramFile\OpenCV-2.4.9-android-sdk\sdk\native\jni\/../3rdparty/libs/armeabi-v7a/liblibpng.a(pngread.c.o): in function png_create_read_struct_2:pngread.c(.text.png_create_read_struct_2+0x112): error: undefined reference to 'inflateInit_'
- D:/ProgramFile/android-ndk-r9d/toolchains/arm-linux-androideabi-4.6/prebuilt/windows/bin/../lib/gcc/arm-linux-androideabi/4.6/../../../../arm-linux-androideabi/bin/ld.exe: D:/ProgramFile\OpenCV-2.4.9-android-sdk\sdk\native\jni\/../3rdparty/libs/armeabi-v7a/liblibpng.a(pngread.c.o): in function png_read_row:pngread.c(.text.png_read_row+0x218): error: undefined reference to 'inflate'
- D:/ProgramFile/android-ndk-r9d/toolchains/arm-linux-androideabi-4.6/prebuilt/windows/bin/../lib/gcc/arm-linux-androideabi/4.6/../../../../arm-linux-androideabi/bin/ld.exe: D:/ProgramFile\OpenCV-2.4.9-android-sdk\sdk\native\jni\/../3rdparty/libs/armeabi-v7a/liblibpng.a(pngread.c.o): in function png_read_destroy:pngread.c(.text.png_read_destroy+0x96): error: undefined reference to 'inflateEnd'
- D:/ProgramFile/android-ndk-r9d/toolchains/arm-linux-androideabi-4.6/prebuilt/windows/bin/../lib/gcc/arm-linux-androideabi/4.6/../../../../arm-linux-androideabi/bin/ld.exe: D:/ProgramFile\OpenCV-2.4.9-android-sdk\sdk\native\jni\/../3rdparty/libs/armeabi-v7a/liblibpng.a(pngwrite.c.o): in function png_write_flush:pngwrite.c(.text.png_write_flush+0x1c): error: undefined reference to 'deflate'
- </span></span>
<span style="font-family: 'Comic Sans MS';"><span style="font-size: 18px;">$ ndk-build
Android NDK: WARNING: APP_PLATFORM android-19 is larger than android:minSdkVersion 16 in ./AndroidManifest.xml
[armeabi-v7a] Compile++ thumb: ProcessImg <= DetectFace_JNI.cpp
jni/DetectFace_JNI.cpp: In function '_jstring* Java_org_yan_processlib_LibProcessImg_processIplImg(JNIEnv*, jobject, jintArray, int, int)':
jni/DetectFace_JNI.cpp:99:44: warning: converting 'false' to pointer type for argument 2 of 'jint* _JNIEnv::GetIntArrayElements(jintArray, jboolean*)' [-Wconversion-null]
jni/DetectFace_JNI.cpp: In function '_jstring* Java_org_yan_processlib_LibProcessImg_processStaticImg(JNIEnv*, jobject, jintArray, int, int)':
jni/DetectFace_JNI.cpp:133:44: warning: converting 'false' to pointer type for argument 2 of 'jint* _JNIEnv::GetIntArrayElements(jintArray, jboolean*)' [-Wconversion-null]
[armeabi-v7a] Compile++ thumb: ProcessImg <= copyToAssets.cpp
[armeabi-v7a] Compile++ thumb: ProcessImg <= detectFace.cpp
[armeabi-v7a] SharedLibrary : libProcessImg.so
D:/ProgramFile/android-ndk-r9d/toolchains/arm-linux-androideabi-4.6/prebuilt/windows/bin/../lib/gcc/arm-linux-androideabi/4.6/../../../../arm-linux-androideabi/bin/ld.exe: D:/ProgramFile\OpenCV-2.4.9-android-sdk\sdk\native\jni\/../libs/armeabi-v7a/libopencv_core.a(persistence.cpp.o): in function icvGets(CvFileStorage*, char*, int):persistence.cpp(.text._ZL7icvGetsP13CvFileStoragePci+0x7e): error: undefined reference to 'gzgets'
D:/ProgramFile/android-ndk-r9d/toolchains/arm-linux-androideabi-4.6/prebuilt/windows/bin/../lib/gcc/arm-linux-androideabi/4.6/../../../../arm-linux-androideabi/bin/ld.exe: D:/ProgramFile\OpenCV-2.4.9-android-sdk\sdk\native\jni\/../libs/armeabi-v7a/libopencv_core.a(persistence.cpp.o): in function icvXMLSkipSpaces(CvFileStorage*, char*, int):persistence.cpp(.text._ZL16icvXMLSkipSpacesP13CvFileStoragePci+0x1c4): error: undefined reference to 'gzeof'
D:/ProgramFile/android-ndk-r9d/toolchains/arm-linux-androideabi-4.6/prebuilt/windows/bin/../lib/gcc/arm-linux-androideabi/4.6/../../../../arm-linux-androideabi/bin/ld.exe: D:/ProgramFile\OpenCV-2.4.9-android-sdk\sdk\native\jni\/../libs/armeabi-v7a/libopencv_core.a(persistence.cpp.o): in function icvYMLSkipSpaces(CvFileStorage*, char*, int, int) [clone .constprop.65]:persistence.cpp(.text._ZL16icvYMLSkipSpacesP13CvFileStoragePcii.constprop.65+0x122): error: undefined reference to 'gzeof'
D:/ProgramFile/android-ndk-r9d/toolchains/arm-linux-androideabi-4.6/prebuilt/windows/bin/../lib/gcc/arm-linux-androideabi/4.6/../../../../arm-linux-androideabi/bin/ld.exe: D:/ProgramFile\OpenCV-2.4.9-android-sdk\sdk\native\jni\/../libs/armeabi-v7a/libopencv_core.a(persistence.cpp.o): in function icvPuts(CvFileStorage*, char const*):persistence.cpp(.text._ZL7icvPutsP13CvFileStoragePKc+0x32): error: undefined reference to 'gzputs'
D:/ProgramFile/android-ndk-r9d/toolchains/arm-linux-androideabi-4.6/prebuilt/windows/bin/../lib/gcc/arm-linux-androideabi/4.6/../../../../arm-linux-androideabi/bin/ld.exe: D:/ProgramFile\OpenCV-2.4.9-android-sdk\sdk\native\jni\/../libs/armeabi-v7a/libopencv_core.a(persistence.cpp.o): in function icvClose(CvFileStorage*, std::string*):persistence.cpp(.text._ZL8icvCloseP13CvFileStoragePSs+0x132): error: undefined reference to 'gzclose'
D:/ProgramFile/android-ndk-r9d/toolchains/arm-linux-androideabi-4.6/prebuilt/windows/bin/../lib/gcc/arm-linux-androideabi/4.6/../../../../arm-linux-androideabi/bin/ld.exe: D:/ProgramFile\OpenCV-2.4.9-android-sdk\sdk\native\jni\/../libs/armeabi-v7a/libopencv_core.a(persistence.cpp.o): in function cvOpenFileStorage:persistence.cpp(.text.cvOpenFileStorage+0x1ac): error: undefined reference to 'gzrewind'
D:/ProgramFile/android-ndk-r9d/toolchains/arm-linux-androideabi-4.6/prebuilt/windows/bin/../lib/gcc/arm-linux-androideabi/4.6/../../../../arm-linux-androideabi/bin/ld.exe: D:/ProgramFile\OpenCV-2.4.9-android-sdk\sdk\native\jni\/../libs/armeabi-v7a/libopencv_core.a(persistence.cpp.o): in function cvOpenFileStorage:persistence.cpp(.text.cvOpenFileStorage+0x6d4): error: undefined reference to 'gzclose'
D:/ProgramFile/android-ndk-r9d/toolchains/arm-linux-androideabi-4.6/prebuilt/windows/bin/../lib/gcc/arm-linux-androideabi/4.6/../../../../arm-linux-androideabi/bin/ld.exe: D:/ProgramFile\OpenCV-2.4.9-android-sdk\sdk\native\jni\/../libs/armeabi-v7a/libopencv_core.a(persistence.cpp.o): in function cvOpenFileStorage:persistence.cpp(.text.cvOpenFileStorage+0x75a): error: undefined reference to 'gzopen'
D:/ProgramFile/android-ndk-r9d/toolchains/arm-linux-androideabi-4.6/prebuilt/windows/bin/../lib/gcc/arm-linux-androideabi/4.6/../../../../arm-linux-androideabi/bin/ld.exe: D:/ProgramFile\OpenCV-2.4.9-android-sdk\sdk\native\jni\/../libs/armeabi-v7a/libopencv_core.a(persistence.cpp.o): in function cvOpenFileStorage:persistence.cpp(.text.cvOpenFileStorage+0xd80): error: undefined reference to 'gzclose'
D:/ProgramFile/android-ndk-r9d/toolchains/arm-linux-androideabi-4.6/prebuilt/windows/bin/../lib/gcc/arm-linux-androideabi/4.6/../../../../arm-linux-androideabi/bin/ld.exe: D:/ProgramFile\OpenCV-2.4.9-android-sdk\sdk\native\jni\/../3rdparty/libs/armeabi-v7a/liblibpng.a(pngread.c.o): in function png_create_read_struct_2:pngread.c(.text.png_create_read_struct_2+0x112): error: undefined reference to 'inflateInit_'
D:/ProgramFile/android-ndk-r9d/toolchains/arm-linux-androideabi-4.6/prebuilt/windows/bin/../lib/gcc/arm-linux-androideabi/4.6/../../../../arm-linux-androideabi/bin/ld.exe: D:/ProgramFile\OpenCV-2.4.9-android-sdk\sdk\native\jni\/../3rdparty/libs/armeabi-v7a/liblibpng.a(pngread.c.o): in function png_read_row:pngread.c(.text.png_read_row+0x218): error: undefined reference to 'inflate'
D:/ProgramFile/android-ndk-r9d/toolchains/arm-linux-androideabi-4.6/prebuilt/windows/bin/../lib/gcc/arm-linux-androideabi/4.6/../../../../arm-linux-androideabi/bin/ld.exe: D:/ProgramFile\OpenCV-2.4.9-android-sdk\sdk\native\jni\/../3rdparty/libs/armeabi-v7a/liblibpng.a(pngread.c.o): in function png_read_destroy:pngread.c(.text.png_read_destroy+0x96): error: undefined reference to 'inflateEnd'
D:/ProgramFile/android-ndk-r9d/toolchains/arm-linux-androideabi-4.6/prebuilt/windows/bin/../lib/gcc/arm-linux-androideabi/4.6/../../../../arm-linux-androideabi/bin/ld.exe: D:/ProgramFile\OpenCV-2.4.9-android-sdk\sdk\native\jni\/../3rdparty/libs/armeabi-v7a/liblibpng.a(pngwrite.c.o): in function png_write_flush:pngwrite.c(.text.png_write_flush+0x1c): error: undefined reference to 'deflate'
</span></span>
上兩張運行效果圖,分別是預覽界面檢測人臉和拍照後檢測:
二、Android以JNI調OpenCV的第二種配置方法
Application.mk文件同上,Android.mk文件如下:
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
OpenCV_INSTALL_MODULES:=on
OPENCV_CAMERA_MODULES:=off
OPENCV_LIB_TYPE:=SHARE
ifeq ("$(wildcard $(OPENCV_MK_PATH))","")
include D:\ProgramFile\OpenCV-2.4.9-android-sdk\sdk\native\jni\OpenCV.mk
else
include $(OPENCV_MK_PATH)
endif
LOCAL_MODULE := ProcessImg
LOCAL_SRC_FILES := DetectFace_JNI.cpp \
src/copyToAssets.cpp \
src/detectFace.cpp
LOCAL_LDLIBS := -lm -llog
include $(BUILD_SHARED_LIBRARY)
唯一的變化時將OPENCV_LIB_TYPE:=STATIC 變成了SHARE.即通過動態鏈接的方式連接OpenCV的so。編譯信息如下:
- <span style="font-size: 18px;">Administrator@yanzi /cygdrive/e/WorkSpaces/OpenCV4Android/FaceDetectLiu2
- $ ndk-build
- Android NDK: WARNING: APP_PLATFORM android-19 is larger than android:minSdkVersi on 16 in ./AndroidManifest.xml
- Android NDK: WARNING:jni/Android.mk:ProcessImg: non-system libraries in linker f lags: -lopencv_java
- Android NDK: This is likely to result in incorrect builds. Try using LOCAL_S TATIC_LIBRARIES
- Android NDK: or LOCAL_SHARED_LIBRARIES instead to list the library dependenc ies of the
- Android NDK: current module
- [armeabi-v7a] Compile++ thumb: ProcessImg <= DetectFace_JNI.cpp
- jni/DetectFace_JNI.cpp: In function '_jstring* Java_org_yan_processlib_LibProces sImg_processIplImg(JNIEnv*, jobject, jintArray, int, int)':
- jni/DetectFace_JNI.cpp:99:44: warning: converting 'false' to pointer type for ar gument 2 of 'jint* _JNIEnv::GetIntArrayElements(jintArray, jboolean*)' [-Wconver sion-null]
- jni/DetectFace_JNI.cpp: In function '_jstring* Java_org_yan_processlib_LibProces sImg_processStaticImg(JNIEnv*, jobject, jintArray, int, int)':
- jni/DetectFace_JNI.cpp:133:44: warning: converting 'false' to pointer type for a rgument 2 of 'jint* _JNIEnv::GetIntArrayElements(jintArray, jboolean*)' [-Wconve rsion-null]
- [armeabi-v7a] Compile++ thumb: ProcessImg <= copyToAssets.cpp
- [armeabi-v7a] Compile++ thumb: ProcessImg <= detectFace.cpp
- [armeabi-v7a] SharedLibrary : libProcessImg.so
- D:/ProgramFile/android-ndk-r9d/toolchains/arm-linux-androideabi-4.6/prebuilt/windows/bin/../lib/gcc/arm-linux-androideabi/4.6/../../../../arm-linux-androideabi/bin/ld.exe: warning: hidden symbol '__aeabi_atexit' in D:/ProgramFile/android-ndk-r9d/sources/cxx-stl/gnu-libstdc++/4.6/libs/armeabi-v7a/libgnustl_static.a(atexit_arm.o) is referenced by DSO D:/ProgramFile\OpenCV-2.4.9-android-sdk\sdk\native\jni\/../libs/armeabi-v7a/libopencv_java.so
- [armeabi-v7a] Install : libProcessImg.so => libs/armeabi-v7a/libProcessImg.so
- </span>
<span style="font-size: 18px;">Administrator@yanzi /cygdrive/e/WorkSpaces/OpenCV4Android/FaceDetectLiu2
$ ndk-build
Android NDK: WARNING: APP_PLATFORM android-19 is larger than android:minSdkVersi on 16 in ./AndroidManifest.xml
Android NDK: WARNING:jni/Android.mk:ProcessImg: non-system libraries in linker f lags: -lopencv_java
Android NDK: This is likely to result in incorrect builds. Try using LOCAL_S TATIC_LIBRARIES
Android NDK: or LOCAL_SHARED_LIBRARIES instead to list the library dependenc ies of the
Android NDK: current module
[armeabi-v7a] Compile++ thumb: ProcessImg <= DetectFace_JNI.cpp
jni/DetectFace_JNI.cpp: In function '_jstring* Java_org_yan_processlib_LibProces sImg_processIplImg(JNIEnv*, jobject, jintArray, int, int)':
jni/DetectFace_JNI.cpp:99:44: warning: converting 'false' to pointer type for ar gument 2 of 'jint* _JNIEnv::GetIntArrayElements(jintArray, jboolean*)' [-Wconver sion-null]
jni/DetectFace_JNI.cpp: In function '_jstring* Java_org_yan_processlib_LibProces sImg_processStaticImg(JNIEnv*, jobject, jintArray, int, int)':
jni/DetectFace_JNI.cpp:133:44: warning: converting 'false' to pointer type for a rgument 2 of 'jint* _JNIEnv::GetIntArrayElements(jintArray, jboolean*)' [-Wconve rsion-null]
[armeabi-v7a] Compile++ thumb: ProcessImg <= copyToAssets.cpp
[armeabi-v7a] Compile++ thumb: ProcessImg <= detectFace.cpp
[armeabi-v7a] SharedLibrary : libProcessImg.so
D:/ProgramFile/android-ndk-r9d/toolchains/arm-linux-androideabi-4.6/prebuilt/windows/bin/../lib/gcc/arm-linux-androideabi/4.6/../../../../arm-linux-androideabi/bin/ld.exe: warning: hidden symbol '__aeabi_atexit' in D:/ProgramFile/android-ndk-r9d/sources/cxx-stl/gnu-libstdc++/4.6/libs/armeabi-v7a/libgnustl_static.a(atexit_arm.o) is referenced by DSO D:/ProgramFile\OpenCV-2.4.9-android-sdk\sdk\native\jni\/../libs/armeabi-v7a/libopencv_java.so
[armeabi-v7a] Install : libProcessImg.so => libs/armeabi-v7a/libProcessImg.so
</span>
可以看到上面說找不到non-system libraries in linker flags: -lopencv_java這個東西,關於這個問題我曾作如下嘗試:
LOCAL_LDLIBS += -lopencv_java 或 LOCAL_SHARED_LIBRARIES += libopencv_java均沒有解決這個warning。原本運行正常的程序報錯如下:
java.lang.UnsatisfiedLinkError: Cannot load library: soinfo_link_image(linker.cpp:1635): could not load library "libopencv_java.so" needed by "libProcessImg.so";
caused by load_library(linker.cpp:745): library "libopencv_java.so" not found
說是自己編譯的這個庫libProcessImg.so依賴於libopencv_java.so,沒有找到它所以程序掛了。再看生成的libProcessImg.so大小爲437KB,比第一種方式少了好幾倍啊。腫麼讓程序正常運行呢?將安裝目錄D:\ProgramFile\OpenCV-2.4.9-android-sdk\sdk\native\libs\armeabi-v7a下的libopencv_java.so拷貝到libs\armeabi-v7a文件夾下,然後再調用庫的時候方法變更爲:
- <span style="font-size: 18px;">package org.yan.processlib;
- public class LibProcessImg {
- static{
- System.loadLibrary("opencv_java");
- System.loadLibrary("ProcessImg");
- }
- public static native void initProcessLib(String str);
- public static native String processIplImg(int[] buf, int w, int h);
- public static native String processStaticImg(int[] buf, int w, int h);
- }
- </span>
<span style="font-size: 18px;">package org.yan.processlib;
public class LibProcessImg {
static{
System.loadLibrary("opencv_java");
System.loadLibrary("ProcessImg");
}
public static native void initProcessLib(String str);
public static native String processIplImg(int[] buf, int w, int h);
public static native String processStaticImg(int[] buf, int w, int h);
}
</span>
先調用這個依賴的庫,然後調用我們自己的,注意這個libopencv_java.so有9M多。如此程序又可以正常運行了。最終apk的大小爲4.83M,是第一種的2倍,但還沒大的離譜,可以接受。
對比上面兩種方法不難發現,雖然在mk裏都有include $(BUILD_SHARED_LIBRARY) 也即生成的庫都是動態庫,但這個庫指的是我們自己寫的,我們的庫要進一步調用OpenCV的庫,否則的話直接就能用OpenCV庫裏的函數,咋可能有這事呢。至於怎麼調OpenCV的庫,可以靜態,也可以動態。這也就是爲什麼第二種方法生成的so的大小隻有437KB,而第一種方法生成的庫有4M的大小。事實上在我們第一種方式ndk-build的時候會發現有大量的各種.a .a被加載進去的情形,只不過這隻出現一次,原因就在這。打開安裝目錄下的libs:
可以看到除了帶Camera的so外,其他大量都是.a,而且這些.a是按包名劃分的。而so只有libopencv_java.so和libopencv_info.so,在功能上這些.a靜態調用時等同於動態加載這兩個so。之所以這麼多.a就是供我們採用第一種方法時使用的。關於靜態和動態的優缺點參見這裏
第三種方法:java和jni混用
搞完第二種,既然動態加載我表面上沒用libopencv_java.so,還要把它加載進來,那我乾脆爲啥不用用java的api呢?既然要用java的api那肯定要jar包弄進來,於是導進來OpenCV Library - 2.4.9工程如下圖所示:
這樣就可以用java的api了。如果你有強迫症,覺得OpenCV Library - 2.4.9這個工程一直開着心裏不爽,那也可以將sdk bin目錄生成的opencv library - 2.4.9.jar拷貝到自己工程的libs文件夾下,記得將剛纔添加的Libraries remove掉。右鍵opencv library - 2.4.9.jar----------build path--------------add to build path,這樣照樣使用Java的api。其實這塊很明顯,只要jar包弄進去了你就可以正常使用api了,即編譯時不報錯,但apk到手機上能不能正常運行則是另一碼子事。
此時的mk文件跟第二種類似,記得把libopencv_java.so拷貝到相應目錄。相較於第二種,並沒有增加什麼,僅僅是開發時將jar包導入就可以正常編譯了,能否正常運行還依賴於libopencv_java.so。需注意的是,每clean一次,這個libopencv_java.so就會不見一次,還要手動拷或者自己寫個腳本拷。最終apk的大小爲4.94M,相比第二種多點,原因是那個jar包的原因,以及我們代碼裏又加了幾句:
- <span style="font-size: 18px;">package org.yan.processlib;
- import org.opencv.android.OpenCVLoader;
- import android.util.Log;
- public class LibProcessImg {
- static{
- if(!OpenCVLoader.initDebug()){
- Log.i("yanzi", "OpenCVLoader.initDebug() 失敗");
- }else{
- System.loadLibrary("opencv_java");
- System.loadLibrary("ProcessImg");
- }
- }
- public static native void initProcessLib(String str);
- public static native String processIplImg(int[] buf, int w, int h);
- public static native String processStaticImg(int[] buf, int w, int h);
- }
- </span>
<span style="font-size: 18px;">package org.yan.processlib;
import org.opencv.android.OpenCVLoader;
import android.util.Log;
public class LibProcessImg {
static{
if(!OpenCVLoader.initDebug()){
Log.i("yanzi", "OpenCVLoader.initDebug() 失敗");
}else{
System.loadLibrary("opencv_java");
System.loadLibrary("ProcessImg");
}
}
public static native void initProcessLib(String str);
public static native String processIplImg(int[] buf, int w, int h);
public static native String processStaticImg(int[] buf, int w, int h);
}
</span>
當然你可以加其他的很多OpenCV的java接口,比如Bitmap轉mat,直接傳Mat指針到jni等等,隨便自己怎麼玩。官網上的JNI使用OpenCV其實就是這種java和jni混用的情況,其實大多情況下麼有啥必要,看個人了。至於動態加載OpenCV的庫還是靜態,也全看個人,我是傾向於第一種,以apk的體積最清爽爲準。我們用initDebug一下,其實這塊你不寫也行的。另外就是這個加載庫用static方法跟放Activity裏的onResume裏差不多,我是習慣了放單獨的一個靜態方法裏。記住千萬不要用OpenCVLoader.initAsync()方法啊,本文的主線就是不用OpenCVManager!!!
最後我們打開一個OpenCV_2.4.9_Manager_2.18_armv7a-neon.apk來看一下:
哈哈,看到了吧,裏面的精髓就是lib下的so以及那個引擎so。在使用OpenCVManager的情況下,這些庫隨着OpenCVManager.apk的裝入都事先安裝到手機了,不論是使用java也好,還是用jni再使用動態鏈接OpenCV庫的方法(使自己的so體積最小),都不用往libs文件夾額外加so了,因爲so隨着OpenCVManager已安好了。這就是之所以加個OpenCVManager的半個初衷啊,另半個初衷是binder service 框架上的原因!!!
最後補充3點:
1.有時Cygwin會有記憶效應,比如你修改了mk裏從static變成share,但是它還是按照static來編譯的。解決方法是退了重新進,或重啓電腦吧,汗。
2.除了ndk-build命令外,還應該記住ndk-build -B 強制全編 和 ndk-build clean 清理 這兩個命令。
3.有些教程用到jni時還要把工程轉成C++工程,再配置ndk-build.cmd命令,其實這個在前文也曾說過。個人覺得真心麼必要啊。
------------------------本文系原創,轉載請註明作者:yanzi1225627
歡迎大家加入OpenCV4Android聯盟羣:66320324 備註:yanzi