AndroidBitmap_lockPixels和AndroidBitmap_unlockPixels的底层逻辑就是在处理bitmap中的数据的时候,把内存锁定,防止像素缓存被改变导致数据变化。
这篇文章有具体介绍相关的机制
下面是Bitmap操作的示例代码:
#include
#include
#include
#include
using namespace cv;
extern "C" {
JNIEXPORT jobject JNICALL Java_com_example_NativeUtils_processImage(JNIEnv *env, jobject obj, jobject bitmap) {
AndroidBitmapInfo info;
void *pixels = 0;
//获取bitmap的信息
if (AndroidBitmap_getInfo(env, bitmap, &info) < 0) {
return NULL;
}
//获取bitmap的像素数据
if (AndroidBitmap_lockPixels(env, bitmap, &pixels) < 0) {
return NULL;
}
//将像素数据转为Mat矩阵
Mat src(info.height, info.width, CV_8UC4, pixels);
//图像处理,这里以灰度化为例
cvtColor(src, src, COLOR_RGBA2GRAY);
//将处理后的图像数据转为Bitmap
jobject newBitmap = createBitmap(env, src);
//释放像素数据
AndroidBitmap_unlockPixels(env, bitmap);
return newBitmap;
}
jobject createBitmap(JNIEnv *env, Mat &src) {
jclass bitmapCls = env->FindClass("android/graphics/Bitmap");
jmethodID createBitmapMethodID = env->GetStaticMethodID(bitmapCls, "createBitmap", "(IIZ)Landroid/graphics/Bitmap;");
jobject newBitmap = env->CallStaticObjectMethod(bitmapCls, createBitmapMethodID, src.cols, src.rows, Bitmap_Config::ARGB_8888);
AndroidBitmapInfo info;
void *pixels = 0;
//获取Bitmap的像素数据
if (AndroidBitmap_getInfo(env, newBitmap, &info) < 0) {
return NULL;
}
if (AndroidBitmap_lockPixels(env, newBitmap, &pixels) < 0) {
return NULL;
}
//将Mat矩阵中的数据复制到Bitmap中
uint8_t *srcPixels = src.data;
uint8_t *dstPixels = reinterpret_cast<uint8_t *>(pixels);
int len = info.width * info.height * 4;
for (int i = 0; i < len; ++i) {
*dstPixels++ = *srcPixels++;
}
//释放像素数据
AndroidBitmap_unlockPixels(env, newBitmap);
return newBitmap;
}
}
Java端代码:
public class NativeUtils {
static {
System.loadLibrary("native-lib");
}
public static native Bitmap processImage(Bitmap bitmap);
}
使用时,可以直接调用NativeUtils中的processImage方法即可。当然,这只是一个简单的示例,实际开发中需要针对不同的图像处理需求进行修改和优化。
在上述代码中,我们使用了createBitmap函数将Mat矩阵中的数据复制到Bitmap中,其中Bitmap_Config::ARGB_8888表示Bitmap的像素格式为ARGB8888,即每个像素点占4个字节。而在Mat矩阵中,我们使用了CV格式来指定矩阵的数据类型。以下是常用的CV格式:
其中,UC表示unsigned char,即无符号字符类型;FC表示float,即浮点数类型。通道数可以根据需要自由组合,例如CV_8UC3表示8位无符号三通道数据。在Mat矩阵中,每个像素点所占的字节数由数据类型和通道数共同决定。例如,CV_8UC3类型的Mat矩阵中,每个像素点占用3个字节。在处理图像时,需要根据图像的实际情况进行不同类型的Mat矩阵的创建和处理。
给bitmap赋像素值的方法有两种:
bitmap.setPixel(int x,int y,color)此方法功能为给bitmap中的某个像素赋RGB值。
参数
x,y
表示该像素的坐标。
color
为整型的RGB值。
bitmap.setPixels(int [] pixels,int index,int stride, int x,int y,int width, int length)
参数
pixels
数组表示像素RGB值
index
表示从数组的那里开始
stride
表示bitmap的跨宽,其中除了一行像素点的个数外还有其他信息,所以通常stride要大于width的值。
x,y
表示从bitmap的哪个坐标开始。
width, length
表示多宽多行
其中记住width*length
要小于或等于pixels的数组长度,否则会抛出异常。
//输入的数据类型可以根据需要更改
public static Bitmap ConvertToBinaryBitmap(float[][] data,float threshold){
int width = data[0].length;
int height = data.length;
//创建初始图像
Bitmap result = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
//bitmap赋值
int[] pixs = new int[width * height];
for(int i=0;i<height;i++){
for(int j=0;j<width;j++){
int x = i*width+j;
//大于阈值,设置为白色
if(data[i][j]>threshold){
int r = ((pixs[x] >> 16) & 0xff)|0xff;
int g = ((pixs[x] >> 8) & 0xff)|0xff;
int b =( pixs[x] & 0xff)|0xff;
pixs[x] = 0xff000000 | (r << 16) | (g << 8) | b;
}
//小于阈值,设置为黑色
else {
pixs[x] = 0xff000000;
}
}
}
//写入图像
result.setPixels(pixs,0,width,0,0,width,height);
return result;
}
Bitmap转化Matrix:
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;
}
Matrix转化Bitmap:
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;
}
convertTo函数的用法
void Mat::convertTo( Mat& m, int rtype, double alpha=1, double beta=0 )
const;
输入参数:
m
目标矩阵。如果m的大小与原矩阵不一样,或者数据类型与参数不匹配,那么在函数convertTo内部会先给m重新分配空间。
rtype
指定从原矩阵进行转换后的数据类型,即目标矩阵m的数据类型。当然,矩阵m的通道数应该与原矩阵一样的。如果rtype是负数,那么m矩阵的数据类型应该与原矩阵一样。
alpha
缩放因子。默认值是1。即把原矩阵中的每一个元素都乘以alpha。
beta
增量。默认值是0。即把原矩阵中的每一个元素都乘以alpha,再加上beta。
功能:
把一个矩阵从一种数据类型转换到另一种数据类型,同时可以带上缩放因子和增量,公式如下:
m(x,y)=saturate_cast<rType>(alpha*(*this)(x,y)+beta);
由于有数据类型的转换,所以需要用saturate_cast来处理数据的溢出。