Android studio使用JAVA与JNI调用OpenCV

Android studio使用JAVA与JNI调用OpenCV

      • 后记

1.准备材料
1.在这里下载OpenCV-android-sdk
下载解压后
Android studio使用JAVA与JNI调用OpenCV_第1张图片
2.新建Android Studio工程
Android studio使用JAVA与JNI调用OpenCV_第2张图片Android studio使用JAVA与JNI调用OpenCV_第3张图片3.将在工程目录下的 app/src/main 下面新建两个文件夹,分别是cpp和jniLibs,如果有就不用新建。
把OpenCV-android-sdk\sdk\native\libs中内容拷贝到jniLibs中
把OpenCV-android-sdk\sdk\native\jni中的include文件夹拷贝到cpp中。

Android studio使用JAVA与JNI调用OpenCV_第4张图片
4.修改Cmakelist.txt
在Cmakelists中添加以下内容:

include_directories(${CMAKE_SOURCE_DIR}/src/main/cpp/include)

add_library(libopencv_java3 SHARED IMPORTED)
set_target_properties(libopencv_java3 PROPERTIES IMPORTED_LOCATION
            ${CMAKE_SOURCE_DIR}/src/main/jniLibs/${ANDROID_ABI}/libopencv_java3.so)

修改target_link_libraries:

target_link_libraries( # Specifies the target library.
                       native-lib   libopencv_java3
                       jnigraphics
                       # Links the target library to the log library
                       # included in the NDK.
                       ${log-lib} )

Android studio使用JAVA与JNI调用OpenCV_第5张图片
Cmakelists中添加的代码解释以及常见错误请参考这篇博客!

5.传入bitmap到JNI测试Canny边缘检测
在MainActivity中创建:

public native void Canny(Object bitmap);

鼠标点到Canny上,按住alt+Enter代码提示在native-lib.cpp文件中生成C函数接口:
Android studio使用JAVA与JNI调用OpenCV_第6张图片Android studio使用JAVA与JNI调用OpenCV_第7张图片
在native-lib.cpp文件中导入:

#include <opencv/cv.h>
#include <opencv2/opencv.hpp>
#include <android/bitmap.h>
using namespace cv;

这里使用这篇博文里面的在native-lib中代码实现Mat与Bitmap互转我先把代码贴这:

void BitmapToMat2(JNIEnv *env, jobject& bitmap, Mat& mat, jboolean needUnPremultiplyAlpha) {
    AndroidBitmapInfo info;
    void *pixels = 0;
    Mat &dst = mat;

    try {
        CV_Assert(AndroidBitmap_getInfo(env, bitmap, &info) >= 0);
        CV_Assert(info.format == ANDROID_BITMAP_FORMAT_RGBA_8888 ||
                  info.format == ANDROID_BITMAP_FORMAT_RGB_565);
        CV_Assert(AndroidBitmap_lockPixels(env, bitmap, &pixels) >= 0);
        CV_Assert(pixels);
        dst.create(info.height, info.width, CV_8UC4);
        if (info.format == ANDROID_BITMAP_FORMAT_RGBA_8888) {
            Mat tmp(info.height, info.width, CV_8UC4, pixels);
            if (needUnPremultiplyAlpha) cvtColor(tmp, dst, COLOR_mRGBA2RGBA);
            else tmp.copyTo(dst);
        } else {
//             info.format == ANDROID_BITMAP_FORMAT_RGB_565
            Mat tmp(info.height, info.width, CV_8UC2, pixels);
            cvtColor(tmp, dst, COLOR_BGR5652RGBA);
        }
        AndroidBitmap_unlockPixels(env, bitmap);
        return;
    } catch (const cv::Exception &e) {
        AndroidBitmap_unlockPixels(env, bitmap);
        jclass je = env->FindClass("org/opencv/core/CvException");
        if (!je) je = env->FindClass("java/lang/Exception");
        env->ThrowNew(je, e.what());
        return;
    } catch (...) {
        AndroidBitmap_unlockPixels(env, bitmap);
        jclass je = env->FindClass("java/lang/Exception");
        env->ThrowNew(je, "Unknown exception in JNI code {nBitmapToMat}");
        return;
    }
}

void BitmapToMat(JNIEnv *env, jobject& bitmap, Mat& mat) {
    BitmapToMat2(env, bitmap, mat, false);
}

