Home · Pages · Index · Overviews

Tiff Class Reference

Direct I/O of individual multi-channel Tiff Image File Descriptors (IFDs). More...

 #include <image.h>

Enumerated Scalars

  Channel_Kind : { PLAIN_CHAN, MAPPED_CHAN, RED_CHAN,
    GREEN_CHAN, BLUE_CHAN, ALPHA_CHAN }
 

Descriptive Types

  RTiff : Tiff
  WTiff : Tiff
A Tiff opened for reading
A Tiff opened for writing

Routines

Tiff * Open_TiffG (string file_name, string mode)
void Close_Tiff (Tiff *tif F)

boolean Get_IFD_Shape (RTiff *tif, int *width, int *height, int *num_chan)
Value_Type Get_IFD_Channel_Type (RTiff *tif, int channel)
Channel_Kind Get_IFD_Channel_Kind (RTiff *tif, int channel)
boolean Get_IFD_Channel (RTiff *tif, int channel, Array *plane M)
string Get_IFD_Annotation (RTiff *tif)
boolean Get_IFD_Map (RTiff *tif, Array *map M)

void Advance_Tiff (RTiff *tif M)
void Rewind_Tiff (RTiff *tif M)
boolean Tiff_EOF (RTiff *tif)

boolean Add_IFD_Channel (WTiff *tif M, Array *plane, Channel_Kind kind)
boolean Set_IFD_Map (WTiff *tif M, Array *map)

void Update_Tiff (WTiff *tif M, Image_Compress compress)

Detailed Description

While the Image I/O module provides the simplist possible interface to get images and stacks into and out of tiff-encoded files, the Tiff class provides more flexibility, at the expense of somewhat greater complexity, by (a) realizing routines that give serial access to the individual IFDs of the file, (b) removing the restriction that all IFDs have to be of the same dimensions, and (c) not automatically mapping color-mapped channels. Before introducing the routines, we describe the aspects of a tiff encoding that need to be understood by a user of the class.

A Tiff-encoded file contains a sequence of one or more image file descriptors (IFDs) each of which contains any number of channels each of which is a 2D array, all of the same shape. Each channel's array can contain signed or unsigned integers of some specific number of bits, or a 32-bit floating point number. Each channel also has a photometric interpretation which in effect can be one of RED, GREEN, BLUE, ALPHA, MAPPED, or PLAIN, with the restriction that the only channel that can be MAPPED is the first. (Tiff actually uses a somewhat baroque and complicated set of tags to encode these interpretations, but in effect the list just given is the information that is present.) Note carefully, that unlike the Image I/O module, the IFDs in this context do *not* have to have the same dimensions or number and types of channels/layers as other IFDs in the tif file. This can be useful if you want to store something more complicated in a tiff. For example, every even-numbered IFD could be a thumbnail of the previous odd-numbered IFD. What is in the sequence of IFDs could even be encoded in the text field of the first channel of the first IFD, which is put into or taken from the JF_ANO_BLOCK tag of the tiff (there is only one per file, see 2. below).

If the first channel is MAPPED then the IFD also contains a 16-bit rgb-color map, presumably for mapping the values of that channel. Unfortunately, the tiff format is not general enough to associate color maps with different channels, there is at most one map and its presence is always contingent on the first channel being designated as MAPPED. However, there is nothing to prevent the application of this map to other channels if it suites your purpose, and there is generally no reason why the application can't arrange the channels so the first one is a color-mapped one.

The Tiff class, unlike the routines of the Image I/O module, does not use the concept of layers, but works directly with the channels in each IFD. One should further note the following subtleties:

  1. The scale of an array is actually operative here: the values of a 12-bit UINT16_TYPE array will be written as 12-bit numbers to the tiff file, which in turn, when read, will produce an array with a scale of 12.
  2. The text field of the array that is the first channel of the first IFD output, is written to a JF_ANO_BLOCK tag upon the write, and this text tag can be fetched from the first IFD when reading with Get_IFD_Annotation if it is present. There is no limitation on the size of a text field, so one can in principle encode arbitrarily complex meta-data about an image in this field in an ASCII-oriented format such as XML.
  3. The software also applies to LSM files produced by Zeiss microscopes. The format is a small variation on the tiff format. LSM's frequently contain thumbnails as well as the primary image. These are not ignored by a Tiff reader (unlike the Image I/O module routines which filter them out).
  4. All the I/O routines may return a value signalling an error, such as a file can't be openede, the tiff contents are improperly formatted, or a parameter is not of the right shape, kind, or type. In such an error event, one may get a descriptive string of the problem by calling Image_Error, right after the error occurs. In order for this to be thread-safe, if multiple threads encounter an error, only one of the threads (which is unspecified) records a descriptive string. One must subsequently "release" the recorded error (with Image_Error_Release) in order for a thread to record a new error, if one occurs. So an error message must be released even when one's application is not threaded. These routines are shared with the Image I/O module.

A Tiff object manages either the reading or writing of a tiff file. It has the concept of a current IFD, which for reading is one in the sequence of IFDs in the file or none if the Tiff is at the end of the file. For writing, the current IFD is always the next one to be written to the file's sequence of IFDs and this IFD, immediately after the previous one has been written, is empty in that it has no channels. A Tiff is opened for either reading ("r") or writing ("w") with Open_Tiff and must be closed with Close_Tiff when all I/O involving the Tiff is complete. To clearly distinguish between Tiffs open for reading versus those open for writing, the descriptive type names RTiff and WTiff are introduced.

One can advance an RTiff to the next IFD in the file's sequence with Advance_Tiff and query as to whether or not the end of the sequence has been reached with Tiff_EOF. An RTiff can also be rewound with Rewind_Tiff which sets the current IFD to the first IFD in the associated file. For example, a simple code to count the number of IFDs in a tiff file is:

 int  nifd;
 Tiff t;

 Open_Tiff("foo.tif","r");
 nifd = 0;
 while ( ! Tiff_EOF(t))
   { nifd += 1;
     Advance_Tiff(t);
   }
 Close_Tiff(t);
 printf("There are %d IFD's in foo.tif\n",nifd);

An RTiff object gives one finer control over reading the contents of the current IFD of a tiff file. Get_IFD_Shape returns the width, height, and number of channels in the current IFD. Get_IFD_Channel_Kind and Get_IFD_Channel_Type return the Channel_Kind and Value_Type of any given channel in the current IFD. These routines allow a user to know what kind of Array container might hold the channel images of the IFD and whether or not it has a color map and how big it is. Get_IFD_Channel fetches the contents of a given channel into an Array supplied by the caller, and Get_IFD_Map fetches the color map (if there is one) into an Array supplied by the caller. It is frequently the case that these Arrays are actually Array_Bundles into a slice of a larger array that contains the entire stack encoded in the file. For example, suppose it is known that a tiff file contains an RGB_KIND stack as might be interpreted by the routines of the Image I/O module. The following code would read such a stack into the Array stack:

 int    width, height, depth, nchan;
 Array *stack;

 Open_Tiff("RGB_3D_Stack.tif","r");
 depth = 0;
 while ( ! Tiff_EOF(t))
   { depth += 1;
     Advance_Tiff(t);
   }
 Rewind_Tiff(t);
 Get_IFD_Shape(&width,&height,&nchan);
 stack = Make_Array_With_Shape(RGB_KIND,UINT8_TYPE,Coord3(depth,height,width));
 depth = 0;
 while ( ! Tiff_EOF(t))
   { for (j = 0; j < nchan; j++)
       { Array_Bundle plane = *stack;
         Get_IFD_Channel(t,j,Get_Array_Plane(Get_Array_Plane(&plane,j),depth));
       }
     depth += 1;
     Advance_Tiff(t);
   }
 Close_Tiff(t);

