1. 程式人生 > >Android Studio Cmake & OpenCV3.2環境

Android Studio Cmake & OpenCV3.2環境

前言:

  • Cmake是跨平臺的一個編譯配置工具,android studio 2.2之後就開始支援這個cmake工具了,之前Android studio開發c++都是利用NDK的方式,也就是Android.mk + Application.mk + soucre.cpp的構建方式,然後在gradle構建工具下執行一條命令生成c++ so庫並打包進去。而cmake則是 CMakeLists.txt + source.cpp 的形式。

  • 對比起來,我更喜歡用CMakeLists.txt的形式,結構看起來比較清晰,比較適合從linux下轉到android上的人來使用。

官方的說明:

● Android 原生開發工具包 (NDK):這套工具集允許您為 Android 使用 C 和 C++ 程式碼,並提供眾多平臺庫,讓您可以管理原生 Activity 和訪問物理裝置元件,例如感測器和觸控輸入。
● CMake:一款外部構建工具,可與 Gradle 搭配使用來構建原生庫。如果您只計劃使用 ndk-build,則不需要此元件。

準備資源:

下載安裝CMake工具

開啟SDK Manager :
這裡寫圖片描述

複選CMake和NDK 然後下載。

簡單的CMake工程

新建一個Android Studio工程,記得勾選 Include C++ Supports。這樣子新建完的工程,目錄結構是這樣的:
這裡寫圖片描述

紅色方框標出來的就是跟普通android工程不一樣的地方,多了CMake工程一些需要的。
這三個東西都是NDK部分:
1. .externalNativeBuild資料夾:cmake編譯好的檔案, 顯示支援的各種硬體等資訊。系統生成。
2. cpp資料夾:存放C/C++程式碼檔案,native-lib.cpp檔案是該Demo中自帶的,可更改。需要自己編寫。
3. CMakeLists.txt檔案:CMake指令碼配置的檔案。需要自己配置編寫。

編譯的過程:
開啟和CMkaeLists.txt的同級目錄下的gradle檔案:
image

關注其中兩部分,一個是defaultconfig 模組中的externalNativeBuild,這裡面主要是配置cmake一些編譯屬性,標誌等,具體語法android官方有說明。下面那個模組主要是指定編譯的CMakeLists.txt的檔案。

CMakeLists.txt檔案用於配置JNI專案屬性,主要用於宣告CMake使用版本、so庫名稱、C/CPP檔案路徑等資訊,下面是該檔案內容:

# For more information about using CMake with Android Studio, read the
# documentation: https://d.android.com/studio/projects/add-native-code.html

# Sets the minimum version of CMake required to build the native library.

cmake_minimum_required(VERSION 3.4.1)

# Creates and names a library, sets it as either STATIC
# or SHARED, and provides the relative paths to its source code.
# You can define multiple libraries, and CMake builds them for you.
# Gradle automatically packages shared libraries with your APK.

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 )

# Searches for a specified prebuilt library and stores the path as a
# variable. Because CMake includes system libraries in the search path by
# default, you only need to specify the name of the public NDK library
# you want to add. CMake verifies that the library exists before
# completing its build.

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 )

# Specifies libraries CMake should link to your target library. You
# can link multiple libraries, such as libraries you define in this
# build script, prebuilt third-party libraries, or system libraries.

target_link_libraries( # Specifies the target library.
                       native-lib

                       # Links the target library to the log library
                       # included in the NDK.
                       ${log-lib} )
  • cmake_minimum_required(VERSION 3.4.1)

       CMake最小版本使用的是3.4.1。
    
  • *add_library*()

    配置so庫資訊(為當前當前指令碼檔案新增庫)

  • native-lib

    這個是宣告引用so庫的名稱,在專案中,如果需要使用這個so檔案,引用的名稱就是這個。值得注意的是,實際上生成的so檔名稱是libnative-lib。當Run專案或者build專案是,在Module級別的build檔案下的intermediates\transforms\mergeJniLibs\debug\folders\2000\1f\main下會生成相應的so庫檔案。

  • SHARED

       這個引數表示共享so庫檔案,也就是在Run專案或者build專案時會在目錄intermediates\transforms\mergeJniLibs\debug\folders\2000\1f\main下生成so庫文。此外,so庫檔案都會在打包到.apk裡面,可以通過選擇選單欄的*Build->Analyze Apk...**檢視apk中是否存在so庫檔案,一般它會存放在lib目錄下。
    
  • src/main/cpp/native-lib.cpp

    構建so庫的原始檔。

