1 头文件
libtiff定义一系列C
语言类型的数据结构,调用时包含的头文件为:
#include "tiffio.h"
2 文件读写
1 /* read from an existing TIFF image */
2 void main()
3 {
4 TIFF* tif = TIFFOpen("foo.tif", "r");
5 ... do stuff ...
6 TIFFClose(tif); // or TIFFFlush(tif);
7 }
8
9 /* create or overwrite a TIFF image */
10 void main()
11 {
12 TIFF* tif = TIFFOpen("foo.tif", "w");
13 ... do stuff ...
14 TIFFClose(tif); // or TIFFFlush(tif);
15 }
不同于stdio library
对TIFF文件的操作可以同时支持读和写,libtiff
对于TIFF
文件的操作模式是不可变更的,也就是说对一个指定的TIFF
文件,一次只能支持对文件的读或写中的一种操作。
3 多目录文件读写
TIFF
格式支持将多个图像文件存储为一个文件的功能,每个图片都有一个对应的数据结构称为一个目录,其中包括全部的信息格式和图像数据内容。图像之间可以是相关的也可以使不相关的。
1 #include "tiffio.h"
2 int main(int argc, char* argv[])
3 {
4 TIFF* tif = TIFFOpen(argv[1], "r");
5 if (tif)
6 {
7 int dircount = 0;
8 do {
9 dircount++;
10 } while (TIFFReadDirectory(tif));
11
12 printf("%d directories in %s
", dircount, argv[1]);
13 TIFFClose(tif);
14 }
15 return 0;
16 }
17
18 // write: TIFFWriteDirectory()
4 标签读取与设置
图像相关的信息例如宽、高、通道数、定向信息、颜色信息等。libtiff
中提供了获取和设置标签值的函数:TIFFGetField
和TIFFSetField
:
1 /* read the tags */
2 uint32 width, height;
3 uint16 ncn;
4
5 TIFFGetField(tif, TIFFTAG_IMAGEWIDTH, &width); // image width in pixels
6 TIFFGetField(tif, TIFFTAG_IMAGELENGTH, &height); // image height in pixels
7 TIFFGetField(tif, TIFFTAG_SAMPLESPERPIXEL, &ncn); // samples per pixel -> channels
8
9 cout << width << " " << height << " " << ncn << endl;
10
11 /* set the tags */
12 TIFFSetField( imageWrite, TIFFTAG_IMAGEWIDTH, width );
13 TIFFSetField( imageWrite, TIFFTAG_IMAGELENGTH, height );
14 TIFFSetField( imageWrite, TIFFTAG_BITSPERSAMPLE, 8); // 8 bits per channel
15 TIFFSetField( imageWrite, TIFFTAG_SAMPLESPERPIXEL, 4); // 4 channels
下面列出几种常用的TIFF
图像信息标签:
1 #define TIFFTAG_IMAGEWIDTH 256 /* image width in pixels */
2 #define TIFFTAG_IMAGELENGTH 257 /* image height in pixels */
3 #define TIFFTAG_BITSPERSAMPLE 258 /* bits per channel (sample) */
4 #define TIFFTAG_SAMPLESPERPIXEL 277 /* samples per pixel */
5 #define TIFFTAG_COMPRESSION 259 /* data compression technique */
6 #define TIFFTAG_PHOTOMETRIC 262 /* photometric interpretation */
7 #define TIFFTAG_PLANARCONFIG 284 /* storage organization */
8 #define TIFFTAG_XRESOLUTION 282 /* pixels/resolution in x */
9 #define TIFFTAG_YRESOLUTION 283 /* pixels/resolution in y */
10 #define TIFFTAG_RESOLUTIONUNIT 296 /* units of resolutions */
5 RGBA 图像读取与存储
对于4通道的图像,libtiff
提供的数据颜色顺序为A B G R,并且整合为32-bit无符号整型数据(每个通道为8 bits),数据读取方法为使用TIFFReadRGBAImage
函数:
1 #include "tiffio.h"
2
3 // first method: TIFFReadRGBAImage
4 int main(int argc, char* argv[])
5 {
6 TIFF* tif = TIFFOpen(argv[1], "r");
7 if (tif) {
8 uint32 w, h;
9 size_t npixels;
10 uint32* raster;
11
12 TIFFGetField(tif, TIFFTAG_IMAGEWIDTH, &w);
13 TIFFGetField(tif, TIFFTAG_IMAGELENGTH, &h);
14 npixels = w * h;
15 raster = (uint32*) _TIFFmalloc(npixels * sizeof (uint32));
16 if (raster != NULL) {
17 if (TIFFReadRGBAImage(tif, w, h, raster, 0)) {
18 ...process raster data...
19 }
20 _TIFFfree(raster);
21 }
22 TIFFClose(tif);
23 }
24 return 0;
25 }
26
27 // second method: TIFFRGBAImageBegin & TIFFRGBAImageGet
28 int main(int argc, char* argv[])
29 {
30 TIFF* tif = TIFFOpen(argv[1], "r");
31 if (tif) {
32 TIFFRGBAImage img;
33 char emsg[1024];
34
35 if (TIFFRGBAImageBegin(&img, tif, 0, emsg)) {
36 size_t npixels;
37 uint32* raster;
38
39 npixels = img.width * img.height;
40 raster = (uint32*) _TIFFmalloc(npixels * sizeof (uint32));
41 if (raster != NULL) {
42 if (TIFFRGBAImageGet(&img, raster, img.width, img.height)) {
43 ...process raster data...
44 }
45 _TIFFfree(raster);
46 }
47 TIFFRGBAImageEnd(&img);
48 } else
49 TIFFError(argv[1], emsg);
50 TIFFClose(tif);
51 }
52 return 0;
53 }
以TIFFReadRGBAImage
为例,读取图像后,获得其某一通道的结果,可使用:
1 // image channel read order : A B G R
2 if ( TIFFReadRGBAImage(tif, width, height, raster, 0) )
3 {
4 BYTE *imageR = new BYTE[nPixels];
5 // image pixels are in an inverted order, which is same as bmp format
6 uint32* rowPoint2Src = raster + (height-1)*width;
7 BYTE *rowPointerToR = imageR;
8
9 for ( int rows = height-1; rows >= 0; --rows )
10 {
11 uint32 *colPoint2Src = rowPoint2Src;
12 BYTE* colPoint2R = rowPointerToR;
13 for ( int cols = 0; cols < width; cols ++ )
14 {
15 // read the channel : A
16 *colPoint2R = (BYTE)TIFFGetA(*colPoint2Src);
17 // or : colPoint2R[0] = (BYTE)TIFFGetA(colPoint2Src[0]);
18 colPoint2R++;
19 colPoint2Src++;
20 }
21 rowPoint2Src -= width;
22 rowPointerToR += width;
23 }
24 cv::Mat imageR_mat( height, width, CV_8UC1, imageR, width );
25 imwrite("E:\0-Alpha.jpg", imageR_mat);
26
27 _TIFFfree(imageR);
28 }
如果想把4通道TIFF
文件,读入内存后转为Mat
格式,可以这么做:
1 /* save as a Mat */
2
3 cv::Mat image(height, width, CV_8UC4, cv::Scalar::all(0));
4 if ( TIFFReadRGBAImage(tif, width, height, raster, 0) )
5 {
6 uchar* imageData = (uchar*)image.data;
7 uint32* rowPoint2Src = raster + (height-1)*width;
8
9 for ( int rows = height-1; rows >= 0; --rows )
10 {
11 uint32 *colPoint2Src = rowPoint2Src;
12 // image pixels are in an inverted order, which is same as bmp format
13 uchar* colPoint = image.ptr<uchar>( height - rows - 1 );
14 for ( int cols = 0; cols < width; cols ++ )
15 {
16 *colPoint++ = (uchar)TIFFGetB(*colPoint2Src); // B
17 *colPoint++ = (uchar)TIFFGetG(*colPoint2Src); // G
18 *colPoint++ = (uchar)TIFFGetR(*colPoint2Src); // R
19 *colPoint++ = (uchar)TIFFGetA(*colPoint2Src); // A
20
21 colPoint2Src++;
22 }
23 rowPoint2Src -= width;
24 }
25 }
创建并保存4通道TIFF
图像可以按照下面的方法:
1 /* creat and write a ABGR tiff image */
2 #include <iostream>
3 #include <vector>
4
5 #include "cv.h"
6 #include "highgui.h"
7
8 #include "tiffio.h"
9
10 using namespace std;
11 using namespace cv;
12
13 void main()
14 {
15 cv::Mat imageGray = cv::imread( "C:\Users\Leo\Desktop\Test\0.jpg" );
16 cv::Mat imageAlpha = cv::imread( "C:\Users\Leo\Desktop\Test\0-R.jpg" );
17
18 if ( imageGray.channels() == 3 )
19 cv::cvtColor( imageGray, imageGray, CV_RGB2GRAY );
20 if ( imageAlpha.channels() == 3 )
21 cv::cvtColor( imageAlpha, imageAlpha, CV_RGB2GRAY );
22
23 int cols = imageGray.cols;
24 int rows = imageGray.rows;
25
26 cv::Mat imageMerged(rows, cols, CV_8UC4, cv::Scalar::all(0));
27
28 uchar* data = (uchar*) imageMerged.data;
29 uchar* data_gray = (uchar*) imageGray.data;
30 uchar* data_alpha = (uchar*) imageAlpha.data;
31
32 for ( int i=0; i<rows; i++ )
33 {
34 for ( int j=0; j<cols; j++ )
35 {
36 int index = i*cols + j;
37 data[index*4] = data_gray[index];
38 data[index*4+1] = data_gray[index];
39 data[index*4+2] = data_gray[index];
40 data[index*4+3] = data_alpha[index];
41 }
42 }
43
44 uint32 width, height;
45 width = cols;
46 height = rows;
47
48 /* save as PNG */
49 std::vector<int> compression_params;
50 compression_params.push_back(CV_IMWRITE_PNG_COMPRESSION);
51 compression_params.push_back(9);
52 cv::imwrite( "C:\Users\Leo\Desktop\Test\0-1.png", imageMerged, compression_params );
53
54 /* save as TIFF */
55 TIFF *imageWrite = TIFFOpen( "C:\Users\Leo\Desktop\Test\0-2.tif", "w" );
56 if ( imageWrite )
57 {
58 TIFFSetField( imageWrite, TIFFTAG_IMAGEWIDTH, width );
59 TIFFSetField( imageWrite, TIFFTAG_IMAGELENGTH, height );
60 TIFFSetField( imageWrite, TIFFTAG_COMPRESSION, COMPRESSION_PACKBITS);
61 TIFFSetField( imageWrite, TIFFTAG_PLANARCONFIG, PLANARCONFIG_CONTIG);
62 TIFFSetField( imageWrite, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_RGB);
63 TIFFSetField( imageWrite, TIFFTAG_BITSPERSAMPLE, 8);
64 TIFFSetField( imageWrite, TIFFTAG_SAMPLESPERPIXEL, 4);
65
66 uchar *bits = (uchar*) imageMerged.data;
67 // uchar* pdst = new uchar[cols*4];
68
69 for ( int i=0; i<rows; i++ )
70 {
71 // int curidx_bit = i * cols * 4;
72 // for ( int idx = 0; idx < cols; idx ++ )
73 // {
74 // int curidx_dst = idx * 4;
75 // int curidx_bit2 = curidx_bit + curidx_dst;
76 //
77 // pdst[curidx_dst] = bits[curidx_bit2];
78 // pdst[curidx_dst+1] = bits[curidx_bit2+1];
79 // pdst[curidx_dst+2] = bits[curidx_bit2+2];
80 // pdst[curidx_dst+3] = bits[curidx_bit2+3];
81 // }
82 TIFFWriteScanline( imageWrite, &bits[i*cols*4], i, 0 );
83 // TIFFWriteScanline( imageWrite, pdst, i, 0 );
84 }
85 TIFFClose( imageWrite );
86 }
87 else
88 {
89 std::cout << "Open file error!" << std::endl;
90 exit(1);
91 }
92 }
这段代码读取了两张图像,一张为灰度图,另一张为对应的Alpha
通道图像,然后将其转换为RGBA
图像。代码里给出了TIFFWriteScanline
写TIFF
的两种方法,其中注释掉的部分即为另一种方法。
6 三种图像I/O读写方法
libTIFF
中提供了三种文件读写方式:
- Scanline-based
- Strip-oriented
- Tile-oriented
此处不做过的介绍,详情请阅读 Using The TIFF Library~
Opencv中也有对TIFF
文件的操作,也是基于libTIFF
库,详情参考文件:grfmt_tiff.cpp
。
PS:
- LibTIFF (libtiff.org)
- LibTIFF (remotesensing.org)
- TIFF Documentation