# DVC｜H.264 流程与算法

> 用户问题：讲完他,适当讲解算法
>
> 来源：本聊天 `01a13ae4-6cb2-7f23-b09c-942490bf8387`，公开回复记录第 218 行，2026-10-15 00:06:22 +08:00。
>
> 可核实定位：分块、SAD、变换、量化、熵编码、RDO、重建与I/P/B。

### 原进度消息中的实质性补充（记录第 205 行）

<!-- source-body:C205:begin -->
需要特别分清：预测、变换和熵编码各有不同作用；量化才是这条流程中引入损失的主要环节。我会用数字例子说明这些步骤，再把它们连成完整的编解码流程。

<!-- source-body:C205:end -->

<!-- source-body:R04:begin -->
可以。下面把 **H.264 的核心编解码流程讲完整，并穿插必要的算法**，让你能看懂它与 DVC 的对应关系。

上一题的答案是：残差为 \(108-100=8\)；如果解码出的残差为 6，重建值就是 \(100+6=106\)。

先统一三个符号：\(X\) 表示原始图像块，\(P\) 表示预测块，\(E=X-P\) 表示残差。下面主要以亮度像素为例。

1. **先把图像分成块，分别处理不同区域。**

   一幅图像中，背景可能静止，人物可能运动，人物的手和身体还可能往不同方向运动。因此，编码器需要在局部区域选择合适的预测方法。

   H.264 以包含 \(16\times16\) 个亮度像素的宏块作为基本组织单位。运动补偿时，可以进一步使用 \(16\times8\)、\(8\times16\)、\(8\times8\) 等分块，最小可到 \(4\times4\)。

   大块需要描述的运动信息少，小块能更细致地适应局部运动，却需要更多分块和运动信息。**如何分块，本身也是压缩中的选择题。** [帧间预测与分块说明](https://www.vcodex.com/h264avc-inter-prediction)

2. **帧内预测：利用同一帧中已经重建的邻居。**

   假设一个块上方的四个像素是：

   ```text
   100  102  104  103
   ```

   如果当前区域主要沿竖直方向保持相似，可以把这行数值向下复制，作为整个块的预测。这叫竖直预测。

   其他方法包括从左侧向右复制的水平预测、使用邻居平均值的 DC 预测，以及沿不同方向延伸的预测。

   编码器尝试不同预测方式，选出合适的一种，然后编码：

   \[
   E=X-P
   \]

   接收端根据传来的预测模式，用相同的邻居生成 \(P\)，再加上解码出的残差。

   **帧内编码也有预测，也需要残差。** 它利用的是当前帧内部的信息。[帧内预测说明](https://www.vcodex.com/h264avc-intra-prediction)

3. **帧间预测：先搜索运动，再根据运动取出预测块。**

   对当前块，编码器到已经重建的参考图像里寻找相似区域。两个位置之间的偏移，用运动向量表示：

   \[
   v=(d_x,d_y)
   \]

   一个基础搜索方法是比较候选块与当前块的 **SAD，即绝对差之和**：

   \[
   \operatorname{SAD}(d_x,d_y)
   =\sum_{i,j}
   \left|X(i,j)-P_{d_x,d_y}(i,j)\right|
   \]

   SAD 越小，候选块越接近当前块。教学用的穷举搜索可以写成：

   ```text
   最小误差 = 无穷大

   遍历搜索范围内所有合法偏移 (dx, dy):
       从参考图像取出候选块
       误差 = 当前块与候选块的绝对差之和

       如果误差更小:
           保存这个误差
           保存偏移 (dx, dy)

   返回保存的偏移
   ```

   **寻找偏移叫运动估计；根据偏移生成预测块叫运动补偿。**

   穷举搜索计算量很大，实际编码器会采用更快的搜索策略，并考虑运动信息所需的比特数。H.264 亮度运动补偿还支持 \(1/4\) 像素精度：小数位置的像素通过插值得到。

   运动向量本身也可以预测。例如邻块预测的向量为 \((9,2)\)，当前向量为 \((10,2)\)，就可以传输差值 \((1,0)\)。[亚像素补偿与运动向量预测](https://www.vcodex.com/h264avc-inter-prediction)

4. **变换：把残差换成更容易压缩的表示。**

   相邻残差也可能相似。我们可以把它们分解成“整体变化”和“局部差异”。

   用两个数演示：

   \[
   E=[10,12]
   \]

   做一个简单的求和、求差变换：

   \[
   T=[10+12,\ 10-12]=[22,-2]
   \]

   第一个数描述整体水平，第二个数描述两个位置的差异。如果原来是 \([10,10]\)，变换后就是 \([20,0]\)，差异项变成了零。

   这里使用的是教学变换。H.264 常用的 \(4\times4\) 变换采用整数运算，是对 DCT 的近似；其核心运算可以通过加法和移位高效完成。[H.264 变换设计论文](https://www.microsoft.com/en-us/research/publication/low-complexity-transform-and-quantization-in-h-264avc/)

   **变换本身没有减少数值的数量；它为后面的量化和熵编码创造条件。**

5. **量化：用近似值换取更少的编码数据。**

   一个简化的量化模型是：

   \[
   q=\operatorname{round}(T/\Delta)
   \]

   其中 \(\Delta\) 是量化步长。接收端进行反量化：

   \[
   \hat T=q\Delta
   \]

   接着刚才的例子，令 \(\Delta=5\)：

   \[
   [22,-2]\rightarrow[4,0]\rightarrow[20,0]
   \]

   原来的细微差异丢失了。通过逆变换：

   \[
   \hat E=
   \left[
   \frac{20+0}{2},
   \frac{20-0}{2}
   \right]
   =[10,10]
   \]

   残差从 \([10,12]\) 变成了 \([10,10]\)。

   H.264 使用量化参数 **QP** 控制量化粗细。对于常见的 8 位编码，亮度 QP 通常在 0～51；QP 每增加 6，量化步长加倍。QP 越大，通常码率越低、失真越大。实际运算还包含缩放和舍入规则，上面的除法模型用于理解原理。[变换与量化说明](https://www.vcodex.com/h264avc-4x4-transform-and-quantization)

6. **熵编码：把符号无损地变成比特。**

   量化后，经常出现大量零和小数值。如果每个符号都用同样长的编码，会浪费空间。熵编码利用符号的概率，让常见情况占用更少的平均比特。

   H.264 中两种重要的方法是 CAVLC 和 CABAC。CABAC 可以从三个步骤理解：

   - 把系数、运动差值等信息转换成二元决策。
   - 根据邻近已编码信息等“上下文”，选择和更新概率模型。
   - 使用二进制算术编码，将这些决策编码成比特流。

   算术编码可以理解为不断缩小一个数值区间：概率大的结果分到较大的子区间，概率小的结果分到较小的子区间，最终用足够的比特指出落在哪个区间。

   **熵编码能够精确恢复输入符号。它不会修复量化丢失的信息，也不会进一步损失这些符号。** [CABAC 设计者的算法说明](https://www.hhi.fraunhofer.de/en/departments/vca/research-groups/video-coding-technologies/research-topics-of-video-coding-technologies-group/past-research-topics/statistical-modeling-coding/context-based-adaptive-binary-arithmetic-coding-cabac.html)

7. **率失真优化：决定采用哪一种编码方案。**

   编码器面临许多选择：怎样分块、选哪个参考图像、使用哪种预测模式，以及量化多粗。

   一个常用的目标是：

   \[
   J=D+\lambda R
   \]

   \(D\) 是重建失真，\(R\) 是需要的比特数，\(\lambda\) 控制比特成本的权重。编码器比较候选方案，倾向选择 \(J\) 更小的方案。

   看一个自设例子：

   | 方案 | 失真 \(D\) | 比特数 \(R\) |
   |---|---:|---:|
   | A | 10 | 100 |
   | B | 20 | 20 |

   当 \(\lambda=0.1\)，A 的成本为 20，B 为 22，选择 A。

   当 \(\lambda=1\)，A 的成本为 110，B 为 40，选择 B。

   **所以，残差更小的预测方案，可能因为运动或模式信息太贵，整体反而不划算。** 这就是率失真优化的作用。[率失真编码器控制研究](https://doi.org/10.1109/TCSVT.2003.815168)

8. **编码器也要重建图像，再把它作为后续参考。**

   延续数字例子：原始像素为 \([110,112]\)，预测为 \([100,100]\)，解码残差为 \([10,10]\)，那么重建结果是：

   \[
   \hat X=P+\hat E=[110,110]
   \]

   接收端得到的是这个结果。因此，编码器预测后续图像时，也必须从同样的重建信息出发。

   编码器内部会执行反量化、逆变换和重建。H.264 还对重建图像应用**环内去块滤波**，减轻分块处理造成的边界伪影；滤波后的图像可以用于后续帧间预测。[H.264 总览中的重建与环内滤波](https://www.cs.ubc.ca/~krasic/cpsc538a/papers/h264avc-overview.pdf)

   下面这张图体现了编码器中的两个分支：

   ```mermaid
   flowchart TB
       A["原始块 X"] --> B["减去预测块 P"]
       P["由已重建信息生成预测 P"] --> B
       B --> C["残差：变换 → 量化"]
       C --> D["熵编码"]
       D --> E["输出比特流"]
       C --> F["反量化 → 逆变换"]
       F --> G["解码残差"]
       G --> H["加上预测 P"]
       P --> H
       H --> I["重建与环内滤波"]
       I --> J["存储，供后续帧间预测"]
   ```

9. **最后把这些块放回视频序列，理解 I、P、B 的作用。**

   入门时，可以这样理解：

   | 类型 | 主要允许的预测方式 |
   |---|---|
   | I | 使用当前图像内部的信息 |
   | P | 可以使用已解码参考图像，也可以使用帧内预测 |
   | B | 可以使用两组参考列表，并组合两份预测，也可以使用帧内预测 |

   常见 B 帧例子是同时参考播放顺序中前面和后面的帧。假设播放顺序是：

   ```text
   I0 → B1 → B2 → P3
   ```

   如果 B1、B2 都依赖 P3，那么可以先解码：

   ```text
   I0 → P3 → B1 → B2
   ```

   再按播放顺序输出。因此，**解码顺序和播放顺序可以不同**。严格说，I/P/B 是切片类型；把它们当作帧类型，是入门时常用的简化。[参考关系与图像顺序说明](https://www.cs.ubc.ca/~krasic/cpsc538a/papers/h264avc-overview.pdf)

你现在回到 DVC，可以直接对照这些模块：

| 已经理解的作用 | DVC 中的实现 |
|---|---|
| 搜索运动、生成预测 | 光流估计与运动补偿网络 |
| 表示和压缩运动信息 | 运动编码器、解码器 |
| 变换和恢复残差 | 残差编码器、解码器 |
| 估计编码成本 | 学习潜变量的概率分布 |
| 平衡码率与失真 | 用率失真损失联合训练 |

DVC 写的是 \(L=\lambda D+R\)，上面讲的是 \(J=D+\lambda R\)，权重放的位置不同。对正的权重而言：

\[
\lambda D+R
=\lambda\left(D+\frac{1}{\lambda}R\right)
\]

所以 **DVC 的 \(\lambda\) 越大，越重视画质；上面 \(D+\lambda R\) 中的 \(\lambda\) 越大，越重视省比特**。阅读公式时，要先看权重乘在哪一项上。

这些对应关系见原文：[DVC 主论文](Lu_DVC_An_End-To-End_Deep_Video_Compression_Framework_CVPR_2019_paper.pdf)
<!-- source-body:R04:end -->
