JPEG格式研究——(1)压缩原理及流程
之前一直很好奇图片和视频是如何压缩的,由于视频格式会更复杂,所以先从JPEG下手
因为网上资料太难找太分散,有些又看不太懂,所以根据自己的理解整理了一下
JPEG简介
JPEG(Joint Photographic Experts Group),全称为联合图像专家小组,同时也是我们生活中很常见的一种图像格式,一般以.jpg作扩展名。JPEG采用了多种方法来对图像进行有损压缩,从而使得观感相差不大的情况下减小了较多的文件体积
压缩流程
常见的JPEG图像数据按照如下流程进行压缩:
1.分块
JPEG中会首先对图像以8x8的块为单位划分,不足8x8的会填充剩余部分。不过具体填充什么我没研究过,但是解码显示出来的时候会忽略掉这部分,所以应该是无所谓的。
对图像进行8x8分块也是为了方便下一步进行DCT变化
2.DCT变换
DCT(Discrete Cosine Transform,离散余弦变换)是一种把数据从时域转化到频域的数学方法,把图像数据转换到频域之后,我们就可以从中分离出各种频率的信息。
DCT变换过后数据按照不同频率进行划分,得到的数据实际上是不同频率余弦波的系数,把这些系数乘上对应频率的余弦函数就能得到原来的数据
但要注意的是DCT变换过后数据的大小也不会发生改变,相当于把数据用另一种方式表示,就好像同一句话你用普通话和方言讲出来就是两种表达方式。
3.量化
DCT变换后我们就能区分不同频率的信息了,其中高频信息是图像中的一些细节,将变换后的高频部分系数置0则可以去除这些细节。因为人眼对高频信息不是很敏感,所以观感差距不会很大,就像你有轻度的近视,但不影响你看清一个物体。
比如下图
我们会发现,假如只保留DCT变换后左上角的低频部分,还原出来的图像其实和原图差距不大;而如果看保留高频后还原出来的图像,仔细看会发现只留下了物体边缘等细节部分的一些痕迹(也就是高频部分)
此时选择去除哪些频率的信息就成了关键点,我们要给图片选择一个“近视程度”来让减少高频信息。好在JPEG标准已经准备好了几种量化表,对应了不同的图像质量,我们只需要把DCT变换后的数据除以量化表中的对应项就完成了量化操作。
4.Z字形扫描
对数据进行过量化之后仍然没有减小体积,因为去除的高频系数只是被替换成了0,占用的空间还是不变,这时候就轮到Z字形扫描(Zig-Zag Scan)出场了。
Z字形扫描就如同字面意思一样,按照上图顺序重新排序顺序。因为高频信息聚集在右下角,按照这种方式扫描后就可以把被替换成0的高频系数连续排列了
顺带一提,知名音视频编解码工具FFmpeg的图标就来源于Z字形扫描
5.对DC和AC系数编码
什么是DC系数和AC系数?
简单地说,DC系数就是一个块中第一个数,而AC系数就是剩余的数
(图源网络)上图中1就是DC系数,2-9就是AC系数
对AC系数编码
前面的Z字形扫描完成了准备操作,真正压缩则是交给游程编码(Run Length Encode, RLE)
游程编码以两个数为一组,第一个数表示第二个数的重复次数,对于压缩经过Z字形扫描过后,存在大量连续的0的数据来说再合适不过了
假如直接舍弃后面一半的信息,对画质影响不大的情况下,减少了差不多一半的数据量!
对DC系数编码
对于DC系数,采用的是一种叫差分脉冲编码调制(Differential Pulse code modulation,DPCM)的编码方式
听起来很高深,实际上就是把DC系数换成这一个块和上一个块的DC系数的差值。比如上一个块的DC系数是20,这一个块的DC系数是10,那就写入-10。如果是第一个块,那就不用变(或者也可以看作前面有一个不存在的DC系数为0)。
JPEG中DC系数和AC系数的存储格式:
在实际存储的数据中,一个系数分成两部分
第一部分长度为一个byte。
对于AC系数来说采用了游程编码,低四位表示第二部分的长度(以bit为单位),高四位表示这一数据前0的个数;
对于DC系数来说这一整个byte就表示第二部分数据的长度(也是以bit为单位)第二部分的第1位是符号位,0表示负数,1表示正数。如果是正数,无需处理;如果是负数,则是对原数包括符号位在内的每一位取反的结果
6.熵编码
最后是对所有的数据进行熵编码,一般用的是范式霍夫曼编码(Canonical Huffman Code)。
范式霍夫曼编码基于霍夫曼编码,霍夫曼编码核心思想是为出现频率更高的数据分配更短的编号,为出现频率低的数据分配更长的编号,这样就可以实现无损压缩数据。而由于霍夫曼编码以bit为单位,长度又不确定,读取时无法区分,所以采用了范式霍夫曼编码。
JPEG中范式霍夫曼编码的存储格式:
首先用一个大小为16个字节的数组存储编码后数据中对应长度为1bit~16bit的原始数据的种类数,后面按同样的顺序紧跟所有种类的原始数据
而编码后的数据要通过如下方式得到:
数从长度为1bit的0开始,长度相同时每编码一个数就+1,长度每增加1bit就要左移1位
当然,JPEG标准中也给出了参考的范式霍夫曼编码的对照表
参考资料
JPEG解码系列博客:多媒体-编解码 - 随笔分类 - OnlyTime_唯有时光 - 博客园 (cnblogs.com)
JPEG标准:Microsoft Word - T081E.DOC (w3.org)
白话文理解DCT离散余弦变换 - 乂墨EMO - 博客园 (cnblogs.com)
一个Rust写的JPEG解码器:MROS/jpeg_tutorial: 跟我寫 JPEG 解碼器 (Write a JPEG decoder with me) (github.com)
友情链接
我学习过程中写的JPEG图片查看器:Ryan1202/my-tiny-jpeg-viewer: A Tiny Jpeg Viewer (github.com)