OpenCV android sdk配置OpenCV android NDK开发实例

【尊重原创,转载请注明出处】http://blog.csdn.net/guyuealian/article/details/78374708

在Android应用中调用OpenCV进行图像处理的方法有很多种,考虑到性能问题,本人推荐使用NDK进行开发,毕竟C/C++要比Java性能好的多。博客会给出详细的例子和配置方法,并给出本人Github的Demo代码,亲们只需git clone,并在本地Android Studio Build一下,即可以在本地Android手机上直接运行了。

Demo Github地址:https://github.com/PanJinquan/OpenCV-Android-Demo(NDK和Java都支持OpenCV开发)

老铁要是觉得不错,记得给个“Star”哈

PS :Opencv3.x以后,已经把很多功能模块放在contrib中,要想移植opencv contrib到Android需要自己编译,这个过程还是相当麻烦的。如果你想支持opencv contrib开发,可以下载本人已经编译且移植好的Android Demo:

OpenCV-Contrib-Android-Demo: https://github.com/PanJinquan/OpenCV-Contrib-Android-Demo(NDK和Java都支持OpenCV contrib开发)

整个工程代码都给亲们啦,是不是很照顾大家呢?题外话:之前本人在Android Studio和Eclipse配置OpenCV NDK开发时,踩了不知多少坑,网上教程一大堆,偏偏放在本人机器上就是报各种错误,各种无法解析……。好不容易配置好了,后面又来了个大坑:在Java中Bitmap图像与JNI的OpenCV的Mat图像的数据转换时,出现了各种各样问题,如通道顺序问题,颜色失真问题,数据转换问题等等等等等等……。好在,经过一番折腾,终于把问题解决了。
     言归正传,配置前,先保证版本一样:

  • (1)Android Studio 2.3.3 以上
  • (2)android-ndk-r10d 以上,下载地址:https://developer.android.google.cn/ndk/downloads/index.html
  • (3)OpenCV-3.1.0-android-sdk 以上,下载地址:https://opencv.org/releases.html

PS:与时俱进,目前Github的OpenCV版本已经升级到:opencv-3.4.2-android-sdk,NDK:android-ndk-r14b

目录

OpenCV android sdk配置OpenCV android NDK开发实例

一、OpenCV android sdk配置

1.第一步:

2.第二步:

3.第三步:

4.第四步:CMakeLists.txt配置

二、OpenCV android的图像数据转换

第一种:通过JNI的整型数组传递图像数据:

第二种:通过自定义图像对象传递图像数据:

第三种:使用OpenCV的Java包实现NDK开发

三、三种方法相比


一、OpenCV android sdk配置

1.第一步:

新建工程一定要勾选“Include C++ support”,这样新建的Android工程会直接支持NDK开发,避免各种配置问题,如果提示没有NDK,请下载NDK,并在工程“Project Structure”中导入即可:

2.第二步:

  新建工程勾选了“Include C++ support”,就已经支持NDK开发了(即native-lib),我们需要做的是,根据自己项目需要,增加JNI接口。

3.第三步:

  将下载的“OpenCV-android-sdk”放在工程的根目录下:

4.第四步:CMakeLists.txt配置

类似于Visual Studio的工程配置,我们需要告诉NDK去哪里查找OpenCV的头文件路径和依赖文件,Android Studio现在已经支持Cmake配置了,修改app/CMakeLists.txt如下:

#设置OpenCV的路径
set(OpenCV_DIR ${CMAKE_SOURCE_DIR}/../OpenCV-android-sdk/sdk/native/jni)
find_package(OpenCV REQUIRED)
#包含OpenCV的头文件
include_directories( ${CMAKE_SOURCE_DIR}/../OpenCV-android-sdk/sdk/native/jni/include)

此外,还需要在target_link_libraries添加${OpenCV_LIBS}

