[Android Studio] Using NDK to call OpenCV
NDK才是Android开发通向超高薪之路。(这句话,似乎四年前有云)
难点在于常用的non-free module (sift and surf)
unsw@unsw-UX303UB$ pwd
/home/unsw/Android/5-android/NDKTest/app/src/main
unsw@unsw-UX303UB$ javah -d jni -classpath ../../build/intermediates/classes/debug com.example.unsw.ndktest.NativeClass
生成了jni目录, 并且有了一个.h文件。
jni目录下添加
1. 通过.h文件复制粘贴出个.c文件,这样少输入这么长的名字。
2. 然后,把.h的内容改为.c需要的形式。
/* DO NOT EDIT THIS FILE - it is machine generated */ #include <jni.h> /* Header for class com_example_unsw_ndktest_NativeClass */ #ifndef _Included_com_example_unsw_ndktest_NativeClass #define _Included_com_example_unsw_ndktest_NativeClass #ifdef __cplusplus extern "C" { #endif /* * Class: com_example_unsw_ndktest_NativeClass * Method: getMessageFromJNI * Signature: ()Ljava/lang/String; */ JNIEXPORT jstring JNICALL Java_com_example_unsw_ndktest_NativeClass_getMessageFromJNI (JNIEnv *, jclass); // 改为如下所示:(其他删除) #include <com_example_unsw_ndktest_NativeClass.h> JNIEXPORT jstring JNICALL Java_com_example_unsw_ndktest_NativeClass_getMessageFromJNI (JNIEnv *env, jclass obj) { return env->NewStringUTF("This is message from JNI"); } #ifdef __cplusplus } #endif #endif
3. 添加到此处后,可以尝试编译下,应该不会报错。
gradle.properties添加:
android.useDeprecatedNdk=true
4. 继续添加.mk文件:
Android.mk
LOCAL_PATH := $(call my-dir) include $(CLEAR_VARS) LOCAL_SRC_FILES := com_example_unsw_ndktest_NativeClass.cpp LOCAL_LDLIBS += -llog LOCAL_MODULE := MyLib include $(BUILD_SHARED_LIBRARY)
Application.mk
APP_STL := gnustl_static
APP_CPPFLAGS := -frtti -fexceptions
APP_ABI := armeabi-v7a //如果是本地模拟器,则为x86
APP_PLATFORM := android-23
5. Compile, generate *.so
6. Just use your baby function.
public class MainActivity extends AppCompatActivity {
static {
System.loadLibrary("MyLibs");
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
((TextView) findViewById(R.id.textView)).setText(NativeClass.getMessageFromJNI());
}
}
若c++调用的是OpenCV lib,如下示范
/* DO NOT EDIT THIS FILE - it is machine generated */ #include <jni.h> #include <stdio.h> #include <opencv2/opencv.hpp> #include "opencv2/nonfree/nonfree.hpp" using namespace cv; using namespace std; /* Header for class com_example_unsw_jeff_OpencvNativeClass */ #ifndef _Included_com_example_unsw_jeff_OpencvNativeClass #define _Included_com_example_unsw_jeff_OpencvNativeClass #ifdef __cplusplus extern "C" { #endif /* * Class: com_example_unsw_jeff_OpencvNativeClass * Method: convertGray * Signature: (JJ)I */ int toGray(Mat img, Mat& gray); JNIEXPORT jint JNICALL Java_com_example_unsw_jeff_OpencvNativeClass_convertGray (JNIEnv *, jclass, jlong, jlong); #ifdef __cplusplus } #endif #endif
但要注意一个问题:Nonfree Module (SIFT, SURF)
参见如下:
OpenCV & Android
-
Tutorial - part 1: Using OpenCV Nonfree Module (SIFT, SURF) in Android NDK Projects (可用)
-
Tutorial - part 2: Use OpenCV Nonfree module (SIFT, SURF) in Android Applications via JNI (可用)
Kornel的总结:(opencv 2.4.10)
http://stackoverflow.com/questions/28510045/undefined-reference-to-cvinitmodule-nonfree-in-android
直接使用现成lib的tutor: https://www.youtube.com/watch?v=cLK9CjQ-pNI
作者代码隐藏在了这里:https://dmigit.uqtr.ca/u/nguidjol
1. 工程下新建文件夹 libraries/,并拷贝过来 java文件夹 in opencv-for-android。
unsw@unsw-UX303UB$ cp ./OpenCV-2.4.9-android-sdk/sdk/ etc/ java/ native/
Copy "java" to libraries and change its name to "opencv"
unsw@unsw-UX303UB$ pwd /home/unsw/Programmer/5-android/nonfree_model unsw@unsw-UX303UB$ ls app gradle gradlew.bat nonfree_model.iml build gradle.properties libraries settings.gradle build.gradle gradlew local.properties unsw@unsw-UX303UB$ ls libraries/ libraries.iml opencv
效果如下:
2. opencv文件夹下 create build.gradle.
apply plugin: 'com.android.library' buildscript { repositories { mavenCentral() } dependencies { classpath 'com.android.tools.build:gradle:2.2.3' } } android { compileSdkVersion 23 buildToolsVersion "23.0.2" defaultConfig { minSdkVersion 23 targetSdkVersion 23 versionCode 1 versionName "1.0" } sourceSets { main { manifest.srcFile 'AndroidManifest.xml' java.srcDirs = ['src'] resources.srcDirs = ['src'] res.srcDirs = ['res'] aidl.srcDirs = ['src'] } } }
3. settings.gradle添加库
include ':app' include ':libraries:opencv' //add.
之后,Project structure便可加载libraries:opencv 模块。
4. 添加sift操作java文件
package com.example.unsw.nonfree_model; import android.content.Intent; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.media.FaceDetector; import android.net.Uri; import android.os.Bundle; import android.provider.MediaStore; import android.support.v7.app.AppCompatActivity; import android.view.View; import android.widget.Button; import android.widget.ImageView; import android.widget.VideoView; import org.opencv.android.Utils; import org.opencv.core.Mat; import org.opencv.core.MatOfKeyPoint; import org.opencv.features2d.FeatureDetector; import org.opencv.features2d.Features2d; import org.opencv.imgproc.Imgproc; public class MainActivity extends AppCompatActivity { static { System.loadLibrary("opencv_java"); System.loadLibrary("nonfree"); } private ImageView imageView; private Bitmap inputImage; // make bitmap from image resource private FeatureDetector detector = FeatureDetector.create(FeatureDetector.SIFT); @Override protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState); inputImage = BitmapFactory.decodeResource(getResources(), R.drawable.test); setContentView(R.layout.activity_main); imageView = (ImageView) this.findViewById(R.id.imageView); sift(); } public void onResume(){ super.onResume(); } public void sift(){ Mat rgba = new Mat(); Utils.bitmapToMat(inputImage,rgba); MatOfKeyPoint keyPoints = new MatOfKeyPoint(); Imgproc.cvtColor(rgba, rgba, Imgproc.COLOR_RGBA2GRAY); detector.detect(rgba, keyPoints); Features2d.drawKeypoints(rgba, keyPoints, rgba); Utils.matToBitmap(rgba, inputImage); imageView.setImageBitmap(inputImage); } }
以及相对应的 layout。
5. 添加现成的 .so
新建jniLibs,给jniLibs各个目录内添加.so。
效果图:(可见sift的特征点)
-- 如何生成自己的 .so --
(1)为使OpenCV-2.4.9-android-sdk完整,添加了OpenCV-2.4.9的nonfree文件夹。
unsw@unsw-UX303UB$ ls opencv-2.4.9/modules/nonfree/include/opencv2/nonfree/ features2d.hpp gpu.hpp nonfree.hpp ocl.hpp
unsw@unsw-UX303UB$ ls OpenCV-2.4.9-android-sdk/sdk/native/jni/include/opencv2/nonfree/ features2d.hpp nonfree.hpp
(2)新工程:MyApplication下创建文件夹:nonfree_opencv_android/jni, 其下也添加OpenCV-2.4.9的modules/nonfree/src/* 的内容。
unsw@unsw-UX303UB$ pwd
/home/unsw/Programmer/5-android/MyApplication/nonfree_opencv_android unsw@unsw-UX303UB$ ls jni libs obj //ndk-build后生成的。
// jni 添加以下内容: unsw@unsw-UX303UB$ ls ~/Android/opencv-2.4.9/modules/nonfree/src/
cuda/ opencl/ sift.cpp surf_gpu.cpp
nonfree_init.cpp precomp.hpp surf.cpp surf_ocl.cpp
修改文件里的内容,按照报错提示去掉ocl相关的部分即可。
unsw@unsw-UX303UB$ ~/Android/Sdk/ndk-bundle/ndk-build [x86] Compile++ : nonfree <= nonfree_init.cpp In file included from jni/nonfree_init.cpp:43: jni/precomp.hpp:46:10: fatal error: 'cvconfig.h' file not found #include "cvconfig.h" ^ 1 error generated. make: *** [obj/local/x86/objs/nonfree/nonfree_init.o] Error 1 unsw@unsw-UX303UB$ ~/Android/Sdk/ndk-bundle/ndk-build [x86] Compile++ : nonfree <= nonfree_init.cpp jni/nonfree_init.cpp:69:19: error: unknown type name 'SURF_OCL' CV_INIT_ALGORITHM(SURF_OCL, "Feature2D.SURF_OCL", ^ jni/nonfree_init.cpp:69:19: error: use of undeclared identifier 'SURF_OCL' 2 errors generated. make: *** [obj/local/x86/objs/nonfree/nonfree_init.o] Error 1
(3)再添加Android.mk和Application.mk,编译即可。
APP_ABI := x86 APP_STL := gnustl_static APP_CPPFLAGS := -frtti -fexceptions APP_PLATFORM := android-23
LOCAL_PATH:= $(call my-dir) include $(CLEAR_VARS) OPENCV_INSTALL_MODULES:=on OPENCV_CAMERA_MODULES:=off include /home/unsw/Android/OpenCV-2.4.9-android-sdk/sdk/native/jni/OpenCV.mk LOCAL_C_INCLUDES:= /home/unsw/Android/OpenCV-2.4.9-android-sdk/sdk/native/jni/include LOCAL_MODULE := nonfree LOCAL_CFLAGS := -Werror -O3 -ffast-math LOCAL_LDLIBS += -llog LOCAL_SRC_FILES := nonfree_init.cpp \ sift.cpp \ surf.cpp include $(BUILD_SHARED_LIBRARY)
Compile:
unsw@unsw-UX303UB$ ~/Android/Sdk/ndk-bundle/ndk-build [x86] Compile++ : nonfree <= nonfree_init.cpp [x86] Compile++ : nonfree <= sift.cpp [x86] Compile++ : nonfree <= surf.cpp [x86] Prebuilt : libopencv_java.so <= /home/unsw/Android/OpenCV-2.4.9-android-sdk/sdk/native/jni/../libs/x86/ [x86] SharedLibrary : libnonfree.so [x86] Install : libnonfree.so => libs/x86/libnonfree.so [x86] Install : libopencv_java.so => libs/x86/libopencv_java.so
Success!