深度解析塑造未来的技术文章。

JPEG 压缩原理详解:DCT 与量化表

图解 JPEG 压缩原理:DCT 变换、量化表,以及为什么有些图片压缩效果更好。

一朵花的图像逐渐溶解为像素块,再变成漩涡状的波纹图案

JPEG 就是文件格式里的小强。它 1992 年完成标准化,比网页浏览器还早,并且扛过了每一次想要取代它的尝试。WebP、AVIF、HEIC……它们的压缩率都更高,但 JPEG 仍然是互联网上使用最广泛的图片格式。这里面有惯性的因素,但更重要的是,JPEG 的压缩流程确实精妙,即便你从来不写编码器,它也是应用信号处理的典范,值得了解。

大多数开发者都把 JPEG 当成黑盒:丢进一张图,拿出一个更小的文件,然后把质量滑块拉到自己觉得还能看的位置。但如果弄清楚盒子里发生了什么,你对图片、压缩以及文件大小与视觉质量之间取舍的理解都会不一样。

流程概览

JPEG 压缩是由五个阶段组成的流水线,每一步都很巧妙。精妙之处在于它们环环相扣:每个阶段都为下一个阶段创造更好的条件。

  1. 颜色空间转换 ——将 RGB 转为 YCbCr(把亮度与色彩分离)
  2. 色度抽样 ——降低色彩通道的分辨率
  3. 离散余弦变换(DCT) ——把空间域数据转换为频域数据
  4. 量化 ——丢弃高频细节(这是有损的一步)
  5. 熵编码 ——对量化后的数据做无损压缩

前两步利用的是人类视觉的特性,第三、四步利用的是自然图像的特性,第五步则是标准的数据压缩。每一步单独看都不复杂,关键在于它们的组合顺序。

第一步:颜色空间转换

相机拍出的图像通常是 RGB 格式,即红、绿、蓝三个通道,每个像素每通道 8 位。JPEG 的第一步是把它转换为 YCbCr:一个亮度(明度)通道和两个色度(颜色)通道。这一步还不算压缩,没有丢失任何数据,它只是一次坐标变换,就像旋转坐标轴。

为什么要这么做?因为人眼对亮度的敏感度远高于对颜色的敏感度。黑白照片里的极细节你都能看清,但对色彩分辨率的感知要粗糙得多。把亮度和颜色分开之后,JPEG 就能区别对待:保留亮度细节,同时大力削减色彩细节。

RGB to YCbCr conversion (simplified):
Y  =  0.299R + 0.587G + 0.114B    (luminance — brightness)
Cb = -0.169R - 0.331G + 0.500B    (blue chrominance)
Cr =  0.500R - 0.419G - 0.081B    (red chrominance)
Note: green dominates the luminance calculation because
human eyes have far more green-sensitive cone cells.
This isn't arbitrary — it matches the physiology.

第二步:色度抽样

这里是第一次真正意义上的数据削减。既然人眼无法以全分辨率感知颜色,JPEG 就会降低 Cb 和 Cr 两个通道的分辨率,通常是在水平和垂直方向各缩小一半。这就是所谓的 4:2:0 抽样:每四个亮度像素,只对应一个色度采样。

结果就是:原始数据量直接减半(从 3 个全分辨率通道变成 1 个全分辨率加 2 个四分之一分辨率的通道),而图像看上去几乎与原图一模一样。你可以自己试试:随便找一张照片,把色彩通道的分辨率降低到四分之一,你很难看出区别。大脑会根据亮度信息把色彩细节补全。

这一招对不同图片的效果并不一样。照片压缩得很好,因为自然场景的色彩过渡平滑。但色彩边界分明的图像,比如彩色背景上的文字、白底上的红色圆圈或者像素画,在色度抽样之后会出现明显的色边。这也是 JPEG 处理文字效果很差的原因:锐利的色彩边缘会晕开。

第三步:离散余弦变换

DCT 是 JPEG 真正有意思的地方。图像会被切分成 8×8 的像素块,每个块从空间域(像素亮度值)变换到频域(各种空间频率成分的含量)。

可以这样理解:一个 8×8 的像素块,可以表示为 64 个基础图案的加权和。第一个图案是平坦的色块,代表平均值;接下来的图案加入水平和垂直方向的渐变;编号越大的图案,表示的细节越精细,也就是亮度上越快速的起伏。

