Skip to content

LV020-YUV的格式 ​

一、YUV采样模式 ​

1. 采样模式有哪些? ​

1.1 四种采样模式 ​

YUV 有 4 种采样模式:

  • YUV4:4:4 ,表示完全采样,一个像素 占 3 个字节。
  • YUV4:2:2,表示2:1的水平采样,垂直完全采样,平均一个像素占 2 个字节。
  • YUV4:2:0,表示2:1的水平采样,垂直2:1采样,平均一个像素占 1.5 个字节。
  • YUV4:1:1,表示4:1的水平采样,垂直完全采样,但是这种我只是在维基百科的词条中看到有,实际并没有接触过。

这几个数字怎么读? 可以拿“4 个像素宽、2 个像素高”的参照块来对:

  • 第 1 个数字 4:参照块的宽度,也就是这一宽度里的 Y 采样数;
  • 第 2 个数字:第一行里色度的采样数(4 = 和 Y 一样多,2 = 减半,1 = 减到四分之一);
  • 第 3 个数字:第二行里色度的采样数(和第二个数字相同 ⇒ 垂直方向不减少;是 0 ⇒ 第二行不再单独采样色度)。

对照着看就很清楚了:

  • 4:4:4 → 4/4,两行都跟 Y 一样密,每个像素都有自己的 UV;
  • 4:2:2 → 2/2,两行都只有水平方向减半,左右相邻的 2 个像素共用一组 UV;
  • 4:2:0 → 2/0,水平减半、第二行不再单独采样,折算下来就是 2x2 的 4 个像素共用一组 UV;
  • 4:1:1 → 1/1,两行都只有水平方向减到四分之一,左右相邻的 4 个像素共用一组 UV。

4:2:0 里的这个 “0” 要特别注意:它只是说第二行不再单独采一份色度(直接复用第一行采出来的那一份),并不是“第二行不采样”,更不是“第二行只采另一种色度分量”。网上不少把 4:2:0 讲成“一行只采 U、一行只采 V”的说法,就是把这两件事搞混了。

主流的采样模式就是前面三种,YUV444、YUV422和YUV420。让多个 Y 分量共用一组 UV,既极大地节省了空间,又不太损失质量;之所以可以这样,核心原因在于人眼的生理特性:

(1)人眼对亮度(Y)的敏感度远高于对色度(U/V)的敏感度

  • 亮度分量(Y)包含图像的细节信息,人眼对此非常敏感
  • 色度分量(U/V)包含颜色信息,人眼对此相对不敏感

(2)视觉感知的不对称性

  • 人眼能够分辨出微小的亮度变化,但对颜色的细微变化不敏感
  • 即使大幅减少色度信息,人眼也很难察觉到明显的质量损失

1.2 图片示例 ​

我们可以准备一张图片,使用FFmpeg转换一下,分别得到YUV444、YUV422和YUV420格式的图片,方便后面分析。可以直接在这里下载:yuv-picture.zip:

image-20240622144924936

我们也可以自己找一张jpg图片,转换一下,相关命令如下所示:

shell
ffmpeg.exe -i juren.jpg -s 1920x1080 -pix_fmt yuvj444p juren_yuv_444.yuv
ffmpeg.exe -i juren.jpg -s 1920x1080 -pix_fmt yuvj422p juren_yuv_422.yuv
ffmpeg.exe -i juren.jpg -s 1920x1080 -pix_fmt yuvj420p juren_yuv_420.yuv

从上图可以看到,yuv420 比 yuv444 的 数据量少了一半,我们用 7yuv 看一下图片质量的差异:

juren_yuv_444.yuv(注意格式那里选YUV444 planar)
image-20251218111542793
juren_yuv_420.yuv(注意格式那里选YUV420 planar I420)
image-20251218111743806

观察上面两张图,至少我是没有觉察到有什么变化,所以重点:YUV420 比 YUV444 少了一半数据,视觉体验几乎没有变化。