target_link_libraries( # Specifies the target library.imagePro-lib# Links the target library to the log library# included in the NDK.${log-lib}${OpenCV_LIBS})

完整的 CMakeLists.txt文件:

# For more information about using CMake with Android Studio, read the
# documentation: https://d.android.com/studio/projects/add-native-code.html# Sets the minimum version of CMake required to build the native library.cmake_minimum_required(VERSION 3.4.1)set(OpenCV_DIR ${CMAKE_SOURCE_DIR}/../OpenCV-android-sdk/sdk/native/jni)
find_package(OpenCV REQUIRED)
include_directories( ${CMAKE_SOURCE_DIR}/../OpenCV-android-sdk/sdk/native/jni/include)# Creates and names a library, sets it as either STATIC
# or SHARED, and provides the relative paths to its source code.
# You can define multiple libraries, and CMake builds them for you.
# Gradle automatically packages shared libraries with your APK.
#声明库名称、类型、源码文件
#add_library( # Sets the name of the library.
#             native-lib
#
#            # Sets the library as a shared library.
#            SHARED
#
#            # Provides a relative path to your source file(s).
#           src/main/cpp/native-lib.cpp)add_library( # Sets the name of the library.imagePro-lib# Sets the library as a shared library.SHARED# Provides a relative path to your source file(s).src/main/cpp/imagePro.cppsrc/main/cpp/AndroidDebug.cpp)# Searches for a specified prebuilt library and stores the path as a
# variable. Because CMake includes system libraries in the search path by
# default, you only need to specify the name of the public NDK library
# you want to add. CMake verifies that the library exists before
# completing its build.find_library( # Sets the name of the path variable.log-lib# Specifies the name of the NDK library that# you want CMake to locate.log )# Specifies libraries CMake should link to your target library. You
# can link multiple libraries, such as libraries you define in this
# build script, prebuilt third-party libraries, or system libraries.
#将NDK库链接到native库中,这样native库才能调用NDK库中的函数
#target_link_libraries( # Specifies the target library.
#                       native-lib
#                       imagePro-lib
#
#                       # Links the target library to the log library
#                       # included in the NDK.
#                       ${log-lib} )target_link_libraries( # Specifies the target library.imagePro-lib# Links the target library to the log library# included in the NDK.${log-lib}${OpenCV_LIBS})#include_directories() - 指定关联的头文件目录

clean一下,并重新编译,这样NDK就支持OpenCV开发了。

PS:到这里,只是表示NDK C++层可以支持OpenCV开发了,要是想在Java层中,直接使用OpenCV的Java包,还需要导入“OpenCV-android-sdk”的Java,方法是可以参考:http://blog.csdn.net/u010097644/article/details/56849758

 二、OpenCV android的图像数据转换

   Android开发中,图像类型一般是Bitmap,而在OpenCV是Mat,两者数据的转换需要进行特殊的处理。本OpenCVDemo实现了三种方法,可以现实Android的Bitmap图像与OpenCV的Mat类型的互转。

第一种:通过JNI的整型数组传递图像数据:

(1)定义JNI接口

    public native int[] ImageBlur(int[] pixels,int w,int h);

说明:

pixels:整型数组,存储图像的像素值;

w:图像的宽度;

h:图像的高度;

返回整型数组,处理后的图像像素值;

Android的Bitmap图像需要利用getPixels方法,将Bitmap转为整型数组pixels。再调用ImageBlur接口进行OpenCV的图像处理,处理完后,返回的图像数据需要用setPixels转为Bitmap图像进行显示。问题来了:Bitmap.Config有多个属性,用哪个呢?

public static final Bitmap.Config  ARGB_4444 //4个4位组成即16位
public static final Bitmap.Config  ARGB_8888//4个8位组成即32位
public static final Bitmap.Config  RGB_565//R为5位,G为6位,B为5位共16位

一开始,脑残,直接选了RGB_565,心思细腻测试部门的妹子,发现一个巨坑Bug:原图与OpenCV接收的图像总是有细微的差异,即出现了失真问题。后来,调试发现,就是RGB_565导致转换到CV_8UC3出现数据丢失的情况。不难得出,应该用ARGB_8888类型,毕竟OpenCV中,常用的类型是8位无符号类型:CV_8UC4、CV_8UC3等。

   /*** 调用JNI的ImageBlur(int[] pixels,int w,int h)接口实现图像模糊*/public Bitmap doImageBlur(Bitmap origImage) {int w = origImage.getWidth();int h = origImage.getHeight();int[] pixels = new int[w * h];origImage.getPixels(pixels, 0, w, 0, 0, w, h);int[] image=ImageBlur(pixels,w,h);//JNILog.i(TAG, "ImageBlur called successfully");//最后将返回的int数组转为bitmap类型。Bitmap desImage=Bitmap.createBitmap(w,h,Bitmap.Config.ARGB_8888);//faceall为返回的int数组desImage.setPixels(image,0,w,0,0,w,h);return desImage;}

(2)JNI数据封装:
    需要注意的是,上层Java传递进来的图像数据是四通道的,即ARGB,而在OpenCV图像处理中,我们往往希望处理的图像数据是BGR三通道的。特别需要注意的是,OpenCV处理图像数据的通道顺序是B、G、R,而不是R、G、B,不然很容易出现图像变色的问题。

extern "C"
JNIEXPORT jintArray  JNICALL Java_com_panjq_opencv_alg_ImagePro_ImageBlur(JNIEnv *env, jobject obj, jintArray buf, jint w , jint h){LOGD("ImageBlur: called JNI start...");//读取int数组并转为Mat类型jint *cbuf = env->GetIntArrayElements(buf,JNI_FALSE);if (NULL == cbuf){return 0;}Mat imgData(h,w,CV_8UC4,(unsigned char*) cbuf);cv::cvtColor(imgData,imgData,CV_BGRA2BGR);//这里进行图像相关操作blur(imgData,imgData,Size(20,20));//对图像相关操作完毕cv::cvtColor(imgData,imgData,CV_BGR2BGRA);//这里传回int数组。uchar *ptr = imgData.data;//int size = imgData.rows * imgData.cols;int size = w * h;jintArray result = env->NewIntArray(size);
//    env->SetIntArrayRegion(result, 0, size, cbuf);env->SetIntArrayRegion(result, 0, size, (const jint *) ptr);env->ReleaseIntArrayElements(buf, cbuf, 0);LOGD("ImageBlur: called JNI end...");return result;
}

第二种:通过自定义图像对象传递图像数据:

JNI接口参数除了可以传递基本数据类型,如:int、 float 、char等基本类型,实质上还可以是引用类型,如:类、实例、数组。第一种方法中,传递的数组就是引用类型(不管是对象数组还是基本类型数组,都作为reference types存在)。详见:http://blog.csdn.net/qinjuning/article/details/7599796
      既然可以传递对象,为什么不直接传递一个图像对象,每次都要传递图像的宽度w和高度h,这不是很麻烦么。OK,we do...!
(1)定义JNI接口; 

    public native ImageData ImageProJNI(ImageData image_data);

说明: ImageData是本人定义好的图像数据类,并提供了构造方法,getBitmap和getImageData方法方便数据之间的转换:

package com.panjq.opencv.alg;import android.graphics.Bitmap;/*** Created by panjq1 on 2017/10/23.*/public class ImageData {// public Bitmap bitmap;public int[] pixels;public int w;public int h;ImageData(){}ImageData(Bitmap bitmap){this.w = bitmap.getWidth();this.h = bitmap.getHeight();//将bitmap类型转为int数组this.pixels = new int[this.w * this.h];bitmap.getPixels(this.pixels, 0, this.w, 0, 0, this.w, this.h);}public  Bitmap getBitmap( ){//int数组转为bitmap类型。Bitmap desImage=Bitmap.createBitmap(this.w,this.h,Bitmap.Config.ARGB_8888);desImage.setPixels(this.pixels,0,this.w,0,0,this.w,this.h);return desImage;}public  ImageData  getImageData(Bitmap bitmap){this.w = bitmap.getWidth();this.h = bitmap.getHeight();this.pixels = new int[w * h];bitmap.getPixels( this.pixels, 0, w, 0, 0, w, h);return this;}
}

有了这个类和方法,Android的Bitmap图像与OpenCV的Mat数据,就可以很方便地进行数据的转换啦,接口调用方法如下:

  /*** 调用JNI的ImageProJNI(ImageData image_data)接口实现图像模糊*/public Bitmap ImageProcess(Bitmap origImage) {ImageData imageData=new ImageData(origImage);//创建图像数据imageData对象
//        ImageData imageData=new ImageData();
//        imageData.getImageData(origImage);Log.i(TAG, "input image size:"+imageData.w+","+imageData.h);ImageData out_image=ImageProJNI(imageData);//直接传入图像数据imageData对象Log.i(TAG, "return image size:"+out_image.w+","+out_image.h);Bitmap desImage=out_image.getBitmap();Log.i(TAG, "ImageProJNI called successfully");return desImage;}

有了这个类和方法,Android的Bitmap图像与OpenCV的Mat数据,就可以很方便地进行数据的转换啦,接口调用方法如下:
相比第一种方法,第二种方法仅需传入ImageData对象即可,是不是简单明了很多了啦。

(2)JNI数据封装:
   OK,Java层面简单了,但JNI接口封装就麻烦了,毕竟现在传入的参数是对象,类似与Java的反射机制,在JNI中同样可以获取
   Java类的属性和方法。方法如下:

extern "C"
JNIEXPORT jobject JNICALL Java_com_panjq_opencv_alg_ImagePro_ImageProJNI(JNIEnv *env, jobject obj, jobject image_obj){//获取Java中的实例类// jclass jcInfo = env->FindClass("com/panjq/opencv/alg/ImageData");jclass jcInfo = env->GetObjectClass(image_obj);//获得类属性jfieldID jf_w = env->GetFieldID(jcInfo, "w", "I");//ImageData类中属性wint w = env->GetIntField(image_obj, jf_w);jfieldID jf_h = env->GetFieldID(jcInfo, "h", "I");//ImageData类中属性hint h = env->GetIntField(image_obj, jf_h);//ImageData类中属性pixelsjfieldID jf_pixels = env->GetFieldID(jcInfo, "pixels", "[I");//获得对象的pixels数据,并保存在pixels数组中jintArray pixels = (jintArray)env->GetObjectField(image_obj, jf_pixels);jint *ptr_pixels = env->GetIntArrayElements(pixels, 0);//获得pixels数组的首地址Mat imgData(h,w,CV_8UC4,(unsigned char*) ptr_pixels);cv::cvtColor(imgData,imgData,CV_BGRA2BGR);LOGE("ImageProJNI: input image size=[%d,%d]",imgData.cols,imgData.rows);//释放内存空间env->ReleaseIntArrayElements(pixels, ptr_pixels, 0);//imwrite("/storage/emulated/0/OpencvDemo/input_imgData.jpg",imgData);//****************** here to Opencv image relevant processing*****************/**** 进行OpenCV的图像处理....**/blur(imgData,imgData,Size(20,20));//图像模糊resize(imgData,imgData,Size(imgData.cols/4,imgData.rows/4),INTER_LINEAR);//图像缩小4倍/*****///*********************************** end ************************************jobject obj_result = env->AllocObject(jcInfo);cv::cvtColor(imgData,imgData,CV_BGR2BGRA);//imwrite("/storage/emulated/0/OpencvDemo/out_imgData.jpg",imgData);uchar *ptr = imgData.data;int size = imgData.rows* imgData.cols;jintArray resultPixel = env->NewIntArray(size);jint *ptr_resultPixel = env->GetIntArrayElements(resultPixel, 0);//获得数组的首地址env->SetIntArrayRegion(resultPixel, 0, size, (const jint *) ptr);env->SetObjectField(obj_result, jf_pixels, resultPixel);h=imgData.rows;w=imgData.cols;LOGE("ImageProJNI: ouput image size=[%d,%d]",w,h);env->SetIntField(obj_result, jf_w, w);env->SetIntField(obj_result, jf_h, h);env->ReleaseIntArrayElements(resultPixel, ptr_resultPixel, 0);return  obj_result;
}

第三种:使用OpenCV的Java包实现NDK开发

首先需要给项目添加OpenCV的java依赖模块,可参考:http://blog.csdn.net/u010097644/article/details/56849758 配置过程。利用bitmapToMat将BitMap图像转为Java OpenCV的Mat图像,再利用Mat的getNativeObjAddr()方法获得图像的Native对象地址,利用Native的对象地址,从而实现C++和Java层的图像数据传递。
(1)定义JNI接口

public native void jniImagePro3(long matAddrSrcImage, long matAddrDestImage);

Java层实现过程:

public Bitmap ImageProcess3(Bitmap origImage) {Log.i(TAG, "called JNI:jniImagePro3 ");int w=origImage.getWidth();int h=origImage.getHeight();Mat origMat = new Mat();Mat destMat = new Mat();Utils.bitmapToMat(origImage, origMat);//Bitmap转OpenCV的MatjniImagePro3(origMat.getNativeObjAddr(), destMat.getNativeObjAddr());Bitmap bitImage = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888);Utils.matToBitmap(destMat, bitImage);//OpenCV的Mat转Bitmap显示Log.i(TAG, "jniImagePro3 called successfully");return bitImage;}

(2)JNI数据封装:
   利用OpenCV的Native对象地址作为参数传递,其JNI封装过程就十分简单了。

extern "C"
JNIEXPORT void JNICALL Java_com_panjq_opencv_alg_ImagePro_jniImagePro3(JNIEnv *, jobject, jlong matAddrSrcImage, jlong matAddrDestImage){Mat& srcImage  = *(Mat*)matAddrSrcImage;Mat& destImage = *(Mat*)matAddrDestImage;cv::cvtColor(srcImage,srcImage,CV_BGRA2BGR);blur(srcImage,destImage,Size(20,20));cv::cvtColor(destImage,destImage,CV_BGR2BGRA);
}

与第二种方法相比,第三种方法借助OpenCV android SDK的方法,其JNI封装过程就简单很多了。

三、三种方法相比

(1)性能比较:就运行时间,耗时长久而言:第一种>第二种≈第三种,即第一种最耗时,性能最差,第二种和第三种差不多,不过第三种还是稍微快一点,但相差不大,毕竟是人家官方推荐使用的。
   (2)兼容性:第三种方法需要依赖OpenCV的jar包,而第一种和第二种方法只需要OpenCV的jni就可以,不需要上层Java对OpenCV的支持。因此兼容性好。
   (3)扩展性:显然,第二种方法,通过自定义图像对象传递图像数据的方法,可以在不改变接口参数的情况下,非常方便的新增属性变量,或者删除。

运行处理效果:

如果你觉得该帖子帮到你,还望贵人多多支持,鄙人会再接再厉,继续努力的~

OpenCV android sdk配置OpenCV android NDK开发实例相关推荐

  1. Android SDK+Eclipse+ADT+CDT+NDK 开发环境在windows 7下的搭建

    Android SDK+Eclipse+ADT+CDT+NDK 开发环境在windows 7下的搭建 这几天一直在研究 Android SDK C/C++平台的搭建,尽管以前有成功在Windows X ...

  2. Android环境配置(Eclipse全开发环境下载)

    Android环境配置 前期准备 Android环境的Eclipse: 网站链接:https://www.runoob.com/w3cnote/android-tutorial-eclipse-adt ...

  3. 运行android sdk管理器,Android SDK 安装配置

    Android SDK 可以通过 Android Command line tools(叫命令行工具或 SDK 工具)手动安装,也可以让 Android Studio 自动帮你下载安装,这里详细描述手 ...

  4. 下载Android SDK tools完成Android SDK 安装、配置环境变量

    Table of Contents 一.下载Android SDK tools 二.安装 三.配置变量 四.验证环境变量 开发Android程序必需有Android SDK(Software Deve ...

  5. [Android] 环境配置之Android Studio开发NDK

    2019独角兽企业重金招聘Python工程师标准>>> ======================================================== 作者:qiu ...

  6. phonegap mac android,Mac 10.9x下安装配置phonegap3.0开发环境 (涉及android sdk配置) – willian12345...

    最近突然想弄一下phonegap,之前一直是听说,没亲自配置开发过.结果配置过程非常艰难啊.特别是android平台的配置,那叫一个麻烦,网上搜了半天都没找到非常好的资料.文章也都是抄来抄去,最烦的就 ...

  7. Android从零开始配置opencv+tensorflow进行人脸识别+口罩识别(二:opencv展示当前图像并作适当调整)

    前言 上一章已经成功导入了opencv,但并没说如何展示从摄像头获取图像并展示出来,这章将简单的说说怎么展示,以及里面出现的问题作修正 一.使用opencv正常展示图像 首先准备一个空的activit ...

  8. 基于Android O8.1的ffmpeg NDK 开发 - 2 - APP显示ffmpeg所支持协议,编解码,过滤器,格式,配置等信息

    我们今天的目标是仿照雷神的博客(https://blog.csdn.net/leixiaohua1020/article/details/47008825),把这个APP写出来,先上图: APP中有5 ...

  9. adt+选择android+sdk,eclipse+adt+android SDK 开发搭建环境中遇到的问题

    [先copy下别人的东西作为讲解需要,别介意问题是我遇到的呵呵] 1.下载Eclipse3.7,登录http://www.eclipse.org/downloads/,下载Eclipse Classi ...

最新文章

  1. Ceryx —— 基于 OpenResty 的动态反向代理
  2. 关于CSS中的字体尺寸设置 em rem等
  3. Yarn资源分配示例
  4. phpstudy免费安全检测服务_Phpstudy联合各大安全厂商为用户提供免费安全检测服务...
  5. html怎么填充颜色渐变,CSS实现不规则图形,填充渐变色
  6. MACIOS Socket编程
  7. BJOI2018 简要题解
  8. [转]git merge 与 git rebase的区别
  9. 8255工作方式2——双向选通输入输出(A口)
  10. sql distinct去除重复
  11. 通过刷bios的方式在win8.1平板上启动windows phone模拟器
  12. DHCP服务的介绍及配置详解
  13. React实战入门课程:dva开发一个cnode网站(3)
  14. redis 过期删除策略和淘汰策略 -redis设计与实现笔记
  15. spriteKit 笔记三 —— Drog 精灵
  16. js获取android系统版本号,JS获取系统版本和手机型号
  17. ubuntu搭建php运行环境
  18. C++中函数的重载,重写,重定义
  19. 微信如何开通直播?开通方法有2种
  20. iOS10后友盟推送报错,在iOS9设备上获取到device_token但接不到推送消息,iOS10上报错code=3000

热门文章

  1. 设定自动获得DNS服务器地址
  2. cocos2d-html5 简易 下拉表单 控件
  3. iOS开发之窗口和视图
  4. [HDU] 1181 变形课-简单建模后广搜
  5. 多个project[项目]共享session
  6. 【作品发布】QQ2008远程自助 1.5.1.1
  7. python列表的append/entend/insert
  8. 深入理解计算机系统(4.2)---硬件的魅力
  9. VB的一些项目中常用的通用方法-一般用于验证类
  10. HTML DOM教程 21-HTML DOM Event 对象