AndroidStudio3.0 配置Ndk和OpenCV4.0

起因

我這個人喜歡沒事就去招聘網站刷刷招聘信息,也不全是為了找工作峭咒,因為我個人覺得從招聘信息中税弃,我可以了解到行業(yè)的大方向,了解現(xiàn)在市場上的用人單位都需要一個程序員具備什么樣的素質和技能讹语,程序員這個職業(yè)钙皮,一輩子都需要學習和進步蜂科,如果技術固步自封顽决,不深入底層或者不拓展技術視野短条,那么成為一個十年開發(fā)一年經驗的程序員就不遠了,于是我打開了招聘網站才菠,隨便刷了刷就發(fā)現(xiàn)了問題茸时。

問題就是,對于初級程序員赋访,市面上的公司要求不多可都,除了BAT,基本上回寫bug就行蚓耽,但是如果到了一定歲數(shù)渠牲,你想找一些中高級的Android程序員的工作時,市面上的公司就會有一些特殊的需求了步悠,這需求其實就是門檻签杈,現(xiàn)在我貼幾張招聘簡章的截圖,大家看看:

1.png
2.png
3.png

需求各式各樣鼎兽,但是我發(fā)現(xiàn)一個共同的問題答姥,都需要NDK或者C/C++的技能,看來NDK是未來一個學習的熱點和趨勢谚咬,不僅音視頻鹦付、各種硬件的調用、核心算法的實現(xiàn)以及一些照片的處理都需要用到JNI開發(fā)择卦。

可能你會問了敲长,我是個Java程序員,學這C++開發(fā)相當于跨行了秉继,其實也對潘明,Android都沒整明白呢,就跑去弄什么NDK,但是還是那句老話秕噪,技多不壓身钳降,多學習學習接觸一下肯定沒壞處,最起碼簡單的NDK開發(fā)框架會搭建腌巾,知道一些基本的配置和用法遂填,就算以后項目里用到了基礎的配置和調試也能用得到啊。

開始搭建NDK

我用的AndroidStudio的版本是3.2.1澈蝙,在創(chuàng)建Project時如果選中了支持C++吓坚,AndroidStudio就會自動去網上幫我們下載NDK的開發(fā)環(huán)境,具體情況可以通過File->Setting->Appearance & Behavior->System Setting ->Android SDK->SDK Tools查看灯荧,主要看的就是NDK和CMake兩項是否安裝:


8.png

之后就新建項目礁击,選擇C++選項:


5.png

然后選擇SDK版本,這個隨意:

6.png

在向導的 Customize C++ Support 部分,您可以使用下列選項自定義項目:

  • C++ Standard:使用下拉列表選擇您希望使用哪種 C++ 標準哆窿。選擇 Toolchain Default 會使用默認的 CMake 設置链烈。

  • Exceptions Support:如果您希望啟用對 C++ 異常處理的支持,請選中此復選框挚躯。如果啟用此復選框强衡,Android Studio 會將 -fexceptions 標志添加到模塊級 build.gradle 文件的 cppFlags 中,Gradle 會將其傳遞到 CMake码荔。

  • Runtime Type Information Support:如果您希望支持 RTTI漩勤,請選中此復選框。如果啟用此復選框缩搅,Android Studio 會將 -frtti 標志添加到模塊級 build.gradle 文件的 cppFlags 中越败,Gradle 會將其傳遞到 CMake。

7.png

之后就可以看到自己項目的目錄結構和平時的Android項目有所不同:

9.png

