YUV数据和格式

一、YUV简介

1.YUV说明

 YUV是一种颜色编码方法, 与RGB(红 - 绿 - 蓝)不同。

        <1>Y表示亮度分量,也叫灰阶值:如果只显示Y,图片会是一张黑白照

        <2>U(Cb)表示色度分量:是照片蓝色部分去掉亮度

        <3>V(Cr)表示色度分量:是照片红色部分去掉亮度

2.YUV存储格式

YUV包括紧缩格式(packed )和平面格式(planar )两种格式。

2.1.紧缩格式(packed )

        紧缩格式(packed format)将Y、U、V值储存成Macro Pixels阵列(YUV是混合在一起的),和RGB的存放方式类似。对于YUV4:4:4格式而言,用紧缩格式很合适的,因此就有了UYVY、YUYV等。

2.2.平面格式(planar )

        平面格式(planar formats) 将Y、U、V的三个分量分别存放在不同的矩阵中,U分量必须在Y分量后面,而V分量在U分量后面。平面格式(planar format)有I420(4:2:0)、YV12、IYUV等。

扩展阅读:

VLC提供的wiki

微软Video Rendering with 8-Bit YUV Formats

3.扫描线(scan line)

        扫描线是用来描述电视是如何显示画面的,wiki中是这么解释的:

        电视萤幕由电子枪射出的电子,经由磁场偏向后打在屏幕上而发光,因此每一个图框都由电子枪的扫描线画出来。电子枪的扫描线从左上角像素点到右下角像素点顺序移动,喷射电子显像。

在这里插入图片描述

二、常用的YUV格式

        为节省带宽,大多数YUV格式平均使用的每像素位数都少于24位元。主要的抽样(subsample)格式有YCbCr 4:2:0、YCbCr 4:2:2、YCbCr 4:1:1和YCbCr 4:4:4。

        用三个图来直观地表示采集的方式吧,以黑点表示采样该像素点的Y分量,以空心圆圈表示采用该像素点的UV分量。

image

YUV的表示法称为A:B:C表示法:

  • 4:4:4表示完全取样,每1个Y对应一组UV分量
  • 4:2:2表示2:1的水平取样,垂直完全采样,每2个Y共用一组UV分量
  • 4:2:0表示2:1的水平取样,垂直2:1采样,每4个Y共用一组UV分量
  • 4:1:1表示4:1的水平取样,垂直完全采样。

 

1.YUV4:4:4

YUV4:4:4的采样方式表示:各采样分量在扫面每个像素点时,都不会降低采样率:

在这里插入图片描述

        之所以用四个方格显示,是因为YUV格式中,UV分量最小时需要四个像素共享一个UV分量对。同时,共享一个UV分量对的像素点,在平面上和UV分量都有临近的关系,所以这四个像素点不会是同一条扫面线上的点,而是分布在两条扫描线上。

        一个宏像素最多容纳四个宏像素点,而在YUV4:X:X的表示法中,的4表达的也是这个意思。

        从图可以看出,YUV4:4:4的采样方式是对每个像素点进行Y、U、V分量的全采样。

        关于内存占用:因为YUV模式的每个分量都是存储在一个字节(8bit)中的。所以YUV4:4:4格式需要4*8 + 4*8 + 4*8 = 96位,因此,每个像素深度为24位。

2.YUV4:2:2

        YUV4:2:2的采样方式表示:水平方向Y分量与UV分量2:1采样,垂直方向不降低采样率。也就是这样:

在这里插入图片描述

        水平方向上的两个像素点组成了一个宏像素,两个像素点共享一对UV像素分量。

        对于四个像素,YUV4:2:2格式需要4*8 + 2*8 + 2*8 = 64位,每个像素深度为16位。

3.YUV4:2:0

        目前YUV4:2:0有两种变体,一种用于MPEG-1标准如下图:

在这里插入图片描述

        另一个常用语MPEG-2标准,我们经常见到的4:2:0通常都是这种。如下图:

在这里插入图片描述

        对于四个像素,YUV4:2:0格式需要4*8 + 8 + 8 = 48位,每个像素深度为12位。

4.YUV存储格式

        YUV的存储格式分为打包格式(packet formats)和平面格式(planar formats)。

在打包格式中,Y,U和V组件存储在单个数组中,YUV三个分量是顺序交错存储。 像素被组织成宏像素组,其布局取决于采样格式。

在平面格式中,Y,U和V分量存储在三个不同的平面(数组)中。YUV三个分量被分开存储在三个不同的数组中。

4:4:4,24位像素深度
YUV4:4:4实际上表达的是:采样模式位4:4:4的打包存储的数据。它的存储方式如图:

在这里插入图片描述

一个小方格代表一个字节,一组连续的小方格代表一个像素。

4:2:2,16位像素深度
4:2:2的采样格式共有两种存储方式

YUY2
UYVY
它们的存储方式都是打包格式,其中每个宏像素是两个像素,编码为四个连续字节。

YUY2
在YUY2格式中,中第一个字节包含第一个Y样本,第二个字节包含第一个U(Cb)样本,第三个字节包含第二个Y样本,以及 第四个字节包含第一个V(Cr)样本,如图所示:

在这里插入图片描述

UYVY
这种格式与YUY2相同,只是字节顺序颠倒了 - 也就是说,色度和亮度字节被翻转,如图:

在这里插入图片描述

4:2:0,12位像素深度
下面要介绍的4:2:0格式都采用了平面存储模式,共有四种:

IMC2
IMC4
YV12
NV12
所有的4:2:0模式,色度分量无论是在水平还是垂直方向上,采样数都是亮度分量的1/4。

IMC2
IMC2格式的存储方式如图:

在这里插入图片描述

每个分量以一个字节存储,平面存储格式的意思就是,先存储视频帧中所有的Y分量。Y分量存储完之后,才开始存储色度分量。在IMC2格式中,YUV三分量的存储关系是:先存所有的Y分量、再存所有的V分量,最后存储U分量。

为了便于处理和表达,通常在代码中会以三个数组来分别装着三个分量。

另外需要提一嘴,在IMC2格式中,存储UV分量的内存空间步长分别是存储Y分量的一半。另外因为色度分量的采样书是Y分量的1/4,所以,及时色度分量占用空间是亮度分量的一半,也会有一些空闲的内存。

IMC4

在这里插入图片描述


和IMC2格式类似,只是U、V两个色度分量的存储顺序对调了一下。

YV12&I420

在这里插入图片描述


YV12格式的存储方式又有变化,存储色度分量的内存步幅是亮度分量的一半,首先Y分量数据以unsigned char数组的形式存储,紧跟着后面存V分量,最后存U分量。

I420和YV12的存储方式差不多,区别的地方在于,I420的Y分量后,存储的是U分量,最后存V分量,色度分量的存储顺序替换了一下。另外I420也被称为YUV420P。

YV12、I420、YUV420p这三个名词在多媒体开发中,是出现频率比较高的是那个了。大家不妨记忆一下

NV12

在这里插入图片描述
NV12格式首先存储Y分量平面,作为具有偶数行的无符号字符值数组。 Y平面后面紧跟着一个无符号字符值数组,其中包含打包的U(Cb)和V(Cr)样本。

这里具体再讲解一下大家常用的YUV420类型

3.1) YUV420p和YUV420sp区别

        因为YUV420比较常用, 在这里就重点介绍YUV420。YUV420分为两种:YUV420p和YUV420sp。

YUV420sp格式如下图:

YUV420p数据格式如下图:

3.2) YUV420p和YUV420sp具体分类和详情

YUV420p:又叫planer平面模式,Y ,U,V分别再不同平面,也就是有三个平面。

YUV420p又分为:他们的区别只是存储UV的顺序不一样而已。

I420:又叫YU12,安卓的模式。存储顺序是先存Y,再存U,最后存V。YYYYUUUVVV

YV12:存储顺序是先存Y,再存V,最后存U。YYYVVVUUU

YUV420sp:又叫bi-planer或two-planer双平面,Y一个平面,UV在同一个平面交叉存储。

YUV420sp又分为:他们的区别只是存储UV的顺序不一样而已。

NV12:IOS只有这一种模式。存储顺序是先存Y,再UV交替存储。YYYYUVUVUV

NV21:安卓的模式。存储顺序是先存Y,再存U,再VU交替存储。YYYYVUVUVU

官方文档如下:

YV12

All of the Y samples appear first in memory as an array of unsigned char values. This array is followed immediately by all of the V (Cr) samples. The stride of the V plane is half the stride of the Y plane, and the V plane contains half as many lines as the Y plane. The V plane is followed immediately by all of the U (Cb) samples, with the same stride and number of lines as the V plane (Figure 12).

所有 Y 样例都会作为不带正负号的 char 值组成的数组首先显示在内存中。此数组后面紧接着所有 V (Cr) 样例。V 平面的跨距为 Y 平面跨距的一半,V 平面包含的行为 Y 平面包含行的一半。V 平面后面紧接着所有 U (Cb) 样例,它的跨距和行数与 V 平面相同(图 12)。

大致意思是:先存储完所有的Y,后面紧跟着存V,V的步长(也就是宽)是Y的步长的一半,V的行高是Y的一半。V存储完后面紧跟着存U,所有的U,步长河行高和V相同,也就是都是Y的一半。

