当前环境:Android Studio 3.5.1
Ndk r16b
OpenCv 4.1.0
OpenCv下载地址:https://opencv.org/releases/ 点安卓那个下载
本文主要讲两种方式集成
1.安卓导入 下载的OpenCv Sdk和so库 直接通过Java代码来调用Opencv实现需求
2.安卓导入so库和OpenCv的头文件,通过Jni的方式来调用OpenCv实现需求
先说第一种方式:
新建一个项目,导入Module选中下载的OpenCv里面的java文件夹,导入后添加依赖,如果添加依赖的时候没找到导入的opencv库
那是因为
然后把这个库添加到主项目依赖中,再然后在你主项目src/main下新建一个jniLibs文件夹,把
文件夹下的东西原样复制到jniLibs文件夹下,在build.grade文件里添加cpu架构,然后你就可以在代码中使用了,Activity的代码我贴在下边,xml文件里就一个ImageView和一个Button
package com.sciptv.testopencv
import android.graphics.Bitmap
import android.graphics.BitmapFactory
import android.os.Bundle
import android.provider.Settings
import android.util.Log
import androidx.appcompat.app.AppCompatActivity
import kotlinx.android.synthetic.main.activity_main.*
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import org.opencv.android.BaseLoaderCallback
import org.opencv.android.LoaderCallbackInterface
import org.opencv.android.OpenCVLoader
import org.opencv.android.Utils
import org.opencv.core.CvType
import org.opencv.core.Mat
import org.opencv.imgproc.Imgproc
class MainActivity : AppCompatActivity() {
private var imageMat: Mat? = null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
click.setOnClickListener {
GlobalScope.launch(Dispatchers.IO) {
val time = System.currentTimeMillis()
val bitmap = BitmapFactory.decodeResource(resources, R.drawable.cat)
val matSrc = Mat(bitmap.width, bitmap.height, CvType.CV_8UC4)
Utils.bitmapToMat(bitmap, matSrc)
val matGray = Mat(bitmap.width, bitmap.height, CvType.CV_8UC1)
Imgproc.cvtColor(matSrc, matGray, Imgproc.COLOR_BGRA2GRAY, 1)
val bmpDst =
Bitmap.createBitmap(matGray.cols(), matGray.rows(), Bitmap.Config.ARGB_8888)
Utils.matToBitmap(matGray, bmpDst)
withContext(Dispatchers.Main) {
cat.setImageBitmap(bmpDst)
Log.e("OpenCV Time2:", (System.currentTimeMillis() - time).toString())
}
}
}
}
private val baseLoaderCallback = object : BaseLoaderCallback(this) {
override fun onManagerConnected(status: Int) {
when (status) {
LoaderCallbackInterface.SUCCESS -> {
Log.i("MyOpenCV", "OpenCV loaded successfully")
imageMat = Mat()
}
else -> {
Log.i("MyOpenCV", "OpenCV loaded Failed")
super.onManagerConnected(status)
}
}
}
}
override fun onResume() {
super.onResume()
if (!OpenCVLoader.initDebug()) {
Log.d(
"MyOpenCV",
"Internal OpenCV library not found. Using OpenCV Manager for initialization"
)
OpenCVLoader.initAsync(OpenCVLoader.OPENCV_VERSION, this, baseLoaderCallback)
} else {
Log.d("MyOpenCV", "OpenCV library found inside package. Using it!")
baseLoaderCallback.onManagerConnected(LoaderCallbackInterface.SUCCESS)
}
}
}
至此第一种方式结束
来看第二种方式
新建一个Ndk项目,同样的位置建立jniLibs文件夹导入so库,把opencv2文件夹粘贴在src/main/include(没有就新建)文件夹下
接下来配置build.grade文件
小红框里的东西要写,因为OpenCv4.1(或者是4.0搞忘了)有东西变了,接下来重点来了,配置CmakeList文件
教学一下,当你们配置的时候路径死活不对频繁出错心烦意乱的时候,必杀技:配置成绝对路径 ,记得改路径,链接库的时候记得加上jnigraphics库,因为后面我们需要用到Bitmap,环境配置完毕
Activity代码
package com.sciptv.testopencvndk
import android.graphics.Bitmap
import android.graphics.BitmapFactory
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.util.Log
import kotlinx.android.synthetic.main.activity_main.*
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
class MainActivity : AppCompatActivity() {
companion object {
init {
System.loadLibrary("native-lib")
System.loadLibrary("opencv_java4")
}
}
private lateinit var bitmap: Bitmap
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
bitmap = BitmapFactory.decodeResource(resources, R.drawable.cat);
normal.setOnClickListener {
image.setImageBitmap(bitmap)
}
gray.setOnClickListener {
val time = System.currentTimeMillis()
GlobalScope.launch(Dispatchers.IO) {
val w = bitmap.width
val h = bitmap.height
var piexls = IntArray(w * h)
bitmap.getPixels(piexls, 0, w, 0, 0, w, h)
val resultData = OpenCv.toGray(piexls, w, h)
val resultImage = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888)
resultImage.setPixels(resultData, 0, w, 0, 0, w, h)
withContext(Dispatchers.Main) {
image.setImageBitmap(resultImage)
Log.e("OpenCV NDK Time:", (System.currentTimeMillis() - time).toString())
}
}
}
}
}
Ndk代码
#include <jni.h>
#include "include/opencv2/opencv.hpp"
#include <iostream>
using namespace cv;
using namespace std;
extern "C"
JNIEXPORT jintArray JNICALL
Java_com_sciptv_testopencvndk_OpenCv_toGray(JNIEnv *env, jclass clazz, jintArray buf, jint w,
jint h) {
jint *cbuf;
cbuf = env->GetIntArrayElements(buf, JNI_FALSE);
if (cbuf == NULL) {
return 0;
}
Mat imgData(h, w, CV_8UC4, (unsigned char *) cbuf);
uchar *ptr = imgData.ptr(0);
for (int i = 0; i < w * h; ++i) {
// 计算公式:Y(亮度) = 0.299*R + 0.587*G + 0.114*B
// 对于一个int四字节,其彩色值存储方式为:BGRA
int grayScale = (int) (ptr[4 * i + 2] * 0.299 + ptr[4 * i + 1] * 0.587 +
ptr[4 * i + 0] * 0.114);
ptr[4 * i + 1] = grayScale;
ptr[4 * i + 2] = grayScale;
ptr[4 * i + 0] = grayScale;
}
int size = w * h;
jintArray result = env->NewIntArray(size);
env->SetIntArrayRegion(result, 0, size, cbuf);
env->ReleaseIntArrayElements(buf, cbuf, 0);
return result;
}
OpenCv类的代码
package com.sciptv.testopencvndk;
public class OpenCv {
public native static int[] toGray(int[] array, int w, int h);
}
总结一下:第一种方式适合没有ndk开发基础的人,可以直接在Java代码里使用
第二种方适合:当Java代码已经满足不了你的需求了,需要自己根据c代码来扩展需求,或者觉得Java库太过庞大了,这时候就需要你自己来根据c代码裁剪库的大小了,我还比较了两种方式的耗时,我发现ndk的实现要比java代码实现平均耗时多1/3左右,可能是跟灰度算法有关,jni传递也需要时间,如果配置过程中出现了什么错误可以在下方留言,我可以帮忙分析一下