其实最早接触OpenCV是很久很久之前的事了,大概在2013年的5,6月份,当时还是个菜逼(虽然现在也是个菜逼),在那一段时间,学了一段时间的android(并不算学,一个月都不到),之后再也没接触android,而是一直在接触java web。那次接触OpenCV是因为一个学长的毕业设计,这次接触OpenCV是因为自己的毕业设计。2013年那年技术太菜,ndk环境都搭不好,当初还是eclipse环境,一直按照网上的教程去搭,下什么cygwin,简直就是个坑,网上的文章转来转去,都是过时的。后来一个机会看到了google官方的一个文档,就像发现了新大陆一样,发现ndk环境根本不需要装cygwin,装了你就坑了,装这个东西有好多G呢,时间浪费不说,简直误人子弟啊。后来在那年7月写下一篇博客

NDK开发环境

这段时间在填自己毕业设计的坑,要用到OpenCV,首先得下载到sdk吧,这个从官网上下载就好了
OpenCV for Android

注意下载的是OpenCV for android。当前版本是3.0

Android 使用OpenCV的三种方式(Android Studio)_第1张图片

解压后,里面的内容如下

Android 使用OpenCV的三种方式(Android Studio)_第2张图片

samples目录下是样例代码,sdk目录下是我们需要用到的java层和jni层的代码。apk目录是manager的apk安装包

其实OpenCV最简单的使用方式是使用manager,也就是使用apk目录下的安装包,安装对应的apk,将java层代码导入,使用OpenCVLoader.initAsync()加载库,之后你就可以直接用java代码调用Opencv相关的功能了。

Android 使用OpenCV的三种方式(Android Studio)_第3张图片

但是这种方式除了安装我们自己的apk还需要安装上面提到的manager的apk,用户体验十分不好,不推荐使用,本文的三种方式将完全脱离这个manager的apk。

本文下面的三种方式的内容参考自文章 OpenCV4Android释疑: 透析Android以JNI调OpenCV的三种方式(让OpenCVManager永不困扰)

本篇文章使用android studio作为开发环境,由于实验性的构建工具对ndk支持还不好,所以使用旧的构建方式,在原来写的一篇博客基础上修改即可android studio下ndk开发

这正式介绍三种方式之前,我们需要做一些前期准备。

首先新建一个项目,将OpenCV中sdk目录下的native目录拷到项目根目录

Android 使用OpenCV的三种方式(Android Studio)_第4张图片

然后新建Jni目录

Android 使用OpenCV的三种方式(Android Studio)_第5张图片

在里面新建两个文件

Android 使用OpenCV的三种方式(Android Studio)_第6张图片

编辑gradle.properties文件,增加下面的属性使用旧版的ndk功能(不添加会使用实验性的ndk构建工具)

android.useDeprecatedNdk=true

在local.properties文件中配置ndk目录

ndk.dir=D\:\\AndroidSDK\\sdk\\ndk-bundle

编辑build.gradle,在android节点中增加下面的代码

sourceSets.main.jni.srcDirs = []    //禁止自带的ndk功能    sourceSets.main.jniLibs.srcDirs = ['src/main/libs','src/main/jniLibs']    //重定向so目录为src/main/libs和src/main/jniLibs,原来为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    }    task ndkClean(type: Exec, description: 'Clean NDK Binaries') {        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",'clean', '-C', file('src/main/jni').absolutePath        } else {            commandLine "$ndkDir/ndk-build",'clean', '-C', file('src/main/jni').absolutePath        }    }    clean.dependsOn 'ndkClean'

在之前新建的Application.mk中增加下面的内容

APP_STL := gnustl_staticAPP_CPPFLAGS := -frtti -fexceptionsAPP_ABI := armeabi armeabi-v7aAPP_PLATFORM := android-8

在Android.mk中增加下面的内容

LOCAL_PATH := $(call my-dir)include $(CLEAR_VARS)OpenCV_INSTALL_MODULES := onOpenCV_CAMERA_MODULES := offOPENCV_LIB_TYPE :=STATICifeq ("$(wildcard $(OPENCV_MK_PATH))","")include ..\..\..\..\native\jni\OpenCV.mkelseinclude $(OPENCV_MK_PATH)endifLOCAL_MODULE := OpenCVLOCAL_SRC_FILES :=LOCAL_LDLIBS +=  -lm -lloginclude $(BUILD_SHARED_LIBRARY)

