VC下使用LibTiff處理TIFF文件
一 TIFF簡介 IFF是Tagged Image File Format(標記圖像文件格式)的縮寫,這是現(xiàn)階段印刷行業(yè)使用最廣泛的文件格式,文件擴展名為tif或tiff.TIFF是一種比較靈活的圖像格式,該格式支持單色,8,16,256色、24位真彩色、32位色、48位色等多種色彩位,同時支持rgb、cmyk以及ycbcr等多種色彩模式,支持多平臺。tiff文件可以是不壓縮的,文件體積較大,也可以是壓縮的,支持raw、rle、lzw、jpeg、ccitt3組和4組等多種壓縮方式 TIFF規(guī)范第一版本由Aldus公司在1986年發(fā)布,到現(xiàn)在已經(jīng)發(fā)布到第六版。 我們這里只討論使用libtiff對tif圖進行編程,所以關于TIF的詳細介紹請見Tiff Revision 6.0。 下載網(wǎng)址: 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,現(xiàn)在最新版本是3.7.2,下載網(wǎng)站ftp.或
http://dl./dl/libtiff/。
三 解壓后,在VC++環(huán)境下編譯libtiff 有幾種辦法,簡單舉兩種:
1 可以進入CMD環(huán)境直接運行命令行 "C:\libtiff\libtiff> nmake /f makefile.vc all" ,假設代碼放在C:\libtiff\libtiff> 下面。
2 如果想利用VC的IDE環(huán)境,可以新建立一個生成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文件可以放多幅圖,每幅圖我們在這里稱作一幀,上面這個函數(shù)可以得出總幀數(shù)。 //為了便于理解,假定所有圖都是真彩24位。
TIFFSetDirectory(tiff,0); //我們打開第一幅圖,也就是第0幀,如果是第1幀,第二個參數(shù)寫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; //由上面幾個參數(shù)算出圖像每行所占字節(jié)(BYTE)數(shù)。
DWORD dwLeng = height*dwBytePerLine;//在內存里存放這幀圖像數(shù)據(jù)所需要的長度 LPBYTE pData = new BYTE[dwLeng]; //為存放數(shù)據(jù)分配內存空間
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);
//因為Tif的數(shù)據(jù)存放順序和Windows下的BMP相反,上面這幾句進行轉換。 //轉換結束后,數(shù)據(jù)存在pData里,釋放raster所用內存。
根據(jù)上面得到的數(shù)據(jù),組成一個新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在客戶區(qū)的DC上就可以顯示了。
//如果再打開其他幀的話,從TIFFSetDirectory開始循環(huán)運行,比如取下一幀就是 TIFFSetDirectory(tiff,1); //記得保存時另換一個bmp文件名。 //最后,對這個TIFF文件全部操作結束,記得調用 TIFFClose(tiff);
五 合成TIF文件 上面介紹的是從TIFF里讀出一幀,現(xiàn)在介紹相反的過程,就是生成一個新的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中,數(shù)據(jù)部分存到pData中,供下面使用。 //然后成一個新的TIFF文件 c:\\a.tif,把上面信息寫進來。 //下面的操作和上面讀出數(shù)據(jù)相反,不做過多解釋
uint32 width, height; uint16 depth = 8;
out = TIFFOpen("c:\\a.tif", "w");//打開TIFF文件
TIFFSetField(out, TIFFTAG_SUBFILETYPE, FILETYPE_PAGE);//表示存的圖是多幀的 int nCur ;//當前的幀數(shù) int nTotao;//總幀數(shù) 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文件加了一張圖,如再加下一張的話,循環(huán)操作 全部結束后,一定要 TIFFClose(out);
本文來自CSDN博客,轉載請標明出處:http://blog.csdn.net/mirror_hc/archive/2007/07/26/1710391.aspx |
|