Figure 12:

NV12

All of the Y samples are found first in memory as an array of unsigned char values with an even number of lines. The Y plane is followed immediately by an array of unsigned char values that contains packed U (Cb) and V (Cr) samples, as shown in Figure 13. When the combined U-V array is addressed as an array of little-endian WORD values, the LSBs contain the U values, and the MSBs contain the V values. NV12 is the preferred 4:2:0 pixel format for DirectX VA. It is expected to be an intermediate-term requirement for DirectX VA accelerators supporting 4:2:0 video.

所有 Y 样例都会作为由不带正负号的 char 值组成的数组首先显示在内存中,并且行数为偶数。Y 平面后面紧接着一个由不带正负号的 char 值组成的数组,其中包含了打包的 U (Cb) 和 V (Cr) 样例,如图 13 所示。当组合的 U-V 数组被视为一个由 little-endian WORD 值组成的数组时,LSB 包含 U 值,MSB 包含 V 值。NV12 是用于 DirectX VA 的首选 4:2:0 像素格式。预期它会成为支持 4:2:0 视频的 DirectX VA 加速器的中期要求。

Figure 13:

NV21

  NV21和NV12的内存布局是一样的,只是U、V分量交错存储的顺序是相反的,NV21格式中,是以V-U的交错方式存储,如图所示。

3.3)YUV420的内存计算

width * hight =Y(总和)

U = Y / 4   V = Y / 4

所以YUV420 数据在内存中的长度是 width * hight * 3 / 2(即一个YUV是1.5个字节),所以计算采集的数据大小:width * hight * 1.5*frame*time

以720×488大小图象YUV420 planar为例,

其存储格式是: 共大小为720×480×3 × 1.5字节,

分为三个部分:Y,U和V

Y分量:    (720×480)个字节

U(Cb)分量:(720×480 × 1/4)个字节

V(Cr)分量:(720×480 × 1/4)个字节

三个部分内部均是行优先存储,三个部分之间是Y,U,V 顺序存储。

即YUV数据的0--720×480字节是Y分量值,

720×480--720×480×5/4字节是U分量

720×480×5/4 --720×480×3/2字节是V分量。

一般来说,直接采集到的视频数据是RGB24的格式,RGB24一帧的大小size=width×heigth×3 Bit,RGB32的size=width×heigth×4,YUV标准格式4:2:0 的数据量是 size=width×heigth×1.5 Bit。

在采集到RGB24数据后,需要对这个格式的数据进行第一次压缩。即将图像的颜色空间由RGB2YUV。因为,X264在进行编码的时候需要标准的YUV(4:2:0)。

经过第一次数据压缩后RGB24->YUV(I420)。这样,数据量将减少一半,经过X264编码后,数据量将大大减少。将编码后的数据打包,通过RTP实时传送。到达目的地后,将数据取出,进行解码。完成解码后,数据仍然是YUV格式的,所以,还需要一次转换,就是YUV2RGB24。

3.4)关于IOS

做过iOS硬解码的都知道,创建解码器时,需要指定PixelFormatType。IOS只支持NV12也就是YUV420中的一种,你搜索420,发现有四个,分别如下:

kCVPixelFormatType_420YpCbCr8Planar

kCVPixelFormatType_420YpCbCr8PlanarFullRange

kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange

kCVPixelFormatType_420YpCbCr8BiPlanarFullRange

根据表面意思,可以看出,可以分为两类:planar(平面420p)和 BiPlanar(双平面)。

还有一个办法区分,CVPixelBufferGetPlaneCount(pixel)获取平面数量,发现kCVPixelFormatType_420YpCbCr8Planar和kCVPixelFormatType_420YpCbCr8PlanarFullRange是三个两面,属于420p,iOS不支持。而kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange和kCVPixelFormatType_420YpCbCr8BiPlanarFullRange是两个平面。这就纠结了,到底用哪一个呢?

我查了官网资料,解释如下:

kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange = '420v', /* Bi-Planar Component Y'CbCr 8-bit 4:2:0, video-range (luma=[16,235] chroma=[16,240]).  baseAddr points to a big-endian CVPlanarPixelBufferInfo_YCbCrBiPlanar struct */

kCVPixelFormatType_420YpCbCr8BiPlanarFullRange  = '420f', /* Bi-Planar Component Y'CbCr 8-bit 4:2:0, full-range (luma=[0,255] chroma=[1,255]).  baseAddr points to a big-endian CVPlanarPixelBufferInfo_YCbCrBiPlanar struct */

感觉除了亮度和颜色的范围不一样,没发现其它不一样的。还是纠结,后来查了神网,有人说WWDC视频有,网址:https://developer.apple.com/videos/play/wwdc2011/419/?time=1527(大概在25:30‘)

