最近想尝试下OpenCV,但是又不想抛弃外表很美的Android Studio,因此作者简单研究了下怎么在Android Studio下进行OpenCV开发。


一、搭建环境,获取OpenCV SDK

首先我们需要下载安装好Android开发环境包括JDK、SDK、NDK、Android Studio,这里使用AS版本为Ver2.1.2

其次我们需要在OpenCV官网上下载OpenCV For Android ,下载完解压后的文件目录如下:

android studio编译 Read timed out Android studio编译opencv源码_ndk


二、拷贝native文件夹

接下来我们在AS中新建工程OpenCV,将OpenCV For Android中sdk下native文件夹完全复制到项目目录下,效果如下图所示:

android studio编译 Read timed out Android studio编译opencv源码_Android Studio_02


三、配置Tools

我们需要配置三个额外的工具,javah、ndk-build 、ndk-build clean。javah用于通过java 类直接生成对应jni的.h文件,ndk-build用于编译C、C++文件,ndk-build clean用于清除生成的obj和lib文件。下面以配置javah为例

打开AS->Preferences->Tools->External Tools->下方加号

android studio编译 Read timed out Android studio编译opencv源码_ndk_03


命名和描述为javah,分组为NDK,Program为javah(此处为我们在命令行输入的命令),参数为 -d ../jni $FileClass$,工作目录为 $ModuleFileDir$/src/main/java。其中这些带有$符号的部分都可以在旁边的插入宏按钮中找到。

android studio编译 Read timed out Android studio编译opencv源码_ndk_04


下图为ndk-build、ndk-build clean的配置详情,这里需要注意一点,在配置这两个工具时,Program要在后面的 ... 中选一下执行命令的文件,这样我们就省的配置环境变量了

android studio编译 Read timed out Android studio编译opencv源码_ndk_05


android studio编译 Read timed out Android studio编译opencv源码_jni_06


四、APP目录结构和编码

一个编译过而且运行成功项目的app/src/main文件夹结构应该有如下文件夹,截图一并放上:

java 原本就有

jni  我们自己创建,用于保存C、C++代码、mk文件等

jniLibs  我们自己创建,用于存放一些别人编译好的so文件,这里只放了libopencv_java3.so

libs ndk-build生成

obj ndk-build生成

android studio编译 Read timed out Android studio编译opencv源码_jni_07


如果我们在新建的工程下是以怎么样的流程操作呢?

1.我们编写Java文件设计好native方法,我将native方法都放在了OpenCVHelper中,其中写了灰度化处理和腐蚀处理的两个测试方法:

public class OpenCVHelper {

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

    public static native int[] erode(int[] buf, int w, int h);
}


2.在这个类上点击右键,选择NDK,选择javah,可以看到jni文件夹下生成了对应的h文件


android studio编译 Read timed out Android studio编译opencv源码_native_08



3.编写对应cpp文件,这里不赘述代码作用,都是最简单的OpenCV图像处理

#include "yu_myself_opencv_jni_OpenCVHelper.h"
#include <stdio.h>
#include <stdlib.h>
#include "opencv2/opencv.hpp"

using namespace cv;

extern "C" {

JNIEXPORT jintArray JNICALL Java_yu_myself_opencv_jni_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;
}

JNIEXPORT jintArray JNICALL Java_yu_myself_opencv_jni_OpenCVHelper_erode(
        JNIEnv *env, jclass obj, jintArray buf, int w, int h){

    jint *cbuf;
    cbuf = env->GetIntArrayElements(buf, JNI_FALSE);
    if (cbuf == NULL) {
        return 0;
    }

    Mat srcImage(h, w, CV_8UC4, (unsigned char *) cbuf);

    //腐蚀算法
    Mat element = getStructuringElement(MORPH_RECT, Size(15, 15));
    Mat dstImage;
    erode(srcImage, dstImage, element);

    jint* ptr = (jint*)dstImage.ptr(0);

    int size = w * h;
    jintArray result = env->NewIntArray(size);
    env->SetIntArrayRegion(result, 0, size, ptr);
    env->ReleaseIntArrayElements(buf, cbuf, 0);
    return result;
}

}


4.编写Application.mk,APP_ABI为生成这些平台的so文件,通过测试发现x86的so要比arm的大很多

APP_STL := gnustl_static
APP_CPPFLAGS := -frtti -fexceptions
APP_ABI := armeabi armeabi-v7a x86
APP_PLATFORM := android-8



5.编写Android.mk,其中include使用了相对路径

LOCAL_PATH := $(call my-dir)

include $(CLEAR_VARS)


OpenCV_INSTALL_MODULES := on
OpenCV_CAMERA_MODULES := on

OPENCV_LIB_TYPE :=SHARED

ifeq ("$(wildcard $(OPENCV_MK_PATH))","")
include ../../../../native/jni/OpenCV.mk
else
include $(OPENCV_MK_PATH)
endif

LOCAL_MODULE := OpenCV

LOCAL_SRC_FILES := yu_myself_opencv_jni_OpenCVHelper.cpp

LOCAL_LDLIBS +=  -lm -llog

include $(BUILD_SHARED_LIBRARY)



6.修改build.gradle文件

apply plugin: 'com.android.application'

android {
    compileSdkVersion 24
    buildToolsVersion "24.0.0"

    defaultConfig {
        applicationId "yu.myself.opencv"
        minSdkVersion 15
        targetSdkVersion 24
        versionCode 1
        versionName "1.0"
    }
    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
        }
    }
    //这里是我们添加的jni文件夹配置
    sourceSets {
        main {
            jni.srcDirs = []
            jniLibs.srcDirs = ['src/main/libs', 'src/main/jniLibs']
        }
    }
}

dependencies {
    compile fileTree(dir: 'libs', include: ['*.jar'])
    testCompile 'junit:junit:4.12'
    compile 'com.android.support:appcompat-v7:24.0.0'
}



五、测试效果

我们在cpp文件或者h文件上右键,NDK,ndk-build,如果正常的话会出现libs和obj文件夹,然后我们运行APP,程序编译没有问题,但是在运行的时候会报错,说缺少libopencv_java3.so文件。这个东西在我们拷贝过来的native/libs文件夹下,只拷贝对应的so放到jniLibs的对应平台文件夹下即可。下面放个效果图:

android studio编译 Read timed out Android studio编译opencv源码_jni_09