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分量的长度

  1. I420格式
    字节排列 YYYY YYYY UU VV,全部都是平面型排列,U在V前面
    即:Y分量(width * height)+ U分量(width * height / 2 / 2)+ V分量(width * height / 2 / 2)

  2. NV21格式
    字节排列 YYYY YYYY VU VU,Y平面和VU平面,VU内部是紧凑型
    即:Y分量(width * height)+ VU分量(width * height / 2 )

  3. 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

你可能感兴趣的:(Android CameraX Analyzer ImageProxy YUV_420_888 to NV21)