实验jpg图片:
此图只有7.74kb,分辨率240x240,便于做分析
以十六进制打开图像文件查看像素数据,这一步可以用Binary Viewer或者自己用C++写一个十六进制打开文件的程序。接下来开始分结构展示数据和解析jpg数据结构。
00000000: FF D8 FF E0 00 10 4A 46 49 46 00 01 01 01 00 90
00000010: 00 90 00 00 FF E1 00 22 45 78 69 66 00 00 4D 4D
00000020: 00 2A 00 00 00 08 00 01 01 12 00 03 00 00 00 01
00000030: 00 01 00 00 00 00 00 00 FF DB 00 43 00 02 01 01
SOI | 0xFFD8 | 图像开始(Start Of Scan) |
1--2Byte: FF D8 ,SOI标志位,表示这是一个jpg图片
APP0 | 0xFFE0 | 存储图像参数 |
1--2Byte: FF E0 ,APP0标志位
3--4Byte: 00 10 ,十进制为16,APP0段外的段长度
5--9Byte: 4A 46 49 46 00 ,图片编码方式,“JFIF\0"或者”JFXX\0“等,这里表示用的是JFIF
JFIF-APP0 ,24A 46 49 46 00 ...
JFXX-APP0 , 24A 46 58 58 00 ...
10--11Byte: 01 01 ,JFIF版本 ,这里表示1.01版本
12Byte: 01 ,像素密度单位,00表示无单位(width:height像素宽高比 = Xdensity:Ydensity)
01表示每英寸像素(2.54厘米)
02表示每厘米像素
13--14Byte: 00 90 ,x方向密度,水平像素密度,不得为零
15--16Byte: 00 90 ,y方向密度,垂直像素密度,不得为零
17Byte: 00 ,嵌入的RGB缩略图的水平像素数,可以为零
18Byte: 00 ,嵌入的RGB缩略图的垂直像素数,可以为零
(3nByte:xx ... ,保存了24位的RGB位图,没有缩略位图时,17、18Byte均为0)
APP1 | 0xFFE1 | EXIF |
1--2Byte: FF E1 ,APP1标志位
3--4Byte: 00 22 ,十进制为34,APP1段外的段长度
5--10Byte: 45 78 69 66 00 00 ,Exif Header
11--36Byte: 4D 4D 00 2A 00 00 00 08 00 01 01 12 00 03 00 00 00 01 00 01 00 00 00 00 00 00 ,标识符及内容,内容不定
00000030: 00 01 00 00 00 00 00 00 FF DB 00 43 00 02 01 01
00000040: 02 01 01 02 02 02 02 02 02 02 02 03 05 03 03 03
00000050: 03 03 06 04 04 03 05 07 06 07 07 07 06 07 07 08
00000060: 09 0B 09 08 08 0A 08 07 07 0A 0D 0A 0A 0B 0C 0C
00000070: 0C 0C 07 09 0E 0F 0D 0C 0E 0B 0C 0C 0C FF DB 00
00000080: 43 01 02 02 02 03 03 03 06 03 03 06 0C 08 07 08
00000090: 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C
000000A0: 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C
000000B0: 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C
000000C0: 0C 0C FF C0 00 11 08 00 F0 00 F0 03 01 22 00 02
DQT | 0xFFDB | Define Quantization Table,定义量化表,可以有多个。量化表能影响图片的压缩质量 |
1--2Byte: FF DB ,DQT标志位
3--4Byte: 00 43 ,十进制为67,DQT段外的段长度
5Byte: 00 ,精度和表ID。高4位为精度,0为8位,1为16位。低4位未量化表ID。如此处:
第一次遇见的FFDB段落里,第5字节为00,表示精度为8,id为0
第二次遇见的FFDB段落里,第5字节为01,表示精度为8,id为1
6--(64*(精度+1)+5)Byte: 量化表表项长度为64*(精度+1),如第5字节为00时:
精度为0,表项长度为64*(0+1)=64Bytes
第一次遇见的FFDB段,提取到的量化表( 00, 精度:0, ID:0, 长度64*(0+1) ):
02 01 01 02 01 01 02 02 02 02 02 02 02 02 03 05 03 03 03 03 03 06
04 04 03 05 07 06 07 07 07 06 07 07 08 09 0B 09 08 08 0A 08 07 07
0A 0D 0A 0A 0B 0C 0C 0C 0C 07 09 0E 0F 0D 0C 0E 0B 0C 0C 0C
量化表展现为:
量表0:
2 1 1 2 1 1 2 2
2 2 2 2 2 2 3 5
3 3 3 3 3 6 4 4
3 5 7 6 7 7 7 6
7 7 8 9 11 9 8 8
10 8 7 7 10 13 10 10
11 12 12 12 12 7 9 14
15 13 12 14 11 12 12 12
同理,得到量化表1:
2 2 2 3 3 3 6 3
3 6 12 8 7 8 12 12
12 12 12 12 12 12 12 12
12 12 12 12 12 12 12 12
12 12 12 12 12 12 12 12
12 12 12 12 12 12 12 12
12 12 12 12 12 12 12 12
12 12 12 12 12 12 12 12
000000C0: 0C 0C FF C0 00 11 08 00 F0 00 F0 03 01 22 00 02
000000D0: 11 01 03 11 01 FF C4 00 1F 00 00 01 05 01 01 01
SOF0 | 0xFFC0 | Start Of Frame,SOF0是baseline DCT |
1--2Byte: FF C0 ,SOF0标志位
3--4Byte: 00 11 ,十进制为17,SOF0段外的段长度
5Byte: 08 ,代表精度,每像素数据位数,一般为8bit
6--7Byte: 00 F0 ,十进制为240,图像高度
8--9Byte: 00 F0 ,十进制为240,图像宽度
10Byte: 03 ,颜色分量数,一般为03,01表示灰度图,03表示YCbCr图,04表示CMYK
11--((三色分量)*3+10)Byte: 01 22 00,02 11 01,03 11 01,三个分量的三字节分别表示
颜色分量id,水平和垂直采样分子(水平高四位,垂直低四位),使用的量化表id
这里3个颜色分量的水平/垂直采样因子为:22、11、11
第一个颜色分量,水平采样因子为2,垂直采样因子为2,
第二个和第三个颜色分量,水平采样因子为1,垂直采样因子为1,
即:
[Y0 U0 V2] [Y1 U0 V2] [Y2 U0 V2] [Y3 U0 V2]
是0x411的色度抽样
如果是:11、11、11
即:
[Y0 U0 V0] [Y1 U1 V1] [Y2 U2 V2] [Y3 U3 V3]
则是0x111的色度抽样
000000D0: 11 01 03 11 01 FF C4 00 1F 00 00 01 05 01 01 01
000000E0: 01 01 01 00 00 00 00 00 00 00 00 01 02 03 04 05
000000F0: 06 07 08 09 0A 0B FF C4 00 B5 10 00 02 01 03 03
// 取第一个哈夫曼数据做例子,数据太多不便展示
DHT | 0xFFC4 | Define Huffman Table,定义哈夫曼编码表,可以有多个,具体重建哈夫曼树方法见下 |
1--2Byte: FF C4 ,DHT标志位
3--4Byte: 00 1F ,十进制为31,DHT段外的段长度
5Byte: 00 ,表类型和表ID,高四位为表类型,0表示DC表,1表示AC表,低四位为表ID
6-21Byte: 00 01 05 01 01 01 01 01 01 00 00 00 00 00 00 00 ,不同码字长度的数量,哈夫曼编码表的位数只能是1--16位,因此这里用十六个字节分别表示1--16位的码字长度数量,如下表示:
1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
0 | 1 | 5 | 1 | 1 | 1 | 1 | 1 | 1 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
上图意义为:
第1个字节00表示没有位数为1的编码,01:位数为2的编码有1个,03:位数为3的编码有5个,04:位数为4的编码有1个......并且得到此哈夫曼树有1+5+1+1+1+1+1+1=12个叶子节点
22--33Byte: 00 01 02 03 04 05 06 07 08 09 0A 0B ,建立哈夫曼树如下表示:
权值 0---> 码字长度 2 码字 00
权值 1---> 码字长度 3 码字 010
权值 2---> 码字长度 3 码字 011
权值 3---> 码字长度 3 码字 100
权值 4---> 码字长度 3 码字 101
权值 5---> 码字长度 3 码字 110
权值 6---> 码字长度 4 码字 1110
权值 7---> 码字长度 5 码字 11110
权值 8---> 码字长度 6 码字 111110
权值 9---> 码字长度 7 码字 1111110
权值 a---> 码字长度 8 码字 11111110
权值 b---> 码字长度 9 码字 111111110
表示以上16个叶子节点按照从小到大排序,权值依次为00 01 02 03 04 05 06 07 08 09 0A 0B。
建立规则:
第一个编码的数字必定是0,码字长度为2,编码为00(如果码字长度为3,编码就为000,以此类推),从第二个编码开始,如果它和它前面的编码具有相同的位数,则它的编码是它前面的编码加一,如果它的编码位数比它前面的编码位数大,则它的编码是它前面的编码加一后再在后面添加若干个0,直到满足编码位数的长度为止。
此处第二个编码位数为3,前面的编码位数是2,所以第二个编码是前面的编码加一再补一个零。
为了加深理解,再取实验jpg图片数据中的第二个哈夫曼表做解析
000000F0: 06 07 08 09 0A 0B FF C4 00 B5 10 00 02 01 03 03
00000100: 02 04 03 05 05 04 04 00 00 01 7D 01 02 03 00 04
00000110: 11 05 12 21 31 41 06 13 51 61 07 22 71 14 32 81
00000120: 91 A1 08 23 42 B1 C1 15 52 D1 F0 24 33 62 72 82
00000130: 09 0A 16 17 18 19 1A 25 26 27 28 29 2A 34 35 36
00000140: 37 38 39 3A 43 44 45 46 47 48 49 4A 53 54 55 56
00000150: 57 58 59 5A 63 64 65 66 67 68 69 6A 73 74 75 76
00000160: 77 78 79 7A 83 84 85 86 87 88 89 8A 92 93 94 95
00000170: 96 97 98 99 9A A2 A3 A4 A5 A6 A7 A8 A9 AA B2 B3
00000180: B4 B5 B6 B7 B8 B9 BA C2 C3 C4 C5 C6 C7 C8 C9 CA
00000190: D2 D3 D4 D5 D6 D7 D8 D9 DA E1 E2 E3 E4 E5 E6 E7
000001A0: E8 E9 EA F1 F2 F3 F4 F5 F6 F7 F8 F9 FA FF C4 00
1--2Byte: FF C4 ,DHT标志位
3--4Byte: 00 B5 ,十进制为181,DHT段外的段长度
5Byte: 10 ,此处表示id是0的AC表
6--21Byte: 00 02 01 03 03 02 04 03 05 05 04 04 00 00 01 7D ,不同长度码字数量,如下表示:
1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
0 | 2 | 1 | 3 | 3 | 2 | 4 | 3 | 5 | 5 | 4 | 4 | 0 | 0 | 1 | 125 |
第1个字节00表示没有位数为1的编码,02:位数为2的编码有2个,01:位数为3的编码有1个,03:位数为4的编码有3个......
并且得到此哈夫曼树有2+1+3+3+2+4+3+5+5+4+4+1+125=162个叶子节点
22--183Byte: 162个叶子节点的权值
以上162个叶子节点按照从小到大排序,权值依次为:01 02 03 00 04 11 05 12 21 31 41 06 13 51 61 07 22 71 14 32 81 91 A1 08 23 42 B1 C1 15 52 D1 F0 24 33 62 72 82 09 0A 16 17 18 19 1A 25 26 27 28 29 2A 34 35 36 37 38 39 3A 43 44 45 46 47 48 49 4A 53 54 55 56 57 58 59 5A 63 64 65 66 67 68 69 6A 73 74 75 76 77 78 79 7A 83 84 85 86 87 88 89 8A 92 93 94 95 96 97 98 99 9A A2 A3 A4 A5 A6 A7 A8 A9 AA B2 B3 B4 B5 B6 B7 B8 B9 BA C2 C3 C4 C5 C6 C7 C8 C9 CA D2 D3 D4 D5 D6 D7 D8 D9 DA E1 E2 E3 E4 E5 E6 E7 E8 E9 EA F1 F2 F3 F4 F5 F6 F7 F8 F9 FA
//该图片实例数据没有此字段,表示不存在重开始间隔和标记RST;
DRI | 0xFFDD | Define Restart Interval,重置DC信号的间隔(每解码指定次MCU就重置DC信号) |
1--2Byte: FF DD ,DRI标志位
3--4Byte: 00 04 ,固定值为4,表示DRI段外的段长度
5--6Byte: n ,取值为n,代表每n个MCU块就有一个RSTn标记,第一个标记是RST0,第二个是RST1,RST7之后再从RST0开始重复;如果没有本标记段,或者间隔值为0,就表示不存在重开始间隔和标记RST;
00000280: F6 F7 F8 F9 FA FF DA 00 0C 03 01 00 02 11 03 11
00000290: 00 3F 00 FD 32 D0 6C 30 57 FC 2B B4 D0 34 FC AD
SOS | 0xFFDA | Start Of Scan 扫描开始 |
1--2Byte: FF DA ,SOS标志位
3--4Byte: 00 0C ,十进制为12,SOS段外的段长度
5Byte: 03 ,只有3个可选值,1表示灰度图,3表示YCrCb,4表示CMYK
6Byte: 01 ,颜色分量id
7Byte: 00 ,直流/交流系数表id,高四位表示直流分量的哈夫曼表id,低四位表示交流分量的哈夫曼表id,这里表示id是0的直流(DC)表和id是0的交流(AC)表
第7字节、第8字节的表示颜色分量信息,应该重复出现,有几个颜色变量就出现几次
8Byte: 02 ,颜色分量id
9Byte: 11 ,这里表示id是1的直流表和id是1的交流表
10Byte: 03 ,颜色分量id
11Byte: 11 ,这里表示id是1的直流表和id是1的交流表
12Byte: 00 ,谱选择开始,固定值0x00
13Byte: 3F ,谱选择结束,固定值0x3F
14Byte: 00 ,谱选择,固定值0x00
之后都是压缩数据字段,直到遇到EOI标记结束
EOI | 0xFFD9 | End Of Image 图片结束 |
1--2Byte: FF D9 ,EOI标志位