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 | - |
【未完....喂碗土豆儿后待续】