Android NDK學習筆記:Android Studio3.1+CMAKE+OpenCV3.4配置

 


1 前言

  Android Studio在2.2版本更新之後加入了CMAKE方式配置NDK的方法,這大大簡化了之前通過Android.mk和Application.mk兩個本地配置文件進行NDK開發的方式。這種方法在後續更新的版本中不斷增強,越來越好用,越來越不會出問題。本文基於Android Studio3.1的版本進行配置,使用CMAKE的配置方式配置OpenCV最新版(截止發文時間是OpenCV 3.4.1),並在最後給出一個灰度轉換的測試Demo。

  如果你想通過Android NDK開發配置OpenCV,本文或許對你有用。

2 準備工作

2.1 開發環境

  • Java 8 u161
  • Windows 10 Enterprise 64bit
  • Android Studio 3.1
  • OpenCV for Android 3.4.1
  • Android NDK 16.1.4479499
  • Gradle 4.4
  • CMAKE


注意: NDK更迭比較快,之前一直用14R版本,配置最新版的OpenCV,貌似老出一些莫名其妙的bug,後來更新16R之後,bug莫名其妙的消失了,貌似新版本更強大,推薦用最新版。Gradle腳本大部分是AS自動配置的,一般會自動更新,但如果在update推送彈窗中點了ignore之後,會停止推送,推薦使用最新版本。

2.2 下載相關文件

OpenCV

  OpenCV可以直接在官網上下,官方網址爲:https://opencv.org/releases.html

  選擇Android pack版本即可,大約310MB,由於服務器在國外,下載速度可能會比較慢,使用迅雷要比FireFox自帶的下載器快。
這裏寫圖片描述
NDK

  NDK並不會隨着Android SDK自動下載,需要手動配置——
打開AS -> Settings -> Appearance & Behavior -> System Settings -> Android SDK ->SDK Tools 勾選 NDK,如果CMAKE沒有勾選,也要勾選。

這裏寫圖片描述

  至此,準備工作結束。

3 OpenCV for Android的配置

3.1 創建項目

  創建一個名爲OpenCVTest的Android project,注意勾選include C++選項即可。
這裏寫圖片描述

這裏寫圖片描述

3.2 OpenCV相關文件

  • include文件
      在下載好的OpenCV壓縮包中,打開路徑下的.\opencv-3.4.1-android-sdk\OpenCV-android-sdk\sdk\native\jni 有一個include文件夾,把這個文件夾複製粘貼至我們的OpenCVTest項目中,路徑爲src/main/cpp
  • jni文件
      然後是動態庫(.so文件),打開路徑下的.\opencv-3.4.1-android-sdk\OpenCV-android-sdk\sdk\native ,有一個libs 文件夾,這個文件夾裏面是所有版本的abi的so文件。複製粘貼到我們的項目中,路徑爲src/main/jniLibs 這個文件夾需要自己手動去創建。

注意:
  1. 無論是include還是libs的路徑都可以自定義,習慣上是這樣放,但其實只要在之後的CMakeList配置文件裏面設置正確就沒有問題。
  2. 值得一提的是,OpenCV在最新版本中把動態庫和靜態庫分開了,分別放在libs和staticlbs兩個文件夾中,之前是放在一個文件夾裏的。我們測試Demo僅需要動態庫和頭文件即可。

  最後配置好之後文件結構如圖所示:
這裏寫圖片描述

3.3 配置文件

3.3.1 build.gradle文件

  在android 節點中添加如下代碼:


 
  1. sourceSets{

  2. main{

  3. jniLibs.srcDirs = ['src/main/jniLibs/libs']

  4. }

  5. }

  •  

  這一步設置了動態鏈接庫的路徑地址,用於項目構建時,Native尋找和鏈接相關的so文件。

  然後在Android.defaultConfig.externalNativeBuild 的節點內增加一行過濾器,如下:

