LIBTIFF 读取tif/tiff图像

Demo程序如下:

 1 int TestTIFFDemo()
 2 {
 3     //打开图像
 4     char* fileName = "D:/Image/Color/Beauty.tif";
 5     //char* fileName = "D:/Image/Projects/ShipImage/01001.tif";
 6     //char *fileName = "D:/Image/Color/Example400.tif";
 7     TIFF* tiff =TIFFOpen( fileName, "r");//打开Tiff文件,得到指针,以后所有的操作都通过指针进行
 8 
 9     //获取图像参数
10     int width, height;
11     TIFFGetField(tiff, TIFFTAG_IMAGEWIDTH, &width);
12     TIFFGetField(tiff, TIFFTAG_IMAGELENGTH, &height);
13 
14     //读取图像
15     //注意:TIFFReadRGBAImage读取的通道顺序为:ABGR
16     uint32* image;
17     int pixelCount = width*height;
18     image = (uint32*)malloc(pixelCount * sizeof (uint32));
19     TIFFReadRGBAImage(tiff, width, height, image, 1);
20 
21     //读取R通道
22     //由于tiff格式的图像数据与bmp图存储方式一致,是从下到上,所以读的时候,需要从下往上读
23     BYTE* RImage = new BYTE[pixelCount];    //为存放数据分配内存空间
24     uint32 *rowPointerToSrc = image + (height - 1)*width;
25     BYTE *rowPointerToR = RImage;
26     for (int y = height - 1; y >= 0; --y)
27     {
28         uint32 *colPointerToSrc = rowPointerToSrc;
29         BYTE *colPointerToR = rowPointerToR;
30         for (int x = 0; x <= width - 1; ++x)
31         {
32             colPointerToR[0] = (BYTE)TIFFGetR(colPointerToSrc[0]);//获取R通道
33             //TIFFGetB(colPointerToSrc[0]);//获取B通道
34             //TIFFGetG(colPointerToSrc[0]);//获取G通道
35 
36             colPointerToR++;
37             colPointerToSrc++;
38         }
39         rowPointerToSrc -= width;
40         rowPointerToR += width;
41     }
42 
43     //调试
44     //这里使用了OpenCV
45     Mat RImage_Mat(height, width, CV_8UC1, RImage, width);
46     imwrite("D:/111.bmp", RImage_Mat);
47 
48     //释放空间
49     _TIFFfree(image);
50     _TIFFfree(RImage);
51     TIFFClose(tiff);
52     return 0;
53 }

但是程序运行的时候出现了下面的警告提示
这里写图片描述

这里写图片描述

这里写图片描述

到网上找了下解决方案,都没有解决,最后,在OpenCV源码中找到了解决方案
修改后的程序如下:

 1 //警告处理
 2 static int grfmt_tiff_err_handler_init = 0;
 3 static void GrFmtSilentTIFFErrorHandler(const char*, const char*, va_list) {}
 4 int TestTIFFDemo()
 5 {
 6     //警告处理:防止出现unknown field with tag  33500 encountered警告
 7     if (!grfmt_tiff_err_handler_init)
 8     {
 9         grfmt_tiff_err_handler_init = 1;
10 
11         TIFFSetErrorHandler(GrFmtSilentTIFFErrorHandler);
12         TIFFSetWarningHandler(GrFmtSilentTIFFErrorHandler);
13     }
14 
15     //打开图像
16     char* fileName = "D:/Image/Color/Beauty.tif";
17     //char* fileName = "D:/Image/Projects/ShipImage/01001.tif";
18     //char *fileName = "D:/Image/Color/Example400.tif";
19     TIFF* tiff =TIFFOpen( fileName, "r");//打开Tiff文件,得到指针,以后所有的操作都通过指针进行
20 
21     //获取图像参数
22     int width, height;
23     TIFFGetField(tiff, TIFFTAG_IMAGEWIDTH, &width);
24     TIFFGetField(tiff, TIFFTAG_IMAGELENGTH, &height);
25 
26     //读取图像
27     //注意:TIFFReadRGBAImage读取的通道顺序为:ABGR
28     uint32* image;
29     int pixelCount = width*height;
30     image = (uint32*)malloc(pixelCount * sizeof (uint32));
31     TIFFReadRGBAImage(tiff, width, height, image, 1);
32 
33     //读取R通道
34     //由于tiff格式的图像数据与bmp图存储方式一致,是从下到上,所以读的时候,需要从下往上读
35     BYTE* RImage = new BYTE[pixelCount];    //为存放数据分配内存空间
36     uint32 *rowPointerToSrc = image + (height - 1)*width;
37     BYTE *rowPointerToR = RImage;
38     for (int y = height - 1; y >= 0; --y)
39     {
40         uint32 *colPointerToSrc = rowPointerToSrc;
41         BYTE *colPointerToR = rowPointerToR;
42         for (int x = 0; x <= width - 1; ++x)
43         {
44             colPointerToR[0] = (BYTE)TIFFGetR(colPointerToSrc[0]);//获取R通道
45             //TIFFGetB(colPointerToSrc[0]);//获取B通道
46             //TIFFGetG(colPointerToSrc[0]);//获取G通道
47 
48             colPointerToR++;
49             colPointerToSrc++;
50         }
51         rowPointerToSrc -= width;
52         rowPointerToR += width;
53     }
54 
55     //调试
56     //这里使用了OpenCV
57     Mat RImage_Mat(height, width, CV_8UC1, RImage, width);
58     imwrite("D:/111.bmp", RImage_Mat);
59 
60     //释放空间
61     _TIFFfree(image);
62     _TIFFfree(RImage);
63     TIFFClose(tiff);
64     return 0;
65 }

