文件中段的介绍
APP0段中主要存储的是图片的识别信息(字符串”JFIF\0”)、一些分辨率的信息以及缩略图的信息。在我的实际测试中,发现并不是所有的JPEG文件都有APP0段的,有的仅是有APP2之类的其他段,但是每个文件中肯定是包含APPX的段(X可以取得的值可以查阅相关文档)。
DQT段的内容是量化表的信息。众所周知,一个颜色可以分为RGB(红、绿、兰)三个分量,这三色光组成了我们可以见到的所有色彩。但是,在JPEG文件中,RGB色彩格式需要先转化为YUV的格式。Y分量代表了亮度信息,UV分量代表了色差信息。相比之下,人眼对于Y分量更为敏感。量化表的作用就是对于一些不需要的量进行去除,这也是JPEG有损压缩损失数据的关键。上面的输出可以看到两个量化表,一个给Y分量,另一个给UV分量。其实,他们也可以共用一个量化表。一个量化的结果如下所示(摘自《JPEG压缩编码标准》):
15 0 -1 0 0 0 0 0
-2 -1 0 0 0 0 0 0
-1 -1 0 0 0 0 0 0
0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0
我们可以看到,量化后出现了大量的0,这种结果很有利于我们进行下一步的数据压缩的。至于为什么是8x8的大小,待会你就知道了。
SOF0段的内容是图像的大小信息,每个像素的位数信息,以及YUV每个分量分别得的采样信息(这部分如果读者想要进一步学习,请参考相应书籍和文档)。JPEG文件图像的编码是一个方块一个方块进行的,每块的大小为8x8大小(如果图像不是整数个方块的大小那么就对图像补齐为整数个大小)。简略地说采样信息,就是如何按组记录YUV的信息,即若干个Y方块,若干个U方块,若干个V方块经过量化的数据再次经过编码后组成一组记录,保存在SOS段结束后。
DHT段的内容是一个重头戏,如果没有它,JPEG压缩效率就不会那么高了。它内部定义的是一个Huffman表,不同的DHT段定义不同的Huffman表,有的是直流量的表,有的是交流量的表。什么是直流量,什么是交流量呢?待会我再作介绍。最多的Huffman表示几个呢?YUV各一个,直流交流各一个,因为YUV每个分量都有直流和交流,所以最多时,Huffman表有3x2个,也就是可以有6个DHT段。该文件中有4个DHT表,您可以大概猜出来是哪几个表么?Y的直流和交流各一个Huffman表,UV和起来直流和交流各一个Huffman表。这样说应该比较合理吧。
举上面那个有许多个0的8x8的表的例子说,所谓交流量,是经过量化后的块内部除了左上角15那个值的其余值。实际上,块与块之间左上角那个值是用直流Huffman表来单独编码的。不与块内部一同编码。虽然不同的编码,但是要注意的事,不同的编码方式并不意味着它们是不在一起的,具体的存储编码后的数据的时候,还是按照若干个Y方块,若干个U方块,若干个V方块经过量化的数据再次经过编码后组成一组记录来存储的。
SOS段的内容是关于YUV每个分量的直流和交流各使用那个Huffman表来编码的。
图像数据块内的编码方式
使用那刚才那个包含很多0的量化后的8x8的数据块来说明,把块内剩余的63个数据用行程编码来编码。经过行程编码后的数据的格式是:(x,y)。x表示的是从当前位置开始有多少个连续的零,y表示这些连续的0的后面的第一个非零的数是多少。但是为了解决存储的问题和进行进一步的压缩。最后的压缩格式变为:(x,y)z。xy占有一个字节的长度。z的长度不固定,需要根据y的值来判断。x仍代表从当前位置开始有几个连续的零,但是因为x只能占有四位的长度,也就是它的最大值是15,所以,当多于16个连续的零的时候。会用一个字节的(15,0)来代替前面的16个0,然后继续编码(注意:这时候没有z部分)。当块结束或者当前块后面剩余的都是零的时候,就用(0,0)即EOB代替(同样也是没有z部分)。前面说到z的长度不固定,需要根据y的值来判断,这是为什么呢?简单的来说,z的长度是不一定的,在1~15的范围内。Y的作用简单的来说表示的是z的二进制位数(1~15),也正好是4位二进制的值能够表示的。然后,把xy合成的一个字节单独提取出来,利用DHT里面的Huffman表来进行编码。这样,编码的长度又能够被压缩了。