2. YUV格式详解
各位同学,今天我们来聊聊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格式拆成了三个维度:采样格式决定数据量,存储布局决定数据排列,转换矩阵决定色彩空间。你在ISP pipeline里做降噪时,这三个维度都得搞清楚。采样格式错了,数据量对不上;存储布局搞反了,颜色全乱;转换矩阵用错了,色偏跑不掉。