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


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

    其实最早接触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构建工具)

    android.useDeprecatedNdk=true
    • 1
    • 1

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

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

    编辑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'
    • 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
    • 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

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

     
     
    APP_STL := gnustl_static
    APP_CPPFLAGS := -frtti -fexceptions
    APP_ABI := armeabi armeabi-v7a
    APP_PLATFORM := android-8
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 1
    • 2
    • 3
    • 4
    • 5

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

     
    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)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24

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

    这里写图片描述

    这里写图片描述

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

    声明java层的native方法

     
     
    public class OpenCVHelper {
        static {
            System.loadLibrary("OpenCV");
        }
        public static native int[] gray(int[] buf, int w, int h);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

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

     
     
    /* 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
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23

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

     
    #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, 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;
    }
    }
    • 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
    • 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

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

     
    LOCAL_SRC_FILES := cn_edu_zafu_opencv_OpenCVHelper.cpp
    • 1
    • 1

    然后在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);
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

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

    这里写图片描述

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

    这里写图片描述

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

     
    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)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24

    注意上面的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层的测试方法

    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 grayMat
    Utils.matToBitmap(grayMat, grayBitmap); //convert mat to bitmap
    img.setImageBitmap(grayBitmap);
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

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

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

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

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

    最后附上源码

  • 相关阅读:
    lvs_基础理论
    iptables_表和链(Traversing of tables and chains)
    题解-【集训队作业2018】Simple Tree
    题解-CF559C
    题解-[Violet]天使玩偶/SJY摆棋子
    题解-[POI2014]PRZ-Criminals
    题解-CF961G
    题解-CF1392H
    WorldCreator基础流程
    gstreamer-vaapi 之 README
  • 原文地址:https://www.cnblogs.com/jukan/p/6308531.html
Copyright © 2020-2023  润新知