第十三章|基础专题二:压缩所需的数学基础
用信息熵、预测、残差、DCT、量化、熵编码、率失真优化、PSNR、SSIM 和 VMAF 串起音视频压缩所需的数学基础。
第十三章|基础专题二:压缩所需的数学基础
原稿学习节奏:第 2 周。
建议投入:8~10 小时 前置知识:第一周的像素、位深、RGB/YCbCr、平面格式与原始帧 本周主线:相关性 → 预测 → 残差 → 变换 → 量化 → 符号概率 → 熵编码 → 率失真选择 → 质量评价
本周总览
压缩不是“把文件变小”的单一算法,而是一组相互配合的数学操作:
原始像素或参考帧
↓
预测:尽量猜中当前像素/块
↓
残差:只保留“猜错了多少”
↓
变换:把残差换到更适合压缩的频率坐标系
↓
量化:降低精度,让大量系数变成 0
↓
熵编码:给高概率符号更短的表示
↓
码流
编码器还要反复回答一个工程问题:
多花一些比特换更高质量,是否值得?
这就是率失真优化(Rate-Distortion Optimization,RDO)的核心。
本周不要求推导完整信号处理理论,而是要求做到:看懂公式、能够手算小例子、知道公式位于编码器或解码器的哪一步,并能把它用于 Web 视频工程判断。
一、学习目标
学完本周后,你应当能够:
- 解释自信息、信息熵、条件熵分别在衡量什么。
- 手算一个 2~4 个符号的离散信源熵。
- 解释为什么“概率集中”比“符号种类少”更接近压缩本质。
- 用具体像素序列计算预测值和残差,并判断预测器是否有效。
- 解释 DCT 为什么有利于压缩,同时明确 DCT 本身并不直接压缩数据。
- 看懂一维与二维 DCT 公式,解释 DC、AC、低频与高频。
- 手算一个小型量化与反量化例子,并指出误差从哪里产生。
- 使用 (J=D+\lambda R) 比较多个编码候选方案。
- 计算 MSE 与 PSNR,并解释为什么 PSNR 高不一定主观质量更好。
- 说明 SSIM 与 VMAF 相比逐像素误差指标增加了什么信息。
- 区分“标准规定的解码过程”与“编码器实现使用的 RDO 策略”。
- 把这些数学概念映射到 AI 视频网页和在线 Web 视频编辑器中的码率、质量、代理视频、预览与导出决策。
二、概念地图
2.1 这些数学位于系统的哪一层
原始媒体层
RGB / YCbCr / YUV420P / VideoFrame
│
▼
编码工具层
预测、残差、变换、量化、熵编码、率失真优化
│
▼
编码标准层
JPEG、H.264/AVC、H.265/HEVC 等规定可解码的语法与重建过程
│
▼
编码器实现层
libjpeg、x264、x265、硬件编码器如何搜索模式、估算码率和控制复杂度
│
▼
基础码流层
JPEG scan、H.264/H.265 NALU
│
▼
容器层
MP4、MOV、WebM:轨道、样本、时间戳、索引
│
▼
浏览器 API 层
HTMLVideoElement、MSE、WebCodecs、Canvas、WebGPU
2.2 必须严格区分的边界
| 对象 | 它是什么 | 它不是什么 |
|---|---|---|
| 信息熵 | 对概率分布不确定性的度量 | 某个文件必然能达到的压缩率 |
| 预测 | 利用邻域或参考帧估计当前数据 | 有损操作;预测本身可以完全可逆 |
| DCT | 坐标变换与能量重分布工具 | 文件格式或熵编码器 |
| 量化 | 降低系数精度的映射 | 单纯的数据类型转换 |
| Huffman/算术编码 | 无损地编码符号序列 | 主要画质损失来源 |
| RDO | 编码器搜索方案的代价函数 | H.264/H.265 解码器必须执行的步骤 |
| MSE/PSNR/SSIM/VMAF | 质量评价指标 | 对所有内容都一致可靠的“绝对质量真值” |
| MP4 | 容器 | DCT、预测或视频编码算法 |
关键结论:
- 标准通常规定码流语法和解码后应如何重建。
- 编码器如何搜索运动矢量、如何设置 (\lambda)、搜索多少候选模式,通常属于实现策略。
- WebCodecs 提供编码和解码接口,但通常不会把宏块模式、量化系数、CABAC 上下文或内部 RDO 搜索直接暴露给 JavaScript。
三、直觉解释
3.1 压缩的本质:不要重复描述已经能猜到的信息
假设一行灰度像素为:
100, 101, 101, 102, 103, 103, 104
直接记录每个 8-bit 像素,需要 7 个字节。若使用“左边像素预测当前像素”,除第一个像素外,只需记录:
+1, 0, +1, +1, 0, +1
原始值分布在 100~104,残差则高度集中在 0 和 1。后者通常更容易被熵编码器赋予短码。
预测没有凭空删除信息。解码时执行:
当前像素 = 预测值 + 残差
即可恢复。
3.2 DCT 的直觉:换一套“描述图案”的坐标
逐像素坐标在回答:
每个位置分别是多少?
DCT 坐标在回答:
这个块由多少“平均亮度”、多少“缓慢渐变”、多少“快速交替纹理”组成?
平滑图像块通常可以用少量低频成分描述;高频成分较小。变换后,量化器就可以:
- 对低频保留较高精度;
- 对高频使用更粗的步长;
- 把许多小系数量化为 0。
需要注意:自然图像能量集中在低频是统计倾向,不是对任意图像的数学保证。 棋盘格、细文字和噪声都可能包含大量高频能量。
3.3 量化的直觉:把刻度尺变粗
原系数是 23,量化步长是 8:
23 / 8 = 2.875 → 四舍五入为 3
解码端反量化:
3 × 8 = 24
误差为 1。这个误差通常无法由解码器恢复,因此量化是有损压缩中的主要损失来源。
3.4 RDO 的直觉:同时看质量和价格
候选方案 A 误差小,但需要 500 bit;候选方案 B 误差稍大,只需 100 bit。编码器不能只比较误差,也不能只比较码率,而要计算:
[ J=D+\lambda R ]
其中 (D) 是失真,(R) 是码率,(\lambda) 决定当前更偏向质量还是体积。
3.5 质量指标的直觉
- MSE:平均每个像素“错了多少平方”。
- PSNR:把 MSE 转成对数分贝尺度,数值越高通常误差越小。
- SSIM:比较局部亮度、对比度和结构。
- VMAF:融合多个感知特征,并使用主观数据训练的模型预测视频观看质量。
它们都是工具,不是最终裁判。视频卡顿、音画不同步、AI 人物形变、时序闪烁等问题,不能仅靠单帧 PSNR 判断。
四、数学原理
4.1 自信息:一个结果有多“意外”
事件 (x) 的自信息定义为:
[ I(x)=-\log_2 p(x) ]
其中:
- (p(x)):事件 (x) 的概率;
- (I(x)):观察到该事件时获得的信息量,单位为 bit;
- 以 2 为底,是因为二进制系统中信息量通常用 bit 表示。
例子:
| 事件概率 | 自信息 |
|---|---|
| (1) | (0) bit |
| (1/2) | (1) bit |
| (1/4) | (2) bit |
| (1/8) | (3) bit |
必然发生的事件不意外,因此不提供新的选择信息;低概率事件更意外,自信息更大。
4.2 信息熵:平均每个符号有多少不确定性
离散随机变量 (X) 的熵:
[ H(X)=-\sum_i p_i\log_2 p_i ]
其中:
- (p_i):第 (i) 个符号出现的概率;
- (H(X)):平均信息量,单位为 bit/symbol。
手算例子
符号分布:
| 符号 | 概率 |
|---|---|
| A | (1/2) |
| B | (1/4) |
| C | (1/8) |
| D | (1/8) |
则:
[ \begin{aligned} H(X) &=-\left( \frac12\log_2\frac12+ \frac14\log_2\frac14+ \frac18\log_2\frac18+ \frac18\log_2\frac18 \right)\ &=\frac12\times1+\frac14\times2+\frac18\times3+\frac18\times3\ &=1.75\text{ bit/symbol} \end{aligned} ]
若使用固定长度编码,4 个符号需要 2 bit/symbol。一个可行的前缀码为:
A → 0
B → 10
C → 110
D → 111
平均码长:
[ L=\frac12\times1+\frac14\times2+\frac18\times3+\frac18\times3=1.75 ]
此例恰好达到熵。实际场景中,符号概率、上下文和有限块长都会影响结果。
两个极端
- 公平二元符号:(p(0)=p(1)=0.5)
[ H=1\text{ bit/symbol} ]
- 极不平衡二元符号:(p(0)=0.99, p(1)=0.01)
[ H\approx0.0808\text{ bit/symbol} ]
第二种分布更集中,理论上更容易压缩。
重要边界
- 熵是概率模型的属性,不是单个文件天然携带的固定标签。
- 对一个文件统计频率得到的是经验熵估计。
- 理论下界通常讨论长序列与已知分布;实际码流还包含表、头部、对齐和错误恢复信息。
- “无损压缩”不保证每个输入都变小。对接近随机的数据,压缩后可能更大。
4.3 条件熵:已知邻居后还剩多少不确定性
条件熵:
[ H(X\mid Y)=-\sum_{x,y}p(x,y)\log_2 p(x\mid y) ]
直觉:已知 (Y) 后,预测 (X) 还剩多少不确定性。
图像中,(Y) 可以是左、上、左上像素;视频中,(Y) 可以是参考帧中的匹配块。若邻域相关性强,通常有:
[ H(X\mid Y)<H(X) ]
预测编码正是在利用这种条件关系。一个好的预测器让残差分布集中;一个坏的预测器可能让残差更分散,反而增加码率。
4.4 Huffman 编码与算术编码
Huffman 编码
Huffman 编码为高概率符号分配短码,为低概率符号分配长码,并满足前缀码性质:任何码字都不是另一个码字的前缀。
优点:
- 解码结构清晰;
- 表驱动实现高效;
- 对静态、有限符号表很实用。
限制:
- 每个符号的码长必须是整数 bit;
- 模型粒度不足时,可能离熵极限较远;
- 需要传输或约定码表。
算术编码
算术编码把整段符号序列映射到 ([0,1)) 的一个子区间。它不是给每个符号单独分配整数长度码字,因此长序列的平均码长可以更接近理论熵。
需要准确表述:
“平均每个符号可以是 1.37 bit”并不表示码流中存在 0.37 个物理 bit,而是整段码流长度除以符号数后的平均值。
H.264 的 CABAC(Context-Adaptive Binary Arithmetic Coding,上下文自适应二进制算术编码)进一步使用上下文模型估计二元符号概率。具体上下文状态和二值化规则属于后续 H.264 课程内容。
4.5 预测与残差
预测残差:
[ r=x-\hat{x} ]
其中:
- (x):真实样本或块;
- (\hat{x}):预测样本或块;
- (r):残差。
解码重建:
[ \tilde{x}=\hat{x}+\tilde{r} ]
若残差未量化,则 (\tilde{r}=r),可以无损重建;若残差经过量化,则 (\tilde{r}\neq r),产生失真。
一维例子
原始像素:
100, 102, 101, 103, 104
使用左值预测,首样本单独保存:
预测值: -, 100, 102, 101, 103
残差: 100, 2, -1, 2, 1
除首样本外,残差集中在 (-1)~2。熵编码器通常更容易处理这种集中分布。
图像常见预测思路
- 左像素;
- 上像素;
- 左上像素;
- 左与上的平均;
- 根据边缘方向选择角度预测。
视频常见预测思路
- 帧内预测:从同一帧已重建邻域预测;
- 帧间预测:从参考帧中按运动矢量取预测块;
- 双向预测:组合两个参考方向的预测结果。
重要:编码器和解码器必须使用相同的已重建参考样本。编码器若用原始参考、解码器用量化后的参考,会产生预测漂移。
4.6 DCT:离散余弦变换
一维正交归一 DCT-II
长度为 (N) 的序列 (x_n) 的 DCT:
[ X_k=\alpha(k)\sum_{n=0}^{N-1}x_n \cos\left[\frac{\pi}{N}\left(n+\frac12\right)k\right] ]
其中:
[ \alpha(k)= \begin{cases} \sqrt{\frac1N}, & k=0\ \sqrt{\frac2N}, & k>0 \end{cases} ]
- (k=0):DC 分量,反映平均水平;
- (k>0):AC 分量,反映不同变化速度;
- (k) 越大,基函数振荡越快,通常被称为更高频。
四点手算结果
给定:
x = [52, 55, 61, 66]
按上述正交归一 DCT 计算,约得到:
X ≈ [117.000, -10.770, 1.000, 0.131]
能量主要集中在 DC 和第一个低频 AC 分量,最高频分量很小。这就是“能量集中”的一个小例子。
二维 DCT
对 (N\times N) 块:
[ F(u,v)=\alpha(u)\alpha(v) \sum_{x=0}^{N-1}\sum_{y=0}^{N-1} f(x,y) \cos\left[\frac{\pi}{N}\left(x+\frac12\right)u\right] \cos\left[\frac{\pi}{N}\left(y+\frac12\right)v\right] ]
二维 DCT 可分离执行:
先对每一行做 1D DCT
再对每一列做 1D DCT
这比直接计算每个二维基函数更高效。
JPEG 与视频编码中的注意事项
- 常见 Baseline JPEG 使用 8×8 DCT、量化与 Huffman 编码。
- JPEG 对 8-bit 样本通常先做 level shift,即减去 128,使数据以 0 为中心。
- H.264 的规范变换是整数变换,设计上具有 DCT 类似性质,但不能简单说成“直接执行浮点 8×8 DCT”。
- 变换在理想精度下只是坐标变化;主要不可逆误差来自量化和有限精度处理。
4.7 量化
最简单的标量量化:
[ q_k=\operatorname{round}\left(\frac{X_k}{Q_k}\right) ]
反量化:
[ \tilde{X}_k=q_kQ_k ]
其中:
- (X_k):原变换系数;
- (Q_k):量化步长;
- (q_k):需要编码的量化系数;
- (\tilde{X}_k):解码端反量化后的近似系数。
延续四点 DCT 例子
原系数:
X ≈ [117.000, -10.770, 1.000, 0.131]
量化步长:
Q = [8, 4, 2, 2]
量化:
q = round(X / Q) = [15, -3, 0, 0]
反量化:
X̃ = q × Q = [120, -12, 0, 0]
逆 DCT 后约得到:
x̃ ≈ [52.161, 56.753, 63.247, 67.839]
原序列为:
x = [52, 55, 61, 66]
量化使两个高频系数直接变成 0,符号更容易压缩,但产生了不可恢复误差。
量化步长的影响
- 步长小:误差小、非零系数多、码率高;
- 步长大:误差大、零系数多、码率低;
- 对不同频率使用不同步长,可以利用人眼敏感度差异;
- 量化矩阵、QP 到步长的映射及心理视觉调优,可能由标准语法和编码器策略共同决定,不能混为一谈。
4.8 率失真优化
编码器选择候选模式时常使用拉格朗日形式:
[ J=D+\lambda R ]
其中:
- (J):总代价,越小越好;
- (D):失真,例如 SSE、SATD 近似或其他代价;
- (R):估计或实际需要的比特数;
- (\lambda):码率与失真的权衡系数。
手算例子
| 候选 | 失真 (D) | 码率 (R) |
|---|---|---|
| A | 120 | 20 |
| B | 80 | 50 |
| C | 60 | 100 |
当 (\lambda=1):
| 候选 | (J=D+R) |
|---|---|
| A | 140 |
| B | 130 |
| C | 160 |
选择 B。
当 (\lambda=3):
| 候选 | (J=D+3R) |
|---|---|
| A | 180 |
| B | 230 |
| C | 360 |
选择 A。更大的 (\lambda) 使编码器更重视节省比特。
标准与实现的边界
- 标准要求生成的码流可被符合规范的解码器正确解析。
- 标准通常不强制编码器必须采用某个 RDO 搜索算法。
- (\lambda) 与 QP 的具体关系、是否使用 SATD 预筛选、搜索深度和提前终止,通常属于编码器实现策略。
4.9 MSE 与 PSNR
MSE
[ MSE=\frac1N\sum_{i=1}^{N}(x_i-y_i)^2 ]
- (x_i):参考样本;
- (y_i):失真样本;
- (N):样本数量。
例子:
x = [100, 102, 104, 106]
y = [101, 101, 105, 105]
误差 = [-1, 1, -1, 1]
平方误差 = [1, 1, 1, 1]
MSE = 1
PSNR
[ PSNR=10\log_{10}\frac{MAX^2}{MSE} ]
对 8-bit 样本,通常 (MAX=255)。当 (MSE=1):
[ PSNR=10\log_{10}(255^2)\approx48.13\text{ dB} ]
注意:
- MSE 为 0 时,PSNR 趋于无穷大;工具可能显示
inf。 - 比较 8-bit 与 10-bit 内容时,必须使用正确的 (MAX)。
- 必须明确比较的是 RGB、Y、YCbCr 各平面,还是经过缩放与色彩转换后的结果。
- 两个视频若未对齐帧、分辨率、裁剪区域和时间戳,PSNR 没有可比性。
4.10 SSIM
结构相似性(Structural Similarity Index,SSIM)把局部比较拆为亮度、对比度和结构。常用简化形式:
[ SSIM(x,y)= \frac{(2\mu_x\mu_y+C_1)(2\sigma_{xy}+C_2)} {(\mu_x^2+\mu_y^2+C_1)(\sigma_x^2+\sigma_y^2+C_2)} ]
其中:
- (\mu_x,\mu_y):局部均值;
- (\sigma_x^2,\sigma_y^2):局部方差;
- (\sigma_{xy}):协方差;
- (C_1,C_2):避免分母接近 0 的稳定项。
SSIM 的优势是能反映结构变化,而不仅是逐像素误差;但它仍然依赖窗口、尺度、颜色通道、实现方式与聚合方法。
4.11 VMAF
VMAF(Video Multi-Method Assessment Fusion,视频多方法评估融合)是面向感知的视频质量指标,融合多个特征,并通过主观质量数据训练模型。
工程上必须记录:
- 使用的 VMAF 模型与版本;
- 输入分辨率、像素格式、色彩范围和缩放方式;
- 帧率与帧对齐;
- 池化策略;
- 是否发生裁剪、去噪、锐化或色彩变换。
VMAF 是全参考指标,通常需要“参考视频”和“待测视频”。它不应被解释为对所有观看设备、内容类型和失真类型都绝对准确。
五、底层数据结构
本周数学最终要落到整数、符号和 bit 上。
5.1 数据从像素到 bit 的形态变化
原始样本
[52, 55, 61, 66]
变换系数(通常为有符号数)
[117.000, -10.770, 1.000, 0.131]
量化系数(整数)
[15, -3, 0, 0]
扫描后的符号序列
DC=15, AC=-3, EOB
熵编码后的 bit 串
1010... ← 这里只是概念示意,不代表任何标准的真实语法
必须区分:
- 变换系数是数学域中的值;
- 量化系数是需要编码的离散整数;
run/level、类别、上下文二元符号是熵编码前的语法表示;- bit 串才是基础码流的一部分;
- NALU、JPEG marker、MP4 box 又属于更高层结构。
5.2 为什么会出现大量 0
变换后的高频系数通常较小;量化后:
[80, 17, -6, 3, 1, 0, -1, 0]
↓ 量化
[10, 2, -1, 0, 0, 0, 0, 0]
扫描后会形成长零串,因此可以使用:
- 游程长度;
- 非零系数幅值;
- 块结束符号;
- 上下文概率模型。
实际 JPEG、H.264、H.265 的语法不同。本节只解释共同数学结构,不把概念示意当成标准字段。
5.3 概率表与上下文
静态模型示意:
| 符号 | 计数 | 概率估计 |
|---|---|---|
| 0 | 700 | 0.70 |
| 1 | 160 | 0.16 |
| -1 | 100 | 0.10 |
| 其他 | 40 | 0.04 |
上下文模型则会进一步问:
- 左边系数是否为 0?
- 当前位于低频还是高频?
- 邻块是否有非零残差?
- 当前语法元素此前更常出现 0 还是 1?
同一个二元值在不同上下文中可能有不同概率。
5.4 有限精度
真实编解码器通常使用整数或定点运算,原因包括:
- 保证跨平台重建一致性;
- 避免浮点舍入差异;
- 利于 SIMD 与硬件实现;
- 降低功耗与复杂度。
因此教材中的浮点 DCT 是理解工具,标准中的整数变换才决定规范解码结果。
六、编码器流程
6.1 通用流程
输入原始块 x
↓
枚举预测模式 / 运动候选
↓
生成预测块 x̂
↓
残差 r = x - x̂
↓
变换 T(r)
↓
量化 Q(T(r))
↓
估算语法与系数比特数 R
↓
反量化 + 反变换 + 预测,得到编码器内部重建块 x̃
↓
计算失真 D(x, x̃)
↓
计算 J = D + λR
↓
选择代价最低的候选
↓
熵编码并输出码流
6.2 为什么编码器内部必须重建
编码器不能只保存原始参考帧。解码器看到的是量化后重建结果;未来帧必须基于相同参考,否则:
编码器预测参考 ≠ 解码器预测参考
↓
误差逐帧积累
↓
预测漂移
因此视频编码器通常包含一个“本地解码环路”。
6.3 RDO 伪代码
best = null
for mode in candidateModes:
prediction = predict(reconstructedNeighbors, mode)
residual = original - prediction
coeff = transform(residual)
qcoeff = quantize(coeff, qp)
estimatedBits = estimateSyntaxBits(mode, qcoeff, contexts)
reconstructedResidual = inverseTransform(
dequantize(qcoeff, qp)
)
reconstructed = clip(prediction + reconstructedResidual)
distortion = sse(original, reconstructed)
cost = distortion + lambda * estimatedBits
if best is null or cost < best.cost:
best = { mode, qcoeff, reconstructed, cost }
entropyEncode(best.mode, best.qcoeff)
storeAsReference(best.reconstructed)
实际编码器会使用多级搜索:先用 SAD/SATD 等较便宜指标筛选,再对少量候选做更精确的 RDO,以控制复杂度。
6.4 编码器的主要复杂度来源
并不是一次 DCT 或一次熵编码最耗时,而是:
候选块划分 × 预测模式 × 运动矢量 × 参考帧 × 变换选择 × 量化参数
搜索空间会迅速膨胀。编码 preset 的本质之一,就是在“搜索深度”和“速度”之间做取舍。
七、解码器流程
解码器通常不做全局模式搜索,只需按码流语法执行确定性重建:
读取码流
↓
熵解码,恢复模式、运动信息与量化系数
↓
反量化
↓
反变换
↓
按码流指定模式生成预测
↓
预测 + 重建残差
↓
裁剪到合法样本范围
↓
环路处理(若当前标准规定)
↓
输出帧并保存参考帧
7.1 哪些步骤有损
| 步骤 | 理论上是否有损 | 说明 |
|---|---|---|
| 预测 | 否 | 只要残差完整保留即可恢复 |
| 变换 | 理想精度下否 | 整数近似与舍入需按标准分析 |
| 量化 | 是 | 多个原值映射到同一量化级 |
| 熵编码 | 否 | 必须精确恢复符号序列 |
| 容器封装 | 否 | 不应改变编码样本内容 |
7.2 编码器比解码器复杂的原因
- 编码器要“选择”;解码器只需“执行”。
- 编码器要尝试多个模式并比较 (J)。
- 编码器可能多次变换、量化、反变换同一个块。
- 解码器通常只处理最终选中的一个路径。
这也是很多实时系统中“编码比解码更难、更耗电”的根本原因之一。
八、复杂度与性能
8.1 算法复杂度直觉
| 操作 | 典型复杂度直觉 | 主要瓶颈 |
|---|---|---|
| 统计直方图 | (O(N)) | 内存带宽、并行归并 |
| 一阶预测 | (O(N)) | 依赖方向与缓存局部性 |
| 直接 1D DCT | (O(N^2)) | 乘加次数 |
| 可分离二维 DCT | 对 (N\times N) 块约 (O(N^3)) | 行列两遍变换 |
| 快速 DCT | 可进一步减少乘加 | 固定尺寸优化、SIMD |
| 量化 | (O(N)) | 整数乘除、舍入 |
| 熵编码 | 近似 (O(N)) | 数据依赖、分支、上下文更新 |
| RDO 搜索 | 候选数 × 单候选成本 | 通常是编码器核心瓶颈 |
8.2 CPU、GPU 与专用硬件
适合并行的部分
- 多块变换;
- 像素差、SAD、SSE;
- 缩放、颜色转换;
- 独立帧或 tile 级处理;
- 大规模质量指标计算。
不容易完全并行的部分
- 强上下文依赖的熵编码;
- 依赖已重建邻块的帧内预测;
- 某些严格顺序的码率控制状态更新;
- 过细粒度任务的 GPU 调度与数据搬运。
专用硬件
硬件编解码器通常在功耗和吞吐上优势明显,但:
- 暴露的参数可能少于软件编码器;
- 具体模式搜索和质量策略不可见;
- 不同设备的质量、延迟与支持能力不同;
- Web 环境必须进行运行时能力探测,不能仅凭 codec 字符串假设支持。
8.3 内存
质量评价常需同时保留参考帧和失真帧。以 1920×1080 8-bit YUV420P 为例,一帧约 3.11 MB;若积压 60 对帧:
[ 3.11\times2\times60\approx373.2\text{ MB} ]
还未计入 stride、对齐、GPU 纹理与中间缓冲。因此浏览器中应采用:
- 流式逐帧比较;
- 有界队列;
- 及时
close()不再使用的VideoFrame; - Worker 分离计算;
- backpressure,避免解码速度远高于比较与渲染速度。
九、实际场景
A. AI 生成视频网页
| 问题 | 本周数学的作用 | 工程行动 |
|---|---|---|
| 多个生成结果如何筛选 | SSIM/VMAF 只能评估部分视觉质量 | 结合时序一致性、语义正确性和人工抽检 |
| 边生成边预览 | 码率与复杂度需要平衡 | 低延迟配置、受控 GOP、分片输出、backpressure |
| 生成结果过大 | 量化与 RDO 决定质量/体积 | 选择合理质量目标,不盲目固定高码率 |
| 人脸或文字被破坏 | 高频量化过强可能放大细节损失 | 局部 ROI、较高质量代理、主观检查 |
| 视频闪烁 | 单帧 PSNR 可能检测不到 | 增加时序差分、光流一致性或专门时序指标 |
| 不同模型输出比较 | 指标预处理必须一致 | 固定分辨率、色彩、帧率、对齐和评估模型版本 |
关键判断:AI 视频中的错误可能来自生成模型、后处理、帧插值、颜色管理或编码器。不能看到块效应以外的异常就一概归因于视频编码。
B. 在线 Web 视频编辑器
| 问题 | 本周数学的作用 | 工程行动 |
|---|---|---|
| 代理视频质量 | 用率失真思维平衡流畅和可辨识度 | 低分辨率、较低码率、易 Seek 的 GOP |
| 时间线缩略图 | 不需要原始质量 | 尺寸优先,适度 JPEG/AVIF/WebP 质量设置 |
| 帧精确剪切预览 | 解码负载与内存受限 | 从关键帧向前解码,按需保留少量帧 |
| 滤镜前后质量回归 | MSE/SSIM 可检测确定性回归 | 统一颜色域并对齐帧后再比较 |
| 导出参数 | RDO 决定最终体积/质量效率 | 质量目标、preset、码率上限与编码时间联合设置 |
| 多层合成 | 多次缩放与颜色转换会引入误差 | 尽量减少往返转换,统一中间工作色彩空间 |
| 浏览器内编码 | API 不暴露底层全部模式 | 使用 VideoEncoder 可配置项,复杂导出交给服务端 |
系统设计结论:
预览链路追求低延迟、稳定帧率和低内存;最终导出链路追求更好的率失真效率。两条链路不应使用完全相同的编码策略。
十、Web 实现视角
10.1 WebCodecs 能做什么
WebCodecs 可让 JavaScript 直接处理:
VideoFrame:原始视频帧;EncodedVideoChunk:编码视频块;VideoDecoder/VideoEncoder:解码与编码接口。
但它通常不会直接暴露:
- DCT 系数;
- 量化矩阵或每块 QP;
- 运动搜索候选;
- CABAC 上下文;
- 编码器内部 (\lambda) 与完整 RDO 过程。
若需要教学实验或自定义算法,可使用 JavaScript、WebAssembly、Worker、Canvas、WebGL/WebGPU 对原始帧自行计算;但这不等于实现了完整标准编码器。
10.2 能力探测
不要假设某个浏览器或设备一定支持某个编码配置。应在运行时调用:
const result = await VideoEncoder.isConfigSupported({
codec: "avc1.42001E",
width: 1280,
height: 720,
bitrate: 2_000_000,
framerate: 30,
});
if (!result.supported) {
// 切换编码格式、降低配置,或交给服务端处理
}
H.265/HEVC 等配置同样应探测,不能只根据 UA、扩展名或 codec 字符串下结论。
10.3 VideoFrame 生命周期
逐帧计算质量时:
for await (const frame of decodedFrames) {
try {
await compareFrame(frame);
} finally {
frame.close();
}
}
VideoFrame 可能持有系统或 GPU 媒体资源。等待垃圾回收可能造成队列积压甚至解码停顿。
10.4 TypeScript:经验熵
export function empiricalEntropy(values: readonly number[]): number {
if (values.length === 0) return 0;
const counts = new Map<number, number>();
for (const value of values) {
counts.set(value, (counts.get(value) ?? 0) + 1);
}
let entropy = 0;
for (const count of counts.values()) {
const p = count / values.length;
entropy -= p * Math.log2(p);
}
return entropy;
}
可分别对原始像素和预测残差调用,观察好的预测器是否降低经验熵。
10.5 TypeScript:MSE 与 PSNR
export function mse(
reference: Uint8Array,
distorted: Uint8Array,
): number {
if (reference.length !== distorted.length) {
throw new Error("Input lengths must match");
}
if (reference.length === 0) return 0;
let sum = 0;
for (let i = 0; i < reference.length; i += 1) {
const diff = reference[i] - distorted[i];
sum += diff * diff;
}
return sum / reference.length;
}
export function psnrFromMse(error: number, maxValue = 255): number {
if (error < 0) throw new Error("MSE cannot be negative");
if (error === 0) return Number.POSITIVE_INFINITY;
return 10 * Math.log10((maxValue * maxValue) / error);
}
这段代码只适合已经对齐、同尺寸、同颜色域的样本数组。
10.6 FFmpeg 辅助验证
计算 PSNR:
ffmpeg -i distorted.mp4 -i reference.mp4 \
-lavfi "[0:v][1:v]psnr=stats_file=psnr.log" \
-f null -
计算 SSIM:
ffmpeg -i distorted.mp4 -i reference.mp4 \
-lavfi "[0:v][1:v]ssim=stats_file=ssim.log" \
-f null -
若 FFmpeg 构建包含 libvmaf,可计算 VMAF:
ffmpeg -i distorted.mp4 -i reference.mp4 \
-lavfi "[0:v][1:v]libvmaf=log_fmt=json:log_path=vmaf.json" \
-f null -
实际比较前应先统一:
- 分辨率;
- SAR/DAR;
- 帧率和帧数量;
- 时间对齐;
- 像素格式;
- 色彩 primaries、transfer、matrix 与 range。
十一、易错点
- 熵低不等于文件一定小。 文件还有头部、索引、码表、对齐与封装开销。
- 符号种类少不等于熵一定低。 概率是否集中更关键。
- 单个符号的自信息不等于整个信源的熵。 熵是自信息的期望。
- 预测不一定降低熵。 错误预测器会使残差更分散。
- 残差小不等于码率必然低。 还取决于分布、扫描方式、语法和上下文。
- DCT 不会自动删除信息。 真正使多个值合并的是量化。
- 低频不等于视觉上永远更重要。 文字、边缘、纹理依赖高频。
- JPEG 的 8×8 DCT 不能直接套到所有视频标准。 H.264/H.265 使用规范化整数变换和多种块结构。
- 量化参数不是“质量百分比”。 不同编码器的质量参数含义和映射可能不同。
- RDO 不是解码器流程。 它是编码器寻找可选码流的一种常见实现方法。
- (\lambda) 大不是质量更高。 在 (J=D+\lambda R) 中,通常表示更重视码率。
- PSNR 高不等于主观质量一定好。 相同 MSE 可对应不同视觉结构损伤。
- 不同分辨率、位深和颜色域的 PSNR 不可直接比较。 必须统一评估条件。
- SSIM 不是只看边缘。 它结合局部亮度、对比度和结构。
- VMAF 分数不是跨模型永久可比的绝对刻度。 必须记录模型、版本和预处理。
- 逐帧指标难以覆盖时序闪烁。 视频还需时间维度评价。
- WebCodecs 编码输出不等于完整 MP4。 还需要时间戳组织和 mux。
- Canvas 截图后再比较可能混入颜色转换误差。 应明确比较域。
- 算术编码的“1.3 bit/符号”是长序列平均值。 不是单个符号占用小数个物理 bit。
- 标准与编码器实现不能混说。 标准规定可互操作语法;搜索算法、preset 和心理视觉调优多为实现策略。
十二、面试题
12.1 基础题 10 道
1. 什么是信息熵?
- 标准回答: 熵是离散信源平均不确定性的度量,(H(X)=-\sum p_i\log_2p_i),单位为 bit/symbol。概率越均匀,熵通常越高;概率越集中,越容易使用短平均码长表示。
- 追问: 熵为 0 的条件是什么?
- 常见错误: “熵就是文件大小。”
- 高级回答: 区分理论分布熵、经验熵、条件熵和实际码流开销。
2. 为什么高概率符号使用短码?
- 标准回答: 这样可最小化期望码长;平均码长按概率加权。
- 追问: 为什么码字必须满足前缀条件?
- 常见错误: 只说“节省空间”,不解释平均码长。
- 高级回答: 说明 Huffman 的整数码长限制,以及算术编码如何更接近熵。
3. 什么是预测残差?
- 标准回答: (r=x-\hat{x}),表示真实值与预测值之差;解码通过预测值与重建残差相加恢复样本。
- 追问: 预测器不好会怎样?
- 常见错误: 把残差说成“被删除的像素”。
- 高级回答: 说明残差是否易压缩取决于条件分布与上下文模型。
4. DCT 为什么适合自然图像?
- 标准回答: 自然图像局部相关性强,DCT 往往把能量集中到少数低频系数,便于后续量化和零游程编码。
- 追问: 是否所有图像都集中在低频?
- 常见错误: “DCT 直接压缩掉高频。”
- 高级回答: 强调统计能量聚集、可分离性和近似 KLT 的工程价值。
5. DC 与 AC 系数是什么?
- 标准回答: DC 表示块的平均或零频成分;AC 表示不同空间变化频率的成分。
- 追问: 平坦块的 AC 会怎样?
- 常见错误: 把 DC 解释为亮度通道、AC 解释为色度通道。
- 高级回答: 结合基函数与正交投影解释。
6. 什么是量化?
- 标准回答: 将连续或高精度系数映射到有限离散级别,常见形式为除以步长并取整;反量化只能得到近似值。
- 追问: 为什么量化能降低码率?
- 常见错误: “量化就是把 float 转 int。”
- 高级回答: 说明零区、频率加权、QP 与步长映射。
7. 什么是率失真优化?
- 标准回答: 使用 (J=D+\lambda R) 联合考虑重建失真与码率,选择总代价最小的编码方案。
- 追问: (\lambda) 增大意味着什么?
- 常见错误: 只选择 MSE 最小的模式。
- 高级回答: 说明实际编码器会用多级估算和提前终止降低搜索成本。
8. MSE 与 PSNR 的关系是什么?
- 标准回答: PSNR 是 MSE 的对数变换;在相同位深下,MSE 越小,PSNR 越高。
- 追问: MSE 为 0 时怎样?
- 常见错误: “PSNR 是百分比。”
- 高级回答: 指出评估域、位深与帧对齐前提。
9. SSIM 与 PSNR 有什么区别?
- 标准回答: PSNR 基于逐样本平方误差;SSIM 比较局部亮度、对比度和结构,更接近部分感知特征。
- 追问: SSIM 是否一定优于 PSNR?
- 常见错误: “SSIM 完全等同人眼。”
- 高级回答: 说明窗口、尺度、通道和失真类型会影响结果。
10. VMAF 是什么?
- 标准回答: VMAF 是融合多种特征并基于主观数据训练的全参考视频质量指标。
- 追问: 不同 VMAF 模型的分数能否直接比较?
- 常见错误: “VMAF 是视频编码器。”
- 高级回答: 强调模型版本、缩放、帧对齐、池化和内容域。
12.2 底层实现题 10 道
1. 为什么视频编码器内部也需要解码环路?
- 标准回答: 未来预测必须使用与解码器一致的重建参考,否则会产生漂移。
- 追问: 漂移如何累积?
- 常见错误: “只是为了预览编码结果。”
- 高级回答: 结合参考图像缓冲和闭环预测解释。
2. 为什么熵编码通常放在量化之后?
- 标准回答: 量化使系数离散并产生大量 0,形成更集中的符号分布,适合无损熵编码。
- 追问: 熵编码能否恢复量化前系数?
- 常见错误: 把熵编码说成有损。
- 高级回答: 说明语法二值化、扫描与上下文建模位于两者之间。
3. 为什么二维 DCT 可以分两次一维 DCT?
- 标准回答: 二维 DCT 基函数可分离为横向和纵向基函数乘积。
- 追问: 对复杂度有什么影响?
- 常见错误: “因为图片有宽高两个属性。”
- 高级回答: 用矩阵形式 (F=CfC^T) 解释。
4. 整数变换为什么重要?
- 标准回答: 可保证跨平台重建一致性,便于定点、SIMD 和硬件实现。
- 追问: 整数变换是否一定无误差?
- 常见错误: “整数运算完全没有舍入。”
- 高级回答: 区分规范逆变换、缩放因子合并与有限位宽。
5. 为什么编码器常先用 SAD/SATD,再做完整 RDO?
- 标准回答: 完整 RDO 需要熵比特估计和重建,成本高;便宜指标可先淘汰明显较差候选。
- 追问: 这样会不会错过最优解?
- 常见错误: “SAD 就等于最终失真。”
- 高级回答: 说明速度 preset 本质是搜索精度与计算预算折中。
6. 为什么熵编码难以像像素滤镜一样大规模并行?
- 标准回答: 上下文状态和输出位置常依赖前序符号,存在串行数据依赖。
- 追问: 标准如何提高并行性?
- 常见错误: “熵编码不能并行。”
- 高级回答: 可提 slice、tile、WPP 或独立熵子流,但指出存在压缩效率和边界成本。
7. 为什么量化后容易出现长零串?
- 标准回答: 高频小系数除以量化步长后被舍入为 0;扫描顺序通常使非零低频在前、零高频在后。
- 追问: 扫描顺序为什么重要?
- 常见错误: “DCT 输出天然都是 0。”
- 高级回答: 结合方向扫描和预测模式讨论。
8. 编码器估算 (R) 时必须真的写出完整码流吗?
- 标准回答: 不一定;可用概率上下文、查表或快速模型估算,最终候选再精确编码。
- 追问: 估算不准有什么影响?
- 常见错误: “R 就是像素数量。”
- 高级回答: 说明 CABAC 上下文状态复制与 rollback 的实现成本。
9. 变换跳过有什么意义?
- 标准回答: 对某些小块、屏幕内容或特殊残差,直接量化残差可能比变换更合适。
- 追问: 是否所有标准都支持同样的 transform skip?
- 常见错误: “跳过变换就没有压缩。”
- 高级回答: 强调具体语法依标准和 profile 而异。
10. 为什么解码器一般比编码器快?
- 标准回答: 解码器执行码流指定路径;编码器需要搜索大量候选并反复估算、重建和比较。
- 追问: 是否存在解码仍很重的场景?
- 常见错误: “解码完全没有计算。”
- 高级回答: 讨论高分辨率、复杂参考结构、后处理、软件解码与内存带宽。
12.3 数学题 10 道
1. 公平硬币的熵是多少?
- 标准回答: (-2\times0.5\log_2 0.5=1) bit。
- 追问: 硬币总是正面时呢?
- 常见错误: 回答 2 bit,因为有两个结果。
- 高级回答: 解释最大熵发生在均匀分布。
2. 概率为 (1/8) 的事件自信息是多少?
- 标准回答: (-\log_2(1/8)=3) bit。
- 追问: 概率减半,自信息增加多少?
- 常见错误: 回答 (1/8) bit。
- 高级回答: 说明对数把概率乘法转成信息加法。
3. 分布 ([0.5,0.25,0.25]) 的熵是多少?
- 标准回答: (0.5\times1+0.25\times2+0.25\times2=1.5) bit/symbol。
- 追问: 固定长度编码需要多少 bit?
- 常见错误: 忘记负号或对数底。
- 高级回答: 比较固定 2 bit 与可变长平均码长。
4. 原序列 [10, 12, 13, 13] 使用左值预测,残差是什么?
- 标准回答: 首值单独保存时为
[10, 2, 1, 0]。 - 追问: 使用固定预测值 0 呢?
- 常见错误: 写成
[0,2,1,0]而未说明首样本处理。 - 高级回答: 比较两个残差分布的经验熵。
5. 系数 21,量化步长 8,量化与反量化结果是什么?
- 标准回答: (q=round(21/8)=3),反量化为 24,误差 3。
- 追问: 不同舍入规则会怎样?
- 常见错误: 把反量化写成 21。
- 高级回答: 讨论负数舍入、dead-zone 和规范实现。
6. 两候选 A:(D=100,R=40),B:(D=130,R=20),(\lambda=1) 选谁?
- 标准回答: A 的 (J=140),B 的 (J=150),选 A。
- 追问: (\lambda=3) 呢?
- 常见错误: 只比较 D。
- 高级回答: 求切换阈值:(100+40\lambda=130+20\lambda),得 (\lambda=1.5)。
7. 8-bit 图像 MSE=4,PSNR 约多少?
- 标准回答: (10\log_{10}(255^2/4)\approx42.11) dB。
- 追问: MSE 减半,PSNR 增加多少?
- 常见错误: 用自然对数且不乘 10。
- 高级回答: MSE 减半增加约 3.01 dB。
8. 为什么平方误差对大误差惩罚更重?
- 标准回答: 误差翻倍后平方项变为四倍。
- 追问: L1 误差有什么不同?
- 常见错误: “因为平方误差更准确。”
- 高级回答: 讨论异常值敏感度与优化性质。
9. 常量序列的 DCT 有什么特点?
- 标准回答: 理想正交 DCT 中只有 DC 非零,AC 为 0。
- 追问: 叠加线性渐变后呢?
- 常见错误: “所有系数都相等。”
- 高级回答: 解释与基函数正交投影的关系。
10. 为什么 (H(X\mid Y)) 可能等于 (H(X))?
- 标准回答: 当 (X) 与 (Y) 独立时,知道 (Y) 不减少对 (X) 的不确定性。
- 追问: 什么时候条件熵为 0?
- 常见错误: “条件熵永远更小且严格小于。”
- 高级回答: 说明确定函数关系与互信息。
12.4 Web 实战题 10 道
1. 如何在浏览器中验证预测是否降低了熵?
- 标准回答: 从
VideoFrame拷贝指定平面,计算原始样本直方图与预测残差直方图,再比较经验熵。 - 追问: 为什么要固定颜色平面?
- 常见错误: 直接比较压缩文件大小而不控制其他变量。
- 高级回答: 使用 Worker、分块统计和有界队列。
2. 为什么不能直接对两个 Canvas 截图计算 PSNR?
- 标准回答: Canvas 可能引入颜色转换、Alpha 合成、缩放和色域截断,导致比较域不一致。
- 追问: 应如何统一?
- 常见错误: 认为所有
ImageData都等价于视频原始 YUV。 - 高级回答: 统一到同一 RGB/线性光或同一 YCbCr 平面后比较。
3. WebCodecs 能否读取 H.264 的 DCT 系数?
- 标准回答: 通常不能;WebCodecs 暴露帧与编码块接口,不提供标准内部系数级调试接口。
- 追问: 如何做教学实验?
- 常见错误: 把
EncodedVideoChunk当成解析后的宏块对象。 - 高级回答: 使用专用码流解析器、WASM 解码器或离线工具。
4. 如何避免浏览器质量评估占用过多内存?
- 标准回答: 逐帧处理、限制队列、及时关闭
VideoFrame、避免同时缓存整段参考与失真视频。 - 追问: backpressure 如何实现?
- 常见错误: 全部 decode 完再比较。
- 高级回答: 按时间戳配对两个流并设置最大未匹配帧数。
5. 如何检测当前编码配置是否可用?
- 标准回答: 调用
VideoEncoder.isConfigSupported()或相应能力 API 做运行时探测。 - 追问: 为什么 UA 判断不可靠?
- 常见错误: 看到
.mp4就认为 H.264/H.265 一定可编码。 - 高级回答: 同时验证编码、解码、容器 mux 和实际首帧结果。
6. 为什么 VideoEncoder 输出还不是 MP4?
- 标准回答: 它输出编码 chunk;MP4 还需要 sample table、时间戳、codec 配置与 box 封装。
- 追问: B 帧会增加什么难点?
- 常见错误: 直接拼接 chunk 字节后保存为
.mp4。 - 高级回答: 说明 DTS/PTS 与 muxer 责任。
7. WebGPU 是否一定适合做 8×8 DCT?
- 标准回答: 不一定;小任务可能被上传、调度和同步开销抵消,需要批量处理和基准测试。
- 追问: 哪些部分更适合 GPU?
- 常见错误: “GPU 永远比 CPU 快。”
- 高级回答: 对比 SIMD/WASM、WebGPU 批处理与硬件编码器。
8. 如何在线比较两个视频的 VMAF?
- 标准回答: 浏览器原生无统一 VMAF API,可用服务端 FFmpeg/libvmaf,或在可接受成本下使用 WASM;必须先对齐视频。
- 追问: 为什么移动端不宜长时间本地计算?
- 常见错误: 把 MSE 函数命名为 VMAF。
- 高级回答: 设计分段采样、服务端队列和报告缓存。
9. 代理视频应追求最高 PSNR 吗?
- 标准回答: 不应;代理视频目标是编辑流畅、快速 Seek 和足够辨识,需综合分辨率、GOP、码率、解码复杂度与内存。
- 追问: 文字类素材怎么办?
- 常见错误: 与最终母版使用完全相同参数。
- 高级回答: 内容自适应代理与局部高质量预览。
10. 为什么导出前后出现偏色时 PSNR 可能极低?
- 标准回答: primaries、transfer、matrix 或 range 不一致会造成系统性像素偏移,即使编码器压缩本身很好。
- 追问: 如何排查?
- 常见错误: 立即提高码率。
- 高级回答: 检查元数据、像素格式、Canvas/GPU 转换链和比较域。
12.5 系统设计题 5 道
1. 设计一个浏览器端视频质量回归系统
- 标准回答: demux/decode 两路视频,按时间戳配对,统一尺寸与颜色域,逐帧计算 MSE/SSIM,聚合报告;设置 Worker、有界队列、取消和资源释放。
- 追问: 如何处理缺帧和变帧率?
- 常见错误: 只按数组下标配对帧。
- 高级回答: 时间窗匹配、场景分段、异常帧抽样和服务端 VMAF 补充。
2. 设计 AI 视频多版本自动选优流程
- 标准回答: 先做格式与时序校验,再计算编码质量、时序稳定性和生成语义指标,最后结合成本与人工抽检排序。
- 追问: 如何避免指标奖励过度平滑?
- 常见错误: 只按 PSNR 排名。
- 高级回答: 建立多目标 Pareto 前沿与内容分层阈值。
3. 设计在线编辑器的预览与导出双链路
- 标准回答: 预览使用代理媒体、低延迟解码和 GPU 合成;导出使用原始素材、完整时间线、较慢高质量编码和可靠 mux。
- 追问: 如何保证两条链路视觉一致?
- 常见错误: 浏览器预览参数直接当最终导出参数。
- 高级回答: 共享颜色管理、效果参数和确定性渲染测试。
4. 设计大规模转码参数选择系统
- 标准回答: 对代表性内容采样多个分辨率/码率/preset,计算 VMAF 与编码成本,形成每标题或每场景的码率阶梯。
- 追问: 如何控制计算量?
- 常见错误: 所有内容使用固定码率梯度。
- 高级回答: 内容复杂度特征、贝叶斯/分层搜索、缓存和停止条件。
5. 设计低内存的双视频逐帧比较管线
- 标准回答: 两路 demux/decode 按时间戳推进,每路仅缓存小窗口;匹配后立即计算并关闭帧;队列到阈值时暂停上游。
- 追问: Seek 或用户取消怎么办?
- 常见错误: 两个视频全部解码进数组。
- 高级回答: generation token、AbortSignal、解码器 reset、过期任务丢弃和统计一致性。
十三、练习
练习 1:熵手算
分布为:
A: 0.5
B: 0.25
C: 0.125
D: 0.125
- 计算熵。
- 设计一个前缀码。
- 计算平均码长。
参考答案
熵为 1.75 bit/symbol。可用 A=0, B=10, C=110, D=111,平均码长也是 1.75 bit/symbol。
练习 2:比较预测器
原序列:
[30, 31, 32, 32, 33, 34, 34, 35]
分别使用:
- 预测器 P0:恒定预测 0;
- 预测器 P1:左值预测。
计算残差并比较经验分布。
参考答案
P0 残差等于原值,分布较分散。P1 在首值单独编码时,残差为 [30,1,1,0,1,1,0,1],除首值外高度集中在 0 和 1,更适合熵编码。
练习 3:量化与重建
系数:
[40, -13, 7, 2]
量化步长:
[8, 4, 4, 4]
计算量化与反量化结果,并列出系数误差。
参考答案
量化系数约为 [5,-3,2,1];反量化为 [40,-12,8,4];误差为 [0,1,1,2]。具体负数舍入需明确所用规则。
练习 4:RDO 选择
| 模式 | D | R |
|---|---|---|
| Intra-A | 90 | 70 |
| Intra-B | 110 | 40 |
| Inter-A | 140 | 15 |
分别在 (\lambda=0.5) 和 (\lambda=2) 时选择模式。
参考答案
- (\lambda=0.5):代价分别为 125、130、147.5,选 Intra-A。
- (\lambda=2):代价分别为 230、190、170,选 Inter-A。
练习 5:PSNR
8-bit 图像的 MSE 分别为 1、4、16。计算 PSNR,并观察 MSE 每增大 4 倍时 PSNR 的变化。
参考答案
约为 48.13 dB、42.11 dB、36.09 dB。MSE 每增大 4 倍,PSNR 下降约 6.02 dB。
练习 6:伪代码
实现以下流程:
输入一行灰度像素
→ 左值预测
→ 统计原始值与残差的经验熵
→ 输出两者差值
要求处理空数组和首样本。
练习 7:Debug
你用 FFmpeg 比较两个“肉眼几乎相同”的视频,PSNR 只有 18 dB。列出至少 8 个检查项。
参考答案
时间偏移、帧率、缺帧、裁剪、缩放、SAR、像素格式、色彩矩阵、full/limited range、HDR transfer、参考/失真输入顺序、去隔行、旋转元数据、Alpha 合成和不同解码路径。
练习 8:架构设计
画出一个“浏览器预览 + 服务端高质量导出”的双链路图,并在图中标出:
- 哪一步使用原始帧;
- 哪一步发生有损量化;
- 哪一步进行 mux;
- 哪一步做 backpressure;
- 哪一步计算质量指标。
十四、本章速查表
14.1 核心公式
| 名称 | 公式 | 一句话解释 |
|---|---|---|
| 自信息 | (I(x)=-\log_2p(x)) | 越罕见越意外 |
| 熵 | (H(X)=-\sum p_i\log_2p_i) | 平均不确定性 |
| 残差 | (r=x-\hat{x}) | 只编码预测没猜中的部分 |
| 量化 | (q=round(X/Q)) | 降低精度,制造更多 0 |
| RDO | (J=D+\lambda R) | 同时考虑质量和码率 |
| MSE | (\frac1N\sum(x_i-y_i)^2) | 平均平方误差 |
| PSNR | (10\log_{10}(MAX^2/MSE)) | 对数化的误差指标 |
| SSIM | 亮度×对比度×结构 | 局部结构相似性 |
14.2 核心流程
编码:预测 → 残差 → 变换 → 量化 → 熵编码
解码:熵解码 → 反量化 → 反变换 → 预测重建
14.3 面试结论
- 预测利用相关性,变换集中能量,量化制造可压缩的离散分布,熵编码无损地缩短平均表示。
- DCT 不是压缩格式;量化才是主要有损环节。
- 编码器靠 RDO 选方案,解码器按码流执行,不重复搜索。
- PSNR 必须在同位深、同颜色域、同尺寸、同时间对齐条件下比较。
- SSIM/VMAF 更关注感知,但仍不能替代时序指标和主观评估。
- WebCodecs 不等于完整编码器内部调试接口,也不等于 MP4 muxer。
14.4 本周时间安排
| 学习日 | 内容 | 建议时间 | 输出 |
|---|---|---|---|
| Day 1 | 自信息、熵、Huffman | 1.5 h | 手算 3 组分布 |
| Day 2 | 条件熵、预测、残差 | 1.0 h | 比较两个预测器 |
| Day 3 | 1D/2D DCT | 2.0 h | 手算并画频率基函数直觉图 |
| Day 4 | 量化与反量化 | 1.0 h | 完成 3 组量化题 |
| Day 5 | RDO | 1.0 h | 完成模式选择表 |
| Day 6 | MSE、PSNR、SSIM、VMAF | 1.5 h | 用 FFmpeg 比较两段视频 |
| Day 7 | Web 映射、面试与复盘 | 1.0~2.0 h | 画编码器/解码器流程图 |
14.5 通过标准
你能够在不看资料的情况下完成:
- 计算四符号熵与平均码长。
- 对一行像素生成预测残差。
- 解释 DCT 的 DC/AC 和能量集中。
- 完成量化、反量化与误差计算。
- 用两个不同 (\lambda) 完成 RDO 选择。
- 计算 MSE 和 8-bit PSNR。
- 解释 SSIM/VMAF 的价值与边界。
- 画出编码器闭环和解码器流程。
- 说明浏览器逐帧质量评估的内存与 backpressure 方案。
- 明确说出哪些是标准规定,哪些是编码器实现策略。
十五、参考资料
- Claude E. Shannon, A Mathematical Theory of Communication:信息熵与信源编码理论基础。 https://people.math.harvard.edu/~ctm/home/text/others/shannon/entropy/entropy.pdf
- JPEG Committee, JPEG 1:JPEG 1 的 DCT、Huffman、算术编码、无损与分层编码概览。 https://jpeg.org/jpeg/
- ITU-T, H.264: Advanced video coding for generic audiovisual services:H.264/AVC 规范入口。 https://www.itu.int/rec/t-rec-h.264
- Z. Wang, A. C. Bovik, H. R. Sheikh, E. P. Simoncelli, Image Quality Assessment: From Error Visibility to Structural Similarity:SSIM 原始论文。 https://www.cns.nyu.edu/pub/lcv/wang03-preprint.pdf
- Netflix, VMAF — Video Multi-Method Assessment Fusion:VMAF 官方开源实现与文档。 https://github.com/Netflix/vmaf
- W3C, WebCodecs:
VideoFrame、VideoDecoder、VideoEncoder等接口规范。 https://www.w3.org/TR/webcodecs/ - FFmpeg Documentation:PSNR、SSIM、libvmaf 与媒体处理工具文档入口。 https://ffmpeg.org/documentation.html