理解JPEG文件头的格式
来源:互联网 发布:ipad怎么开淘宝店铺 编辑:程序博客网 时间:2024/05/16 07:07
1. JPEG
1)why jpeg?
jpeg作为图片传输格式使用最为普遍,压缩比最高。每天我们都会产出和传输大量的jpeg格式数据。手机拍出来的格式默认是jpeg,朋友圈各种分享。。。磁盘上积累了大量的jpeg。。。
因此本人一直对jpeg头部数据非常好奇,想着有时间深入一下jpeg格式,看看头部到底存储了哪些数据?记得研究生时有专门的信息隐藏专业,基本原理可能是保持jpeg现有格式框架下插入一些自己的二进制数据。。。
本人只是对jpeg头部尝试做一些解释,至于jpeg核心的压缩技术涉及DCT、哈夫曼编码,过于深奥理解起来有难度。。。下面这些内容,都是最近两天查了各种资料以后的一些综合认识,先全部写出来,讲解和示例代码都比较粗糙,后续有时间再继续完善。
2)怎么决定文件是否是jpeg格式?
二进制形式打开文件,文件开始字节为FF D8,文件结束两字节为FF D9。则初步判定文件为jpeg。
jpeg的SOI(start of image) 为ff d8,EOD(end of image)为ff d9
3)文件头采用何种格式存储?
The marker 0xFFE0~0xFFEF is named "Application Marker", not necessary for decoding JPEG image. They are used by user application. For example, older olympus/canon/casio/agfa digicams use JFIF(JPEG File Interchange Format) for storing images. JFIF uses APP0(0xFFE0) Marker for inserting digicam configuration data and thumbnail image.
Also Exif uses an Application Marker for inserting data, but Exif uses APP1(0xFFE1) Marker to avoid a conflict with JFIF format. Every Exif file formats starts from this format;
大概意思是:老式相机采用JFIF格式,即以FF E0开始,头部含有 .. JFIF...信息。现在Exif更加流行,Exif以FF E1字节开始。
不管JFIF还是Exif格式都类似:
0xFF + Marker Number(1 byte) + Data size(2 bytes) + Data(n bytes)JFIF的marker为E0,Exif的marker为E1,maker后面两个字节是长度(包含长度两个字节),长度后面是数据(数据的为长度为长度-2)。
找了一张在照相馆拍的一寸照,邮件打开里面同时有JFIF段和Exif段:
图-1
蓝色框内为JFIF段,长度字节流为00 10 = 16,后面14个字节为内容。4A 46 49 46是JFIF的asci码。后面10个字节不清楚。直接通过JFIF长度跳过即可。
红色箭头开始位置为Exif段,头部的10个字节含义如下:
FF E1开始,16 6F = 24096,约24k的数据,信息量还是相当丰富的,45 78 69 66 为Exif的asci码流,00 00 未使用
4)Exif的数据部分如何组织?
数据部分采用TIFF格式,TIFF格式参见refer[2],IFD(Image File Directory)
TIFF主要包含IFD0和IFD1两部分,IFD0有两部分组成:IFD的目录部分,以及Link to LFD1的部包(一个32bit的偏移量)。IFD的目录的每个记录指向一个IFD entry,每个IFD内部可能嵌套包含一系列IFD。。IFD1的结构类似。
FFE1APP1 MarkerSSSSAPP1 DataAPP1 Data Size45786966 0000Exif Header49492A00 08000000TIFF HeaderXXXX. . . .IFD0 (main image)DirectoryLLLLLLLLLink to IFD1XXXX. . . .Data area of IFD0XXXX. . . .Exif SubIFDDirectory00000000End of LinkXXXX. . . .Data area of Exif SubIFDXXXX. . . .IFD1(thumbnail image)Directory00000000End of LinkXXXX. . . .Data area of IFD1FFD8XXXX. . . XXXXFFD9Thumbnail image图-2
图-2为TIFF的格式框架图,对应图1中,49 49 2A 00 08 00 00 00 为TIFF的头部,4949表示II小端存储,2A 00 为约定的常量数值,08 00 00 00 为TIFF数据相对TIFF开始位置的offset,头部为8个字节所以这个值一般为8,该offset用4字节表示,理论上可以是文件任何地方。
接下来0A 00 为IFD数据开始位置(也可以通过TIFF的头部开始位置加上offset of 0th IFD),两个字节0A 00,小端模式下为10,即Number of Directory Entries = 10。
每个IFD Entry(索引)有12个字节组成,图-2右子图,
- tag[0:2], tag是一些预定的标签
- type[2:4], type表示数据类型,1是BYTE,2是ASCII,3是SHORT等
- count[4:8], count是长度
- value[8:12],当count<=4时候,value里面存储的就是内容,否则value是一个offset值,指向内容的位置,结合count和type解析得到内容(这里的内容有可能是IFD Entry数组,即嵌套包含了IFD)。
length = 5743exif = b'Exif\x00\x00'b'Exif'b'II'tiff_tag = 42, tiff_off = 8number of directory entries = 10==> OFFSET 8 - 1232tag=271,type=2,count=6,value=134 actual_data = Canon tag=272,type=2,count=16,value=140 actual_data = Canon EOS 1100D tag=274,type=3,count=1,value=1tag=282,type=5,count=1,value=156tag=283,type=5,count=1,value=164tag=296,type=3,count=1,value=2tag=305,type=2,count=27,value=172 actual_data = Adobe Photoshop CS Windows tag=306,type=2,count=20,value=199 actual_data = 2013:06:05 17:25:16 tag=531,type=3,count=1,value=2tag=34665,type=4,count=1,value=220number of directory entries = 6==> OFFSET 1232 - 0tag=259,type=3,count=1,value=6tag=282,type=5,count=1,value=1310tag=283,type=5,count=1,value=1318tag=296,type=3,count=1,value=2tag=513,type=4,count=1,value=1326tag=514,type=4,count=1,value=4409-------------------------------------网上较全面的解析jpeg Exif的python 代码:
2. 示例
00: ff d8 ff e1 02 62 45 78 69 66 00 00 4d 4d 00 2a
10: 00 00 00 08 00 08 01 0f 00 02 00 00 00 04 48 54
20: 43 00 01 10 00 02 00 00 00 0a 00 00 00 6e 01 1a
30: 00 05 00 00 00 01 00 00 00 78 01 1b 00 05 00 00
40: 00 01 00 00 00 80 01 28 00 03 00 00 00 01 00 02
50: 00 00 02 13 00 03 00 00 00 01 00 01 00 00 87 69
60: 00 04 00 00 00 01 00 00 00 88 88 25 00 04 00 00
70: 00 01 00 00 01 60 00 00 00 00 44 65 73 69 72 65
80: 20 48 44 00 00 00 00 48 00 00 00 01 00 00 00 48
90: 00 00 00 01 00 0b 88 27 00 03 00 00 00 01 00 88
a0: 00 00 90 00 00 07 00 00 00 04 30 32 32 30 90 03
b0: 00 02 00 00 00 14 00 00 01 12 90 04 00 02 00 00
c0: 00 14 00 00 01 26 91 01 00 07 00 00 00 04 01 02
d0: 03 00 92 0a 00 05 00 00 00 01 00 00 01 3a a0 00
e0: 00 07 00 00 00 04 30 31 30 30 a0 01 00 03 00 00
f0: 00 01 00 01 00 00 a0 02 00 04 00 00 00 01 00 00
00: ff d8 ff e1 02 62 45 78 69 66 00 00
ff d8: SOI (start of image) – JPEG files always start with this
ff e1 Exif头部开始标志,
02 62:Exif数据长度,2*256+6*16+2 = 610
45 78 69 66 :Exif字符串的asci码
00 00 两个字节保留
Exif数据部分采用TIFF格式存储,参考refer[3],TIFF数据的开始于0C即第12个字节位置。
TIFF Image File Header –8 bytes
0C: 4d 4d 00 2a 00 00 00 08
Bytes 0-1 = 4d 4d = "MM" Big Endian Order"
Bytes 2-3 = 00 2a = 42 = Id as TIFF File
Bytes 4-7 = 8 = offset in bytes of 0th IFD
0C + offset 开始读取IFD数据==>
0th IFD (1st IFD) (2 bytes long)
14: 00 08
Bytes 0-1 = Number of entries = 8,一共8个IFD索引。
1st Tag in 0th IFD (12 bytes long)
16: 01 0f 00 02 00 00 00 04 48 54 43 00
Bytes 0-1 (Tag ID) = 01 0f = 271 = Make
Bytes 2-3 (Tag Type) = 00 02 = 2 = Ascii
Bytes 4-7 (Count) = 00 00 00 04 = 4 = 4 Characters
Bytes 8-11 (Value) = 48 54 43 00 = "HTC\0" – tag value is here (not offset)
2nd Tag in 0th IFD (12 bytes long)
22: 01 10 00 02 00 00 00 0a 00 00 00 6e
Bytes 0-1 (Tag ID) = 01 10 = 272 = Model
Bytes 2-3 (Tag Type) = 00 02 = 2 = Ascii
Bytes 4-7 (Count) = 00 00 00 0a = 10 = 10 Characters
Bytes 8-11 (Value) = 00 00 00 6e = Offset = 6e = 110 Bytes
- This means the value is an Ascii string (length 10 starting at 122 Bytes = 0x78)
78: 44 65 73 69 72 65 29 48 44 00 = "Desire HD\0"
122的偏移是这么算出来的:0C + 110 = 122 = 0x78
3. refer
- 理解JPEG文件头的格式
- Bitmap的文件头格式
- 如何判断图形文件的格式是否是JPEG呢?
- WAV文件的头信息(文件头格式)
- mybatis的映射文件的头格式
- hibernate映射描述文件头的格式
- DLL头文件的格式和应用
- pcap文件头的组织格式
- DLL头文件的格式和应用
- BMP文件头格式
- 头文件标准格式
- 头文件标准格式
- JPEG的文件结构
- Jpeg的文件信息
- JPEG文件编码格式说明_新
- 对预编译头文件的理解
- 一些关于头文件的理解
- 头文件和库的理解
- 命令解析器(基础循环)
- 数据类型和Json格式
- error: exception handling disabled, use -fexceptions to enable
- mysql函数大全
- java jsp+servlet+mysql实现登录网页设计
- 理解JPEG文件头的格式
- 算法练习1
- SSH实现无密码访问--以centOS为例
- oracle 常用的闪回操作
- php文件的跨域上传方式之一 CURL
- java 字符串转成 json 数组并且遍历
- Java 8 简明教程
- 很无聊 但实用的小例子:C#获取类的成员变量名及对其赋值
- 2014-11-18