3. 图像划分:CTU / CU / PU / TU 与四叉树结构
H.265 相比 H.264 最大的变化在哪?我个人觉得,就是图像划分方式彻底变了。H.264 用宏块,大小固定 16x16。H.265 呢?它搞了一套 CTU、CU、PU、TU 的层级体系,配合四叉树递归划分。说白了,就是把一张图切成无数个大小不一的块,然后针对每个块做最合适的编码。
你想想看,一张画面里,有的区域很平坦(比如天空),有的区域细节特别多(比如树叶)。如果都用同样大小的块去处理,要么平坦区域浪费码率,要么细节区域压缩质量不够。H.265 这套划分机制,就是为了解决这个矛盾。
3.1 最大编码单元(CTU)与最小编码单元
CTU,全称 Coding Tree Unit,编码树单元。它是 H.265 图像划分的起点。一张 4K 或 1080p 的帧,首先会被分割成若干个 CTU。CTU 的大小是可配置的,标准支持 64x64、32x32 和 16x16。实际项目中,64x64 是最常用的。
核心概念:CTU 是四叉树的根节点。一个 CTU 可以递归向下划分,直到达到最小编码单元(SCU,Smallest Coding Unit)。SCU 的大小通常是 8x8 或 4x4。
我刚开始接触 HEVC 时,有个误区:以为 CTU 越大越好。64x64 确实能带来更高的压缩比,但编码复杂度也直线上升。后来在优化一个实时编码器时,我吃过这个亏——64x64 的 CTU 在软件编码器上跑不动,延迟飙到 200ms。最后不得不根据分辨率动态调整:1080p 用 64x64,720p 以下用 32x32。
| 参数 | CTU(最大编码单元) | SCU(最小编码单元) |
|---|---|---|
| 典型大小 | 64x64 | 8x8 |
| 作用 | 四叉树根节点,划分起点 | 四叉树叶节点,划分终点 |
| 对码率影响 | 越大压缩比越高 | 越小细节保留越好 |
| 对复杂度影响 | 越大编码越慢 | 越小编码越慢 |
3.2 四叉树划分结构
四叉树是 H.265 划分的核心算法。它的逻辑很简单:一个节点(方块)可以分成四个子节点(四个小方块)。每个子节点又可以继续分,直到满足条件为止。
为什么会用四叉树?因为图像内容在空间上往往是自相似的。平坦区域不需要细分,纹理复杂区域需要细分到小块。四叉树正好能自适应地表达这种结构。
下面这张图展示了一个 64x64 CTU 经过四叉树划分后的典型结果:
实战技巧:四叉树的深度直接决定了编码质量与速度的平衡。深度越深,块越小,细节保留越好,但码率和耗时都会增加。我一般建议:深度不超过 3 层(即最小块 8x8),再往下分收益就很低了。
3.3 CU、PU、TU 三者的关系
CTU 划分完成后,每个叶节点就是一个 CU(Coding Unit,编码单元)。但 CU 只是负责「这块归我管」,具体怎么编码,还要看 PU 和 TU。
这三者的分工很明确:
- CU(编码单元):决策单元。决定用帧内还是帧间预测,以及划分方式。
- PU(预测单元):预测单元。负责具体的预测操作,比如运动估计、帧内方向选择。
- TU(变换单元):变换单元。负责残差数据的 DCT 变换和量化。
一个 CU 可以包含多个 PU 和多个 TU。PU 的划分方式决定了预测的精细程度,TU 的划分方式决定了变换的粒度。它们之间是独立的,但又有联系——PU 的边界会影响 TU 的划分。
注意:PU 和 TU 的划分不能跨越 CU 边界。也就是说,一个 CU 内部的 PU 和 TU 再怎么分,都不能跑到隔壁 CU 去。这是 H.265 标准强制规定的。
3.4 PU 的划分模式
PU 的划分取决于 CU 用的是帧内预测还是帧间预测。帧内预测的 PU 只能是正方形(2Nx2N 或 NxN),而帧间预测的 PU 可以是非正方形的矩形。
帧间预测的 PU 划分模式有 8 种:
| 模式 | 描述 | 适用场景 |
|---|---|---|
| 2Nx2N | 不划分,整个 CU 作为一个 PU | 平坦区域、静止背景 |
| 2NxN | 水平分成两个矩形 PU | 水平方向有运动边界 |
| Nx2N | 垂直分成两个矩形 PU | 垂直方向有运动边界 |
| NxN | 分成四个正方形 PU | 复杂运动区域 |
| 2NxnU | 上 1/4、下 3/4 不对称划分 | 物体边缘在 CU 上部 |
| 2NxnD | 上 3/4、下 1/4 不对称划分 | 物体边缘在 CU 下部 |
| nLx2N | 左 1/4、右 3/4 不对称划分 | 物体边缘在 CU 左侧 |
| nRx2N | 左 3/4、右 1/4 不对称划分 | 物体边缘在 CU 右侧 |
我曾经在优化运动估计时,发现不对称划分模式(2NxnU 等)对斜向运动的物体特别有效。比如一个球从画面左上向右下运动,用对称划分很难精确匹配,但不对称划分就能很好地贴合运动边界。不过,不对称划分的计算量也大,低功耗设备上建议禁用。
3.5 TU 与变换块
TU 负责残差数据的变换。H.265 支持 4x4、8x8、16x16 和 32x32 四种大小的 DCT 变换。TU 的划分也是基于四叉树,但它的根节点是 CU,而不是 CTU。
TU 的大小选择会影响编码质量:
- 大 TU(16x16、32x32):能量集中效果好,适合平坦区域,码率低。
- 小 TU(4x4、8x8):细节保留好,适合纹理复杂区域,码率高。
关键点:TU 的划分深度不能超过 CU 的划分深度。比如一个 16x16 的 CU,TU 最大只能是 16x16,最小可以是 4x4。但 TU 不能比 CU 还大。
3.6 实际编码中的划分策略
说了这么多理论,实际编码器里是怎么选划分方式的?我总结了几条经验:
- 先粗分,再细分:从 CTU 开始,先尝试不划分(2Nx2N),计算 RD cost。如果效果不好,再递归向下划分。这叫「自上而下」的搜索策略。
- 提前终止:如果当前 CU 的 RD cost 已经足够低,就不需要再往下分了。这个阈值需要根据码率控制目标动态调整。
- 跳过对称划分:在低延迟场景下,不对称划分(2NxnU 等)的收益往往不如直接分成更小的 CU。我一般会直接跳过不对称模式,省下 20%-30% 的编码时间。
- TU 大小跟随 CU:如果 CU 是 32x32,TU 优先尝试 32x32。如果残差能量集中在低频,大 TU 就够了;如果残差有高频细节,再递归分小。
嗯,这里要注意:划分决策是编码器最耗时的部分。一个 1080p 的帧有大约 510 个 64x64 CTU,每个 CTU 平均要评估几十种划分组合。如果全遍历,编码时间会爆炸。所以实际产品中,都会用各种启发式算法来加速。
我的建议:如果你在写自己的编码器,先实现一个简单的「固定深度划分」版本。比如所有 CU 固定为 16x16,PU 只用 2Nx2N。跑通了再逐步加入四叉树递归和不对称模式。一步到位容易出 bug,而且性能调优无从下手。
最后说一句,CTU/CU/PU/TU 这套体系,是 H.265 相比 H.264 最大的进步,也是最大的复杂度来源。理解了它,你就掌握了 HEVC 的一半精髓。