2. 不同采样模式 ​

2.1 YUV444 ​

YUV 4:4:4 采样,意味着 Y、U、V 三个分量的采样比例相同,因此在生成的图像里,每个像素的三个分量信息完整,都是 8 bit,也就是一个字节。

这种格式占用空间最大,每个像素点都有一个 Y 分量 + 一个 U 分量 + 一个 V 分量,所以和 RGB 一样,每个像素点占用 3 个字节!不过 UV 的存放顺序不一样时,又会派生出不同的格式,这就属于“存储格式”的范畴了,后面再说。

image-20251218111958508

如上图所示,YUV444 就是每个像素点都包含了三个完整的 YUV 分量,也就是每个 Y 都单独配一组自己的 UV,所以

txt
单个像素点占用的空间为: pixel = 1Byte(Y) + 1Byte(U) + 1Byte(V) = 3Byte
一帧图像占用的空间为  : frameSize = (frameWidth * frameHeight * 3)Byte

举个例子 :

假如图像像素为:[Y0 U0 V0]、[Y1 U1 V1]、[Y2 U2 V2]、[Y3 U3 V3]

那么采样的码流为:Y0 U0 V0 Y1 U1 V1 Y2 U2 V2 Y3 U3 V3

最后映射出的像素点依旧为 [Y0 U0 V0]、[Y1 U1 V1]、[Y2 U2 V2]、[Y3 U3 V3]

不存在共用:每个像素都保留自己的 U、V,所以数据量最大。

2.2 YUV422 ​

YUV 4:2:2 采样,意味着 U、V 分量各自只有 Y 分量采样数的一半(也就是水平方向 2:1 采样、垂直方向不减少)。如果水平方向有 10 个像素点,那么会采样 10 个 Y 分量,而 U、V 各只采样 5 个,即 5 组 UV。

image-20251218112056509

也就是说,左右相邻的两个 Y 共用一组 UV(一组 U + V),如上图所示:

image-20251218112125627

txt
单个像素点占用的空间为: pixel = 1Byte(Y) + 1Byte(U)/2 + 1Byte(V)/2 = 2Byte
一帧图像占用的空间为  : frameSize = (frameWidth * frameHeight * 2)Byte

举个例子 :

假如图像像素为:[Y0 U0 V0]、[Y1 U1 V1]、[Y2 U2 V2]、[Y3 U3 V3]

那么采样的码流为:Y0 U0 Y1 V1 Y2 U2 Y3 V3

其中,每个像素点都会采样 Y 分量,而 U、V 分量都是隔一个像素采一次。这里写成 Y0 U0 Y1 V1 是为了和后文 Packed 的存储顺序对上,下标只表示这个值采自哪个位置——U0 和 V1 这一对代表的是 Y0、Y1 这两个像素,而不是“U 只属于 Y0、V 只属于 Y1”。

最后映射出的像素点为 [Y0 U0 V1]、[Y1 U0 V1]、[Y2 U2 V3]、[Y3 U2 V3]

采样的码流映射为像素点,还是要满足每个像素点有 Y、U、V 三个分量。但是可以看到,第一和第二像素点公用了 U0、V1 分量,第三和第四个像素点公用了 U2、V3 分量,这样就节省了图像空间。一张 1280 x 720 大小的图片,在 YUV 4:2:2 采样时的大小为:

bash
(1280x720x8 + 1280x720x8x0.5 + 1280x720x8x0.5) / 8 / 1024 / 1024 = 1.76M

可以看到 YUV 4:2:2 采样的图像比 RGB 模型图像节省了三分之一的存储空间,在传输时占用的带宽也会随之减少。

2.3 YUV420 ​

