libtiff的使用
作者:互联网
最近的一个项目用到tif图片格式读写。tif是一种图像文件格式,最初用于黑白传真,后来也支持彩色。相对于其他图像格式,tif有点像容器,支持多页不同尺寸、不同的压缩格式。黑白的压缩算法常见为CCITT 4/6,无损压缩,不支持灰度和彩色;彩色的常见压缩算法为LZW无损压缩,对文字和矢量图形的效果不错,但对于照片的压缩率很差。最新的tif格式也支持jpeg有损压缩和zip压缩,不过很多旧版软件不支持,如XP图片查看器等。
最初为了图方便,我使用了windows自带的gdi+来读写tif,但后来发现几个无法解决的问题
1.在32位系统上,打开2G以上的tif文件失败;
2.被某些应用(如splwow64)调用时,总是失败;
无奈之下只好换方案,使用libtiff库。本来想下载编译好的dll文件,不过都没64位的,干脆自己编一下吧。
【编译】
libtiff引用了jpeg,zip库,一开始我用不着这两个,就把他们去掉了:
1.libtiff\makefile.vc 注释tif_jpeg/pixarlog/zip三行
2.tiffconf.h 注释 JPEG_SUPPORT,PIXARLOG,ZIP三行
编译64位版本是我用了VS2008 x64 win64 command prompt tools,运行 nmake /f makefile.vc 但编译出来的dll依赖mfc90.dll等文件,最好改为静态链接: nmake.opt OPTFLAGS, MD->MT 另外可以不生成pdb: nmake.opt- LD=link /nologo 加上 /pdb:none
32位也可以用VC6编译:VC6\VC98\Bin\VCVARS32.bat 命令同上
编译生成libtiff.dll, libtif_i.lib(dynamic), libtiff.lib(static) 不过我还是建了一个VC工程来编译,更方便一些。 ==========================================================
如果要支持jpeg编码,请到http://www.ijg.org/ 下载源码包jpegsrxc
copy jconfig.vc jconfig.h ; nmake /f makefile.vc libjpeg.lib ; 默认生成libjpeg.lib静态库 在libtiff\makefile.vc,tiffconf.h开启jpeg行,nmake.opt中打开jpeg项目并写入路径 64位编译时,要在makefile.vc CFLAGS=..加上/Ox /MT /GX /W3 静态链接MFC.
==========================================================
用VC编译应用程序没问题,但在WDK编译驱动时遇到错误[unresolved external symbol __imp__TIFFOpen@4 referenced].这里TIFFOpen后面为什么有个@4?看了下Lib文件里有这些函数但没有@序号。这是因为libtiff都是c函数,默认是cdecl调用方式,dll输出函数不带@序号。而WDK编译驱动时默认是stdcall(/Gz)编译方式,链接时就找不到了。这里需要把libtiff里的函数都加上__stdcall修饰再重编译,不过改动比较多,也可以加上/Gz编译选项,或在VC的Code Generation/Advanced - calling convention 选为stdcall(/Gz)编译,注意libjpeg也要重新编译。如果以stdcall编译后,头文件tiffio.h中的函数也必须加上__stdcall修饰。
【使用】
1 #include "tiffio.h" 2 int main() 3 { 4 int i,nret,nw,nh,nbpp,npage=1; 5 TIFF* pTif = TIFFOpen("d:\\1.tif", "r"); 6 TIFFSetDirectory(pTif, 1); // 跳到指定的页数1 7 nret = TIFFGetField(pTif, TIFFTAG_IMAGEWIDTH, &nw); // 获取图像长、宽 8 nret = TIFFGetField(pTif, TIFFTAG_IMAGELENGTH, &nh); 9 npage = TIFFNumberOfDirectories(pTif); // 读取页数 10 TIFFClose(pTif); 11 }
错误和警告信息
libtiff使用CallBack方式显示错误和警告。定义如下函数
1 void TIFFErrorProc(const char* pModule, const char* pFormat, va_list pArg) 2 { 3 char szMsg[512]; 4 vsprintf(szMsg, pFormat, pArg); 5 printf("tifferr-%s: %s", pModule, szMsg); 6 } 7 <br data-filtered="filtered">//然后设置为错误和警告处理函数,最好分开两个。 8 <br data-filtered="filtered">TIFFSetWarningHandler(TIFFWarnProc); 9 <br data-filtered="filtered">TIFFSetErrorHandler(TIFFErrorProc); 10 <br data-filtered="filtered">//其中pModule是函数模块,如TiffEncode; pFormat是带参数的信息如"height should be %d", pArg是可变参数表
Directories 多页
一个tif文件可以包含多页,每页的宽高大小都可以不同,在libtiff中称为Directories.
获取页数 npage = TIFFNumberOfDirectories(TIFF*);
跳到指定页数 TIFFSetDirectory(TIFF*, tdir_t);
写入一页 TIFFWriteDirectory(TIFF*);
tif图像的三种压缩和组织形式scanline,strip,tile
scanline:每行图像压缩,只支持ccitt等算法,不支持lzw,jpeg等
strip:图像分为几个横条压缩,
tile: 图像分为若干个正方形块进行压缩
与其他图像格式转换的问题
彩色tif内的颜色顺序为rgb,在bmp内的顺序为bgr,两者需要翻转
黑白tif可调用TIFFSetField(pTif, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_MINISBLACK/WHITE)来指定0-1为黑、白,不过有些软件不支持。
tif内的每行数据为1字节对齐,bmp为4字节对齐 使用jpeg算法压缩时,strip的高度必须为8的倍数
最后附上tif-bmp转换的代码例子
1 void SaveBmpFile(LPTSTR pszBmp, int nW, int nH, int nBpp, LPBYTE pBuf) 2 { 3 BITMAPFILEHEADER bfh={0}; 4 BITMAPINFOHEADER bih={0}; 5 DWORD pal[256]={0,0XFFFFFF}; 6 int nLineByte,nwb,y; 7 8 bfh.bfType = 'MB'; 9 bfh.bfSize = sizeof bfh; 10 bfh.bfOffBits = sizeof(bfh)+sizeof(bih); 11 if(nBpp == 1) 12 bfh.bfOffBits += 8; 13 bih.biSize = sizeof bih; 14 bih.biWidth = nW; 15 bih.biHeight = nH; 16 bih.biBitCount = nBpp; 17 bih.biPlanes = 1; 18 nwb = (nW*nBpp+31)/32*4; //bmp 4 bytes align 19 bih.biSizeImage = nwb*nH; 20 21 CFile fBmp(pszBmp, CFile::modeCreate|CFile::modeWrite); 22 fBmp.Write(&bfh, sizeof bfh); 23 fBmp.Write(&bih, sizeof bih); 24 if(nBpp == 1) 25 fBmp.Write(pal, 8); 26 27 nLineByte = (nW*nBpp+7)/8; //tif 1 bytes align 28 LPBYTE pLine = pBuf+nLineByte*(nH-1); 29 for(y=0; y<nH; y++) 30 { 31 fBmp.Write(pLine, nLineByte); 32 if(nwb > nLineByte) 33 fBmp.Write(pal, nwb-nLineByte); 34 pLine -= nLineByte; 35 } 36 fBmp.Close(); 37 } 38 void SaveTif2Bmp(LPTSTR pszTif, LPTSTR pszBmp) 39 { 40 TIFF* pTif = TIFFOpen(pszTif, "r"); 41 if(!pTif) 42 return; 43 int i,nret,nw,nh,npage,nrps; 44 unsigned short nComp, nPho, nBps,nSpp; 45 TIFFSetErrorHandler(TIFFErrorHandler); 46 nret = TIFFGetField(pTif, TIFFTAG_IMAGEWIDTH, &nw); 47 nret = TIFFGetField(pTif, TIFFTAG_IMAGELENGTH, &nh); 48 nret = TIFFGetField(pTif, TIFFTAG_COMPRESSION, &nComp); 49 nret = TIFFGetField(pTif, TIFFTAG_PHOTOMETRIC, &nPho); 50 nret = TIFFGetField(pTif, TIFFTAG_BITSPERSAMPLE, &nBps); 51 nret = TIFFGetField(pTif, TIFFTAG_SAMPLESPERPIXEL, &nSpp); 52 nret = TIFFGetField(pTif, TIFFTAG_ROWSPERSTRIP, &nrps); 53 npage = TIFFNumberOfDirectories(pTif); 54 int nSize = TIFFStripSize(pTif); 55 int nStrip = TIFFNumberOfStrips(pTif); 56 uint32* bc; // wrong size?? 57 nret = TIFFGetField(pTif, TIFFTAG_STRIPBYTECOUNTS, &bc); 58 59 int nwb = (nw*nBps*nSpp+31)/32*4; // 4-byte align for bmp 60 LPBYTE pBufBmp = new BYTE[nwb*nh]; 61 //uint32 stripsize = bc[0]; 62 //tdata_t buf = _TIFFmalloc(nSize);//stripsize); 63 LPBYTE pStripBmp = pBufBmp; 64 for (i=0; i<nStrip; i++) 65 { 66 nret = TIFFReadEncodedStrip(pTif, i, pStripBmp, nSize); 67 int nHStrip = nret/(nw*nBps*nSpp/8); 68 pStripBmp += nHStrip * nwb; 69 } 70 //uint32* raster = (uint32*) _TIFFmalloc(nw*nh*sizeof(uint32)); 71 //TIFFReadRGBAImage(tif, nw, nh, raster, 0); 72 SaveBmpFile(pszBmp, nw,nh, nBps*nSpp, (LPBYTE)pBufBmp); 73 //_TIFFfree(buf); 74 delete [] pBufBmp; 75 TIFFClose(pTif); 76 } 77 78 // pszBmp: d:\1.bmp|d:\2.bmp 79 void SaveBmp2Tif(LPTSTR pszBmp, LPTSTR pszTif) 80 { 81 BITMAPFILEHEADER bfh; 82 BITMAPINFOHEADER bih; 83 DWORD pal[256]; 84 int i,x,nwb; 85 LPBYTE pBuf,pLine; 86 //LPDWORD pdw; 87 LPTSTR pBmpFile = pszBmp, pc; 88 89 TIFF *pTif = TIFFOpen(pszTif, "w+"); 90 if(!pTif) return; 91 92 pc = strchr(pszBmp, '|'); 93 while (pBmpFile) 94 { 95 if(pc) *pc = 0; 96 CFile fBmp(pBmpFile, CFile::modeRead); 97 fBmp.Read(&bfh, sizeof(bfh)); 98 fBmp.Read(&bih, sizeof(bih)); 99 if (bih.biBitCount == 1) 100 { 101 fBmp.Read(pal, 2*4); 102 nwb = (bih.biWidth+31)/32 * 4; 103 } else if (bih.biBitCount == 24) 104 { 105 nwb = (bih.biWidth*3+3)/4*4; 106 } 107 pBuf = (LPBYTE)malloc(nwb*bih.biHeight); 108 fBmp.Read(pBuf, nwb*bih.biHeight); 109 fBmp.Close(); 110 111 TIFFSetField(pTif, TIFFTAG_IMAGEWIDTH, bih.biWidth); 112 TIFFSetField(pTif, TIFFTAG_IMAGELENGTH, bih.biHeight); 113 //TIFFSetField(pTif, TIFFTAG_FILLORDER, FILLORDER_MSB2LSB); 114 TIFFSetField(pTif, TIFFTAG_PLANARCONFIG, PLANARCONFIG_CONTIG); //single image plane 115 TIFFSetField(pTif, TIFFTAG_XRESOLUTION, 300.0); // must be double 116 TIFFSetField(pTif, TIFFTAG_YRESOLUTION, 300.0); 117 TIFFSetField(pTif, TIFFTAG_RESOLUTIONUNIT, RESUNIT_INCH); 118 TIFFSetField(pTif, TIFFTAG_IMAGEDESCRIPTION, "PaperSize=2100x2970;"); 119 TIFFSetField(pTif, TIFFTAG_DOCUMENTNAME, "tif-test by chaos;"); 120 if (bih.biBitCount == 1) 121 { 122 TIFFSetField(pTif, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_MINISBLACK); // 0=black 123 TIFFSetField(pTif, TIFFTAG_COMPRESSION, COMPRESSION_CCITT_T6);// 124 TIFFSetField(pTif, TIFFTAG_BITSPERSAMPLE, 1); 125 TIFFSetField(pTif, TIFFTAG_SAMPLESPERPIXEL, 1); 126 TIFFSetField(pTif, TIFFTAG_ROWSPERSTRIP, bih.biHeight); 127 pLine = pBuf + nwb*(bih.biHeight-1); 128 for (i=0; i<bih.biHeight; i++) 129 { 130 //pdw = (LPDWORD)pLine; // invert color 131 //for(x=0; x<nwb/4; x++) 132 //{ 133 // *pdw = ~(*pdw); 134 // pdw++; 135 //} 136 TIFFWriteScanline(pTif, pLine, i); 137 pLine -= nwb; 138 } 139 } else if (bih.biBitCount == 24) 140 { 141 TIFFSetField(pTif, TIFFTAG_COMPRESSION, COMPRESSION_JPEG);//LZW);// 142 TIFFSetField(pTif, TIFFTAG_BITSPERSAMPLE, 8); 143 TIFFSetField(pTif, TIFFTAG_SAMPLESPERPIXEL, 3); 144 TIFFSetField(pTif, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_RGB); 145 int nJpegQuality = 75; 146 TIFFSetField(pTif, TIFFTAG_JPEGQUALITY, nJpegQuality); 147 TIFFSetField(pTif, TIFFTAG_JPEGCOLORMODE, JPEGCOLORMODE_RGB); 148 // bgr->rgb 149 BYTE btmp, *pDot; 150 pLine = pBuf + nwb*(bih.biHeight-1); 151 for (i=0; i<bih.biHeight; i++) 152 { 153 pDot = pLine; 154 for (x=0; x<bih.biWidth; x++) 155 { 156 btmp = *pDot; 157 *pDot = pDot[2]; 158 pDot[2] = btmp; 159 pDot += 3; 160 } 161 pLine -= nwb; 162 } 163 TIFFSetField(pTif, TIFFTAG_ROWSPERSTRIP, bih.biHeight); 164 TIFFWriteEncodedStrip(pTif, 0, pBuf, nwb*bih.biHeight); 165 //Sleep(100); 166 //TIFFWriteEncodedStrip(pTif, 1, pBuf+nwb*(bih.biHeight/2), nwb*(bih.biHeight/2)); 167 } 168 TIFFWriteDirectory(pTif); 169 free(pBuf); 170 171 if(pc) 172 { 173 pBmpFile = pc+1; 174 pc = strchr(pBmpFile, '|'); 175 } else 176 pBmpFile = NULL; 177 } 178 TIFFClose(pTif); 179 }
标签:bih,TIFFTAG,libtiff,使用,pTif,编译,nret,tif 来源: https://www.cnblogs.com/ybqjymy/p/13672543.html