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