新程序可以正常运行了。
原图
这里写图片描述

结果图

这里写图片描述

原来是需要加入一个警告处理。

注意:
1、由于tiff格式的图像数据与bmp图存储方式一致,是从下到上,所以读的时候,需要从下往上读,否则图像会出错
2、 image = (uint32*)malloc(pixelCount * sizeof (uint32)); 如果需要申请的图像内存比较大,可以通过修改VS属性的办法申请大内存:properties->Linker->System->Heap Reserve Size
这里写图片描述

这里顺便贴出tiff的OpenCV的源码:
源码在sourcesmodulesimgcodecssrc中的grfmt_tiff.hpp和grfmt_tiff.cpp中
相关源码如下:

 1 //tif图像解码器(grfmt_tiff.hpp)
 2 class TiffDecoder : public BaseImageDecoder
 3 {
 4 public:
 5     TiffDecoder();
 6     virtual ~TiffDecoder();
 7 
 8     bool  readHeader();
 9     bool  readData( Mat& img );
10     void  close();
11     bool  nextPage();
12 
13     size_t signatureLength() const;
14     bool checkSignature( const String& signature ) const;
15     ImageDecoder newDecoder() const;
16 
17 protected:
18     void* m_tif;
19     int normalizeChannelsNumber(int channels) const;
20     bool readHdrData(Mat& img);
21     bool m_hdr;
22 };

