android中通過JNI讀取Bitmap文件,並調用opencv進行處理

在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

待調好了這種方式,再作補充。


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章