An 8x8 DCT block conceptually:
DC ──► Low frequency ──────────────► High frequency
┌─────┬─────┬─────┬─────┬─────┬─────┬─────┬─────┐
│ AVG │  →  │  →  │  →  │  →  │  →  │  →  │  →  │ Low freq
│     │     │     │     │     │     │     │     │   ↓
│  ↓  │  ↘  │     │     │     │     │     │     │
│     │     │  ↘  │     │     │     │     │     │
│     │     │     │  ↘  │     │     │     │     │
│     │     │     │     │  ↘  │     │     │     │
│     │     │     │     │     │  ↘  │     │     │
│     │     │     │     │     │     │  ↘  │     │
│     │     │     │     │     │     │     │ FINE │ High freq
└─────┴─────┴─────┴─────┴─────┴─────┴─────┴─────┘
Top-left = low frequency (smooth gradients)
Bottom-right = high frequency (sharp edges, fine detail)
Natural images: most energy concentrated in top-left
Text/line art: significant energy in bottom-right

DCT 本身是无损的,你可以从这 64 个 DCT 系数完美还原原始的 8×8 块。但关键特性在于:对于自然图像(照片、渐变、有机纹理),大部分能量集中在低频系数上。代表精细细节的高频系数通常很小。这正是下一步得以奏效的原因。

为什么是 8×8?这是一种折中。更大的块(16×16、32×32)能更好地集中能量,但计算成本更高,而且在低质量设置下会产生明显的块效应。更小的块(4×4)能减轻块效应,但能量集中的效果又不够好。8×8 恰好是个甜点,经过三十多年依然沿用至今。

第四步:量化——数据在这里消亡

这是有损的一步。每个 DCT 系数都会除以量化表中对应的数值,然后四舍五入到最近的整数。对于高频系数,除数较大,于是许多小数值会被舍入为零。这些零一旦产生就永远回不来了,信息就是在这里被永久丢弃的。

Example quantization (quality ~50):
DCT coefficient: 42.7    Quantization divisor: 16    Result: round(42.7/16) = 3
DCT coefficient: 8.3     Quantization divisor: 40    Result: round(8.3/40)  = 0
DCT coefficient: -2.1    Quantization divisor: 99    Result: round(-2.1/99) = 0
The quality slider in your image editor controls the quantization table.
Quality 100: divisors close to 1 (almost no rounding, huge file)
Quality 75:  moderate divisors (good balance, typical web use)
Quality 50:  large divisors (noticeable artifacts, small file)
Quality 10:  very large divisors (blocky mess, tiny file)

量化表是决定 JPEG 质量的最关键因素。JPEG 标准定义了一张默认表,但编码器可以使用任意的表。现代编码器如 MozJPEG 使用经过优化的量化表,依据人眼的感知敏感度调整除数,对人眼不太敏感的频率施加更激进的量化,从而在更小的文件里榨出更好的画质。

量化之后,中等质量下的典型 8×8 块,64 个系数中大约只有 6 到 10 个是非零的,其余都是零。非零值的大幅减少,正是最后的压缩步骤能够生效的基础。

第五步:熵编码

最后一步是对量化后的系数做无损压缩。JPEG 结合了游程编码(RLE)和霍夫曼编码。系数会按照 Z 字形(zigzag)顺序从 8×8 块中读出:从直流分量(左上角的 DC)开始,沿着频率递增的方向锯齿状扫过。这样一来,所有尾部的零就被集中到了一起,游程编码可以高效处理:与其存储『0, 0, 0, 0, 0, 0, ..., 0』,不如直接存『四十七个零』。

霍夫曼编码再为出现频率高的值分配较短的比特序列,为罕见的值分配较长的序列。由于大多数非零系数都是小整数(1、-1、2、-2),它们会得到非常短的编码。结果是:原本 64 个数值的量化 8×8 块,通常会被压缩到 20 到 40 字节,具体取决于图像内容和质量设置。

为什么有些图片比其他图片压缩得更好

理解了整个流程,许多看似神秘的压缩表现就能解释清楚了。

  • 照片压缩效果好,因为它们色彩过渡平滑(色度抽样效果极佳),并且大部分能量集中在低频 DCT 系数上(量化可以把许多系数归零,而不会带来明显的视觉损失)。
  • 文字和线稿压缩效果差,因为锐利的边缘会产生大量高频 DCT 系数,如果强行归零就会出现明显瑕疵。文字边缘周围的振铃效应会暴露出 8×8 块的边界。
  • 噪点会毁掉压缩,因为传感器噪声是高频的随机数据。每个噪点像素都会增加非零的高频系数,而这些系数很难被量化掉。同一场景的照片,在相同质量下,带噪点的版本可能比干净版本大 3 到 5 倍。
  • 渐变几乎是免费的,因为 8×8 块内的平滑渐变只需 2 到 3 个 DCT 系数就能表示,即便不做量化,其余系数也都是零。
  • 反复保存会降低画质,因为每次保存都会再次进行量化,舍入误差会不断累积。以质量 75 保存一张 JPEG 十次,明显比只保存一次的效果差。所以你应该始终从原始源文件开始编辑。

