EMAN2
tifio.cpp
Go to the documentation of this file.
1/*
2 * Author: Steven Ludtke, 04/10/2003 (sludtke@bcm.edu)
3 * Copyright (c) 2000-2006 Baylor College of Medicine
4 *
5 * This software is issued under a joint BSD/GNU license. You may use the
6 * source code in this file under either license. However, note that the
7 * complete EMAN2 and SPARX software packages have some GPL dependencies,
8 * so you are responsible for compliance with the licenses of these packages
9 * if you opt to use BSD licensing. The warranty disclaimer below holds
10 * in either instance.
11 *
12 * This complete copyright notice must be included in any revised version of the
13 * source code. Additional authorship citations may be added, but existing
14 * author citations must be preserved.
15 *
16 * This program is free software; you can redistribute it and/or modify
17 * it under the terms of the GNU General Public License as published by
18 * the Free Software Foundation; either version 2 of the License, or
19 * (at your option) any later version.
20 *
21 * This program is distributed in the hope that it will be useful,
22 * but WITHOUT ANY WARRANTY; without even the implied warranty of
23 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
24 * GNU General Public License for more details.
25 *
26 * You should have received a copy of the GNU General Public License
27 * along with this program; if not, write to the Free Software
28 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
29 *
30 * */
31
32#ifdef USE_TIFF
33
34#include "tifio.h"
35#include "util.h"
36#include "geometry.h"
37
38#include <tiffio.h>
39#include <climits>
40
41using namespace EMAN;
42
43namespace {
44 /* x% weighting -> fraction of full color */
45 constexpr int PCT(int x) {return (x*255+127)/100;}
46 const int RED = PCT(30); /* 30% */
47 const int GREEN = PCT(59); /* 59% */
48 const int BLUE = PCT(11); /* 11% */
49}
50
51TiffIO::TiffIO(const string & fname, IOMode rw)
52: ImageIO(fname, rw), tiff_file(0),
53 bitspersample(0), photometric(0),
54 rendermin(0.0), rendermax(0.0), renderbits(16), nimg(1),
55 is_big_endian(ByteOrder::is_host_big_endian())
56{}
57
58TiffIO::~TiffIO()
59{
60 if (tiff_file) {
61 TIFFClose(tiff_file);
62 tiff_file = 0;
63 }
64}
65
66void TiffIO::init()
67{
69
70 if (initialized) {
71 return;
72 }
73
74 initialized = true;
75
76 bool is_new_file = false;
77
78 FILE * tmp_in = sfopen(filename, rw_mode, & is_new_file, true);
79
80 if (! tmp_in) {
81 throw ImageReadException(filename, "open TIFF");
82 }
83
84 if (! is_new_file) {
85 char buf[64];
86
87 if (fread(buf, sizeof(buf), 1, tmp_in) != 1) {
88 throw ImageReadException(filename, "first block");
89 }
90
91 if (!is_valid(&buf)) {
92 throw ImageReadException(filename, "invalid TIFF");
93 }
94
95 is_big_endian = (buf[0] == TIFF_BIG_ENDIAN);
96 }
97
98 fclose(tmp_in);
99 tmp_in = 0;
100
101 TIFFSetWarningHandler(0);
102
103 if (rw_mode == ImageIO::READ_ONLY) {
104 tiff_file = TIFFOpen(filename.c_str(), "r");
105
106 if (! tiff_file) {
107 throw ImageReadException(filename, "open TIFF");
108 }
109
110 TIFFGetField(tiff_file, TIFFTAG_BITSPERSAMPLE, &bitspersample);
111
112 if (bitspersample != CHAR_BIT &&
113 bitspersample != (CHAR_BIT * sizeof(short)) &&
114 bitspersample != (CHAR_BIT * sizeof(float)) ) {
115
116 char desc[256];
117 sprintf(desc, "invalid %d bits. only %d-bit and %d-bit TIFF are supported",
118 bitspersample, CHAR_BIT, (int)(CHAR_BIT * sizeof(short)));
119 throw ImageReadException(filename, desc);
120 }
121 }
122 else {
123 tiff_file = TIFFOpen(filename.c_str(), "w");
124
125 if (! tiff_file) {
126 throw ImageReadException(filename, "open TIFF");
127 }
128 }
129
130 if (tiff_file) {
131 nimg = 0;
132
133 do {
134 nimg++;
135 } while (TIFFReadDirectory(tiff_file));
136 }
137
138 EXITFUNC;
139}
140
141bool TiffIO::is_valid(const void *first_block)
142{
143 ENTERFUNC;
144 bool result = false;
145
146 if (! first_block) {
147 result = false;
148 }
149 else {
150 const char *data = static_cast < const char *>(first_block);
151
152 if ((data[0] == data[1]) && (data[0] == TIFF_LITTLE_ENDIAN ||
153 data[1] == TIFF_BIG_ENDIAN)) {
154 result = true;
155 }
156 }
157
158 EXITFUNC;
159 return result;
160}
161
162int TiffIO::get_nimg()
163{
164 init();
165
166 return nimg;
167}
168
169int TiffIO::read_header(Dict & dict, int image_index, const Region * area, bool)
170{
171 ENTERFUNC;
172
173 init();
174
175 if (image_index == -1) {
176 image_index = 0;
177 }
178
179 TIFFSetDirectory(tiff_file, image_index);
180
181 int nx = 0;
182 int ny = 0;
183
184 TIFFGetField(tiff_file, TIFFTAG_IMAGEWIDTH, &nx);
185 TIFFGetField(tiff_file, TIFFTAG_IMAGELENGTH, &ny);
186
187 check_region(area, IntSize(nx, ny));
188
189 float min = 0;
190 float max = 0;
191
192 TIFFDataType data_type = TIFF_NOTYPE;
193
194 float resolution_x = 0;
195 float resolution_y = 0;
196
197 TIFFGetField(tiff_file, TIFFTAG_MINSAMPLEVALUE, &min);
198 TIFFGetField(tiff_file, TIFFTAG_MAXSAMPLEVALUE, &max);
199
200 TIFFGetField(tiff_file, TIFFTAG_PHOTOMETRIC, &photometric);
201
202 TIFFGetField(tiff_file, TIFFTAG_SAMPLEFORMAT, &data_type);
203 TIFFGetField(tiff_file, TIFFTAG_XRESOLUTION, &resolution_x);
204 TIFFGetField(tiff_file, TIFFTAG_YRESOLUTION, &resolution_y);
205
206 int xlen = 0, ylen = 0;
207
208 EMUtil::get_region_dims(area, nx, &xlen, ny, &ylen);
209
210 dict["nx"] = xlen;
211 dict["ny"] = ylen;
212 dict["nz"] = 1;
213
214 dict["nimg"] = nimg;
215 dict["minimum"] = min;
216 dict["maximum"] = max;
217
218 if (bitspersample == CHAR_BIT) {
219 dict["datatype"] = EMUtil::EM_UCHAR;
220 }
221 else if (bitspersample == sizeof(unsigned short) * CHAR_BIT) {
222 dict["datatype"] = EMUtil::EM_USHORT;
223 }
224 else if (bitspersample == sizeof(float) * CHAR_BIT) {
225 dict["datatype"] = EMUtil::EM_FLOAT;
226 }
227
228 dict["TIFF.bitspersample"] = bitspersample;
229 dict["TIFF.resolution_x"] = resolution_x;
230 dict["TIFF.resolution_y"] = resolution_y;
231
232 EXITFUNC;
233 return 0;
234}
235
236int TiffIO::read_data(float *rdata, int image_index, const Region * area, bool)
237{
238 ENTERFUNC;
239
240 check_read_access(image_index, rdata);
241
242 TIFFSetDirectory(tiff_file, image_index);
243
244 int nx = 0;
245 int ny = 0;
246
247 TIFFGetField(tiff_file, TIFFTAG_IMAGEWIDTH, & nx);
248 TIFFGetField(tiff_file, TIFFTAG_IMAGELENGTH, & ny);
249
250 int err = 0;
251
252 /* for grey scale image, use TIFFReadEncodedStrip() and TIFFReadEncodedTile()
253 * because the reading of strip image is twice time faster than TIFFReadRGBAImage -Grant */
254
255 if (photometric == PHOTOMETRIC_MINISWHITE || photometric == PHOTOMETRIC_MINISBLACK) {
256 unsigned char *cdata; //buffer for strip or tile
257
258 if (TIFFIsTiled(tiff_file)) {
259 tsize_t tileSize = TIFFTileSize(tiff_file);
260 tsize_t tileMax = TIFFNumberOfTiles(tiff_file);
261 tsize_t tileCount;
262
263 uint32 tileWidth, tileLength;
264 TIFFGetField(tiff_file, TIFFTAG_TILEWIDTH, &tileWidth);
265 TIFFGetField(tiff_file, TIFFTAG_TILELENGTH, &tileLength);
266
267 if ((cdata=(unsigned char*)_TIFFmalloc(tileSize))==NULL){
268 fprintf(stderr,"Error: Could not allocate enough memory\n");
269 return -1;
270 }
271
272 int tilePerLine = nx/tileWidth + 1;
273 int NX, NY; // (NX, NY) is the cordinates of tile
274 int xpos, ypos; // (xpos, ypos) is the actual coordinates of pixel (j,i) in image
275
276 for (tileCount=0; tileCount<tileMax; tileCount++) {
277 if (TIFFReadEncodedTile(tiff_file, tileCount, cdata, tileSize) == -1) {
278 fprintf(stderr,"Error reading tiled image\n");
279 return -1;
280 }
281 else {
282 NX = tileCount%tilePerLine;
283 NY = tileCount/tilePerLine;
284 uint32 i, j;
285
286 for (i=0; i<tileLength; i++) {
287 for (j=0; j<tileWidth; j++) {
288 xpos = NX*tileWidth + j;
289 ypos = NY*tileLength + i;
290
291 if (bitspersample == CHAR_BIT) {
292 if (xpos<nx && ypos<ny) { // discard those pixel in tile which is out of actual image's boundary
293 photometric == PHOTOMETRIC_MINISWHITE ?
294 rdata[nx*(ny-1)-(ypos*nx)+xpos] = -(float) ((unsigned char*)cdata)[i*tileWidth+j] :
295 rdata[nx*(ny-1)-(ypos*nx)+xpos] = (float) ((unsigned char*)cdata)[i*tileWidth+j];
296 }
297 }
298 else if (bitspersample == sizeof(unsigned short) * CHAR_BIT) {
299 if(xpos<nx && ypos<ny) { // discard those pixel in tile which is out of actual image's boundary
300 photometric == PHOTOMETRIC_MINISWHITE ?
301 rdata[nx*(ny-1)-(ypos*nx)+xpos] = -(float) ((unsigned short*)cdata)[i*tileWidth+j] :
302 rdata[nx*(ny-1)-(ypos*nx)+xpos] = (float) ((unsigned short*)cdata)[i*tileWidth+j];
303 }
304 }
305 else if (bitspersample == sizeof(float) * CHAR_BIT) {
306 photometric == PHOTOMETRIC_MINISWHITE ?
307 rdata[nx*(ny-1)-(ypos*nx)+xpos] = -((float*)cdata)[i*tileWidth+j] :
308 rdata[nx*(ny-1)-(ypos*nx)+xpos] = ((float*)cdata)[i*tileWidth+j];
309 }
310 else {
311 fprintf(stderr,"BAILING OUT:Allow only 8- or 16-bits image\n");
312 return -1;
313 }
314 }
315 }
316 }
317 }
318
319 }
320 else {
321 check_region(area, IntSize(nx, ny));
322 int xlen = 0, ylen = 0, x0 = 0, y0 = 0;
323
324 EMUtil::get_region_dims(area, nx, &xlen, ny, &ylen);
325 EMUtil::get_region_origins(area, &x0, &y0);
326
327 int strip_size = TIFFStripSize(tiff_file);
328 uint32 num_strips = TIFFNumberOfStrips(tiff_file);
329
330 if ((cdata = static_cast < unsigned char *>(_TIFFmalloc(strip_size)))==NULL) {
331 fprintf(stderr,"Error: Could not allocate enough memory\n");
332 return -1;
333 }
334
335 int k = 0;
336 int num_read = 0;
337 int mode_size = bitspersample / CHAR_BIT;
338 int total_rows = 0;
339
340 for (uint32 i = 0; i < num_strips; i++) {
341 if ((num_read = TIFFReadEncodedStrip(tiff_file, i, cdata, strip_size)) == -1) {
342 LOGERR("reading stripped TiFF image '%s' failed", filename.c_str());
343 err = 1;
344 break;
345 }
346
347 int nitems = num_read / mode_size;
348 int nrows = nitems / nx;
349 total_rows += nrows;
350
351 int y_start = 0;
352 int y_end = nrows;
353
354 if (area) {
355 if (total_rows >= y0 && total_rows < y0 + nrows) {
356 y_start = nrows - (total_rows - y0);
357 }
358 else if (total_rows >= (y0 + ylen) && total_rows < (y0 + ylen + nrows)) {
359 y_end = y0 + ylen - total_rows + nrows;
360 }
361 else if (total_rows >= (y0 + ylen + nrows)) {
362 break;
363 }
364 }
365
366 for (int l = y_start; l < y_end; l++) {
367 for (int j = x0; j < x0 + xlen; j++) {
368 if (bitspersample == CHAR_BIT) {
369 photometric == PHOTOMETRIC_MINISWHITE ?
370 rdata[k] = -(float) ((unsigned char*)cdata)[l * nx + j] :
371 rdata[k] = (float) ((unsigned char*)cdata)[l * nx + j];
372 }
373 else if (bitspersample == sizeof(unsigned short) * CHAR_BIT) {
374 photometric == PHOTOMETRIC_MINISWHITE ?
375 rdata[k] = -(float)((unsigned short*)cdata)[l * nx + j] :
376 rdata[k] = (float)((unsigned short*)cdata)[l * nx + j];
377 }
378 else if (bitspersample == sizeof(float) * CHAR_BIT) {
379 photometric == PHOTOMETRIC_MINISWHITE ?
380 rdata[k] = -((float*)cdata)[l * nx + j] :
381 rdata[k] = ((float*)cdata)[l * nx + j];
382 }
383 k++;
384 }
385 }
386 }
387
388 Util::flip_image(rdata, xlen, ylen);
389 }
390
391 _TIFFfree(cdata);
392 }
393 else { // process color image, convert to greyscale
394 size_t npixels = nx * ny;
395 uint32 * raster = (uint32*) _TIFFmalloc(npixels * sizeof(uint32));
396
397 if (raster != NULL) {
398 if (TIFFReadRGBAImage(tiff_file, nx, ny, raster, 0)) {
399 int abgr = 0; // raw ABGR pixel value
400 int red=0, green=0, blue=0;
401
402 for (int i=0; i<nx; ++i) {
403 for (int j=0; j<ny; ++j) {
404 abgr = raster[j+ny*i];
405 red = TIFFGetR(abgr);
406 green = TIFFGetG(abgr);
407 blue = TIFFGetB(abgr);
408 rdata[j+ny*i] = static_cast<float>(red*RED+green*GREEN+blue*BLUE);
409 }
410 }
411
412 _TIFFfree(raster);
413 }
414 }
415 }
416
417 EXITFUNC;
418 return err;
419}
420
421int TiffIO::write_header(const Dict & dict, int image_index, const Region *,
422 EMUtil::EMDataType datatype, bool)
423{
424 ENTERFUNC;
425
426 image_index = 0;
427
428 if (image_index == -1) {
429 image_index = 0;
430 }
431
432// TIFFSetDirectory(tiff_file, image_index);
433
434 check_write_access(rw_mode, image_index);
435
436 nx = (unsigned int) (int) dict["nx"];
437 ny = (unsigned int) (int) dict["ny"];
438 nz = (unsigned int) (int)dict["nz"];
439
440 if (nz != 1) {
441 LOGERR("Only support 2D TIFF file write");
442 return 1;
443 }
444
445// EMUtil::EMDataType datatype = (EMUtil::EMDataType) (int) dict["datatype"];
446
447 if (datatype == EMUtil::EM_UCHAR) {
448 bitspersample = CHAR_BIT;
449 }
450 else if (datatype == EMUtil::EM_USHORT) {
451 bitspersample = CHAR_BIT * sizeof(short);
452 }
453 else if (datatype == EMUtil::EM_FLOAT) {
454 bitspersample = CHAR_BIT * sizeof(float);
455 }
456 else {
457 LOGWARN("Don't support data type '%s' in TIFF. Convert to '%s'.",
458 EMUtil::get_datatype_string(datatype),
459 EMUtil::get_datatype_string(EMUtil::EM_USHORT));
460 bitspersample = CHAR_BIT * sizeof(short);
461 }
462
463 TIFFSetField(tiff_file, TIFFTAG_BITSPERSAMPLE, bitspersample);
464 TIFFSetField(tiff_file, TIFFTAG_SAMPLESPERPIXEL, 1);
465 TIFFSetField(tiff_file, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_MINISBLACK);
466 TIFFSetField(tiff_file, TIFFTAG_IMAGEWIDTH, nx);
467 TIFFSetField(tiff_file, TIFFTAG_IMAGELENGTH, ny);
468 TIFFSetField(tiff_file, TIFFTAG_ROWSPERSTRIP, ny);
469 TIFFSetField(tiff_file, TIFFTAG_PLANARCONFIG, PLANARCONFIG_CONTIG);
470 TIFFSetField(tiff_file, TIFFTAG_SOFTWARE, "EMAN2" );
471
472 // TIFFSetField(tiff_file, TIFFTAG_COMPRESSION, NO_COMPRESSION);
473 // TIFFSetField(tiff_file, TIFFTAG_FILLORDER, FILLORDER_MSB2LSB);
474
475 EMUtil::getRenderLimits(dict, rendermin, rendermax, renderbits);
476
477 EXITFUNC;
478 return 0;
479}
480
481int TiffIO::write_data(float * data, int image_index, const Region *,
482 EMUtil::EMDataType, bool)
483{
484 ENTERFUNC;
485
486 image_index = 0;
487
488// TIFFSetDirectory(tiff_file, image_index);
489
490 // If we didn't get any parameters in 'render_min' or 'render_max',
491 // we need to find some good ones
492 int truebits=bitspersample;
493 if (renderbits==0 || renderbits>truebits) renderbits=truebits;
494
495 EMUtil::getRenderMinMax(data, nx, ny, rendermin, rendermax, renderbits);
496
497 if (bitspersample == CHAR_BIT) {
498 vector<unsigned char> cdata(nx*ny);
499
500 int src_idx, dst_idx;
501
502 for (unsigned int i = 0; i < ny; ++i) {
503 for (unsigned int j = 0; j < nx; ++j) {
504 src_idx = i*nx+j;
505 dst_idx = nx*(ny-1) - (i*nx) +j;
506
507 if (data[src_idx] < rendermin){
508 cdata[dst_idx] = 0;
509 }
510 else if (data[src_idx] > rendermax) {
511 cdata[dst_idx] = UCHAR_MAX;
512 }
513 else {
514 cdata[dst_idx] = (unsigned char)((data[src_idx] - rendermin) /
515 (rendermax - rendermin) * UCHAR_MAX);
516 }
517 }
518 }
519
520 if (TIFFWriteEncodedStrip(tiff_file, 0, cdata.data(), nx*ny) == -1) {
521 printf("Fail to write tiff file.\n");
522
523 return -1;
524 }
525 }
526 else if (bitspersample == CHAR_BIT*sizeof(short)) {
527 vector<unsigned short> sdata(nx*ny);
528
529 int src_idx, dst_idx;
530
531 for (unsigned int i = 0; i < ny; ++i) {
532 for (unsigned int j = 0; j < nx; ++j) {
533 src_idx = i*nx+j;
534 dst_idx = nx*(ny-1) - (i*nx) +j;
535
536 if (data[src_idx] < rendermin){
537 sdata[dst_idx] = 0;
538 }
539 else if (data[src_idx] > rendermax) {
540 sdata[dst_idx] = USHRT_MAX;
541 }
542 else {
543 sdata[dst_idx] = (unsigned short)((data[src_idx] - rendermin) /
544 (rendermax - rendermin) * USHRT_MAX);
545 }
546 }
547 }
548
549 if (TIFFWriteEncodedStrip(tiff_file, 0, sdata.data(), nx*ny*sizeof(short)) == -1) {
550 printf("Fail to write tiff file.\n");
551
552 return -1;
553 }
554 }
555 else if (bitspersample == CHAR_BIT*sizeof(float)) {
556 vector<float> fdata(nx*ny);
557
558 int src_idx, dst_idx;
559
560 for (unsigned int i = 0; i < ny; ++i) {
561 for (unsigned int j = 0; j < nx; ++j) {
562 src_idx = i*nx+j;
563 dst_idx = nx*(ny-1) - (i*nx) +j;
564 fdata[dst_idx] = data[src_idx];
565 }
566 }
567
568 if (TIFFWriteEncodedStrip(tiff_file, 0, fdata.data(), nx*ny*sizeof(float)) == -1) {
569 printf("Fail to write tiff file.\n");
570
571 return -1;
572 }
573 }
574 else {
575 LOGWARN("TIFF in EMAN2 only support data type 8 bit, 16 bit or 32 bit.");
576 }
577
578 EXITFUNC;
579 return 0;
580}
581
582void TiffIO::flush()
583{
584 TIFFFlush(tiff_file);
585}
586
587bool TiffIO::is_complex_mode()
588{
589 return false;
590}
591
592bool TiffIO::is_image_big_endian()
593{
594 init();
595 return is_big_endian;
596}
597
598
599#endif //USE_TIFF
#define rdata(i)
Definition: analyzer.cpp:592
ByteOrder defines functions to work on big/little endian byte orders.
Definition: byteorder.h:59
Dict is a dictionary to store <string, EMObject> pair.
Definition: emobject.h:385
EMDataType
Image pixel data type used in EMAN.
Definition: emutil.h:92
ImageIO classes are designed for reading/writing various electron micrography image formats,...
Definition: imageio.h:127
IntSize is used to describe a 1D, 2D or 3D rectangular size in integers.
Definition: geometry.h:49
Region defines a 2D or 3D rectangular region specified by its origin coordinates and all edges' sizes...
Definition: geometry.h:497
void read_data(string fsp, size_t loc, const Region *area=0, const int file_nx=0, const int file_ny=0, const int file_nz=0)
Read the image pixel data in native byte order from a disk file.
void write_data(string fsp, size_t loc, const Region *const area=0, const int file_nx=0, const int file_ny=0, const int file_nz=0)
Dump the image pixel data in native byte order to a disk file.
#define ImageReadException(filename, desc)
Definition: exception.h:204
#define LOGWARN
Definition: log.h:53
#define LOGERR
Definition: log.h:51
#define ENTERFUNC
Definition: log.h:48
#define EXITFUNC
Definition: log.h:49
E2Exception class.
Definition: aligner.h:40
#define x(i)
Definition: projector.cpp:1517