1. 概述
TIFF,英文全称为 Tag Image File Format,中文译为_标签图像文件格式_,是一种灵活的位图格式,以.tif
或者.tiff
为扩展名,主要用来存储包括照片和艺术图在内的图像,它与 JPEG、PNG 一起成为流行的高位彩色图像格式。TIFF 文件使用 32 位偏移量,因此一个 TIFF 文件最大为 4GB(即:2**32 字节)。
TIFF 作为一种标记语言,与其他文件格式最大的不同在于:除了图像数据以外,它还可以记录很多图像的其他信息,而且记录方式也比较灵活。理论上讲,任何其他的图像格式都能为 TIFF 所用,嵌入到 TIFF 里面。由于 TIFF 的扩展性,在数字影像、遥感、医学等领域得到了广泛的应用。
2. 文件结构
特殊说明:如果没有特别的标记,后续十六进制均需要转为十进制值。
TIFF 文件使用一种3级体系结构,即:IFH(Image File Header,图像文件头)、IFD(Image File Directory,图像文件目录)和 DE(Directory Entry,目录项)。
2.1 IFH
每一幅 TIFF 图像都以 IFH 开头,IFH 占用 8 字节空间,具体内容为:
- 字节0-1 :Word 类型(无符号短整型,占用2字节),表示文件的字节序,可选值为16进制
4949
和4D4D
,分别对应 II 和 MM。II,Little Endian,小端字节序,表示小字节在前,即:对于16位和32位整数,字节顺序始终从最低有效字节到最高有效字节;MM,Big Endian,大端字节序,表示大字节在前,即:对于16位和32位整数,字节顺序总是从最高有效字节到最低有效字节。 - 字节2-3 :标识当前文件为 TIFF 文件,标准 TIFF 文件对应16进制均为
002A
,十进制值为 42。 - 字节4-7 :第一个 IFD 的偏移量,即:第一个 IFD 在文件字节序列中的索引,它可以位于文件头后面任意位置,但必须以词边界开始,尤其是,第一个 IFD 可能在它所描述的图像数据的后面。
需要注意:
- TIFF 文件中所使用的偏移量,都是相对于文件头位置的偏移量;
- 偏移量必须是以词边界(Word 边界)开始,也就是说,所有偏移量必须是偶数。
2.1.1 如何理解字节顺序
详细描述可以查看阮一峰的博客
- 计算机硬件有 2 种存储数据的方式:大端字节序(Big Endian)和小端字节序(Little Endian)。
- 计算机处理字节序的时候,不知道什么是高位字节,什么是低位字节,只知道按照顺序读取字节。假设计算机硬件要存储数值
0x2211
(一个16进制数),它需要使用2个字节存储,其中22
表示高位字节、11
表示低位字节。如果使用大端字节序方式存储,则高位字节在前,低位字节在后;如果使用小端字节序,则低位字节在前,高位字节在后,即以0x1122
方式存储。 - 为什么会有字节顺序?答案是,计算机电路优先处理低位字节,效率比较高,因为计算都是从低位开始的,所以,计算机内部处理都是小端字节序。
- 字节序的处理,就是一句话,只有读取的时候,才必须区分字节序,其他情况都不用考虑,向外部设备写入数据时,也不用考虑字节序,外部设备自己会考虑。
2.2 IFD
IFD 是 TIFF 文件中非常重要的数据结构,它描述了 TIFF 文件的信息。一个 TIFF 文件可以包含多个 IFD,这种情况下,这个 TIFF 文件包含多个图像,一个 IFD 表示一个图像的属性信息。一个 TIFF 文件必须包含一个 IFD,一个 IFD 必须包含一个 DE。
IFD 内容也包括3部分,具体为:
- 字节0-1:表示当前 IFD 包含多少个 DE。
- 字节2-(B*12):DE 的详细描述,每个 DE 占用12个字节,所以共占用第2 - (B*12 + 1) 字节。
- **最后4个字节:**下一个 IFD 的偏移量,如果没有则设置为0。
2.3 DE
每个 DE 占用 12 个字节的空间,其中内容共有4个部分,具体为:
- 字节0-1:标识字段的 TAG。
- 字节2-3:字段类型。
- 字节4-7:字段值的数量。通过类型和数量可以确定存储当前字段所需要的字节大小。
- 字节8-11:如果占用的字节数少于 4,则数据直接存在于此。如果超过4个,那么这里存放的是指向实际数据的指针。
2.3.1 术语
TIFF 字段是一个逻辑实体,由 TIFF TAG 和其值组成,这个逻辑概念通过 DE 实现。在绝大多数上下文中,TIFF 字段和 DE 是一个意思。
2.3.2 顺序
IFD 中的 DE 顺序必须按照 TAG 升序排列。
2.3.3 TAG 标识符
TAG 标识符是 TIFF 中很重要的概念,它是 DE 的唯一标识符,详细说明详见附件第117页。 TIFF6.pdf
2.3.4 数据类型
在 TIFF 6.0 中,定义了12种数据类型,分别是:
- 1:BYTE,8位无符号整数
- 2:ASCII,8位字节,包含7为 ASCII 码,但最后一个字节必须为 NUL(二进制0)
- 3:SHORT,16位无符号整数,2字节
- 4:LONG,32为无符号整数,4字节
- 5:RATIONAL, 2个LONG,第一个代表分子,第二个代表分数的分母
- 6:SBYTE,8位有符号(二进制补码)整数
- 7:UNDEFINED, 一个 8 位字节,可能包含任何内容,具体取决于字段的定义
- 8:SSHORT, 16位(2字节)有符号(二进制补码)整数
- 9:SLONG,36位(4字节)有符号(二进制补码)整数
- 10:SRATIONAL,2个SLONG, 第一个代表分数的分子,第二个代表分数的分母
- 11:FLOAT,单精度(4字节)IEEE 格式
- 12:DOUBLE,单精度(8字节)IEEE 格式
对于数据类型的,额外的补充说明:
- 对于 ASCII 类型的数据,统计数据量时会包含 NUL 值;如果需要填充,统计数据量时不包括填充的字节。
- 对于 ASCII 类型的字段,它可以包含多个字符串,每个字符串都是以 NUL 结尾,TIFF 文件标准中不推荐使用多字符串。多字符串的字段计数是该字符串中所有的字节数,包含了 NUL 字节。
- 读取 TIFF 文件时必须检查数据类型用以验证该字段包含了所期望的值。TIFF 标准允许一些字段超过1个有效类型,比如,对于 ImageWidth 和 ImageLength 字段类型通常为 SHORT,但是对于行列超过 64K 的 TIFF 文件来说,它们的类型必须是 LONG。
3. 文件解析示例
3. 1 示例文件及代码说明
3. 1.1 示例代码获取
从 Gitee 上克隆示例代码,访问地址:gitee.com/shixxa/tiff...。安装 POM 依赖后,运行 Main.java 文件即可。
示例代码所用的内容及版本如下:
- JDK 1.8
- Maven 3.8.5
3.1.2 示例文件说明
示例文件存放在src/main/resource
文件中,名称为TIFF例子.tif
,是一个9行7列的数据,示意图如下:
3.2 IFH 解析结果分析
IFH 内容的打印结果如下:
objectivec
IFH中的字节顺序:4949
IFH中的标识符:2A00
IFH中第一个IFD的偏移量(十六进制,大端字节序):CA000000
根据第2.1内容分析可得:
- 十六进制的
4949
表示小端字节序,也就说后,后续所有的读取顺序与打印的顺序是相反的。 - 标识符为
2A00
,读取结果为002A
,对应的十进制值为 42,表示当前文件是一个标准的 TIFF 文件。 - 第一个 IFD 的偏移量为
CA000000
,读取结果为000000CA
,对应的十进制值为 202,表示第一个 IFD 的内容从读取结果的第 202 个字节位开始。
3.3 IFD 及 DE 解析结果分析
当前 TIFF 文件中只有一个 IFD。
3.3.1 IFD 中 DE 的数量
第一个 IFD 中的 DE 的数量打印结果如下:
第一个IFD中的DE的数量:0F00
对应的读取顺序为000F
,十进制值为15
,表示该 IFD 共有 15 个 DE。
3.3.2 IFD 中的 DE 解析结果
3.3.2.1 第0个DE
diff
-第0个DE的内容:
--DE中的TAG标识:FE00
--DE中的数据类型:0400
--DE中的数据数量:01000000
--DE中的ValueOffset:00000000
- TAG 的读取结果为
00FE
,十进制值为254,对应 NewSubfileType; - 数据类型的读取结果为
0004
,十进制值为4,根据2.3.2内容可知,对应的数据类型为 LONG; - 数据数量的读取结果为
00000001
,十进制值为1,表示共1条数据; - 数据值的偏移量读取结果为
00000000
,十进制值为0,根据 NewSubfileType 的描述,该图像不是其他图像的缩略图、单页图像(也就是只有一个IFD)并且是不透明的。
3.3.2.2 第1个DE
diff
-第1个DE的内容:
--DE中的TAG标识:0001
--DE中的数据类型:0400
--DE中的数据数量:01000000
--DE中的ValueOffset:07000000
- TAG 的读取结果为
0100
,十进制值为256,对应 ImageWidth; - 数据类型的读取结果为
0004
,十进制值为4,对应的数据类型为 LONG; - 数据数量的读取结果为
00000001
,十进制值为1,表示共1条数据; - 数据值的偏移量读取结果为
00000007
,十进制值为7,表示图像的宽度为7,这里表示文件宽为7个像素。
3.3.2.3 第2个DE
diff
-第2个DE的内容:
--DE中的TAG标识:0101
--DE中的数据类型:0400
--DE中的数据数量:01000000
--DE中的ValueOffset:09000000
- TAG 的读取结果为
0101
,十进制值为257,对应 ImageLength; - 数据类型的读取结果为
0004
,十进制值为4,对应的数据类型为 LONG; - 数据数量的读取结果为
00000001
,十进制值为1,表示共1条数据; - 数据值的偏移量读取结果为
00000009
,十进制值为9,表示图像的高度为9,这里表示文件高为9个像素。
3.3.2.4 第3个DE
diff
-第3个DE的内容:
--DE中的TAG标识:0201
--DE中的数据类型:0300
--DE中的数据数量:03000000
--DE中的ValueOffset:84010000
- TAG 的读取结果为
0102
,十进制值为258,对应 BitsPerSample,对应每个通道的位数; - 数据类型的读取结果为
0003
,十进制值为3,对应的数据类型为 SHORT; - 数据数量的读取结果为
00000003
,十进制值为3,表示共3条数据,即:图像有3个通道; - 数据值的偏移量读取结果为
00000184
,十进制值为388。SHORT 类型占用2个字节,该 DE 中共有3条 SHORT 类型的数据,共占用了6个字节,大于4个字节,因此该部分表示的数据值的偏移量,真实数据的字节位置为388-393。
该 DE 的数据打印结果如下:
第3个DE中BitsPerSample的值:080008000800
打印结果解析:
- 读取结果为
000800080008
,每个通道的像素深度的读取结果为0008
,十进制为8,也就说,每个通道的像素为8位深度,每个颜色通道的取值为0~255。
3.3.2.5 第4个DE
diff
-第4个DE的内容:
--DE中的TAG标识:0301
--DE中的数据类型:0300
--DE中的数据数量:01000000
--DE中的ValueOffset:05000000
- TAG 的读取结果为
0103
,表示 Compression,压缩属性; - 数据类型的读取结果为
0003
,对应的数据类型为 SHORT; - 数据数量的读取结果为
00000001
,表示共1条数据; - 数据值的偏移量读取结果为
00000005
,十进制为5,表示该数据是使用 LZW 压缩的。
3.3.2.6 第5个DE
diff
-第5个DE的内容:
--DE中的TAG标识:0601
--DE中的数据类型:0300
--DE中的数据数量:01000000
--DE中的ValueOffset:02000000
- TAG 的读取结果为
0106
,十进制值为262,对应 PhotometricInterpretation,表示颜色空间; - 数据类型的读取结果为
0003
,十进制值为3,对应的数据类型为 SHORT; - 数据数量的读取结果为
00000001
,十进制值为1,表示共1条数据; - 数据值的偏移量读取结果为
00000002
,十进制值为2,表示当前数据为 RGB 图像。
3.3.2.7 第6个DE
diff
-第6个DE的内容:
--DE中的TAG标识:1101
--DE中的数据类型:0400
--DE中的数据数量:01000000
--DE中的ValueOffset:08000000
- TAG 的读取结果为
0111
,十进制值为273,对应 StripOffsets,对于每个条带,该条带的字节偏移量; - 数据类型的读取结果为
0004
,十进制值为4,对应的数据类型为 LONG; - 数据数量的读取结果为
00000001
,十进制值为1,表示共1条数据; - 数据值的偏移量读取结果为
00000008
,十进制为8,表示该条带的数据从第8个字节位开始。
3.3.2.8 第7个DE
diff
-第7个DE的内容:
--DE中的TAG标识:1501
--DE中的数据类型:0300
--DE中的数据数量:01000000
--DE中的ValueOffset:03000000
- TAG 的读取结果为
0115
,十进制值为277,对应 SamplesPerPixel,表示每个像素的通道数; - 数据类型的读取结果为
0003
,十进制值为3,对应的数据类型为 SHORT; - 数据数量的读取结果为
00000001
,十进制值为1,表示共1条数据; - 数据值的偏移量读取结果为
00000003
,十进制值为3,表示每个像素有3个通道。
3.3.2.9 第8个DE
diff
-第8个DE的内容:
--DE中的TAG标识:1601
--DE中的数据类型:0400
--DE中的数据数量:01000000
--DE中的ValueOffset:09000000
- TAG 的读取结果为
0116
,十进制值为278,对应 RowsPerStrip,表示每个条带有多少行; - 数据类型的读取结果为
0004
,十进制值为4,对应的数据类型为 LONG; - 数据数量的读取结果为
00000001
,十进制值为1,表示共1条数据; - 数据值的偏移量读取结果为
00000009
,十进制值为9,表示每个条带有9行。
3.3.2.10 第9个DE
diff
-第9个DE的内容:
--DE中的TAG标识:1701
--DE中的数据类型:0400
--DE中的数据数量:01000000
--DE中的ValueOffset:C2000000
- TAG 的读取结果为
0117
,十进制值为279,对应 StripByteCounts,表示每个条带的长度; - 数据类型的读取结果为
0004
,十进制值为4,对应的数据类型为 LONG; - 数据数量的读取结果为
00000001
,十进制值为1,表示共1条数据; - 数据值的偏移量读取结果为
000000C2
,十进制值为194,表示当前条带的数据共用194个字节。
3.3.2.11 第10个DE
diff
-第10个DE的内容:
--DE中的TAG标识:1A01
--DE中的数据类型:0500
--DE中的数据数量:01000000
--DE中的ValueOffset:8A010000
- TAG 的读取结果为
011A
,十进制值为282,对应 XResolution,表示在 ImageWidth 方向上每个 ResolutionUnit 的像素数,即水平方向上的分辨率; - 数据类型的读取结果为
0005
,十进制值为5,对应的数据类型为 RATIONAL; - 数据数量的读取结果为
00000001
,十进制值为1,表示共1条数据; - 数据值的偏移量读取结果为
0000018A
,十进制值为394。RATIONAL 类型占用8个字节,该 DE 中共有1条 RATIONAL 类型的数据,共占用了8个字节,大于4个字节,因此该部分表示的数据值的偏移量,对应的数据位置为第394~401字节位。
数据打印结果如下:
第10个DE中XResolution的值:00770100E8030000
根据 RATIONAL 类型的描述,前4个字节(00770100
,读取结果为00017700
,十进制值为96000)表示分子,后4个字节表示(E8030000
,读取结果为000003E8
,十进制值为1000)表示分母,因此,该文件水平方向的分辨率为96,也就是通常说的96 DPI。
3.3.2.12 第11个DE
diff
-第11个DE的内容:
--DE中的TAG标识:1B01
--DE中的数据类型:0500
--DE中的数据数量:01000000
--DE中的ValueOffset:92010000
- TAG 的读取结果为
011B
,十进制值为283,对应 YResolution,表示在 ImageLength 方向上每个 ResolutionUnit 的像素数,即垂直方向上的分辨率; - 数据类型的读取结果为
0005
,十进制值为5,对应的数据类型为 RATIONAL; - 数据数量的读取结果为
00000001
,十进制值为1,表示共1条数据; - 数据值的偏移量读取结果为
00000192
,十进制值为402。RATIONAL 类型占用8个字节,该 DE 中共有1条 RATIONAL 类型的数据,共占用了8个字节,大于4个字节,因此该部分表示的数据值的偏移量,对应的数据位置为第402~409字节位。
数据打印结果如下:
第11个DE中YResolution的值:00770100E8030000
根据 RATIONAL 类型的描述,前4个字节(00770100
,读取结果为00017700
,十进制值为96000)表示分子,后4个字节表示(E8030000
,读取结果为000003E8
,十进制值为1000)表示分母,因此,该文件垂直方向的分辨率为96,也就是通常说的96 DPI。
3.3.2.13 第12个DE
diff
-第12个DE的内容:
--DE中的TAG标识:1C01
--DE中的数据类型:0300
--DE中的数据数量:01000000
--DE中的ValueOffset:01000000
- TAG 的读取结果为
011C
,十进制值为284,对应 PlanarConfiguration,表示如何存储每个像素的分量; - 数据类型的读取结果为
0003
,十进制值为3,对应的数据类型为 SHORT; - 数据数量的读取结果为
00000001
,十进制值为1,表示共1条数据; - 数据值的偏移量读取结果为
00000001
,十进制值为1,表示 Chunky 格式,即:每个像素的分量值是连续存储的, 例如,对于RGB数据,数据存储为F8A1A4。
3.3.2.14 第13个DE
diff
-第13个DE的内容:
--DE中的TAG标识:2801
--DE中的数据类型:0300
--DE中的数据数量:01000000
--DE中的ValueOffset:02000000
- TAG 的读取结果为
0128
,十进制值为296,对应 ResolutionUnit,表示 XResolution 和 YResolution 的测量单位; - 数据类型的读取结果为
0003
,十进制值为3,对应的数据类型为 SHORT; - 数据数量的读取结果为
00000001
,十进制值为1,表示共1条数据; - 数据值的偏移量读取结果为
00000002
,十进制值为2,分辨率单位为 Inch(英寸)。
3.3.2.15 第14个DE
diff
-第14个DE的内容:
--DE中的TAG标识:3D01
--DE中的数据类型:0300
--DE中的数据数量:01000000
--DE中的ValueOffset:02000000
- TAG 的读取结果为
013D
,十进制值为317,对应 Predictor,表示在应用编码方案之前应用于图像数据的数学运算符; - 数据类型的读取结果为
0003
,十进制值为3,对应的数据类型为 SHORT; - 数据数量的读取结果为
00000001
,十进制值为1,表示共1条数据; - 数据值的偏移量读取结果为
00000002
,十进制值为2,表示横向差分。
3.3.3 下一个 IFD 的偏移量
本文件中,第一个 IFD 最后4个字节对应的索引值为:384(202 + 2 + 12 * 15)、385、386、387,打印结果为:
第2个IFD的偏移量:00000000
该结果对应的十进制的值为0,也就说,没有下一个 IFD 了,本文件只有一个 IFD。
3.4 其他说明
- 示例文件大小为410字节,而通过
readAndPrintTiffContent
函数获取到的数组的长度也同样为410; - 在获取的数组中,索引8-201之间的数据为图像数据,通过 Predictor + LZW 算法压缩得到。