天天看點

VC下使用LibTiff處理TIFF檔案

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);