仔細觀察可以發(fā)現(xiàn)硼瓣,多了一個cpp包眉尸,而且還多了個CMakeLists.txt,打開local.properties還會發(fā)現(xiàn)多了個參數(shù)巨双,其實就是ndk的指向路徑:

  ## This file must *NOT* be checked into Version Control Systems,
  # as it contains information specific to your local configuration.
  #
  # Location of the SDK. This is only used by Gradle.
  # For customization when using a Version Control System, please read the
  # header note.
  #Wed Apr 03 09:21:33 CST 2019
  ndk.dir=E\:\\AndroidSDK\\adt-bundle-windows-x86_64-20140321\\sdk\\ndk-bundle
  sdk.dir=E\:\\AndroidSDK\\adt-bundle-windows-x86_64-20140321\\sdk</pre>
  • 在 cpp 組中噪猾,您可以找到屬于項目的所有原生源文件、標頭和預構建庫筑累。對于新項目袱蜡,Android Studio 會創(chuàng)建一個示例 C++ 源文件 native-lib.cpp,并將其置于應用模塊的 src/main/cpp/ 目錄中慢宗。本示例代碼提供了一個簡單的 C++ 函數(shù) stringFromJNI()坪蚁,此函數(shù)可以返回字符串“Hello from C++”。

  • 在 External Build Files 組中镜沽,您可以找到 CMake 或 ndk-build 的構建腳本敏晤。與 build.gradle 文件指示 Gradle 如何構建應用一樣,CMake 和 ndk-build 需要一個構建腳本來了解如何構建您的原生庫缅茉。對于新項目嘴脾,Android Studio 會創(chuàng)建一個 CMake 構建腳本 CMakeLists.txt,并將其置于模塊的根目錄中蔬墩。

我們來看看這個文件:

有關使用CMake在Android Studio的更多信息,請閱讀文檔:https://d.android.com/studio/projects/add-native-code.html
?
好了译打,至此,我們的NDK就算是配置完成了拇颅,現(xiàn)在開始嘗試加入OpenCV奏司。

引入OpenCV

OpenCV是一個基于BSD許可(開源)發(fā)行的跨平臺計算機視覺庫,可以運行在Linux樟插、Windows韵洋、Android和Mac OS操作系統(tǒng)上竿刁。它輕量級而且高效——由一系列 C 函數(shù)和少量 C++ 類構成,同時提供了Python搪缨、Ruby食拜、MATLAB等語言的接口,實現(xiàn)了圖像處理和計算機視覺方面的很多通用算法勉吻。

知道了概念,現(xiàn)在我們來引入它

第一步:下載OpenCV的Android包

下載地址

10.png

最新版是4.0.1旅赢,然后我們點擊那個Android pack齿桃,會下載好一個壓縮包,解壓縮后是這樣的:
11.png

include文件

在下載好的OpenCV壓縮包中煮盼,打開路徑下的 OpenCV-android-sdk\sdk\native\jni 有一個include文件夾短纵,把這個文件夾復制粘貼至我們的OpenCVTest項目中,路徑為src/main/cpp

jni文件

然后是動態(tài)庫(.so文件)僵控,打開路徑下的 OpenCV-android-sdk\sdk\native 香到,有一個libs 文件夾,這個文件夾里面是所有版本的abi的so文件报破。復制粘貼到我們的項目中悠就,路徑為 src/main/jniLibs 這個文件夾需要自己手動去創(chuàng)建。

注意:

  1. 無論是include還是libs的路徑都可以自定義充易,習慣上是這樣放梗脾,但其實只要在之后的CMakeList配置文件里面設置正確就沒有問題。

  2. 值得一提的是盹靴,OpenCV在最新版本中把動態(tài)庫和靜態(tài)庫分開了炸茧,分別放在libs和staticlbs兩個文件夾中,之前是放在一個文件夾里的稿静。我們測試Demo僅需要動態(tài)庫和頭文件即可梭冠。

最后配置好之后文件結構如圖所示(注意,這是Project方式看的):


12.png

配置Gradle

最終的配置文件如下:

apply plugin: 'com.android.application'
  ?
  android {
      compileSdkVersion 28
      defaultConfig {
          applicationId "com.dhcc.www.ndkapplication"
          minSdkVersion 21
          targetSdkVersion 28
          versionCode 1
          versionName "1.0"
          externalNativeBuild {
              cmake {
                  cppFlags "-std=c++11 -frtti -fexceptions"
                  abiFilters 'armeabi-v7a'
              }
          }
          ndk{
              abiFilters 'armeabi-v7a'
          }
      }
      sourceSets{
          main{
              jniLibs.srcDirs = ['src/main/jniLibs/libs']
          }
      }
      buildTypes {
          release {
              minifyEnabled false
              proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
              ndk{
                  abiFilters 'armeabi-v7a'
              }
          }
      }
      externalNativeBuild {
          cmake {
              path "CMakeLists.txt"
          }
      }
  }
  ?
  dependencies {
      implementation fileTree(include: ['*.jar'], dir: 'libs')
      implementation 'com.android.support:appcompat-v7:28.0.0'
      implementation 'com.android.support.constraint:constraint-layout:1.1.3'
  }

