TIFF 格式
Tagged Image File Format
文件头结构
TIFF 是图片元数据的老祖宗。你在 JPEG 里看到的 EXIF 数据, 其实就是一段嵌在文件里的 TIFF 结构。搞懂 TIFF, 也就搞懂了大多数图片元数据的运作方式。
这个格式以一个 8 字节的文件头开场,它告诉你两件关键的事: 字节序(Intel 还是 Motorola),以及第一个 IFD 在哪里。 从这里开始,剩下的就全是顺着指针跳来跳去了。
TIFF 文件结构
TIFF 文件头
| Offset | Hex | ASCII | |||||||
|---|---|---|---|---|---|---|---|---|---|
| 0000 | 49492A0008000000 | II*..... | |||||||
字节序
49 49(II)Intel:小端序(低位字节在前)
4D 4D(MM)Motorola:大端序(高位字节在前)
IFD 结构
IFD 是 Image File Directory 的缩写,你可以把它当成一份目录。 它是一张扁平的标签列表,每个标签指向一块数据。妙的地方在于, IFD 还能指向别的 IFD,从而形成一棵树。
IFD 的布局
标签条目的结构(12 字节)
如果数据能塞进 4 字节,就直接存在值字段里;否则值字段存的是指向数据的偏移量。
标签类型
TIFF 是强类型的。每个标签不仅说明自己装着什么,还说明这些字节该怎么解释。 所以要正确解析 TIFF,就必须先搞懂它的类型系统。
| ID | 名称 | 大小 | 说明 |
|---|---|---|---|
| 1 | BYTE | 1 | 8 位无符号整数 |
| 2 | ASCII | 1 | 以空字符结尾的字符串(8 位) |
| 3 | SHORT | 2 | 16 位无符号整数 |
| 4 | LONG | 4 | 32 位无符号整数 |
| 5 | RATIONAL | 8 | 两个 LONG:分子/分母 |
| 6 | SBYTE | 1 | 8 位有符号整数 |
| 7 | UNDEFINED | 1 | 原始字节(二进制数据) |
| 8 | SSHORT | 2 | 16 位有符号整数 |
| 9 | SLONG | 4 | 32 位有符号整数 |
| 10 | SRATIONAL | 8 | 两个 SLONG:有符号的分子/分母 |
| 11 | FLOAT | 4 | IEEE 32 位浮点数 |
| 12 | DOUBLE | 8 | IEEE 64 位双精度浮点数 |
| 13 | IFD | 4 | 指向嵌套 IFD 的指针 |
值的存放规则
如果 大小 × 数量 ≤ 4 字节,值就直接存在那 4 字节的值字段里; 否则该字段存的是一个偏移量,指向数据在文件中的实际位置。
IFD 偏移量的计算
解析 TIFF 最难的部分是跟着偏移量一路追下去。所有偏移量都相对于文件开头, 你得来回跳很多次。计算方式是这样的:
IFD0_offset = bytes[4:8] // 来自 TIFF 文件头
next_IFD = IFD0_offset + 2 + (entry_count × 12) + 4
subIFD_offset = tag_value // 来自指针标签(0x8769、0x8825)
重要的标签
图像数据标签(会保留)
0x0100:ImageWidth0x0101:ImageLength0x0102:BitsPerSample0x0103:Compression0x0111:StripOffsets0x0117:StripByteCounts元数据标签(会清除)
0x010F:Make(相机厂商)0x0110:Model(相机型号)0x0132:DateTime0x013B:Artist0x8298:Copyright0x9003:DateTimeOriginal指针标签(子 IFD)
0x8769:ExifIFDPointer0x8825:GPSInfoIFDPointer0xA005:InteroperabilityIFDPointer子 IFD
主 IFD(IFD0)里常常有一些“指针标签”,指向各个子目录。 EXIF、GPS 和其他元数据就是这样被分成一组组逻辑单元, 同时让主 IFD 保持清爽。
EXIF 子 IFD
存放相机相关的元数据:曝光、光圈、ISO、焦距、闪光灯、白平衡等等。
GPS 子 IFD
存放地理位置数据:纬度、经度、海拔、时间戳、卫星、速度、方向。 隐私上的头号问题。
Maker Notes
相机厂商的私有数据,格式各家不同, 里面可能有序列号、固件版本和自定义设置。
PicScrub 如何处理 TIFF
解析文件头
判断字节序(II/MM),并找到第一个 IFD
建立标签白名单
找出渲染图像所必需的标签
过滤 IFD 条目
删除元数据标签,把子 IFD 的指针清零
重写文件
用干净的 IFD 和原样复制的图像数据重新组装 TIFF
偏移量的重新计算
TIFF 文件大量依赖文件内的偏移量。清除元数据之后, PicScrub 必须重新计算所有偏移量,才能保证文件仍然完整可用。