这个格式也是应用最广泛的,视频会议、数字电视、DVD 都用的 YUV420 格式。4:2:0 并不是指只采样 U 分量而不采样 V 分量;按前面说的读法,它就是“水平减半 + 第二行不再单独采样”,所以 U、V 在水平和垂直两个方向上的采样率都只有 Y 的一半:2x2 的 4 个 Y 像素对应 1 个 U 和 1 个 V,U、V 在横向、纵向都是隔一个像素才采一次样,而不是“某一行只采 U、另一行只采 V”。

image-20251218112218867

如上图所示,4x4 的像素点里,Y 分量是 16 个,而 U、V 分量各只有 4 个,每个 2x2 的像素块共用一组 UV(图中把 U、V 分别标在上下两行,只是为了在一个 2x2 块里同时展示这两个分量;实际采样时 U、V 落在同一个采样格点上,只是这个格点相对 Y 网格的位置在不同标准里略有差异)。所以四个像素点才能包含一组完整的 YUV 信息:

image-20251218112248355

第一行的第 1、第 2 像素,第二行的第 1、第 2 像素(也就是一个 2x2 的像素块)共用一组 UV,以此类推。为什么不是第一行的第 1~4 个像素共享一组 UV?因为 4:2:0 的色度在垂直方向上同样做了 2:1 采样,纵向也要隔一个像素才采一次,所以必须是 2x2 的块共用一组 UV,而不是同一行里的 4 个像素共用。所以四个 Y 分量共用一组 UV 分量:

txt
单个像素点占用的空间为: pixel = 1Byte(Y) + 1Byte(U)/4 + 1Byte(V)/4 = 1.5Byte
一帧图像占用的空间为  : frameSize = (frameWidth * frameHeight * 1.5)Byte

举个例子 :

假设图像像素为:

[Y0 U0 V0]、[Y1 U1 V1]、[Y2 U2 V2]、[Y3 U3 V3] [Y4 U4 V4]、[Y5 U5 V5]、[Y6 U6 V6]、[Y7 U7 V7]

那么采样的数据为:Y0 Y1 Y2 Y3 Y4 Y5 Y6 Y7(8 个 Y 分量)+ U0 U2(2 个 U 分量)+ V0 V2(2 个 V 分量)

其中,每个像素点都会采样 Y 分量,而 U、V 分量在横向、纵向上都是隔一个像素才采一次,也就是每个 2x2 的像素块共用一组 UV。

最后映射出的像素点为:

[Y0 U0 V0]、[Y1 U0 V0]、[Y2 U2 V2]、[Y3 U2 V2] [Y4 U0 V0]、[Y5 U0 V0]、[Y6 U2 V2]、[Y7 U2 V2]

这 4 个像素共用到的 1 个 U(V)值是代表这 4 个像素的,至于怎么由 4 个像素的 U(V)值算出这一个值,其实有好几种算法:

(1)平均值,4 个像素的 U 值加起来除以 4。

(2)加权,例如,第一第二个像素的 U 权重大点,第三第四个像素的 U 权重小一点。

(3)直接丢弃第二、第三、第四个像素的 U 值,只取第一个像素的 U 值。V 值同理。

上面例子里为了直观,用的就是第(3)种:直接取 2x2 块里左上角那个像素的 U、V 值。

一张 1280 * 720 大小的图片,在 YUV 4:2:0 采样时的大小为:

bash
(1280 x 720 x 8 + 1280 x 720 x 0.25 x 8 x 2)/ 8 / 1024 / 1024 = 1.32 MB

可以看到 YUV 4:2:0 采样的图像比 RGB 模型图像节省了一半的存储空间,因此它也是比较主流的采样方式。

3. 总结 ​

image-20221101140216424

如上图所示:

