
Exif 标准是由JEIDA制定的,但目前网络上还没有公开的文档资料。此文档由TsuruZoh Tachibanaya无偿贡献。
什么是Exif文件格式
通常来说,Exif文件格式和JPEG文件格式是等价的。Exif基于JPEG标准向JPEG文件格式中插入了一些图片信息和产生图片的设备(相机、制图软件等)的信息。
JPEG格式和标识符
所有的JPEG图片都是以0xFF 0xD8作为文件开头,以0xFF 0xD9作为文件结尾。这些0xFF 0x??形式的字节内容,我们将其称为标识符,它标记了JPEG信息的数据段。其中0xFF为标识符前缀,后面跟随的0x??就是标识符定义了。
0xFF 0xD8被定义为SOI(Start of image),图片起始点。
0xFF 0xD9被定义为EOI(End of image),图片截止点。
0xFF 0xDB被定义为DQT(Quantization Table),量化表。
0xFF 0xC4被定义为DHT(Huffman Table),霍夫曼编码表。
0xFF 0xDD被定义为DRI(Restart Interval),图片内容定位点。
0xFF 0xC0被定义为SOF(Start of frame),帧头。
0xFF 0xDA被定义为SOS(Start of scan),扫描头。
这两个特殊的标识符后是不会跟随数据的,但其他的标识符后面都会跟随一段数据。
基本的数据段格式如下:
0xFF + 标识符定义(1个字节) + 数据段长度(2个字节) + 数据(n个字节)
- 其中的“数据段长度”是个整型,字节序为大端序(Big endian)
- 数据段的长度是包含了“数据段长度”所占用的2个字节的,所以
真正的数据的长度 = 数据段长度 - 2
如:0xFF 0xC1 0x00 0x0C
标识符为0xFF 0xC1,其后跟随的数据段长度为0x00 0x0C(即12个字节),但真正去除了0xFF 0xC1 0x00 0x0C的数据只有10个字节
在JPEG文件格式中,SOI(图片起始:0xFF 0xD8)标记后,还可以定义很多个数据段,这些数据段的格式都如上所述,一个接着一个排列在SOI之后。所有的数据段结束后,跟随其后的就是SOS(Start of stream,数据流起始点)、图片数据和EOI(图片截止点)。
JPEG文件格式的基本组成如下:
SOI + 数据段1 + 数据段2 + …… + 数据段n + DQT + DHT + (DRI) + SOF + SOS + 图片数据 + EOI
JPEG的固定标识符
从0xFF 0xE0~0xFF 0xEF的标识符都是JPEG定义的固定标识符,这个区段的标识符被称为“应用标识(APPn)”,它们定义了一系列用于存储不同种类信息的应用数据段,这些信息都是用户应用程序所需的一些图像、设备、自定义信息。n从0开始,到15截止,也就是说APP数据段一共有16个。
同时,它们也是JPEG格式中非必须的数据,也就是说,就算JPEG文件里没有它们,JPEG也一样能被正常显示和使用。
例如,老式的Olympus、Canon、Casio、Agfa数码相机使用JIFI(JPEG file interchange format)格式文件来存储拍摄的照片,而JIFI使用APP0(0xFF 0xE0)数据段来存储数码相机的配置信息和照片缩略图。
| 标识符 | 应用标识名 | 用途 |
|---|---|---|
| 0xFF 0xE0 | APP0 | 存储JFIF文件格式的配置信息和图片缩略图 |
| 0xFF 0xE1 | APP1 | 存储EXIF信息 |
| 0xFF 0xE2 | APP2 | - |
| 0xFF 0xE3 | APP3 | - |
| 0xFF 0xE4 | APP4 | - |
| 0xFF 0xE5 | APP5 | - |
| 0xFF 0xE6 | APP6 | - |
| 0xFF 0xE7 | APP7 | - |
| 0xFF 0xE8 | APP8 | - |
| 0xFF 0xE9 | APP9 | - |
| 0xFF 0xEA | APP10 | - |
| 0xFF 0xEB | APP11 | - |
| 0xFF 0xEC | APP12 | - |
| 0xFF 0xED | APP13 | - |
| 0xFF 0xEE | APP14 | - |
| 0xFF 0xEF | APP15 | - |
所以JPEG文件的格式定义可以认为是:
SOI + [APP0] + [APP1] + …… + [APP15] + SOS + 图片数据 + EOI
同样的,Exif格式也是使用APP数据段在JPEG格式文件中插入数据的。为了避免和JIFI冲突,Exif使用APP1(0xFF 0xE1)数据段来存储信息。
Exif格式定义
因为Exif是存储于一个JPEG的APP数据段中,所以Exif数据还是会包装在APP数据段格式中,如下:
SOI + [APP0] + [0xFF 0xE1 0x?? 0x?? + EXIF] + …… + [APPn] + SOS + 图片数据 + EOI
- 其中
0x?? 0x??为APP1数据段的长度
Exif的数据格式为TIFF格式,具体可参考Adobe公司编写的TIFF 6.0 标准文档。
在APP1数据段中包含的Exif信息主要有如下组成部分:
| 标识符 | 数据内容 |
|---|---|
| 0xFF 0xE0 | APP1数据段标识 |
| 0x?? 0x?? | APP1数据段长度(2字节大端序(BigEndian)表示) |
| 0x45 0x78 0x69 0x66 0x00 0x00 | Exif标识头(内容为ASCII码的"Exif\0\0") |
| 0x49 0x49 0x2A 0x00 | TIFF标识头(小端序版本) |
| 0x4D 0x4D 0x00 0x2A | TIFF标识头(大端序版本),TIFF标识头只会存在一个版本 |
| 0x?? 0x?? 0x?? 0x?? | 下一个IFD的地址偏移量 |
| 0x?? ........ 0x?? | IFD0(图片信息索引)的条目列表 |
| 0x?? 0x?? 0x?? 0x?? 0x?? 0x?? 0x?? 0x?? | 下一个IFD的地址偏移量 |
| 0x?? ........ 0x?? | IFD0条目的数据存放区域 |
| 0x?? ........ 0x?? | Exif的条目列表 |
| 0x?? 0x?? 0x?? 0x?? 0x?? 0x?? 0x?? 0x?? | 下一个IFD的地址偏移量 |
| 0x?? ........ 0x?? | Exif条目的数据存放区域 |
| 0x?? ........ 0x?? | IFD1(缩略图信息索引)的条目列表 |
| 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 | 下一个IFD的地址偏移量(为0则代表不再有IFD节点) |
| 0x?? ........ 0x?? | IFD1条目的数据存放区域 |
| 0xFF 0xD8 0x?? ........ 0x?? 0xFF 0xD9 | 缩略图JPEG内容 |
TIFF格式定义
TIFF标识头
虽然JPEG格式的内容只使用大端字节序来存储数据,但Exif所采用的TIFF格式则可以选择大端字节序(Big Endian)或小端字节序(Little Endian)来存储数据。
TIFF标识头的格式如下:
字节序标识(2个字节) + 字节序示例(2个字节) + 第一个IFD的地址偏移(4个字节)
- 字节序标识:用于标记字节序
-
0x49 0x49为ASCII码"II"的字符内容,它表示TIFF采用了以Intel为代表小端字节序 -
0x4D 0x4D为ASCII码"MM"的字符内容,它表示TIFF采用了以Motorola为代表大端字节序
-
- 字节序示例:用于展示整型
42在不同字节序下的表示方式-
0x2A 0x00为小端序表示法 -
0x00 0x2A为大端序表示法
-
- 第一个IFD的地址偏移, 如果TIFF标识头之后紧跟第一个IFD,那么这里记录的偏移量就是8
-
0x08 0x00 0x00 0x00为小端序整型8的表示 -
0x00 0x00 0x00 0x08为大端序整型8的表示
-
IFD(Image file directory)图片信息索引
IFD图片信息索引的内容包含:
信息条目的数量(2个字节) + 条目1(2个字节) + ...... + 条目n(12个字节) + 下一个IFD的地址偏移(4个字节)
IFD的条目由包含下面4个部分:
条目标识(2个字节) + 数据类型(2个字节) + 数值精度(4个字节) + 数值或数值的相对地址(4个字节)
- 条目标识用来标记这个条目的名称,条目名称对照表详见章节“TIFF标识”
- 数据类型用来标记这个条目的数值的数据类型,TIFF定义了12种数据类型,数据类型对照表详见章节“TIFF标识”
- 数值精度跟数据类型是对应的,不同数据类型有不同的数据精度,它表示一个数值占用的字节长度,从1个字节到8个字节不等,数值精度对照表详见章节“TIFF标识”
- 当数据能被4个字节表示,则此处存放的为数值。如果数据的长度超过了4个字节,那么此处则存放一个相对地址,用于指示数据被存放在IFD条目的数据存放区域中的位置
IFD缩略图
缩略图一般放在APP1中的IFD1之后,缩略图可以有3种格式来存储:
TIFF标识
IFD标识
| 标识 | IFD名称 | 数据类型 | 数据长度 | 描述 |
|---|---|---|---|---|
| 0x01 0x0E | ImageDescription | ascii string | ∞ | 图片描述 |
| 0x01 0x0F | Make | ascii string | ∞ | 拍摄图片的设备制造商 |
| 0x01 0x10 | Model | ascii string | ∞ | 拍摄图片的设备型号 |
| 0x01 0x12 | Orientation | unsigned short | 1 | 图片朝向:详见下表 |
| 0x01 0x1A | XResolution | unsigned rational | 1 | 横向分辨率,每单位长度上的像素点数量 |
| 0x01 0x1B | YResolution | unsigned rational | 1 | 纵向分辨率,每单位长度上的像素点数量 |
| 0x01 0x28 | ResolutionUnit | unsigned short | 1 | 分辨率单位,1为无单位,2为英寸,3为厘米 |
| 0x01 0x31 | Software | ascii string | ∞ | 拍摄图片的设备固件版本号 |
| 0x01 0x32 | DateTime | ascii string | 20 | 图片拍摄日期:"YYYY:MM:DD HH:MM:SS\0" |
| 0x01 0x3E | WhitePoint | unsigned rational | 2 | 图片中高光的色温定义 |
| 0x01 0x3F | PrimaryChromaticities | unsigned rational | 6 | 图片的色度均衡定义 |
| 0x02 0x11 | YCbCrCoefficients | unsigned rational | 3 | 如果图片颜色空间为YCbCr,则它表示RGB转换到亮度时,RGB分量的比例关系,默认为“0.299/0.587/0.114” |
| 0x02 0x13 | YCbCrPositioning | unsigned short | 1 | 如果图片颜色空间为YCbCr且使用降采样时,则它表示子像素阵列的色度采样方式,1为中心点采样,2为基准点采样 |
| 0x02 0x14 | ReferenceBlackWhite | unsigned rational | 6 | 图片中黑色和白色的参考值 |
| 0x82 0x98 | Copyright | ascii string | ∞ | 版本信息 |
| 0x87 0x69 | ExifOffset | unsigned long | 1 | 到Exif IFD的地址偏移量 |
| 0x90 0x03 | DateTimeOriginal | ascii string | 20 | 图片拍摄日期,同DateTime(0x01 0x32) |
| 朝向枚举值 | 旋转翻转(顺时针旋转) |
|---|---|
| 1 | 0° |
| 2 | 0° + 水平翻转 |
| 3 | 180° |
| 4 | 180° + 水平翻转 |
| 5 | 270° + 水平翻转 |
| 6 | 270° |
| 7 | 90° + 水平翻转 |
| 8 | 90° |
| 9 | 未定义 |
IFD数据类型
| 标识 | 数据类型 | 数值精度(每个数值占用的字节数) | 备注 |
|---|---|---|---|
| 0x01 | unsigned byte | 1 | - |
| 0x02 | ascii strings | 1 | - |
| 0x03 | unsigned short | 2 | - |
| 0x04 | unsigned long | 4 | - |
| 0x05 | unsigned rational | 8 | 分数类型,前4个字节为分子,后4个字节为分母 |
| 0x06 | signed byte | 1 | - |
| 0x07 | undefined | 1 | - |
| 0x08 | signed short | 2 | - |
| 0x09 | signed long | 4 | - |
| 0x0A | signed rational | 8 | 分数类型,前4个字节为分子,后4个字节为分母 |
| 0x0B | signed float | 4 | - |
| 0x0C | signed double | 8 | - |
【未完....喂碗土豆儿后待续】