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函数了。


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

在OpenCV for Android 2.4.5中使用SURF(nonfree module)的更多相关文章

  1. Android studio 一个项目中添加两个module遇到的bug

    1.在一个Android studio中,我添加了一个模块,然后就是各种bug 找到到R 是在module 名上面 右键 Make Module '模块名' 经过各种google 的时候发现了 htt ...

  2. !! 2.对十份论文和报告中的关于OpenCV和Android NDK开发的总结

    http://hujiaweibujidao.github.io/blog/2013/11/18/android-ndk-and-opencv-development-3/ Android Ndk a ...

  3. NDk编译opencv for Android,并引用在Unity3d游戏中的一般步骤

    本文使用:Unity3d + opencv + Android Unity3d中可以调用opencv 编译好的.so 动态库,在生成Android apk时可以正常运行.   因为Android系统是 ...

  4. OpenCV在Android平台上的应用

    今年8月份, OpenCV 2.3.1发布了. 虽然从2.2开始, OpenCV就号称支持Android平台, 但真正能让OpenCV在Android上运行起来还是在2.3.1版本上. 在这个版本上, ...

  5. opencv for android sample导入有误

    我们下载好opencv for android 后导入eclipse的时候发现人脸检测还有一个sample项目会有小叉,但是好像没有文件有问题.这时我们该怎么办呢? 在window中: 我们右键选择p ...

  6. 【OpenCV for Android】Android Studio JNI和NDK配置及采坑记录

    在配置好Android studio的OpenCV环境后,我们就可以通过Java代码调用OpenCV的API了,但是在通常情况下,用Java代码编写图像处理算法的运行效率是没有C++代码高的,在应用层 ...

  7. 编译并使用带有OpenCL模块的OpenCV for android SDK

    OpenCV Android SDK中提供的静态.动态库是不支持OpenCL加速的,如果在程序中调用OpenCL相关函数,编译时不会报错,但运行时logcat会输出如下信息,提示OpenCL函数不可用 ...

  8. 交叉编译OpenCV的Android版本

    交叉编译OpenCV的Android版本 OpenCV作为一个强大的图像处理库,在Android上也有强大的应用. OpenCV官网提供了SDK的下载,可以直接下载使用 OpenCV官网地址:http ...

  9. OpenCV 4 Android

    OpenCV4Android Want a Quick Start link? Use this tutorial: “OpenCV for Android SDK”. 想要快速开始吗?使用这个教程: ...

随机推荐

  1. LeetCode(15)题解--3Sum

    https://leetcode.com/problems/3sum/ 题目: Given an array S of n integers, are there elements a, b, c i ...

  2. vs2005 未能完成操作。未指定的错误

    具体解决过程是这样的: 1.先把.vcproj 文件剪切到其他地方 2.打开.sln,报错->点“确定”->再点“确定” 3.把 .vcproj 文件 放回来,在vs2008右边的“解决方 ...

  3. FFMPEG SDK流媒体开发2---分离.mp4等输入流音视频而且进行解码输出

    对于FFMPEG SDK  提供的Demuxing 为我们实现多路复用  提供了非常多方便,以下的案案例 实现的是 分离一个媒体文件的音频 视频流 而且解码输出 到  不同的文件里. 对于音频被还原回 ...

  4. openjdk源码目录结构

    1 openjdk源码 http://hg.openjdk.java.net 选择jdk8u这个project, 然后选择jdk8u20这个repository. 2 目录结构 corba: comm ...

  5. cc、gcc、g++区别

    gcc是C编译器:g++是C++编译器:linux下cc一般是一个符号连接,指向gcc:gcc和g++都是GUN(组织)的编译器.而CC则一般是makefile里面的一个名字,即宏定义,嘿,因为Lin ...

  6. [usaco2009nov]奶牛的图片

    Farmer John希望给他的N(1<=N<=100,000)只奶牛拍照片,这样他就可以向他的朋友炫耀他的奶牛.这N只奶牛被标号为1..N.在照相的那一天,奶牛们排成了一排.其中第i个位 ...

  7. window上安装rabbitMQ

    win7下安装RabbitMQ http://my.oschina.net/ydsakyclguozi/blog/528835?fromerr=q7m1OxxF 前辈总结的特别详细.

  8. loj#2340. 「WC2018」州区划分

    FWT&&FMT板子 #include<cstdio> #include<iostream> #include<cstring> #include& ...

  9. 记录下linux好用的命令

    http://mp.weixin.qq.com/s/LU1iAWfssv1x-QMX6hJqmQ

  10. xml文件的解析(用dom4j解析)

    有个第三方的包,用来解析.xml文件比较方便,它是DOM4J.由于是第三方的,所以要使用它就要先下载,并包含进来.步骤: 1.打开dom4j官网,这里下载最新版dom4j-2.1.1.jar. 2.新 ...