热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

OpenCVforAndorid的总结

Android中写的JNI如何调用OpenCV?OpenCV如何配置到Linux服务器上?OpenCVforAndroid如果想实现图片的高斯模糊,图片比较,人脸识别等算法,Ope
  • Android 中写的 JNI 如何调用 OpenCV ?
  • OpenCV 如何配置到 Linux 服务器上?

OpenCV for Android

如果想实现图片的高斯模糊,图片比较,人脸识别等算法,OpenCV 可能是现成库里比较好的选择。
使用 OpenCV 的优缺点:

  • 现成库 C++ 调用,封装很好,实现较为简单,上层 JNI 调用性能较好
  • 失去 Java 的跨平台特性

下边我们来看看在 Android 的 JNI 中如何使用 OpenCV ?

1、下载 OpenCV for Android 的 SDK

OpenCV for Android SDK

解压后有:

  • apk:manger 的 apk,这种方法不需要导入 OpenCV 的 sdk ,但需要在安装自己的apk外,还需安装 这里边的 manager 的 apk,体验不好,不建议使用。
  • samples:样例代码
  • sdk:需要用到的 java 层 或 jni 层的代码

具体的 IDE 配置方法

  • Eclipse:http://docs.opencv.org/2.4/doc/tutorials/introduction/android_binary_package/dev_with_OCV_on_Android.html
  • Android Studio:以下重点来讲

2、Android studio 配置

2.1、拷贝 native

  • 将 OpenCV 中 sdk 目录下的 native 全拷到项目根目录下
  • 新建 JNI Folder,并在目录下创建俩个文件:Android.mk 和 Application.mk

建立在Project展开如下:

《OpenCV for Andorid 的总结》

2.2、 文件配置

  • 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 ..\..\..\..\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 库体积比较大。

    注意:include ..\..\..\..\native\jni\OpenCV.mk 的路径一定要对!!!

  • Application.mk

    APP_STL := gnustl_static
    APP_CPPFLAGS := -frtti -fexceptions
    APP_ABI := armeabi armeabi-v7a
    APP_PLATFORM := android-8

    这里配置了 OpenCV 需要用到的 STL 库,以及编译的硬件平台等。

  • build.gradle

    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 的路径

2.3、JNI 调用

  • 声明 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 文件,实现对应的方法

    cpp 文件名为 com_aliyun_utils_OpenCVHelper.cpp

  • 在 Android.mk 文件中加入源文件

    修改 LOCAL_SRC_FILES := com_aliyun_utils_OpenCVHelper.cpp

2.4、ndk-build

  • 使用 Android studio 的 ndk-build 工具构建so库,点击右侧的 gradle ,展开在 other 下边找到 ndk-build。
  • 然后在 java 层中就可以使用 JNI 层的功能了。

3、静态还是共享链接 OpenCV

上边我们在 Android.mk 中,使得 OPENCV_LIB_TYPE :=STATIC,以静态库的方式导入 OpenCV ,所以生成的 so 库比较大,达到好几M。

另一种方式是使用动态库的方式引入 OpenCV ,即把OPENCV_LIB_TYPE :=SHARED,动态加载所需要的库,在 ndk-build 时,会报 -lopencv_java3 的 warning :

《OpenCV for Andorid 的总结》

这时我们把 native-jni-编译平台下的 libopencv_java3.so 导入即可。

OpenCV for Linux

OpenCV for Android 的 demo 完成了,但配置到服务器上,就是各种坑啊,一个 bug 接着一个 bug,所幸最终都依依解决了,现在把那些 bug 场景重现!

要完成 JNI 中对 OpenCV 库的引用,关键就是需要把 OpenCV 库导入进来,不管是静态方式还是动态方式。
以下有俩种选择导入:

  • 方式一:利用已写好的 OpenCV.mk 引入
  • 方式二:在自己写的 Android.mk 中引入

俩种方式并无本质差别,但用已有的 OpenCV.mk 引入时,会出现更多的问题,所以就单独作一类方法,来总结遇到的坑。

方法一、利用原来的 OpenCV.mk 引入

1、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

恩,没有报这个错误了,但紧接着又报了第二个错误。。。

