轉載說明:保存資料之用,Android+OpenCV+CMAKE
原文鏈接
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
節點中添加如下代碼:
sourceSets{
main{
jniLibs.srcDirs = ['src/main/jniLibs/libs']
}
}
- 1
- 2
- 3
- 4
- 5
這一步設置了動態鏈接庫的路徑地址,用於項目構建時,Native尋找和鏈接相關的so文件。
然後在Android.defaultConfig.externalNativeBuild
的節點內增加一行過濾器,如下:
abiFilters 'x86', 'x86_64', 'armeabi', 'armeabi-v7a', 'arm64-v8a', 'mips', 'mips64'
- 1
.so文件依賴於硬件環境,不同的CPU架構對應不同的.so文件,abiFilters
關鍵字能夠指定Android 所支持的CPU架構,一般是以上7種,最終這些.so文件會被打包進APK,所以可以根據自己的項目進行選擇,比如在AS模擬器上開發APP選一個 x86
就可以了,如果是手機端,一般是arm架構,選 armeabi-v7a
即可。
最終的build.gradle文件如下:
apply plugin: 'com.android.application'
android {
compileSdkVersion 26
defaultConfig {
applicationId "com.example.videomedicine.opencvtest"
minSdkVersion 24
targetSdkVersion 26
versionCode 1
versionName "1.0"
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
externalNativeBuild {
cmake {
cppFlags "-std=c++11 -frtti -fexceptions"
abiFilters 'x86'
}
}
ndk{
abiFilters 'x86'
}
}
sourceSets{
main{
jniLibs.srcDirs = ['src/main/jniLibs/libs']
}
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
externalNativeBuild {
cmake {
path "CMakeLists.txt"
}
}
}
dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar'])
implementation 'com.android.support:appcompat-v7:26.1.0'
implementation 'com.android.support.constraint:constraint-layout:1.0.2'
testImplementation 'junit:junit:4.12'
androidTestImplementation 'com.android.support.test:runner:1.0.1'
androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.1'
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
配置完之後,點擊Sysn Now
。
3.3.2 CMakeList.txt文件
這個文件也是NDK開發最最最關鍵的文件,AS採用CMake腳本語法配置C編譯器的環境,如果你之前有過使用CMAKE的經驗,或許這並非難題,但對於初學者而言,CMAKE的腳本語法,還是略過於生澀,而且AS對該文件的配置並不友好,居然沒有代碼提示,於是不得不查很多文檔。但好在,NDK的開發大多不是大型的C++項目,也不太需要過於複雜的設置(比如OpenCV源代碼的CMAKE文件,大約有幾千行的樣子 Orz~)
OpenCV配置CMakeList文件的方式如下:
# For more information about using CMake with Android Studio, read the
# 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的動態庫
add_library(libopencv_java3 SHARED IMPORTED)
set_target_properties(libopencv_java3 PROPERTIES IMPORTED_LOCATION
${CMAKE_SOURCE_DIR}/src/main/jniLibs/libs/${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 libopencv_java3
# Links the target library to the log library
# included in the NDK.
${log-lib} )
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
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文件
package com.example.videomedicine.opencvtest;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
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.luffy);
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.luffy);
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();
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
- 68
- 69
- 70
- 71
- 72
- 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_example_videomedicine_opencvtest_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,CV_BGRA2GRAY);
cvtColor(imgData,imgData,CV_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;
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<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/luffy" />
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_weight="1"
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>
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
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官方文檔