YUV420 有YU12、YV12、NV12、NV21
YU12存储格式是
YU12存储格式是YU13中的UV顺序反过来
NV12存储格式是
NV21是NV12数据取反
YUV420_888
是YCbCr的泛化格式,不会具体指明是YU12,YV12,NV12,或是是NV21。它能够表示任何4:2:0的平面和半平面格式.
Image.Plane[] planes = image.getPlanes();
planes[0]
总是Y ,planes[1]
总是U(Cb), planes[2]
总是V(Cr)
pixelStride(通过getPixelStride()获得):像素步长,有可能是1、有可能是2。它代表的是行内连续两个颜色值之间的距离(步长)。
rowStride:(通过getRowStride()获得)“每行数据”的“宽度”,这个跟分辨率的宽度不是同个回事,它是每一行实际存储的宽度
width和height 代表分辨率
buffer size 这个主要就是plane数组的大小,一般就通过plane[i].length获取即可。
(1)、Planar格式(P):
1、先看一下6*4的假设图片:
plane[0]的pixelStride是1,说明没有间隔,Y是连续的,rowStride是6,也就是每行6个,length数量是24,24/6 = 4,共4行。
plane[1]的pixelStride也是1,说明没有间隔,U是连续的,rowStride是3,也就是每行3个,length数量是6,6/3 = 2,共2行,符合YUV420的情况,横纵都是2:1采样。
plane[2]与plane[1]相同。
这种其实就是YUV420P的标准格式,跟我们期望的差不多,不用做多解析,直接按照这样将y、u、v分别取出,即是正确的数据。可惜的是,目前测到的手机大部分不是这样的格式,而是下面要介绍的这类SP的情况出现的多一些。
(2)、Semi-Planar格式(SP):
还是先看一下6*4的假设图片:
plane[0]的pixelStride是1,说明没有间隔,Y是连续的,rowStride是6,也就是每行6个,length数量是24,24/6 = 4,共4行。这个Y分量跟Planar格式是一样的。
plane[1]的pixelStride是2,说明有间隔,U是间隔采取的,这里就回到上面我们分析的两个参数的时候,当pixelStride为2的时候,在U分量中,就会间隔插入了V分量,因此每一行由本来是Y的一半也就是3,变成了6(也就是rowStride的值)。同时就像上面分析的一样,会放弃掉最后一个无意义的V分量,所以就lenght会看到是6*2-1=11的,行数还是2,纵向是不变的。
plane[2]与plane[1]相同。
对于这种Semi-Planar格式的,安卓提供的这种方式确实就让人很意向不到,在这种格式下,其实我们有几种取数据方式,首先Y是完整的,直接取即可。对于UV分量可以有2种方式:
I、plane[1]中以索引0,2,4间隔方式去取U分量,plane[2]中以索引0,2,4间隔方式去取V分量,这样就取到了最准确的U和V分量;
II、我们其实可以看到,在plane[1]中其实就包含了U和V分量了,只不过丢掉了最后一个V,对于人眼来说,少点一个V,是完全没有影响的。因此其实可以直接拿plane[1]的数据,就拿到U和V。plane[2]同理其实也是有V和U,那么这样的话,其实就可以plane[0] + plane[1] 可得NV12格式;或者plane[0] + plane[2] 可得NV21格式。
Semi-Planar格式在大多数手机中会经常出现,经过上面的分析也能理解为什么U和V的rowStride会和Y一样,而不是一半。以及为什么U和V的数量最后会少一个分量的原因。
(3)、特殊情况:
rowStride除了有P和SP格式而导致不同之外,它其实还有一个重要的作用,就是在一些特殊的摄像头sensor采集的时候,因为芯片处理器要字节对齐取数据而导致的补齐操作等等原因,可能就有点类似之前的文章中视频相关的一些基本概念的stride。
我们举个例子,比如还是图像是6*4的,但是由于某些原因,相机输出的时候,假如rowStride+2了,如下:
plane[0]的pixelStride是1,说明没有间隔,Y是连续的,rowStride本来应该是6,但是这里是8,后面补了两个空的字节,也就是每行8个,length数量是32,32/8 = 4,共4行。这里分析的时候可以这样判断,getWidth()和getHeight()获取到是6和4,6*4是24,发现32与24不对应,就可以初步判断是有补齐的情况了。而Y的rowStride是8,比getWidth()的6多了2,也就可以推测是每行补齐了2个字节。
这样的话在取数据的时候,就需要每行都去丢弃最后的2个空字节。同理plane[1]和plane[2]也是类似。因此对于这类特殊的camera,我们需要根据pixelStride和rowStride与分辨率的关系,去进行一些特殊的处理才行。