概述
在封装so库的时候,底层c++代码的实现使用了OpenCV对图片进行操作,而Android中从摄像头中获取到的图片数据类型是Bitmap数据类型的,所以这里就避免不了做数据类型的转换。转换的方案主要有两种,
-
一种是封装的JNI接口方法直接接收cv::Mat数据类型的参数,
-
第二种是JNI方法的接口接收Bitmap数据类型的参数,在JNI中实现Bitmap到cv::Mat的转换。
– 资料
JNI将Android Bitmap转为OpenCV的Mat_jni bitmap 转为mat_修炼之路的博客-CSDN博客
使用OpenCV的SDK实现数据类型的转换
第一种方法,如果想在Android中使用cv::Mat的数据类型,我们可以直接通过导入OpenCV的SDK,然后通过opencv的Utils实现bitmap到cv::Mat的转换。
缺点在于需要添加opencv的库文件,会导致最终的apk文件变大。
Android studio中添加OpenCV SDK参考:Android 使用OpenCV SDK 开源库—傻瓜式教程_opencvsdk用法_sunbofiy23的博客-CSDN博客
方式一:
import org.opencv.android.Utils; Mat mat = new Mat(); Bitmap bmp32 = bmp.copy(Bitmap.Config.ARGB_8888, true); Utils.bitmapToMat(bmp32, mat);
在JNI中接收Mat参数的时候,使用Jobject类型,然后再做个强制转换就行了
方式二:
Bitmap bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.my_image); Mat mat = new Mat(bitmap.getHeight(), bitmap.getWidth(), CvType.CV_8UC4); Utils.bitmapToMat(bitmap, mat);
其中,CvType.CV_8UC4 表示位图的数据类型为 8 位无符号整数,该类型表示每个像素使用 4 个通道(RGBA)。
方式三:
Bitmap bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.my_image); Mat mat = new Mat(); mat.create(bitmap.getHeight(), bitmap.getWidth(), CvType.CV_8UC4); Utils.bitmapToMat(bitmap, mat);
其中,mat.create 方法用于创建一个空的 cv::Mat 对象,并指定其行数、列数和数据类型。
需要注意的是,Utils.bitmapToMat 是 OpenCV 中的辅助方法,用于将 Android 的 Bitmap 转换为 cv::Mat,需要在使用前确保已经导入 OpenCV 库。
JNI实现Bitmap到Mat的转换
– 代码1–CSDN找的
JNI将Android Bitmap转为OpenCV的Mat_jni bitmap 转为mat_修炼之路的博客-CSDN博客
#include #include
#include #include #define ASSERT(status, ret) if (!(status)) { return ret; } #define ASSERT_FALSE(status) ASSERT(status, false) bool BitmapToMatrix(JNIEnv * env, jobject obj_bitmap, cv::Mat & matrix) { void * bitmapPixels; // Save picture pixel data AndroidBitmapInfo bitmapInfo; // Save picture parameters ASSERT_FALSE( AndroidBitmap_getInfo(env, obj_bitmap, &bitmapInfo) >= 0); // Get picture parameters ASSERT_FALSE( bitmapInfo.format == ANDROID_BITMAP_FORMAT_RGBA_8888 || bitmapInfo.format == ANDROID_BITMAP_FORMAT_RGB_565 ); // Only ARGB? 8888 and RGB? 565 are supported ASSERT_FALSE( AndroidBitmap_lockPixels(env, obj_bitmap, &bitmapPixels) >= 0 ); // Get picture pixels (lock memory block) ASSERT_FALSE( bitmapPixels ); if (bitmapInfo.format == ANDROID_BITMAP_FORMAT_RGBA_8888) { cv::Mat tmp(bitmapInfo.height, bitmapInfo.width, CV_8UC4, bitmapPixels); // Establish temporary mat tmp.copyTo(matrix); // Copy to target matrix } else { cv::Mat tmp(bitmapInfo.height, bitmapInfo.width, CV_8UC2, bitmapPixels); cv::cvtColor(tmp, matrix, cv::COLOR_BGR5652RGB); } //convert RGB to BGR cv::cvtColor(matrix,matrix,cv::COLOR_RGB2BGR); AndroidBitmap_unlockPixels(env, obj_bitmap); // Unlock return true; } bool MatrixToBitmap(JNIEnv * env, cv::Mat & matrix, jobject obj_bitmap) { void * bitmapPixels; // Save picture pixel data AndroidBitmapInfo bitmapInfo; // Save picture parameters ASSERT_FALSE( AndroidBitmap_getInfo(env, obj_bitmap, &bitmapInfo) >= 0); // Get picture parameters ASSERT_FALSE( bitmapInfo.format == ANDROID_BITMAP_FORMAT_RGBA_8888 || bitmapInfo.format == ANDROID_BITMAP_FORMAT_RGB_565 ); // Only ARGB? 8888 and RGB? 565 are supported ASSERT_FALSE( matrix.dims == 2 && bitmapInfo.height == (uint32_t)matrix.rows && bitmapInfo.width == (uint32_t)matrix.cols ); // It must be a 2-dimensional matrix with the same length and width ASSERT_FALSE( matrix.type() == CV_8UC1 || matrix.type() == CV_8UC3 || matrix.type() == CV_8UC4 ); ASSERT_FALSE( AndroidBitmap_lockPixels(env, obj_bitmap, &bitmapPixels) >= 0 ); // Get picture pixels (lock memory block) ASSERT_FALSE( bitmapPixels ); if (bitmapInfo.format == ANDROID_BITMAP_FORMAT_RGBA_8888) { cv::Mat tmp(bitmapInfo.height, bitmapInfo.width, CV_8UC4, bitmapPixels); switch (matrix.type()) { case CV_8UC1: cv::cvtColor(matrix, tmp, cv::COLOR_GRAY2RGBA); break; case CV_8UC3: cv::cvtColor(matrix, tmp, cv::COLOR_RGB2RGBA); break; case CV_8UC4: matrix.copyTo(tmp); break; default: AndroidBitmap_unlockPixels(env, obj_bitmap); return false; } } else { cv::Mat tmp(bitmapInfo.height, bitmapInfo.width, CV_8UC2, bitmapPixels); switch (matrix.type()) { case CV_8UC1: cv::cvtColor(matrix, tmp, cv::COLOR_GRAY2BGR565); break; case CV_8UC3: cv::cvtColor(matrix, tmp, cv::COLOR_RGB2BGR565); break; case CV_8UC4: cv::cvtColor(matrix, tmp, cv::COLOR_RGBA2BGR565); break; default: AndroidBitmap_unlockPixels(env, obj_bitmap); return false; } } AndroidBitmap_unlockPixels(env, obj_bitmap); // Unlock return true; } JNIEXPORT void JNICALL Java_com_example_MainActivity_JniBitmapExec(JNIEnv * env, jobject /* this */, jobject obj_bitmap, jobject obj_bitmapOut) { cv::Mat matBitmap; bool ret = BitmapToMatrix(env, obj_bitmap, matBitmap); // Bitmap to cv::Mat if (ret == false) { return; } // opencv processing of mat ret = MatrixToBitmap(env, matBitmap, obj_bitmapOut); // Bitmap to cv::Mat if (ret == false) { return; } } 注意在CMakelist文件中添加以下代码,不然编译的时候会报错:
target_link_libraries( # Specifies the target library. #在target_link_libraries中添加下面的依赖项 jnigraphics )
– 代码2 – chatGpt中写的代码
#include
#include using namespace cv; extern "C" JNIEXPORT void JNICALL Java_com_example_android_MainActivity_nativeBitmapToMat(JNIEnv *env, jobject thiz, jobject bitmap) { AndroidBitmapInfo info; void* pixels; Mat img; // 获取 Bitmap 的信息与像素数据 if (AndroidBitmap_getInfo(env, bitmap, &info) != ANDROID_BITMAP_RESULT_SUCCESS || AndroidBitmap_lockPixels(env, bitmap, &pixels) != ANDROID_BITMAP_RESULT_SUCCESS) { return; } // 创建 Mat 对象并拷贝像素数据 img.create(info.height, info.width, CV_8UC4); memcpy(img.data, pixels, info.stride * info.height); // 解锁像素数据,释放资源 AndroidBitmap_unlockPixels(env, bitmap); }
– 代码3 – paddle fastdeploy中 实现
https://github.com/PaddlePaddle/FastDeploy/blob/develop/java/android/fastdeploy/src/main/cpp/fastdeploy_jni/bitmap_jni.cc
#include #include
cv::Mat CreateZeroCopyRGBAFromBitmap(JNIEnv *env, jobject j_argb8888_bitmap) { cv::Mat c_rgba; AndroidBitmapInfo j_bitmap_info; if (AndroidBitmap_getInfo(env, j_argb8888_bitmap, &j_bitmap_info) < 0) { LOGE("Invoke AndroidBitmap_getInfo() failed!"); return c_rgba; } if (j_bitmap_info.format != ANDROID_BITMAP_FORMAT_RGBA_8888) { LOGE("Only Bitmap.Config.ARGB8888 color format is supported!"); return c_rgba; } void *j_bitmap_pixels; if (AndroidBitmap_lockPixels(env, j_argb8888_bitmap, &j_bitmap_pixels) < 0) { LOGE("Invoke AndroidBitmap_lockPixels() failed!"); return c_rgba; } cv::Mat j_bitmap_im(static_cast (j_bitmap_info.height), static_cast (j_bitmap_info.width), CV_8UC4, j_bitmap_pixels); // no copied. c_rgba = j_bitmap_im; // ref only. if (AndroidBitmap_unlockPixels(env, j_argb8888_bitmap) < 0) { LOGE("Invoke AndroidBitmap_unlockPixels() failed!"); return c_rgba; } return c_rgba; } jboolean ARGB888Bitmap2RGBA(JNIEnv *env, jobject j_argb8888_bitmap, cv::Mat *c_rgba) { // Convert the android bitmap(ARGB8888) to the OpenCV RGBA image. Actually, // the data layout of ARGB8888 is R, G, B, A, it's the same as CV RGBA image, // so it is unnecessary to do the conversion of color format, check // https://developer.android.com/reference/android/graphics/Bitmap.Config#ARGB_8888 // to get the more details about Bitmap.Config.ARGB8888 *c_rgba = CreateZeroCopyRGBAFromBitmap(env, j_argb8888_bitmap); if (c_rgba->empty()) { return JNI_FALSE; } return JNI_TRUE; } jboolean ARGB888Bitmap2BGR(JNIEnv *env, jobject j_argb8888_bitmap, cv::Mat *c_bgr) { cv::Mat c_rgba; if (!ARGB888Bitmap2RGBA(env, j_argb8888_bitmap, &c_rgba)) { return JNI_FALSE; } // TODO: Use the neon instruction to optimize this conversion. // COLOR_RGBA2BGR will allocate memories for new mat. cv::cvtColor(c_rgba, *(c_bgr), cv::COLOR_RGBA2BGR); return JNI_TRUE; } jboolean RGBA2ARGB888Bitmap(JNIEnv *env, jobject j_argb8888_bitmap, const cv::Mat &c_rgba) { AndroidBitmapInfo j_bitmap_info; if (AndroidBitmap_getInfo(env, j_argb8888_bitmap, &j_bitmap_info) < 0) { LOGE("Invoke AndroidBitmap_getInfo() failed!"); return JNI_FALSE; } void *j_bitmap_pixels; if (AndroidBitmap_lockPixels(env, j_argb8888_bitmap, &j_bitmap_pixels) < 0) { LOGE("Invoke AndroidBitmap_lockPixels() failed!"); return JNI_FALSE; } // no copied, but point to bitmap data. cv::Mat j_bitmap_im(static_cast (j_bitmap_info.height), static_cast (j_bitmap_info.width), CV_8UC4, j_bitmap_pixels); // TODO: Use zero copy operation or neon to boost performance. c_rgba.copyTo(j_bitmap_im); if (AndroidBitmap_unlockPixels(env, j_argb8888_bitmap) < 0) { LOGE("Invoke AndroidBitmap_unlockPixels() failed!"); return JNI_FALSE; } return JNI_TRUE; } jboolean BGR2ARGB888Bitmap(JNIEnv *env, jobject j_argb8888_bitmap, const cv::Mat &c_bgr) { if (c_bgr.empty()) { return JNI_FALSE; } cv::Mat c_rgba; cv::cvtColor(c_bgr, c_rgba, cv::COLOR_BGR2RGBA); return RGBA2ARGB888Bitmap(env, j_argb8888_bitmap, c_rgba); } – 测试代码
// // Created by admin on 2023/4/14. // #include
#include #include #include #include #include extern "C" { // bitmap --> cv::Mat bool BitmapToMatrix(JNIEnv * env, jobject obj_bitmap, cv::Mat & matrix) { void * bitmapPixels; // Save picture pixel data AndroidBitmapInfo bitmapInfo; // Save picture parameters AndroidBitmap_getInfo(env, obj_bitmap, &bitmapInfo); // Get picture parameters //Only ARGB_8888 and RGB_565 are supported if (bitmapInfo.format != ANDROID_BITMAP_FORMAT_RGBA_8888 && bitmapInfo.format != ANDROID_BITMAP_FORMAT_RGB_565) { __android_log_print(ANDROID_LOG_INFO, "ncnn", "BitmapToMatrix err--1"); return false; } AndroidBitmap_lockPixels(env, obj_bitmap, &bitmapPixels); // Get picture pixels (lock memory block) if (not bitmapPixels) { __android_log_print(ANDROID_LOG_INFO, "ncnn", "BitmapToMatrix err--2"); return false; } if (bitmapInfo.format == ANDROID_BITMAP_FORMAT_RGBA_8888) { cv::Mat tmp(bitmapInfo.height, bitmapInfo.width, CV_8UC4, bitmapPixels); // Establish temporary mat tmp.copyTo(matrix); // Copy to target matrix } else { cv::Mat tmp(bitmapInfo.height, bitmapInfo.width, CV_8UC2, bitmapPixels); cv::cvtColor(tmp, matrix, cv::COLOR_BGR5652RGB); } //convert RGB to BGR cv::cvtColor(matrix,matrix,cv::COLOR_RGB2BGR); AndroidBitmap_unlockPixels(env, obj_bitmap); // Unlock return true; } //bitmap --> cv::Mat bool nativeBitmapToMat(JNIEnv *env, jobject bitmap, cv::Mat& img) { AndroidBitmapInfo info; void* pixels; // 获取 Bitmap 的信息与像素数据 if (AndroidBitmap_getInfo(env, bitmap, &info) != ANDROID_BITMAP_RESULT_SUCCESS || AndroidBitmap_lockPixels(env, bitmap, &pixels) != ANDROID_BITMAP_RESULT_SUCCESS) { __android_log_print(ANDROID_LOG_INFO, "ncnn", "nativeBitmapToMat err"); return false; } // 创建 Mat 对象并拷贝像素数据 img.create(info.height, info.width, CV_8UC4); memcpy(img.data, pixels, info.stride * info.height); // cv::imwrite("bitmap2mat_2.jpg", img); // 解锁像素数据,释放资源 AndroidBitmap_unlockPixels(env, bitmap); return true; } JNIEXPORT jstring JNICALL Java_com_example_ad_1test_MainActivity_detectOneImage(JNIEnv* env, jobject thiz, jlong ptr, jobject bitmap) { cv::Mat img_tt; BitmapToMatrix(env, bitmap, img_tt); __android_log_print(ANDROID_LOG_INFO, "ncnn", "BitmapToMatrix 转换完成"); cv::Mat img_tt2; nativeBitmapToMat(env, bitmap, img_tt2); __android_log_print(ANDROID_LOG_INFO, "ncnn", "nativeBitmapToMat 转换完成"); std::string result = "reinterpret_cast (ptr)->detectOneImage(img_tt)转换完成"; return env->NewStringUTF(result.c_str()); }; }