最近做的项目有涉及到需要在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的运行效率的提升还是很明显的。特别是那些需要访问图像的每个像素的场景。







更多相关文章

  1. GitHub 标星 2.5K+!教你通过玩游戏的方式学习 VIM!
  2. Eclipse中Android公共库的正确建立及调用方法
  3. 自定义对象的监听方式
  4. Android接口回调,最简单的理解方式
  5. Android(安卓)Studio代码自动检测错误提示
  6. IOS APP发布的几个要点
  7. android 学习之图像处理系统(一)
  8. Android(安卓)文件上传的几种方式
  9. 今日头条Android屏幕适配方式

随机推荐

  1. 【Android】WebView设置背景色
  2. android volley ,多文件 表单上传
  3. Android Studio v0.1尝鲜
  4. android 如何依赖android:sharedUserId更
  5. Android入门篇
  6. java websocket client ssl(wss)
  7. Shake Android ui elements
  8. JS判断手机操作系统(ios或android)并跳转到
  9. Android ORM框架GreenDao用法
  10. Android(安卓)databinding 双向绑定(Demo)