WeCodec 知识库 · 视频编码
用一个 8×8 块的真实数字走一遍 DCT 和量化
编码器把画面切成块,对每块做变换,再做量化。变换只是换一种表示方式,把像素换成频率系数,能量集中到左上角。量化把系数除以步长再取整,大部分高频系数变成 0。
变换和后面的熵编码都能精确还原,画质损失来自量化。
编码器不会整帧处理,而是先把画面切成方块,每块分别做预测、变换和量化。H.264 的基本单位叫宏块(macroblock),通常是 16×16 像素(维基百科:Macroblock)。
HEVC 改用编码树单元 CTU(coding tree unit),可以是 64×64、32×32 或 16×16(维基百科:HEVC)。CTU 按四叉树往下切:一个方块要么不切,要么切成四个一样大的小方块,小方块还能继续切。AV1 的对应单位叫超级块(superblock),大小是 128×128 或 64×64(维基百科:AV1)。
天空这类平坦区域,用大块就够了,还能省下描述切分方式的比特。树叶、文字这类细节多的区域,切成小块预测得更准。大块对高分辨率视频更划算:一组 HEVC 测试里,把 CTU 从 64×64 限制到 16×16,码率平均上升 11.0%(维基百科:Coding tree unit)。
视频编码器会先预测每块的内容,再对预测残差做变换,残差就是真实画面减去预测画面。JPEG 没有预测这一步,直接变换像素,但变换和量化的做法一样。下面用维基百科 JPEG 条目里的一个经典例子来演示(维基百科:JPEG)。
DCT(discrete cosine transform,离散余弦变换)把 8×8 个像素值换成 8×8 个频率系数。左上角是直流系数(DC),代表整块的平均亮度。越往右下,系数代表的纹理越细(同上)。
结果里左上角是 -415,其余较大的系数都挤在左上几格,右下角大多接近 0。相邻像素通常很接近,所以能量天然集中在低频,这就是 DCT 好用的原因(维基百科:Discrete cosine transform)。
量化把每个系数除以对应的步长,再四舍五入成整数。步长越大,丢掉的越多。下图上半部分是 JPEG 在质量 50 时常用的量化表:左上角步长小,右下角步长大,因为人眼对细密纹理的强弱不敏感(维基百科:JPEG)。
拿直流系数算一下:-415.37 ÷ 16 ≈ -25.96,取整得 -26。右下角的系数本来就小,再除以 100 左右的步长,全部变成 0。64 个系数量化完,只剩 20 个不是 0。
解码时把整数乘回步长,再做反变换。取整时丢掉的小数部分找不回来,所以重建的像素和原图有误差。步长越大,误差越大,码率越低。
量化后的系数要排成一列才能编码。JPEG 按之字形(zigzag)从左上往右下扫,低频系数排在前面,后面是一长串 0(维基百科:JPEG)。
按这个顺序,前 26 个系数是:-26, -3, 0, -3, -2, -6, 2, -4, 1, -3, 1, 1, 5, 1, 2, -1, 1, -1, 2, 0, 0, 0, 0, 0, -1, -1,后面 38 个全是 0。
连续的 0 很好压。JPEG 用游程编码(run-length encoding)把一串 0 记成一个长度,再用霍夫曼编码给常见的值分配短码(同上)。这两步都是熵编码,只换写法,不丢信息。
视频标准沿用了切块、变换、量化这条路,但细节不同。
| 对比项 | JPEG | H.264 | HEVC |
|---|---|---|---|
| 变换对象 | 原始像素 | 预测残差 | 预测残差 |
| 变换 | 8×8 DCT | 整数变换,4×4 和 8×8 | 整数 DCT 和 DST,4×4 到 32×32 |
| 量化控制 | 量化表 | 对数刻度的步长,可配按频率调整的量化矩阵 | 本页未展开 |
H.264 和 HEVC 的变换信息来自维基百科:Advanced Video Coding和维基百科:HEVC。DST(discrete sine transform,离散正弦变换)是 DCT 的近亲,只是基函数换成了正弦。
视频标准改用整数变换,是为了让编码端和解码端算出的结果逐位一致。H.261、MPEG-2 这类早期标准用浮点公式加容差来定义变换,不同解码器的输出会有细微差别(维基百科:Advanced Video Coding)。VVC 又在 HEVC 的方形变换块之外,加入了长方形变换块(维基百科:Versatile Video Coding)。
在 H.264 里,量化强度由量化参数 QP 控制,取值 0 到 51,数值越大,量化越重,画质越低(Werner Robitza:Understanding Rate Control Modes)。编码器怎么给每帧定 QP,见《码率控制:CBR、VBR、CRF》。