前言
上一篇文章已经介绍了OpenCV的环境配置《OpenCV4Android中NDK开发(一)--- OpenCV4.1.0环境搭建》,今天这篇我们就来直接进行实战,先做最简单的传入图像转为灰度图。
视频效果
按照惯例,我们先上最终实现的效果视频
代码演示
用我们上一篇配置完的Demo,我们在这个基础上进行改造。
布局文件
activity_main.xml
我们在布局文件里把整个布局改为垂直线性布局(LinearLayout),然后加上一个ImageView和一个Button
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context=".MainActivity">
<TextView
android:id="@+id/sample_text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Hello World!"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<ImageView
android:layout_width="300dp"
android:layout_height="500dp"
android:id="@+id/image"/>
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="@+id/btncs"
android:text="测试"/>
</LinearLayout>
样式为
代码文件
MainActivity.java
在布局对应的文件中我们加入控件的加载和事件代码
然后我们定义一个native的方法,用于实现NDK开发,如下图的
刚建好时方法名为红色的,我们还是按ALT+ENTER,直接在C++的文件中自动生成对应的函数方法名。
native-lib.cpp
由于在C++我们也用到了JAVA的Bitmap的类,所以我们在include里面要加入android/bitmap.h
自已写一个将OpenCV的Mat转为Bitmap的方法mat2bitmap
//将Mat转换为bitmap
jobject mat2bitmap(JNIEnv *env, cv::Mat &src, bool needPremultiplyAlpha, jobject bitmap_config) {
jclass java_bitmap_class = (jclass) env->FindClass("android/graphics/Bitmap");
jmethodID mid = env->GetStaticMethodID(java_bitmap_class, "createBitmap",
"(IILandroid/graphics/Bitmap$Config;)Landroid/graphics/Bitmap;");
jobject bitmap = env->CallStaticObjectMethod(java_bitmap_class,
mid, src.size().width, src.size().height,
bitmap_config);
AndroidBitmapInfo info;
void *pixels = 0;
try {
CV_Assert(AndroidBitmap_getInfo(env, bitmap, &info) >= 0);
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) {
cv::Mat tmp(info.height, info.width, CV_8UC4, pixels);
if (src.type() == CV_8UC1) {
cvtColor(src, tmp, cv::COLOR_GRAY2RGBA);
} else if (src.type() == CV_8UC3) {
cvtColor(src, tmp, cv::COLOR_RGB2BGRA);
} else if (src.type() == CV_8UC4) {
if (needPremultiplyAlpha) {
cvtColor(src, tmp, cv::COLOR_RGBA2mRGBA);
} else {
src.copyTo(tmp);
}
}
} else {
// info.format == ANDROID_BITMAP_FORMAT_RGB_565
cv::Mat tmp(info.height, info.width, CV_8UC2, pixels);
if (src.type() == CV_8UC1) {
cvtColor(src, tmp, cv::COLOR_GRAY2BGR565);
} else if (src.type() == CV_8UC3) {
cvtColor(src, tmp, cv::COLOR_RGB2BGR565);
} else if (src.type() == CV_8UC4) {
cvtColor(src, tmp, cv::COLOR_RGBA2BGR565);
}
}
AndroidBitmap_unlockPixels(env, bitmap);
return bitmap;
} catch (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 bitmap;
} catch (...) {
AndroidBitmap_unlockPixels(env, bitmap);
jclass je = env->FindClass("java/lang/Exception");
env->ThrowNew(je, "Unknown exception in JNI code {nMatToBitmap}");
return bitmap;
}
}
然后就是写我们自己生成的那个JNI调用的方法,方法流程是:
完成的native-lib.cpp的代码
#include <jni.h>
#include <string>
#include <android/bitmap.h>
#include <opencv2/opencv.hpp>
#include <iostream>
#include <android/log.h>
#define LOG_TAG "System.out"
#define LOGI(...) __android_log_print(ANDROID_LOG_INFO, LOG_TAG, __VA_ARGS__)
extern "C" JNIEXPORT jstring JNICALL
Java_dem_vac_opencvdemo_MainActivity_stringFromJNI(
JNIEnv *env,
jobject /* this */) {
std::string hello = "Hello from C++";
return env->NewStringUTF(hello.c_str());
}
//将Mat转换为bitmap
jobject mat2bitmap(JNIEnv *env, cv::Mat &src, bool needPremultiplyAlpha, jobject bitmap_config) {
jclass java_bitmap_class = (jclass) env->FindClass("android/graphics/Bitmap");
jmethodID mid = env->GetStaticMethodID(java_bitmap_class, "createBitmap",
"(IILandroid/graphics/Bitmap$Config;)Landroid/graphics/Bitmap;");
jobject bitmap = env->CallStaticObjectMethod(java_bitmap_class,
mid, src.size().width, src.size().height,
bitmap_config);
AndroidBitmapInfo info;
void *pixels = 0;
try {
CV_Assert(AndroidBitmap_getInfo(env, bitmap, &info) >= 0);
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) {
cv::Mat tmp(info.height, info.width, CV_8UC4, pixels);
if (src.type() == CV_8UC1) {
cvtColor(src, tmp, cv::COLOR_GRAY2RGBA);
} else if (src.type() == CV_8UC3) {
cvtColor(src, tmp, cv::COLOR_RGB2BGRA);
} else if (src.type() == CV_8UC4) {
if (needPremultiplyAlpha) {
cvtColor(src, tmp, cv::COLOR_RGBA2mRGBA);
} else {
src.copyTo(tmp);
}
}
} else {
// info.format == ANDROID_BITMAP_FORMAT_RGB_565
cv::Mat tmp(info.height, info.width, CV_8UC2, pixels);
if (src.type() == CV_8UC1) {
cvtColor(src, tmp, cv::COLOR_GRAY2BGR565);
} else if (src.type() == CV_8UC3) {
cvtColor(src, tmp, cv::COLOR_RGB2BGR565);
} else if (src.type() == CV_8UC4) {
cvtColor(src, tmp, cv::COLOR_RGBA2BGR565);
}
}
AndroidBitmap_unlockPixels(env, bitmap);
return bitmap;
} catch (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 bitmap;
} catch (...) {
AndroidBitmap_unlockPixels(env, bitmap);
jclass je = env->FindClass("java/lang/Exception");
env->ThrowNew(je, "Unknown exception in JNI code {nMatToBitmap}");
return bitmap;
}
}
extern "C"
JNIEXPORT jobject JNICALL
Java_dem_vac_opencvdemo_MainActivity_bmp2gray(JNIEnv *env, jobject instance, jobject bmp) {
// TODO
AndroidBitmapInfo bitmapInfo;
void *pixelscolor;
int ret;
//获取图像信息,如果返回值小于0就是执行失败
if ((ret = AndroidBitmap_getInfo(env, bmp, &bitmapInfo)) < 0) {
LOGI("AndroidBitmap_getInfo failed! error-%d", ret);
return NULL;
}
//判断图像类型是不是RGBA_8888类型
if (bitmapInfo.format != ANDROID_BITMAP_FORMAT_RGBA_8888) {
LOGI("BitmapInfoFormat error");
return NULL;
}
//获取图像像素值
if ((ret = AndroidBitmap_lockPixels(env, bmp, &pixelscolor)) < 0) {
LOGI("AndroidBitmap_lockPixels() failed ! error=%d", ret);
return NULL;
}
//生成源图像
cv::Mat src(bitmapInfo.height, bitmapInfo.width, CV_8UC4, pixelscolor);
//将图像转为灰度图
cv::cvtColor(src, src, cv::COLOR_RGBA2GRAY);
//获取原图片的参数
jclass java_bitmap_class = (jclass) env->FindClass("android/graphics/Bitmap");
jmethodID mid = env->GetMethodID(java_bitmap_class, "getConfig",
"()Landroid/graphics/Bitmap$Config;");
jobject bitmap_config = env->CallObjectMethod(bmp, mid);
//将SRC转换为图片
jobject _bitmap = mat2bitmap(env, src, false, bitmap_config);
AndroidBitmap_unlockPixels(env, bmp);
return _bitmap;
}
下图中左边为点开程序加载的图像,右边为点击测试后转为的灰度图
-END-