這里有幾點要講一下:

externalNativeBuild {
              cmake {
                  cppFlags "-std=c++11 -frtti -fexceptions"
                  abiFilters 'armeabi-v7a'
              }
          }
          ndk{
              abiFilters 'armeabi-v7a'
          }
 abiFilters 'x86', 'x86_64', 'armeabi-v7a', 'arm64-v8a'</pre>

cppFlags是配置預處理的設置改备,abiFilters是控制系統(tǒng)查找.SO的文件類型的過濾器控漠,不同的CPU架構對應不同的.so文件,abiFilters關鍵字能夠指定Android 所支持的CPU架構悬钳,一般是以上4種润脸,最終這些.so文件會被打包進APK,所以可以根據(jù)自己的項目進行選擇他去,比如在AS模擬器上開發(fā)APP選一個 x86 就可以了毙驯,如果是手機端,一般是arm架構灾测,選 armeabi-v7a 即可爆价。

然后同步項目即可垦巴。

CMakeList.txt文件

這個文件也是NDK開發(fā)最最最關鍵的文件,AS采用CMake腳本語法配置C編譯器的環(huán)境铭段,如果你之前有過使用CMAKE的經驗骤宣,或許這并非難題,但對于初學者而言序愚,CMAKE的腳本語法憔披,還是略過于生澀,而且AS對該文件的配置并不友好爸吮,居然沒有代碼提示芬膝,于是不得不查很多文檔。但好在形娇,NDK的開發(fā)大多不是大型的C++項目锰霜,也不太需要過于復雜的設置(比如OpenCV源代碼的CMAKE文件,大約有幾千行的樣子 Orz~)

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

  # documentation: https://d.android.com/studio/projects/add-native-code.html
  ?
  # 設置CMAKE的版本號
  cmake_minimum_required(VERSION 3.4.1)
  ?
  # 設置include文件夾的地址
  include_directories(${CMAKE_SOURCE_DIR}/src/main/cpp/include)
  ?
  # 設置opencv的動態(tài)庫
  add_library(libopencv_java4 SHARED IMPORTED)
  set_target_properties(libopencv_java4 PROPERTIES IMPORTED_LOCATION
              ${CMAKE_SOURCE_DIR}/src/main/jniLibs/libs/${ANDROID_ABI}/libopencv_java4.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 libopencv_java4
  ?
                         # Links the target library to the log library
                         # included in the NDK.
                         ${log-lib} )

include_directories 函數(shù)設置了include文件夾的路徑 add_library 函數(shù)設置庫名和類型桐早,其中l(wèi)ibopencv_java3 是用戶自定義的變量名癣缅,前后保持一致即可,SHARE 表示引入的庫是動態(tài)鏈接庫 set_target_properties 設置了OpenCV的動態(tài)鏈接庫的路徑 target_link_libraries 具有依賴關系的動態(tài)庫鏈接到指定目標上哄酝,鏈接順序需符合gcc鏈接規(guī)則友存,這里我們把libopencv_java4log鏈接到了native-lib上。 更詳細的CMAKE配置語法陶衅,可以參考CMAKE官方文檔爬立,我們這里僅配置動態(tài)鏈接庫,以上四個函數(shù)足夠了万哪。

放入指定圖片

lufei.jpg

將上圖存儲后放入路徑:src/main/res/drawable 下進行測試

編寫測試Demo