abiFilters 'x86', 'x86_64', 'armeabi', 'armeabi-v7a', 'arm64-v8a', 'mips', 'mips64'
  •  

  .so文件依賴於硬件環境,不同的CPU架構對應不同的.so文件,abiFilters關鍵字能夠指定Android 所支持的CPU架構,一般是以上7種,最終這些.so文件會被打包進APK,所以可以根據自己的項目進行選擇,比如在AS模擬器上開發APP選一個 x86 就可以了,如果是手機端,一般是arm架構,選 armeabi-v7a 即可。

  最終的build.gradle文件如下:


 
  1. apply plugin: 'com.android.application'

  2.  
  3. android {

  4. compileSdkVersion 26

  5. defaultConfig {

  6. applicationId "com.example.videomedicine.opencvtest"

  7. minSdkVersion 24

  8. targetSdkVersion 26

  9. versionCode 1

  10. versionName "1.0"

  11. testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"

  12. externalNativeBuild {

  13. cmake {

  14. cppFlags "-std=c++11 -frtti -fexceptions"

  15. abiFilters 'x86'

  16. }

  17. }

  18. ndk{

  19. abiFilters 'x86'

  20. }

  21. }

  22. sourceSets{

  23. main{

  24. jniLibs.srcDirs = ['src/main/jniLibs/libs']

  25. }

  26. }

  27. buildTypes {

  28. release {

  29. minifyEnabled false

  30. proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'

  31. }

  32. }

  33. externalNativeBuild {

  34. cmake {

  35. path "CMakeLists.txt"

  36. }

  37. }

  38. }

  39.  
  40. dependencies {

  41. implementation fileTree(dir: 'libs', include: ['*.jar'])

  42. implementation 'com.android.support:appcompat-v7:26.1.0'

  43. implementation 'com.android.support.constraint:constraint-layout:1.0.2'

  44. testImplementation 'junit:junit:4.12'

  45. androidTestImplementation 'com.android.support.test:runner:1.0.1'

  46. androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.1'

  47. }

  配置完之後,點擊Sysn Now 。

3.3.2 CMakeList.txt文件

  這個文件也是NDK開發最最最關鍵的文件,AS採用CMake腳本語法配置C編譯器的環境,如果你之前有過使用CMAKE的經驗,或許這並非難題,但對於初學者而言,CMAKE的腳本語法,還是略過於生澀,而且AS對該文件的配置並不友好,居然沒有代碼提示,於是不得不查很多文檔。但好在,NDK的開發大多不是大型的C++項目,也不太需要過於複雜的設置(比如OpenCV源代碼的CMAKE文件,大約有幾千行的樣子 Orz~)

  OpenCV配置CMakeList文件的方式如下:


 
  1. # For more information about using CMake with Android Studio, read the

  2. # documentation: https://d.android.com/studio/projects/add-native-code.html

  3.  
  4. # 設置CMAKE的版本號

  5. cmake_minimum_required(VERSION 3.4.1)

  6.  
  7. # 設置include文件夾的地址

  8. include_directories(${CMAKE_SOURCE_DIR}/src/main/cpp/include)

  9.  
  10. # 設置opencv的動態庫

  11. add_library(libopencv_java3 SHARED IMPORTED)

  12. set_target_properties(libopencv_java3 PROPERTIES IMPORTED_LOCATION

  13. ${CMAKE_SOURCE_DIR}/src/main/jniLibs/libs/${ANDROID_ABI}/libopencv_java3.so)

  14.  
  15. add_library( # Sets the name of the library.

  16. native-lib

  17.  
  18. # Sets the library as a shared library.

  19. SHARED

  20.  
  21. # Provides a relative path to your source file(s).

  22. src/main/cpp/native-lib.cpp )

  23.  
  24. find_library( # Sets the name of the path variable.

  25. log-lib

  26.  
  27. # Specifies the name of the NDK library that

  28. # you want CMake to locate.

  29. log )

  30.  
  31. target_link_libraries( # Specifies the target library.

  32. native-lib libopencv_java3

  33.  
  34. # Links the target library to the log library

  35. # included in the NDK.

  36. ${log-lib} )

include_directories 函數設置了include文件夾的路徑
add_library 函數設置庫名和類型,其中libopencv_java3 是用戶自定義的變量名,前後保持一致即可,SHARE 表示引入的庫是動態鏈接庫
set_target_properties 設置了OpenCV的動態鏈接庫的路徑
target_link_libraries 具有依賴關係的動態庫鏈接到指定目標上,鏈接順序需符合gcc鏈接規則,這裏我們把libopencv_java3和log鏈接到了native-lib上。

  更詳細的CMAKE配置語法,可以參考CMAKE官方文檔,我們這裏僅配置動態鏈接庫,以上四個函數足夠了。

3.3.3 資源文件

  把一張圖片放入src/main/res/drawable 下,作爲我們的測試圖,注意,資源文件首字母不能大寫。
這裏寫圖片描述
  至此,配置工作結束。

4 測試Demo:灰度化圖片