2、TARGET_ARCH_ABI 的问题

报错

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

《OpenCV for Andorid 的总结》

mm -B 编译时,可以发现所编译的平台,根据使用的 CPU 架构来选取一个合理值,比如:

TARGET_ARCH_ABI := armeabi-v7a

恩,以为没问题了,但下一个错误马上来~~~

3、编译,连接的问题

报错:

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_LIBRARIESSHARED_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 中。

解决:

既然出错了,那把这俩句话都注释掉试试~

恩,注释肯定没用,但引入了下一个问题,既然我们把引入库的语句给删了,那接下来,肯定是需要我们把库的链接一个一个的引入咯。

4、STL 的头文件问题

报错:

packages/apps/DVRRecorder/jni/OpenCV/../../native/jni/include/opencv2/core/base.hpp:53:21: fatal error: algorithm: No such file or directory
#include

STL 模板库的头文件没找到,所以需要引入 algorithm 的头文件。

解决:

找到头文件所在的位置,并以如下方式引入在 Android.mk 中:

LOCAL_C_INCLUDES := external/stlport/stlport/
LOCAL_C_INCLUDES += bionic

解决了这个问题,发现又引入了更多的 bug ,但都是库的链接问题,接下来的库引入和第二种方式一样,只是第二种,就直接去掉了 OpenCV.mk,直接在 Android.mk 中一步一步的引入链接库。

方法二、另启炉灶,在 Android.mk 中一个库一个库的引入

1、静态库的引入

报错:

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

找不到如 imreadGaussianBlur 的链接,而且这些链接应该是 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++

2、STL 库的引入

报错

已经解决了 OpenCV 库的链接错误,但又报了如下错误:

error: undefined reference to 'vtable for std::basic_istringstream, std::allocator >'

此处省略一堆同样的错误~~~
找度娘发现是 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++

  • 先定义一个路径的变量
  • 然后再在 LOCAL_LDFLAGS 中引入该链接库

!!!但是,错误还是没变,但通过 $(warning $(LOCAL_LDFLAGS)) 打印,确实可以找到对应的链接,这又是什么原因???

找了很久,决定在对应路径下再看看有没有其他的相同链接库,诶,发现有同名的共享 so 库,换成动态链接库试试~~

LOCAL_LDFLAGS += -L$(prebuilt_stdcxx_PATH)/libs/armeabi-v7a -lgnustl_shared -lsupc++

竟然发现报该错误的链接消失了!!神奇。

猜测原因:

可能是我们在 Android.mk 中要编译的是动态 so 库,所以在既有静态库,也有动态库的情况下,需要引入和要编译的库相同类型的。

3、LOCAL_LDIBS 的引入

报错:

libopencv_core.a(ocl.cpp.o):ocl.cpp:function initOpenCLAndLoad: error: undefined reference to 'dlopen'

此处省略一堆同样的错误~~~

解决:

LOCAL_LDLIBS += -lm -llog -ldl -lz

上边的的错误是没有引入 -ldl 的原因。

总结

这次使用 OpenCV 的经历,bug 是一浪接着一浪。有些命令很有用,总结如下:

  • 强大的搜索:find * -exec grep -iHn ‘搜索名’ {} ;
  • $(warning $(PATHD)):打印 makefile 中的 PATHD 变量
  • PATHD=$(shell pwd):获取当前工作目录,而不用手动书写全路径。
  • LOCAL_C_INCLUDES:额外的 C/C++ 编译头文件路径
  • LOCAL_LDLIBS:即 ldlibs,就是指定那些存在于系统目录下本模块需要连接的库。如果某一个库既有动态库又有静态库,那么在默认情况下是链接的动态库而非静态库
  • 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/,欢迎指教。


