Android CameraX Analyzer ImageProxy YUV_420_888 to NV21
第一次接触YUV格式的图片,在网上查了各种方法转,发现都是Camera和Camera2监控用的,都没有CameraX。
后面没办法只能去研究YUV格式原理,累啊,虽然有说原理资料文档,但是不够细,导致我跑偏了,花了几天时间才解决,记录一下。
一、Camera
Camera预览监控的就是NV21图片格式,网上大把资料,这个我就不写了,网上查询一下就行。
二、Camera2
Camera2返回的是Image对象,Image里面有宽高、步长、getPlanes YUV的数据通道、图片格式等。
具体网络上也有资料,也能查找出来,偷下懒了。
三、CameraX
CameraX监听Analyzer可以拿到实时预览的ImageProxy YUV_420_888的图片。
CameraX只返回YUV_420_888格式的图片,对我们来说就跟好处理了。
1、简介
- YUV编码是流媒体的常用编码方式
- “Y”表示明亮度(Luminance、Luma),“U”和“V”则是色度、浓度(Chrominance、Chroma)
- 主流采样方式:YUV4:4:4,YUV4:2:2,YUV4:2:0
我这主要讲YUV4:2:0,其他可以参考YUV数据格式与YUV_420_888
2、YUV4:2:0
格式类型:I420、NV21、YV12
存储格式:图片宽度 * 图片高度 * 3 / 2 (width * height * 3 / 2)
三个分量:Y分量(width * height),U分量和V分量加起来是Y分量的一半
即U分量(width * height / 2 / 2)、U分量(width * height / 2 / 2)
width * height * 3 / 2 等于 Y分量 + U分量 + V分量的长度
I420格式
字节排列 YYYY YYYY UU VV,全部都是平面型排列,U在V前面
即:Y分量(width * height)+ U分量(width * height / 2 / 2)+ V分量(width * height / 2 / 2)NV21格式
字节排列 YYYY YYYY VU VU,Y平面和VU平面,VU内部是紧凑型
即:Y分量(width * height)+ VU分量(width * height / 2 )YV12格式
字节排列 YYYY YYYY VV UU,全部都是平面型排列,V在U前面
即:Y分量(width * height)+ V分量(width * height / 2 / 2)+ U分量(width * height / 2 / 2)
3、注意
CameraX返回ImageProxy
Y通道长度是(width * height)
image.getPlanes()[0].getBuffer().remaining() == width * height
U通道长度是(width * height / 2 -1)
image.getPlanes()[1].getBuffer().remaining() == width * height / 2 -1
V通道长度是(width * height / 2 -1)
image.getPlanes()[2].getBuffer().remaining() == width * height / 2 -1
4、实现
知道上面原理后,就能根据返回的ImageProxy转换成我们想要的NV21格式了
上代码
/**
* YUV_420_888转NV21
*
* @param image CameraX ImageProxy
* @return byte array
*/
public static byte[] yuv420ToNv21(ImageProxy image) {
ImageProxy.PlaneProxy[] planes = image.getPlanes();
ByteBuffer yBuffer = planes[0].getBuffer();
ByteBuffer uBuffer = planes[1].getBuffer();
ByteBuffer vBuffer = planes[2].getBuffer();
int ySize = yBuffer.remaining();
int uSize = uBuffer.remaining();
int vSize = vBuffer.remaining();
int size = image.getWidth() * image.getHeight();
byte[] nv21 = new byte[size * 3 / 2];
yBuffer.get(nv21, 0, ySize);
vBuffer.get(nv21, ySize, vSize);
byte[] u = new byte[uSize];
uBuffer.get(u);
//每隔开一位替换V,达到VU交替
int pos = ySize + 1;
for (int i = 0; i < uSize; i++) {
if (i % 2 == 0) {
nv21[pos] = u[i];
pos += 2;
}
}
return nv21;
}
参考
详解YUV420数据格式
YUV数据格式与YUV_420_888