OpenCV - Android Studio 2.2 中利用CAMKE進行OpenCV的NDK開發

  我在http://www.cnblogs.com/fx-blog/p/8206737.html一文中提到了如何在Android Studio中Java層導入OpenCV(包含opencv_contrib部分),可是這僅僅是Java層的導入,隨着學習的深刻,咱們能夠漸漸的發現OpenCV庫對Java的支持不是很給力,好比我使用SIFT算法時,通常提取出來的特徵點有一萬多個,這其中包含了大量的無效特徵點,若是我想指定特徵點的數目,好比說500個(通過測試,Java中利用OpenCV提取特徵點默認爲500),可是OpenCV Java庫中並無提供這樣的方法(或者是我沒有發現,若是有大神知曉,還望告知)。可是在C++中,是能夠給SIFT指定特徵點的,這裏面就須要有一個在Java中調用C++的功能,Java中咱們知道能夠利用JNI或者JNA解決,那麼在Androd中咱們即可以利用NDK作到Java對C++的調用。html

  在Android Studio 2.2以前,咱們一般是經過Android.mk和Application.mk兩個文件設置本地開發;可是在Android Studio 2.2以後的版本,加入了利用CMAKE配置編譯NDK項目的方法,這無疑是一個很好的消息,咱們終於能夠拋棄以前那種繁瑣的方法啦,本篇文章只要講的就是在Android Studio中利用CMAKE進行OpenCV的NDK開發。java

準備工做:

  首先,咱們須要在Android Studio中配置CMAKE、NDK工具,打開Android Studio 2.2,點擊按鈕打開SDK Manager,在SDK Platforms中選擇你所須要的Android版本,這裏我使用的是Android 7.0。android

    

   在SDK Tools中選擇紅框標出的部分(這裏推薦SDK Manager當中提供的NDK,NDK安裝好後路徑爲<Android SDK Path>\ndk-bundle):c++

    

正式開始:

  建立一個新項目,在建立的過程當中,咱們須要勾選Include C++ Support,以後的步驟默認便可,能夠與http://www.cnblogs.com/fx-blog/p/8206737.html相應正,寫的不是很好,你們見諒。算法

  項目建立成功以後,會自動在app\src\main下創建一個名爲cpp的文件夾,其中包含一個native-lib.cpp文件。同時,在app目錄下會多出一個CMakeLists.txt文件,Android Studio調用CMAKE利用該文件來協調C++代碼的編譯(默認使用Clang編譯),並將產生的.so文件提供給apk文件的打包過程。app

  而後須要在Java層導入OpenCV,OpenCV 3.2 Android SDK能夠選用以前編譯好的庫,下載地址:https://pan.baidu.com/s/1kVOejLt;在Android Studio中點擊File -> New… -> Import Module,而後在Source Directory中選取<OpenCV 3.2 Android SDK>\sdk\java目錄,這時Module Name就會自動變成「openCVLibrary320」,以後的步驟採用默認設置便可。ide

  剛剛導入OpenCV包以後,Android Studio會嘗試自動編譯,因爲其默認的build.gradle文件設置並不適合最新版本,因此會報錯。修改openCVLibrary320\build.gradle爲以下內容就會糾正這些錯誤(紅框標出的部分須要與app\build.gradle一致)。函數

    

   點擊File -> Project Structure,在左邊的Modules中點擊「app」,而後點擊右邊的加號,再選擇Module Dependency,而後在彈出框中選擇:openCVLibrary320。這樣,就爲咱們的項目app在Java層上添加了OpenCV支持(其中library是我使用的另一個庫,不用理會)。工具

    

接下來就是配置利用CMAKE配置OpenCV應用了:

  首先,須要將應用OpenCV C++所須要頭文件和庫文件所有複製到項目中,將<OpenCV 3.2 Android SDK>\sdk\native\jni\include文件夾複製到app\src\main\cpp當中,把<OpenCV 3.2 Android SDK>\sdk\native\libs文件夾複製到app\src\main當中,並將文件夾重命名爲jniLibs。學習

    

  而後將 app\build.gradle修改成(各位看官能夠對比修改):

apply plugin: 'com.android.application'

