VC下使用LibTiff处理TIFF文件

VC下使用LibTiff处理TIFF文件

 

一 TIFF简介

IFF是Tagged Image FileFormat(标记图像文件格式)的缩写,这是现阶段印刷行业使用最广泛的文件格式,文件扩展名为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 /fmakefile.vc all" ,假设代码放在C:\libtiff\libtiff> 下面。

 

2如果想利用VC的IDE环境,可以新建立一个生成dll的工程,把刚才下载的.h和.cpp文件导进来,然后在在"project->Settings->C/C++",在"Category"里选"PrecompiledHeaders",下面有4个单选,缺省选第四个"使用stdafx.h",这里改一下,选中第

一个:"Not using precompiled headers".然后编译就可以了。

 

新建一个MFC工程,把生成的libtiff.lib和libtiff.dll复制过来,并进行如下设置 :

在"project->Settings->C/C++",在"Category"里选"Preprocessor",在"Additionalinclude 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 = newBYTE[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;

    }

    elseif(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;

    }

    elseif(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++)

{

    DWORDdwWidthBytes = ((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);

    deletep;

    p =NULL;

}

 

_TIFFfree(scanbuf);

TIFFWriteDirectory(out);

delete pData;

pData = NULL;

delete pInfo;

pInfo = NULL;

//到这里,向TIFF文件加了一张图,如再加下一张的话,循环操作

全部结束后,一定要

TIFFClose(out);

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值