安卓集成OpenCv

当前环境: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实现需求

先说第一种方式:

安卓集成OpenCv_第1张图片安卓集成OpenCv_第2张图片

新建一个项目,导入Module选中下载的OpenCv里面的java文件夹,导入后添加依赖,如果添加依赖的时候没找到导入的opencv库

那是因为安卓集成OpenCv_第3张图片

然后把这个库添加到主项目依赖中,再然后在你主项目src/main下新建一个jniLibs文件夹,把安卓集成OpenCv_第4张图片

文件夹下的东西原样复制到jniLibs文件夹下,安卓集成OpenCv_第5张图片在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(没有就新建)文件夹下

安卓集成OpenCv_第6张图片

接下来配置build.grade文件安卓集成OpenCv_第7张图片

小红框里的东西要写,因为OpenCv4.1(或者是4.0搞忘了)有东西变了,接下来重点来了,配置CmakeList文件

安卓集成OpenCv_第8张图片

教学一下,当你们配置的时候路径死活不对频繁出错心烦意乱的时候,必杀技:配置成绝对路径 ,记得改路径,链接库的时候记得加上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 
#include "include/opencv2/opencv.hpp"
#include 

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传递也需要时间,如果配置过程中出现了什么错误可以在下方留言,我可以帮忙分析一下

你可能感兴趣的:(应用层,Ndk)