libtiff的使用

转自: https://www.cnblogs.com/chaos77/p/6740242.html

最近的一个项目用到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

2

3

4

5

6

7

8

9

10

11

#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方式显示错误和警告。定义如下函数

1

2

3

4

5

6

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转换的代码例子

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

78

79

80

81

82

83

84

85

86

87

88

89

90

91

92

93

94

95

96

97

98

99

100

101

102

103

104

105

106

107

108

109

110

111

112

113

114

115

116

117

118

119

120

121

122

123

124

125

126

127

128

129

130

131

132

133

134

135

136

137

138

139

140

141

142

143

144

145

146

147

148

149

150

151

152

153

154

155

156

157

158

159

160

161

162

163

164

165

166

167

168

169

170

171

172

173

174

175

176

177

178

179

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

}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值