经过 DCT 以后,每个 的样本块都变成了一个 的系数矩阵。左上角的系数表示低频信息,越靠近右下角,表示的频率越高。
不过,DCT 本身并没有减少数据量。变换前有 64 个样本,变换后仍然有 64 个系数;而且这些系数通常还是带小数的。如果直接把它们全部保存下来,文件甚至可能比原始图像更大。
DCT 真正的作用,是把原本散落在各个像素中的信息重新集中起来:自然图像中的能量通常集中在少数低频系数上,许多高频系数都很小。接下来只要降低这些系数的精度,就有机会把其中一大部分变成 0。这个过程称为量化(quantization)。
量化是怎么做的
所谓的量化,就是用一个较为粗略的数代替原来精确的 DCT 系数。比如,我们计划只用 10 的倍数来表示系数:
那么
换句话说,量化所做的事情就是降低现有值的精度。精度降低之后,就可能产生更多的 0 值,有利于后续压缩。比如,我们可以把
记成
同时记录着量化步长,在这个例子中,量化步长就是 10。量化步长越小,可供选择的近似值越多,结果通常也越接近原系数;量化步长越大,可供选择的近似值越少,误差通常就越大,但更多系数会变成 0,这就更有利于压缩。
一个 DCT 系数块共有 64 个位置,每个位置表示的频率不同,适合采用的量化步长也不同。因此,JPEG 允许编码器为这 64 个位置分别指定步长,再按照对应位置排列成一个 的矩阵,这就是量化表。量化表中的每个数,都是相同位置上 DCT 系数的量化步长。
现在令 DCT 系数矩阵为 ,量化表为 ,量化后的系数矩阵为 。对每个位置分别执行前面描述的操作,就可以写成:
其中, 是某个 DCT 系数, 是它对应的量化步长, 是编码器实际记录的整数。
经过这一步,原来的小数系数变成了整数,绝对值较小的系数还会直接变成 0。由于高频系数本来就往往较小,再配合较大的量化步长,一个系数块的右下区域通常会出现大量连续的 0,这就给后续的编码过程带来了非常有利的条件。
量化表
量化表决定了每个频率要保留多少精度。下面是 JPEG 标准附录(T.81 Annex K、Table K.1)中给出的一张亮度量化表的示例:
可以看到,左上角的量化步长总体较小,右下角的量化步长总体较大。这与人眼的感知特性有关:低频分量描述大面积的明暗和缓慢变化,一旦出现较大的误差,整块图像的亮度都会偏离;高频分量主要描述细小纹理与锐利边缘,适当降低精度通常不太容易被察觉。因此,量化表实际上控制着压缩率与图像质量之间的平衡。
同一个分量的所有 块都会使用同一张量化表,但不同分量可以选择不同的表。通常,Y 分量单独使用一张亮度量化表,Cb、Cr 分量则共同使用另一张色度量化表。由于人眼对色度细节相对不敏感,色度量化表往往会采用更大的量化步长。
不过,JPEG 标准并没有固定量化表中的具体数值。编码器可以自行生成量化表,并将实际使用的表写入文件,解码器则可以按照文件中的量化表进行还原。
在很多图像处理软件中,当我们选择将图像导出为 JPEG 格式时,都会有一个“质量”参数,我们所调节的“质量”,通常就是调节了量化表中各个步长的大小。较高的质量对应较小的量化步长,较低的质量对应较大的量化步长。
量化为什么会损失信息
解码时,量化后的系数会乘回对应的量化步长:
假设量化步长现在是 10,那么原系数 13 会被量化成 1:
它在还原后则会得到:
显然,还原出的 10 与原来的 13 相差了 3。这个误差就是量化造成的信息损失。
换句话说,量化其实是一种多对一的映射,也是 JPEG 压缩流程中造成信息损失的核心步骤。DCT 只是换了一种方式表达图像,若保留足够精度,它本身可以逆变换回去;量化则真正丢弃了部分精度,让后续压缩能够大幅减少数据量。
量化完成了整个压缩流程中最关键的一次数据整理,它将原本带小数的 DCT 系数,变成了许多小整数和大量的 0。这就给后续的编码过程带来了非常有利的条件。JPEG 的编码器会进一步利用这些整数的统计分布特点,把它们转换成更短的码流,从而实现更高的压缩率。
量化误差会带来什么
量化较轻时,误差通常隐藏在纹理和颜色的细微变化里,不容易直接看出来。可随着量化强度的增加,一些典型的 JPEG 伪影便会逐渐出现:
- 平滑区域中的明暗或颜色过渡变得不自然;
- 锐利边缘附近出现波纹,也就是所谓的振铃(ringing);
- 相邻 块之间出现明显边界,也就是所谓的块效应(blocking)。