1 什么是TIFF?
TIFF是Tagged Image File Format的缩写。在现在的标准中,只有TIFF存在,其他的提法已经舍弃不用了。做为一种标记语言,TIFF与其他文件格式最大的不同在于除了图像数据,它还可以记录很多图像的其他信息。它记录图像数据的方式也比较灵活,理论上来说,任何其他的图像格式都能为TIFF所用,嵌入到TIFF里面。比如JPEG,Lossless JPEG,JPEG2000和任意数据宽度的原始无压缩数据都可以方便的嵌入到TIFF中去。由于它的可扩展性,TIFF在数字影像、遥感、医学等领域中得到了广泛的应用。TIFF文件的后缀是.tif 或者.tiff
2 TIFF文件结构
TIFF文件中的三个关键词是:图像文件头Image File Header(IFH),图像文件目录Image File Directory(IFD)和目录项Directory Entry(DE)。每一幅图像是以8字节的IFH开始的,这个IFH指向了第一个IFD。IFD包含了图像的各种信息,同时也包含了一个指向实际图像数据的指针。
IFH的构成:
Byte 0-1: 字节顺序标志位,值为II或者MM。II表示小字节在前,又称为little-endian。MM表示大字节在前,又成为big-endian。
Byte 2-3: TIFF的标志位,一般都是42
Byte 4-7: 第一个IFD的偏移量。可以在任意位置,但必须是在一个字的边界,也就是说必须是2的整数倍。
IFD的构成(0代表此IFD的起始位置):
Byte 0-1: 表示此IFD包含了多少个DE,假设数目为n
Byte 2-(n*12+1): n个DE
Byte (n*12+2)-(n*12+5): 下一个IFD的偏移量,如果没有则置为0
DE的构成:
Byte 0-1: 此TAG的唯一标识
Byte 2-3: 数据类型。
Byte 4-7: 数量。通过类型和数量可以确定存储此TAG的数据需要占据的字节数
Byte 8-11: 如果占用的字节数少于4,则数据直接存于此。如果超过4个,则这里存放的是指向实际数据的指针
可以用以下的图来表示(https://www.sodocs.net/doc/304176382.html,/windcsn/archive/2009/03/12/1158.html)
在TIFF6.0中,定义了12种数据类型,分别是:
1 = BYTE 8-bit unsigned integer.
2 = ASCII 8-bit byte that contains a 7-bit ASCII code; the last byte
must be NUL (binary zero).
3 = SHORT 16-bit (2-byte) unsigned integer.
4 = LONG 32-bit (4-byte) unsigned integer.
5 = RATIONAL Two LONGs: the first represents the numerator
6 = SBYTE An 8-bit signed (twos-complement) integer.
7 = UNDEFINED An 8-bit byte that may contain anything, depending on
the definition of the field.
8 = SSHORT A 16-bit (2-byte) signed (twos-complement) integer.
9 = SLONG A 32-bit (4-byte) signed (twos-complement) integer.
10 = SRATIONAL Two SLONG’s: the first represents the numerator of a
fraction, the second the denominator.
11 = FLOAT Single precision (4-byte) IEEE format.
12 = DOUBLE Double precision (8-byte) IEEE format.
-个TIFF文件可能包含多个IFD,每一个IFD都是一个子文件。Baseline解码器只要求解第一个IFD所对应的图像数据。扩展的TIFF图像经常包含多个IFD,每一个IFD都包含了不同的信息。
3 TIFF,TIFF/EP以及DNG的关系
TIFF/EP的全称是"Tag Image File Format / Electronic Photography "。它是一个名为“Electronic still-picture imaging – Removable memory – Part 2: TIFF/EP image data format”ISO标准,标准号为ISO 12234-2
DNG(Digital Negative)是Adobe开发的一种开放的raw image file format。里面使用的tag 基本上都定义在TIFF或者TIFF/EP中,在DNG Sepcification中只是定义或者建议了数据的组织方式,颜色空间的转换等等。
就我个人的理解,这三者之间的关系应该是这样的:
(1) TIFF和DNG同为Specification,分别定义了后缀名为.tif/.tiff和.dng的文件格式
(2) 同时在TIFF Specification也定义个baseline及部分扩展的tag。TIFF/EP则定义并规范了在电子影像中所使用的TAG。
(3) DNG同时与TIFF和TIFF/EP兼容,并包含了EXIF和XMP信息。DNG实际上就是扩张的TIFF,把DNG的扩展名改成TIF就可以直接预览图片
(4) 虽然版权都归Adobe所有,但都可以无偿使用
在DNG出现以前,各个数码相机制造商都有自己的格式,比如Canon(cr2/crw), Nikon(nef), Olympus(orf), Pentex(pef)等等。之所以出现这么多格式,一方面的原因是在这之前没有统一的raw格式,但更重要的是,各个厂商希望用这个只对自己公开的数据格式来保护自己的私密信息。Adobe推出DNG希望能一统raw的天下。但很遗憾,只有极少的数码制造商响应,比如sony,hasselblad,目前的结果也仅仅是多了一种raw的格式。虽然如何,DNG仍然是成功的。由于有很成熟的DNG编解码及转换公司,很多小厂商乐
于使用DNG作为自己的文件格式。随着时间的推进,迫于消费者的意愿,大的数码厂商被迫支持DNG。最后DNG统一这个数码raw格式仍然是大势所趋。
4 TIFF的特点
(1)应用广泛。①TIFF可以描述多种类型的图像;②TIFF拥有一系列的压缩方案可供选择;③TIFF不依赖于具体的硬件;④TIFF是一种可移植的文件格式。
(2)TIFF具有可扩展性。在TIFF 6.0中定义了许多扩展,它们允许TIFF提供以下通用功能:
①几种主要的压缩方法;②多种色彩表示方法;③图像质量增强;④特殊图像效果;⑤文档的存储和检索帮助。
(3)格式复杂。TIFF文件的复杂性给它的应用带来了一些问题。一方面,要写一种能够识别所有不同标记的软件非常困难。另一方面,一个TIFF文件可以包含多个图像,每个图像都有自己的IFD和一系列标记,并且采用了多种压缩算法。这样也增加了程序设计的复杂度。
5 TIFF的局限及将来的发展
TIFF的最大局限在于用4byte来表示偏移量,这样导致文件最大只能有4G。在20年前指定TIFF标准的时候可能觉得4G足够用了。但是现在这确实成了制约TIFF反展的一个瓶颈。目前BigTIFF已经提出用8个字节来表示偏移量。这样数据量应该足够大了。也许在不久的将来,这会成为新的tiff的baseline
6基本TIFF TAGS
本页主要摘自http://www.awaresystems.be/imaging/tiff/tifftags/baseline.html。每一个TAG 均有原始链接,可以点击查看详细的描述。
这些基本Tag是所有TIFF编解码器必须支持的Tag
解码:
//首先定义一些必须的全局变量和一些有用的函数,主要是读取各种数据类型的函数short order;
FILE *ifp = ifp = fopen ("filename.tiff", "rb");
ushort CLASS sget2 (uchar *s)
{
if (order == 0x4949) /* "II" means little-endian */ return s[0] | s[1] << 8;
else /* "MM" means big-endian */
return s[0] << 8 | s[1];
}
ushort CLASS get2()
{
uchar str[2] = { 0xff,0xff };
fread (str, 1, 2, ifp);
return sget2(str);
}
unsigned CLASS sget4 (uchar *s)
{
if (order == 0x4949)
return s[0] | s[1] << 8 | s[2] << 16 | s[3] << 24; else
return s[0] << 24 | s[1] << 16 | s[2] << 8 | s[3]; }
#define sget4(s) sget4((uchar *)s)
unsigned CLASS get4()
{
uchar str[4] = { 0xff,0xff,0xff,0xff };
fread (str, 1, 4, ifp);
return sget4(str);
}
unsigned CLASS getint (int type)
{
return type == 3 ? get2() : get4();
}
float CLASS int_to_float (int i)
{
union { int i; float f; } u;
u.i = i;
return u.f;
}
double CLASS getreal (int type)
{
union { char c[8]; double d; } u;
int i, rev;
switch (type) {
case 3: return (unsigned short) get2();
case 4: return (unsigned int) get4();
case 5: u.d = (unsigned int) get4();
return u.d / (unsigned int) get4();
case 8: return (signed short) get2();
case 9: return (signed int) get4();
case 10: u.d = (signed int) get4();
return u.d / (signed int) get4();
case 11: return int_to_float (get4());
case 12:
rev = 7 * ((order == 0x4949) == (ntohs(0x1234) == 0x1234));
for (i=0; i < 8; i++)
u.c[i ^ rev] = fgetc(ifp);
return u.d;
default: return fgetc(ifp);
}
}
//先解IFH,得到order和第一个IFD的偏移量
//然后解IFD
//最后是图像数据
//对于多个IFD,采用同样的处理方法
int CLASS parse_tiff ()
{
int doff;
fseek (ifp, 0, SEEK_SET);
order = get2();//"II"或者"MM"
get2();//这个值应该是42, 跳过之
doff = get4();//第一个IFD的偏移量
fseek (ifp, doff, SEEK_SET);
parse_tiff_ifd ();
//根据从Tag中得到的值去获取或者解压相应的图像数据}
int CLASS parse_tiff_ifd ()
{
entries = get2();
if (entries > 512) return 1;
while (entries--) {
//先读取Tag,Type和Count的值
//根据Type和Count确定存储的是值还是位置,
}
//获取下一个IFD的偏移量
}