准备
OpenCV http://opencv.org/downloads.html 选择 OpenCV for Android 下载解压
Android Studio 2.2.2 点击图标打开SDK Manager。选择SDK Tools ,勾选NDK ,点击Apply。要等一段时间下载,下载后的文件在<Android SDK>\ndk-bundle。
开始
创建一个Empty Activity项目。然后创建一个jni目录
在jni文件夹中再创建3个文件,Android.mk,Application.mk以及gray-process.cpp。
Android.mk:
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
#opencv
OPENCV_CAMERA_MODULES:=on
OPENCV_INSTALL_MODULES:=on
#OPENCV_LIB_TYPE:=STATIC
OPENCV_LIB_TYPE := SHARED
include /Users/wolf/code/test/opencvTest1/native/jni/OpenCV.mk
LOCAL_SRC_FILES := gray-process.cpp
LOCAL_LDLIBS += -llog
LOCAL_MODULE := gray_process
include $(BUILD_SHARED_LIBRARY)
OPENCV_CAMERA_MODULES:=on 使用摄像头功能
OPENCV_INSTALL_MODULES:=on 自动将依赖的OpenCV的so库拷贝到libs目录下,然后才能自动打包到sdk中。
OPENCV_LIB_TYPE:=STATIC 通过静态的方式链接OpenCV的so,将STATIC改为SHARED,就是通过动态的方式链接,这涉及到最后生成so的大小
include /Users/wolf/code/test/opencvTest1/native/jni/OpenCV.mk 指向你的OpenCV Android SDK中对应文件的位置,OpenCV Android SDK你可以放在电脑的任意位置。
OpenCV.mk 文件可以打开看一看,里面会有需要判断 OPENCV_LIB_TYPE或OPENCV_INSTALL_MODULES来执行一些操作
LOCAL_LDLIBS += -llog 链接本地动态库 可以通过__android_log_print在ndk中打印日志
LOCAL_MODULE := opencv_gray_process 生成库的名字
LOCAL_SRC_FILES := gray-process.cpp 编译so所需要包含的文件,如果有多个文件就 LOCAL_SRC_FILES += 文件名
Application.mk
APP_CPPFLAGS := -frtti -fexceptions -std=c++11
APP_STL := gnustl_static
APP_ABI := armeabi-v7a x86
APP_PLATFORM := android-16
//
// Created by wolf on 16/11/17.
//
#include <jni.h>
#include <stdio.h>
#include <stdlib.h>
#include <opencv2/opencv.hpp>
#include <android/log.h>
#define NELEM(x) ((int) (sizeof(x) / sizeof((x)[0])))
#define LOG_TAG "opencv_gray_process"
#define LOGD(...) ((void)__android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, __VA_ARGS__))
using namespace std;
using namespace cv;
extern "C" {
static jintArray OpenCVHelper_gray
(JNIEnv *env, jclass obj, jintArray buf, int w, int h){
jboolean ptfalse = false;
jint* srcBuf = env->GetIntArrayElements(buf, &ptfalse);
if(srcBuf == NULL){
return 0;
}
int size=w * h;
LOGD("w: %d h: %d",w,h);
Mat srcImage(h, w, CV_8UC4, (unsigned char*)srcBuf);
Mat grayImage;
cvtColor(srcImage, grayImage, COLOR_BGRA2GRAY);
cvtColor(grayImage, srcImage, COLOR_GRAY2BGRA);
jintArray result = env->NewIntArray(size);
env->SetIntArrayRegion(result, 0, size, srcBuf);
env->ReleaseIntArrayElements(buf, srcBuf, 0);
return result;
}
#define JNIREG_CLASS "com/yanzhao/wolf/opencvtest1/OpenCVHelper"//指定要注册的类
static JNINativeMethod g_methods[] ={
{ "gray", "([III)[I", (void *) OpenCVHelper_gray },
};
JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM* vm, void* reserved)
{
JNIEnv* env = NULL;
jint result = -1;
if (vm->GetEnv((void**) &env, JNI_VERSION_1_4) != JNI_OK) {
return -1;
}
assert(env != NULL);
jclass clazz = env->FindClass(JNIREG_CLASS);
env->RegisterNatives(clazz, g_methods, NELEM(g_methods));
return JNI_VERSION_1_4;
}
JNIEXPORT void JNI_OnUnload(JavaVM *jvm, void *reserved)
{
}
}
这里我采用了动态注册的方式,将c函数注册到了java类里。
OpenCVHelper.java
package com.yanzhao.wolf.opencvtest1;
/**
* Created by wolf on 16/11/10.
* e-mail : 12983672@163.com
*/
public class OpenCVHelper {
static {
System.loadLibrary("gray-process");
}
public static native int[] gray(int[] buf, int w, int h);
}
然后就需要修改app目录下的build.gradle文件
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
sourceSets { main {
jniLibs.srcDirs = ['src/main/libs', 'src/main/jniLibs']
jni.srcDirs = []
} }
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'
现在可以点击ndkBuild来编译so文件了。或者通过终端 cd 到jni 目录下执行ndk-build命令。
下面是如何调用native函数
Bitmap photo = bmp;
int w = photo.getWidth();
int h = photo.getHeight();
int[] pixels = new int[w*h];
photo.getPixels(pixels, 0, w, 0, 0, w, h);
int[] resultInt = OpenCVHelper.gray(pixels, w, h);
Bitmap resultImg = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888);
resultImg.setPixels(resultInt, 0, w, 0, 0, w, h);
imageView.setImageBitmap(resultImg);