在android中,通過JNI讀取Bitmap文件並調用opencv進行處理,分爲四步:1,activity中創建Bitmap對象;2,通過JNI定義native方法並傳遞Bitmap對象進去;,3,解析Bitmap對象;4,利用解析數據生成opencv的Mat對象,並進行相關處理。
1,activity中創建Bitmap對象
android支持幾種Bitmap格式,RGBA_8888,RGB_565,RGBA_4444,A_8等等,格式不同存儲方式就不同,在後面解析時的處理方法也自然不同。在activity中讀入圖像文件並創建Bitmap對象時,可以指定Bitmap的格式。爲了後面更易於轉換成Mat,我設定的格式是RGBA_8888。方法如下:
//指定圖像的路徑,這裏是在sd卡中
String src_path = Environment.getExternalStorageDirectory().getPath()+"/up.jpg";
String body_path = Environment.getExternalStorageDirectory().getPath()+"/down.jpg";
//讀取sd卡中的圖像,變爲bitmap對象
File file1 = new File(src_path);
File file2 = new File(body_path);
//若該文件存在
if (file1.exists() && file2.exists()) {
//指定Bitmap的格式
Options opt = new Options();
opt.inPreferredConfig = Bitmap.Config.ARGB_8888;
//讀取文件並創建Bitmap對象
Bitmap bmp1 = BitmapFactory.decodeFile(src_path,opt);
Bitmap bmp2 = BitmapFactory.decodeFile(body_path,opt);
}
2,通過JNI定義native方法並傳遞Bitmap對象進去
要把Bitmap作爲native方法的參數傳遞進去,只要將其定義爲Object類型即可,例如:
public native int bodyDetect(Object bmp1,Object bmp2,double meanRatio,int[] target_rect);
前兩個參數都是Bitmap對象,利用javah生成的頭文件如下:
JNIEXPORT jint JNICALL Java_com_example_helloworld_BodyDetect_bodyDetect (JNIEnv *, jobject, jobject, jobject, jdouble, jintArray);
3,解析Bitmap對象
在jni的cpp文件中將傳進來的Bitmap對象的內容解析出來,主要利用了android的兩個函數:AndroidBitmap_getInfo(...),AndroidBitmap_lockPixels(..),AndroidBitmap_unlockPixels(...)。流程如下:
#ifndef LOGE
#define LOGE(...) __android_log_print(ANDROID_LOG_ERROR,"@",__VA_ARGS__)
#endif
JNIEXPORT jint JNICALL Java_com_example_helloworld_BodyDetect_bodyDetect
(JNIEnv * env, jobject obj, jobject jbmp1, jobject jbmp2, jdouble jmeanRatio,jintArray jtarget_rect)
{
AndroidBitmapInfo bmp1info;
void* bmp1pixels;
AndroidBitmapInfo bmp2info;
void* bmp2pixels;
int height,width,ret,y,x;
//解析bitmap
if ((ret = AndroidBitmap_getInfo(env, jbmp1, &bmp1info)) < 0) {
LOGE("AndroidBitmap_getInfo() failed ! error=%d", ret);
return -1;
}
if ((ret = AndroidBitmap_getInfo(env, jbmp2, &bmp2info)) < 0) {
LOGE("AndroidBitmap_getInfo() failed ! error=%d", ret);
return -1;
}
if (bmp1info.format != ANDROID_BITMAP_FORMAT_RGBA_8888 || bmp1info.format != ANDROID_BITMAP_FORMAT_RGBA_8888) {
LOGE("Bitmap format is not RGBA_8888!");
return -1;
}
if ((ret = AndroidBitmap_lockPixels(env, jbmp1, &bmp1pixels)) < 0) {
LOGE("First Bitmap LockPixels Failed return=%d!", ret);
return -1;
}
if ((ret = AndroidBitmap_lockPixels(env, jbmp2, &bmp2pixels)) < 0) {
LOGE("Second Bitmap LockPixels Failed return=%d!", ret);
return -1;
}
。。。
AndroidBitmap_unlockPixels(env, jbmp1);
AndroidBitmap_unlockPixels(env, jbmp2);
}
4,利用解析數據生成opencv的Mat對象,並進行相關處理。
RGBA_8888格式的Bitmap,一個像素佔32位,分別是A:8bit,R:8bit;G:8bit;B:8bit。對應到opencv的Mat對象,有個一個Mat的構造函數,結果接受外部數據數組並生Mat對象,這將大大方便轉換過程,不用逐像素的操作了。如下:
。。。
//將bitmap轉成灰度圖像
height = bmp1info.height;
width = bmp1info.width;
Mat src(height,width,CV_8UC4,bmp1pixels);
Mat body(height,width,CV_8UC4,bmp2pixels);
if(!(src.data &&body.data)){
LOGE("bitmap failed convert to Mat return=%d!", ret);
return -1;
}
//轉換爲灰度圖像
cvtColor(src,src,CV_RGBA2GRAY);
cvtColor(body,body,CV_RGBA2GRAY);
。。。
這裏由Bitmap對象轉換成Mat這麼容易,是因爲RGBA_8888格式的存儲方式正好與Mat的CV_8UC4對應。如果是其他格式的Bitmap轉成Mat,則要麻煩多了。可以參考幾個帖子:
http://blog.csdn.net/youngc527/article/details/25424729
http://bbs.csdn.net/topics/390773967
http://blog.csdn.net/wwj_748/article/details/8206299
待調好了這種方式,再作補充。