本篇文章主要记录一下camera2输出摄像头的编码格式,及数据之间转换。本文章参考链接:
https://blog.csdn.net/qq_39312146/article/details/129252235。
http://www.360doc.com/content/21/0522/14/17136639_978452591.shtml
只为了记录使用。
一、Camera2 获取摄像头数据代码
mImageReader = ImageReader.newInstance(mCameraWidth,mCameraHeight, ImageFormat.YUV_420_888,2);
mImageReader.setOnImageAvailableListener(mOnImageAvailableListener, mChildHandler);private ImageReader.OnImageAvailableListener mOnImageAvailableListener = new ImageReader.OnImageAvailableListener() {@Overridepublic void onImageAvailable(ImageReader reader) {//这里一定要调用reader.acquireNextImage()和img.close方法否则不会一直回掉了Image img = reader.acquireLatestImage();if (img == null) {
// Log.d("lilitest","img is null");return;} else {
// Log.d("lilitest","img is not null");}byte[] bytes = Utils.getDataFromImage(img);}};
上述代码是camera2通过ImageReader获取摄像头图像的主要代码。获取到Image之后,将image转化为byte数据,通过opencv,ffmpeg等工具对摄像头画面进行处理,之后显示。本文主要记录各种编码的区别,及编码格式。
使用ImageReader.newInstance(mCameraWidth, mCameraHeight, ImageFormat.YUV_420_888, 2)创建的ImageReader对象将返回一个支持YUV_420_888格式的图像。但是这个格式仅仅是指定了数据的存储格式,并没有具体指定是NV21还是NV12。
实际上,Android Camera2 API并不直接支持返回NV21或NV12格式的图像,而是以YUV_420_888格式返回原始的YUV数据,并通过Plane对象提供对Y、U和V分量的访问。因此,如果你需要将YUV_420_888格式的图像转换为NV21或NV12格式,你需要手动进行转换。
在Camera2中,ImageReader读取摄像头数据,选择的格式只有ImageFormat.YUV_420_888 和ImageFormat.JPEG两种。
1、图像重要特性
1)image.getPlanes:获取图像的像素平面阵列。返回Plane[],当图像格式设置为ImageFormat.YUV_420_888 时,返回数组长度为3,说明该格式图像由三个单色平面组成。当图像格式设置为ImageFormat.JPEG时,返回数组长度为1,说明该格式图像只有一个单色平面。
2)image.getFormat: 获取此图片的格式。ImageReader设置的是什么格式,这里获取到的就是什么格式。
3)image.getBitsPerPixel:检索ImageFormat的每个像素的位数。
2、单色平面性质
1)plane.getPixelStride : 获取行内连续两个颜色值之间的距离(步长)。我的理解,获取每个平面中两个相邻像素之间的距离。
2)plane.getRowStride:获取行间像素之间的距离。我的理解,获取每行的距离。例如1280X720的图片宽为1280,获取的plane.getRowStride可能也为1280,但是plane.getRowStride有可能与图片的宽度不相同。
3)plane.getBuffer():获取第i个平面的数据,保存到buffer。返回ByteBuffer。
二、camera2 YUV420_888
参考链接:https://blog.csdn.net/qq_39312146/article/details/129252235
是YCbCr的泛化格式,能够表示任何4:2:0的平面和半平面格式,每个分量用8 bits 表示。带有这种格式的图像使用3个独立的Buffer表示,每一个Buffer表示一个颜色平面(Plane),除了Buffer外,它还提供rowStride、pixelStride来描述对应的Plane。
U/V的平(Planar)面和半平面(Semi-Planar)
1、U/V的Planar存储(YUV420P)
YUV420P的平面模式又分为YU12 及 YV12 两种格式。该模式的存储方式为:先保存所有Y分量,然后保存所有U分量,最后保存所有V分量。以4X2 图像为例,三分量保存如下:
该格式具有y u v三个单色平面。对于1280 * 720 图像来说,像素大小为 1280 * 720 = 921600。每个平面获取的特性如下:
1)y 分量平面: PixelStride = 1; RowStride = 1280; 说明该分量每行有1280个像素,每行中两个像素之间的距离为1。并且该平面有720行。
2)u 分量平面:由于YUV420_888采样模式为4:2:0。三个分量的像素分布格式如下。图片中黑色实心点为y分量,白色空心圆为uv分量。从图中可以看出,上下左右四个y像素共用一组uv像素。u 分量平面获取的PixelStride = 1;RowStride = 640.(每行的像素个数是原图像分辨率的一半,通过图中可以看出每行两个y公用一个u,所以u分量上的像素个数是原分辨率的一半)。通过下图可以看出,u平面的像素个数是原像素个数的1/4,可以计算出u平面分量的行数为:1280 * 720 / 4 / 640 = 360。可以看出u平面分量的列数也为原图片分辨率的一半。
3)v分量平面与u相同。
注意:平面式的yuv格式分为yu12和yv12两种,其中yu12的数据封装为先平铺所有的y,再平铺所有的u,最后平铺所有的v。
yv12数据封装为先平铺所有的y,再平铺所有的v,最后平铺所有的u。
2、U/V的Semi-Planar存储 (YUV420SP)
YUV420SP的平面模式又分为NV12 及 NV21两种格式。该模式的存储方式为:先保存所有Y分量,然后交叉保存u分量及v分量。以4X2 图像为例,三分量保存如下:
注意:上图为NV12格式,NV21格式为先v后u,既vuvuvu存储。
该格式具有y u v三个单色平面。对于1280 * 720 图像来说,像素大小为 1280 * 720 = 921600。每个平面获取的特性如下:
1)y 分量平面与420p相同: PixelStride = 1; RowStride = 1280; 说明该分量每行有1280个像素,每行中两个像素之间的距离为1。并且该平面有720行。
2)u 分量平面:由于YUV420_888采样模式为4:2:0。三个分量的像素分布格式如下。图片中黑色实心点为y分量,白色空心圆为uv分量。从图中可以看出,上下左右四个y像素共用一组uv像素。u 分量平面获取的PixelStride = 2;RowStride = 1280.(每行的像素个数是原图像分辨率的一半,通过图中可以看出每行两个y公用一个u,所以u分量上的像素个数是原分辨率的一半)。通过下图可以看出,u平面的像素个数是原像素个数的1/4,可以计算出u平面分量的行数为:1280 * 720 / 4 / 640 = 360。可以看出u平面分量的列数也为原图片分辨率的一半。
3)v分量平面与u相同。
因为uv是交叉分布的,所以索引0 2 4 6 对于u分量有效,索引1 3 5 7 对于v分量有效。
下面计算对于YUV420_888格式每个像素所占用的bit。通过代码获取ImageFormat.getBitsPerPixel(format) = 12。计算方法如下:
假设图像分辨率为w * h,则y分量的像素个数为wh。u分量像素个数为wh/4。v分量像素个数为wh/4。三分量总的像素个数为wh + wh/2 = 3wh/2。对于wh分辨率的图像的像素点有w*h个,每个占8bit,平均每个像素点占的bit = 1.5 * w * h * 8 / w * h = 12。表达的不够清晰。