开发环境
Windows 7 x64 旗舰版
Android Studio 2.1.2
JDK 1.8.0————(C:\Program Files\Java\jdk1.8.0_102)
Android 6.0(API 23)
OpenCV 3.1.0 Android SDK————(E:\Android-Config\OpenCV-android-sdk\sdk\native\jni)
Android NDK r12b————————(E:\Android-Config\android-ndk-r12b)
1、打开Android Studio 2.1.2,点击图标打开SDK Manager。选择SDK Tools,勾选NDK如下图
我是另外下载,解压到上面“开发环境”的路径。
2、打开Android Studio,新建一个项目;(参考一)
File——Project Structure
--Android SDK Location:C:\Users\duan\AppData\Local\Android\Sdk
--JDK Location:C:\Program Files\Java\jdk1.8.0_102
--Android NDK Location:E:\Android-Config\android-ndk-r12b
3、右键main,新建jni——在源码目录创建一个jni文件夹。右击main文件夹,选择New->Directory,输入jni;
再新建Android.mk,Application.mk,gray-process.cpp。
4、此时项目的文件结构如下图所示,比较重要的文件用红色框标记:
5、每个文件的内容:
MainActivity:
grayProc()、loadLibrary();
package com.example.duanjiwei.opencvndk;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Bitmap.Config;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.ImageView;
public class MainActivity extends AppCompatActivity implements OnClickListener {
private Button btnProc;
private ImageView imageView;
private Bitmap bmp;
public static native int[] grayProc(int[] pixels, int w, int h);
static {
System.loadLibrary("gray-process");
}
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
btnProc = (Button) findViewById(R.id.btn_gray_process);
imageView = (ImageView) findViewById(R.id.image_view);
bmp = BitmapFactory.decodeResource(getResources(), R.drawable.pic);
imageView.setImageBitmap(bmp);
btnProc.setOnClickListener(this);
}
@Override
public void onClick(View v) {
int w = bmp.getWidth();
int h = bmp.getHeight();
int[] pixels = new int[w*h];
bmp.getPixels(pixels, 0, w, 0, 0, w, h);
int[] resultInt = grayProc(pixels, w, h);
Bitmap resultImg = Bitmap.createBitmap(w, h, Config.ARGB_8888);
resultImg.setPixels(resultInt, 0, w, 0, 0, w, h);
imageView.setImageBitmap(resultImg);
}
@Override
public void onResume(){
super.onResume();
}
}
activity_main.xml
添加Button、ImageView控件
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingBottom="@dimen/activity_vertical_margin"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
tools:context="com.example.duanjiwei.opencvndk.MainActivity">
<Button
android:id="@+id/btn_gray_process"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="@string/str_proc"/>
<ImageView
android:id="@+id/image_view"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:contentDescription="@string/str_proc"
android:layout_marginTop="60dp"
android:layout_centerHorizontal="true" />
</RelativeLayout>
Android.mk
注意:OpenCV路径,使用 “/” 做分隔; 使用"\\"出现错误;
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
#opencv
include E:/Android-Config/OpenCV-android-sdk/sdk/native/jni/OpenCV.mk
OPENCV_CAMERA_MODULES:=on
OPENCV_INSTALL_MODULES:=on
OPENCV_LIB_TYPE:=SHARED
LOCAL_SRC_FILES := gray-process.cpp
LOCAL_LDLIBS += -llog
LOCAL_MODULE := gray-process
include $(BUILD_SHARED_LIBRARY)
Application.mk
APP_ABI中添加适用范围;
APP_STL := gnustl_shared
APP_CPPFLAGS := -frtti -fexceptions -std=c++11
APP_ABI := armeabi armeabi-v7a x86
APP_PLATFORM := android-15
gray_process.cpp
那么C++和Java之间的是如何通过JNI来进行互相调用的呢?
我们知道,在Android中,当Java文件被编译成dex文件之后,会由类加载器加载到Dalvik VM(DVM)中,由DVM来进行解释,翻译成机器语言之后,才能由机器来运行。
而对于C/C++来说,其源代码经由Android提供的NDK工具包,可以编译成可执行动态库(即.so文件),之后,Java和C++之间就可以进行通讯了。
jni.h源文件:
//
// Created by duanjiwei on 2016/9/8.
//
#include <jni.h>
#include <opencv2/opencv.hpp>
extern "C" {
using namespace cv;
using namespace std;
JNIEXPORT jintArray JNICALL Java_com_example_duanjiwei_opencvndk_MainActivity_grayProc(JNIEnv *env, jclass obj, jintArray buf, jint w, jint h){
jboolean ptfalse = false;
jint* srcBuf = env->GetIntArrayElements(buf, &ptfalse);
if(srcBuf == NULL){
return 0;
}
int size=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;
}
}
res--values--strings.xml
<resources>
<string name="app_name">OpenCVNDK</string>
<string name="str_proc">gray process</string>
<string name="str_desc">image description</string>
</resources>
src下的build.gradle
import org.apache.tools.ant.taskdefs.condition.Os
apply plugin: 'com.android.application'
android {
compileSdkVersion 23
buildToolsVersion "23.0.3"
defaultConfig {
applicationId "com.example.duanjiwei.opencvndk"
minSdkVersion 15
targetSdkVersion 23
versionCode 1
versionName "1.0"
}
sourceSets.main {
jniLibs.srcDirs = ['src/main/libs', 'src/main/jniLibs']
jni.srcDirs = [] //disable automatic ndk-build call
}
project.ext.versionCodes = ['armeabi':1, 'armeabi-v7a':2, 'arm64-v8a':3, 'mips':5, 'mips64':6, 'x86':8, 'x86_64':9]
//versionCode digit for each supported ABI, with 64bit>32bit and x86>armeabi-*
android.applicationVariants.all { variant ->
// assign different version code for each output
variant.outputs.each { output ->
output.versionCodeOverride =
project.ext.versionCodes.get(output.getFilter(com.android.build.OutputFile.ABI), 0) * 1000000 + defaultConfig.versionCode
}
}
// call regular ndk-build(.cmd) script from app directory
task ndkBuild(type: Exec) {
Properties properties = new Properties()
properties.load(project.rootProject.file('local.properties').newDataInputStream())
def ndkDir = properties.getProperty('ndk.dir')
if (Os.isFamily(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
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
}
dependencies {
compile fileTree(dir: 'libs', include: ['*.jar'])
testCompile 'junit:junit:4.12'
compile 'com.android.support:appcompat-v7:23.3.0'
}
app下的build.gradle
// Top-level build file where you can add configuration options common to all sub-projects/modules.
buildscript {
repositories {
jcenter()
}
dependencies {
classpath 'com.android.tools.build:gradle:2.1.2'
// NOTE: Do not place your application dependencies here; they belong
// in the individual module build.gradle files
}
}
allprojects {
repositories {
jcenter()
}
}
task clean(type: Delete) {
delete rootProject.buildDir
}
local.properties
Android sdk路径、NDK路径;
## This file is automatically generated by Android Studio.
# Do not modify this file -- YOUR CHANGES WILL BE ERASED!
#
# This file should *NOT* be checked into Version Control Systems,
# as it contains information specific to your local configuration.
#
# Location of the SDK. This is only used by Gradle.
# For customization when using a Version Control System, please read the
# header note.
sdk.dir=C\:\\Users\\duanjiwei\\AppData\\Local\\Android\\Sdk
ndk.dir=E\:\\Android-Config\\android-ndk-r12b
模拟器与手机上均能成功运行:
===================================================
网上方法:
先import module
添加依赖
app中创jniLibs
sourceSets改成这样