跳到主要内容
浏览器
导航
图片浏览器

渐进式 JPEG 与交错 PNG 的编码原理

对比基线编码与渐进编码的数据组织方式,说明渐进式 JPEG 的谱选择与逐次逼近扫描、交错 PNG 的 Adam7 七遍扫描,以及它们在可辨认画面出现时间、总字节数与解码内存占用上的取舍。

发布于 4 分钟阅读 1361 字

同样一张图片,按不同顺序排列压缩数据,会得到总长度相近但「读取体验」不同的文件。基线编码把像素按扫描顺序依次压缩,解码器必须读到接近结尾才能画出完整画面;渐进编码则把信息按重要程度重新排序,让解码器读到文件前部时就能给出可辨认的近似结果。对图片浏览器而言,这直接决定了加载过程如何呈现。

两种组织的差别不在压缩算法本身,而在扫描顺序与传输单元的划分。下面分别看 JPEG 与 PNG 的做法。

基线编码的数据组织

JPEG 基线编码把图像划分为 8×8 的块,对每个块做 DCT、量化、熵编码,然后按从左到右、从上到下的顺序输出。解码器需要同时处理的是当前 MCU 行,配合重启标记,可以在不保留整幅系数的情况下边解边输出。

PNG 属于无损格式,没有 DCT 与量化,压缩依赖逐行滤波与 zlib 的 LZ77 加霍夫曼编码。逐行排列的 PNG 按行顺序写入 IDAT,解码器解出一行即可交给显示层。

渐进式 JPEG 的扫描组织

渐进式 JPEG 放弃了「一次把所有系数发完」的做法,改为把码流切成多个扫描(scan),按重要程度分批发送。它使用两个正交的维度来组织扫描:

  • 谱选择(spectral selection):把 64 个 DCT 系数分成若干频带,按频带分批发送。低频系数承载主要的结构信息,先发;高频系数承载细节,后发。
  • 逐次逼近(successive approximation):同一频带分多次发送,每轮只发送若干位平面。首轮发送若干高位,后续扫描补充低位,使系数逐步精确。

一个扫描由若干参数描述:作用在哪些分量上、覆盖哪个谱段、本轮发送哪一段位精度。下面的片段以 JSON 形式示意这类参数的组织:

{
  "scans": [
    { "components": ["Y"],  "spectral_start": 0, "spectral_end": 0,  "high_bit": 1, "low_bit": 0 },
    { "components": ["Cb"], "spectral_start": 0, "spectral_end": 63, "high_bit": 2, "low_bit": 1 },
    { "components": ["Y"],  "spectral_start": 1, "spectral_end": 5,  "high_bit": 2, "low_bit": 1 }
  ]
}

编码器会把低频与高位精度的扫描排在前面。解码器每读完一个扫描,就可以用已获得的系数重建一幅近似图像,因此前几个扫描结束时画面轮廓已经可辨认,随后的扫描只是逐轮细化。整个流程重复执行「系数 → 逆变换 → 上采样 → 输出」,其中上采样所用的插值方式可参见图像缩放插值算法。

交错 PNG 的 Adam7 扫描

PNG 的交错方式称为 Adam7,把图像按 8×8 的像素格划分为 7 遍扫描,每一遍覆盖格子中特定位置的像素:

遍起始 x起始 y水平步长垂直步长
10088
24088
30448
42044
50224
61022
70112

前几遍只包含稀疏的像素,读完后即可给出整幅图的粗略预览;第 7 遍补齐剩余像素后图像完整。遍历位置可以用一段循环生成:

passes = [(0, 0, 8, 8), (4, 0, 8, 8), (0, 4, 4, 8), (2, 0, 4, 4),
          (0, 2, 2, 4), (1, 0, 2, 2), (0, 1, 1, 2)]

def iter_pass(width, height, x0, y0, dx, dy):
    for y in range(y0, height, dy):
        for x in range(x0, width, dx):
            yield y * width + x   # 该遍扫描包含的像素索引

每一遍都要单独做行滤波与 zlib 压缩。由于同一条扫描行内的相邻像素在物理上并不相邻,滤波的预测效果下降,压缩率通常低于逐行版本;多次压缩流也带来额外开销,因此交错 PNG 的总字节数一般大于同内容的逐行文件。

可辨认画面出现时间与总字节数

渐进编码的核心取舍可以概括为一张表:

维度基线 / 逐行渐进 / 交错
可辨认画面初次出现的位置接近文件末尾文件前部即可
总字节数相对较小通常略大
达到完整质量所需数据全部全部
传输中断时的可用性几乎不可用可保留中间质量

渐进编码并不减少总数据量,它改变的是数据到达顺序。在带宽受限、或传输可能中断的场景下,可辨认内容出现得更早;在文件完整到达后才显示的场景下,到达顺序不影响显示结果,反而要为略大的体积付出代价。

解码复杂度与内存占用

  • 渐进式 JPEG:多个扫描作用于同一组系数,解码器必须保留整幅图像的系数缓冲直到全部扫描完成,内存占用高于基线;若扫描数较多,重建近似图像也需要重复执行逆变换。
  • 交错 PNG:像素按 7 遍散布到全图,解码器需要一块完整的图像缓冲区,且必须缓存尚未凑齐整行的样本,无法像逐行方式那样只保留少量行。
  • 基线 JPEG:配合重启标记可以按 MCU 行流水处理,行缓冲的数量取决于实现,内存占用与图像宽度相关而非与整幅面积相关。

在内存有限的设备上,这些差异决定了能否直接解码某些文件,或必须改用分块解码。

图片浏览器 · 相关文章