MainActivity.java

      ?
      import android.support.v7.app.AppCompatActivity;
      import android.os.Bundle;
      import android.graphics.Bitmap;
      import android.graphics.BitmapFactory;
      import android.view.View;
      import android.widget.Button;
      import android.widget.ImageView;
      ?
      public class MainActivity extends AppCompatActivity  implements View.OnClickListener{
      ?
          // Used to load the 'native-lib' library on application startup.
          static {
              System.loadLibrary("native-lib");
          }
      ?
          private Button btn_1;
          private Button btn_2;
          private ImageView imageView;
          private Bitmap bitmap;
      ?
          @Override
          protected void onCreate(Bundle savedInstanceState) {
              super.onCreate(savedInstanceState);
              setContentView(R.layout.activity_main);
              btn_1 = (Button)findViewById(R.id.button_1);
              imageView = (ImageView)findViewById(R.id.image);
              bitmap = BitmapFactory.decodeResource(getResources(),R.drawable.lufei);
              imageView.setImageBitmap(bitmap);
              btn_1.setOnClickListener(this);
      ?
              btn_2 = (Button)findViewById(R.id.button_2);
              btn_2.setOnClickListener(this);
          }
          public void showImage(){
              bitmap = BitmapFactory.decodeResource(getResources(),R.drawable.lufei);
              imageView.setImageBitmap(bitmap);
          }
      ?
          public void gray(){
              int w = bitmap.getWidth();
              int h = bitmap.getHeight();
              int[] piexls = new int[w*h];
              bitmap.getPixels(piexls,0,w,0,0,w,h);
              int[] resultData =Bitmap2Grey(piexls,w,h);
              Bitmap resultImage = Bitmap.createBitmap(w,h, Bitmap.Config.ARGB_8888);
              resultImage.setPixels(resultData,0,w,0,0,w,h);
              imageView.setImageBitmap(resultImage);
          }
      ?
          @Override
          public void onClick(View view){
              switch(view.getId()){
                  case R.id.button_1:showImage();break;
                  case R.id.button_2:gray();break;
              }
          }
      ?
          /**
           * A native method that is implemented by the 'native-lib' native library,
           * which is packaged with this application.
           */
          public native int[] Bitmap2Grey(int[] pixels,int w,int h);
      ?
          @Override
          public void onResume(){
              super.onResume();
          }
      }

native-lib.cpp文件

 #include <jni.h>
          #include<opencv2/opencv.hpp>
          #include<iostream>
          using namespace cv;
          using namespace std;
          ?
          extern "C" JNIEXPORT jintArray
          ?
          JNICALL
          Java_com_dhcc_www_ndkapplication_MainActivity_Bitmap2Grey(
                  JNIEnv *env,
                  jobject /* this */,jintArray buf,jint w,jint h) {
              jint *cbuf;
              jboolean ptfalse = false;
              cbuf = env->GetIntArrayElements(buf, &ptfalse);
              if(cbuf == NULL){
                  return 0;
              }
          ?
              Mat imgData(h, w, CV_8UC4, (unsigned char*)cbuf);
              // 注意侠驯,Android的Bitmap是ARGB四通道,而不是RGB三通道
              cvtColor(imgData,imgData,COLOR_BGRA2GRAY);
              cvtColor(imgData,imgData,COLOR_GRAY2BGRA);
          ?
              int size=w * h;
              jintArray result = env->NewIntArray(size);
              env->SetIntArrayRegion(result, 0, size, (jint*)imgData.data);
              env->ReleaseIntArrayElements(buf, cbuf, 0);
              return result;
          }

這里要注意:Java_com_dhcc_www_ndkapplication_MainActivity_Bitmap2Grey 這個名字每個項目的包名不同,寫法也不同奕巍,其主要是 “Java包名Activity名方法名”的組合方式吟策,這里一定要寫對了,不然會報錯: 這個錯其實就是引入失敗或者找不到方法的錯誤報告的止,我一開始復制的是別人的代碼檩坚,找了半天才發(fā)現(xiàn)是這里的問題,沒辦法诅福,誰讓自己不懂呢.....

activity_main.xml

          <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
              xmlns:app="http://schemas.android.com/apk/res-auto"
              xmlns:tools="http://schemas.android.com/tools"
              android:layout_width="match_parent"
              android:layout_height="match_parent"
              android:orientation="vertical"
              tools:context=".MainActivity">
          ?
              <LinearLayout
                  android:layout_width="match_parent"
                  android:layout_height="wrap_content"
                  android:layout_weight="1"
                  android:orientation="vertical">
          ?
                  <ImageView
                      android:id="@+id/image"
                      android:layout_width="match_parent"
                      android:layout_height="match_parent"
                      android:layout_weight="1"
                      app:srcCompat="@drawable/lufei" />
              </LinearLayout>
          ?
              <LinearLayout
                  android:layout_width="match_parent"
                  android:layout_height="wrap_content"
                  android:orientation="horizontal">
          ?
                  <Button
                      android:id="@+id/button_2"
                      android:layout_width="match_parent"
                      android:layout_height="wrap_content"
                      android:layout_weight="1"
                      android:text="灰度圖" />
          ?
                  <Button
                      android:id="@+id/button_1"
                      android:layout_width="match_parent"
                      android:layout_height="wrap_content"
                      android:layout_weight="1"
                      android:text="色圖" />
              </LinearLayout>
          ?
          </LinearLayout>

