zoukankan      html  css  js  c++  java
  • 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.libtiffmakefile.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编译:VC6VC98BinVCVARS32.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静态库 在libtiffmakefile.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修饰。

    【使用】

    #include "tiffio.h"
    int main()
    {
      int i,nret,nw,nh,nbpp,npage=1;
      TIFF* pTif = TIFFOpen("d:\1.tif", "r");
      TIFFSetDirectory(pTif, 1); // 跳到指定的页数1
      nret = TIFFGetField(pTif, TIFFTAG_IMAGEWIDTH, &nw); // 获取图像长、宽
      nret = TIFFGetField(pTif, TIFFTAG_IMAGELENGTH, &nh);
      npage = TIFFNumberOfDirectories(pTif); // 读取页数
      TIFFClose(pTif);
    }
    

     错误和警告信息

    libtiff使用CallBack方式显示错误和警告。定义如下函数

    void TIFFErrorProc(const char* pModule, const char* pFormat, va_list pArg)
    {
        char szMsg[512];
        vsprintf(szMsg, pFormat, pArg);
        printf("tifferr-%s: %s", pModule, szMsg);
    }
    //然后设置为错误和警告处理函数,最好分开两个。
    TIFFSetWarningHandler(TIFFWarnProc);
    TIFFSetErrorHandler(TIFFErrorProc);
    //其中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转换的代码例子

    void SaveBmpFile(LPTSTR pszBmp, int nW, int nH, int nBpp, LPBYTE pBuf)
    {
    	BITMAPFILEHEADER bfh={0};
    	BITMAPINFOHEADER bih={0};
    	DWORD pal[256]={0,0XFFFFFF};
    	int nLineByte,nwb,y;
    
    	bfh.bfType = 'MB';
    	bfh.bfSize = sizeof bfh;
    	bfh.bfOffBits = sizeof(bfh)+sizeof(bih);
    	if(nBpp == 1)
    		bfh.bfOffBits += 8;
    	bih.biSize = sizeof bih;
    	bih.biWidth = nW;
    	bih.biHeight = nH;
    	bih.biBitCount = nBpp;
    	bih.biPlanes = 1;
    	nwb = (nW*nBpp+31)/32*4; //bmp 4 bytes align
    	bih.biSizeImage = nwb*nH;
    
    	CFile fBmp(pszBmp, CFile::modeCreate|CFile::modeWrite);
    	fBmp.Write(&bfh, sizeof bfh);
    	fBmp.Write(&bih, sizeof bih);
    	if(nBpp == 1)
    		fBmp.Write(pal, 8);
    
    	nLineByte = (nW*nBpp+7)/8; //tif 1 bytes align
    	LPBYTE pLine = pBuf+nLineByte*(nH-1);
    	for(y=0; y<nH; y++)
    	{
    		fBmp.Write(pLine, nLineByte);
    		if(nwb > nLineByte)
    			fBmp.Write(pal, nwb-nLineByte);
    		pLine -= nLineByte;
    	}
    	fBmp.Close();
    }
    void SaveTif2Bmp(LPTSTR pszTif, LPTSTR pszBmp)
    {
    	TIFF* pTif = TIFFOpen(pszTif, "r");
    	if(!pTif)
    		return;
    	int i,nret,nw,nh,npage,nrps;
    	unsigned short nComp, nPho, nBps,nSpp;
    	TIFFSetErrorHandler(TIFFErrorHandler);
    	nret = TIFFGetField(pTif, TIFFTAG_IMAGEWIDTH, &nw);
    	nret = TIFFGetField(pTif, TIFFTAG_IMAGELENGTH, &nh);
    	nret = TIFFGetField(pTif, TIFFTAG_COMPRESSION, &nComp);
    	nret = TIFFGetField(pTif, TIFFTAG_PHOTOMETRIC, &nPho);
    	nret = TIFFGetField(pTif, TIFFTAG_BITSPERSAMPLE, &nBps);
    	nret = TIFFGetField(pTif, TIFFTAG_SAMPLESPERPIXEL, &nSpp);
    	nret = TIFFGetField(pTif, TIFFTAG_ROWSPERSTRIP, &nrps);
    	npage = TIFFNumberOfDirectories(pTif);
    	int nSize = TIFFStripSize(pTif);
    	int nStrip = TIFFNumberOfStrips(pTif);
    	uint32* bc; // wrong size??
    	nret = TIFFGetField(pTif, TIFFTAG_STRIPBYTECOUNTS, &bc);
    
    	int nwb = (nw*nBps*nSpp+31)/32*4; // 4-byte align for bmp
    	LPBYTE pBufBmp = new BYTE[nwb*nh];
    	//uint32 stripsize = bc[0];
    	//tdata_t buf = _TIFFmalloc(nSize);//stripsize);
    	LPBYTE pStripBmp = pBufBmp;
    	for (i=0; i<nStrip; i++)
    	{
    		nret = TIFFReadEncodedStrip(pTif, i, pStripBmp, nSize);
    		int nHStrip = nret/(nw*nBps*nSpp/8);
    		pStripBmp += nHStrip * nwb;
    	}
    	//uint32* raster = (uint32*) _TIFFmalloc(nw*nh*sizeof(uint32));
    	//TIFFReadRGBAImage(tif, nw, nh, raster, 0);
    	SaveBmpFile(pszBmp, nw,nh, nBps*nSpp, (LPBYTE)pBufBmp);
    	//_TIFFfree(buf);
    	delete [] pBufBmp;
    	TIFFClose(pTif);
    }
    
    // pszBmp: d:1.bmp|d:2.bmp
    void SaveBmp2Tif(LPTSTR pszBmp, LPTSTR pszTif) 
    {
    	BITMAPFILEHEADER bfh;
    	BITMAPINFOHEADER bih;
    	DWORD pal[256];
    	int i,x,nwb;
    	LPBYTE pBuf,pLine;
    	//LPDWORD pdw;
    	LPTSTR pBmpFile = pszBmp, pc;
    
    	TIFF *pTif = TIFFOpen(pszTif, "w+");
    	if(!pTif)	return;
    
    	pc = strchr(pszBmp, '|');
    	while (pBmpFile)
    	{
    		if(pc)	*pc = 0;
    		CFile fBmp(pBmpFile, CFile::modeRead);
    		fBmp.Read(&bfh, sizeof(bfh));
    		fBmp.Read(&bih, sizeof(bih));
    		if (bih.biBitCount == 1)
    		{
    			fBmp.Read(pal, 2*4);
    			nwb = (bih.biWidth+31)/32 * 4;
    		} else if (bih.biBitCount == 24)
    		{
    			nwb = (bih.biWidth*3+3)/4*4;
    		}
    		pBuf = (LPBYTE)malloc(nwb*bih.biHeight);
    		fBmp.Read(pBuf, nwb*bih.biHeight);
    		fBmp.Close();
    
    		TIFFSetField(pTif, TIFFTAG_IMAGEWIDTH, bih.biWidth);
    		TIFFSetField(pTif, TIFFTAG_IMAGELENGTH, bih.biHeight);
    		//TIFFSetField(pTif, TIFFTAG_FILLORDER, FILLORDER_MSB2LSB);
    		TIFFSetField(pTif, TIFFTAG_PLANARCONFIG, PLANARCONFIG_CONTIG); //single image plane
    		TIFFSetField(pTif, TIFFTAG_XRESOLUTION, 300.0); // must be double
    		TIFFSetField(pTif, TIFFTAG_YRESOLUTION, 300.0);
    		TIFFSetField(pTif, TIFFTAG_RESOLUTIONUNIT, RESUNIT_INCH);
    		TIFFSetField(pTif, TIFFTAG_IMAGEDESCRIPTION, "PaperSize=2100x2970;");
    		TIFFSetField(pTif, TIFFTAG_DOCUMENTNAME, "tif-test by chaos;");
    		if (bih.biBitCount == 1)
    		{
    			TIFFSetField(pTif, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_MINISBLACK); // 0=black
    			TIFFSetField(pTif, TIFFTAG_COMPRESSION, COMPRESSION_CCITT_T6);//
    			TIFFSetField(pTif, TIFFTAG_BITSPERSAMPLE, 1);
    			TIFFSetField(pTif, TIFFTAG_SAMPLESPERPIXEL, 1);
    			TIFFSetField(pTif, TIFFTAG_ROWSPERSTRIP, bih.biHeight);
    			pLine = pBuf + nwb*(bih.biHeight-1);
    			for (i=0; i<bih.biHeight; i++)
    			{
    				//pdw = (LPDWORD)pLine; // invert color
    				//for(x=0; x<nwb/4; x++)
    				//{
    				//	*pdw = ~(*pdw);
    				//	pdw++;
    				//}
    				TIFFWriteScanline(pTif, pLine, i);
    				pLine -= nwb;
    			}
    		} else if (bih.biBitCount == 24)
    		{
    			TIFFSetField(pTif, TIFFTAG_COMPRESSION, COMPRESSION_JPEG);//LZW);//
    			TIFFSetField(pTif, TIFFTAG_BITSPERSAMPLE, 8);
    			TIFFSetField(pTif, TIFFTAG_SAMPLESPERPIXEL, 3);
    			TIFFSetField(pTif, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_RGB);
    			int nJpegQuality = 75;
    			TIFFSetField(pTif, TIFFTAG_JPEGQUALITY, nJpegQuality);
    			TIFFSetField(pTif, TIFFTAG_JPEGCOLORMODE, JPEGCOLORMODE_RGB);
    			// bgr->rgb
    			BYTE btmp, *pDot;
    			pLine = pBuf + nwb*(bih.biHeight-1);
    			for (i=0; i<bih.biHeight; i++)
    			{
    				pDot = pLine;
    				for (x=0; x<bih.biWidth; x++)
    				{
    					btmp = *pDot;
    					*pDot = pDot[2];
    					pDot[2] = btmp;
    					pDot += 3;
    				}
    				pLine -= nwb;
    			}
    			TIFFSetField(pTif, TIFFTAG_ROWSPERSTRIP, bih.biHeight);
    			TIFFWriteEncodedStrip(pTif, 0, pBuf, nwb*bih.biHeight);
    			//Sleep(100);
    			//TIFFWriteEncodedStrip(pTif, 1, pBuf+nwb*(bih.biHeight/2), nwb*(bih.biHeight/2));
    		}
    		TIFFWriteDirectory(pTif);
    		free(pBuf);
    
    		if(pc)
    		{
    			pBmpFile = pc+1;
    			pc = strchr(pBmpFile, '|');
    		} else
    			pBmpFile = NULL;
    	}
    	TIFFClose(pTif);
    }
    
  • 相关阅读:
    nginx-1.8.1的安装
    ElasticSearch 在3节点集群的启动
    The type java.lang.CharSequence cannot be resolved. It is indirectly referenced from required .class files
    sqoop导入导出对mysql再带数据库test能跑通用户自己建立的数据库则不行
    LeetCode 501. Find Mode in Binary Search Tree (找到二叉搜索树的众数)
    LeetCode 437. Path Sum III (路径之和之三)
    LeetCode 404. Sum of Left Leaves (左子叶之和)
    LeetCode 257. Binary Tree Paths (二叉树路径)
    LeetCode Questions List (LeetCode 问题列表)- Java Solutions
    LeetCode 561. Array Partition I (数组分隔之一)
  • 原文地址:https://www.cnblogs.com/chaos77/p/6740242.html
Copyright © 2011-2022 走看看