若是想实现图片的高斯模糊,图片比较,人脸识别等算法,OpenCV 多是现成库里比较好的选择。
使用 OpenCV 的优缺点:html
下边咱们来看看在 Android 的 JNI 中如何使用 OpenCV ?java
OpenCV for Android SDKandroid
创建在Project展开以下:c++
Android.mkgit
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 ..\..\..\..\native\jni\OpenCV.mk else include $(OPENCV_MK_PATH) endif LOCAL_MODULE := OpenCV LOCAL_SRC_FILES := LOCAL_LDLIBS += -lm -llog include $(BUILD_SHARED_LIBRARY)
这里是以静态连接导入 OpenCV 库,而后编译成 共享的 so 库,因此会致使最终的 so 库体积比较大。 github
注意:include ..\..\..\..\native\jni\OpenCV.mk
的路径必定要对!!!web
Application.mk算法
APP_STL := gnustl_static APP_CPPFLAGS := -frtti -fexceptions APP_ABI := armeabi armeabi-v7a APP_PLATFORM := android-8
这里配置了 OpenCV 须要用到的 STL 库,以及编译的硬件平台等。shell
build.gradleapache
sourceSets.main.jni.srcDirs = [] //禁止自带的ndk功能 sourceSets.main.jniLibs.srcDirs = ['src/main/libs','src/main/jniLibs'] //重定向so目录为src/main/libs,原来为src/main/jniLibs task ndkBuild(type: Exec, description: 'Compile JNI source with NDK') { Properties properties = new Properties() properties.load(project.rootProject.file('local.properties').newDataInputStream()) def ndkDir = properties.getProperty('ndk.dir') if (org.apache.tools.ant.taskdefs.condition.Os.isFamily(org.apache.tools.ant.taskdefs.condition.Os.FAMILY_WINDOWS)) { commandLine "$ndkDir/ndk-build.cmd", '-C', file('src/main/jni').absolutePath } else { commandLine "$ndkDir/ndk-build", '-C', file('src/main/jni').absolutePath } } tasks.withType(JavaCompile) { compileTask -> compileTask.dependsOn ndkBuild }
local.properties
ndk.dir=D\:\\program\\Android\\sdk\\ndk-bundle
这里配置 NDK 的路径
声明 java 层的 native 方法
public class OpenCVHelper { static { System.loadLibrary("OpenCV"); } public static native double compareImages(String old_image, String new_image,int type); }
使用 javah 命令生成头文件
javah -jni com.aliyun.utils.OpenCVHelper
其中,javah的环境须要配置到电脑的 path
中去;
生成了 com_aliyun_utils_OpenCVHelper.h
的头文件。
cpp 文件名为 com_aliyun_utils_OpenCVHelper.cpp
,
修改 LOCAL_SRC_FILES := com_aliyun_utils_OpenCVHelper.cpp
上边咱们在 Android.mk 中,使得 OPENCV_LIB_TYPE :=STATIC
,以静态库的方式导入 OpenCV ,因此生成的 so 库比较大,达到好几M。
另外一种方式是使用动态库的方式引入 OpenCV ,即把OPENCV_LIB_TYPE :=SHARED
,动态加载所须要的库,在 ndk-build 时,会报 -lopencv_java3
的 warning :
这时咱们把 native-jni-编译平台下的 libopencv_java3.so
导入便可。
OpenCV for Android 的 demo 完成了,但配置到服务器上,就是各类坑啊,一个 bug 接着一个 bug,所幸最终都依依解决了,如今把那些 bug 场景重现!
要完成 JNI 中对 OpenCV 库的引用,关键就是须要把 OpenCV 库导入进来,不论是静态方式仍是动态方式。
如下有俩种选择导入:
俩种方式并没有本质差异,但用已有的 OpenCV.mk 引入时,会出现更多的问题,因此就单独做一类方法,来总结遇到的坑。
代码:
include ..\..\native\jni\OpenCV.mk
报错:
packages/apps/DVRRecorder/jni/OpenCV/Android.mk:9: ..\..\native\jni\OpenCV.mk: No such file or directory
代码修改为这样:
$(LOCAL_PATH)\..\..\native\jni\OpenCV.mk
仍是报一样的错误。。。
认真思考一番,有办法了!!!
解决:
能够用 ls 命令在服务器上看能不能进入这个目录~,发现进不了,再细细思考一下,应该把斜杠给改反一下。
好比这样:
include $(LOCAL_PATH)/../../native/jni/OpenCV.mk
恩,没有报这个错误了,但紧接着又报了第二个错误。。。
报错:
packages/apps/DVRRecorder/jni/OpenCV/../../native/jni/OpenCV.mk:40: packages/apps/DVRRecorder/jni/OpenCV/../../native/jni/OpenCV-.mk: No such file or directory
OpenCV-.mk 是个什么鬼,但明显已经解决了 OpenCV.mk 的路径问题,怎么又出来 OpenCV-.mk 呢?
深刻:
这时深刻 OpenCV.mk,发现有这样的一段代码:
OPENCV_SUB_MK:=$(call my-dir)/OpenCV-$(TARGET_ARCH_ABI).mk
这里找到了 - 号,颇有多是 TARGET_ARCH_ABI
的问题,因而在 导入的 native 目录下,全局搜索,发现
jni/OpenCV.mk:14:OPENCV_TARGET_ARCH_ABI:=$(TARGET_ARCH_ABI) jni/OpenCV.mk:17:OPENCV_LIBS_DIR:=$(OPENCV_THIS_DIR)/../libs/$(OPENCV_TARGET_ARC H_ABI) jni/OpenCV.mk:18:OPENCV_3RDPARTY_LIBS_DIR:=$(OPENCV_THIS_DIR)/../3rdparty/libs/$ (OPENCV_TARGET_ARCH_ABI) jni/OpenCV.mk:22:OPENCV_SUB_MK:=$(call my-dir)/OpenCV-$(TARGET_ARCH_ABI).mk jni/OpenCV.mk:64:ifeq ($(OPENCV_MK_$(OPENCV_TARGET_ARCH_ABI)_ALREADY_INCLUDED),) jni/OpenCV.mk:76: OPENCV_MK_$(OPENCV_TARGET_ARCH_ABI)_ALREADY_INCLUDED:=on
搜索后发现只有使用,但没有定义!!!
问题就很明显了,没有导入编译的硬件平台,颇有可能也是路径问题。
解决:
这里咱们就不用去找 TARGET_ARCH_ABI 的路径了,直接根据所编译的平台,合理取值。
在 Android developer Guide上有说明:https://developer.android.com/ndk/guides/android_mk.html
用 mm -B
编译时,能够发现所编译的平台,根据使用的 CPU 架构来选取一个合理值,好比:
TARGET_ARCH_ABI := armeabi-v7a
恩,觉得没问题了,但下一个错误立刻来~~~
报错:
No rule to make target `out/target/product/aeon6735_65c_s_l1/obj_arm/STATIC_LIBRARIES/opencv_shape_intermediates/export_includes', needed by `out/target/product/aeon6735_65c_s_l1/obj_arm/SHARED_LIBRARIES/OpenCV_intermediates/import_includes'. Stop.
这就懵逼了~~~
追踪:
但有关键字,STATIC_LIBRARIES
,SHARED_LIBRARIES
,且也确定是 OpenCV.mk 出问题了,在 Android.mk 中咱们声明了这俩个条件:
OpenCV_INSTALL_MODULES := on OpenCV_CAMERA_MODULES := off OPENCV_LIB_TYPE :=STATIC
因而追踪到 OpenCV.mk 中发现:
ifeq ($(OPENCV_INSTALL_MODULES),on) LOCAL_$(OPENCV_LIB_TYPE)_LIBRARIES += $(foreach mod, $(OPENCV_LIBS), opencv_$(mod)) else LOCAL_LDLIBS += -L$(call host-path,$(LOCAL_PATH)/$(OPENCV_LIBS_DIR)) $(foreach lib, $(OPENCV_LIBS), -lopencv_$(lib)) endif ifeq ($(OPENCV_LIB_TYPE),STATIC) LOCAL_STATIC_LIBRARIES += $(OPENCV_3RDPARTY_COMPONENTS) endif
猜想是这俩个条件判断后进去的语句出问题了,
on
时,以模块方式加载 LOCAL_STATIC_LIBRARIES
,当为 off
时,以 LOCAL_LDLIBS
方式加载lib下的库。OPENCV_LIB_TYPE==STATIC
时,第三方的lib加载到 LOCAL_STATIC_LIBRARIES
中。解决:
既然出错了,那把这俩句话都注释掉试试~
恩,注释确定没用,但引入了下一个问题,既然咱们把引入库的语句给删了,那接下来,确定是须要咱们把库的连接一个一个的引入咯。
报错:
packages/apps/DVRRecorder/jni/OpenCV/../../native/jni/include/opencv2/core/base.hpp:53:21: fatal error: algorithm: No such file or directory #include <algorithm>
STL 模板库的头文件没找到,因此须要引入 algorithm
的头文件。
解决:
找到头文件所在的位置,并以以下方式引入在 Android.mk 中:
LOCAL_C_INCLUDES := external/stlport/stlport/ LOCAL_C_INCLUDES += bionic
解决了这个问题,发现又引入了更多的 bug ,但都是库的连接问题,接下来的库引入和第二种方式同样,只是第二种,就直接去掉了 OpenCV.mk,直接在 Android.mk 中一步一步的引入连接库。
报错:
packages/apps/DVRRecorder/jni/OpenCV/com_aliyun_utils_OpenCVHelper.cpp:30: error: undefined reference to 'cv::imread(cv::String const&, int)' packages/apps/DVRRecorder/jni/OpenCV/../../native/jni/include/opencv2/core/cvstd.hpp:667: error: undefined reference to 'cv::String::deallocate()' packages/apps/DVRRecorder/jni/OpenCV/com_aliyun_utils_OpenCVHelper.cpp:31: error: undefined reference to 'cv::imread(cv::String const&, int)' packages/apps/DVRRecorder/jni/OpenCV/../../native/jni/include/opencv2/core/cvstd.hpp:667: error: undefined reference to 'cv::String::deallocate()' packages/apps/DVRRecorder/jni/OpenCV/com_aliyun_utils_OpenCVHelper.cpp:34: error: undefined reference to 'cv::GaussianBlur(cv::_InputArray const&, cv::_Outp
找不到如 imread
,GaussianBlur
的连接,并且这些连接应该是 OpenCV 库中的。
解决:
这里咱们以静态库的方式引入 OpenCV 中的静态连接库,把 lib/armeabi-v7a 和 3rdparty/libs/armeabi-v7a 目录下的库全引入。
shell pwd:显示当前工做目录
这里咱们以全路径的方式加入对应的库:
PATHD=$(shell pwd) LOCAL_LDFLAGS +=-L$(PATHD)/$(LOCAL_PATH)/lib/ -l$(PATHD)/$(LOCAL_PATH)/lib/libopencv_core.a \ -l$(PATHD)/$(LOCAL_PATH)/lib/libopencv_imgproc.a\ -l$(PATHD)/$(LOCAL_PATH)/lib/libopencv_highgui.a\ -l$(PATHD)/$(LOCAL_PATH)/lib/libopencv_imgcodecs.a \ -l$(PATHD)/$(LOCAL_PATH)/lib/libIlmImf.a \ -l$(PATHD)/$(LOCAL_PATH)/lib/liblibjpeg.a \ -l$(PATHD)/$(LOCAL_PATH)/lib/liblibwebp.a \ -l$(PATHD)/$(LOCAL_PATH)/lib/liblibtiff.a \ -l$(PATHD)/$(LOCAL_PATH)/lib/liblibpng.a \ -l$(PATHD)/$(LOCAL_PATH)/lib/liblibjasper.a \ -l$(PATHD)/$(LOCAL_PATH)/lib/libtbb.a \ -l$(PATHD)/$(LOCAL_PATH)/lib/libopencv_java3.so \ -lstdc++
报错
已经解决了 OpenCV 库的连接错误,但又报了以下错误:
error: undefined reference to 'vtable for std::basic_istringstream<char, std::char_traits<char>, std::allocator<char> >'
此处省略一堆一样的错误~~~
找度娘发现是 stl 标准库里边的,因而乎,咱们还要引入关于 libgnustl libsupc++ libstlport。
解决:
咱们以静态的连接库引入 stl 等一系列库,以下:
prebuilt_stdcxx_PATH := prebuilts/ndk/current/sources/cxx-stl/gnu-libstdc++ LOCAL_LDFLAGS += -L$(prebuilt_stdcxx_PATH)/libs/armeabi-v7a -lgnustl_static -lsupc++
!!!可是,错误仍是没变,但经过 $(warning $(LOCAL_LDFLAGS))
打印,确实能够找到对应的连接,这又是什么缘由???
找了好久,决定在对应路径下再看看有没有其余的相同连接库,诶,发现有同名的共享 so 库,换成动态连接库试试~~
LOCAL_LDFLAGS += -L$(prebuilt_stdcxx_PATH)/libs/armeabi-v7a -lgnustl_shared -lsupc++
居然发现报该错误的连接消失了!!神奇。
猜想缘由:
多是咱们在 Android.mk 中要编译的是动态 so 库,因此在既有静态库,也有动态库的状况下,须要引入和要编译的库相同类型的。
报错:
libopencv_core.a(ocl.cpp.o):ocl.cpp:function initOpenCLAndLoad: error: undefined reference to 'dlopen'
此处省略一堆一样的错误~~~
解决:
LOCAL_LDLIBS += -lm -llog -ldl -lz
上边的的错误是没有引入 -ldl 的缘由。
此次使用 OpenCV 的经历,bug 是一浪接着一浪。有些命令颇有用,总结以下:
LOCAL_LDFLAGS:这个编译变量传递给连接器一个一些额外的参数,好比想传递给外面的库和库路径给ld,那么就要加到这个上面,如:
LOCAL_LDFLAGS += -L$(prebuilt_stdcxx_PATH1)/libs/armeabi-v7a -lstlport_shared
或者直接加上绝对路径库的全名:
LOCAL_LDFLAGS +=-L$(PATHD)/$(LOCAL_PATH)/lib/ -l$(PATHD)/$(LOCAL_PATH)/lib/libopencv_core.a \ -l$(PATHD)/$(LOCAL_PATH)/lib/libopencv_imgproc.a\ -l$(PATHD)/$(LOCAL_PATH)/lib/libopencv_highgui.a\ -l$(PATHD)/$(LOCAL_PATH)/lib/libopencv_imgcodecs.a \
且须要注意:若是是非系统的第三方库,只能用LOCAL_LDFLAGS方式,LOCAL_LDLIBS方式不行。
本文发表于我的博客:http://lavnfan.github.io/,欢迎指教。