最近想尝试下OpenCV,但是又不想抛弃外表很美的Android Studio,因此作者简单研究了下怎么在Android Studio下进行OpenCV开发。
一、搭建环境,获取OpenCV SDK
首先我们需要下载安装好Android开发环境包括JDK、SDK、NDK、Android Studio,这里使用AS版本为Ver2.1.2
其次我们需要在OpenCV官网上下载OpenCV For Android ,下载完解压后的文件目录如下:
二、拷贝native文件夹
接下来我们在AS中新建工程OpenCV,将OpenCV For Android中sdk下native文件夹完全复制到项目目录下,效果如下图所示:
三、配置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->下方加号
命名和描述为javah,分组为NDK,Program为javah(此处为我们在命令行输入的命令),参数为 -d ../jni $FileClass$,工作目录为 $ModuleFileDir$/src/main/java。其中这些带有$符号的部分都可以在旁边的插入宏按钮中找到。
下图为ndk-build、ndk-build clean的配置详情,这里需要注意一点,在配置这两个工具时,Program要在后面的 ... 中选一下执行命令的文件,这样我们就省的配置环境变量了
四、APP目录结构和编码
一个编译过而且运行成功项目的app/src/main文件夹结构应该有如下文件夹,截图一并放上:
java 原本就有
jni 我们自己创建,用于保存C、C++代码、mk文件等
jniLibs 我们自己创建,用于存放一些别人编译好的so文件,这里只放了libopencv_java3.so
libs ndk-build生成
obj ndk-build生成
如果我们在新建的工程下是以怎么样的流程操作呢?
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文件
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的对应平台文件夹下即可。下面放个效果图: