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.libtiff.org和http://www.remotesensing.org/libtiff/
我們可下載完整的Libtiff,現在最新版本是3.7.2,下載網站ftp.remotesensing.org或

http://dl.maptools.org/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);
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章