A more careful code would check the return values of all the calls for error conditions and would further check that indeed all the IFD's have the same shape, that nchan = 3, and that the Channel_Kinds are RED_CHAN, GREEN_CHAN, and BLUE_CHAN in that order.

Writing a tiff file is simply a matter of building up the current IFD by adding channels to it with Add_IFD_Channel, and possibly adding a color map with Set_IFD_Map (if the first channel added is a MAPPED_CHAN), and then appending it to the sequence of IFDs already written with Update_Tiff. The routine below presents a more complex example involving Tiff objects and specifically illustrates how to write a tiff file. The routine take a file named input and looks to see if a color thumbnail is encoded in the even IFDs of the file's IFD sequence, as is the case for most Zeiss LSM files. If so, then it extracts and outputs this thumbnail to the file output. In this example, care was taken to at least protect every call, returning true if there was an error of any kind. Note also the care that is taken to clean up any open objects regardless of how the routine terminates.

 boolean Extract_Thumbnail(string input, string output)
 { Tiff  *R, *W;
   Array *P;
   int    w1[2], h1[2], n1[2];
   int    w, h, d, n, i, c;
   int    status = 1;

   if ((R = Open_Tiff(input,"r")) == NULL) return (1);
   if ((W = Open_Tiff(output,"w")) == NULL) goto exit1;

   for (d = 0; ! Tiff_EOF(R); d++)
     { if (d < 2)
         { if (Get_IFD_Shape(R,w1+d,h1+d,n1+d)) goto exit2; }
       else
         { if (Get_IFD_Shape(R,&w,&h,&n)) goto exit2;
           if (w1[d%2] != w || h1[d%2] != h || n1[d%2] != n) goto exit2;
           if (d%2)
             { if (n != 3) goto exit2;
               for (c = 0; c < n; c++)
                 if (Get_IFD_Channel_Kind(R,c) != RED_CHAN+c ||
                     Get_IFD_Channel_Type(R,c) != UINT8_TYPE) goto exit2;
             }
         }
       Advance_Tiff(R);
     }
   Rewind_Tiff(R);

   P = Make_Array_With_Shape(RGB_KIND,UINT8_TYPE,Coord2(h1[1],w1[1]));

   for (d = 0; ! Tiff_EOF(R); d++)
     { if (d%2)
         { for (c = 0; c < 3; c++)
             { Array_Bundle p = *P;
               if (Get_IFD_Channel(R,c,Get_Array_Plane(&p,c))) goto exit3;
               if (Add_IFD_Channel(W,&p,RED_CHAN+c)) goto exit3;
             }
           Update_Tiff(W,DONT_PRESS);
         }
       Advance_Tiff(R);
     }

        status = 0;
 exit3: Free_Array(P);
 exit2: Close_Tiff(W);
 exit1: Close_Tiff(R);
        return (status);
 }

Enumerated Scalars Documentation

Channel_Kind: { PLAIN_CHAN, MAPPED_CHAN, RED_CHAN,
    GREEN_CHAN, BLUE_CHAN, ALPHA_CHAN }

The possible photometric interpretations for a channel. As noted in the detailed description the only channel in an IFD that can have Channel_Kind MAPPED_CHAN is the first. Even if you ask Add_IFD_Channel to add a MAPPED_CHAN it will end up being PLAIN_CHAN if it is not the first. If it is the first, then you should supply the expected color map with Set_IFD_Map.


Routine Documentation

Tiff * Open_TiffG (string file_name, string mode)

Open file_name for reading if mode = "r", or for writing if mode = "w" as a tiff-encoded file return a Tiff object that models the current state of the I/O. The Tiff is setup so that its current IFD is the first in the sequence. NULL is returned if there are any problems and if so the nature of the problem can be diagnosed with Image_Error.

void Close_Tiff (Tiff *tif F)

Close the tif I/O object. This routine must be called to properly complete the writing of a tiff file. It frees the tif object.

boolean Get_IFD_Shape (RTiff *tif, int *width, int *height, int *num_chan)