解释如下:

但是我我还是不清楚,清楚的人请告知我一下。

然后我创建的时候分辨使用了kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange和kCVPixelFormatType_420YpCbCr8BiPlanarFullRange,视频播放出来没发现什么不一样,唯一不一样是计算的步长不一样。

比如:480*640

如果是:kCVPixelFormatType_420YpCbCr8BiPlanarFullRange

Y和UV的步长是512(采用了64字节对齐 非对齐的补0) Y的行宽是640,UV行宽是320

如果是:kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange

Y和UV的步长是480(实际长度,未补齐) Y的行宽是640,UV行宽是320

我采集的时候setPreset了,所以按照上面提示(但是我还是不是很理解),最后我项目里面还是选择了kCVPixelFormatType_420YpCbCr8BiPlanarFullRange。

四、存储方式

下面我用图的形式给出常见的YUV码流的存储方式,并在存储方式后面附有取样每个像素点的YUV数据的方法,其中,Cb、Cr的含义等同于U、V。

(1) YUVY 格式 (属于YUV422)

image

YUYV为YUV422采样的存储格式中的一种,相邻的两个Y共用其相邻的两个Cb、Cr,分析,对于像素点Y'00、Y'01 而言,其Cb、Cr的值均为 Cb00、Cr00,其他的像素点的YUV取值依次类推。

(2) UYVY 格式 (属于YUV422)

image

UYVY格式也是YUV422采样的存储格式中的一种,只不过与YUYV不同的是UV的排列顺序不一样而已,还原其每个像素点的YUV值的方法与上面一样。

(3) YUV422P(属于YUV422)

image

YUV422P也属于YUV422的一种,它是一种Plane模式,即平面模式,并不是将YUV数据交错存储,而是先存放所有的Y分量,然后存储所有的U(Cb)分量,最后存储所有的V(Cr)分量,如上图所示。其每一个像素点的YUV值提取方法也是遵循YUV422格式的最基本提取方法,即两个Y共用一个UV。比如,对于像素点Y'00、Y'01 而言,其Cb、Cr的值均为 Cb00、Cr00。

(4)YV12,YU12格式(属于YUV420)

image

YU12和YV12属于YUV420格式,也是一种Plane模式,将Y、U、V分量分别打包,依次存储。其每一个像素点的YUV数据提取遵循YUV420格式的提取方式,即4个Y分量共用一组UV。注意,上图中,Y'00、Y'01、Y'10、Y'11共用Cr00、Cb00,其他依次类推。

(5)NV12、NV21(属于YUV420)

image

NV12和NV21属于YUV420格式,是一种two-plane模式,即Y和UV分为两个Plane,但是UV(CbCr)为交错存储,而不是分为三个plane。其提取方式与上一种类似,即Y'00、Y'01、Y'10、Y'11共用Cr00、Cb00


I420: YYYYYYYY UU VV    =>YUV420P
YV12: YYYYYYYY VV UU    =>YUV420P
NV12: YYYYYYYY UVUV     =>YUV420SP
NV21: YYYYYYYY VUVU     =>YUV420SP

YUV视频查看工具:Android YUV文件查看工具_Alex_designer的博客-CSDN博客_yuv查看器

备注:以上很多信息来源比较多,有些已忘记从哪看到的了,主要是记录下来用于理解YUV视频 NV12与NV21 转换,YUV大致理解后,再看代码很好理解!

参照:

详解YUV数据格式 - yooooooo - 博客园

图像和流媒体 -- 详解YUV数据格式_聚优致成的博客-CSDN博客_yuv数据

五、案例

1、yuv生成jpg图片

YUV视频流生成转成jpg图片_Alex_designer的博客-CSDN博客_yuv转图片

2、NV12转NV21   Y都是一样的,只是UV 排列相反,

 
 
  1. //获取的nv12视频流数据

  2. byte[] mdata = new byte[width * height * 3 / 2];

  3. //mdata --->NV12

  4. // YYYY

  5. // UVUV

  6. //To ----->NV21

  7. // YYYY

  8. // VUVU

  9. //swap UV

  10. // int j,i;

  11. int uv_len = 1280 * 720 /2;

  12. int uv_pos = 1280 * 720;

  13. for (int i = 0; i < uv_len;i+=2) {

  14. byte swap = mdata[uv_pos+i];

  15. mdata[uv_pos+i] = mdata[uv_pos+i+1];

  16. mdata[uv_pos+i+1] = swap;

  17. }

.......待更新中

猜你喜欢

转载自blog.csdn.net/weixin_35804181/article/details/124347496