http://blog.csdn.net/ruifdu/article/details/9120559

在OpenCV4Android中没有nonfree module,因此也就没有了SURF和SIFT组件。但是我们可以通过OpenCV for Windows的nonfree module开源代码通过NDK将其编译为Android可以使用的.so库文件,然后通过JNI技术,将该.so文件挂载到JNI的库中。

具体实现方法如下:

需要的工具:

  1. NDK
  2. OpenCV for Android
  3. OpenCV for Windows(其实只需要两个头文件)
  4. Android ADT

电脑操作系统为Windows7 x86,Android开发环境为ADT 20130512。下面说明如何在OpenCV中使用nonfree module:

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文件。此时文件组织如下:

    博主NDK的位置为C:\android-ndk-r8e,在系统变量中创建NDKROOT,变量值为C:\android-ndk-r8e,在该文件夹下有ndk-build.cmd脚本命令文件,该文件是使用NDK编译C代码的关键。配置完毕后,在Android工程中配置C++编译命令,配置如下所示,意味使用环境变量中NDKROOT/ndk-build.cmd进行编译

    在Behaviour选项卡中勾选Build on resource save(Auto build)。至此NDK配置完毕
 

3.编写makefile文件

    在Android.mk中代码如下
[plain] view
plain
 copy

  1. LOCAL_PATH:= $(call my-dir)
  2. include $(CLEAR_VARS)
  3. OPENCV_INSTALL_MODULES:=on
  4. OPENCV_CAMERA_MODULES:=off
  5. include ..\OpenCV-2.4.5-android-sdk\sdk\native\jni\OpenCV.mk
  6. LOCAL_C_INCLUDES:= ../OpenCV-2.4.5-android-sdk/sdk/native/jni/include
  7. LOCAL_MODULE    := nonfree
  8. LOCAL_CFLAGS    := -Werror -O3 -ffast-math
  9. LOCAL_LDLIBS    += -llog
  10. LOCAL_SRC_FILES := nonfree_init.cpp \
  11. precomp.cpp \
  12. sift.cpp \
  13. surf.cpp
  14. include $(BUILD_SHARED_LIBRARY)
在Android.mk中使用了OpenCV.mk的路径和jni/include的路径,我的工程是和OpenCV SDK放在同一文件夹下的,因此可以这样写。两处使用绝对路径也可以,但是不能有空格。

在Application.mk中代码如下

[plain] view
plain
 copy

  1. #APP_ABI := armeabi
  2. APP_ABI := armeabi-v7a
  3. APP_STL := gnustl_static
  4. APP_CPPFLAGS := -frtti -fexceptions
  5. APP_PLATFORM := android-8

这里可能有问题,APP_PLATFORM按照Andoird工程建立时最小SDK填写,否则编译不过。



    如果之前勾选了Auto Build,这里工程会自动编译,在libs下会生成两个库,为libnonfree.so和libopencv_java.so,这两个库就是需要的库文件了。使用这两个库文件,就可以通过JNI技术在Android中使用SURF算法。

4. 在Android中使用SURF的例子

    和2中一样,首先创建一个带有NDK工程的Android工程,并创建jni文件夹,用javah生成native的类头文件(如何生成类文件请参看另一边博文),并将刚才编译好的libnonfree.so和libopencv_java.so拷贝值jni文件夹下(当然你也可以把这两个东西放到OpenCV的库文件夹下,一劳永逸,只要能在链接的时候能搜索到就行了)。编写好native方法的c++实现,这里我实现了利用SURF的特征点检测和描述符生成。代码如下:

[cpp] view
plain
 copy

  1. #include "NativeSurf.h"
  2. #include <opencv2/opencv.hpp>
  3. #include <stdio.h>
  4. #include <opencv2/nonfree/features2d.hpp>
  5. #include <opencv2/nonfree/nonfree.hpp>
  6. using namespace cv;
  7. using namespace std;
  8. void KeyPoint2Mat(vector<KeyPoint>& keypoints, Mat& mat)
  9. {
  10. int i = 0;
  11. int size = keypoints.size();
  12. mat.create(size,1,CV_32FC(7));
  13. float* buff = (float*)mat.data;
  14. for(i=0;i<size;i++)
  15. {
  16. KeyPoint kp = keypoints[i];
  17. buff[7*i+0] = kp.pt.x;
  18. buff[7*i+1] = kp.pt.y;
  19. buff[7*i+2] = kp.size;
  20. buff[7*i+3] = kp.angle;
  21. buff[7*i+4] = kp.response;
  22. buff[7*i+5] = kp.octave;
  23. buff[7*i+6] = kp.class_id;
  24. }
  25. }
  26. JNIEXPORT void JNICALL Java_com_ruif_nativeSurf_NativeSurf_SurfDetect
  27. (JNIEnv *, jclass, jlong imgObj, jlong keyPointsObj, jlong descriptorObj)
  28. {
  29. //Create Mats
  30. Mat* img = (Mat*)imgObj;                //img
  31. Mat* descriptor = (Mat*)descriptorObj;  //Descriptor
  32. Mat* keyPointsMat = (Mat*)keyPointsObj;
  33. vector<KeyPoint> keyPointvec;
  34. SurfFeatureDetector surfDetector(1000);
  35. SurfDescriptorExtractor surfExtractor;
  36. surfDetector.detect(*img,keyPointvec);
  37. surfExtractor.compute(*img,keyPointvec,*descriptor);
  38. KeyPoint2Mat(keyPointvec,*keyPointsMat);
  39. }

