昨天花了一天的时间试着写了一下JPEG解码器,虽然遇到了一些麻烦,不过最后还是成功地解出了图像的数据。总共400行代码左右。
(解码的核心也就这50行左右,因为只是验证一下以前了解的信息,所以看起来还是有点乱,而且没有做各种错误处理,所以不要太在意啦┑( ̄Д  ̄)┍)
下面是JPEG的编码过程的整理(因为编码和解码是完全对称的所以就只说编码,解码可以自行脑补)(仔细看的话都能在上面那段代码中找到对应哦):
(图片自制) RGB转YCbCr:因为人眼对亮度比较敏感,而对于色度不那么敏感,所以,我们就先将RGB的数据转换到YCbCr色彩空间,便于下面的处理。
降采样:转到YCbCr色彩空间后,就可以将 Cb 和 Cr 这两个通道进行降采样,这里一般是将 2*2 个像素变为 1*1 个像素,虽然分辨率下降到了四分之一,但对于人眼来说差别是不大的。(这一步是有损的)
分块:顾名思义,将图像分为若干个 8*8 的小块,方便下面的处理。
DCT:这一步的目的和RGB转YCbCr有一些相似之处,都是将人眼较为敏感和不敏感的部分进行分离,然后就可以对减少人眼不敏感的部分的信息量。在这一步中,DCT可以将图像的低频(人眼敏感)和高频(人眼不敏感)部分进行分离。这样得到的结果是每个 8*8 小块得到 8*8 的系数矩阵。
量化:将DCT后得到的每个系数都除以量化矩阵中对应的值,然后进行取整。通常来说频率较高的部分对应的量化参数比较大,这样一来就能够在较好地保留图像的低频部分并去除一些高频部分。这一步下来得到的矩阵中高频部分几乎全部变为0,这也为进一步的操作提供了便利。值得注意的是,JPEG中压缩率的调整是在这一步中,量化参数越大,压缩后的大小就会越小,但信息的损失也就越多,图片的失真也会更严重。(这一步是有损的)
Huffman编码:准确地说是Huffman编码和RLE,将上一步得到的矩阵进行进一步地压缩(这一步是无损的)。量化后得到的矩阵左上角的那个数比其他数来得大得多,所以我们将它单独拿出来进行编码,称之为直流分量(DC),将剩下的称之为交流系数(AC)。这一步会将矩阵按照 zigzag 的顺序摊成一维,如下图所示:
这样做的好处是矩阵的高频部分(右下角)会被安排在后面,因为它们基本上全是0,所以在编码中有一个特殊的标记表示这之后的系数全是0,从而减少压缩后的大小。
(话说回来,Huffman解码这一步应该是解码中最麻烦的一部分……位的顺序搞了好久)
上面这些步骤完成之后,再加上一些必要的其他信息(量化矩阵和Huffman的信息,这两个是直接没有压缩就存储在文件中的),就得到了最终的JPEG数据。
暂时先整理这么多,以后有时间再认真写一下解码器,争取某一天能够达到 libjpeg 的水平吧 :) (以后应该会包含在 Medicat 中←某个妄图把各种图像/音频格式都自己实现一遍的轮子)
参考: