在AS中,有时会要传递bitmap给c++函数,使用opencv等工具进行处理。这时会将bitmap传递给jni函数。bitmap在jni的函数参数中,是jobject类型。下面介绍如何将传入的bitmap转化成opencv常用的mat类型,从而进行处理
首先要包含bitmap的头文件。这个头文件中定义了一些重要信息。了解这些信息非常必要
AndroidBitmapInfo 一个包含bitmap常用信息的结构体
/** Bitmap info, see AndroidBitmap_getInfo(). */typedef struct{
/** The bitmap width in pixels. */uint32_t width;
/** The bitmap height in pixels. */uint32_t height;
/** The number of byte per row. */uint32_t stride;
/** The bitmap pixel format. See {@link AndroidBitmapFormat} */int32_t format;
/** Unused. */uint32_t flags; // 0 for now} AndroidBitmapInfo;
AndroidBitmapFormat 图片格式的枚举类型。格式为图片的像素位数。最常见的是ANDROID_BITMAP_FORMAT_RGBA_8888
/** Bitmap pixel format. */enumAndroidBitmapFormat {
/** No format. */ANDROID_BITMAP_FORMAT_NONE = 0,
/** Red: 8 bits, Green: 8 bits, Blue: 8 bits, Alpha: 8 bits. **/ANDROID_BITMAP_FORMAT_RGBA_8888 = 1,
/** Red: 5 bits, Green: 6 bits, Blue: 5 bits. **/ANDROID_BITMAP_FORMAT_RGB_565 = 4,
/** Deprecated in API level 13. Because of the poor quality of this configuration, it is advised to use ARGB_8888 instead. **/ANDROID_BITMAP_FORMAT_RGBA_4444 = 7,
/** Alpha: 8 bits. */ANDROID_BITMAP_FORMAT_A_8 = 8,
};
AndroidBitmap_getInfo 获取bitmap信息的函数。返回值为后面的枚举类型。成功读取的返回值为0
/*** Given a java bitmap object, fill out the AndroidBitmapInfo struct for it.* If the call fails, the info parameter will be ignored.*/intAndroidBitmap_getInfo(JNIEnv* env, jobject jbitmap,
AndroidBitmapInfo* info);
/** AndroidBitmap functions result code. */enum{
/** Operation was successful. */ANDROID_BITMAP_RESULT_SUCCESS = 0,
/** Bad parameter. */ANDROID_BITMAP_RESULT_BAD_PARAMETER = -1,
/** JNI exception occured. */ANDROID_BITMAP_RESULT_JNI_EXCEPTION = -2,
/** Allocation failed. */ANDROID_BITMAP_RESULT_ALLOCATION_FAILED = -3,
};
AndroidBitmap_lockPixels 非常关键的一个函数。将bitmap的像素在内存中的地址进行锁定,确保这一块内存不会被移动。传入的addrPtr是一个二级指针,*addPtr是指向bitmap像素地址的指针。该函数正确调用后,一定要在该像素地址指针不再使用后,使用AndroidBitmap_unlockPixels进行释放。
/*** Given a java bitmap object, attempt to lock the pixel address.* Locking will ensure that the memory for the pixels will not move* until the unlockPixels call, and ensure that, if the pixels had been* previously purged, they will have been restored.** If this call succeeds, it must be balanced by a call to* AndroidBitmap_unlockPixels, after which time the address of the pixels should* no longer be used.** If this succeeds, *addrPtr will be set to the pixel address. If the call* fails, addrPtr will be ignored.*/intAndroidBitmap_lockPixels(JNIEnv* env, jobject jbitmap, void** addrPtr);
AndroidBitmap_unlockPixels
/*** Call this to balance a successful call to AndroidBitmap_lockPixels.*/intAndroidBitmap_unlockPixels(JNIEnv* env, jobject jbitmap);
了解以上信息之后,就可以探索如何将传入的bitmap转为需要的mat了。通用的做法是先获取bitmap的信息
AndroidBitmapInfo img_info;
CV_Assert(AndroidBitmap_getInfo(env, img, &img_info) >= 0);
CV_Assert(img_info.format == ANDROID_BITMAP_FORMAT_RGBA_8888);
使用CV_Assert确保获取信息成功
然后初始化一个指针,并将该指针的地址传递给AndroidBitmap_lockPixels,若成功则该指针即指向bitmap的像素在内存中的地址
void*ori_img_pxl = NULL;
CV_Assert(AndroidBitmap_lockPixels(env, img, &ori_img_pxl) >= 0);
CV_Assert(ori_img_pxl);
然后就可以将该指针传递给mat的构造函数,创建出我们需要的mat了。
Mat ori_img_mat(img_info.height, img_info.width, CV_8UC4, ori_img_pxl);
需要注意得到的mat的数据指针是指向bitmap的像素内存的,因此mat中对像素的操作会直接反映在bitmap中
AndroidBitmap_unlockPixels(env, img);
最后要记得释放锁定!