4.1 代碼

  • MainActivity.java文件

 
  1. package com.example.videomedicine.opencvtest;

  2.  
  3. import android.graphics.Bitmap;

  4. import android.graphics.BitmapFactory;

  5. import android.support.v7.app.AppCompatActivity;

  6. import android.os.Bundle;

  7. import android.view.View;

  8. import android.widget.Button;

  9. import android.widget.ImageView;

  10.  
  11.  
  12. public class MainActivity extends AppCompatActivity implements View.OnClickListener{

  13.  
  14. // Used to load the 'native-lib' library on application startup.

  15. static {

  16. System.loadLibrary("native-lib");

  17. }

  18.  
  19. private Button btn_1;

  20. private Button btn_2;

  21. private ImageView imageView;

  22. private Bitmap bitmap;

  23.  
  24. @Override

  25. protected void onCreate(Bundle savedInstanceState) {

  26. super.onCreate(savedInstanceState);

  27. setContentView(R.layout.activity_main);

  28. btn_1 = (Button)findViewById(R.id.button_1);

  29. imageView = (ImageView)findViewById(R.id.image);

  30. bitmap = BitmapFactory.decodeResource(getResources(),R.drawable.luffy);

  31. imageView.setImageBitmap(bitmap);

  32. btn_1.setOnClickListener(this);

  33.  
  34. btn_2 = (Button)findViewById(R.id.button_2);

  35. btn_2.setOnClickListener(this);

  36. }

  37. public void showImage(){

  38. bitmap = BitmapFactory.decodeResource(getResources(),R.drawable.luffy);

  39. imageView.setImageBitmap(bitmap);

  40. }

  41.  
  42. public void gray(){

  43. int w = bitmap.getWidth();

  44. int h = bitmap.getHeight();

  45. int[] piexls = new int[w*h];

  46. bitmap.getPixels(piexls,0,w,0,0,w,h);

  47. int[] resultData =Bitmap2Grey(piexls,w,h);

  48. Bitmap resultImage = Bitmap.createBitmap(w,h, Bitmap.Config.ARGB_8888);

  49. resultImage.setPixels(resultData,0,w,0,0,w,h);

  50. imageView.setImageBitmap(resultImage);

  51. }

  52.  
  53. @Override

  54. public void onClick(View view){

  55. switch(view.getId()){

  56. case R.id.button_1:showImage();break;

  57. case R.id.button_2:gray();break;

  58. }

  59. }

  60.  
  61. /**

  62. * A native method that is implemented by the 'native-lib' native library,

  63. * which is packaged with this application.

  64. */

  65. public native int[] Bitmap2Grey(int[] pixels,int w,int h);

  66.  
  67. @Override

  68. public void onResume(){

  69. super.onResume();

  70. }

  71. }

  • native-lib.cpp文件

 
  1. #include <jni.h>

  2. #include<opencv2/opencv.hpp>

  3. #include<iostream>

  4. using namespace cv;

  5. using namespace std;

  6.  
  7. extern "C" JNIEXPORT jintArray

  8.  
  9. JNICALL

  10. Java_com_example_videomedicine_opencvtest_MainActivity_Bitmap2Grey(

  11. JNIEnv *env,

  12. jobject /* this */,jintArray buf,jint w,jint h) {

  13. jint *cbuf;

  14. jboolean ptfalse = false;

  15. cbuf = env->GetIntArrayElements(buf, &ptfalse);

  16. if(cbuf == NULL){

  17. return 0;

  18. }

  19.  
  20. Mat imgData(h, w, CV_8UC4, (unsigned char*)cbuf);

  21. // 注意,Android的Bitmap是ARGB四通道,而不是RGB三通道

  22. cvtColor(imgData,imgData,CV_BGRA2GRAY);

  23. cvtColor(imgData,imgData,CV_GRAY2BGRA);

  24.  
  25. int size=w * h;

  26. jintArray result = env->NewIntArray(size);

  27. env->SetIntArrayRegion(result, 0, size, (jint*)imgData.data);

  28. env->ReleaseIntArrayElements(buf, cbuf, 0);

  29. return result;

  30. }

  • activity_main.xml

4.2 程序結果

程序結果

5 參考鏈接

[1]. Android Studio 2.3利用CMAKE進行OpenCV 3.2的NDK開發
[2]. CMake常用語法總結
[3]. android使用CMake進行jni編寫遇到的一些問題
[4]. 設置Button多個監聽事件
[5]. androidstudio視頻錄製和截圖功能
[6]. CMAKE官方文檔

版權聲明:實不相瞞,我也想成爲大佬 https://blog.csdn.net/CV_Jason/article/details/79758823

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章