推荐阅读
  • 如何实现织梦DedeCms全站伪静态
    本文介绍了如何通过修改织梦DedeCms源代码来实现全站伪静态,以提高管理和SEO效果。全站伪静态可以避免重复URL的问题,同时通过使用mod_rewrite伪静态模块和.htaccess正则表达式,可以更好地适应搜索引擎的需求。文章还提到了一些相关的技术和工具,如Ubuntu、qt编程、tomcat端口、爬虫、php request根目录等。 ... [详细]
  • 本文介绍了在开发Android新闻App时,搭建本地服务器的步骤。通过使用XAMPP软件,可以一键式搭建起开发环境,包括Apache、MySQL、PHP、PERL。在本地服务器上新建数据库和表,并设置相应的属性。最后,给出了创建new表的SQL语句。这个教程适合初学者参考。 ... [详细]
  • Android中高级面试必知必会,积累总结
    本文介绍了Android中高级面试的必知必会内容,并总结了相关经验。文章指出,如今的Android市场对开发人员的要求更高,需要更专业的人才。同时,文章还给出了针对Android岗位的职责和要求,并提供了简历突出的建议。 ... [详细]
  • Mac OS 升级到11.2.2 Eclipse打不开了,报错Failed to create the Java Virtual Machine
    本文介绍了在Mac OS升级到11.2.2版本后,使用Eclipse打开时出现报错Failed to create the Java Virtual Machine的问题,并提供了解决方法。 ... [详细]
  • HTML学习02 图像标签的使用和属性
    本文介绍了HTML中图像标签的使用和属性,包括定义图像、定义图像地图、使用源属性和替换文本属性。同时提供了相关实例和注意事项,帮助读者更好地理解和应用图像标签。 ... [详细]
  • CentOS 7部署KVM虚拟化环境之一架构介绍
    本文介绍了CentOS 7部署KVM虚拟化环境的架构,详细解释了虚拟化技术的概念和原理,包括全虚拟化和半虚拟化。同时介绍了虚拟机的概念和虚拟化软件的作用。 ... [详细]
  • Allegro总结:1.防焊层(SolderMask):又称绿油层,PCB非布线层,用于制成丝网印板,将不需要焊接的地方涂上防焊剂.在防焊层上预留的焊盘大小要比实际的焊盘大一些,其差值一般 ... [详细]
  • android ... [详细]
  • 在一对一直播源码使用过程中,有时会出现软键盘切换闪屏问题,就是当切换表情的时候屏幕会跳动,因此要对一对一直播源码表情面板无缝切换进行优化。 ... [详细]
  • Python瓦片图下载、合并、绘图、标记的代码示例
    本文提供了Python瓦片图下载、合并、绘图、标记的代码示例,包括下载代码、多线程下载、图像处理等功能。通过参考geoserver,使用PIL、cv2、numpy、gdal、osr等库实现了瓦片图的下载、合并、绘图和标记功能。代码示例详细介绍了各个功能的实现方法,供读者参考使用。 ... [详细]
  • 前端性能优化无损压缩webp格式的图片
    一、什么是webpWebP格式,谷歌开发的一种旨在加快图片加载速度的图片格式。图片压缩体积大约只有JPEG的23,并能节省大量的服务器宽带资源和数据空 ... [详细]
  • 荣耀V8搭载基于Android 6.0的EMUI 4.1,功能介绍及用户体验
    本文介绍了荣耀V8搭载基于Android 6.0的EMUI 4.1的功能,包括色温调节、护眼模式、智灵键和学生模式等。荣耀V8在色温调节方面提供了多种选择,用户可以根据自己的喜好进行调节。护眼模式能够减少屏幕蓝光辐射,预防眼部疲劳。智灵键位于机身侧面,用户可以自定义其功能,方便快捷操作。学生模式需要密码才能开启或关闭,为家长提供了更好的控制。通过本文,读者可以了解荣耀V8的功能特点及用户体验。 ... [详细]
  • 今天就跟大家聊聊有关怎么在Android应用中实现一个换肤功能,可能很多人都不太了解,为了让大家更加了解,小编给大家总结了以下内容,希望大家根 ... [详细]
  • 这个问题困扰了我两天,卸载Dr.COM客户端(我们学校上网要装这个客户端登陆服务器,以后只能在网页里输入用户名和密码了),问题解决了。问题的现象:在实验室机台式机上安装openfire和sp ... [详细]
  • 近来有一个需求,是需要在androidjava基础库中插入一些log信息,完成这个工作需要的前置条件有编译好的android源码具体android源码如何编译,这 ... [详细]
author-avatar
个信2502907653
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有