VC下使用LibTiff处理TIFF文件
一 TIFF简介 IFF是Tagged Image File Format(标记图像文件格式)的缩写,这是现阶段印刷行业使用最广泛的文件格式,文件扩展名为tif或tiff.TIFF是一种比较灵活的图像格式,该格式支持单色,8,16,256色、24位真彩色、32位色、48位色等多种色彩位,同时支持rgb、cmyk以及ycbcr等多种色彩模式,支持多平台。tiff文件可以是不压缩的,文件体积较大,也可以是压缩的,支持raw、rle、lzw、jpeg、ccitt3组和4组等多种压缩方式 TIFF规范第一版本由Aldus公司在1986年发布,到现在已经发布到第六版。 我们这里只讨论使用libtiff对tif图进行编程,所以关于TIF的详细介绍请见Tiff Revision 6.0。 下载网址: http://partners.adobe.com/asn/developer/PDFS/TN/TIFF6.pdf 阅读本文章之前,要求读者对BMP位图有一定的了解。
二
libtiff是在UNIX下用来读写TIFF文件的一个工具软件集合,包括关于TIFF的文档,lib文件,还提供了一些小工具,比如把TIFF转成PDF或传真等文件格式,是完全开放源码的。 libtiff详细介绍见: http://www.和http://www./libtiff/ 我们可下载完整的Libtiff,现在最新版本是3.7.2,下载网站ftp.或
http://dl./dl/libtiff/。
三 解压后,在VC++环境下编译libtiff 有几种办法,简单举两种:
1 可以进入CMD环境直接运行命令行 "C:\libtiff\libtiff> nmake /f makefile.vc all" ,假设代码放在C:\libtiff\libtiff> 下面。
2 如果想利用VC的IDE环境,可以新建立一个生成dll的工程,把刚才下载的.h和.cpp文件导进来,然后在在"project->Settings->C/C++",在"Category"里选"Precompiled Headers",下面有4个单选,缺省选第四个"使用stdafx.h",这里改一下,选中第 一个:"Not using precompiled headers".然后编译就可以了。
新建一个MFC工程,把生成的libtiff.lib和libtiff.dll复制过来,并进行如下设置 : 在"project->Settings->C/C++",在"Category"里选"Preprocessor",在"Additional include directories:" 里,把libtiff的相对路径或绝对路径写进去,比如"..\libtiff"
四 使用libtiff读出Tiff文件并显示出来
TIFF* tiff = TIFFOpen(szFileName, "r");//打开Tiff文件,得到指针,以后所有的操作都通过指针进行。
int nTotalFrame = TIFFNumberOfDirectories(tiff); //一个TIFF文件可以放多幅图,每幅图我们在这里称作一帧,上面这个函数可以得出总帧数。 //为了便于理解,假定所有图都是真彩24位。
TIFFSetDirectory(tiff,0); //我们打开第一幅图,也就是第0帧,如果是第1帧,第二个参数写1,由此类推。因为Windows下图像基本 //操作都是以BMP格式进行,我们读出该帧并转成BMP格式。
char *dtitle; TIFFGetField(tiff,TIFFTAG_PAGENAME,&dtitle); //得到该帧的名字,存放在dtitle中。
int width,height; TIFFGetField(tiff, TIFFTAG_IMAGEWIDTH, &width); //得到宽度 TIFFGetField(tiff, TIFFTAG_IMAGELENGTH, &height);//得到高度
float resolution = max(xres,yres);
uint16 bitspersample=1; uint16 samplesperpixel=1;
TIFFGetField(m_tif, TIFFTAG_SAMPLESPERPIXEL, &samplesperpixel); //每个像素占多少机器字,24位图samplesperpixel应该等于3。 TIFFGetField(m_tif, TIFFTAG_BITSPERSAMPLE, &bitspersample); //每一个机器字长,这里应为8。
uint16 bitsperpixel = bitspersample * samplesperpixel; //算出每个像素占多少bit,24位图,值为24 DWORD dwBytePerLine = (width*bitsperpixel+31)/32 *4; //由上面几个参数算出图像每行所占字节(BYTE)数。
DWORD dwLeng = height*dwBytePerLine;//在内存里存放这帧图像数据所需要的长度 LPBYTE pData = new BYTE[dwLeng]; //为存放数据分配内存空间
uint32* raster; uint32 *row; raster = (uint32*)_TIFFmalloc(width * height * sizeof (uint32)); TIFFReadRGBAImage(tiff, width, height, raster, 1); //以上几行读出该帧数据,保存到raster中。
row = &raster[0]; bits2 = pData; for (y = 0; y < height; y++) {
bits = bits2; for (x = 0; x < width; x++) { *bits++ = (BYTE)TIFFGetB(row[x]); *bits++ = (BYTE)TIFFGetG(row[x]); *bits++ = (BYTE)TIFFGetR(row[x]); } row += width; bits2 += dwBytePerLine; } _TIFFfree(raster);
//因为Tif的数据存放顺序和Windows下的BMP相反,上面这几句进行转换。 //转换结束后,数据存在pData里,释放raster所用内存。
根据上面得到的数据,组成一个新BMP位图:
LPBITMAPINFO pInfo = new BITMAPINFO; pInfo->bmiHeader.biSize = sizeof(BITMAPINFOHEADER); pInfo->bmiHeader.biWidth = width; pInfo->bmiHeader.biHeight = width; pInfo->bmiHeader.biCompression = BI_RGB;
pInfo->bmiHeader.biClrUsed = 0; pInfo->bmiHeader.biClrImportant = 0; pInfo->bmiHeader.biPlanes = 1; pInfo->bmiHeader.biBitCount = 24; pInfo->bmiHeader.biSizeImage = dwLeng;
float xres,yres; uint16 res_unit; //解析度单位:如是英寸,厘米 TIFFGetFieldDefaulted(tiff, TIFFTAG_RESOLUTIONUNIT, &res_unit);
if(TIFFGetField(tiff, TIFFTAG_XRESOLUTION, &xres) == 0) { m_pInfo->bmiHeader.biXPelsPerMeter = 0; } else { if(res_unit == 2) //英寸 { pInfo->bmiHeader.biXPelsPerMeter = xres * 10000 / 254; } else if(res_unit == 3) //厘米 { pInfo->bmiHeader.biXPelsPerMeter = xres * 100; } else { pInfo->bmiHeader.biXPelsPerMeter = 0; } } //得到该帧TIFF横向解析度,并计算出m_pInfo->bmiHeader.biXPelsPerMeter
if(TIFFGetField(tiff, TIFFTAG_YRESOLUTION, &yres) == 0) { pInfo->bmiHeader.biYPelsPerMeter = 0; } else { if(res_unit == 2) //英寸 { pInfo->bmiHeader.biYPelsPerMeter = yres * 10000 / 254; } else if(res_unit == 3) //厘米 { pInfo->bmiHeader.biYPelsPerMeter = yres * 100; } else { pInfo->bmiHeader.biYPelsPerMeter = 0; } } //得到该帧TIFF纵向解析度,并计算出m_pInfo->bmiHeader.biYPelsPerMeter
BITMAPFILEHEADER bmheader; bmheader.bfType=0x4d42; bmheader.bfSize=0; bmheader.bfReserved1=0; bmheader.bfReserved2=0; bmheader.bfOffBits=54; //这几句是生成bmp文件的头结构
CFile bmp; bmp.Open("c:\\test.bmp",CFile::modeCreate|CFile::modeWrite); bmp.Write(&bmheader,sizeof(BITMAPFILEHEADER)); bmp.Write(&(pInfo->bmiHeader),sizeof(BITMAPINFOHEADER)); bmp.Write(pData,dwLeng); bmp.Close();
//这里,把该帧TIFF保存到了C盘的test.bmp中,可以用看图软件打开浏览一下。
//记得释放内存空间 delete pInfo; pInfo = NULL; delete pData; pData = NULL; //如果想直接显示,就不需要释放,调用StretchDIBits在客户区的DC上就可以显示了。
//如果再打开其他帧的话,从TIFFSetDirectory开始循环运行,比如取下一帧就是 TIFFSetDirectory(tiff,1); //记得保存时另换一个bmp文件名。 //最后,对这个TIFF文件全部操作结束,记得调用 TIFFClose(tiff);
五 合成TIF文件 上面介绍的是从TIFF里读出一帧,现在介绍相反的过程,就是生成一个新的TIFF,向里面添加一幅BMP图,为介绍方便,同样假设图为24真彩色。 1 首先打开一个BMP文件,
BITMAPFILEHEADER bh; CFile file; file.Open("c:\\a.bmp",CFile::modeRead,NULL); file.Read(&bh,sizeof(BITMAPFILEHEADER)); DWORD dwSize = sizeof(BITMAPINFO) + 256*sizeof(RGBQUAD);
LPBITMAPINFO pInfo = (BITMAPINFO*)new BYTE[sizeof(BITMAPINFO) + 256*sizeof(RGBQUAD)]; file.Read(pInfo,sizeof(BITMAPINFO)+ 256*sizeof(RGBQUAD));
int height = m_pInfo->bmiHeader.biHeight; int width = m_pInfo->bmiHeader.biWidth;
int dwBytePerLine = 4*(width * pInfo->bmiHeader.biBitCount + 31)/32;
int nSize = m_pInfo->bmiHeader.biSizeImage;
if(nSize == 0) { nSize = height * dwBytePerLine; }
LPBYTE pData = new BYTE[nSize+32]; file.Seek(bh.bfOffBits,SEEK_SET); file.Read(pData,nSize); file.Close(); //把C盘上的位图文件a.bmp读出来,把信息部分存到pInfo中,数据部分存到pData中,供下面使用。 //然后成一个新的TIFF文件 c:\\a.tif,把上面信息写进来。 //下面的操作和上面读出数据相反,不做过多解释
uint32 width, height; uint16 depth = 8;
out = TIFFOpen("c:\\a.tif", "w");//打开TIFF文件
TIFFSetField(out, TIFFTAG_SUBFILETYPE, FILETYPE_PAGE);//表示存的图是多帧的 int nCur ;//当前的帧数 int nTotao;//总帧数 TIFFSetField(out, TIFFTAG_PAGENUMBER, nCur,nTotal);//设置该帧属性
width = pInfo->bmiHeader.biWidth; height = pInfo->bmiHeader.biHeight;
TIFFSetField(out, TIFFTAG_IMAGEWIDTH, width); TIFFSetField(out, TIFFTAG_IMAGELENGTH, height); TIFFSetField(out, TIFFTAG_ORIENTATION, ORIENTATION_TOPLEFT); TIFFSetField(out, TIFFTAG_SAMPLESPERPIXEL, 3); TIFFSetField(out, TIFFTAG_BITSPERSAMPLE, depth); TIFFSetField(out, TIFFTAG_PLANARCONFIG, PLANARCONFIG_CONTIG);
uint16 photometric = PHOTOMETRIC_RGB;//表示存放格式为RGB TIFFSetField(out, TIFFTAG_PHOTOMETRIC, photometric); TIFFSetField(out, TIFFTAG_ROWSPERSTRIP,height);
uint16 compression = COMPRESSION_LZW;// TIFFSetField(out, TIFFTAG_COMPRESSION, compression); //TIFF按LZH压缩
uint32 offset, size; char *scanbuf; size = ((width * pInfo->bmiHeader.biBitCount + 31) /32)*4; scanbuf = (char *) _TIFFmalloc(size); int nBitsPerPixel = pInfo->bmiHeader.biBitCount; for(int nLines = 0; nLines < height; nLines++) { DWORD dwWidthBytes = ((width * pInfo->bmiHeader.biBitCount + 31) /32)*4; LPBYTE p = new BYTE[dwWidthBytes]; memcpy(p, pData + (height - 1 - nLines) * dwWidthBytes, dwWidthBytes); //从BGR 转到RGB if(nBitsPerPixel == 24 || nBitsPerPixel == 32) { LPBYTE pTempData = p; for(int i = 0; i < width; i++) { BYTE temp = *pTempData; //R *pTempData = *(pTempData + 2); *(pTempData + 2) = temp; //m_dwBitsPerPixel may be 32 or 24 pTempData += nBitsPerPixel / 8; } } TIFFWriteScanline(out, p, nLines, 0); delete p; p = NULL; }
_TIFFfree(scanbuf); TIFFWriteDirectory(out); delete pData; pData = NULL; delete pInfo; pInfo = NULL; //到这里,向TIFF文件加了一张图,如再加下一张的话,循环操作 全部结束后,一定要 TIFFClose(out);
本文来自CSDN博客,转载请标明出处:http://blog.csdn.net/mirror_hc/archive/2007/07/26/1710391.aspx |
|