運行效果

如果一切正常匾委,可以看到如下效果圖:

點擊灰度圖時:

demo1.jpg

點擊色圖時:

demo2.jpg

至此,整個教程完畢氓润!

相關學習鏈接:

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

AndroidStudio3.0 NDK開發(fā)- 如何在已有項目中進行NDK開發(fā)

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末赂乐,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子咖气,更是在濱河造成了極大的恐慌挨措,老刑警劉巖挖滤,帶你破解...
    沈念sama閱讀 218,122評論 6 505
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異浅役,居然都是意外死亡斩松,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,070評論 3 395
  • 文/潘曉璐 我一進店門觉既,熙熙樓的掌柜王于貴愁眉苦臉地迎上來惧盹,“玉大人,你說我怎么就攤上這事瞪讼【” “怎么了?”我有些...
    開封第一講書人閱讀 164,491評論 0 354
  • 文/不壞的土叔 我叫張陵尝艘,是天一觀的道長演侯。 經常有香客問我姿染,道長背亥,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,636評論 1 293
  • 正文 為了忘掉前任悬赏,我火速辦了婚禮狡汉,結果婚禮上,老公的妹妹穿的比我還像新娘闽颇。我一直安慰自己盾戴,他們只是感情好,可當我...
    茶點故事閱讀 67,676評論 6 392
  • 文/花漫 我一把揭開白布兵多。 她就那樣靜靜地躺著尖啡,像睡著了一般。 火紅的嫁衣襯著肌膚如雪剩膘。 梳的紋絲不亂的頭發(fā)上衅斩,一...
    開封第一講書人閱讀 51,541評論 1 305
  • 那天,我揣著相機與錄音怠褐,去河邊找鬼畏梆。 笑死,一個胖子當著我的面吹牛奈懒,可吹牛的內容都是我干的奠涌。 我是一名探鬼主播,決...
    沈念sama閱讀 40,292評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼磷杏,長吁一口氣:“原來是場噩夢啊……” “哼溜畅!你這毒婦竟也來了?” 一聲冷哼從身側響起极祸,我...
    開封第一講書人閱讀 39,211評論 0 276
  • 序言:老撾萬榮一對情侶失蹤达皿,失蹤者是張志新(化名)和其女友劉穎天吓,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體峦椰,經...
    沈念sama閱讀 45,655評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡龄寞,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 37,846評論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了汤功。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片物邑。...
    茶點故事閱讀 39,965評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖滔金,靈堂內的尸體忽然破棺而出色解,到底是詐尸還是另有隱情,我是刑警寧澤餐茵,帶...
    沈念sama閱讀 35,684評論 5 347
  • 正文 年R本政府宣布科阎,位于F島的核電站,受9級特大地震影響忿族,放射性物質發(fā)生泄漏锣笨。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,295評論 3 329
  • 文/蒙蒙 一道批、第九天 我趴在偏房一處隱蔽的房頂上張望错英。 院中可真熱鬧,春花似錦隆豹、人聲如沸椭岩。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,894評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽判哥。三九已至,卻和暖如春碉考,著一層夾襖步出監(jiān)牢的瞬間塌计,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,012評論 1 269
  • 我被黑心中介騙來泰國打工豆励, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留夺荒,地道東北人。 一個月前我還...
    沈念sama閱讀 48,126評論 3 370
  • 正文 我出身青樓良蒸,卻偏偏與公主長得像技扼,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子嫩痰,可洞房花燭夜當晚...
    茶點故事閱讀 44,914評論 2 355

推薦閱讀更多精彩內容