3. 编码树单元(CTU)与四叉树划分:CTU/CU/PU/TU概念、四叉树递归划分、编码块结构优势
好,咱们今天聊聊HEVC里最核心的一个概念——CTU和四叉树划分。
说实话,我刚从H.264转到HEVC时,第一感觉就是:这玩意儿怎么这么复杂?H.264里一个宏块16x16搞定一切,到了HEVC,块大小可变、形状可变、还能递归拆分。但等你真正用上手,你就会发现——这种灵活性,正是HEVC压缩效率大幅提升的关键。
3.1 从宏块到编码树单元:一次彻底的升级
H.264时代,我们习惯说“宏块”(Macroblock),固定16x16像素。到了HEVC,这个概念被彻底重构了。
HEVC引入了编码树单元(CTU,Coding Tree Unit)。你可以把它理解成一个“超级宏块”。它的尺寸不再是固定的16x16,而是可以由编码器自由选择——通常是64x64,也可以扩展到128x128,或者缩小到32x32、16x16。
核心要点:CTU是HEVC编码的顶层单元。一个视频帧被分割成若干个互不重叠的CTU,每个CTU独立进行编码决策。CTU的尺寸越大,对平坦区域的编码效率越高。
我做过一个测试:同样是1080p的视频,用64x64的CTU比用16x16的宏块,在低码率下码率节省了约15%-20%。为什么?因为大块能更好地利用空间冗余,减少划分开销。
3.2 CTU、CU、PU、TU:四个概念,一个体系
很多初学者会被CTU、CU、PU、TU这四个缩写搞晕。我当年也花了点时间才理清楚。咱们用个表格来对比:
| 概念 | 全称 | 核心作用 | 最大/最小尺寸 | 我的理解 |
|---|---|---|---|---|
| CTU | Coding Tree Unit | 顶层根节点,包含亮度+色度 | 64x64(典型) | 就是那个“大框” |
| CU | Coding Unit | 编码决策的基本单元(帧内/帧间) | 64x64 ~ 8x8 | 决定“怎么编” |
| PU | Prediction Unit | 预测操作的基本单元 | 与CU相关,可更小 | 决定“怎么预测” |
| TU | Transform Unit | 变换和量化操作的基本单元 | 32x32 ~ 4x4 | 决定“怎么变换” |
说白了,CTU是一棵树的根,CU是树上的节点,PU和TU是节点上的“叶子”,各自负责不同的任务。
个人经验:我在调优时,习惯先固定CTU尺寸为64x64,然后通过调整CU的最小划分尺寸来控制编码复杂度。如果编码速度太慢,就把最小CU从8x8改成16x16——速度能提升30%以上,画质损失却很小。
3.3 四叉树递归划分:HEVC的“分而治之”
HEVC怎么决定一个CTU要不要拆?答案是——四叉树递归划分。
你想想看,一个64x64的CTU,如果画面内容很复杂(比如一片树叶),直接用一个64x64的CU去编码,预测残差会很大,码率反而高。这时候,编码器会尝试把CTU拆成4个32x32的子块,每个子块独立判断要不要继续拆。
这个过程是递归的:
- 从CTU(深度0)开始,计算当前块的编码代价(RD-cost)。
- 尝试拆分成4个子块(深度+1),分别计算每个子块的编码代价。
- 比较“不拆”和“拆”的总代价。如果拆了更划算,就拆;否则保留。
- 对拆出来的子块,重复步骤1-3,直到达到最小CU尺寸(通常是8x8)。
这就是HEVC的率失真优化(RDO)决策过程。说白了,编码器在“拆”和“不拆”之间反复权衡,找到最优解。
避坑指南:我曾经在一个项目中,为了追求极致压缩率,把最大CTU设成了128x128,最小CU设成了4x4。结果编码速度直接掉了5倍,而且码率节省不到3%。后来我意识到:不是拆得越细越好。对于大多数视频内容,64x64的CTU + 8x8的最小CU已经是最佳平衡点。
3.4 编码块结构优势:为什么HEVC要这么设计?
你可能会问:H.264的16x16宏块用得好好的,HEVC为什么要搞这么复杂?
原因很简单——视频内容不是均匀的。
一片蓝天,用64x64的大块编码,一个DC模式就能搞定,码率极低。但蓝天里有一只鸟,鸟的轮廓区域纹理复杂,就需要拆成小块来精细编码。四叉树结构让编码器能自适应地选择块大小,而不是一刀切。
具体优势有这几点:
- 灵活性高:大块处理平坦区域,小块处理纹理区域,各取所需。
- 码率节省:相比固定16x16宏块,HEVC的四叉树结构平均节省约20%-30%的码率。
- 预测更准:PU可以进一步细分,让运动估计更精确,尤其是对复杂运动区域。
- 变换更优:TU支持从32x32到4x4的变换,大块变换去相关性更强,小块变换避免振铃效应。
嗯,这里要注意:灵活性是有代价的。编码器需要遍历所有可能的划分方式,计算量呈指数级增长。这也是HEVC编码比H.264慢得多的主要原因之一。
3.5 四叉树划分的SVG结构图
光说可能不够直观,我画了一张图,展示一个64x64的CTU经过四叉树递归划分后的典型结构:
3.6 PU和TU:预测与变换的“小单元”
CU决定“怎么编”之后,PU和TU开始干活。
PU(预测单元)负责具体的预测操作。帧内预测时,PU可以是不对称的(比如2NxN、Nx2N),用来适应图像中的边缘方向。帧间预测时,PU可以拆成更小的块来做运动估计,提高运动补偿的精度。
我遇到过的一个案例:一个视频里有大量水平运动的物体(比如赛车),如果用正方形的PU去做运动估计,效果很差。后来我把PU划分模式从SQUARE改成了SMP(对称运动划分),码率直接降了8%。
TU(变换单元)负责残差数据的变换和量化。TU的尺寸可以独立于CU和PU。比如一个32x32的CU,如果残差很平滑,可以用32x32的TU做DCT变换,去相关性更强;如果残差有局部突变,可以拆成4个16x16的TU,避免振铃效应。
调优技巧:我个人习惯在编码器配置中,把TU的最大尺寸设为32x32,最小设为4x4。对于大多数内容,这个范围已经足够。如果编码目标是低延迟场景(比如视频会议),我会把TU最大尺寸限制在16x16,减少变换计算量。
3.7 实际编码中的块结构决策流程
最后,咱们梳理一下编码器实际处理一个CTU时的决策流程:
输入:一个64x64的CTU
输出:最优的四叉树划分 + 每个CU的PU/TU模式
1. 从CTU根节点开始(深度0)
2. 对当前CU:
a. 遍历所有可能的PU划分模式(2Nx2N, 2NxN, Nx2N, NxN等)
b. 对每种PU模式,遍历所有可能的TU划分
c. 计算每种组合的率失真代价(J = D + λ·R)
d. 记录当前CU的最优代价
3. 尝试将当前CU拆分为4个子CU(深度+1)
4. 对每个子CU,递归执行步骤2-3
5. 比较“不拆”和“拆”的总代价
6. 选择代价更小的方案
7. 输出最终的划分树和编码参数
这个过程,说白了就是“暴力搜索+剪枝”。编码器在搜索空间里找最优解,但也会通过一些启发式规则(比如提前终止、SKIP模式检测)来加速。
性能警告:如果你在开发实时编码器,千万别开全搜索。我曾经试过在x265里把rd-level开到6,结果编码速度从30fps直接掉到2fps。对于实时场景,建议把rd-level控制在3-4之间,配合early-skip和fast-intra决策,能在画质和速度之间取得不错的平衡。
好了,CTU和四叉树划分这块,核心就是这些。记住一句话:HEVC的块结构,本质上是让编码器学会“因材施教”——平坦的地方用大块,复杂的地方用小块。理解了这个思想,后面的帧内预测、帧间预测、变换量化,你都会觉得顺理成章。