LIBTIFF 圖像讀取與保存


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中提供了獲取和設置標簽值的函數:TIFFGetFieldTIFFSetField

 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圖像。代碼里給出了TIFFWriteScanlineTIFF的兩種方法,其中注釋掉的部分即為另一種方法。

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

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM