6.2.4 变换、量化与熵编码(Transform, Quantization & Entropy Coding)
预测环把能量压进了残差,接下来轮到四环的后半程:变换负责 "聚能",量化负责 "取舍",熵编码负责 "打包"。H.264 在这三环的每一项,都针对前代的痛点做了手术。
4×4 整数变换:向浮点误差宣战
前代规格使用 8×8 浮点 DCT,理论上优雅,工程上却有个慢性病:浮点乘法在不同平台、不同实现间的舍入结果并不一致,编解两端的重建像素会缓慢偏离——6.1.3 所说的 漂移(Drift) 的元凶之一。
H.264 的处方是 整数变换:把 DCT 的浮点系数矩阵近似为一个只含小整数的 4×4 矩阵 [8]:
它与 DCT 的余弦基形状高度相似(3.5.1 的 IDCT 推导在这里完全适用),但全部运算只需加减与移位;基函数的归一化缩放则被抽出来,吸收进量化步骤 一并完成。由此,编解两端的变换结果 逐比特一致,漂移被彻底根除。
还有两个细节:
- 对 16×16 帧内宏块,16 个 4×4 块的 直流系数 会再抽出来做一次 4×4 哈达玛变换(Hadamard Transform)(即 3.5.2 的 WHT),把大块平坦区域的直流能量再压一次;色度直流则用 2×2 哈达玛
- 2005 年的 FRExt 扩展 为 High Profile 增加了 8×8 整数变换,与 4×4 自适应选择——对付 1080p 中大片缓变区域更顺手
52 级量化:对数刻度的总阀门
H.264 定义了 52 个量化参数 QP(Quantization Parameter,0~51),量化步长随 QP 按对数增长:QP 每增加 6,步长翻倍。这样的设计让码率控制非常平滑——编码器微调一两个 QP,就能得到码率的精细变化,实践中常见的 CRF(恒定质量因子)模式便建立在这把对数标尺上。
6.1.3 说过,量化是四环中唯一有损的环节。H.264 的工程巧思在于把变换的缩放、量化步长与取整偏移全部折叠进 预先算好的乘法查找表——解码器看到的反量化同样是纯整数运算。
熵编码双雄:CAVLC 与 CABAC
量化之后,系数块的典型形态是:寥寥几个非零值挤在左上角,拖着长长的零尾巴。H.264 提供两套熵编码方案 [5]:
CAVLC(Context-Adaptive Variable-Length Coding,上下文自适应变长编码)。沿用变长编码的框架,但码表不再固定:根据已编码邻块的非零系数个数,在多组 VLC 码表间自适应切换——邻块稀疏则当前块大概率也稀疏。系数的描述被拆成 拖尾 ±1 个数(Trailing Ones)、非零系数幅值(Levels)、零游程(Run) 等语法元素,各配专用码表;头部信息(模式、运动矢量差等)则统一使用 Exp-Golomb 指数哥伦布编码。
CABAC(Context-Adaptive Binary Arithmetic Coding,上下文自适应二进制算术编码)。换了一条更激进的技术路线:
算术编码(6.1.2 提过它逼近熵下限的能力)允许一个符号占用 小数比特;上下文建模则为每个二进制位维护随编码过程动态更新的概率估计——相邻块的模式、系数大小都会影响当前位的概率表。其代价是计算复杂度高、且本质串行难以并行。实测 CABAC 相比 CAVLC 平均再省 约 10% 的码率 [5],这也是 Main Profile 以上才被允许使用的原因。
环路去块滤波:写入标准的画质补救
分块变换 + 量化的副作用是 块效应——块边界处的像素断层。前代规格(H.263)只把它列为可选附录,H.264 则把 去块滤波(Deblocking Filter) 直接写进规范的重建环路:
- 滤波强度按块边界自适应(边界强度 BS 分 0~4 级):帧内块边界、有非零系数的边界、运动矢量差异大的边界,滤得更重
- 判定阈值 α、β 随 QP 调整:量化越粗,允许的平滑力度越大
- 滤波发生在 环路内——滤波后的帧才进入参考帧缓冲。块边界干净了,后续帧的运动补偿精度也跟着受益,一举多得
至此,H.264 的四环工具全部拆解完毕。下一节换个视角:不看技术,看身份——Profile 与 Level 如何为千差万别的解码设备划定能力边界,以及我们该如何亲手翻开这份厚重的官方规格。