Android中使用OpenCV详细入门教程
最近做的项目有涉及到需要在Android平台上使用OpenCV,上网查了很多资料,也走了不少弯路,特此做些记录,供各位读者参考。
一、OpenCV到底是什么?
OpenCV是一个开源的图像处理引擎,使用C++编写,对外提供了C,C++,Java,Python的调用接口,最初是由Intel 公司开发,现在由Willow Garage公司维护。
二、我们能利用OpenCV做什么?
往小了说,比如你需要把普通的彩色图像转换成灰度图像,把视频数据格式YUV转换成RGB格式,对图像做缩放、裁剪等等事情,OpenCV都提供了便捷的的使用方式。
往大了说,你需要把转换图像颜色风格,把图像中的人脸替换为其他人的人脸,对图像进行边缘增强等等,OpenCV也提供了比较友好的接口供你使用(当然,不是说什么需求都只需要调用一个接口就能解决,世界上没有这么便宜的事)。
总之,能利用OpenCV做什么最后还是取决于读者的功力。
三、如何在Android上进行OpenCV开发?
Android的开发者可以通过两种方式使用OpenCV:
—— 使用OpenCV提供的Java api
—— 使用jni方式
首先请下载OpenCV的的android sdk,此处为链接,一般来说请下载最新的(截止到笔者写这篇文章为止,OpenCV sdk最新版本为3.4)
下载之后解压缩,通常会发现有三个子目录,如图
apk目录下,是名字为OpenCV_xxx_Manager_xxx.apk的安装包,其存在的作用是提供一种跨进程使用OpenCV api的方式,这种方式的好处是不需要在你的项目中包含OpenCV的动态库,可以减少包体size,坏处是用户的机器上还需要安装这个OpenCV Manager这种app。说实话,这种使用方式极其不科学,你能指望用户安装了你开发的app之后,还要去自己搜索一个OpenCV Manager应用并且安装吗?所以说,这种方式用户体验非常不好,不知道OpenCV的作者是怎么想的,所以请各位读者忽略这种使用OpenCV的方式。
samples目录下是使用OpenCV的简单例子,其基本上都是利用OpenCV Manager进行使用的,所以各位读者想运行这些例子体验一番的话,得先在机器中安装OpenCV Manager,就是apk目录下的安装包。
既然这种使用方式用户体验很差,那肯定有更合理的使用方式,请看下文
1、使用Java api
sdk目录下有三个子目录,如下图所示
其中java目录下是一个eclipse的android工程(都什么年代了,还用eclipse,OpenCV的的工程师没有紧跟潮流啊),我们可以在自己的Android studio工程下新建一个Android Library module,例如名字叫OpenCVLibrary,然后把这个java目录下的源码拷贝到OpenCVLibrary相应的目录下(不要告诉我你不知道怎么拷贝源码),然后在OpenCVLibrary新建一个目录叫jniLibs,在jniLibs下新建目录armeabi-v7a(一般来说手机只需要这个平台就好了,有需要的自己可以加x86之类的),然后把如图所示目录下的libopencv_java3.so拷贝到刚才创建的jniLibs/armeabi-v7a下面(其它平台如x86之类的有需要的话自己对应着拷贝进去)
然后在OpenCVLibrary的build.gradle里面配置一下,把jni目录指向刚才的目录,如图所示
这样操作之后,只要在主工程里面引用OpenCVLibrary,就可以使用OpenCV的api了。
使用方式如下:
static { OpenCVLoader.initDebug(); //加载OpenCV动态库 } private void doJob() { Mat rgbMat = new Mat(); Mat grayMat = new Mat(); Bitmap srcBitmap = BitmapFactory.decodeResource(getResources(), R.mipmap.test); Bitmap grayBitmap = Bitmap.createBitmap(srcBitmap.getWidth(), srcBitmap.getHeight(), Bitmap.Config.RGB_565); Utils.bitmapToMat(srcBitmap, rgbMat);//把Bitmap转为Mat. Imgproc.cvtColor(rgbMat, grayMat, Imgproc.COLOR_RGB2GRAY);//把rgb格式的mat转为gray的mat Utils.matToBitmap(grayMat, grayBitmap); //把gray的mat转为Bitmap mTestImageView.setImageBitmap(grayBitmap); }
Mat是OpenCV的重要概念,以后有需要的话另开文章进行介绍。
2、使用jni方式
下面介绍以android studio结合cmake的方式进行开发。cmake貌似需要Android Studio2.2及以上,以及NDKr12b及以上,请读者自行下载安装。
首先在OpenCVLibary下的build.gradle文件添加开启cmake编译的脚本
android {...externalNativeBuild { cmake { path "CMakeLists.txt" } } sourceSets { main { jniLibs.srcDirs = ['src/main/cpp']}}...}
然后工程目录下新建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)#支持-std=gnu++11set(CMAKE_VERBOSE_MAKEFILE on)set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=gnu++11")set(CMAKE_CXX_VISIBILITY_PRESET hidden)#src/main/cpp/OpencvUtil.cpp为在工程中自己编写的代码,其编写要求与传统的Android.mk方式没什么不同add_library( opencvutil SHARED src/main/cpp/OpencvUtil.cpp)add_library(lib_opencv SHARED IMPORTED)#填写你本地的OpenCV目录的的对应子目录include_directories(D:\\OpenCV-android-sdk\\sdk\\native\\jni\\include)#引入libopencv_java3.so文件,填写你本地的OpenCV目录的对应子目录set_target_properties(lib_opencv PROPERTIES IMPORTED_LOCATION D:\\OpenCV-android-sdk\\sdk\\native\\libs\\armeabi-v7a\\libopencv_java3.so)find_library( log-lib log )target_link_libraries(opencvutil jnigraphics ${log-lib} lib_opencv)
然后,是 OpencvUtil.h和OpencvUtil.cpp的源码:
#ifndef OPENCVDEMO_OPENCVUTIL_H#define OPENCVDEMO_OPENCVUTIL_Hextern "C" {JNIEXPORT void JNICALLJava_com_jieend_csdndemo_OpencvUtil_rgb2gray(JNIEnv *env, jclass type, jobject bitmap, jlong grayAddr);};#endif //OPENCVDEMO_OPENCVUTIL_H
#include #include #include #include #include "OpencvUtil.h"JNIEXPORT void JNICALLJava_com_jieend_csdndemo_OpencvUtil_rgb2gray(JNIEnv *env, jclass type, jobject bitmap, jlong grayAddr) {// TODO AndroidBitmapInfo bmpInfo; int ret = 0; ret = AndroidBitmap_getInfo(env, bitmap, &bmpInfo); if (ret < 0) { return; } int* pixels = NULL; ret = AndroidBitmap_lockPixels(env, bitmap, (void**)&pixels); if (ret < 0 || pixels == NULL) { return; } cv::Mat rgb(cv::Size(bmpInfo.width, bmpInfo.height), CV_8UC4, pixels); cv::Mat* gray = (cv::Mat*)grayAddr; cv::cvtColor(rgb, *gray, CV_BGRA2GRAY); AndroidBitmap_unlockPixels(env, bitmap);}
接下来,是Java层的jni接口: import android.graphics.Bitmap;public class OpencvUtil { static { System.loadLibrary("opencvutil"); } public static native void rgb2gray(Bitmap bitmap, long grayAddr);}
以及使用代码:
private void doNativeJog() { Mat grayMat = new Mat(); Bitmap srcBitmap = BitmapFactory.decodeResource(getResources(), R.mipmap.test); OpencvUtil.rgb2gray(srcBitmap, grayMat.nativeObj); Bitmap grayBitmap = Bitmap.createBitmap(srcBitmap.getWidth(), srcBitmap.getHeight(), Bitmap.Config.RGB_565); Utils.matToBitmap(grayMat, grayBitmap); //convert mat to bitmap mTestImageView.setImageBitmap(grayBitmap); }
当然OpenCV除了提供动态库,也提供了静态库,各位有兴趣的读者可以尝试改造成调用静态库。 3、总结:这两种方式各有优劣,使用Java api对于不熟悉C++的人来说可以很快上手,能够尽快入门OpenCV,但是OpenCV的Java api本质上都是使用C++实现的,这是因为图像处理通常会涉及到很多复杂的运算,Java的运行效率实在是有心无力,所以从深入学习的角度出发,笔者更推荐第二种方式,基本上来说,只要是稍微复杂的图像处理效果,使用jni的方式效比Java api的运行效率的提升还是很明显的。特别是那些需要访问图像的每个像素的场景。
更多相关文章
- GitHub 标星 2.5K+!教你通过玩游戏的方式学习 VIM!
- Eclipse中Android公共库的正确建立及调用方法
- 自定义对象的监听方式
- Android接口回调,最简单的理解方式
- Android(安卓)Studio代码自动检测错误提示
- IOS APP发布的几个要点
- android 学习之图像处理系统(一)
- Android(安卓)文件上传的几种方式
- 今日头条Android屏幕适配方式