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修饰。

     

    【使用】

     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 }
  • 相关阅读:
    昨晚值班将发dla的程序改好后放入正式环境
    本来今天打算不带电脑去值班
    有时候你会觉得,你的不真实让人怀疑,自己却非常尴尬
    其实对于公司的事情分布,我是昨天没有干什么
    异常处理
    反射及内置方法
    绑定方法与非绑定方法
    面向对象之多态
    面向对象之封装
    面向对象三大特性
  • 原文地址:https://www.cnblogs.com/ybqjymy/p/13672543.html
Copyright © 2011-2022 走看看