android {
    compileSdkVersion 25
    buildToolsVersion "27.0.1"
    defaultConfig {
        applicationId "com.example.demo02"
        minSdkVersion 15
        targetSdkVersion 25
        versionCode 1
        versionName "1.0"
        testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
        externalNativeBuild {
            cmake {
                cppFlags "-std=c++11", "-frtti", "-fexceptions"
                abiFilters 'x86', 'x86_64', 'armeabi', 'armeabi-v7a', 'arm64-v8a', 'mips', 'mips64'
            }
        }
    }
    sourceSets {
        main {
            jniLibs.srcDirs = ['src/main/jniLibs']
        }
    }
    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
        }
    }
    externalNativeBuild {
        cmake {
            path "CMakeLists.txt"
        }
    }
}

dependencies {
    compile 'com.android.support:design:25.3.1'
    compile fileTree(include: ['*.jar'], dir: 'libs')
    androidTestCompile('com.android.support.test.espresso:espresso-core:2.2.2', {
        exclude group: 'com.android.support', module: 'support-annotations'
    })
    compile 'com.android.support:appcompat-v7:25.3.1'
    testCompile 'junit:junit:4.12'
    compile project(':openCVLibrary320')
}

  app\CMakeLists.txt修改成:

cmake_minimum_required(VERSION 3.4.1)

set(CMAKE_VERBOSE_MAKEFILE on)
set(ocvlibs "${CMAKE_SOURCE_DIR}/src/main/jniLibs")
include_directories(${CMAKE_SOURCE_DIR}/src/main/cpp/include)

add_library(libopencv_java3 SHARED IMPORTED )
set_target_properties(libopencv_java3 PROPERTIES
                      IMPORTED_LOCATION "${ocvlibs}/${ANDROID_ABI}/libopencv_java3.so")

add_library( # Sets the name of the library.
             native-lib

             # Sets the library as a shared library.
             SHARED

             # Provides a relative path to your source file(s).
             src/main/cpp/native-lib.cpp )

find_library( # Sets the name of the path variable.
              log-lib

              # Specifies the name of the NDK library that
              # you want CMake to locate.
              log )

target_link_libraries( # Specifies the target library.
                       native-lib android log libopencv_java3

                       # Links the target library to the log library
                       # included in the NDK.
                       ${log-lib} )

  配置部分就此完成了,貼一下關鍵部分的代碼吧,NDK幫助類OpenCVNDKHelper:

1 package com.example.ndk;
2 
3 public class OpenCVNDKHelper {
4     static {
5         System.loadLibrary("native-lib");
6     }
7     public native static void detectFeatures(long srcMatAddr, long dstMatAddr);
8 }

  C++文件native-lib.cpp:

 1 #include <jni.h>
 2 #include <string>
 3 #include <opencv2/core/core.hpp>
 4 #include <opencv2/features2d/features2d.hpp>
 5 #include <opencv2/xfeatures2d/nonfree.hpp>
 6 
 7 using namespace std;
 8 using namespace cv;
 9 using namespace xfeatures2d;
10 
11 extern "C"
12 {
13     JNIEXPORT void JNICALL Java_com_example_ndk_OpenCVNDKHelper_detectFeatures
14         (JNIEnv *, jclass, jlong srcMatAddr, jlong dstMatAddr) {
15         Mat* srcMat = (Mat*)srcMatAddr;
16         Mat* descriptors = (Mat*)dstMatAddr;
17         vector<KeyPoint> Keypoints;
18         Ptr<SIFT> detector = SIFT::create(1000);
19         detector->detect(*srcMat, Keypoints);
20         detector->compute(*srcMat, Keypoints, *descriptors);
21     }
22 }

  這段代碼主要的做用就是提取Sift特徵(這裏我就不介紹生成.h文件的過程了,只要掌握.cpp文件中的函數命名的方法,這個過程是能夠省略的啦)。

  補充一點:這個代碼其實也解釋瞭如何在Java中將Mat傳遞到C++中的方法,在java中的調用以下(src和srcMat爲Mat對象):

OpenCVNDKHelper.detectFeatures(src.getNativeObjAddr(), srcMat.getNativeObjAddr());

 

  PS:文章中使用的jniLibs是直接拷貝到目錄中的,這個項目的存儲空間就比較大了,大約1G左右,每次新建項目都須要從新拷貝,你們也能夠用軟連接或者絕對路徑代替,這裏就很少介紹啦。可是最終生成的APK文件大小是差很少的。

  over~~~

 

跑下題:

  寫博客的過程當中,老爸突然找我視頻聊天,咱們聊了蠻多,主要就是催我找個女友,難道95後也要被家長催找女友了嗎,感受從我6月份畢業以來(可能更早),這個話題就沒有停過啊,腦補過年回家的場面。。。

相關文章
相關標籤/搜索