void MatToBitmap2
        (JNIEnv *env, Mat& mat, jobject& bitmap, jboolean needPremultiplyAlpha) {
    AndroidBitmapInfo info;
    void *pixels = 0;
    Mat &src = mat;

    try {
        CV_Assert(AndroidBitmap_getInfo(env, bitmap, &info) >= 0);
        CV_Assert(info.format == ANDROID_BITMAP_FORMAT_RGBA_8888 ||
                  info.format == ANDROID_BITMAP_FORMAT_RGB_565);
        CV_Assert(src.dims == 2 && info.height == (uint32_t) src.rows &&
                  info.width == (uint32_t) src.cols);
        CV_Assert(src.type() == CV_8UC1 || src.type() == CV_8UC3 || src.type() == CV_8UC4);
        CV_Assert(AndroidBitmap_lockPixels(env, bitmap, &pixels) >= 0);
        CV_Assert(pixels);
        if (info.format == ANDROID_BITMAP_FORMAT_RGBA_8888) {
            Mat tmp(info.height, info.width, CV_8UC4, pixels);
            if (src.type() == CV_8UC1) {
                cvtColor(src, tmp, COLOR_GRAY2RGBA);
            } else if (src.type() == CV_8UC3) {
                cvtColor(src, tmp, COLOR_RGB2RGBA);
            } else if (src.type() == CV_8UC4) {
                if (needPremultiplyAlpha)
                    cvtColor(src, tmp, COLOR_RGBA2mRGBA);
                else
                    src.copyTo(tmp);
            }
        } else {
            // info.format == ANDROID_BITMAP_FORMAT_RGB_565
            Mat tmp(info.height, info.width, CV_8UC2, pixels);
            if (src.type() == CV_8UC1) {
                cvtColor(src, tmp, COLOR_GRAY2BGR565);
            } else if (src.type() == CV_8UC3) {
                cvtColor(src, tmp, COLOR_RGB2BGR565);
            } else if (src.type() == CV_8UC4) {
                cvtColor(src, tmp, COLOR_RGBA2BGR565);
            }
        }
        AndroidBitmap_unlockPixels(env, bitmap);
        return;
    } catch (const cv::Exception &e) {
        AndroidBitmap_unlockPixels(env, bitmap);
        jclass je = env->FindClass("org/opencv/core/CvException");
        if (!je) je = env->FindClass("java/lang/Exception");
        env->ThrowNew(je, e.what());
        return;
    } catch (...) {
        AndroidBitmap_unlockPixels(env, bitmap);
        jclass je = env->FindClass("java/lang/Exception");
        env->ThrowNew(je, "Unknown exception in JNI code {nMatToBitmap}");
        return;
    }
}

void MatToBitmap(JNIEnv *env, Mat& mat, jobject& bitmap) {
    MatToBitmap2(env, mat, bitmap, false);
}

在刚才生成的C接口函数中输入:

    Mat mat_1;
    //bitmap转化成mat
    BitmapToMat(env,bitmap,mat_1);
    //将原始图转化为灰度图
    cvtColor(mat_1, mat_1, COLOR_BGR2GRAY);
    //先使用3*3内核来降噪
    blur(mat_1, mat_1, Size(3, 3));
    //运行canny算子
    Canny(mat_1, mat_1, 20, 50, 3);
    MatToBitmap(env,mat_1,bitmap);

Android studio使用JAVA与JNI调用OpenCV_第8张图片
实现将传入的bitmap进行Canny检测!
在/res/drawable/下放入一张图片 该资源文件必须以字母开头且不为大写
Android studio使用JAVA与JNI调用OpenCV_第9张图片

打开activity_main.xml文件切换到Design快速拖拽创建一个imageView和两个个Button
Android studio使用JAVA与JNI调用OpenCV_第10张图片MainActivite中设置控件并绑定监听:

Android studio使用JAVA与JNI调用OpenCV_第11张图片运行图
Android studio使用JAVA与JNI调用OpenCV_第12张图片Android studio使用JAVA与JNI调用OpenCV_第13张图片

至此Android项目可以使用JNI调用OpenCV,但是往往JAVA调用OpenCV与JNI调用OpenCV结合起来一起用才爽,JAVA调用OpenCV也很简单:

1.将OpenCV Java库作为Module导入。具体File->New->Import Module,然后将OpenCV-android-sdk\sdk\java目录导入,然后Next->Finish。

Android studio使用JAVA与JNI调用OpenCV_第14张图片Android studio使用JAVA与JNI调用OpenCV_第15张图片2.按照app的build.gradle的几个版本号 更改opencvLibraryXXX的build.gradle
更改前:
Android studio使用JAVA与JNI调用OpenCV_第16张图片Android studio使用JAVA与JNI调用OpenCV_第17张图片
更改后:
Android studio使用JAVA与JNI调用OpenCV_第18张图片
3.给项目添加Opencv Java依赖,具体步骤 File->Project Structure->app->Dependencies->右上角的 + -> Module dependency->opencvLibraryXXX->OK
Android studio使用JAVA与JNI调用OpenCV_第19张图片Android studio使用JAVA与JNI调用OpenCV_第20张图片
Android studio使用JAVA与JNI调用OpenCV_第21张图片
4.测试
稍稍改动一下之前的Button2的监听代码,让他实现JAVA调用OpenCV实现二值化

Bitmap bitmap1 = BitmapFactory.decodeResource(getResources(), R.drawable.a26);
Mat a = new Mat();
Utils.bitmapToMat(bitmap1,a);
Imgproc.cvtColor(a, a, Imgproc.COLOR_RGB2GRAY);
Imgproc.threshold(a, a, 100, 255,Imgproc.THRESH_BINARY);
Utils.matToBitmap(a,bitmap1);
tv.setImageBitmap(bitmap1);

Android studio使用JAVA与JNI调用OpenCV_第22张图片

运行效果图

Android studio使用JAVA与JNI调用OpenCV_第23张图片

后记

在不配置jni的情况下只配置Java依赖项遇到的问题:

错误:java.lang.UnsatisfiedLinkError: No implementation found for long org.opencv.core.Mat.n_Mat()

原因是没有加载Opencv的库,一种解决方法是在手机上安装OpenCV Manager可以参考这篇博客。
我把他的代码贴在下面:

@Override
    protected void onResume() {
        super.onResume();
        if (!OpenCVLoader.initDebug()) {
            Log.d(TAG, "Internal OpenCV library not found. Using OpenCV Manager for initialization");
            OpenCVLoader.initAsync(OpenCVLoader.OPENCV_VERSION_3_0_0, this, mLoaderCallback);
        } else {
            Log.d(TAG, "OpenCV library found inside package. Using it!");
            mLoaderCallback.onManagerConnected(LoaderCallbackInterface.SUCCESS);
        }
    }
//openCV4Android 需要加载用到
    private BaseLoaderCallback mLoaderCallback = new BaseLoaderCallback(this) {
        @Override
        public void onManagerConnected(int status) {
            switch (status) {
                case LoaderCallbackInterface.SUCCESS: {
                    Log.i(TAG, "OpenCV loaded successfully");
//                    mOpenCvCameraView.enableView();
//                    mOpenCvCameraView.setOnTouchListener(ColorBlobDetectionActivity.this);
                }
                break;
                default: {
                    super.onManagerConnected(status);
                }
                break;
            }
        }
    };

这种方法最大的缺点就是需要单独下载另一个APP,这就很不舒服。所以还有另外一种更方便的方法:
类似在配置JNI的第三步,
1.将在工程目录下的 app/src/ 下面新建文件夹libs,如果有就不用新建。
把OpenCV-android-sdk\sdk\native\libs中内容拷贝到jniLibs中。
2.在app-build.gradle中的android节点中加入自定义libs的地址。

    sourceSets {
        main {
            jniLibs.srcDirs = ['src/libs']
        }
    }

3.创建staticLoadCVLibraries()方法并在MainActivity的onCreate方法中调用:

    //OpenCV库静态加载并初始化
    private void staticLoadCVLibraries(){
        boolean load = OpenCVLoader.initDebug();
        if(load) { Log.i("CV", "Open CV Libraries loaded..."); }
    }

这种方法的缺点就是把.so静态库文件放入app后会使app变得很大,可以根据自己机型的框架删去不必要的框架。

希望这篇文章对您有帮助,感谢阅读!

你可能感兴趣的:(Android,OpenCV,Android,opencv)