「倒退式」JPEG:图片加载的感知魔术

「倒退式」JPEG:图片加载的感知魔术

图像压缩JPEG编码Web性能

数据源:HN + Lobsters

2026 年 7 月 17 日,一篇题为「Regressive JPEGs」的博客登上了 Hacker News 榜首(389 points)。作者 Maurycy 展示了一个令人惊讶的技巧:把猫的照片和一个「猫走过来」的视频塞进同一个 JPEG 文件里,依赖的底层机制是 JPEG 规范里一个被大多数人遗忘的特性——渐进式扫描。

这个技巧不是新格式,也不是新标准。它只是把 JPEG 规范里的几个旧零件重新组装了一下,做出了规范从未设想过的事。

JPEG 到底是怎么压缩一张图的?

要理解这个技巧,得先回到 JPEG 压缩的基础——DCT(离散余弦变换)。

JPEG 编码的第一步是把 RGB 图像转成 YCbCr 色彩空间。Y 通道代表亮度(luminance),Cb 和 Cr 代表色度(chrominance)。这样分开处理的原因是:人眼对亮度变化远比色度变化敏感。所以在编码时,色度通道可以大胆地降低精度,肉眼几乎看不出来。

接下来,JPEG 把图像切成 8×8 的像素块,对每个块做离散余弦变换。这个变换把 64 个像素值转换成 64 个 DCT 系数——第一个叫 DC 系数,代表这个 8×8 块的平均颜色;后面 63 个叫 AC 系数,从低频到高频排列,分别描述块内的渐变、纹理和细节。

这些系数经过量化(除以一个量化矩阵然后取整)后,大部分高频分量会变成零。量化之后的系数以「之」字形(zigzag)顺序排列,先低频后高频,然后用霍夫曼编码做无损压缩。这就是基线 JPEG 的完整流程。

那问题来了——既然基线 JPEG 压缩效果已经不错了,为什么还需要渐进式 JPEG?

渐进式 JPEG:先看个大概,再慢慢变清楚

基线 JPEG 加载时,图像从上到下逐行展开。在慢速网络下,用户对着半张图的空白区干等,体验并不好。

渐进式 JPEG 则换了一种思路。它的核心机制是把压缩数据拆分成多个「扫描」(scan),每个扫描只携带一部分信息。浏览器收到第一个扫描就能渲染出完整图像——虽然模糊,但用户立刻知道「图在加载了」。

JPEG 规范提供了两种组织扫描的方式:

频谱选择(Spectral Selection):按频率分批。第一次扫描只发 DC 系数(最低频,决定每个 8×8 块的平均色),后续扫描逐步发送中频和高频 AC 系数。因为先收到的是低频信息(整体轮廓和渐变),后收到的是高频信息(纹理和细节),图像会从模糊逐渐变清晰。

逐次逼近(Successive Approximation):按精度分批。第一次扫描用粗精度量化(比如只保留系数的最高几位),后续扫描补充低位比特来提升精度。这也能产生从模糊到清晰的渐进效果。

一个典型的渐进式 JPEG 大约包含 10 个扫描,每个扫描以 FF DA(Start of Scan)标记开头。Maurycy 在博客里完整展示了一个渐进式 JPEG 的 10 个扫描布局:

扫描序号通道DCT 频率范围精度
0Y Cb Cr0–0 (仅 DC)半精度 (−1 bit)
1Y1–5 (低频 AC)四分之一精度 (−2 bit)
2Cb1–63 (全部 AC)半精度
3Cr1–63半精度
4Y6–63 (中高频 AC)四分之一精度
5Y1–63半精度
6Y Cb Cr0–0全精度
7Cr1–63全精度
8Cb1–63全精度
9Y1–63全精度

注意扫描 4 的频率范围是 6–63——它在填补扫描 1(1–5)留下的空隙,这样扫描 5 就有了完整的四分之一精度数据作为基础。这种精细的频率范围编排,让图像在最短时间内呈现出最多的视觉信息。

还有个有趣的细节:Maurycy 的示例把色度数据放在了亮度前面。这看似违背了「亮度更重要」的原则,但因为色度通道做了色度子采样(chroma subsampling),分辨率只有亮度的一半,所以全色度数据(Cb+Cr)的实际数据量只有亮度的一半——先传完它,不会拖慢整体的感知进度。

「倒退式」JPEG:当扫描不再叠加,而是覆盖

渐进式 JPEG 的正常用法是:每个后续扫描在之前扫描的基础上叠加细节。AC 扫描会「精炼」(refine)之前已经渲染的系数,让图像一步步变清晰。

但这里有一个规范层面的关键事实:每个扫描独立声明自己的频谱范围。解码器不会检查「这个扫描的数据和上一个是否属于同一张图」。

Maurycy 抓住了这个空隙。他发现可以这样做:

  1. 把多张分辨率相同的图像分别编码为渐进式 JPEG。
  2. 把它们的扫描数据拼接在一起,去掉中间的 SOI(Start of Image)、SOF(Start of Frame)和 EOI(End of Image)标记。
  3. 得到一个混杂了多张图像数据的 JPEG 文件。

当浏览器解码这个文件时,它会忠实地逐个渲染每个扫描。因为后续扫描覆盖了之前的频谱范围,渲染出来的画面就会在不同图像之间「切换」。博客里展示了一只草地上的猫加载到一半变成水泥地上另一只猫的效果。

草地猫切换为水泥地猫

这就是「倒退式」(regressive)的由来——正常渐进模式让同一张图越来越清晰(progressive),而倒退式在加载过程中从一张图切换成另一张图,甚至倒退成一段「视频」。