STATIC:靜態庫,是目標檔案的歸檔檔案,在連結其它目標的時候使用。
SHARED:動態庫,會被動態連結,在執行時被載入。
MODULE:模組庫,是不會被連結到其它目標中的外掛,但是可能會在執行時使用dlopen-系列的函式動態連結。

  • 標頭檔案

    也可以配置標頭檔案路徑,方法是(注意這裡指定的是目錄而非檔案):
    include_directories([AFTER|BEFORE] [SYSTEM] dir1 [dir2 …])
    下面的配置實際上與自定義的JNI專案(自定義的so庫)沒有太大關係。
    find_library()
    這個方法與我們要建立的so庫無關而是使用NDK的Apis或者庫,預設情況下Android平臺集成了很多NDK庫檔案,所以這些檔案是沒有必要打包到apk裡面去的。直接宣告想要使用的庫名稱即可(猜測:貌似是在Sytem/libs目錄下)。在這裡不需要指定庫的路徑,因為這個路徑已經是CMake路徑搜尋的一部分。如示例中使用的是log相關的so庫。

  • log-lib

    這個指定的是在NDK庫中每個型別的庫會存放一個特定的位置,而log庫存放在log-lib中
    
  • log

    指定使用log庫

  • target_link_libraries()

    如果你本地的庫(native-lib)想要呼叫log庫的方法,那麼就需要配置這個屬性,意思是把NDK庫關聯到本地庫。

  • native-lib

    要被關聯的庫名稱

  • ${log-lib}

    要關聯的庫名稱,要用大括號包裹,前面還要有$符號去引用。

更具體的資訊可以去android官網查詢。

程式碼裡面也自動載入庫:

 protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

    // Example of a call to a native method
    TextView tv = (TextView) findViewById(R.id.sample_text);
    tv.setText(stringFromJNI());
    }

    /**
     * A native method that is implemented by the 'native-lib' native library,
     * which is packaged with this application.
     */
    public native String stringFromJNI();

    // Used to load the 'native-lib' library on application startup.
    static {
        System.loadLibrary("native-lib");
    }

點選執行,就會出現Hello from C++的字串了。

構建OpenCV cmake環境

下載下來的opencv 包目錄如下:
- apk
- samples
- sdk

這邊搭建的android opencv的 jni環境,所以只需要sdk/native/jni/include 標頭檔案和sdk/native/libs 對應平臺的so檔案即可。其他的一些cmake裡面原生的配置檔案是opencv自帶的為了和ndk結合,需要在Android.mk 去執行OpenCV.mk 檔案,動態生成opencv的庫,這樣的好處是可以修改opencv原始碼,比較複雜。

把include檔案拷貝到cpp目錄下,在main目錄下建立一個jniLibs/ 目錄,把libs下的對應平臺的資料夾拷貝進去。

再次配置gradle檔案:

  • 加入abi 過濾:
ndk {
            abiFilters  'x86'

        }

這個模組加在defaultconfig 模組中 externalNativeBuild的下面,這樣子編譯的時候就只會去找你過濾的平臺了,我這邊實在電腦上執行虛擬機器,所以是 ‘x86’,可以根據實際情況改,比如手機裝置可以改成 armeabi-v7a 。

  • 修改CMakeLists.txt檔案
set(CMAKE_VERBOSE_MAKEFILE on)
set(libs "${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 "${libs}/${ANDROID_ABI}/libopencv_java3.so")

#......
#......

#新增連結的opencv庫和 android一些常用的庫比如ljnigraphics,bitmap需要用到
target_link_libraries( # Specifies the target library.
                       native-lib android log -ljnigraphics libopencv_java3

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

這麼一來opencv的cmake環境就配置好了,開啟native-lib.cpp

#include <jni.h>
#include <string>
#include <opencv/cv.h>
#include <opencv2/opencv.hpp>


using namespace cv;

extern "C"
JNIEXPORT jstring JNICALL
Java_com_opencv_wmy_opencvtest_MainActivity_stringFromJNI(
        JNIEnv* env,
        jobject /* this */) {
    Mat m = imread("bike.jpg");



    std::string hello = "Hello from C++";
    return env->NewStringUTF(hello.c_str());
}

opencv的標頭檔案加在正確,在此環境下函式都有程式碼提示,點選執行,成功執行,在介面會顯示一個 Hello from C++,表示opencv環境已經好了。

opencv主要是用來影象計算和處理的,放在C++層效率會更快一點,後面會陸續推出幾篇部落格,如何在java層和jni傳影象資料,敬請期待!