0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看技术视频
  • 写文章/发帖/加入社区
会员中心
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

运用JNI连结OpenCV开发C++并打包成aar lib的使用教学

新机器视觉 来源:深度学习与计算机视觉 作者:深度学习与计算机 2022-08-15 09:24 次阅读

动机

  • 本身是作影像视觉相关的工作,多数时候都使用 Python 作为开发语言,但OpenCV 本身是C++开发,学 C++ 应该有帮助。
  • 公司Android/iOS Team ,有些功能需要与他们整合,多了解对方领域可以减少沟通成本。
  • 网络上有关 OpenCV 与 Android Studio 整合的教学零散文章,这次整合成功后,把这些碎片化资讯整理起来记录,避免之后有同样需求时又辛苦一次,这次整合采了太多坑,如果不做个记录,三个月后就会忘了。

环境

  • Android Studio Chipmunk | 2021.2.1 Patch 1 (Ubuntu 环境)
  • JNI 有 cmake 和ndk-build两种方法,我是用Cmake (版本3.22.1 )如果你参考的教学有用到 Android.mk 或Application.mk ,那这篇有关 build 的方式会很不一样。
  • OpenCV 4.5.5 / 4.6.0

Android Studio 连结 JNI

开启新工程,使用Base Activity 即可。540546ce-1c2c-11ed-ba43-dac502259ad0.png取名叫MyApp,方便之后模拟使用这个lib 的人。54271fce-1c2c-11ed-ba43-dac502259ad0.png由于我们最终目的是要做一个 aar lib 。所以 myapp 用来当作 application 使用,另外,再新增一个Module 来作为 C++ lib 开发。点选 [Files]>[New]>[New Module…] 新增 MyOpenCv。543b9198-1c2c-11ed-ba43-dac502259ad0.png这时的目录结构如下,除了 app 以外,多了MyOpenCv 这个Module,有CMakeLists.txt, myopencv.cpp, NativeLib三个档案。5450c2e8-1c2c-11ed-ba43-dac502259ad0.png此时,到 app 的 build.gradle 新增刚刚的module 作为依赖并执行gradle sync。5469d558-1c2c-11ed-ba43-dac502259ad0.pngsync 后,可以到 app 的MainActivity.java 测试是不是可以抓到 JNI 的信息5485aa58-1c2c-11ed-ba43-dac502259ad0.png54b3c38e-1c2c-11ed-ba43-dac502259ad0.png这阶段就完成了从Android 呼叫C++ Lib部分。备注1: 这边 CMakeLists.txt 能与Android 连接是通过build.gradle 里的这段:

	externalNativeBuild{ cmake{ path"src/main/cpp/CMakeLists.txt" version"3.18.1" } } 备注2: MyOpenCv Module 和app 可以相连是通过settings.gradle 后两行的include。

	pluginManagement{ repositories{ gradlePluginPortal() google() mavenCentral() } } dependencyResolutionManagement{ repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS) repositories{ google() mavenCentral() } } rootProject.name="MyApp" include':app' include':MyOpenCv' 

Cmake 与OpenCV 依赖

