1.准备材料
1.在这里下载OpenCV-android-sdk
下载解压后
2.新建Android Studio工程
3.将在工程目录下的 app/src/main 下面新建两个文件夹,分别是cpp和jniLibs,如果有就不用新建。
把OpenCV-android-sdk\sdk\native\libs中内容拷贝到jniLibs中
把OpenCV-android-sdk\sdk\native\jni中的include文件夹拷贝到cpp中。
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} )
Cmakelists中添加的代码解释以及常见错误请参考这篇博客!
5.传入bitmap到JNI测试Canny边缘检测
在MainActivity中创建:
public native void Canny(Object bitmap);
鼠标点到Canny上,按住alt+Enter代码提示在native-lib.cpp文件中生成C函数接口:
在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);
实现将传入的bitmap进行Canny检测!
在/res/drawable/下放入一张图片 该资源文件必须以字母开头且不为大写
打开activity_main.xml文件切换到Design快速拖拽创建一个imageView和两个个Button:
MainActivite中设置控件并绑定监听:
至此Android项目可以使用JNI调用OpenCV,但是往往JAVA调用OpenCV与JNI调用OpenCV结合起来一起用才爽,JAVA调用OpenCV也很简单:
1.将OpenCV Java库作为Module导入。具体File->New->Import Module,然后将OpenCV-android-sdk\sdk\java目录导入,然后Next->Finish。
2.按照app的build.gradle的几个版本号 更改opencvLibraryXXX的build.gradle
更改前:
更改后:
3.给项目添加Opencv Java依赖,具体步骤 File->Project Structure->app->Dependencies->右上角的 + -> Module dependency->opencvLibraryXXX->OK
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);
运行效果图
在不配置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变得很大,可以根据自己机型的框架删去不必要的框架。
希望这篇文章对您有帮助,感谢阅读!