zoukankan      html  css  js  c++  java
  • 在OpenCV for Android 2.4.5中使用SURF(nonfree module)

    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 Filesopencv,OpenCV4Android路径为 E:My DocumentsAndroidOpenCV-2.4.5-android-sdk。

       nonfree module 的源代码存储在C:Program Filesopencvmodules onfreesrc中,头文件存储在C:Program Filesopencvmodules onfreeincludeopencv2 onfree中。

        编译的过程中需要两个头文件,一个为nonfree.hpp,一个为features2d.hpp,将这两个头文件拷贝至OpenCV4Android SDK的include文件夹下:E:My DocumentsAndroidOpenCV-2.4.5-android-sdksdk ativejniincludeopencv2 onfree文件夹下。

        为了编译库文件,我们还需要nonfree module的源代码。源代码为C:Program Filesopencvmodules onfreesrc中的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-sdksdk ativejniOpenCV.mk  
    6.   
    7. LOCAL_C_INCLUDES:= ../OpenCV-2.4.5-android-sdk/sdk/native/jni/include  
    8. LOCAL_MODULE    := nonfree  
    9. LOCAL_CFLAGS    := -Werror -O3 -ffast-math  
    10. LOCAL_LDLIBS    += -llog  
    11. LOCAL_SRC_FILES := nonfree_init.cpp   
    12.  precomp.cpp   
    13.  sift.cpp   
    14.  surf.cpp  
    15. 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.   
    7. using namespace cv;  
    8. using namespace std;  
    9.   
    10. void KeyPoint2Mat(vector<KeyPoint>& keypoints, Mat& mat)  
    11. {  
    12.     int i = 0;  
    13.     int size = keypoints.size();  
    14.     mat.create(size,1,CV_32FC(7));  
    15.     float* buff = (float*)mat.data;  
    16.     for(i=0;i<size;i++)  
    17.     {  
    18.         KeyPoint kp = keypoints[i];  
    19.         buff[7*i+0] = kp.pt.x;  
    20.         buff[7*i+1] = kp.pt.y;  
    21.         buff[7*i+2] = kp.size;  
    22.         buff[7*i+3] = kp.angle;  
    23.         buff[7*i+4] = kp.response;  
    24.         buff[7*i+5] = kp.octave;  
    25.         buff[7*i+6] = kp.class_id;  
    26.     }  
    27. }  
    28.   
    29. JNIEXPORT void JNICALL Java_com_ruif_nativeSurf_NativeSurf_SurfDetect  
    30.   (JNIEnv *, jclass, jlong imgObj, jlong keyPointsObj, jlong descriptorObj)  
    31. {  
    32.     //Create Mats  
    33.     Mat* img = (Mat*)imgObj;                //img  
    34.     Mat* descriptor = (Mat*)descriptorObj;  //Descriptor  
    35.   
    36.     Mat* keyPointsMat = (Mat*)keyPointsObj;  
    37.     vector<KeyPoint> keyPointvec;  
    38.     SurfFeatureDetector surfDetector(1000);  
    39.     SurfDescriptorExtractor surfExtractor;  
    40.     surfDetector.detect(*img,keyPointvec);  
    41.     surfExtractor.compute(*img,keyPointvec,*descriptor);  
    42.     KeyPoint2Mat(keyPointvec,*keyPointsMat);  
    43. }  

    编写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.   
    7. include $(CLEAR_VARS)  
    8. LOCAL_MODULE    := opencv_java_prebuilt  
    9. LOCAL_SRC_FILES := libopencv_java.so  
    10. include $(PREBUILT_SHARED_LIBRARY)  
    11.   
    12. include $(CLEAR_VARS)  
    13. include ../OpenCV-2.4.5-android-sdk/sdk/native/jni/OpenCV.mk  
    14. LOCAL_MODULE    := Surf  
    15. LOCAL_CFLAGS    := -Werror -O3 -ffast-math  
    16. LOCAL_LDLIBS    += -llog -ldl   
    17. LOCAL_SHARED_LIBRARIES := nonfree opencv_java_prebuilt  
    18. LOCAL_SRC_FILES := Surf.cpp  
    19. 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函数了。

    网上这方面资料很少,希望对大家有所帮助。
  • 相关阅读:
    Android 锁屏 临时屏蔽
    单编译framework相关模块
    02
    pad 强制加载 Hdpi资源 (2.3 dpi < 240)
    设置form的默认按钮
    How to Be a Good Graduate Student
    我怕你们急于求成
    希腊字母读音表
    数据库札记(二)
    Ubuntu 9.04下jdk的安装与配置
  • 原文地址:https://www.cnblogs.com/nafio/p/9137281.html
Copyright © 2011-2022 走看看