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'] -
} -
}
这一步设置了动态链接库的路径地址,用于项目构建时,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文件如下:
-
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' -
}
配置完之后,点击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} )
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(); -
} -
}
- 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; -
}
- 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