具体实现方法如下:
需要的工具:
- NDK
- OpenCV for Android
- OpenCV for Windows(其实只需要两个头文件)
- Android ADT
1.编译nonfree module库
博主OpenCV for Windows安装路径为C:\Program Files\opencv,OpenCV4Android路径为 E:\My Documents\Android\OpenCV-2.4.5-android-sdk。
nonfree module 的源代码存储在C:\Program Files\opencv\modules\nonfree\src中,头文件存储在C:\Program Files\opencv\modules\nonfree\include\opencv2\nonfree中。
编译的过程中需要两个头文件,一个为nonfree.hpp,一个为features2d.hpp,将这两个头文件拷贝至OpenCV4Android SDK的include文件夹下:E:\My Documents\Android\OpenCV-2.4.5-android-sdk\sdk\native\jni\include\opencv2\nonfree文件夹下。
为了编译库文件,我们还需要nonfree module的源代码。源代码为C:\Program Files\opencv\modules\nonfree\src中的nonfree_init.cpp,precomp.cpp,sift.cpp,surf.cpp和precomp.hpp五个文件。为了简单起见,我们需要一个简单的Android JNI的工程来帮我们编译这些源代码。
2.配置NDK环境
在Android中创建JNI工程的方法是在一个已经创建好的Android工程上点击右键,new->Ohter->Convert to a C/C++ Project(Adds C/C++ Nature),并在Android工程目录中创建jni文件夹,将上述5个源代码文件拷贝至jni文件夹中。并在jni文件夹中创建Android.mk和Application.mk文件。此时文件组织如下:
3.编写makefile文件
LOCAL_PATH:= $(call my-dir)
include $(CLEAR_VARS)
OPENCV_INSTALL_MODULES:=on
OPENCV_CAMERA_MODULES:=off
include ..\OpenCV-2.4.5-android-sdk\sdk\native\jni\OpenCV.mk
LOCAL_C_INCLUDES:= ../OpenCV-2.4.5-android-sdk/sdk/native/jni/include
LOCAL_MODULE := nonfree
LOCAL_CFLAGS := -Werror -O3 -ffast-math
LOCAL_LDLIBS += -llog
LOCAL_SRC_FILES := nonfree_init.cpp \
precomp.cpp \
sift.cpp \
surf.cpp
include $(BUILD_SHARED_LIBRARY)
在Application.mk中代码如下
#APP_ABI := armeabi
APP_ABI := armeabi-v7a
APP_STL := gnustl_static
APP_CPPFLAGS := -frtti -fexceptions
APP_PLATFORM := android-8
这里可能有问题,APP_PLATFORM按照Andoird工程建立时最小SDK填写,否则编译不过。
4. 在Android中使用SURF的例子
#include "NativeSurf.h"
#include <opencv2/opencv.hpp>
#include <stdio.h>
#include <opencv2/nonfree/features2d.hpp>
#include <opencv2/nonfree/nonfree.hpp>
using namespace cv;
using namespace std;
void KeyPoint2Mat(vector<KeyPoint>& keypoints, Mat& mat)
{
int i = 0;
int size = keypoints.size();
mat.create(size,1,CV_32FC(7));
float* buff = (float*)mat.data;
for(i=0;i<size;i++)
{
KeyPoint kp = keypoints[i];
buff[7*i+0] = kp.pt.x;
buff[7*i+1] = kp.pt.y;
buff[7*i+2] = kp.size;
buff[7*i+3] = kp.angle;
buff[7*i+4] = kp.response;
buff[7*i+5] = kp.octave;
buff[7*i+6] = kp.class_id;
}
}
JNIEXPORT void JNICALL Java_com_ruif_nativeSurf_NativeSurf_SurfDetect
(JNIEnv *, jclass, jlong imgObj, jlong keyPointsObj, jlong descriptorObj)
{
//Create Mats
Mat* img = (Mat*)imgObj; //img
Mat* descriptor = (Mat*)descriptorObj; //Descriptor
Mat* keyPointsMat = (Mat*)keyPointsObj;
vector<KeyPoint> keyPointvec;
SurfFeatureDetector surfDetector(1000);
SurfDescriptorExtractor surfExtractor;
surfDetector.detect(*img,keyPointvec);
surfExtractor.compute(*img,keyPointvec,*descriptor);
KeyPoint2Mat(keyPointvec,*keyPointsMat);
}
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE := nonfree
LOCAL_SRC_FILES := libnonfree.so
include $(PREBUILT_SHARED_LIBRARY)
include $(CLEAR_VARS)
LOCAL_MODULE := opencv_java_prebuilt
LOCAL_SRC_FILES := libopencv_java.so
include $(PREBUILT_SHARED_LIBRARY)
include $(CLEAR_VARS)
include ../OpenCV-2.4.5-android-sdk/sdk/native/jni/OpenCV.mk
LOCAL_MODULE := Surf
LOCAL_CFLAGS := -Werror -O3 -ffast-math
LOCAL_LDLIBS += -llog -ldl
LOCAL_SHARED_LIBRARIES := nonfree opencv_java_prebuilt
LOCAL_SRC_FILES := Surf.cpp
include $(BUILD_SHARED_LIBRARY)
编写Application.mk文件如下:
APP_ABI := armeabi-v7a
APP_STL := gnustl_static
APP_CPPFLAGS := -frtti -fexceptions
APP_PLATFORM := android-8
此时你会发现各种语法错误和不能解析的变量。其实他们并不影响编译,所有的头文件在Android.mk和Application.mk中已经声明,编译仍然会成功,但是自己编写程序时为了使用Eclipse的提示功能,需要对工程进行如下设置。
你会发现所有都正常了,此时工程结构组织如下:
在libs下的libSurf.so库就是我们需要使用的JNI库。
5.使用JNI库
private BaseLoaderCallback mLoaderCallback = new BaseLoaderCallback(this) {
@Override
public void onManagerConnected(int status) {
switch (status) {
case LoaderCallbackInterface.SUCCESS:
{
Log.i(Unity.TAG,"OpenCV loaded successfully");
System.loadLibrary("nonfree");
System.loadLibrary("opencv_java");
System.loadLibrary("Surf");
isOpenCVLoad = true;
} break;
default:
{
Log.i(Unity.TAG,"OpenCV loaded Failed!");
super.onManagerConnected(status);
} break;
}
}
};
一定要顺序加载,否则会报错。至此就能够在JAVA中使用Native函数了。