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的源码:
源码在sources\modules\imgcodecs\src\中的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源码真是个好东西,能够从中学习到很多优秀的编程技巧,能够帮助你解决很多问题。