YUV格式详解:从采样到存储,再到RGB转换

各位同学,今天我们来聊聊YUV格式。说实话,YUV这玩意儿在ISP里太常见了,但很多人只知其然不知其所以然。我刚开始做图像处理时,也踩过不少坑——比如YUV420的数据排列搞反了,导致整张图颜色全乱套。嗯,咱们今天就把这事彻底讲清楚。

一、YUV与RGB的本质区别

先问个问题:为什么不用RGB,非要搞个YUV?

RGB是把颜色拆成红绿蓝三个分量,每个像素三个值。但人眼对亮度敏感,对颜色没那么敏感。YUV就是利用这个特点——Y代表亮度,U和V代表色度。说白了,我们可以把色度信息压缩一下,人眼也察觉不到。

我在项目中遇到过一件事:用RGB做降噪,三个通道都得处理,计算量翻三倍。换成YUV后,只对Y通道做细节降噪,UV通道用简单滤波就够了,速度提升明显。

核心概念:

  • Y(Luma):亮度分量,包含图像细节
  • U(Chroma Blue):蓝色色度分量
  • V(Chroma Red):红色色度分量

二、三种采样格式:YUV444、YUV422、YUV420

这三种格式的区别,说白了就是色度信息采多采少的问题。

采样格式 每4个像素的Y:U:V样本数 数据量(相对于RGB) 典型应用
YUV444 4:4:4 100% 专业视频编辑、高保真图像
YUV422 4:2:2 66.7% 广播级视频、高端相机
YUV420 4:2:0 50% 消费级相机、视频通话、H.264/H.265

1. YUV444

每个像素都有自己的Y、U、V值。没有压缩,信息完整。但数据量和RGB一样大,存储和传输都不划算。我只有在做算法验证时才会用YUV444,方便调试。

2. YUV422

每两个像素共享一组UV。水平方向色度分辨率减半。举个例子:像素1和像素2共用U1V1,像素3和像素4共用U2V2。视觉上几乎看不出区别,但数据量省了三分之一。

3. YUV420

这是ISP里最常见的格式。每四个像素(2x2块)共享一组UV。水平和垂直方向色度分辨率都减半。你想想看,一张1080p的图,Y是1920x1080,U和V只有960x540。数据量直接砍半。

避坑指南:我曾经在对接某个传感器时,厂商文档写的是YUV420,但实际输出的是YUV422。结果降噪算法跑出来全是色块。后来我养成了一个习惯——拿到数据先打印分辨率,确认Y、U、V的宽高是否匹配。

三、YUV数据存储布局:Planar vs Interleaved

采样格式决定了有多少数据,存储布局决定了数据怎么排。这两个概念容易搞混,我分开讲。

1. Planar(平面模式)

所有Y数据连续存放,然后所有U数据,最后所有V数据。就像三块地,各占一片。

// YUV420 Planar 示例(I420格式)
// 假设图像宽=4,高=4
// Y平面:16个字节
Y Y Y Y
Y Y Y Y
Y Y Y Y
Y Y Y Y

// U平面:4个字节(宽高各减半)
U U
U U

// V平面:4个字节
V V
V V

// 内存布局:[Y0 Y1 ... Y15] [U0 U1 U2 U3] [V0 V1 V2 V3]

我个人习惯用Planar做算法处理。因为Y通道单独拿出来做降噪,不用考虑UV的干扰。内存访问也连续,cache友好。

2. Interleaved(交错模式)

Y和UV交错排列。常见的有NV12、NV21、YUYV等。

// YUV420 Interleaved 示例(NV12格式)
// 内存布局:[Y0 Y1 Y2 ... Y15] [U0 V0 U1 V1 U2 V2 U3 V3]
// UV部分是交错存放的:U、V、U、V...

// YUYV格式(YUV422 Interleaved)
// 内存布局:Y0 U0 Y1 V0  Y2 U1 Y3 V1  ...

Interleaved的好处是UV数据在一起,做颜色处理时方便。但降噪时要注意——如果直接对交错数据做滤波,可能会把U和V混在一起算,结果就错了。

布局方式 优点 缺点 常见格式
Planar 通道分离清晰,算法处理方便 UV访问需要额外计算偏移 I420、YV12
Interleaved UV数据紧凑,硬件DMA友好 降噪时容易误处理UV NV12、NV21、YUYV

注意:NV12和NV21的区别在于UV的顺序。NV12是U在前V在后,NV21是V在前U在后。Android相机默认输出NV21,而iOS和大多数ISP硬件输出NV12。我曾经因为没注意这个顺序,在Android平台上用NV12的假设去解析数据,结果颜色完全反了。

四、YUV与RGB转换矩阵

YUV和RGB之间的转换,本质上是一个线性变换。不同标准(BT.601、BT.709、BT.2020)的系数不一样。

1. BT.601标准(标清视频)

// RGB → YUV(BT.601,范围0-255)
Y =  0.299 * R + 0.587 * G + 0.114 * B
U = -0.169 * R - 0.331 * G + 0.500 * B + 128
V =  0.500 * R - 0.419 * G - 0.081 * B + 128

// YUV → RGB(BT.601)
R = Y + 1.402 * (V - 128)
G = Y - 0.344 * (U - 128) - 0.714 * (V - 128)
B = Y + 1.772 * (U - 128)

2. BT.709标准(高清视频)

// RGB → YUV(BT.709,范围0-255)
Y =  0.213 * R + 0.715 * G + 0.072 * B
U = -0.117 * R - 0.394 * G + 0.511 * B + 128
V =  0.511 * R - 0.464 * G - 0.047 * B + 128

// YUV → RGB(BT.709)
R = Y + 1.540 * (V - 128)
G = Y - 0.183 * (U - 128) - 0.459 * (V - 128)
B = Y + 1.814 * (U - 128)

你可能会问:这两个标准差别大吗?说实话,肉眼几乎看不出来。但在ISP pipeline里,如果传感器输出的是BT.709,而你用BT.601去转,颜色会偏一点。我建议统一用BT.709,因为现在大部分高清传感器都支持这个标准。

工程建议:实际项目中不要用浮点运算做转换,太慢了。用定点数或者查表法。比如把系数乘以1024取整,然后右移10位。我一般这样写:

// 定点数实现BT.601 YUV→RGB
int Y_tmp = Y << 10;
int R = (Y_tmp + 1436 * (V - 128)) >> 10;
int G = (Y_tmp - 352 * (U - 128) - 731 * (V - 128)) >> 10;
int B = (Y_tmp + 1815 * (U - 128)) >> 10;

五、知识体系总览

下面这张图把YUV格式的核心知识点串起来了。你可以把它当作一个速查表。

YUV格式知识体系 YUV格式知识体系 YUV格式 采样格式 存储布局 转换矩阵 YUV444 YUV422 YUV420 Planar(平面模式) Interleaved(交错模式) BT.601 BT.709 BT.2020 降噪算法通常只处理Y通道,UV通道做简单滤波或直接复制 存储布局决定了如何从内存中正确读取Y、U、V数据

这张图把YUV格式拆成了三个维度:采样格式决定数据量,存储布局决定数据排列,转换矩阵决定色彩空间。你在ISP pipeline里做降噪时,这三个维度都得搞清楚。采样格式错了,数据量对不上;存储布局搞反了,颜色全乱;转换矩阵用错了,色偏跑不掉。