2. H265编码结构:CTU、CU、PU、TU

H265/HEVC 的编码结构,说白了就是一套「分而治之」的策略。我刚开始接触 H264 转 H265 的项目时,最直观的感受就是——这货把「块」的概念玩出了花。H264 里的宏块(Macroblock)是 16x16 固定大小,到了 H265 这里,变成了四叉树划分的灵活结构。

嗯,咱们一个一个来看。

2.1 编码树单元(CTU)—— 最大的块

CTU 是 H265 编码的起点。你可以把它理解成一张图片被切成的「瓦片」,每个 CTU 的大小由编码器决定。

CTU 尺寸范围: 64x64、32x32、16x16(H264 的宏块最大才 16x16)

我个人习惯在编码器中把 CTU 设成 64x64,尤其是处理 4K 视频时。为什么?因为平坦区域(比如天空、白墙)用一个 64x64 的块就能搞定,码率省得不是一星半点。

每个 CTU 会递归地向下划分,形成一棵四叉树。这棵树长什么样?我画了个图,你一看就明白。

CTU 四叉树划分结构 CTU (64x64) CU (32x32) CU (32x32) CU (32x32) CU (16x16) CU (16x16) CU (16x16) CU (8x8) CU (8x8) CU (8x8) 说明: • 蓝色:CTU 根节点(64x64) • 绿色:第一层划分(32x32) • 橙色/红色:继续递归划分(16x16 / 8x8)

2.2 编码单元(CU)—— 真正的决策者

CTU 划分出来的每个节点,就是一个 CU。CU 是编码决策的基本单位——编码器在这里决定:用帧内预测还是帧间预测?用哪个模式?

CU 的大小可以从 64x64 一直降到 8x8。你想想看,这意味着什么?

  • 平坦区域:用大 CU(64x64 或 32x32),一个块搞定,码率极低。
  • 纹理复杂区域:用小 CU(16x16 或 8x8),精细刻画细节。
  • 运动剧烈区域:同样用小 CU,运动估计更精准。

我的经验: 在编码监控视频时,背景基本不动,我习惯把最大 CU 设成 64x64,深度设到 3 层。这样背景用大块,前景运动物体用小块,码率能省 20% 以上。

2.3 预测单元(PU)—— 预测的最小单位

CU 决定「怎么编」,PU 决定「怎么预测」。一个 CU 可以包含 1 个或多个 PU。

PU 的划分方式取决于 CU 的预测模式:

预测模式 PU 划分方式 说明
帧内预测 2N×2N 或 N×N 只有正方形,大小与 CU 相同或 1/4
帧间预测 2N×2N、2N×N、N×2N、N×N、2N×nU、2N×nD、nL×2N、nR×2N 支持矩形和非对称划分

帧间预测的 PU 划分花样很多。我记得有一次调试运动估计的 bug,发现某个运动剧烈的区域用 2N×nU(上宽下窄)的划分效果特别好——因为物体上半部分在动,下半部分静止。这种非对称划分在 H264 里是没有的。

注意: PU 的划分深度不能超过 CU 的深度。也就是说,PU 最小是 4x4(当 CU 为 8x8 且采用 N×N 划分时)。

2.4 变换单元(TU)—— 残差变换的载体

TU 负责对预测残差做 DCT 变换(或 DST 变换)。TU 的大小也是可变的,从 32x32 到 4x4。

TU 的划分独立于 PU,但受 CU 约束。说白了:

  • TU 不能超出 CU 的边界。
  • TU 的最大尺寸由编码器指定(通常 32x32)。
  • TU 的最小尺寸是 4x4。

为什么 TU 要独立划分?我举个例子你就懂了:

假设一个 CU 是 32x32,里面包含一个 32x32 的 PU(帧内预测)。预测做完后,残差信号可能在某些区域很平坦,某些区域有细节。这时候:

  • 平坦区域用 32x32 的 TU,变换后高频系数几乎为零,直接跳过。
  • 细节区域用 8x8 或 4x4 的 TU,精细保留纹理。

这种灵活性,是 H265 压缩效率比 H264 高 30%-50% 的重要原因之一。

2.5 四者关系总结

CTU、CU、PU、TU 的关系,可以用一句话概括:

CTU 是根,CU 是枝,PU 和 TU 是叶。

CTU 划分出 CU,CU 决定预测模式后划分出 PU,CU 决定变换方式后划分出 TU。

我习惯用一张表来对比它们的核心属性:

单元 全称 大小范围 核心作用
CTU Coding Tree Unit 16x16 ~ 64x64 编码树的根节点,图像分块
CU Coding Unit 8x8 ~ 64x64 编码决策(帧内/帧间、模式选择)
PU Prediction Unit 4x4 ~ 64x64 预测操作(运动估计、帧内预测)
TU Transform Unit 4x4 ~ 32x32 残差变换(DCT/DST)

2.6 实际编码中的坑

讲几个我在项目中踩过的坑,希望能帮你少走弯路。

坑一:CU 深度与编码速度的权衡

我曾经在一个实时编码项目中,把 CU 最大深度设成了 4(允许 8x8 的 CU)。结果编码速度直接掉了一半。后来发现,对于 1080p 的视频,深度 3(最小 16x16)就足够了,再深收益很小,代价却很大。

坑二:TU 大小与码率控制

有次做低码率编码,我把 TU 最大尺寸设成 32x32,结果画面出现块效应。后来分析发现:低码率下大 TU 的量化步长太大,细节全丢了。改成最大 16x16 后,块效应明显改善。

建议: 如果你用 FFmpeg 的 libx265 编码,可以通过参数控制这些结构:

# 设置 CTU 大小 64x64,CU 最大深度 3
-crf 23 -x265-params "ctu=64:max-cu-depth=3"

# 限制 TU 最大为 16x16
-x265-params "tu-intra-depth=2:tu-inter-depth=2"

嗯,CTU、CU、PU、TU 这套四叉树结构,是 H265 的核心骨架。理解了它们,你就掌握了 H265 编码的一半。剩下的,就是预测、变换、量化这些具体操作了。