其部分实现(grfmt_tiff.cpp)

  1 #include "tiff.h"
  2 #include "tiffio.h"
  3 
  4 static int grfmt_tiff_err_handler_init = 0;
  5 static void GrFmtSilentTIFFErrorHandler( const char*, const char*, va_list ) {}
  6 
  7 TiffDecoder::TiffDecoder()
  8 {
  9     m_tif = 0;
 10 
 11     //警告处理:防止出现unknown field with tag  33500 encountered警告
 12     if( !grfmt_tiff_err_handler_init )
 13     {
 14         grfmt_tiff_err_handler_init = 1;
 15 
 16         TIFFSetErrorHandler( GrFmtSilentTIFFErrorHandler );
 17         TIFFSetWarningHandler( GrFmtSilentTIFFErrorHandler );
 18     }
 19     m_hdr = false;
 20 }
 21 
 22 
 23 void TiffDecoder::close()
 24 {
 25     if( m_tif )
 26     {
 27         TIFF* tif = (TIFF*)m_tif;
 28         TIFFClose( tif );
 29         m_tif = 0;
 30     }
 31 }
 32 
 33 TiffDecoder::~TiffDecoder()
 34 {
 35     close();
 36 }
 37 
 38 size_t TiffDecoder::signatureLength() const
 39 {
 40     return 4;
 41 }
 42 
 43 bool TiffDecoder::checkSignature( const String& signature ) const
 44 {
 45     return signature.size() >= 4 &&
 46         (memcmp(signature.c_str(), fmtSignTiffII, 4) == 0 ||
 47         memcmp(signature.c_str(), fmtSignTiffMM, 4) == 0);
 48 }
 49 
 50 int TiffDecoder::normalizeChannelsNumber(int channels) const
 51 {
 52     return channels > 4 ? 4 : channels;
 53 }
 54 
 55 ImageDecoder TiffDecoder::newDecoder() const
 56 {
 57     return makePtr<TiffDecoder>();
 58 }
 59 
 60 //读取文件头
 61 bool TiffDecoder::readHeader()
 62 {
 63     bool result = false;
 64 
 65     TIFF* tif = static_cast<TIFF*>(m_tif);
 66     if (!m_tif)
 67     {
 68         // TIFFOpen() mode flags are different to fopen().  A 'b' in mode "rb" has no effect when reading.
 69         // http://www.remotesensing.org/libtiff/man/TIFFOpen.3tiff.html
 70         //打开tif文件
 71         tif = TIFFOpen(m_filename.c_str(), "r");
 72     }
 73 
 74     if( tif )
 75     {
 76         uint32 wdth = 0, hght = 0;
 77         uint16 photometric = 0;
 78         m_tif = tif;
 79 
 80         //获取属性
 81         if( TIFFGetField( tif, TIFFTAG_IMAGEWIDTH, &wdth ) &&
 82             TIFFGetField( tif, TIFFTAG_IMAGELENGTH, &hght ) &&
 83             TIFFGetField( tif, TIFFTAG_PHOTOMETRIC, &photometric ))
 84         {
 85             uint16 bpp=8, ncn = photometric > 1 ? 3 : 1;
 86             TIFFGetField( tif, TIFFTAG_BITSPERSAMPLE, &bpp );
 87             TIFFGetField( tif, TIFFTAG_SAMPLESPERPIXEL, &ncn );
 88 
 89             m_width = wdth;
 90             m_height = hght;
 91             if((bpp == 32 && ncn == 3) || photometric == PHOTOMETRIC_LOGLUV)
 92             {
 93                 m_type = CV_32FC3;
 94                 m_hdr = true;
 95                 return true;
 96             }
 97             m_hdr = false;
 98 
 99             if( bpp > 8 &&
100                ((photometric != 2 && photometric != 1) ||
101                 (ncn != 1 && ncn != 3 && ncn != 4)))
102                 bpp = 8;
103 
104             int wanted_channels = normalizeChannelsNumber(ncn);
105             switch(bpp)
106             {
107                 case 8:
108                     m_type = CV_MAKETYPE(CV_8U, photometric > 1 ? wanted_channels : 1);
109                     break;
110                 case 16:
111                     m_type = CV_MAKETYPE(CV_16U, photometric > 1 ? wanted_channels : 1);
112                     break;
113 
114                 case 32:
115                     m_type = CV_MAKETYPE(CV_32F, photometric > 1 ? 3 : 1);
116                     break;
117                 case 64:
118                     m_type = CV_MAKETYPE(CV_64F, photometric > 1 ? 3 : 1);
119                     break;
120 
121                 default:
122                     result = false;
123             }
124             result = true;
125         }
126     }
127 
128     if( !result )
129         close();
130 
131     return result;
132 }
133 
134 bool TiffDecoder::nextPage()
135 {
136     // Prepare the next page, if any.
137     return m_tif &&
138            TIFFReadDirectory(static_cast<TIFF*>(m_tif)) &&
139            readHeader();
140 }
141 
142 //读取图像数据
143 bool  TiffDecoder::readData( Mat& img )
144 {
145     if(m_hdr && img.type() == CV_32FC3)
146     {
147         return readHdrData(img);
148     }
149     bool result = false;
150     bool color = img.channels() > 1;
151     uchar* data = img.ptr();
152 
153     if( img.depth() != CV_8U && img.depth() != CV_16U && img.depth() != CV_32F && img.depth() != CV_64F )
154         return false;
155 
156     //读图像数据
157     if( m_tif && m_width && m_height )
158     {
159         TIFF* tif = (TIFF*)m_tif;
160         uint32 tile_width0 = m_width, tile_height0 = 0;
161         int x, y, i;
162         int is_tiled = TIFFIsTiled(tif);
163         uint16 photometric;
164         TIFFGetField( tif, TIFFTAG_PHOTOMETRIC, &photometric );
165         uint16 bpp = 8, ncn = photometric > 1 ? 3 : 1;
166         TIFFGetField( tif, TIFFTAG_BITSPERSAMPLE, &bpp );
167         TIFFGetField( tif, TIFFTAG_SAMPLESPERPIXEL, &ncn );
168         const int bitsPerByte = 8;
169         int dst_bpp = (int)(img.elemSize1() * bitsPerByte);
170         int wanted_channels = normalizeChannelsNumber(img.channels());
171 
172         if(dst_bpp == 8)
173         {
174             char errmsg[1024];
175             if(!TIFFRGBAImageOK( tif, errmsg ))
176             {
177                 close();
178                 return false;
179             }
180         }
181 
182         if( (!is_tiled) ||
183             (is_tiled &&
184             TIFFGetField( tif, TIFFTAG_TILEWIDTH, &tile_width0 ) &&
185             TIFFGetField( tif, TIFFTAG_TILELENGTH, &tile_height0 )))
186         {
187             if(!is_tiled)
188                 TIFFGetField( tif, TIFFTAG_ROWSPERSTRIP, &tile_height0 );
189 
190             if( tile_width0 <= 0 )
191                 tile_width0 = m_width;
192 
193             if( tile_height0 <= 0 )
194                 tile_height0 = m_height;
195 
196             AutoBuffer<uchar> _buffer( size_t(8) * tile_height0*tile_width0);
197             uchar* buffer = _buffer;
198             ushort* buffer16 = (ushort*)buffer;
199             float* buffer32 = (float*)buffer;
200             double* buffer64 = (double*)buffer;
201             int tileidx = 0;
202 
203             for( y = 0; y < m_height; y += tile_height0, data += img.step*tile_height0 )
204             {
205                 int tile_height = tile_height0;
206 
207                 if( y + tile_height > m_height )
208                     tile_height = m_height - y;
209 
210                 for( x = 0; x < m_width; x += tile_width0, tileidx++ )
211                 {
212                     int tile_width = tile_width0, ok;
213 
214                     if( x + tile_width > m_width )
215                         tile_width = m_width - x;
216 
217                     switch(dst_bpp)
218                     {
219                         case 8:
220                         {
221                             uchar * bstart = buffer;
222                             if( !is_tiled )
223                                 ok = TIFFReadRGBAStrip( tif, y, (uint32*)buffer );
224                             else
225                             {
226                                 ok = TIFFReadRGBATile( tif, x, y, (uint32*)buffer );
227                                 //Tiles fill the buffer from the bottom up
228                                 bstart += (tile_height0 - tile_height) * tile_width0 * 4;
229                             }
230                             if( !ok )
231                             {
232                                 close();
233                                 return false;
234                             }
235 
236                             for( i = 0; i < tile_height; i++ )
237                                 if( color )
238                                 {
239                                     if (wanted_channels == 4)
240                                     {
241                                         icvCvt_BGRA2RGBA_8u_C4R( bstart + i*tile_width0*4, 0,
242                                                              data + x*4 + img.step*(tile_height - i - 1), 0,
243                                                              cvSize(tile_width,1) );
244                                     }
245                                     else
246                                     {
247                                         icvCvt_BGRA2BGR_8u_C4C3R( bstart + i*tile_width0*4, 0,
248                                                              data + x*3 + img.step*(tile_height - i - 1), 0,
249                                                              cvSize(tile_width,1), 2 );
250                                     }
251                                 }
252                                 else
253                                     icvCvt_BGRA2Gray_8u_C4C1R( bstart + i*tile_width0*4, 0,
254                                                               data + x + img.step*(tile_height - i - 1), 0,
255                                                               cvSize(tile_width,1), 2 );
256                             break;
257                         }
258 
259                         case 16:
260                         {
261                             if( !is_tiled )
262                                 ok = (int)TIFFReadEncodedStrip( tif, tileidx, (uint32*)buffer, (tsize_t)-1 ) >= 0;
263                             else
264                                 ok = (int)TIFFReadEncodedTile( tif, tileidx, (uint32*)buffer, (tsize_t)-1 ) >= 0;
265 
266                             if( !ok )
267                             {
268                                 close();
269                                 return false;
270                             }
271 
272                             for( i = 0; i < tile_height; i++ )
273                             {
274                                 if( color )
275                                 {
276                                     if( ncn == 1 )
277                                     {
278                                         icvCvt_Gray2BGR_16u_C1C3R(buffer16 + i*tile_width0*ncn, 0,
279                                                                   (ushort*)(data + img.step*i) + x*3, 0,
280                                                                   cvSize(tile_width,1) );
281                                     }
282                                     else if( ncn == 3 )
283                                     {
284                                         icvCvt_RGB2BGR_16u_C3R(buffer16 + i*tile_width0*ncn, 0,
285                                                                (ushort*)(data + img.step*i) + x*3, 0,
286                                                                cvSize(tile_width,1) );
287                                     }
288                                     else if (ncn == 4)
289                                     {
290                                         if (wanted_channels == 4)
291                                         {
292                                             icvCvt_BGRA2RGBA_16u_C4R(buffer16 + i*tile_width0*ncn, 0,
293                                                 (ushort*)(data + img.step*i) + x * 4, 0,
294                                                 cvSize(tile_width, 1));
295                                         }
296                                         else
297                                         {
298                                             icvCvt_BGRA2BGR_16u_C4C3R(buffer16 + i*tile_width0*ncn, 0,
299                                                 (ushort*)(data + img.step*i) + x * 3, 0,
300                                                 cvSize(tile_width, 1), 2);
301                                         }
302                                     }
303                                     else
304                                     {
305                                         icvCvt_BGRA2BGR_16u_C4C3R(buffer16 + i*tile_width0*ncn, 0,
306                                                                (ushort*)(data + img.step*i) + x*3, 0,
307                                                                cvSize(tile_width,1), 2 );
308                                     }
309                                 }
310                                 else
311                                 {
312                                     if( ncn == 1 )
313                                     {
314                                         memcpy((ushort*)(data + img.step*i)+x,
315                                                buffer16 + i*tile_width0*ncn,
316                                                tile_width*sizeof(buffer16[0]));
317                                     }
318                                     else
319                                     {
320                                         icvCvt_BGRA2Gray_16u_CnC1R(buffer16 + i*tile_width0*ncn, 0,
321                                                                (ushort*)(data + img.step*i) + x, 0,
322                                                                cvSize(tile_width,1), ncn, 2 );
323                                     }
324                                 }
325                             }
326                             break;
327                         }
328 
329                         case 32:
330                         case 64:
331                         {
332                             if( !is_tiled )
333                                 ok = (int)TIFFReadEncodedStrip( tif, tileidx, buffer, (tsize_t)-1 ) >= 0;
334                             else
335                                 ok = (int)TIFFReadEncodedTile( tif, tileidx, buffer, (tsize_t)-1 ) >= 0;
336 
337                             if( !ok || ncn != 1 )
338                             {
339                                 close();
340                                 return false;
341                             }
342 
343                             for( i = 0; i < tile_height; i++ )
344                             {
345                                 if(dst_bpp == 32)
346                                 {
347                                     memcpy((float*)(data + img.step*i)+x,
348                                            buffer32 + i*tile_width0*ncn,
349                                            tile_width*sizeof(buffer32[0]));
350                                 }
351                                 else
352                                 {
353                                     memcpy((double*)(data + img.step*i)+x,
354                                          buffer64 + i*tile_width0*ncn,
355                                          tile_width*sizeof(buffer64[0]));
356                                 }
357                             }
358 
359                             break;
360                         }
361                         default:
362                         {
363                             close();
364                             return false;
365                         }
366                     }
367                 }
368             }
369 
370             result = true;
371         }
372     }
373 
374     return result;
375 }

OpenCV源码真是个好东西,能够从中学习到很多优秀的编程技巧,能够帮助你解决很多问题。

原文地址:https://www.cnblogs.com/ybqjymy/p/13678821.html