TIFF是最复杂的一种位图文件格式。TIFF是基于标记的文件格式,它广泛地应用于对图像质量要求较高的图像的存储与转换。由于它的结构灵活和包容性大,它已成为图像文件格式的一种标准,绝大多数图像系统都支持这种格式。
TIFF 是一个灵活适应性强的文件格式,通过在文件头中包含“标签”它能够在一个文件中处理多幅图像和数据。标签能够标明图像的如图像大小这样的基本几何尺寸或者定义图像数据是如何排列的并且是否使用了各种各样的图像压缩选项。例如,TIFF可以包含JPEG和行程长度编码压缩的图像。TIFF文件也可以包含基于矢量的裁剪区域(剪切或者构成主体图像的轮廓)。使用无损格式存储图像的能力使TIFF文件成为图像存档的有效方法。与JPEG不同,TIFF文件可以编辑然后重新存储而不会有压缩损失。其它的一些TIFF文件选项包括多层或者多页。
尽管现今它是一种被广泛接受的标准格式,当TIFF最初出现的时候,它的可扩展性带来了很多兼容问题。程序员可以随意定义新的标签和选项,但是并不是所有的实现程序都能支持这些所有这些创造出的标签。作为结果,它的一个最小特性集成为了“这个”TIFF,即使是在今天大量的TIFF文件和读取它们的代码都是基于简单的32位非压缩图像。
TIFF有一个使用LZW压缩的选项,这是一种减小文件大小的无损技术,但是这项技术在不同的司法权限内为几个专利所涵盖。到了2005年,除了一个之外这些专利都已经到期,其中包括Unisys所拥有的广为人知又有很多争议的专利。另外一个著名的专利是IBM拥有的将在2006年8月11日到期的专利,IBM也没有要加强它的意思(who has shown no interest to date in enforcing it)。
每个TIFF文件都是从指示字节顺序的两个字节开始的。“II”表示小字节在先、“MM”表示大字节在先字节顺序。后面的两个字节表示数字42。数字42是“为了其深刻的哲学意义"而选择的。 42的读法取决于头两个字节所表示的字节顺序。整个文件根据所指出的字节顺序进行读取。
字节顺序在Apple Macintosh和微软视窗程序之间可能产生兼容性的问题,它们通常为TIFF文件使用不同的字节顺序。一些程序提供了保存为Mac或者是Windows字节顺序的选项以使文件能在交叉平台使用。
TIFF文件以.tif为扩展名。其数据格式是一种3级体系结构,从高到低依次为:文件头、一个或多个称为IFD的包含标记指针的目录和数据。
在每一个TIFF文件中第一个数据结构称为图像文件头或IFH,它是图像文件体系结构的最高层。这个结构在一个TIFF文件中是惟一的,有固定的位置。它位于文件的开始部分,包含了正确解释TIFF文件的其他部分所需的必要信息。
IFH数据结构包含3个成员共计8个字节,Byte order成员可能是“MM”(0x4d4d)或“II”(0x4949),0x4d4d表示该TIFF图是摩托罗拉整数格式 0x4949表示该图是Intel整数格式;Version成员总是包含十进制42(0x2a),它用于进一步校验该文件是否为TIF格式,42这个数并 不是一般人 想象中的那样认为是tif软件的版本,实际上,42这个数大概永远不会变化;第三个成员是IFD(接下来要说的第二个数据结构)相对文件开始处的偏移量。
IFD是TIFF文件中第2个数据结构,它是一个名为标记(tag)的用于区分一个或多个可变长度数据块的表,标记中包含了有关于图像的所有信息。IFD提供了一系列的指针(索引),这些指针告诉我们各种有关的数据字段在文件中的开始位置,并给出每个字段的数据类型及长度。这种方法允许数据字段定位在文件的任何地方,且可以是任意长度,因此文件格式十分灵活。
IFD是TIF图中最重要的数据结构,它包含了一个TIF文件中最重要的信息,一个TIF图可能有多个IFD,这说明文件中有多个图像,每个IFD标 识1个图像的基本属性。 IFD结构中包含了三类成员,Directory Entry Count指出该结构里面有多少个目录入口;接下来就是N个线性排列的DE序列,数量不定(这就是 为什么称TIF格式文件为可扩充标记的文件,甚至用户可以添加自定义的标记属性),每个DE标识了图像的某一个属性;最后就是一个偏移量, 标识下一个文件目录相对于文件开始处的位置,当然,如果该TIF文件只包含了一幅图像,那么就只有一个IFD,显然,这个偏移量就等于0;
共12个字节,见图二。简单说,一个DE就是一幅图像的某一个属性。例如图像的大小、分辨率、是否压缩、像素的行列数、一个像素由几位 表示(1位代表黑白两色,8位代表256色等等)等。其中:tag成员是该属性的编号,在图像文件目录中,它是按照升序排列的。我们可以通过读 这些编号,然后到TIF格式官方白皮书中查找相应的含义。属性是用数据来表示的,那么type就是代表着该数据的类型,TIF官方指定的有5种数据类型。type=1就是BYTE类型(8位无标记整数)、type=2是ASCII类型(7位ASCII码加1位二进制0)、type=3是SHORT类型 (16位无标记整数)、type=4是LONG 类型(32位无标记整数)、type=5是RATIONAL类型(2个LONG,第一个是分子,第二个是分母)。length成员是数据的数量而不是数据类型的长度。 第4个成员valueOffset很重要,它是tag标识的属性代表的变量值相对文件开始处的偏移量。如果变量值占用的空间小于4个字节,那么该值就存放在 valueOffset中即可,没必要再另外指向一个地方了。
根据IFD所指向的地址.存储相关的图像信息
以下是在6.0下运行的代码
#ifndef TIFREADER_H #define TIFREADER_H #include <stdio.h> #include <string.h> #ifndef NULL #define NULL 0 #endif #ifndef TRUE #define TRUE 1 #define FALSE 0 #endif typedef struct { unsigned short Byte_order;// unsigned short Version;//校验文件是否是TIF文件 unsigned int OffsetToFirstFID;//相对对文件开始处的偏移量 // unsigned short wDECount;//多少目录入口 }IFH; typedef struct { unsigned short tag;//属性的编号 unsigned short type;//数据类型 unsigned long length;//数据的数量 unsigned long valueOffset;//tag标识的属性代表的变量值相对文件开始处的偏移量 }DE; typedef struct { int width; int height; }Size; typedef struct { int *data; }DATA; typedef struct { DE *pde; int wDECount; }PDE; bool readTIF(char* path,IFH &ifh,PDE &de,Size &size,DATA &Data) { unsigned char *data; int *dat; unsigned short wDECount;//多少目录入口 // ZeroMemory(&ifh, sizeof(IFH)); // ZeroMemory(&de, sizeof(DE)); FILE *fp; fp=fopen(path,"rb"); if(fp == NULL) { cout<<"open file error"<<endl; return false; } if(sizeof(IFH) != fread(&ifh, 1,sizeof(IFH),fp)) { cout<<"读TIF文件头失败"; return FALSE; } if(0x2a != ifh.Version) { cout<<"该文件不是TIF格式,读文件失败"; return FALSE; } if(0x4949 != ifh.Byte_order) { cout<<"该TIF文件不是IBMPC字节序,读文件失败"; return FALSE; } fseek(fp,ifh.OffsetToFirstFID,SEEK_SET);//将文件指针定位到IFD //读文件有多少个目录入口 if(2 != fread(&wDECount,1,sizeof(unsigned short),fp)) { cout<<"无法获得TIF文件目录入口数量"; return FALSE; } cout<<"该TIF文件有"<<wDECount<<"个目录入口"<<endl; //创建DE数组,接收信息,数组中有wDECount个元素 de.pde = new DE[wDECount]; DE* pTemp = de.pde; de.wDECount = wDECount; memset(de.pde, 0, sizeof(DE)*wDECount); if(sizeof(DE)*wDECount != fread(de.pde,1, sizeof(DE)*wDECount,fp)) { cout<<"读图象文件目录失败"; delete []de.pde; return false ; } //把图像的大小和图像数据的容量保存到成员变量中 int m_size_x; int m_size_y; int m_size; int i; for(i=0; i<wDECount; i++) { pTemp =de.pde + i; if(256 == pTemp->tag) //tag为256的目录入口中的变量标识了图象宽度 { m_size_x = pTemp->valueOffset; } if(257 == pTemp->tag) //图象高度 { m_size_y = pTemp->valueOffset; } if(273 == pTemp->tag) //计算图象数据占用字节数 { //m_dwBmSize = pTemp->valueOffset - sizeof(IFH); //或者把tag=256的valueOffset乘以tag=257的valueOffset m_size = m_size_x * m_size_y; } } //填充所有像素数据, 颠倒图象数据从最后一行开始读起 int j = 0; // int i = 0; data = (unsigned char*)malloc(m_size*sizeof(BYTE)); dat = (int*)malloc(m_size*sizeof(int)); for(i=m_size_y-1; i>=0; i--) { fseek(fp,sizeof(IFH) + i*m_size_x, SEEK_SET); fread((BYTE*)(data + 1) + j*m_size_x,sizeof(BYTE), m_size_x,fp); j++; } cout<<"width:"<<m_size_x<<endl; cout<<"height:"<<m_size_y<<endl; unsigned char* p; p = data; int *ptr; ptr = dat; for (i=0;i<m_size;i++,p++,ptr++) { *ptr = (int)(*p); int h= *ptr; // cout<<h<<" "; } size.width = m_size_x; size.height = m_size_y; Data.data = dat; return TRUE; } bool saveTIF(char* path,IFH ifh,PDE de,Size size,DATA Data) { unsigned char *data; // unsigned short wDECount;//多少目录入口 // ZeroMemory(&ifh, sizeof(IFH)); // ZeroMemory(&de, sizeof(DE)); FILE *fp; fp=fopen(path,"wb"); if(fp == NULL) { cout<<"open file error"<<endl; return false; } if(sizeof(IFH) != fwrite(&ifh, 1,sizeof(IFH),fp)) { cout<<"写TIF文件头失败"; return FALSE; } fseek(fp,ifh.OffsetToFirstFID,SEEK_SET);//将文件指针定位到IFD //读文件有多少个目录入口 if(2 != fwrite(&de.wDECount,1,sizeof(unsigned short),fp)) { cout<<"无法获得TIF文件目录入口数量"; return FALSE; } //创建DE数组,接收信息,数组中有wDECount个元素 if(sizeof(DE)*de.wDECount != fwrite(de.pde,1, sizeof(DE)*de.wDECount,fp)) { cout<<"读图象文件目录失败"; return false ; } //填充所有像素数据, 颠倒图象数据从最后一行开始读起 int j = 0; int i = 0; data = (unsigned char*)malloc(size.width*size.height*sizeof(BYTE)); int *ptr = Data.data; unsigned char* p; p = data; for (i=0;i<size.width*size.height;i++,p++,ptr++) { *p = (unsigned char)(*ptr); // int h= *ptr; // cout<<h<<" "; } for(i=size.height-1; i>=0; i--) { fseek(fp,sizeof(IFH) + i*size.width, SEEK_SET); fwrite((BYTE*)(data + 1) + j*size.width,sizeof(BYTE), size.width,fp); j++; } return TRUE; } #endif