这时候,使用gradle构建一下,如果能成功构建出so,说明配置没问题,如下图,点击as右侧的gradle展开,双击ndkBuild进行构建

Android 使用OpenCV的三种方式(Android Studio)_第7张图片

Android 使用OpenCV的三种方式(Android Studio)_第8张图片

下面开始讲第一种方法,纯jni层的代码,该方法基于上面的所有步骤,为静态链接库

声明java层的native方法

public class OpenCVHelper {    static {        System.loadLibrary("OpenCV");    }    public static native int[] gray(int[] buf, int w, int h);}

使用javah命令生成头文件,内容如下

/* DO NOT EDIT THIS FILE - it is machine generated */#include /* Header for class cn_edu_zafu_opencv_OpenCVHelper */#ifndef _Included_cn_edu_zafu_opencv_OpenCVHelper#define _Included_cn_edu_zafu_opencv_OpenCVHelper#ifdef __cplusplusextern "C" {#endif/* * Class:     cn_edu_zafu_opencv_OpenCVHelper * Method:    gray * Signature: ([III)[I */JNIEXPORT jintArray JNICALL Java_cn_edu_zafu_opencv_OpenCVHelper_gray        (JNIEnv *, jclass, jintArray, jint, jint);#ifdef __cplusplus}#endif#endif

新建cpp文件,实现对应的方法,就是灰度处理

#include "cn_edu_zafu_opencv_OpenCVHelper.h"#include #include #include using namespace cv;extern "C" {JNIEXPORT jintArray JNICALL Java_cn_edu_zafu_opencv_OpenCVHelper_gray(        JNIEnv *env, jclass obj, jintArray buf, int w, int h);JNIEXPORT jintArray JNICALL Java_cn_edu_zafu_opencv_OpenCVHelper_gray(        JNIEnv *env, jclass obj, jintArray buf, int w, int h) {    jint *cbuf;    cbuf = env->GetIntArrayElements(buf, JNI_FALSE );    if (cbuf == NULL) {        return 0;    }    Mat imgData(h, w, CV_8UC4, (unsigned char *) cbuf);    uchar* ptr = imgData.ptr(0);    for(int i = 0; i < w*h; i ++){        //计算公式:Y(亮度) = 0.299*R + 0.587*G + 0.114*B        //对于一个int四字节,其彩色值存储方式为:BGRA        int grayScale = (int)(ptr[4*i+2]*0.299 + ptr[4*i+1]*0.587 + ptr[4*i+0]*0.114);        ptr[4*i+1] = grayScale;        ptr[4*i+2] = grayScale;        ptr[4*i+0] = grayScale;    }    int size = w * h;    jintArray result = env->NewIntArray(size);    env->SetIntArrayRegion(result, 0, size, cbuf);    env->ReleaseIntArrayElements(buf, cbuf, 0);    return result;}}

之后,需要将cpp文件编译进去,在Andorid.mk文件中加入

LOCAL_SRC_FILES := cn_edu_zafu_opencv_OpenCVHelper.cpp

然后在java层写个测试方法测试一下是否进行灰度化了

Bitmap bitmap = ((BitmapDrawable) getResources().getDrawable(        R.drawable.ic)).getBitmap();int w = bitmap.getWidth(), h = bitmap.getHeight();int[] pix = new int[w * h];bitmap.getPixels(pix, 0, w, 0, 0, w, h);int [] resultPixes=OpenCVHelper.gray(pix,w,h);Bitmap result = Bitmap.createBitmap(w,h, Bitmap.Config.RGB_565);result.setPixels(resultPixes, 0, w, 0, 0,w, h);img.setImageBitmap(result);

运行效果如下,灰度化后的结果

Android 使用OpenCV的三种方式(Android Studio)_第9张图片

上面的这种方法生成的so库的大小见下图,大约有1.4M左右

这里写图片描述

第二种方法也是纯jni的,但是是动态链接库,在第一种基础上,修改Android.mk文件为

LOCAL_PATH := $(call my-dir)include $(CLEAR_VARS)OpenCV_INSTALL_MODULES := onOpenCV_CAMERA_MODULES := offOPENCV_LIB_TYPE := SHAREDifeq ("$(wildcard $(OPENCV_MK_PATH))","")include ..\..\..\..\native\jni\OpenCV.mkelseinclude $(OPENCV_MK_PATH)endifLOCAL_MODULE := OpenCVLOCAL_SRC_FILES := cn_edu_zafu_opencv_OpenCVHelper.cppLOCAL_LDLIBS +=  -lm -lloginclude $(BUILD_SHARED_LIBRARY)

