1. JPEG
JPEG是众多常见的图像编码格式之一,主要分为无损压缩和有损压缩,无损压缩利用空域相关性进行预测,主要用于医学、卫星遥感等领域;有损压缩主要通过DCT变换,然后进行量化来压缩数据。本文将以一个简单的16×8灰度图像作为例子,简单讲述JPEG图像解码的主要过程。
1.1.JPEG图片结构
图1(a)所示的图片是一个简单的16×8的JPEG灰度图片,是由图1(b)所示的yuv420的图像利用1(c)所示的ffmpeg命令生成1(d)的bmp图像(图1(d)是3通道的RGB图像,且三个通道的值都相同,所以在1(d)中只列出了一个值),然后利用libjpeg编码为jpeg图像,最终的JPEG图像显示出来的效果和1(a)是一样的。
(a) 示例JPEG图片 (b)YUV图片各分量像素值
(c)将YUV转换为RGB的BMP图片
(d)BMP图片各坐标像素值
图1. 示例图片
将JPEG图片按16进制格式打开,如下2(a)所示,其中红框标记的部分都是JPEG的标记(Marker)。顺序编码和无损编码的结构(层次结构的JPEG图像不讨论)如下图2(b)所示,一个正常的JPEG码流以SOI(FFD8)标记开始,以EOI(FFD9)标记结束,中间是一帧的图像信息,包括各种数据(如 huffman表FFC4部分,量化表FFC0部分,以及APP和COM等部分)和SCAN部分(FFDA部分)等。JPEG图像包含一个或者多个SCAN(progressive模式包含多个SCAN),一个SCAN下面有一个或者多个RST(Restart Interval),一个RST里有一个或者多个MCU,一个MCU里有一个或者多个Block。
(a)JPEG十六进制
(b)JPEG高层语法结构
图2. JPEG结构
下面将详细量化表的解析,huffman表的解析和构造以及SCAN里MCU系数的解析和系数的反量化变换。
1.2.量化表解析
图3是量化表的语法结构,其中3(a)是示例图片中量化表所对应的码流,3(b)是标准中量化表对应的语法,具体每个字段的含义可以参考JPEG标准,此处不再详述。
(a)示例图片中量化表部分码流
(b)量化表语法
(c)zig-zag扫描顺序 (d)反量化表
图3. 量化表相关语法
码流中FFDB到FFC0之间的信息为DQT部分的码流,可以得到如下信息。Lq=0x43=67,Pq=0(8比特),Tq=0(指定当前量化表将被安装的位置索引),剩下64个元素按照3(c)所示的zig-zag扫描的顺序被解析为3(d)反量化表
1.3.Huffman表
图4是Huffman表的语法结构,其中4(a)是示例图片中Huffman表所对应的码流,4(b)是标准中Huffman表对应的语法,具体每个字段的含义可以参考JPEG标准,此处不再详述。
(a)示例所示Huffman部分码流
(b)Huffman表语法
图4. Huffman语法结构
其中第一个FFC4是解DC系数所对应的Huffman表。Lh=0x1F=31,Tc=0(DC),Th=0(index),接下来的16字节表示每个码字长度的个数,详细信息如下所示。
idx | bits | huffsize | huffcode | huffval |
0 | 0 |
|
|
|
1 | 0 |
|
|
|
2 | 1 | 2 | 00 | 0 |
3 | 5 | 3、3、3、3、3 | 010、011、100、101、110 | 1、2、3、4、5 |
4 | 1 | 4 | 1110 | 6 |
5 | 1 | 5 | 11110 | 7 |
6 | 1 | 6 | 111110 | 8 |
7 | 1 | 7 | 1111110 | 9 |
8 | 1 | 8 | 11111110 | A |
9 | 1 | 9 | 111111110 | B |
10 | 0 |
|
|
|
11 | 0 |
|
|
|
12 | 0 |
|
|
|
13 | 0 |
|
|
|
14 | 0 |
|
|
|
15 | 0 |
|
|
|
16 | 0 |
|
|
|
第二个FFC4是解AC系数所对应的Huffman表。其详细的信息如下所示。
idx | bits | huffsize | huffcode | huffval |
0 | 0 |
|
|
|
1 | 0 |
|
|
|
2 | 2 | 2、2 | 00、01 | 01、02 |
3 | 1 | 3 | 100 | 03 |
4 | 3 | 4、4、4 | 1010、1011、1100 | 00、04、11 |
5 | 3 | 5、5、5 | 11010、11011、11100 | 05、12、21 |
6 | 2 | 6、6 | 111010、111011 | 31、41 |
7 | 4 | 7、7、7、7 | 0x78、0x79、0x7a、0x7b | 06、13、51、61 |
8 | 3 | 8、8、8 | 0xf8、0xf9、0xfa | 07、22、71 |
9 | 5 | 9、9、9、9、9 | 0x1f6、0x1f7、0x1f8、 | 14、32、81、91、A1 |
10 | 5 | 10、10、10、10、10 | 0x3f6、0x3f7、0x3f8、 | 08、23、42、B1、C1 |
11 | 4 | 11、11、11、11 | 0x7f6、0x7f7、 | 15、52、D1、F0 |
12 | 4 | 12、12、12、12 | 0xff4、0xff5、 | 24、33、62、72 |
13 | 0 |
|
|
|
14 | 0 |
|
|
|
15 | 1 | 15 | 0x7fc0 | 82 |
16 | 125 | 16、16… | 0xff82、0xff83、0xff84、0xff85、0xff86、0xff87、0xff88、0xff89、 | 09、0A、16、17、18、19、1A、25、26、27、28、 |
1.4.SOS
从FF DA开始是Start of Scan,SOS分为图5(a)和(b)两部分。
(a)SCAN码流
(b)Scan header高层语法结构
图5. SCAN语法结构
其中,Ls = 0x000C=12,从03~1A部分的码流是ScanHeader部分,剩余部分直到FFD9都是SCAN需要解码的内容。对每个Block,都有一个DC分量,紧接着63个AC分量。将“0X F0 1A F0 1A F0 1A”二进制化“1111 0000 0001 1010 1111 0000 0001 1010 1111 0000 0001 1010”,按照huffman解码规则,解码过程如下:
Block 1
DC分量:
11110,对应的huffval=7,说明接下去的7个比特“000 0001”表示为DC=-126值。
AC分量:
1010,对应的huffval=0,End of Block,表示接下来的63个分量都为0。
对Block2和Block3按照同样的方式解码,得到表1所示结果。
表1. MCU1的DC和AC系数
Bits | 11110 0000001 | 1010 | 11110 0000001 | 1010 | 11110 0000001 | 1010 |
MCU | 1 | |||||
Component | R | G | B | |||
AC / DC | DC | AC | DC | AC | DC | AC |
Value | -126 | 0 | -126 | 0 | -126 | 0 |
按照同样的原理对第二个MCU进行解码。AC系数最后将每个组合以“[RRRR/SSSS]+尾码”表示,其中RRRR为0的行程的长度,SSSS表示尾码的有效位数B,即当前非零系数所占的比特数。系数值大于等于0,尾码的码字为系数值的B位原码;否则,为系数值的绝对值的B位反码。解码结果如下表2所示。MCU2的第一个Block的系数矩阵如表3所示。
表2. MCU1的DC和AC系数
Bits | 11110 1101110 | 1111000 000111,… |
MCU | 2 | |
Component | R | |
AC / DC | DC | AC |
Value | 110,最终结果为110-126=-16 | -56,还剩下62个系数待解析 |
表3 MCU2、Block1的系数矩阵
-16 | -56 | 1 | -4 | 0 | 0 | 0 | 0 |
-56 | 1 | 1 | 0 | 0 | 0 | 0 | 0 |
0 | 1 | 1 | 0 | 0 | 0 | 0 | 0 |
-5 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
-1 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
1.5.量化变换
在JPEG中是以8×8大小的图像块作为DCT变换的单位。反量化矩阵已经在前面1.2节解析出来。8×8二维DCT定义如下:
可以将其转换为如下公式,
其中,矩阵A的值如下所示:
经过反量化反变换便可得到图1(d)所示的最终的解码结果。