但问题马上来了:大多数解码器(包括 Chrome)在超过一定数量的扫描后会放弃解码。Maurycy 推测这是为了防止 ZIP 炸弹式的攻击。Chrome 大约能渲染 90 个扫描,Firefox 耐心更多——但 90 个扫描最多也就是 9 帧(每帧 10 个扫描),做不了真正的「视频」。

最小化每帧的扫描数

如果想在一个文件里塞更多帧,就必须把每帧的扫描数压缩到最少。

基线 JPEG 整个图像只有一个扫描,但基线解码器读完第一个扫描就停止了,不会继续读后面的数据。所以必须用渐进模式。

渐进模式有个限制:一个扫描不能同时包含 DC(频率 0)和 AC(频率 1–63)的数据。DC 和 AC 必须分开在不同的扫描里。既然 AC 数据必须跟在 DC 数据后面,最小的「渐进式」单位就是一个只含 DC 的扫描。

这意味着每个 DC-only 扫描只能渲染出原始分辨率的 1/16——因为 DCT 在 8×8 块上运行,DC 系数给的是每个 8×8 块的平均颜色,相当于把图像的每个 8×8 块压缩成一个像素。

扫描序号通道DCT 频率范围精度
0Y Cb Cr0–0全精度

这就是最优解:每帧一个 DC-only 扫描,90 个扫描就是 90 帧。虽然每帧只有 1/16 的分辨率,但足以呈现连贯的动作。

而且这种方案有个意外的好处:它避免了原生渐进扫描的「鬼影」问题。正常渐进 JPEG 里,AC 扫描会精炼已经渲染的系数——但如果这些系数来自上一帧(不同图像),精炼操作会产生错误的混合。DC-only 模式没有 AC 扫描,也就没有精炼行为,每个扫描的画面干净独立。

创建这种文件不需要特殊工具。Maurycy 的示例只用到了 jpegtran

cat > frame.scans<<EOF
# DC only scan:
0,1,2:0-0,0,0;
# and nothing else
EOF
jpegtran -scans frame.scans -outfile out.jpg in.jpg

然后把这些 DC-only 帧的扫描数据拼接起来,就得到了一个「视频 JPEG」。

博客里展示了一个黑猫走向镜头的「视频」,完整嵌入在单个 JPEG 文件中。 猫走向镜头的视频 JPEG

那问题来了:为什么基线 JPEG 赢了渐进式 JPEG?

如果渐进式 JPEG 的用户体验明显更好——完整预览、渐进清晰——为什么今天互联网上大部分 JPEG 仍然是基线模式?

解码器生态和历史惯性提供了更完整的解释。

1992 年 JPEG 标准发布时,渐进模式就是规范的一部分。但早期的 JPEG 解码器(包括微软 IE 浏览器的多个版本)对渐进式 JPEG 支持极差——有些干脆不渲染,等整张图下载完才显示。在拨号上网时代,这等于没有任何好处。

解码渐进式 JPEG 需要维护一个缓冲区来暂存中间的系数数据,对内存有限的 90 年代设备来说是真实负担。编码也更慢——多次遍历图像需要更多计算。

维度基线 JPEG渐进式 JPEG
编码速度快(单次遍历)慢(多次遍历)
解码内存低(无需缓冲)高(需要系数缓冲)
加载体验从上到下逐步显示从模糊到清晰
浏览器支持全面历史上有缺口
文件大小通常稍小通常稍大(5–10%)
感知速度慢(空白区域多)快(立即有完整预览)

到 2010 年代,主流浏览器都已支持渐进式 JPEG。MozJPEG 等工具的出现让渐进编码更普及。据 2023 年一项研究估计,全球前 100 万网站中约 30% 的 JPEG 图像使用了渐进模式。但基线模式仍占多数——惯性使然。

「倒退式」的遗产:没有实际应用,但说明了什么?

Maurycy 自己在博客结尾坦承:这个技术没有任何实际用途。文件中没有时间信息,播放速度完全取决于网络延迟。它不能被暂停、倒放、或控制帧率。本质上,它是一个依赖网络速度的、不可控的幻灯片。

但他也展示了更有趣的变体。有人利用部分渲染的特性做了一个纯 HTML 的 Bad Apple 视频(使用 <dialog> 标签和部分加载的 JPEG),还有一个没有 CSS 和 JavaScript 的单页交互应用——它通过 HTTP Range 请求渐进式 JPEG 的不同扫描段来实现页面切换。

这些都不是产品级别的方案。它们更像是黑客式的探索:「规范里没说不让我这么做」

JPEG 教会我们什么?

从 1992 年至今,JPEG 经历了 JPEG 2000、JPEG XR、WebP、HEIF、AVIF 等多轮挑战,仍然是互联网上使用最广泛的图像格式。它在兼容性、压缩效率和计算成本之间找到了一个所有利益方都能接受的平衡点——这比任何单一维度的技术优势都更能解释它的长寿。

渐进式 JPEG 在技术上优于基线 JPEG——体验更好,带宽利用更智能。但它输给了早期浏览器的实现惰性和生态惯性。WebP 和 AVIF 今天也在类似的位置上:它们确实更小更好,但取代 JPEG 需要整个工具链(相机、CMS、CDN、浏览器、社交媒体)的配合。

「倒退式 JPEG」提醒我们一件事:图像格式不只是压缩算法的较量。协议里的每一个字段、解码器里的每一个边界条件,都是可以重新解释的创意空间。

而规范写了什么不重要——重要的是,规范没有禁止什么。

参考链接:

  • Maurycy 博客:Regressive JPEGs
  • Hacker News 讨论帖