JPEG 质量滑块并非线性的

大多数开发者都把 JPEG 质量滑块当成线性的取舍:质量 50 的画质是质量 100 的一半,文件也只有一半大小。这两点都不对。

质量 100 并不等于『无损』,它依然会做量化,只是除数很小。文件会非常大(常常比 PNG 还大),而画面与质量 95 几乎无法区分。对于照片而言,质量 95 与质量 90 在视觉上也难以分辨。质量 85 和 75 之间的感知差异,要比 100 和 85 之间的差异大得多。质量低于 50 时,瑕疵会变得很严重,文件体积的下降也会放缓。

网页图片的甜点区间通常是质量 75 到 85。低于 75,在正常观看尺寸下就能看到瑕疵;高于 85,文件体积大幅增加,视觉上的提升却微乎其微。MozJPEG 默认的质量 75 选得很好,对大多数照片来说,它正好落在质量与体积曲线的拐点上。

现代 JPEG 编码器比你想象的更聪明

JPEG 标准规定的是解码格式,而不是编码器。这意味着,不同的编码器实现即便都输出符合规范的文件,压缩效率也可能天差地别。由 Mozilla 开发的 MozJPEG,通过多项技术,在同等视觉质量下,比 libjpeg 生成的文件小 5% 到 15%。

  • 网格量化(Trellis quantization)——它不再独立地对每个 DCT 系数做四舍五入,而是考虑每个舍入决策对熵编码阶段的影响,从而做出全局更优的选择。
  • 优化的霍夫曼表——JPEG 默认的霍夫曼表是通用的。MozJPEG 会针对每张图片实际的系数分布,生成定制的表。
  • 渐进式编码——渐进式 JPEG 会分多遍存储系数(先粗略,再逐步细化)。这通常比顺序编码压缩得更好,因为粗略的那一遍系数分布更可预测。
  • 自适应量化表——自适应编码器不再对整张图使用同一张量化表,而是按区域调整量化:在复杂区域(那里的瑕疵不易察觉)更激进,在平滑区域则更保守。

如果你在网页上提供图片,把默认的 libjpeg 编码器换成 MozJPEG,是最容易获得的性能提升之一。大多数图片 CDN 和图像处理库都支持它。编码速度会慢一些(但对预处理好的静态图片来说无关紧要),同等质量下输出的文件则更小。

JPEG 与新格式的对比

WebP、AVIF 和 HEIC 都采用了更现代的压缩技术,比如更大的变换块、环路去块滤波器,以及帧内预测(利用相邻块来预测当前块)。在同等视觉质量下,它们比 JPEG 的体积要小 20% 到 50%。其中 AVIF 基于 AV1 视频编解码器,效率令人印象深刻。

那为什么 JPEG 依然无处不在?因为它的兼容性无可替代。每个浏览器、每个图片查看器、每个操作系统、每台相机、每部手机,以及每一个能处理图片的软件都支持 JPEG。WebP 在浏览器中几乎已经普及,但原生操作系统支持还不够完善;AVIF 的支持仍然参差不齐;HEIC 主要局限在苹果的生态里。对于一个必须到处都能用的格式来说,JPEG 仍然是唯一稳妥的选择。

实用的建议是:如果你的服务基础设施支持(大多数 CDN 都通过内容协商支持),可以使用 AVIF 或 WebP,并以 JPEG 作为回退。如果只能选一种格式,那么用 MozJPEG 以质量 80 编码的 JPEG,对照片来说很难被超越。对于截图、图表和文字较多的图片,PNG 仍然是更好的选择——也可以探索更多关于图像表达的创意方法。

征服世界的 8×8 块

JPEG 的压缩流程堪称工程学的教科书式范例:它顺应人类感知而非与之对抗。每一个阶段都利用了某种特定属性(人眼对色彩的不敏感、自然图像的频率分布、量化噪声的感知掩蔽),它们组合成一个系统,能在几乎不产生可见影响的情况下将文件缩小 10 到 20 倍。三十四年过去了,当年在 1992 年的硬件上运行的 8×8 DCT 块,至今依然承载着互联网上的大部分图片。这正是一个把基础打牢了的设计所留下的印记。