欢迎来到视频编码之旅的最后一站(在解码器端是第一站)——熵编码。在此前的章节中,我们已经通过预测、变换和量化三大核心工具,极大地压缩了原始视频数据中的各种冗余。然而,经过量化后的变换系数、运动矢量等语法元素,其统计特性仍然存在着巨大的压缩潜力。熵编码就是挖掘这种统计冗余的无损压缩技术,它负责将这些语法元素高效地打包成最终的比特流,是决定编码效率的关键一步。
在现代视频编码标准中,熵编码通常能够实现额外 20%-40% 的压缩增益。对于一个典型的 1080p 视频,如果没有熵编码,输出码率可能需要增加数倍才能保持相同的视觉质量。这使得熵编码成为整个编解码流程中回报最高的技术环节之一。
本章将深入探讨熵编码的理论基础,从信息论的经典原理出发,解析两种主流的编码方法——CAVLC 和 CABAC 的精妙设计,并展望 ANS、神经网络等更前沿的技术在此领域的创新与应用。
在混合编码框架中,熵编码是唯一的无损压缩环节,也是编码器的”最后守门人”。它的核心任务是为编码器输出的各种语法元素(如量化后的变换系数、运动矢量差、预测模式、量化参数等)分配尽可能短的码字。其基本思想源于信息论的根本原理:出现概率越高的符号,分配越短的码字;出现概率越低的符号,分配越长的码字。这样,在总体上就能用最短的平均码长来表示整个符号序列。
一个理想的熵编码器能将符号序列压缩到其信息熵的理论极限。信息熵 $H(X)$ 定义为: \(H(X) = -\sum_{i=1}^{n} p(x_i) \log_2 p(x_i)\) 其中,$X$ 是一个随机变量,代表要编码的符号集合,$x_i$ 是集合中的一个具体符号,$p(x_i)$ 是该符号出现的概率。$H(X)$ 的单位是比特/符号,它表示了编码一个符号所需的理论最小平均比特数。
关键洞察:香农第一定理告诉我们,任何无损数据压缩的极限都由源的熵决定。这意味着即使是最先进的熵编码器,也无法突破这个理论极限,只能无限接近它。
在视频编码中,各种语法元素都展现出强烈的非均匀分布特性:
这些高度偏斜的概率分布为熵编码提供了巨大的压缩空间。一个精心设计的熵编码器能够将这些偏斜性转化为显著的比特节省。
实践要点:理解符号的统计特性是设计高效熵编码的前提。现代编码标准在设计熵编码方案时,都是基于大量视频序列的统计分析,针对不同内容类型(如自然场景、动画、屏幕内容等)优化其概率模型。
CAVLC (Context-Adaptive Variable-Length Coding) 是一种主要用于 H.264/AVC 标准中对量化变换系数进行编码的熵编码方法。它结合了变长编码 (VLC) 和上下文自适应的思想,相比于简单的 Huffman 编码,它能更好地适应局部数据的统计特性,从而获得更高的压缩效率。
CAVLC 的设计巧妙地利用了量化后 DCT 系数的几个典型统计特征,这些特征反映了自然图像的基本规律:
高频系数多为零:经过 DCT 和量化后,图像块的能量通常集中在左上角的低频区域,而右下角的高频系数大部分为零。这是因为自然图像往往具有相对平缓的变化,高频细节较少。统计显示,在典型的视频序列中,超过 60-80% 的高频 DCT 系数在量化后变为零。
具体数据示例:
Zig-Zag 扫描的智慧:通过 Zig-Zag 扫描将二维系数矩阵转换为一维序列,可以使得连续的零值(Trailing Zeros)集中在一起,便于使用行程编码 (Run-Length Encoding)。
4×4 块的 Zig-Zag 扫描路径:
DC 1 5 6
2 4 7 12
3 8 11 13
9 10 14 15
扫描顺序:0→1→2→3→4→5→6→7→8→9→10→11→12→13→14→15
这种扫描方式的优势:
非零系数的幅值分布:非零系数的幅值遵循拉普拉斯分布,小幅值(如 ±1, ±2)的出现概率远高于大幅值。在典型视频中,约 70-80% 的非零系数的绝对值小于等于 2。特别是幅值为 ±1 的系数(称为 TrailingOnes)在高频区域尤为常见。
典型分布统计:
局部相关性:相邻块的非零系数个数 (TotalCoeffs) 具有强相关性。如果当前块的左邻块和上邻块包含较多非零系数,那么当前块也倾向于有较多非零系数。这种相关性可以用来预测当前块的系数分布,从而选择更合适的编码表。
相关性数据:相邻块的 TotalCoeffs 相关系数通常在 0.6-0.8 之间,这种强相关性是 CAVLC 上下文自适应的核心基础。
CAVLC 的编码过程可以分解为以下几个关键步骤,它并不直接对每个系数值进行编码,而是对一系列组合语法元素进行编码。让我们通过一个具体例子来深入理解这个过程:
完整编码示例: 假设我们有一个 4×4 系数块(经过 Zig-Zag 扫描后):
原始序列: [5, 0, 3, 1, -1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
位置编号: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11,12,13,14,15]
示例分析:
5, 3, 1, -1, 1,所以 TotalCoeffs = 51(位置5)是最后一个非零系数,幅值为1;-1(位置4)也是±1;1(位置3)也是±13(≠±1),所以拖尾停止TrailingOnes = 3(三个连续的 ±1:位置3、4、5的系数)上下文选择机制:
nC = (nA + nB + 1) >> 1nC < 2: 使用 coeff_token 表 0(期望较少非零系数)2 ≤ nC < 4: 使用 coeff_token 表 14 ≤ nC < 8: 使用 coeff_token 表 2nC ≥ 8: 使用 coeff_token 表 3(期望较多非零系数)对于步骤 1 中确定的每个 TrailingOne,用 1 比特表示其符号:
0 代表正号 +1 代表负号 -编码顺序是从 Zig-Zag 扫描的最后一个 TrailingOne 开始,向前编码。
示例继续:
1→位置4的-1→位置3的10 (正号) + 1 (负号) + 0 (正号) = 010对于除了 TrailingOnes 之外的非零系数,需要编码其完整的幅值(包括符号)。编码顺序是按 Zig-Zag 扫描的逆序进行。
自适应 VLC 表选择:
level_prefix 表 0|level| > threshold,则切换到下一级表TotalZeros 是指在最后一个非零系数之前所有零系数的总数。它等于:
TotalZeros = (最后一个非零系数的位置) - (TotalCoeffs - 1)
根据 TotalCoeffs 的值选择相应的 total_zeros VLC 表。当 TotalCoeffs = 16 时(块满),不需要编码 TotalZeros。
从最后一个非零系数开始,往前为每个非零系数编码其前面连续零的个数。
游程分配过程:
ZerosLeft,初始值为 TotalZerosRunBefore 值RunBefore,更新 ZerosLeft -= RunBeforeZerosLeft 值选择相应的 run_before VLC 表这种设计确保了所有的零都被准确分配,无需为最后一个非零系数编码 RunBefore。
Rule-of-thumb: CAVLC 的设计哲学是一种“化整为零”的策略。它不直接编码一个复杂的系数序列,而是将其拆解为多个更易于用统计模型描述的语法元素(如 TotalCoeffs, TrailingOnes, RunBefore 等),然后为每个元素设计上下文自适应的编码表,从而在计算复杂度和压缩效率之间取得了很好的平衡。
由于这些缺点,CAVLC 在 H.264/AVC 的更高级别 (Main Profile 及以上) 中被 CABAC 所取代,并且在 HEVC 和 VVC 等后续标准中已不再使用。
CABAC (Context-Adaptive Binary Arithmetic Coding) 是目前主流视频编码标准(如 H.264/AVC, HEVC, VVC)中使用的高效熵编码方案。它通过将算术编码与上下文模型相结合,提供了比 CAVLC 高出 5-15% 的编码效率。
CABAC 的强大之处在于其将编码过程分为三个独立的阶段:
这种分离使得 CABAC 能够极其灵活和精确地对数据进行建模和压缩。
二值化的目标是将一个语法元素值转换为一串二进制码(bin 序列)。不同的语法元素采用不同的二值化方案,每种方案都针对特定的概率分布优化:
主要二值化方案:
n,编码为 n 个 “1” 后面跟一个 “0”。例如:
0 → 01 → 103 → 1110
适合编码几何分布(小值概率高)的语法元素,如参考帧索引。cMax 已知时,最大值不需要终止符 “0”:
cMax = 3:0→0, 1→10, 2→110, 3→111
常用于编码量化参数增量等有界值。v 分为前缀(一元码)和后缀(定长码)两部分EG0: 值 v = 前缀长度 + 2^(前缀长度) - 1 + 后缀值EG0(5) = 00100 (前缀 001, 后缀 00)[0, cMax] 已知时,使用 ⌈log₂(cMax+1)⌉ 比特:
复合二值化方案: CABAC 中很多语法元素使用组合方案,如 截断一元码 + k阶指数哥伦布码 (TU+EGk):
v < cMax 时使用截断一元码v ≥ cMax 时使用 TU(cMax) + EGk(v - cMax)实际示例:运动矢量差 (MVD) 的二值化:
|MVD| 使用 TU(9)+EG3 方案:
|MVD| = 0 → 0|MVD| = 1 → 10|MVD| = 3 → 1110|MVD| = 12 → 111111111 + 010 (= TU(9) + EG3(3))0=正, 1=负)MVD = +3 的完整二值化为:1110 + 0二值化方案的选择原理: 不同的二值化方案适用于不同的概率分布模式:
二值化的设计目标是使高概率的值对应较短的 bin 序列,这样就能为后续的概率建模提供良好的基础。
这是 CABAC 的精髓所在。对于二值化后的序列中的每一个 bin,CABAC 都会选择一个上下文模型来预测其概率。上下文选择基于已编码的相关语法元素信息,体现了强大的自适应能力。
上下文模型的结构: 每个上下文模型是一个有限状态机,包含:
p_LPS = rangeTabLPS[pStateIdx]概率量化的精妙设计: CABAC 使用 64 个量化级别来表示概率,这种设计平衡了以下需求:
每个概率级别对应的 LPS 概率大致遵循几何级数递减:
p_LPS[0] ≈ 0.5, p_LPS[1] ≈ 0.46, ..., p_LPS[63] ≈ 0.0156
上下文选择策略:
概率更新机制:
if (binVal == valMPS) {
// 编码了 MPS,增强 MPS 概率
if (pStateIdx < 62) pStateIdx++;
} else {
// 编码了 LPS,处理概率逆转
if (pStateIdx == 0) valMPS = 1 - valMPS;
pStateIdx = transIdxLPS[pStateIdx];
}
这种更新确保了模型能够快速适应局部统计特性的变化。当 LPS 连续出现时,模型会逐渐将其转变为新的 MPS,实现概率估计的自适应性。
上下文初始化: 不同片类型(I/P/B)对应不同的初始化参数:
pStateIdx = Clip3(1, 126, ((m * QP) >> 4) + n)算术编码是一种能以接近信息熵理论极限进行压缩的编码技术。CABAC 使用的是专门优化的二进制算术编码引擎,采用整数运算避免浮点计算的精度问题。
编码引擎的核心状态:
编码过程:
qCodIRangeIdx = (codIRange >> 6) & 3 // 量化区间大小
codIRangeLPS = rangeTabLPS[pStateIdx][qCodIRangeIdx]
codIRangeMPS = codIRange - codIRangeLPS
if (binVal == valMPS) {
// 编码 MPS
codIRange = codIRangeMPS
} else {
// 编码 LPS
codILow += codIRangeMPS
codIRange = codIRangeLPS
}
codIRange < 256 时触发,确保区间大小维持在合理范围:
while (codIRange < 256) {
if (codILow < 256) {
putBit(0)
flushBitsOutstanding(0)
} else if (codILow >= 512) {
putBit(1)
flushBitsOutstanding(1)
codILow -= 512
} else {
bitsOutstanding++
codILow -= 256
}
codIRange <<= 1
codILow <<= 1
}
旁路模式 (Bypass Mode): 对于概率接近 0.5 的 bin(如符号位、EG 后缀),CABAC 使用简化的旁路编码:
codIRange >>= 1算术编码的关键优势:
解码过程: 解码器维护相同的状态变量,通过比较当前码值与区间边界来恢复符号:
if (codIValue >= codIRangeMPS) {
binVal = !valMPS // 解码出 LPS
codIValue -= codIRangeMPS
codIRange = codIRangeLPS
} else {
binVal = valMPS // 解码出 MPS
codIRange = codIRangeMPS
}
Rule-of-thumb: CABAC 的高效率来自于“专业化分工”。二值化将问题简化为“是/否”的判断;上下文建模利用局部相关性为这个判断提供最精准的“猜测”;算术编码则根据这个“猜测”的置信度,用最高效的方式记录下最终的“答案”。
| 特性 | CAVLC | CABAC | | :— | :— | :— | | 核心技术 | 上下文自适应变长编码 | 上下文自适应二进制算术编码 | | 压缩效率 | 较高 | 非常高 (比 CAVLC 高 5-15%) | | 概率模型 | 静态VLC表,上下文选择表 | 动态更新的概率模型 | | 输出 | 整数个比特 | 小数个比特(理论上) | | 计算复杂度| 较低 | 较高(需要乘法运算) | | 适用性 | 主要用于 H.264 的系数编码 | 适用于所有语法元素 |
ANS 是一种现代熵编码方法,由 Jarosław (Jarek) Duda 在 2009 年提出。它在性能上达到了与算术编码相近的压缩率,但计算速度要快得多,因为它完全避免了乘法运算。这使得 ANS 成为许多现代压缩算法(如 Facebook 的 Zstandard, Apple 的 LZFSE)的核心。
ANS 的工作方式与算术编码有很大不同。算术编码通过不断缩小一个区间来表示信息,而 ANS 则将整个信息序列映射到一个巨大的整数上。它的状态(state)就是一个整数 x,编码一个符号 s 就是将当前状态 x 更新为一个新的状态 x',这个过程可以表示为一个函数 x' = C(x, s)。解码则是通过逆函数 (x, s) = D(x') 来恢复出原始状态和符号。
ANS 有两种主流的变体:
Rule-of-thumb: 如果说算术编码像是在一个区间 [0, 1) 上进行“小数运算”,那么 ANS 就像是在一个巨大的整数环上进行“整数运算”。通过巧妙的数字游戏,ANS 在不使用乘法的情况下实现了与算术编码同等级别的压缩率,是速度与效率完美结合的典范。
在视频编码领域,虽然主流标准尚未采纳 ANS,但它作为下一代熵编码技术的有力竞争者,在许多研究和特定应用中展示了巨大的潜力。
传统熵编码(如 CABAC)的上下文模型是人工设计和高度优化的,但它终究是基于有限的局部信息进行预测。随着深度学习的发展,研究人员开始探索使用神经网络来替代或增强 CABAC 中的上下文模型。
其核心思路是利用神经网络强大的非线性建模能力,从更广泛的上下文信息中学习出更精确的符号概率分布。一个典型的神经熵编码模型会:
这种方法在理论上可以捕捉到更复杂、更长距离的依赖关系,从而生成比传统 CABAC 更精确的概率模型,带来更高的压缩率。
尽管前景广阔,但将神经网络集成到熵编码中仍面临巨大挑战:
目前,这类技术主要出现在一些端到端的神经视频编码研究中,或者作为对现有编码器特定模块的增强。随着硬件计算能力的提升和网络模型优化技术的发展,基于 AI 的熵编码有望在未来的视频编码标准中扮演更重要的角色。
谈及熵编码,就不能不提及其理论基石的奠定者——克劳德·香农 (Claude Shannon)。在 1948 年发表的划时代论文《通信的数学理论》(A Mathematical Theory of Communication) 中,香农系统地创立了信息论,为现代数字通信和数据压缩领域铺平了道路。
香农在论文中首次提出了“比特”(bit) 作为信息的度量单位,并定义了“信息熵”的概念,从数学上精确地描述了信息的不确定性。他证明了任何无损压缩算法的压缩率都不可能低于信源的信息熵,为所有熵编码技术(从 Huffman 编码到算术编码)设定了理论上的黄金标准。
香农的工作不仅是一个理论突破,更是一种思想上的革命。他将信息从其具体的物理载体(如声音、图像)中抽象出来,作为一种可以被量化、分析和处理的数学实体。没有香农和他的信息论,我们今天所熟知的数字世界,包括高效的视频编解码,都将无从谈起。
进入 21 世纪,熵编码领域最重要的突破之一就是上文提到的 ANS。而将这一理论成功转化为工业级应用并大力推广的,当属 Google 和 Facebook 两大科技巨头。
Google 与 rANS:Google 在其视频编码器 VP9 的后续研究和 AV1 标准的早期探索中,对 rANS 进行了深入研究和实现,并将其作为一种潜在的下一代熵编码方案。虽然最终 AV1 仍然采用了传统的算术编码,但 Google 的工作极大地推动了 ANS 在开源社区的认知度和应用。
Facebook 与 Zstandard (Zstd):由 Yann Collet 在 Facebook 领导开发的 Zstandard 是一种通用的无损数据压缩算法,其核心就是 ANS。Zstd 在压缩率上媲美传统的 Deflate (zip) 算法,但压缩和解压速度却快得多。它的成功使得 ANS 技术在服务器、数据库、文件系统等领域得到了大规模应用,成为现代数据压缩的事实标准之一。
这些当代事件表明,即使在信息论诞生 70 多年后的今天,熵编码这一古老而基础的领域依然充满了创新与活力。
本章深入探讨了熵编码,这是视频压缩流程中至关重要的无损压缩环节。