Set the width, height, and number of channels in the current IFD of the readable Tiff tif at the locations pointed at by width, height, and num_chan, respectively. True is returned if any I/O or decoding errors occur in attempting to get the shape of the IFD, and if so the nature of the problem can be diagnosed with Image_Error.

Value_Type Get_IFD_Channel_Type (RTiff *tif, int channel)

Return the Value_Type of channel channel in the current IFD of the readable Tiff tif. If there is any error in accessing the current IFD or the parameter channel is out of range, then -1 is returned and the nature of the problem can be diagnosed with Image_Error.

Channel_Kind Get_IFD_Channel_Kind (RTiff *tif, int channel)

Return the Channel_Kind of channel channel in the current IFD of the readable Tiff tif. If there is any error in accessing the current IFD or the parameter channel is out of range, then -1 is returned and the nature of the problem can be diagnosed with Image_Error.

boolean Get_IFD_Channel (RTiff *tif, int channel, Array *plane M)

Read channel channel from the current IFD of readable Tiff tif into the data vector of array plane. The array plane must be a PLAIN_KIND 2D array whose dimensions match those of the channels of the IFD and whose type must be consistent with the Value_Type of the channel (i.e. Get_IFD_Channel_Type(tif,channel)). True is returned if any I/O or decoding errors occur in attempting to fetch the channel, and if so the nature of the problem can be diagnosed with Image_Error.

string Get_IFD_Annotation (RTiff *tif)

Return a pointer to the text of the JF_ANO_BLOCK tag of the current IFD of the readable Tiff tif if it is present. If it is not present than a pointer to an empty string is returned, and if there is an error than NULL is returned.

boolean Get_IFD_Map (RTiff *tif, Array *map M)

Place the value of the color map of the current IFD of the readable Tiff tif in the array map. The array map must be an RGB_KIND 1D array whose size is 2scale where scale is the number of bits in the first channel that must further be of kind CHAN_MAPPED. If it is not, or any other I/O or decoding error occurs then true is returned and the nature of the problem can be diagnosed with Image_Error.

void Advance_Tiff (RTiff *tif M)

The readable Tiff tif, has its current IFD updated to the next one in sequence within the underlying tiff file.

void Rewind_Tiff (RTiff *tif M)

The readable Tiff tif, has its current IFD reset to the first one in the sequence of IFDs within the underlying tiff file.

boolean Tiff_EOF (RTiff *tif)

Returns true only if the tif, that must be opened for reading, is at the end of the file, i.e. it has advanced beyond the last IFD.

boolean Add_IFD_Channel (WTiff *tif M, Array *plane, Channel_Kind kind)

Add a new channel to the current IFD of the writeable Tiff tif. The contents of the channel will be those of plane when the Tiff is updated and plane must be a PLAIN_KIND 2D array. If it is the first channel added, then its dimensions set the dimensions of the channels of the current IFD, otherwise its dimensions must match those of the previously added channels. The setting of the parameter kind determines the photometric interpretation or Channel_Kind for the channel. NB: All the arrays added as channels to a current IFD must not be modified until after the Tiff is updated with a call to Update_Tiff as their data is copied directly from the array to the underlying file at the time of the update and not before. If there are any errors then true is returned and the problem can be diagnosed with a call to Image_Error.

boolean Set_IFD_Map (WTiff *tif M, Array *map)

Set the value of the colormap of the writeable Tiff tif to the value in the array map. Unlike Add_IFD_Channel, the values of map are copied immediately so there is no requirement for map to remain untouched until the Tiff is updated. However, at least the first channel must have been added to the current IFD and it must be of kind MAPPED_CHAN. Moreover, the array map must be an RGB_KIND 1D array whose size is 2scale where scale is the number of bits in the first channel. If there are any errors then true is returned and the problem can be diagnosed with a call to Image_Error.

void Update_Tiff (WTiff *tif M, Image_Compress compress)

Write the contents of the current IFD of the writeable Tiff tif as the next IFD in the underlying tiff file's sequence (as long as it is not empty), and reset the current IFD to be empty.