Android --图像处理相关

1,327 阅读3分钟

Mat、Bitmap、byte

  • Mat转Bitmap
  public static Bitmap matToBitmap(Mat mat) {
        Bitmap bitmap = null;
        if (mat != null) {
            Imgproc.cvtColor(mat, mat, Imgproc.COLOR_BGR2RGB);
            bitmap = Bitmap.createBitmap(mat.width(), mat.height(), Bitmap.Config.ARGB_8888);
            Mat temp = mat.channels() == 1 ? new Mat(width, height, CvType.CV_8UC1, new Scalar(1)) : new Mat(width, height, CvType.CV_8UC4);
           try {
            if (mat.channels() == 3){
                cvtColor(mat, temp, Imgproc.COLOR_RGB2RGBA);
            } else if (mat.channels() == 1){
                cvtColor(mat, temp, Imgproc.COLOR_GRAY2RGBA);
            }
            Utils.matToBitmap(temp, bitmap);
        } catch (CvException e){
            Log.d("DetailActivity", e.getMessage());
        }
        }
        return bitmap;
    }
   
  • byte 转 Bitmap
public static Bitmap bytesToBitmap(byte[] byte) {
        if (byte.length != 0) {
            return BitmapFactory.decodeByteArray(byte, 0, byte.length);
        } else {
            return null;
        }
    }
  
  • bitmap 转 Mat
 private Mat convertBitmapToMat(Bitmap bitmap){
        Mat mat = new Mat(bitmap.getHeight(), bitmap.getWidth(), CvType.CV_8UC4);
        Bitmap bmp32 = bitmap.copy(Bitmap.Config.ARGB_8888, true);
        Utils.bitmapToMat(bmp32, mat);

        Mat rgbMat = new Mat(bitmap.getHeight(), bitmap.getWidth(), CvType.CV_8UC3);
        cvtColor(mat, rgbMat, Imgproc.COLOR_RGBA2BGR, 3);
        return rgbMat;
    }

将camera获取到的YuvData在jni中转化为Mat方法

  • android camera获得的Byte[]格式是NV21(yuv420sp)
  • 获得灰度图:
jbyte * pNV21FrameData = env->GetByteArrayElements(NV21FrameData, 0); //输入yuv数据 
Mat mGray(height, width, CV_8UC1, (unsigned char *)pNV21FrameData);	//构建灰度图时构造函数
env->ReleaseByteArrayElements(NV21FrameData, pNV21FrameData, 0);
  • 获得Rgb彩色图
jbyte * pBuf = (jbyte*)env->GetByteArrayElements(yuv, 0);
Mat image(height + height/2,width,CV_8UC1,(unsigned char *)pBuf);	//注意这里是height+height/2
Mat mBgr;
cvtColor(image, mBgr, CV_YUV2BGR_NV21);
env->ReleaseByteArrayElements(yuv, pBuf, 0); 

Android中可以通过camera获取图像,并实时处理,不同的手机camera支持的的图像格式不同,可以采用getCameraPreviewFormat来得到preview支持的图像编码格式,Android默认使用NV21(yuv420sp)的图像格式,因为大部分手机都支持。

为了达到实时处理的目的,很多时候我们将得到的yuv数据直接传入jni中的c++使用,减少上层转化图像格式的过程,yuv420本身属于单通道图像,若图像处理中只需要获取灰度图像,可以在c++中直接通过Mat构造函数构造生成灰度图像

extern "C"
jboolean
Java_my_project_MyRealTimeImageProcessing_CameraPreview_ImageProcessing(
		JNIEnv* env, jobject thiz,
		jint width, jint height,
		jbyteArray NV21FrameData, jintArray outPixels)
{
	jbyte * pNV21FrameData = env->GetByteArrayElements(NV21FrameData, 0); //输入yuv数据 
	Mat mGray(height, width, CV_8UC1, (unsigned char *)pNV21FrameData);	//构建灰度图时构造函数
	env->ReleaseByteArrayElements(NV21FrameData, pNV21FrameData, 0);
	return true;
}

若图像处理时需要彩色图像,则需要先将nv21类型的数据转化为yuv格式,再将yuv转化为BGR彩色图像,构造yuv时的构造函数和直接构造灰度图有些不同,在YUV420中一个像素对应一个Y,一个2*2的小方块对应一个UV,对于所有YUV420图像,它们的Y值排列是完全相同的,因为只有Y的图像就是灰度图像。YUV420sp与YUV420p的数据格式它们的UV排列在原理上是完全不同的。420p它是先把U存放完后,再存放V,也就是说UV它们是连续的。而420sp它是UV、UV这样交替存放的。对于一个YUV420在内存中存放的大小:

Y = width*height
U = Y/4
v = Y/4

所以获取灰度图只需要Y的数据大小就可以,所以构造函数中宽高都是图像的宽高,而要获取YUV彩色图像则需要获取的内存长度为widthheight3/2

构造函数中图像高度需再加上1/2*height,代码如下:

JNIEXPORT int JNICALL Java_com_ProjectName_nativecaller_ClassName_readYUV420SP(JNIEnv *env, jclass clz, jbyteArray yuv,jint len,jint height,jint width)
{
 
	jbyte * pBuf = (jbyte*)env->GetByteArrayElements(yuv, 0);
 
	Mat image(height + height/2,width,CV_8UC1,(unsigned char *)pBuf);	//注意这里是height+height/2
	Mat mBgr;
	cvtColor(image, mBgr, CV_YUV2BGR_NV21);
	imwrite("/mnt/sdcard/readYuv.jpg",mBgr);
	env->ReleaseByteArrayElements(yuv, pBuf, 0);  
	return 0;
}