zoukankan      html  css  js  c++  java
  • Android 使用OpenCV的三种方式(Android Studio)

    http://blog.csdn.net/sbsujjbcy/article/details/49520791

    Android 使用OpenCV的三种方式(Android Studio)

    其实最早接触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

    这里写图片描述

    解压后,里面的内容如下

    这里写图片描述

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

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

    这里写图片描述

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

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

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

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

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

    这里写图片描述

    然后新建Jni目录

    这里写图片描述

    在里面新建两个文件

    这里写图片描述

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

    ?
    1
    <code bash=""class="hljs">android.useDeprecatedNdk=true</code>

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

    ?
    1
    <codeclass="hljs"tex="">ndk.dir=D:AndroidSDKsdk dk-bundle</code>

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

    ?
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    compileTask.dependsOn ndkBuild
       }
     
       task ndkClean(type: Exec, description: 'Clean NDK Binaries') {
           Properties properties = newProperties()
           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'data-snippet-id=ext.57cfe881f1b599f1cacdf54006a6556e data-snippet-saved=falsedata-csrftoken=jAbtS2Wo-dGpwcxcGt2vqP0mibIzN7nCKugQ data-codota-status=done><codeclass="hljs"mel="">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 = newProperties()
           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 = newProperties()
           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'</code>

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

    ?
    1
    2
    3
    4
    5
    <codeclass="hljs"makefile="">APP_STL := gnustl_static
    APP_CPPFLAGS := -frtti -fexceptions
    APP_ABI := armeabi armeabi-v7a
    APP_PLATFORM := android-8
    </code>

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

    ?
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    <codeclass="hljs"ruby="">LOCAL_PATH := $(call my-dir)
     
    include $(CLEAR_VARS)
     
     
    OpenCV_INSTALL_MODULES := on
    OpenCV_CAMERA_MODULES := off
     
    OPENCV_LIB_TYPE :=STATIC
     
    ifeq ($(wildcard $(OPENCV_MK_PATH)),)
    include ........
    ativejniOpenCV.mk
    else
    include $(OPENCV_MK_PATH)
    endif
     
    LOCAL_MODULE := OpenCV
     
    LOCAL_SRC_FILES :=
     
    LOCAL_LDLIBS +=  -lm -llog
     
    include $(BUILD_SHARED_LIBRARY)
    </code>

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

    这里写图片描述

    这里写图片描述

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

    声明java层的native方法

    ?
    1
    2
    3
    4
    5
    6
    7
    <codeclass="hljs"cs="">publicclass OpenCVHelper {
        static{
            System.loadLibrary(OpenCV);
        }
        publicstatic native int[] gray(int[] buf, intw, inth);
    }
    </code>

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

    ?
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    /* Header for class cn_edu_zafu_opencv_OpenCVHelper */
     
    #ifndef _Included_cn_edu_zafu_opencv_OpenCVHelper
    #define _Included_cn_edu_zafu_opencv_OpenCVHelper
    #ifdef __cplusplus
    extern 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
     data-snippet-id=ext.ba8837e741e5214b4467247cc79e58a3 data-snippet-saved=falsedata-csrftoken=jJMOrjOS-jHPgZkClm2cErR8IXlybhUpqg4E data-codota-status=done><codeclass="hljs"vala="">/* DO NOT EDIT THIS FILE - it is machine generated */
    #include <jni.h>
    /* Header for class cn_edu_zafu_opencv_OpenCVHelper */
     
    #ifndef _Included_cn_edu_zafu_opencv_OpenCVHelper
    #define _Included_cn_edu_zafu_opencv_OpenCVHelper
    #ifdef __cplusplus
    extern 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
    </jni.h></code>

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

    ?
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    #include <stdlib.h>
    #include <opencv2 opencv.hpp="">
     
    using namespace cv;
     
    extern C {
     
    JNIEXPORT jintArray JNICALL Java_cn_edu_zafu_opencv_OpenCVHelper_gray(
            JNIEnv *env, jclass obj, jintArray buf, intw, inth);
     
     
     
    JNIEXPORT jintArray JNICALL Java_cn_edu_zafu_opencv_OpenCVHelper_gray(
            JNIEnv *env, jclass obj, jintArray buf, intw, inth) {
     
        jint *cbuf;
        cbuf = env->GetIntArrayElements(buf, JNI_FALSE );
        if(cbuf == NULL) {
            return0;
        }
     
        Mat imgData(h, w, CV_8UC4, (unsigned char*) cbuf);
     
        uchar* ptr = imgData.ptr(0);
        for(inti = 0; i < w*h; i ++){
            //计算公式:Y(亮度) = 0.299*R + 0.587*G + 0.114*B
            //对于一个int四字节,其彩色值存储方式为:BGRA
            intgrayScale = (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;
        }
     
        intsize = w * h;
        jintArray result = env->NewIntArray(size);
        env->SetIntArrayRegion(result,0, size, cbuf);
        env->ReleaseIntArrayElements(buf, cbuf, 0);
        returnresult;
    }
    } data-snippet-id=ext.7031b99c9209dfeeef3168f2def15639 data-snippet-saved=falsedata-csrftoken=1DJbOl7m-UVQNbl8xQw8YX34eXWB5DaBgWq4 data-codota-status=done><codeclass="hljs"perl="">#include cn_edu_zafu_opencv_OpenCVHelper.h
    #include <stdio.h>
    #include <stdlib.h>
    #include <opencv2 opencv.hpp="">
     
    using namespace cv;
     
    extern C {
     
    JNIEXPORT jintArray JNICALL Java_cn_edu_zafu_opencv_OpenCVHelper_gray(
            JNIEnv *env, jclass obj, jintArray buf, intw, inth);
     
     
     
    JNIEXPORT jintArray JNICALL Java_cn_edu_zafu_opencv_OpenCVHelper_gray(
            JNIEnv *env, jclass obj, jintArray buf, intw, inth) {
     
        jint *cbuf;
        cbuf = env->GetIntArrayElements(buf, JNI_FALSE );
        if(cbuf == NULL) {
            return0;
        }
     
        Mat imgData(h, w, CV_8UC4, (unsigned char*) cbuf);
     
        uchar* ptr = imgData.ptr(0);
        for(inti = 0; i < w*h; i ++){
            //计算公式:Y(亮度) = 0.299*R + 0.587*G + 0.114*B
            //对于一个int四字节,其彩色值存储方式为:BGRA
            intgrayScale = (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;
        }
     
        intsize = w * h;
        jintArray result = env->NewIntArray(size);
        env->SetIntArrayRegion(result,0, size, cbuf);
        env->ReleaseIntArrayElements(buf, cbuf, 0);
        returnresult;
    }
    }</opencv2></stdlib.h></stdio.h></code></opencv2></stdlib.h>

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

    ?
    1
    <codeclass="hljs"fix="">LOCAL_SRC_FILES := cn_edu_zafu_opencv_OpenCVHelper.cpp</code>

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

    ?
    1
    2
    3
    4
    5
    6
    7
    8
    9
    <code avrasm=""class="hljs">Bitmap bitmap = ((BitmapDrawable) getResources().getDrawable(
            R.drawable.ic)).getBitmap();
    intw = bitmap.getWidth(), h = bitmap.getHeight();
    int[] pix = newint[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);</code>

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

    这里写图片描述

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

    这里写图片描述

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

    ?
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    <codeclass="hljs"ruby="">LOCAL_PATH := $(call my-dir)
     
    include $(CLEAR_VARS)
     
     
    OpenCV_INSTALL_MODULES := on
    OpenCV_CAMERA_MODULES := off
     
    OPENCV_LIB_TYPE := SHARED
     
    ifeq ($(wildcard $(OPENCV_MK_PATH)),)
    include ........
    ativejniOpenCV.mk
    else
    include $(OPENCV_MK_PATH)
    endif
     
    LOCAL_MODULE := OpenCV
     
    LOCAL_SRC_FILES := cn_edu_zafu_opencv_OpenCVHelper.cpp
     
    LOCAL_LDLIBS +=  -lm -llog
     
    include $(BUILD_SHARED_LIBRARY)
    </code>

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

    这里写图片描述

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

    这里写图片描述

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

    这里写图片描述

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

    这里写图片描述

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

    这里写图片描述

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

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

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

    这里写图片描述

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

    这里写图片描述

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

    这里写图片描述

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

    这里写图片描述

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

    java层的测试方法

    ?
    1
    2
    3
    4
    5
    6
    7
    8
    9
    <code avrasm=""class="hljs">OpenCVLoader.initDebug();
    Mat rgbMat = newMat();
    Mat grayMat = newMat();
    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 grayMat
    Utils.matToBitmap(grayMat, grayBitmap); //convert mat to bitmap
    img.setImageBitmap(grayBitmap);</code>

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

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

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

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

    最后附上源码

    CSDN又抽了,正常之后有空补上

  • 相关阅读:
    LVS基于DR模式负载均衡的配置
    Linux源码安装mysql 5.6.12 (cmake编译)
    HOSt ip is not allowed to connect to this MySql server
    zoj 3229 Shoot the Bullet(无源汇上下界最大流)
    hdu 3987 Harry Potter and the Forbidden Forest 求割边最少的最小割
    poj 2391 Ombrophobic Bovines(最大流+floyd+二分)
    URAL 1430 Crime and Punishment
    hdu 2048 神、上帝以及老天爷(错排)
    hdu 3367 Pseudoforest(最大生成树)
    FOJ 1683 纪念SlingShot(矩阵快速幂)
  • 原文地址:https://www.cnblogs.com/nafio/p/9137293.html
Copyright © 2011-2022 走看看