Exif 格式
JPEG 文件格式的基本结构如图 1 所示,由各个段组成。其中的 DQT、DHT、SOF、SOS 段已经在文章 写一个简易的 JPEG 解码器 中介绍过了。还未介绍的 APP 段是用来存储各种应用数据的,本篇文章介绍其中的一种应用数据 —— Exif,它存储在 APP1 段。
Exif 是 Exchangeable image file format 的缩写,即“可交换图像文件格式”。它是专门为数码相机照片设定的文件格式,可以记录照片的各种元数据。Exif 的大致结构如图 1 的右边所示,后续会详细介绍。
Exif 格式基于 TIFF 格式(Tagged Image File Format,标签图像文件格式)。以下是 Exif 和 TIFF 的标准文档:
本篇文章虽然只基于 Exif 格式铺开,但是从标准文档中也可以看出,JPEG、Exif、TIFF 已经是“互存”的关系了,每篇文档里都会对彼此花上不少篇幅的介绍。
APP1
APP1 标记段的结构如图 2 所示,首先是 2 字节的标记符——0xFF 0xE1,代表是 APP1 段。接着是 2 字节的长度域,表示该段的总长度(包括此域自身,不包括标记符)。这些是 JPEG 标准文档中规定的内容。
再往后是 ascii 码—— Exif\0\0,称作 Exif 标识码,作用是为了防止与其他的应用冲突,起到标识的作用。
再往后就是 Exif 的真正内容了,因为其基于 TIFF 格式,所以下一节开始介绍 TIFF 格式。
TIFF
在说明 TIFF 结构 3 之前,先说明一下相关的术语:一条记录(或一个参数)记录在一个 Entry 里面。多个 Entry 组成一组,称为 Image File Directory (IFD)。
TIFF 的总体结构如图 3 所示,头部结构中指示第一个 IFD,IFD 中存储了各个 Entry。IFD 的尾部指示下一个 IFD,以类似链表的方式连接在一起。单个 Entry 的结构在图 3 的右侧,后续会详细说明各个字段。
头部
TIFF 的头部由 8 个字节组成,依次如下:
Bytes 0-1 : 指明 TIFF 数据的字节端序。"II" 表示小端序;"MM" 表示大端序。
Bytes 2-3 : 值为 42。指示是一个 TIFF 文件。有点类似魔数的概念。注意字节端序从现在已经“生效”了。
Bytes 4-7 : 第一个 IFD 的偏移,单位为字节。偏移是以 TIFF 文件为参照的,文件第一个字节(即这边介绍的头部)的偏移是 0。
TIFF 中的偏移基于 TIFF 头部,并未基于之前所说的 APP1 段。
IFD
IFD 由 3 部分组成:首先是 2 字节指明 Entry 的数量;接着是指定数量的 Entry,每个 Entry 占 12 字节;最后是 4 字节,指示下一个 IFD 的偏移,如果没有下一个 IFD 则为 0。
如果没有下一个 IFD,最后 4 字节为 0x00000000。
Entry
一个 IFD Entry 占 12 字节,布局如下:
Bytes 0-1 : Tag 值,指明值的含义。
Bytes 2-3 : Type 值,指明值的类型。
Bytes 4-7 : Count 值,指明值的个数。注意这边的单位是“个”,实际存储的大小依据值的类型。比如值为一个 16bit 的字(SHORT),则 Count 值为 1,不是 2。
Bytes 8-11 : 值的偏移或者值本身(Value/Offset)。如果值占的总字节数不大于 4,则值直接放在此字段。否则此字段存放的是值所在的偏移。
Tags
Entry 中的 Tag 字段指明值的含义,具体什么值对应什么含义,这在标准文档中都有定义。因为目前自己只对 Exif 信息和缩略图感兴趣,所以后续我们重点关注一下和它们相关的 Tag。
Exif IFD
Exif IFD 存储了一组 Tag,它记录了 Exif 特有的属性信息。Exif IFD 是由一个 Exif 私有 Tag (34665)指定的,其 Value/Offset 字段指定了其从 TIFF 头部开始的偏移地址。Entry 定义如下:
- Tag = 34665(8769.H)
- Type = LONG
- Count = 1
- Default = None
简单的说,这个 Tag 信息就是指示 Exif IFD 的“指针”。IFD 结构与上文介绍的一致。
缩略图
缩略图可以支持 RGB、YUV 和 JPEG 格式。由于手头的相机图片缩略图都是以 JPEG 存储的,所以这边只说明 JPEG 缩略图的存储方式。其结构如图 4 所示,可以看到 JPEG 缩略图就是以标准的 JPEG 格式进行存储的。由此也可以发现一些好处,像一些宽高、采样率的信息就不必存储在额外的 Tag 中了(自包含在 JPEG 格式中),只需告诉我们缩略图存储的地方和大小就可以。
我们使用 Compression 来区分缩略图是否使用 JPEG 压缩。如果缩略图使用 JPEG 压缩,这个 Tag 值是 6:
- Tag = 259(103.H)
- Type = SHORT
- Count = 1
- Default = None
- 6 = JPEG compression(thumbnails only)
- Other = reserved
JPEG 缩略图存储的地方使用 JPEGInterchangeFormat 记录:
- Tag = 513(201.H)
- Type = LONG
- Default = None
JPEG 缩略图的长度使用 JPEGInterchangeFormatLength 记录:
- Tag = 514(202.H)
- Type = LONG
- Default = None
提取出来的 JPEG 缩略图数据,直接重命名为 .jpg 后缀就可以查看。
1 《Exif standard version 2.3》 : 4.5.4 Basic Structure of JPEG Compressed Data
2 《Exif standard version 2.3》 : 4.7.2 B APP1 Interoperability structure
3 《TIFF Revision 6.0》 : Section 2 TIFF Structure
4 《Exif standard version 2.3》 : 4.5.8 Basic Structure of Thumbnail Data