YUV格式每个像素所占字节数谁和谁共用一组 UV
YUV444pixel_size=1Byte(Y)+1Byte(U)+1Byte(V)=3Byte每个 Y 单独一组 UV
YUV422pixel_size=[2Byte(Y)+1Byte(U)+1Byte(V)]/2=2Byte同一行左右相邻的 2 个 Y 共用一组
YUV420pixel_size=[4Byte(Y)+1Byte(U)+1Byte(V)]/4=1.5Byte上下左右 2x2 的 4 个 Y 共用一组
YUV411pixel_size=[4Byte(Y)+1Byte(U)+1Byte(V)]/4=1.5Byte同一行左右相邻的 4 个 Y 共用一组

顺带一提:YUV411 和 YUV420 每个像素都占 1.5 字节,区别在于色度是在横向减少的(411:一行里 4 个像素共用一组 UV),还是横竖都减少(420:2x2 的 4 个像素共用一组 UV)。

二、存储格式 ​

1. 有哪些格式? ​

上面了解了采样模式了,那采样结束肯定需要存储的啊,怎么存?YUV存储方式主要分为三种:打包格式(Packed formats) 、平面格式(Planar formats)和半平面(semi-Planar)。

存储格式说明
P:Planar按平面分开存放,先把Y存完,再存U,再存V,就是先连续存储所有像素点的Y,紧接着存储所有像素点的U,随后是所有像素点的V,即YYY UUU VVV 这样
SP:semi-Planar按半平面存放,只有Y是一个平面,UV数据共用一个平面,UV分量交叉存储。
Packed打包格式,就是把YUV三种分量按顺序存储,即YUV YUV YUV ... 这样

所以不同的采样方式加上不同的存储方式就会有多种不同的YUV编码方式。

2. 常用的格式 ​

2.1 YUV420 ​

YUV420是使用很多的,它结合不同的存储方式,常用的会有YUV420P和YUV420SP。

2.1.1 YUV420P ​

YUV420P属于YUV420采样,Planar存储,它又会分为I420(也叫YU12)、YV12两种,I420 和 YV12 格式都属于 YUV 420P 类型,即先存储 Y 分量,再存储 U、V 分量,区别在于:I420 是先 Y 再 U 后 V,而 YV12 是先 Y 再 V 后 U 。

image-20240622171227455

I420(属于 YUV 420 Planar) I420 是 YUV 420 Planar 的一种,YUV 分量分别存放,先是 w * h 长度的 Y,后面跟 w * h * 0.25 长度的 U, 最后是 w * h * 0.25 长度的 V,总长度为 w * h * 1.5。

YV12(属于 YUV 420 Planar) YV12 是 YUV 420 Planar 的一种,YUV 分量分别存放,先是 w * h 长度的 Y,后面跟 w * h * 0.25 长度的 V, 最后是 w * h * 0.25 长度的 U,总长度为 w * h * 1.5。与 I420 不同的是,YV12 是先 V 后 U。

2.1.2 YUV420SP ​

YUV420SP属于YUV420采样,semi-Planar存储,分为NV12、NV21,YUV420先存储了 Y 分量,但接下来并不是再存储所有的 U 或者 V 分量,而是把 UV 分量交替连续存储。NV12 在 iOS 的相机数据里很常见,它的存储顺序是先存 Y 分量,再 UV 进行交替存储。NV21 在安卓的相机数据里很常见,它的存储顺序是先存 Y 分量,再 VU 交替存储。

image-20240622171938988

NV12(属于 YUV 420 Semi-Planar) NV12 是 YUV 420 Semi-Planar 的一种,Y 分量单独存放,UV 分量交错存放,UV 在排列的时候,从 U 开始。总长度为 w * h * 1.5。

NV21(属于 YUV 420 Semi-Planar) NV21 是 YUV 420 Semi-Planar 的一种,Y 分量单独存放,UV 分量交错存放,与 NV12 不同的是,UV 在排列的时候,从 V 开始。总长度为 w * h * 1.5。

2.2 YUV422 ​

2.2.1 YUV422P ​

YUV422P属于YUV422采样,它的存储格式是Planar,分I422(也叫YU16)和YV16两种:

image-20251218115106940

