Andorid调用OpenCV过程中Bitmap与Mat类之间数据传递的一些小问题

最近在自学Android开发,要用到OpenCV来进行有关的图像处理,在过程中遇到了一些小问题

小白刚开始学,自己总结了一些原因,可能有错误,还请大家赐教。

下面是我在用OpenCV中的灰度化函数对图片进行灰度化的时候遇到问题和解决办法

参考别人的资料,一般都是将java中的Bitmap转换为数组传入到JNI,然后JNI中将数组转换为Mat类型进行处理,再将处理好的图片通过数组形式传回java层;

下面是将Bitmap转换为数组的过程

//Bitmap bmp = BitmapFactory.decodeResource(getResources(), R.drawable.cat);
        bmp=selectbp;
        int w = bmp.getWidth();
        int h = bmp.getHeight();
        int[] pixels = new int[w * h]; 

上面为JAVA层获取图片大小和每一个像素的代码,pixels数组中保存像素值。

下面是JNI中对图像灰度化的处理过程

Java_com_example_liuxiaodong_myphoto_NDKloader_getGrayImage(JNIEnv *env, jclass type,
                                                            jintArray buf, jint w, jint h) {
    jint *cbuf;
    cbuf = env->GetIntArrayElements(buf, NULL);
    if (cbuf == NULL) {
        return 0;
    }
    Mat image(h, w, CV_8UC4, (unsigned char*) cbuf);
    cvtColor(image,image,COLOR_BGR2GRAY);
    int* outImage = new int[w * h];
    int n = 0;
    for(int i = 0; i < h; i++) {
        uchar* data = image.ptr(i);
        for(int j = 0; j < w; j++) {
                outImage[n++] = data[j];
        }
    }
    int size = w * h;
    jintArray result = env->NewIntArray(size);
    env->SetIntArrayRegion(result, 0, size, outImage);
    env->ReleaseIntArrayElements(buf, cbuf, 0);
    return result;

 

执行如上操作进行灰度化之后,我发现输出的结果与实际不符合,如果生成的Bitmap为ARGB_8888,则输出是空白,如果是RGB_565,则是如下图所示。

 

原图:

Andorid调用OpenCV过程中Bitmap与Mat类之间数据传递的一些小问题_第1张图片

处理后的图:

Andorid调用OpenCV过程中Bitmap与Mat类之间数据传递的一些小问题_第2张图片

然后我在VS中调试发现,在执行完cvtColor(image,image,COLOR_BGR2GRAY)这个函数后,原本的4通道或者三通道的Mat对象变成了单通道,这时候在VS上的输出是没有影响的。但是当我们把image的数据传回的Bitmap类时,却发生了变化,原因如下:

在Bitmap中,不管是四通道还是三通道的图片,取得的pixels数组元素个数都为上面的w*h,以ARGB_8888为例,pixels数组中一个元素保存了该像素点的ARGB四个值的信息,以一个8位的16进制数表示,数据范围为0x00000000-0xffffffff,其中最高的两位表示透明度,然后往下依次是RGB三个通道的信息,每个通道取值都是0-255;所以我们直接输出pixels数组中元素的值的时候,会发现他为一个很大的负数,这是由于32位系统的第一位为符号位,一般图片透明度为255,也就是不透明,所以自然就变成了负数。当我们将数组pixels通过Mat image(h, w, CV_8UC4, (unsigned char*) cbuf)得到Mat对象的时候,由于这里的M  at对象为四通道,所以原本的 w*h的矩阵变成了w*4*h的矩阵,这里我举一个简单的例子。假设有一个3x3像素的图片保存在Bitmap中如下:

 

0xffffffff 0xffffffff   0xffffffff
0xffffffff 0xffffffff 0xffffffff
0xffffffff 0xffffffff 0xffffffff

那么它经过Mat image(h, w, CV_8UC4, (unsigned char*) cbuf)产生的四通道Mat对象image内的数据为:

 

255 255 255 255 255 255 255 255 255 255 255 255
255 255 255 255 255 255 255 255 255 255 255 255
255 255 255 255 255 255 255 255 255 255 255 255

而将image执行完cvtColor(image,image,COLOR_BGR2GRAY)这个函数后,image内的数据变成了

 

255 255 255
255 255 255
255 255 255

所以当Bitmap接收到上面的输出时,如果接收的Bitmap对象是RGB_565格式生成的,那么就代表最低的2位有0-255的值,也就是蓝色通道有不同的值,所以上面的图片为蓝色,而当接收的Bitmap对象是ARGB_8888格式生成的,尽管蓝色通道有值,但是ALPHA通道的值为0.即图片完全透明,所以看到的是空白。

由于Bitmap中没有单通道的表示(可能有但是我不知道),所以要使得Android输出为我们所想要的灰度图,还需要将红色通道和绿色通道的值变成和蓝色通道一样,这样Bitmap就能得到我们想要的结果,这里以RGB_565的Bitmap为例;

在我们经过cvtColor(image,image,COLOR_BGR2GRAY)函数后,将原本的输出变为如下的函数

int* outImage = new int[w * h];
    int n = 0;
    for(int i = 0; i < h; i++) {
        uchar* data = image.ptr(i);
        for(int j = 0; j < w; j++) {
            int a = 0;
            a=trn(env,data[j]);
            outImage[n++] =a;
        }
    }

trn的作用是把0x5f变为0x5f5f5f,即将红色通道和绿色通道的值变成和蓝色通道一样

jint trn(JNIEnv* env, jint in){
    jint out;
    out = (in/ 16) * 256*16 + (in % 16) * 256 + (in % 16) * 256*256+ (in / 16) * 256 * 16*256+in;
    return out;
}

这样我们就可以得到如下所示的灰度图像:

 

希望能对大家有所帮助!Andorid调用OpenCV过程中Bitmap与Mat类之间数据传递的一些小问题_第3张图片

 

总而言之,关键点在于把Mat和Bitmap中的数据保存方式弄清楚~

 

你可能感兴趣的:(OpenCV学习笔记,OPENCV,Android,Bitmap,Mat,JNI)