2. 色彩空间与采样:RGB与YUV色彩空间、4:4:4/4:2:2/4:2:0采样格式、色度子采样原理

说到视频编码,第一个绕不开的概念就是色彩空间。很多刚入行的朋友觉得这玩意儿就是换个颜色表示方式,没什么大不了的。嗯,我当年也这么想,直到有一次在项目中,因为色彩空间转换没做对,整个视频流在播放器里颜色全偏了,被测试部门追着改了三天……从那以后,我再也不敢小看这个基础环节了。

2.1 为什么我们需要YUV?

先说说RGB。RGB就是红绿蓝三原色,每个像素用三个分量表示。显示器、摄像头传感器,本质上都是RGB的天下。但问题来了——RGB三个通道的信息量是平等的,每个通道都占同样的带宽。这在我们做视频压缩时,就是个灾难。

为什么?因为人眼对亮度的敏感度远高于对颜色的敏感度。你想想看,黑白电影看起来细节依然清晰,但你把颜色通道全抹掉,轮廓还在。这说明什么?说明颜色信息其实可以「偷懒」。

YUV就是为这个「偷懒」而生的。Y代表亮度(Luma),U和V代表色度(Chroma)。说白了,就是把亮度和颜色拆开,颜色通道可以压缩得更狠,人眼还看不出来。

核心认知:RGB是给机器看的,YUV是给人眼看的。视频编码之所以能压到那么小的体积,YUV色彩空间功不可没。

2.2 RGB → YUV 的数学变换

RGB到YUV不是随便映射的,有一套标准的转换矩阵。以BT.601标准为例(标清视频常用):

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

注意看Y的系数——绿色通道占了0.587,红色0.299,蓝色只有0.114。这跟人眼对绿光最敏感、蓝光最不敏感的生理特性完全吻合。我个人习惯在调试编码器时,先看一眼Y分量的直方图,基本就能判断画面亮度分布是否合理。

反过来,YUV转RGB的公式也要烂熟于心:

R = Y + 1.402 * (V - 128)
G = Y - 0.344 * (U - 128) - 0.714 * (V - 128)
B = Y + 1.772 * (U - 128)

我曾经踩过的坑:在做硬件编码器调优时,发现输出画面偏绿。查了两天,最后发现是YUV转RGB时,U和V的偏移量忘了减128。这种低级错误,一旦上了流水线,排查成本极高。

2.3 色度子采样:4:4:4 / 4:2:2 / 4:2:0

好,现在进入本章的重头戏——色度子采样。说白了,就是「颜色信息可以少存一点」。YUV本身已经分离了亮度和色度,而子采样进一步压缩色度通道的分辨率。

先看一张我手绘的结构图,帮你快速建立空间感:

色度子采样格式对比(4×4像素块) 4:4:4 Y 全分辨率 U 全分辨率 V 全分辨率 每个像素都有完整YUV 数据量 = 100% 4:2:2 Y 全分辨率 U 水平1/2 V 水平1/2 水平方向色度减半 数据量 ≈ 66% 4:2:0 Y 全分辨率 U 水平1/2 V 垂直1/2 水平+垂直色度减半 数据量 ≈ 50% 关键结论 4:4:4 保留全部色度信息 → 适合屏幕录制、文字图像 4:2:0 丢弃75%色度信息 → 适合自然视频、直播、流媒体 人眼对色度分辨率不敏感,4:2:0 在视觉上几乎无损

2.4 三种采样格式的深入对比

上面那张图是宏观印象,下面我们逐一说细节。

4:4:4 —— 无损色度

每个像素都保留完整的Y、U、V三个分量。说白了,就是RGB的YUV版本,没有任何压缩。我一般在做屏幕编码、远程桌面、医学影像这类场景时,才会用4:4:4。因为文字边缘和细线条一旦丢了色度信息,锯齿感会非常明显。

4:2:2 —— 水平减半

亮度Y还是每个像素都有,但U和V只在水平方向上每两个像素取一个。垂直方向不变。举个例子:一行4个像素,原来需要4个U值,现在只需要2个。这在广播级视频制作中很常见,既能省带宽,又不会明显影响画质。

4:2:0 —— 最常用的格式

这就是我们今天看到的绝大多数视频的格式。H.264、H.265、VP9、AV1,默认都是4:2:0。U和V在水平和垂直方向都减半。一个4×4的像素块,原来需要16个U值和16个V值,现在只需要4个U和4个V。数据量直接砍掉一半。

我的建议:如果你刚开始做编码器调优,先把4:2:0吃透。因为90%以上的实际场景都在用这个格式。等把4:2:0的编码器调明白了,再去看4:2:2和4:4:4,会发现很多原理是相通的。

2.5 色度子采样的实现细节

采样不是简单扔掉像素,而是有固定的位置关系。以4:2:0为例,U和V的采样点通常位于Y像素的交叉位置:

Y采样点(每个位置都有):
  Y00  Y01  Y02  Y03
  Y10  Y11  Y12  Y13
  Y20  Y21  Y22  Y23
  Y30  Y31  Y32  Y33

U/V采样点(每2×2块共享一个):
  U00  ——  U02  ——
  ——   ——   ——   ——
  U20  ——  U22  ——
  ——   ——   ——   ——

解码时,缺失的U和V需要通过插值恢复。最简单的做法是最近邻插值,但画质粗糙。我习惯用双线性插值,效果更平滑。当然,如果你在做硬件编码器,还得考虑插值滤波器的延迟和面积开销。

2.6 不同场景下的采样格式选择

应用场景 推荐采样格式 原因
屏幕录制 / 远程桌面 4:4:4 文字边缘、UI图标需要完整色度
广播级视频制作 4:2:2 后期调色、抠像需要较高色度精度
流媒体 / 短视频 4:2:0 带宽优先,人眼对色度不敏感
医学影像 / 卫星图 4:4:4 不允许任何信息损失
视频会议 4:2:0 低延迟、低码率,画质够用即可

我曾经踩过的坑:有一次做视频会议系统优化,为了省带宽把采样格式从4:2:0改成了4:2:0的变种,结果在分享PPT时,红色图表上的文字边缘出现了严重的彩色锯齿。后来发现是色度插值算法没对齐。记住:采样格式变了,插值算法也得跟着变。

2.7 色度子采样对编码器调优的影响

最后聊点实战层面的东西。色度子采样直接影响编码器的三个关键指标:

  • 码率:4:2:0比4:4:4省一半的色度数据,码率自然更低。但要注意,码率节省不是线性的,因为亮度通道才是码率消耗的大头。
  • 画质:色度细节丢失后,在纯色边缘、文字区域会引入伪影。编码器调优时,我习惯在色度通道上单独调整量化参数(QP),给色度稍微多分配一点码率。
  • 运动估计:色度子采样后,运动估计的精度会受影响。因为色度块变大了,运动矢量的粒度也变粗了。有些编码器会单独为色度做亚像素运动补偿,效果会好很多。

嗯,色彩空间和采样这部分,说白了就是「用最小的代价,骗过人眼」。理解了这个底层逻辑,后面学变换、量化、熵编码时,你会看得更通透。