2.初步了解图像数据流的结构
1)理论说明
分析图像数据流的结构,笔者准备以一个从宏观到微观的顺序为读者详细剖析,即:
数据流à最小编码单元à数据单元与颜色分量。
a) 在图片像素数据流中,信息可以被分为一段接一段的最小编码单元(Minimum Coded Unit,MCU)数据流。所谓MCU,是图像中一个正方矩阵像素的数据。
矩阵的大小是这样确定的:
查阅标记SOF0,可以得到图像不同颜色分量的采样因子,即Y、Cr、Cb三个分量各自的水平采样因子和垂直采样因子。大多图片的采样因子为4:1:1或1:1:1。其中,4:1:1即(2*2):(1*1):(1*1));1:1:1即(1*1):(1*1):(1*1)。记三个分量中水平采样因子最大值为Hmax,垂直采样因子最大值为Vmax,那么单个MCU矩阵的宽就是Hmax*8像素,高就是Vmax*8像素。
如果,整幅图像的宽度和高度不是MCU宽度和高度的整数倍,那么编码时会用某些数值填充进去,保证解码过程中MCU的完整性(解码完成后,可直接忽视图像宽度和高度外的数据)。
在数据流中,MCU的排列方法是从左到右,从上到下。
b) 每个MCU又分为若干个数据单元。数据单元的大小必定为8*8,所以每个MCU的数据单元个数为Hmax*Vmax。
另外JPEG的压缩方法与BMP文件有所不同,它不是把每个像素的颜色分量连续存储在一起的,而是把图片分成Y,Cr,Cb三张子图,然后分别压缩。而三个颜色分量的采样密度(即采样因子)可能一样(例如1:1:1)也可能不一样(例如4:1:1)。
每个MCU内部,数据的顺序是Y、Cr、Cb。如果一个颜色分量有多个数据单元,则顺序是从左到右,从上到下。
2)举例说明
下面通过一幅32*35的图像,对上面两个问题列出两种采样因子的具体说明。
图 1 整张完整的图像(4:1:1) 图 2 将图像的MCU1放大
图1及图3中灰色部分为实际图像大小(32px*35px);粗虚线表示各个MCU的分界;细虚线表示MCU内部数据单元的分界。
a) 采样因子为4:1:1
此时,Hmax=max(2,1,1)=2,Vmax=max(2,1,1)=2。所以,MCU的宽为Hmax*8=16像素,高为Vmax*8=16像素。图像实际的宽刚好是2个MCU,但高则稍稍大于2个MCU的高度,所以要补足3行MCU。
在数据流中,MCU的顺序是MCU1àMCU2àMCU3àMCU4àMCU5àMCU6。
每个MCU又分为Hmax*Vmax=2*2=4个数据单元。由于采样因子是4:1:1,即(2*2):(1*1):(1*1),所以Y分量的水平和垂直方向都是每2个像素采样2次;Cr分量和Cb分量的水平和垂直方向都是每2个像素采样1次。因此,在一个MCU来里边,Y分量有256个采样点,即4个完整的数据单元;Cr分量和Cb分量各自只有64个采样点。
如果以MCU1说明MCU数据的次序,则依次为Y1 、Y2 、Y5 、Y6 、Cr1256 、Cb1256 。图2中全部256个点均是Y的采样点,红色部分为Cr分量和Cr分量的采样点。
换句话说,对于整张图片来说,数据流的数据依次是:
[Y1 、Y2 、Y5 、Y6 、Cr1256 、Cb1256] 、[Y3 、Y4 、Y7 、Y8 、Cr3478 、Cb3478] 、[Y9 、Y10 、Y13 、Y14 、Cr9101314 、Cb9101314 ]、……
图 3 整张完整的图像(1:1:1)
b) 采样因子为1:1:1
如图3。Hmax=max(1,1,1)=1,Vmax=max(1,1,1)=2。所以,MCU的宽为Hmax*8=8像素,高为Vmax*8=8像素。图像实际的宽刚好是4个MCU,但高则稍稍大于4个MCU的高度,所以要补足5行MCU。
在数据流中,MCU的顺序是:
MCU1àMCU2àMCU3àMCU4à ………… àMCU18àMCU19àMCU20。
每个MCU又分为Hmax*Vmax=1*1=1个数据单元,也就是一个数据单元就是一个MCU。由于采样因子是1:1:1,即(1*1):(1*1):(1*1),所以Y分量、Cr分量和Cb分量的水平和垂直方向都是每1个像素采样1次,也就是图象的每一个像素都是采样点。因此,在一个MCU来里边,Y分量、Cr分量和Cb分量各自有64个采样点。有
因此,对于整张图片来说,数据流的数据依次是:
[Y1 、Cr1、Cb1] 、[Y2 、Cr2 、Cb2] 、[Y3 、Cr3、Cb3] 、………… [Y19 、Cr19、Cb19]、[Y20 、Cr20、Cb20]。
3.颜色分量单元的内部解码
1)理论说明
“颜色分量单元”是笔者为说明问题而建立的概念,指的是MCU中某个颜色分量中的一个8*8数据块,例如上面提到的Y1、Cr1、Cb1 都是一个颜色分量单元。
图像数据流是以位(bit)为单位存储信息的。并且内部的数据都是在编码时通过正向离散余弦变换(FDCT)进行时空域向频率域变换而得到的结果,所以对于每个颜色分量单元都应该由两部分组成:1个直流分量和63个交流分量。
解码的过程其实就是哈夫曼树的查找过程。
首先查阅标记段SOS中的颜色分量信息,可以得出各个颜色分量对应使用的直流分量和交流分量使用的哈夫曼树编号。一般来说,
Y分量:直流分量:直流0号哈夫曼树,交流分量:交流0号哈夫曼树;
Cr分量:直流分量:直流1号哈夫曼树,交流分量:交流1号哈夫曼树;
Cb分量:直流分量:直流1号哈夫曼树,交流分量:交流1号哈夫曼树。
颜色分量单元内部综合运用了RLE行程编码和哈夫曼编码来压缩数据。每个像素的数据流由两部分构成:编码和数值,并且两者基本以互相隔开方式出现(除非该编码的权值为零)。具体读入单个颜色分量单元的步骤如下:
a)从此颜色分量单元数据流的起点开始一位一位的读入,直到读入的编码与该分量直流哈夫曼树的某个码字(叶子结点)一致,然后用直流哈夫曼树查得该码字对应的权值。权值(共8位)表示该直流分量数值的二进制位数,也就是接下来需要读入的位数。
b)继续读入位数据,直到读入的编码与该分量交流哈夫曼树的某个码字(叶子结点)一致,然后用交流哈夫曼树查得该码字对应的权值。权值的高4位表示当前数值前面有多少个连续的零,低4位表示该交流分量数值的二进制位数,也就是接下来需要读入的位数。
c)不断重复步骤b,直到满足交流分量数据结束的条件。而结束条件有两个,只要满足其中一个即可:
①当读入码字的权值为零,表示往后的交流变量全部为零;
②已经读入63个交流分量。
d)各个数值的译码是按下表进行的:
实际数值 |
编码长度 |
编码 |
0 |
0 |
- |
-1,1 |
1 |
0,1 |
-3,-2,2,3 |
2 |
00,01,10,11 |
-7,-6,-5,-4,4,5,6,7 |
3 |
000,001,010,011,100,101,110,111 |
-15,……,-8,8,……,15 |
4 |
0000,……,0111,1000,……,1111 |
-31,……,-16,16,……,31 |
5 |
00000,……,01111,10000,……,11111 |
-63,……,-32,32,……,63 |
6 |
…… |
-127,……,-64,64,……,127 |
7 |
…… |
-255,……,-128,128,……,255 |
8 |
…… |
-511,……,-256,256,……,511 |
9 |
…… |
-1023,……,-512,512,……,1023 |
10 |
…… |
-2047,……,-1024,1024,……,2047 |
11 |
…… |
-4095,……,-2048,2048,……,4095 |
12 |
…… |
-8191,……,-4096,4096,……,8191 |
13 |
…… |
-16383,……,-8192,8192,……,16383 |
14 |
…… |
-32767,……,-16384,16384,……,32767 |
15 |
…… |
2)举例说明
下面举例说明以上几点。某个颜色分量单元数据如下:
D3 5E 6E 4D 35 f5 8A
若以二进制表示,则为:
1101 0011 0101 1110 0110 1110 0100 1101 0011 0101 1111 0101 1000 1010
假设该颜色分量单元对应以下直流哈夫曼树和交流哈夫曼树,则可将各个以位为单位的数据流拆分如下:
110 1001101 01 1 11001 101 11001 001 101 00 11010 1 1111010 11 00 01010
直流哈夫曼树 交流哈夫曼树
序号 |
码字长度 |
码字 |
权值 |
1 |
2 |
00 |
0x00 |
2 |
2 |
01 |
0x01 |
3 |
2 |
10 |
0x02 |
4 |
3 |
110 |
0x07 |
5 |
4 |
1110 |
0x1e |
6 |
5 |
11110 |
0x2e |
序号 |
码字长度 |
码字 |
权值 |
1 |
2 |
00 |
0x00 |
2 |
2 |
01 |
0x01 |
3 |
3 |
100 |
0x11 |
4 |
3 |
101 |
0x02 |
5 |
5 |
11000 |
0x21 |
6 |
5 |
11001 |
0x03 |
7 |
5 |
11010 |
0x31 |
8 |
5 |
11011 |
0x41 |
9 |
5 |
11100 |
0x12 |
10 |
6 |
111010 |
0x51 |
11 |
7 |
1110110 |
0x61 |
12 |
7 |
1110111 |
0x71 |
13 |
7 |
1111000 |
0x81 |
14 |
7 |
1111001 |
0x91 |
15 |
7 |
1111010 |
0x22 |
16 |
7 |
1111011 |
0x13 |
17 |
8 |
11111000 |
0x32 |
详细说明一下:
读入数据流并对照直流哈夫曼树,第一个哈夫曼编码为110,其权值为6,所以往后读入6位数据“1001101”,译码成数值为77。因为每个颜色分量单元只有一个直流分量,所以下一个就是第一个交流分量了。
继续读入数据流并对照交流哈夫曼树,得哈夫曼编码为01,其权值为1,所以它的前面没有零,并往后读如1位数据“1”,译码成数值为1。如此往复,最后读到哈夫曼编码“00”,其权值为0,所以满足交流变量结束条件(最后剩余的“01010”对本颜色分量单元来说是冗余的,它可能属于下一个颜色分量单元)。
实际上,这段数据译码为:
77,(0,1),(0,5),(0,-6),(0,-3),(5,1),(2,3)
因此,把它置于1个8*8的矩阵中应为:
77 |
1 |
5 |
-6 |
-3 |
0 |
0 |
0 |
0 |
0 |
1 |
0 |
0 |
3 |
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 |
0 |
0 |
0 |