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的子块,每个子块独立判断要不要继续拆。

这个过程是递归的:

  1. 从CTU(深度0)开始,计算当前块的编码代价(RD-cost)。
  2. 尝试拆分成4个子块(深度+1),分别计算每个子块的编码代价。
  3. 比较“不拆”和“拆”的总代价。如果拆了更划算,就拆;否则保留。
  4. 对拆出来的子块,重复步骤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经过四叉树递归划分后的典型结构:

CTU四叉树递归划分示意图(64x64) CTU 64x64 深度0 CU 32x32 深度1 CU 32x32 深度1 CU 32x32 深度1 CU 32x32 深度1 16x16 深度2 16x16 深度2 16x16 深度2 8x8 8x8 8x8 8x8 对应的四叉树 CTU 0 1 2 3 0 1 2 3 0 1 2 3 图例说明 • 左侧:CTU空间划分结果 • 右侧:对应的四叉树结构 • 颜色越深,划分深度越大 • 右下角区域拆到了8x8(深度3) 深度0: 64x64 深度1: 32x32 深度2: 16x16 深度3: 8x8 映射

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的块结构,本质上是让编码器学会“因材施教”——平坦的地方用大块,复杂的地方用小块。理解了这个思想,后面的帧内预测、帧间预测、变换量化,你都会觉得顺理成章。