VC下使用LibTiff處理TIFF文件
一 TIFF簡(jiǎn)介
IFF是Tagged Image File Format(標(biāo)記圖像文件格式)的縮寫,這是現(xiàn)階段印刷行業(yè)使用最廣泛的文件格式,文件擴(kuò)展名為tif或tiff.TIFF是一種比較靈活的圖像格式,該格式支持單色,8,16,256色、24位真彩色、32位色、48位色等多種色彩位,同時(shí)支持rgb、cmyk以及ycbcr等多種色彩模式,支持多平臺(tái)。tiff文件可以是不壓縮的,文件體積較大,也可以是壓縮的,支持raw、rle、lzw、jpeg、ccitt3組和4組等多種壓縮方式
TIFF規(guī)范第一版本由Aldus公司在1986年發(fā)布,到現(xiàn)在已經(jīng)發(fā)布到第六版。
我們這里只討論使用libtiff對(duì)tif圖進(jìn)行編程,所以關(guān)于TIF的詳細(xì)介紹請(qǐng)見(jiàn)TiffRevision 6.0。
下載網(wǎng)址:
http://partners.adobe.com/asn/developer/PDFS/TN/TIFF6.pdf
閱讀本文章之前,要求讀者對(duì)BMP位圖有一定的了解。
二
libtiff是在UNIX下用來(lái)讀寫TIFF文件的一個(gè)工具軟件集合,包括關(guān)于TIFF的文檔,lib文件,還提供了一些小工具,比如把TIFF轉(zhuǎn)成PDF或傳真等文件格式,是完全開(kāi)放源碼的。
libtiff詳細(xì)介紹見(jiàn): http://www.libtiff.org和http://www.remotesensing.org/libtiff/
我們可下載完整的Libtiff,現(xiàn)在最新版本是3.7.2,下載網(wǎng)站ftp.remotesensing.org或
http://dl.maptools.org/dl/libtiff/。
三
解壓后,在VC++環(huán)境下編譯libtiff
有幾種辦法,簡(jiǎn)單舉兩種:
1 可以進(jìn)入CMD環(huán)境直接運(yùn)行命令行"C:\libtiff\libtiff> nmake /f makefile.vc all" ,假設(shè)代碼放在C:\libtiff\libtiff> 下面。
2 如果想利用VC的IDE環(huán)境,可以新建立一個(gè)生成dll的工程,把剛才下載的.h和.cpp文件導(dǎo)進(jìn)來(lái),然后在在"project->Settings->C/C++",在"Category"里選"Precompiled Headers",下面有4個(gè)單選,缺省選第四個(gè)"使用stdafx.h",這里改一下,選中第
一個(gè):"Notusing precompiled headers".然后編譯就可以了。
新建一個(gè)MFC工程,把生成的libtiff.lib和libtiff.dll復(fù)制過(guò)來(lái),并進(jìn)行如下設(shè)置 :
在"project->Settings->C/C++",在"Category"里選"Preprocessor",在"Additional includedirectories:"
里,把libtiff的相對(duì)路徑或絕對(duì)路徑寫進(jìn)去,比如"..\libtiff"
四 使用libtiff讀出Tiff文件并顯示出來(lái)
TIFF* tiff = TIFFOpen(szFileName,"r");//打開(kāi)Tiff文件,得到指針,以后所有的操作都通過(guò)指針進(jìn)行。
int nTotalFrame =TIFFNumberOfDirectories(tiff);
//一個(gè)TIFF文件可以放多幅圖,每幅圖我們?cè)谶@里稱作一幀,上面這個(gè)函數(shù)可以得出總幀數(shù)。
//為了便于理解,假定所有圖都是真彩24位。
TIFFSetDirectory(tiff,0);
//我們打開(kāi)第一幅圖,也就是第0幀,如果是第1幀,第二個(gè)參數(shù)寫1,由此類推。因?yàn)閃indows下圖像基本
//操作都是以BMP格式進(jìn)行,我們讀出該幀并轉(zhuǎn)成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);
//每個(gè)像素占多少機(jī)器字,24位圖samplesperpixel應(yīng)該等于3。
TIFFGetField(m_tif, TIFFTAG_BITSPERSAMPLE,&bitspersample);
//每一個(gè)機(jī)器字長(zhǎng),這里應(yīng)為8。
uint16 bitsperpixel = bitspersample *samplesperpixel;
//算出每個(gè)像素占多少bit,24位圖,值為24
DWORD dwBytePerLine =(width*bitsperpixel+31)/32 *4;
//由上面幾個(gè)參數(shù)算出圖像每行所占字節(jié)(BYTE)數(shù)。
DWORD dwLeng = height*dwBytePerLine;//在內(nèi)存里存放這幀圖像數(shù)據(jù)所需要的長(zhǎng)度
LPBYTE pData = new BYTE[dwLeng]; //為存放數(shù)據(jù)分配內(nèi)存空間
uint32* raster;
uint32 *row;
raster = (uint32*)_TIFFmalloc(width *height * sizeof (uint32));
TIFFReadRGBAImage(tiff, width, height,raster, 1);
//以上幾行讀出該幀數(shù)據(jù),保存到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);
//因?yàn)門if的數(shù)據(jù)存放順序和Windows下的BMP相反,上面這幾句進(jìn)行轉(zhuǎn)換。
//轉(zhuǎn)換結(jié)束后,數(shù)據(jù)存在pData里,釋放raster所用內(nèi)存。
根據(jù)上面得到的數(shù)據(jù),組成一個(gè)新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橫向解析度,并計(jì)算出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縱向解析度,并計(jì)算出m_pInfo->bmiHeader.biYPelsPerMeter
BITMAPFILEHEADER bmheader;
bmheader.bfType=0x4d42;
bmheader.bfSize=0;
bmheader.bfReserved1=0;
bmheader.bfReserved2=0;
bmheader.bfOffBits=54;
//這幾句是生成bmp文件的頭結(jié)構(gòu)
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中,可以用看圖軟件打開(kāi)瀏覽一下。
//記得釋放內(nèi)存空間
delete pInfo;
pInfo = NULL;
delete pData;
pData = NULL;
//如果想直接顯示,就不需要釋放,調(diào)用StretchDIBits在客戶區(qū)的DC上就可以顯示了。
//如果再打開(kāi)其他幀的話,從TIFFSetDirectory開(kāi)始循環(huán)運(yùn)行,比如取下一幀就是
TIFFSetDirectory(tiff,1);
//記得保存時(shí)另?yè)Q一個(gè)bmp文件名。
//最后,對(duì)這個(gè)TIFF文件全部操作結(jié)束,記得調(diào)用
TIFFClose(tiff);
五
合成TIF文件
上面介紹的是從TIFF里讀出一幀,現(xiàn)在介紹相反的過(guò)程,就是生成一個(gè)新的TIFF,向里面添加一幅BMP圖,為介紹方便,同樣假設(shè)圖為24真彩色。
1 首先打開(kāi)一個(gè)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*)newBYTE[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讀出來(lái),把信息部分存到pInfo中,數(shù)據(jù)部分存到pData中,供下面使用。
//然后成一個(gè)新的TIFF文件 c:\\a.tif,把上面信息寫進(jìn)來(lái)。
//下面的操作和上面讀出數(shù)據(jù)相反,不做過(guò)多解釋
uint32 width, height;
uint16 depth = 8;
out = TIFFOpen("c:\\a.tif","w");//打開(kāi)TIFF文件
TIFFSetField(out, TIFFTAG_SUBFILETYPE,FILETYPE_PAGE);//表示存的圖是多幀的
int nCur ;//當(dāng)前的幀數(shù)
int nTotao;//總幀數(shù)
TIFFSetField(out, TIFFTAG_PAGENUMBER,nCur,nTotal);//設(shè)置該幀屬性
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 轉(zhuǎn)到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文件加了一張圖,如再加下一張的話,循環(huán)操作
全部結(jié)束后,一定要
TIFFClose(out);
本文來(lái)自CSDN博客,轉(zhuǎn)載請(qǐng)標(biāo)明出處:http://blog.csdn.net/mirror_hc/archive/2007/07/26/1710391.aspx
聯(lián)系客服