环境
win10
android studio2.0
OpenCV-3.1.0-android-sdk
android-ndk-r10e-windows-x86_64
jdk-8u102-windows-x64
1配置ndk环境
2 新建JNI目录
默认生成在app/src/main/jni/
3 在jni中加入Application.mk和Android.mk
Application.mk
APP_STL := gnustl_static //标准c++ stl库 APP_CPPFLAGS := -frtti -fexceptions APP_ABI := armeabi armeabi-v7a //还可以添加x86等 APP_PLATFORM := android-23 //可以不写
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 D:softOpenCV-android-sdksdk ativejniOpenCV.mk//这个路径配自己的 else include $(OPENCV_MK_PATH) endif LOCAL_MODULE := OpenCV //打包so的名称,实际名称是libOpenCV.so 加载时只需要加载OpenCV.so LOCAL_SRC_FILES := com_n_topencv_OpencvUtils.cpp//要放进so的cpp LOCAL_LDLIBS += -lm -llog include $(BUILD_SHARED_LIBRARY)
说明:
如果有多个放入so的cpp
可以按如下写法
LOCAL_SRC_FILES := playercore-jni.cpp libplayer.cpp event.cpp lock.cpp receiver.cpp decoder.cpp render.cpp androidaudio.cpp androidvideo.cpp blit.cpp yuv2rgbv7.S
4 工程配置
app下的build.gradle
apply plugin: 'com.android.application' android { compileSdkVersion 23 buildToolsVersion "23.0.3" defaultConfig { applicationId "com.n.topencv" minSdkVersion 15 targetSdkVersion 23 versionCode 1 versionName "1.0" //ndk{ // moduleName "OpenCV" //生成的so名字 // abiFilters "armeabi", "armeabi-v7a" //输出指定三种abi体系结构下的so库。目前可有可无。 //} } buildTypes { release { minifyEnabled false proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' } } sourceSets { main() { jniLibs.srcDirs = ['src/main/libs','src/main/jniLibs'] //把jni库指定为中括号中地址(原地址src/main/jniLibs)默认ndk-build生成so位置为libs jni.srcDirs = [] //这个如果不设置,.h文件会提示找不到jni.h(这个文件在java sdk的include中) } } } dependencies { compile fileTree(dir: 'libs', include: ['*.jar']) testCompile 'junit:junit:4.12' compile 'com.android.support:appcompat-v7:23.2.1' }
gradle.properties (补充,之后下载的gradle没这个文件,在gradle/wrapper/下只有gradle-wrapper.properties)
添加一句,这句是使用旧版ndk,否则使用的是实验版构建方式(实验版- -!你懂的)
android.useDeprecatedNdk=true
local.properties
sdk.dir=D:\soft\Android\sdk ndk.dir=D:\soft\android-ndk-r10e
5 添加java代码
MainActivity
package com.n.topencv; import android.app.Activity; import android.graphics.Bitmap; import android.graphics.drawable.BitmapDrawable; import android.media.Image; import android.support.v7.app.AppCompatActivity; import android.os.Bundle; import android.util.Log; import android.view.Gravity; import android.widget.FrameLayout; import android.widget.ImageView; public class MainActivity extends Activity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); Log.e("nafio","OnCreate"); OpencvUtils opencv = new OpencvUtils(); Log.e("nafio","InitSo"); ImageView img = new ImageView(this); Bitmap bitmap = ((BitmapDrawable) getResources().getDrawable( R.mipmap.ic_launcher)).getBitmap(); int w = bitmap.getWidth(), h = bitmap.getHeight(); int[] pix = new int[w * h]; bitmap.getPixels(pix, 0, w, 0, 0, w, h); Log.e("nafio","befor opencv!"); int [] resultPixes=opencv.gray(pix,w,h); Log.e("nafio","after opencv!"); Bitmap result = Bitmap.createBitmap(w,h, Bitmap.Config.RGB_565); result.setPixels(resultPixes, 0, w, 0, 0,w, h); //ImageView img = (ImageView) findViewById(R.id.myImageView); if(null!=img)img.setImageBitmap(result); else{ Log.e("nafio","null fuck!"); } setContentView(img); } }
OpencvUtils
package com.n.topencv; /** * Created by cuizheng on 2016/8/1. */ public class OpencvUtils { static { System.loadLibrary("OpenCV"); //defaultConfig.ndk.moduleName } public native int[] gray(int[] pix,int w,int h); }
xml
<?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.n.topencv"> <application android:allowBackup="true" android:icon="@mipmap/ic_launcher" android:label="@string/app_name" android:supportsRtl="true" android:theme="@style/AppTheme"> <activity android:name=".MainActivity"> <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity> </application> </manifest>
6 添加jni c++代码
com_n_topencv_OpencvUtils.h
这个文件可以自动生成
写完OpencvUtils.java(里面包含有native方法)这个类后,build一下工程,会在
下生成OpencvUtils.class
Terminal进入这个debug文件夹 执行javah -jni com.n.topencv.OpencvUtils
执行成功会在debug下生成com_n_topencv_OpencvUtils.h,把这个头文件copy到jni中
/* DO NOT EDIT THIS FILE - it is machine generated */ #include <jni.h> /* Header for class com_n_topencv_OpencvUtils */ #ifndef _Included_com_n_topencv_OpencvUtils #define _Included_com_n_topencv_OpencvUtils #ifdef __cplusplus extern "C" { #endif /* * Class: com_n_topencv_OpencvUtils * Method: gray * Signature: ([III)[I */ JNIEXPORT jintArray JNICALL Java_com_n_topencv_OpencvUtils_gray (JNIEnv *, jobject, jintArray, jint, jint); #ifdef __cplusplus } #endif #endif
// // Created by cuizheng on 2016/8/1. // #include "com_n_topencv_OpencvUtils.h" #include <stdio.h> #include <stdlib.h> #include <opencv2/opencv.hpp> using namespace cv; extern "C" { JNIEXPORT jintArray JNICALL Java_com_n_topencv_OpencvUtils_gray( JNIEnv *env, jobject obj, jintArray buf, int w, int h); JNIEXPORT jintArray JNICALL Java_com_n_topencv_OpencvUtils_gray( JNIEnv *env, jobject 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; } }
问题说明,这两个文件打开android studio会一直提示,就算点了Sync Now也没用,还是这个提示,不用管,不影响打包和运行
7 用ndk-build打包so
Terminal进入jni文件夹
执行ndk-build,如果执行成功会生成如下文件夹
说明:一旦so包打出来了,jni文件夹就可有可无了,都不影响程序运行
8 运行
一些常见错误报错说明
Error: NDK integration is deprecated in the current plugin. Consider trying the new experimental plugin. For details, see http://tools.android.com/tech-docs/new-build-system/gradle-experimental. Set "android.useDeprecatedNdk=true" in gradle.properties to continue using the current NDK integration.需要在gradle.properties中添加android.useDeprecateNdk = true;使用旧版的ndk功能(不添加会使用实验性的ndk构建工具)
android studio上写c++头文件,引入jni.h时发现报红,cannot find jni.h
解决方法在app->build.gradle中,android标签中
sourceSets { main() { jni.srcDirs = []; } }
Error: Your project contains C++ files but it is not using a supported native build system
这个问题解决方法跟上面类似
gradle.properties中加
Android.useDeprecatedNdk=true
build.gradle(Module:app)中加
sourceSets {
main {
jni.srcDirs = []
}
}