JPEG解码原理详解
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)所示的最终的解码结果。