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\n", 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