注意上面的OPENCV_LIB_TYPE属性的改动,从STATIC改为了SHARED,这时候再用ndkBuild一下,你会发现会输出一些警告以及一部分红色的内容

Android 使用OpenCV的三种方式(Android Studio)_第10张图片

生成的so库的大小为310k,小了好几倍

这里写图片描述

这时候如果你直接取运行程序,会报错误

Android 使用OpenCV的三种方式(Android Studio)_第11张图片

原因是我们使用的是动态库加载方式,还需要将依赖的so加进去,这个so就是图中的libopencv_java3.so,他在我们的最开始加到项目里的native目录中

Android 使用OpenCV的三种方式(Android Studio)_第12张图片

将它拷到我们的jniLibs目录中去,这里只拷贝armeabi和armeabi-v7a中的,至于其他的按需拷贝

Android 使用OpenCV的三种方式(Android Studio)_第13张图片

这时候运行就不会报错了。

既然我们使用了动态链接库,那么我们同样也可以使用java层的接口,优点是java开发速度相对快一点。第三种方法在第二种方法基础上,使用纯java层代码进行处理。

在此之前,我们需要将sdk目录中的java代码拷到项目中去

Android 使用OpenCV的三种方式(Android Studio)_第14张图片

但是org.opencv.engine包中是一个aidl,我们需要将它剪贴到aidl目录中去,就像这样子

Android 使用OpenCV的三种方式(Android Studio)_第15张图片

最后还有一个资源文件attrs.xml,拷过来

Android 使用OpenCV的三种方式(Android Studio)_第16张图片

build一下项目,不出意外应该会报错,这时候找到该类,引入自己的R文件包就可以了

这里写图片描述

再次build应该就不会有什么问题了。

java层的测试方法

OpenCVLoader.initDebug();Mat rgbMat = new Mat();Mat grayMat = new Mat();Bitmap srcBitmap = BitmapFactory.decodeResource(getResources(), R.drawable.ic);Bitmap grayBitmap = Bitmap.createBitmap(srcBitmap.getWidth(), srcBitmap.getHeight(), Bitmap.Config.RGB_565);Utils.bitmapToMat(srcBitmap, rgbMat);//convert original bitmap to Mat, R G B.Imgproc.cvtColor(rgbMat, grayMat, Imgproc.COLOR_RGB2GRAY);//rgbMat to gray grayMatUtils.matToBitmap(grayMat, grayBitmap); //convert mat to bitmapimg.setImageBitmap(grayBitmap);

注意使用OpenCVLoader.initDebug();进行初始化而不是使用OpenCVLoader.initAsync()

这种方法的特点是处理都在java层,不怎么会涉及jni层的代码,除非java层完成不了的工作会转移到jni层去。

三种方法各有各的优点,根据自己的情况进行选择。

  • 如果c++特别好的,建议使用第一种方法
  • 如果更习惯java代码的,并且java层的函数都能进行处理的,建议选择第三种方法
  • 第二种方法建议在第三种方法不满足条件的情况下进行辅助使用,因为使用了第三种方法的前提是使用第二种方法的动态链接库。

最后附上源码

  • http://download.csdn.net/detail/sbsujjbcy/9275897

更多相关文章

  1. 浅谈android中的图片处理之基本绘图(一)
  2. Android百度地图之位置定位和附近查找代码简单实现 (上)
  3. Android 内置图片
  4. Android Fresco图片处理库用法API英文原文文档4(Facebook开源Andr
  5. Android Studio中配置代码和资源的路径
  6. Android 代码设置EditText过滤汉字
  7. Android 新浪微博代码
  8. Android 长按显示上下文菜单代码

随机推荐

  1. Android实现文件的保存与读取功能示例
  2. android 单击和滚动效果调整
  3. Android(安卓)WebRTC 音视频开发总结(二)
  4. 从J2EE转向Android的第十二天 -----继续
  5. Android(安卓)jogball 驱动分析 (转)
  6. Android中动画效果alpha、translate、sca
  7. android adt与android sdk介绍
  8. 2012年度最受欢迎中国开源软件评选
  9. android SQLite 优化(一)insert优化 1
  10. Android(安卓)studio设置字体格式之Typef