I422(属于 YUV 422 Planar,也叫YU16) I422 是 YUV 422 Planar 的一种,YUV 分量分别存放,先是 w * h 长度的 Y,后面跟 w * h * 0.5 长度的 U, 最后是 w * h * 0.5 长度的 V,总长度为 w * h * 2。

YV16(属于 YUV 422 Planar) YV16 是 YUV 422 Planar 的一种,YUV 分量分别存放,先是 w * h 长度的 Y,后面跟 w * h * 0.5 长度的 V, 最后是 w * h * 0.5 长度的 U,总长度为 w * h * 2。与 I422 不同的是,YV16 是先 V 后 U。

2.2.2 YUV422SP ​

YUV422SP 属于YUV422采样,分为NV16、NV61两种:

image-20251218115129842

NV16(属于 YUV 422 Semi-Planar) NV16 是 YUV 422 Semi-Planar 的一种,Y 分量单独存放,UV 分量交错存放,UV 在排列的时候,从 U 开始。总长度为 w * h * 2。

NV61(属于 YUV 422 Semi-Planar) NV61 是 YUV 422 Semi-Planar 的一种,Y 分量单独存放,UV 分量交错存放,UV 在排列的时候,从 V 开始。总长度为 w * h * 2。

2.2.3 Packed格式 ​

image-20251218115159020

YUYV(也写作 YUY2,属于 YUV 422 Interleaved) YUYV 属于 YUV 422 Interleaved 的一种。事实上,Interleaved 是属于 Packed 的,但是在 422 中,用 Interleaved 更加形象一些。在 Packed 内部,YUV 的排列顺序是 Y U Y V,两个 Y 共用一组 UV。

UYVY(属于 YUV 422 Interleaved) UYVY 属于 YUV 422 Interleaved 的一种。在 Packed 内部,YUV 的排列顺序是 UYVY,两个 Y 共用一组 UV。

三、总结 ​

其中打包格式(如YUYV)多见于老旧采集设备,现代系统已逐步淘汰。其实用的多的还是YUV420,其中NV12,NV21较为常见。

YUV格式

四、YUV图片数据分析 ​

YUV 这种格式的文件,不像 BMP 文件,BMP 虽然里面也是 RGB 的裸数据,但是 BMP 里面有一些头数据,宽高什么的。

但是 YUV文件格式,没有头信息,没有宽高信息,所以用 7yuv 打开 YUV 文件的时候,需要指定宽高,要指定采样格式是 4:4:4。如果我们有一个 YUV图片,但是忘记了 它的宽高跟采样格式,那就无法正常显示这个 YUV图片。

所以,YUV图片文件除了 原始的像素数据,什么都没有的。由于 4:4:4 跟 RGB24 一样,每个像素占 3 字节,图片宽高是 1920x1080 ,所以这个 YUV图片的大小如下:

txt
SIZE=1920x1080x3=6075KB

可以看到,它的大小确实也是6075KB左右:

image-20240622193154796

我们用Notepad++打开juren_yuv_444.yuv文件,这里还需要用插件来查看十六进制数据才行。

image-20240622193503749

会发现开头数据大量是0,看一下图片会发现开头大部分都是全黑的像素点,那我们来修改一下这个YUV文件,在最开始修改的话不是很好改,我们大概确定一个位置,我们想在第20行开始将像素点全部改为FF,这样起始地址大概就是1920x3x20=115200=1C200,我们大概就从这个地址附近选一个开始,直接插入FF数据,我们放大一点看:

image-20240622194845908

发现确实出现了白线。

参考资料

数字音视频技术: 谈谈 YUV的采样格式和存储格式 - 夜行过客 - 博客园

学习YUV编码格式及存储方式,看这篇就够了_yuyv格式-CSDN博客

一文读懂 YUV 的采样与格式 - 知乎

YUV 格式详解,只看这一篇就够了(转) - 知乎