前言:
前段时间做项目,需要给AS配置Opencv环境,查了很多博客,遇到了很多坑,为了避免遗忘,在此记录,如有错误,请指教。
1、前期准备
- 下载opencv androidSdk:https://opencv.org/releases/
这里网速太慢,我截个图示意一下,我之前下载的是3.7版本
2、 新建AS项目
这一步要注意,AS3.2版本的时候可以勾选include C++ Surpport,而AS3.4.2把 NativeC++单独拿了出来,需要下拉右侧滑动条选择 Native C++,我之前找这个找了半天?
3、配置NDK
NDK需要手动配置,在刚刚新建好的项目中,选择 file —>setting—>Android SDK —> SDK tools 勾选NDK和cmake
4、复制opencv的相关文件
- include文件
将下载好的opencv安装包解压后在路径*OpenCV-android-sdksdk ativejni
下有一个include文件夹,复制该文件夹到我们项目中的src/main/cpp
下 - jni 文件
在项目的main文件夹下新建一个名为jniLibs
的文件夹,然后在路径*OpenCV-android-sdksdk ative
下有一个libs文件夹,将此文件夹复制到刚刚新建的jniLibs
文件夹下。项目结构如下图所示
5、配置build-gradle
在 app下的build-gradle中的android
节点下添加以下代码
sourceSets {
main {
jniLibs.srcDirs = ['src/main/jniLibs/libs']
}
}
此代码的作用就是设置动态链接库的地址,android
节点是下图圈出的部分
然后在Android.defaultConfig.externalNativeBuild
的节点内增加过滤器,如下:
abiFilters 'x86', 'x86_64', 'armeabi', 'armeabi-v7a', 'arm64-v8a', 'mips', 'mips64'
其中'x86', 'x86_64', 'armeabi', 'armeabi-v7a', 'arm64-v8a', 'mips', 'mips64'
,选择一个就行,如果是在模拟器上开发就选择X86
,如果在手机上选择armeabi-v7a
,这一步要注意的是过滤器添加的位置不要弄错了,是添加在下图中上第一个圈出来的节点。
完整的build-gradle文件,如下
apply plugin: 'com.android.application'
android {
compileSdkVersion 29
buildToolsVersion "29.0.1"
defaultConfig {
applicationId "com.example.opencvdemo"
minSdkVersion 23
targetSdkVersion 29
versionCode 1
versionName "1.0"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
externalNativeBuild {
cmake {
cppFlags "-std=c++11"
abiFilters 'armeabi-v7a'
}
ndk {
abiFilters 'armeabi-v7a'
}
}
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
}
externalNativeBuild {
cmake {
path "src/main/cpp/CMakeLists.txt"
version "3.10.2"
}
}
sourceSets {
main {
jniLibs.srcDirs = ['src/main/jniLibs/libs']
}
}
}
dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar'])
implementation 'androidx.appcompat:appcompat:1.1.0'
implementation 'androidx.constraintlayout:constraintlayout:1.1.3'
testImplementation 'junit:junit:4.12'
androidTestImplementation 'androidx.test:runner:1.2.0'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0'
}
配置完后,Sysn Now
一下
6、配置Cmake
还记得之前项目结构中的CMakeList.txt
文件吗?这里我们需要先把该文件移动到app
文件夹下,这样方便之后配置include文件夹
路径和动态链接库的路径,与此同时需要将app
下build-gradle文件中android.externalNativeBuild
节点下cmake的路径修改为"CMakeLists.txt"
,即第二张图中 的path
改为path "CMakeLists.txt"
在CMakeLists.txt
文件中添加如下代码。
# 设置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)
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上。
7、测试
经过上面一些列配置,opencv环境已经配置完成,可以编写代码进行测试.
- native-lib
#include <jni.h>
#include<opencv2/opencv.hpp>
#include<iostream>
using namespace cv;
using namespace std;
extern "C" JNIEXPORT jintArray
JNICALL
Java_com_example_opencvdemo_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;
}
- MainActivity
package com.example.opencvdemo;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.ImageView;
import androidx.appcompat.app.AppCompatActivity;
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.flower);
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.flower);
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();
}
}
- activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<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:src="@drawable/flower" />
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<Button
android:id="@+id/button_2"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="灰度图" />
<Button
android:id="@+id/button_1"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="色图" />
</LinearLayout>
</LinearLayout>
- 效果
8 参考链接
[1].Android NDK学习笔记:Android Studio3.1+CMAKE+OpenCV3.4配置
[2].AndroidStudio NDK之使用OpenCV
[3].Android Studio配置及使用OpenCV
[4].Android Studio中配置OpenCV