实验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 0100 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,0211 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 0101 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 0506 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 0506 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 0302 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 0512 21 31 41 06 13 51 61 07 22 71 14 32 8191 A1 08 23 42 B1 C1 15 52 D1 F0 24 33 62 72 8209 0A 16 17 18 19 1A 25 26 27 28 29 2A 34 35 3637 38 39 3A 43 44 45 46 47 48 49 4A 53 54 55 5657 58 59 5A 63 64 65 66 67 68 69 6A 73 74 75 7677 78 79 7A 83 84 85 86 87 88 89 8A 92 93 94 9596 97 98 99 9A A2 A3 A4 A5 A6 A7 A8 A9 AA B2 B3B4 B5 B6 B7 B8 B9 BA C2 C3 C4 C5 C6 C7 C8 C9 CAD2 D3 D4 D5 D6 D7 D8 D9 DA E1 E2 E3 E4 E5 E6 E7E8 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标志位