编写Android.mk文件如下:

[plain] view
plain
 copy

  1. LOCAL_PATH := $(call my-dir)
  2. include $(CLEAR_VARS)
  3. LOCAL_MODULE    := nonfree
  4. LOCAL_SRC_FILES := libnonfree.so
  5. include $(PREBUILT_SHARED_LIBRARY)
  6. include $(CLEAR_VARS)
  7. LOCAL_MODULE    := opencv_java_prebuilt
  8. LOCAL_SRC_FILES := libopencv_java.so
  9. include $(PREBUILT_SHARED_LIBRARY)
  10. include $(CLEAR_VARS)
  11. include ../OpenCV-2.4.5-android-sdk/sdk/native/jni/OpenCV.mk
  12. LOCAL_MODULE    := Surf
  13. LOCAL_CFLAGS    := -Werror -O3 -ffast-math
  14. LOCAL_LDLIBS    += -llog -ldl
  15. LOCAL_SHARED_LIBRARIES := nonfree opencv_java_prebuilt
  16. LOCAL_SRC_FILES := Surf.cpp
  17. include $(BUILD_SHARED_LIBRARY)

编写Application.mk文件如下:

[plain] view
plain
 copy

  1. APP_ABI := armeabi-v7a
  2. APP_STL := gnustl_static
  3. APP_CPPFLAGS := -frtti -fexceptions
  4. APP_PLATFORM := android-8

此时你会发现各种语法错误和不能解析的变量。其实他们并不影响编译,所有的头文件在Android.mk和Application.mk中已经声明,编译仍然会成功,但是自己编写程序时为了使用Eclipse的提示功能,需要对工程进行如下设置。

在C/C++ General->Paths and Symbols中设置Includes为:
${NDKROOT}/platforms/android-14/arch-arm/usr/include
${NDKROOT}/sources/cxx-stl/gnu-libstdc++/4.7/include
${NDKROOT}/sources/cxx-stl/gnu-libstdc++/4.7/libs/armeabi-v7a/include
../OpenCV-2.4.5-android-sdk/sdk/native/jni/include

你会发现所有都正常了,此时工程结构组织如下:

在libs下的libSurf.so库就是我们需要使用的JNI库。

5.使用JNI库

    该JNI库需要结合OpenCV使用,因此仍然需要调用OpenCV4Anroid库,调用的方法参见 OpenCV文档。需等待OpenCV库加载完毕后才能加载我们编写的库。2.4.5中应该不用添加initial_nonfree()这个函数了。运行能够成功。
[java] view
plain
 copy

  1. private BaseLoaderCallback mLoaderCallback = new BaseLoaderCallback(this) {
  2. @Override
  3. public void onManagerConnected(int status) {
  4. switch (status) {
  5. case LoaderCallbackInterface.SUCCESS:
  6. {
  7. Log.i(Unity.TAG,"OpenCV loaded successfully");
  8. System.loadLibrary("nonfree");
  9. System.loadLibrary("opencv_java");
  10. System.loadLibrary("Surf");
  11. isOpenCVLoad = true;
  12. } break;
  13. default:
  14. {
  15. Log.i(Unity.TAG,"OpenCV loaded Failed!");
  16. super.onManagerConnected(status);
  17. } break;
  18. }
  19. }
  20. };

一定要顺序加载,否则会报错。至此就能够在JAVA中使用Native函数了。


网上这方面资料很少,希望对大家有所帮助。

最新文章

  1. python 学习(json)(转)
  2. HTML5 十大新特性(七)——拖放API
  3. C#扇形的绘制与Hittest交互、图种制作
  4. c c++怎么判断一个字符串中是否含有汉字
  5. SCU3185 Black and white(二分图最大点权独立集)
  6. LeetCode----67. Add Binary(java)
  7. GSM BTS Hacking: 利用BladeRF和开源BTS 5搭建基站
  8. MVC项目页面获取控制器的信息
  9. hdoj 2546 饭卡
  10. SDWebImage 清除缓存
  11. win8下在microsoft visual studio 2012利用ODP.NET连接ORACLE 12c
  12. 浅谈linux读写同步机制RCU
  13. 强连通分量tarjan缩点——POJ2186 Popular Cows
  14. Thinking in Java 第二章学习笔记
  15. asp.net mvc 5 关闭xss过滤
  16. (Python3 自定义函数实现数字金字塔 代码
  17. edgedb 基本试用
  18. React面试题
  19. C#-异常处理(十四)
  20. 【python练习题】程序12

热门文章

  1. 【MatConvNet】配置GPU
  2. easyui datagrid 加载静态文件中的json数据
  3. c# winform中预防窗体重复打开
  4. Node 文件上传,ZIP
  5. 安装mysql时,提示This application requires .NET framework 4.0问题
  6. SAP建数据库索引
  7. 【Windows核心编程】一个使用内存映射文件进行进程间通信的例子
  8. windows定时计划备份MySql
  9. MongoDB 倾向于将数据都放在一个 Collection 下吗?
  10. NOIP2015_提高组Day2_3_运输计划