一、Android Bitmap 转 cv::Mat
要将Android Bitmap
转换为cv::Mat
格式,可以使用OpenCV库中的数据结构和函数。以下是一个高效简洁的方法来完成这一任务:
1. 安装OpenCV和Android NDK
确保你已经在Android项目中配置了OpenCV和Android NDK。你可以参考OpenCV的官方文档来配置你的Android项目。
2. 将Bitmap
转换为cv::Mat
以下是Kotlin和C++代码的示例,演示如何将Bitmap
对象转换为cv::Mat
对象。
Kotlin代码
external fun convertBitmapToMat(bitmap: Bitmap): Long
fun main() {
val bitmap: Bitmap = ... // 获取或生成Bitmap对象
val matAddr = convertBitmapToMat(bitmap)
if (matAddr != 0L) {
val mat = Mat(matAddr) // 使用matAddr在Java/Kotlin中构建cv::Mat对象
// 可以在这里使用mat进行进一步的处理
}
}
C++代码
首先,确保在C++代码中包含OpenCV头文件和Android Bitmap处理头文件:
#include <jni.h>
#include <opencv2/opencv.hpp>
#include <android/bitmap.h>
using namespace cv;
extern "C"
JNIEXPORT jlong JNICALL
Java_com_example_yourapp_MainActivity_convertBitmapToMat(JNIEnv *env, jobject obj, jobject bitmap) {
AndroidBitmapInfo info;
void* pixels = nullptr;
cv::Mat mat;
// 获取Bitmap信息
if (AndroidBitmap_getInfo(env, bitmap, &info) < 0) {
return 0; // 返回0表示失败
}
// 锁定Bitmap以获取像素数据
if (AndroidBitmap_lockPixels(env, bitmap, &pixels) < 0) {
return 0; // 返回0表示失败
}
// 将Bitmap转换为cv::Mat
if (info.format == ANDROID_BITMAP_FORMAT_RGBA_8888) {
mat = cv::Mat(info.height, info.width, CV_8UC4, pixels);
} else if (info.format == ANDROID_BITMAP_FORMAT_RGB_565) {
mat = cv::Mat(info.height, info.width, CV_8UC2, pixels);
cv::cvtColor(mat, mat, cv::COLOR_BGR5652BGR);
} else {
// 解锁Bitmap
AndroidBitmap_unlockPixels(env, bitmap);
return 0; // 不支持的格式
}
// 复制数据以确保在Bitmap解锁后仍然可用
mat = mat.clone();
// 解锁Bitmap
AndroidBitmap_unlockPixels(env, bitmap);
// 返回cv::Mat对象的指针地址
return reinterpret_cast<jlong>(new cv::Mat(mat));
}
解释
-
获取Bitmap信息:
使用AndroidBitmap_getInfo(env, bitmap, &info)
获取Bitmap
的宽度、高度和格式等信息。 -
锁定Bitmap以获取像素数据:
使用AndroidBitmap_lockPixels(env, bitmap, &pixels)
锁定Bitmap
并获取指向其像素数据的指针。 -
将Bitmap转换为cv::Mat:
根据Bitmap
的格式创建对应的cv::Mat
对象。对于RGBA_8888
格式,直接创建CV_8UC4
的cv::Mat
。对于RGB_565
格式,创建CV_8UC2
的cv::Mat
并使用cvtColor
进行颜色转换。 -
复制数据:
使用clone()
方法复制cv::Mat
对象的数据,以确保在解锁Bitmap
后数据仍然可用。 -
解锁Bitmap:
使用AndroidBitmap_unlockPixels(env, bitmap)
解锁Bitmap
对象。 -
返回cv::Mat对象的指针地址:
返回新创建的cv::Mat
对象的指针地址。在Kotlin中,可以使用这个指针地址来创建cv::Mat
对象进行进一步处理。
通过这种方式,你可以高效且简洁地将Bitmap
对象转换为cv::Mat
对象,并在C++代码中使用OpenCV进行进一步的图像处理。
二、 cv::Mat 转回 Android Bitmap
在 C++ 中,如果你使用了 OpenCV 将 Bitmap
转换为 cv::Mat
格式,可以通过以下步骤将 cv::Mat
转回 Bitmap
。
假设你已经有一个 cv::Mat
类型的图像 mat
,你可以使用以下步骤将其转换回 Bitmap
:
-
获取
cv::Mat
的数据:
首先,你需要从cv::Mat
中获取像素数据。注意,cv::Mat
的数据是连续的,可以直接使用指针操作。 -
创建
Bitmap
对象:
你需要使用与原始Bitmap
相同的规格创建一个新的Bitmap
对象,确保大小和格式与cv::Mat
一致。 -
将
cv::Mat
数据复制到Bitmap
:
你可以使用Bitmap
提供的copyPixelsFromBuffer
或者直接操作Bitmap
的内存来复制数据。
以下是一个示例代码:
#include <opencv2/opencv.hpp>
#include <jni.h>
#include <android/bitmap.h>
// 假设有一个 cv::Mat 类型的图像 mat 和 JNIEnv* env, jobject bitmap
void matToBitmap(JNIEnv* env, cv::Mat &mat, jobject bitmap) {
void* bitmapPixels;
AndroidBitmapInfo info;
// 获取 Bitmap 信息
AndroidBitmap_getInfo(env, bitmap, &info);
// 锁定 Bitmap,以便将数据写入其中
AndroidBitmap_lockPixels(env, bitmap, &bitmapPixels);
// 假设 mat 的类型与 bitmap 的格式匹配,例如都是 CV_8UC4 (ARGB_8888)
// 如果类型不匹配,需要先转换 mat 的类型
if (mat.type() == CV_8UC4) {
memcpy(bitmapPixels, mat.data, mat.total() * mat.elemSize());
} else {
// 其他类型的转换可以使用 cv::cvtColor 来实现
cv::Mat temp;
cv::cvtColor(mat, temp, cv::COLOR_RGB2BGRA);
memcpy(bitmapPixels, temp.data, temp.total() * temp.elemSize());
}
// 解锁 Bitmap
AndroidBitmap_unlockPixels(env, bitmap);
}
这个示例代码假定 cv::Mat
和 Bitmap
的格式是匹配的(例如都是 CV_8UC4
格式)。如果它们不匹配,你需要使用 cv::cvtColor
等 OpenCV 函数来进行格式转换。此外,请确保在调用 JNI 函数时处理可能的异常情况。