先至 OpenCV 官网(https://opencv.org/releases/)下载Andoird 版本并解压缩到想要的文件夹,这边示例是在/home/user/Documents/54d396c8-1c2c-11ed-ba43-dac502259ad0.png接着在CMakeLists.txt 加入OpenCV 依赖并执行Gradle Sync。其中 jnigraphics-lib 是等会将图片格式由Android Bitmap 转成OpenCV Mat 格式时会用到的。

	cmake_minimum_required(VERSION3.18.1) set(OpenCV_DIR"/home/jason9075/Documents/OpenCV-android-sdk/sdk/native/jni") find_package(OpenCVREQUIRED) project("myopencv") add_library(myopencv SHARED myopencv.cpp) include_directories(${OpenCV_INCLUDE_DIRS}) find_library(log-lib log) #ForAndroidBitverttocv::Mat find_library(jnigraphics-libjnigraphics) target_link_libraries(myopencv ${OpenCV_LIBS} ${jnigraphics-lib}#ForAndroidBitmapCoverttocv::Mat ${log-lib}) 这时你会发现Sync 失败, 问题出在CMakeLists.txt 第五行没有抓到OpenCV 套件。54f2cf5c-1c2c-11ed-ba43-dac502259ad0.png检视警告页面提示为:无法找到abi binary,位置在OpenCVConfig.cmake:4755032a28-1c2c-11ed-ba43-dac502259ad0.png然后…经过我漫长的寻找…不断的在cmake file 里用message() 确认各个变数,终于发现在OpenCVConfig.cmake 这个档案里的第39行中ANDROID_NDK_ABI_NAME 的值都是空的!理论上它应该会是:[arm64-v8a, armeabi-v7a, x86, x86_64] ,各代表在不同环境的CPU架构。552f5634-1c2c-11ed-ba43-dac502259ad0.pngOpenCVConfig.cmake尝试注解掉原本的ANDROID_NDK_ABI_NAME 后,替换成 ANDROID_ABI 就可以成功的sync。5544a246-1c2c-11ed-ba43-dac502259ad0.png等 sync 完成后,就可以在myopencv.cpp 档案里引用OpenCV 而不会出错。556c8de2-1c2c-11ed-ba43-dac502259ad0.png备注:如果你自己的电脑本身有安装OpenCV ,不使用官网下载的OpenCV_DIR ,它可能会自己跑去找系统的版本(/usr/local/lib/cmake/opencv4),而发生错误。我自己有发生set(OpenCV_DIR path) 路径打错,跑去抓/usr/local 底下的版本,然后不断出现:

	C/C++:CMakeFiles/cvmodule.dir/cvmodule.cpp.o(.data+0x0):error:undefinedreferenceto'typeinfoforcv::Exception' 的错误。

OpenCV bash 开发

  1. 开发图片转灰度功能
新增一个将图片转成灰度的功能测试,这边要注意的是在JNI 使用上,必须定义好function 的signature,像名称要和java 的package name 对应。

	extern"C"JNIEXPORTvoidJNICALL Java_com_jason9075_myopencv_NativeLib_toGrey( JNIEnv*env, jobject, jobjectbitmapIn, jobjectbitmapOut){ Matsrc,greyOut; bitmapToMat(env,bitmapIn,src,false); cvtColor(src,greyOut,CV_BGR2GRAY); matToBitmap(env,greyOut,bitmapOut,false); } 然后在NativeLib.java 里要宣告与C++对应的function。

	publicclassNativeLib{ static{ System.loadLibrary("myopencv"); } publicnativeStringstringFromJNI(); publicnativevoidtoGrey(BitmapbitmapIn,BitmapbitmapOut); } 为了测试我们到应用程式app文件夹,把Android绿色机器人图片放到drawable 文件夹,然后在MainActivity 转成灰色。557d1db0-1c2c-11ed-ba43-dac502259ad0.png

	@Override protectedvoidonCreate(BundlesavedInstanceState){ super.onCreate(savedInstanceState); binding=ActivityMainBinding.inflate(getLayoutInflater()); setContentView(binding.getRoot()); setSupportActionBar(binding.toolbar); NavControllernavController=Navigation.findNavController(this,R.id.nav_host_fragment_content_main); appBarConfiguration=newAppBarConfiguration.Builder(navController.getGraph()).build(); NavigationUI.setupActionBarWithNavController(this,navController,appBarConfiguration); ImageViewiv=findViewById(R.id.imageView); binding.fab.setOnClickListener(newView.OnClickListener(){ @Override publicvoidonClick(Viewview){ Snackbar.make(view,"Replacewithyourownaction",Snackbar.LENGTH_LONG) .setAction("Action",null).show(); } }); //TestHelloWorldFromMyOpenCv NativeLibcv=newNativeLib(); System.out.println(cv.stringFromJNI()); Bitmapimage=BitmapFactory.decodeResource(getResources(),R.drawable.android); cv.toGrey(image,image); iv.setImageBitmap(image); } 558f71a4-1c2c-11ed-ba43-dac502259ad0.png
  1. 开发读取图片宽高功能
比如说今天Android Team 有个需求是传一张图片,我们分析图片宽高等资讯,而且希望他们可以直接拿到 Java Class。我们先在Module 里新增一个DTO 叫ImageInfo。

	packagecom.jason9075.myopencv; publicclassImageInfo{ privatefinalintwidth; privatefinalintheight; publicImageInfo(intwidth,intheight){ this.width=width; this.height=height; } publicintgetWidth(){ returnwidth; } publicintgetHeight(){ returnheight; } @Override publicStringtoString(){ return"ImageInfo{"+ "width="+width+ ",height="+height+ '}'; } } 在 NativeLib.java 宣告相对应的 function getInfo()

	packagecom.jason9075.myopencv; importandroid.graphics.Bitmap; publicclassNativeLib{ static{ System.loadLibrary("myopencv"); } publicnativeStringstringFromJNI(); publicnativevoidtoGrey(BitmapbitmapIn,BitmapbitmapOut); publicnativeImageInfogetInfo(Bitmapbitmap); } 然后在myopencv.cpp 新增C++ 实做方式。须注意的一点是,因为我们最终要回传 Java Object,所以在C++这边要定义clsPath ,要找你预期回传的 Java Class 长的怎么样,还有这个Class 的Constructor 需要什么样的signature (这边 width 和 height 都是 Int 所以是(II)V)。

	extern"C" JNIEXPORTjobjectJNICALL Java_com_jason9075_myopencv_NativeLib_getInfo(JNIEnv*env,jobjectthiz,jobjectbitmap){ Matsrc; bitmapToMat(env,bitmap,src,false); intwidth=src.cols; intheight=src.rows; //returnjavaobject constchar*clsPath="com/jason9075/myopencv/ImageInfo"; jclasscls=env->FindClass(clsPath); jmethodIDconstructor=env->GetMethodID(cls,"","(II)V"); returnenv->NewObject(cls,constructor,width,height); } 完成后,再回到 app 里实际测试。

	//TestHelloWorldFromMyOpenCv NativeLibcv=newNativeLib(); System.out.println(cv.stringFromJNI()); Bitmapimage=BitmapFactory.decodeResource(getResources(),R.drawable.android); cv.toGrey(image,image); iv.setImageBitmap(image); System.out.println(">>>"+cv.getInfo(image)); 我们可以成功读取到宽高分别为2688 和3197。(这边和原图宽高不同的原因是Android 的Drawable 会自动缩放,若想测试原图可以改放Asset文件夹)559f4566-1c2c-11ed-ba43-dac502259ad0.png

打包成 aar 给其他工程使用

上面的工程主要有两个部分,一个是 app 拥有Activity 来模拟使用这个lib的情况,令一个部分是 MyOpenCv 这个module 为实际lib 的内容,这是我们接下来要介绍的,如何将这个module 转成aar。首先,我们先点击 [Build]>[Select Build Varient] 开启选单,再生成 aar 时可以选择 debug 或是 release 来发布,这边我选用release 做示范。55c3758a-1c2c-11ed-ba43-dac502259ad0.png设成release 之后,再去[Build]>[Rebuild Project]让工程建构一下,完成后会在build 文件夹的outputs 找到我们要的aar。55cf6372-1c2c-11ed-ba43-dac502259ad0.png打开MyOpenCv-release.aar 可以看到在jni 里面存放各个不同架构的.so,代表正常。55f10590-1c2c-11ed-ba43-dac502259ad0.png我们开启令一个Android Project 叫AnotherApp ,并把MyOpenCv-release.aar 放入app/libs 里。560910f4-1c2c-11ed-ba43-dac502259ad0.png然后在AnotherApp 的build.gradle 加入依赖。5624df78-1c2c-11ed-ba43-dac502259ad0.png然后回到AnotherApp 的MainActivity我们加入这四行,测试自己撰写的aar lib 能不能成功使用。5634d91e-1c2c-11ed-ba43-dac502259ad0.png执行结果是程序有抓到图片在画面上的宽高。565f587e-1c2c-11ed-ba43-dac502259ad0.png以上就完成了运用JNI 连结OpenCV 开发C++,并打包成aar lib 的使用教学。

代码:

MyApp: https://github.com/jason9075/Android_with_OpenCV_Module温馨提醒:使用时module 的CMakeLists.txt 请将OpenCV_DIR换成自己的sdk路径。AnotherApp: https://github.com/jason9075/Android_use_OpenCV_AAR_lib

Ref

https://developer.android.com/studio/projects/configure-cmakehttps://github.com/ValYouW/AndroidOpenCVDemohttps://stackoverflow.com/questions/9433257/how-to-specify-array-of-class-in-getmethodid-method-signature-parameterhttps://stackoverflow.com/questions/22300848/return-object-from-java-native-methodhttps://stackoverflow.com/questions/51107185/how-to-create-new-android-aar-in-android-studio

审核编辑:汤梓红


声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
  • C++
    C++
    +关注

    关注

    21

    文章

    2098

    浏览量

    73453
  • OpenCV
    +关注

    关注

    29

    文章

    624

    浏览量

    41212
  • JNI
    JNI
    +关注

    关注

    0

    文章

    8

    浏览量

    7845

原文标题:​如何将 OpenCV 整合 Android JNI 开发 C++ 代码并打包成 aar lib 给其他工程使用

文章出处:【微信号:vision263com,微信公众号:新机器视觉】欢迎添加关注!文章转载请注明出处。

收藏 人收藏

    评论

    相关推荐

    【实用开发工具】将BAT脚本打包成exe可执行文件

    【实用开发工具】将BAT脚本打包成exe可执行文件
    的头像 发表于 08-21 19:51 2.6w次阅读
    【实用<b class='flag-5'>开发</b>工具】将BAT脚本<b class='flag-5'>打包成</b>exe可执行文件

    lib封装库如何反汇编成C语言

    小弟想请教论坛上的大神们:用C语言编写的程序打包成lib封装库如何反汇编成C语言??
    发表于 06-12 20:52

    请问C++如何运用到具体的工程中?

    学了很多C语言,现在想学习一下C++,但是现在遇到一个问题,就是不知道C++怎么运用到具体的工程中。学习C语言可以在单片机和Linux上
    发表于 03-24 04:35

    在鸿蒙的Module中使用了JNI无法调试代码中的C++是为什么

    各位大佬,请教个问题。目前在做JNI开发中,在鸿蒙的Module中使用了JNI,如果编译使用正常,但是调试时无法在C++中调试代码中的C++
    发表于 04-24 11:11

    OpenCV C++程序编译与演示

    1、在JetsonNano上编译OpenCV源码与OpenCV C++ YOLOv5程序演示  编译OpenCV最新4.5.x版本  Jetson Nano自带的
    发表于 11-10 16:42

    RK3399 Android 7.1系统JNI层使用C/C++输出Log

    Platform: RK3399OS: Android 7.1Kernel: v4.4.83JNI层使用C/C++需要输出Log.如果不要TAG,那么只要如下使用就可以:#include &
    发表于 11-14 18:00

    一文简析JNI层使用C/C++需要输出Log

    Platform: RK3399OS: Android 7.1Kernel: v4.4.83JNI层使用C/C++需要输出Log.如果不要TAG,那么只要如下使用就可以:#include &
    发表于 11-22 17:59

    能请教一下官方的pyOCD是怎么打包成独立exe的吗?

    能请教一下官方的 pyOCD 是怎么打包成独立 exe 的?最近我也想打包一个 pyOCD,但是用 pyinstaller 打包后,运行提示OSError: cannot load library
    发表于 12-05 11:33

    C++课程设计教学大钢

    C++课程设计教学大钢 课程名称:C++课程设计英文名称:Course design of C++ Programming面向专业:对计算机要求较高的理工科专业课程总学时:___ 4
    发表于 06-25 23:28 9次下载

    程序设计及C++语言课程设计教学大钢

    程序设计及C++语言课程设计教学大钢:程序设计及C++语言》课程设计教学大钢(课程设计类) 课程设计 名称:C++语言课程设计英文名称:C
    发表于 06-25 23:29 15次下载

    OpenCV2.3的C++速查表免费下载

    本文档的主要内容详细介绍的是OpenCV2.3的C++速查表免费下载。
    发表于 11-06 17:48 4次下载

    RSA算法的JNI封装步骤

    要求较高的算法往往是基于C/C++语言(与硬件关联性更强)实现的。如果应用程序需要基于JAVA编程实现时,这就会有一些矛盾。此时,通过JNI技术,Java开发者可以在不了解算法内容的情
    的头像 发表于 06-04 17:45 2164次阅读
    RSA算法的<b class='flag-5'>JNI</b>封装步骤

    如何使用Borland C++ Builder6.0来开发OpenCV的程序

    本文档的主要内容详细介绍的是如何使用Borland C++ Builder6.0来开发OpenCV的程序。
    发表于 05-26 17:32 14次下载
    如何使用Borland <b class='flag-5'>C++</b> Builder6.0来<b class='flag-5'>开发</b><b class='flag-5'>OpenCV</b>的程序

    【Python】如何将Python脚本打包成exe可执行文件

    【Python实用技巧】如何将Python脚本打包成exe可执行文件?
    的头像 发表于 08-18 12:40 1.8w次阅读
    【Python】如何将Python脚本<b class='flag-5'>打包成</b>exe可执行文件

    基于QT5+OpenCV+OpenVINO C++的应用打包过程

    我用QT C++写了一个YOLOv5模型推理演示应用。
    的头像 发表于 01-26 10:17 1079次阅读
    基于QT5+<b class='flag-5'>OpenCV</b>+OpenVINO <b class='flag-5'>C++</b>的应用<b class='flag-5'>打包</b>过程