Chapter 3. Accessing External Image Data

This chapter describes how to use IL to read and write image data. IL uses the Image Format Library (IFL) to accomplish all image input and output (I/O). IFL provides an abstraction of low level image I/O that lets users write applications without dealing with IL or the details of the image file formats that are being used. It is not even necessary to know what image file format is being used (though IFL does provide facilities for determining the format of an image and accessing special features which are not part of IFL's image I/O abstraction).

IL provides access to images using the ilFileImg class. Its objects can be part of image chains. This class caches image data in order to reduce the amount of image I/O. IL often provides more flexible access to image data than that provided by IFL since IFL's focus is on a simple I/O abstraction. For example, some image formats do not support paging. When using IFL to access an image stored in such a format, an application is forced to read the entire image and then extract the portions of the image of interest. IL can provide this functionality much more simply using its extensive image access facilities.

The rest of this chapter describes how to use IL to access image data. While reference is often made to IFL, the focus is on using the ilFileImg class to read and write image data. To learn more about using IFL directly to access image data, see the IFL(3) manual page.

This chapter contains the following major sections:

Supported IFL Image File Formats

IL provides access to images stored in a variety of formats using IFL. Table 1-1 shows all of the ifl-supported image file formats and their customary suffixes. The sources to many of the file format modules supported by IFL are located in /usr/share/src/ifl/.

Any image file format that is accessible using IFL is also accessible using IL.

IFL can be extended to accommodate new image file formats. For more information about adding a new image file format, see Appendix E, “Implementing Your Own Image File Format.”

The following sections describe the file formats currently supported by the Image Format Library, and, by extension, the ImageVision Library.

FIT

The FIT file format is a simple, tiled format developed along with IFL. You might use FIT as a starting point for defining your own file format.

FIT supports the full flexibility of the IFL model: all data types, orders, and page sizes. It uses a default page size of 128 x 128. FIT allows you to reserve space to hold user extensions to the file format. FIT is the only format that supports paging in the channel dimension. This functionality is useful for multispectral imagery.

GIF

The GIF file format is used to read image files stored in the CompuServe Graphics Image File (GIF) format. GIF does not support paging. It stores images in palette-color-compressed using the Lempel-Ziv & Welch algorithm[1] . GIF images are limited to using unsigned character data and an upper-left coordinate system. To obtain more information about GIF specifications, contact CompuServe, Incorporated, Columbus, Ohio.

JFIF (JPEG)

JFIF implements the JPEG file format using the JPEG library, libjpeg, made available by the Independent JPEG Group. In addition to providing the IFL image I/O abstraction, the entire JPEG library is provided as is for use by software that has been developed for use with libjpeg.

Version 6, 2-Aug-95 of the Independent JPEG Group's compression library, libjpeg, and its standard headers, jconfig.h, jpeglib.h, jmorecfg.h, and jerror.h, are installed as part of IFL.

ilTCL

The ilTCL file format allows you to save an operator chain in a file using a TCL-based scripting language. That file can then be treated like any other image file.

Kodak Photo CD Image Pac

The PCD file format supports image files produced by the Kodak Photo CD system. Photo CD establishes a system for storing high-resolution, digital photographic images on compact discs. The Kodak Photo CD system

  • scans photographic film

  • processes the scanned images (color correction, color encoding, hierarchical decomposition, and compression)

  • records these images as a series of digitally-coded images on a Kodak Photo CD disc

In addition to digital images, Kodak Photo CD can produce digital audio data and playback control data. However, IFL only handles the image data files from a Photo CD disc.

Photo CD Images

A photographic image on a Kodak Photo CD disc is stored as a hierarchy of images, each of which represents the original image at a different resolution. This image hierarchy is stored in a structure called an Image Pac. You can get a maximum of seven different resolutions of an image from an image pack. These resolutions are:

name

 

resolution

 

image index

Base/64

(96x64)

0

Base/16

(192x128)

1

Base/4

(384x256)

2

Base

(768x512)

3

4Base

(1536x1024)

4

16Base

(3072x2048)

5

64Base

(6144x4096)

6

An Image Pac file always contains the first four resolutions listed above. The resolutions, 4Base and higher, can be omitted when the Photo CD disc is created. Resolutions Base/64 through Base are stored directly and can be accessed quickly. Resolutions 4Base and higher, if they are available in the image file, are stored in a compressed form.

You can use the ilFileImg member function, getNumImgs(), to determine the number of images in your Photo CD file. You can use the setCurrentImg() and getCurrentImg() functions to select and query the current resolution. If you use setCurrentImg() to select an image resolution that does not exist in the image pack, the function returns the ilStatus value iflBADPARAMS but does not set the image's status. The default image resolution is Base/4.

The page size of a Photo CD Image is the full x dimension of the image by 16 in the y dimension (16 rows in Kodak Photo CD jargon).


Note: The supplied Photo CD format is not capable of writing Photo CD image files.


Photo CD Color Model

The color model of a Kodak Photo CD image is YCC. Photo YCC is a luminance/chrominance data metric that is based on video primaries. It is designed to allow simple video display without compromising the colors available in photographic media. You can convert from the YCC color model to another color model using IL. You cannot, however, use IFL to do the reverse: conversion from another color model to YCC.

Kodak Photo CD Overview Pac

Every Kodak Photo CD contains a file in the Kodak Photo CD Overview Pac format. This format contains a low resolution representation of each image on the Photo CD. The ilPCDOImg class allows you to retrieve each of the overview images at either Base/16 or Base/64 resolution (the default is Base/16).

You can use the ilFileImg member function, getNumImgs(), to determine the number of images in your Photo CD Overview file. You can use the setCurrentImg() and getCurrentImg() functions to select and query the current resolution. If you use setCurrentImg() to select an image resolution that does not exist in the overview pack, the function returns the ilStatus value iflBADPARAMS but does not set the image's status.


Note: The Photo CD Overview format supplied with IFL is not capable of writing Photo CD image files.


PNG

PNG implements the PNG file format using version 0.88 of the Portable Network Graphics library, libpng, and version 1.0 of the ZIP deflate/inflate compression library, libzlib. These libraries and their standard headers, png.h, pngconf.h, zlib.h, and zconf.h, are installed as part of IFL.

PPM/PGM/PBM

PPM, PGM, and PBM implement the PPM, PGM, and PBM file formats using release 7, December 1993 of the NETPBM libraries, libppm, libpgm, and libpbm. These libraries and their standard headers, ppm.h, pgm.h, and pbm.h, are installed as part of IFL.

Raw

The iflRaw image file format accesses raw image data stored in a file. The data must be organized in raster fashion. If the data is in pages, the pages must be a fixed size (with partial pages at the image edge padded to fill out the fixed size).

The iflRaw format supports the full flexibility of the IFL model: all data types, color models, orders, orientations and page sizes are supported. Like all file formats supported by IFL, you access raw images using the generic object class (or the ilFileImg object for IL users).

The default extension for image files in the ilRaw format is .raw.

SGI

SGI is the first format defined by Silicon Graphics for storing image data. SGI files are typically stored in files suffixed by .bw, .rgb, .rgba, .sgi, or .screen. SGI files support full color, color palette, and monochrome images of either one or two bytes per color component. Image data can be stored in either raw form or run-length encoding (RLE) compression. You can create SGI files with RLE compression but you cannot later rewrite a portion of a compressed SGI file.


Note: If an SGI-formatted image is RGB Palette, its corresponding color map must be stored in a separate (also SGI-formatted) file with the name imgName.map, where imgName is the name of the SGI image.

Page width for SGI files is the width of the image. Page height is a value in the range 16 through 32 that evenly divides the overall height of the image. The SGI format makes the image order interleaved. SGI supports only unsigned data and a lower-left coordinate space.

TIFF

The TIFF file format, created by Aldus Corporation, is an extended version of the Tag Image File Format, using version 3.4beta24 of Sam Leffler's TIFF library, libtiff. This library implements version 6.0 of the TIFF specification. The library and its standard header files, tiff.h and tiffio.h, are installed as part of IFL

The purpose of TIFF is to describe and store raster image data. TIFF can describe bilevel, grayscale, palette-color, and full-color image data in several color spaces. TIFF includes a number of compression schemes that allow you to choose the best space or time trade-offs for your applications. TIFF can also store multiple images per file. IFL uses the following extensions to TIFF 6.0: Tilewidth, Tilelength, and SampleFormat. These tags provide necessary support for the image data types and tiles as defined by the IFL.

The Introduction of this Programming Guide tells you how to obtain more information about the TIFF 6.0 specification. Refer to ifl/iflTIFF.h to learn more about TIFF tags and the TIFF specification.

YUV

The YUV file format is the standard 8-bit 4:2:2 (YUV) format used by the Sirius board and almost all digital disk recorders ( Abekas, Accom etc ). There is extensive documentation at the top of the source file for the tags.

Alias

The Alias file format supports both variations of the format: 8-bit RGB and 8-bit matte.

SOFTIMAGE

The SOFTIMAGE file format supports reading all types of image files and writes only mixed RLE compressed. There is no current support for depth buffer files or rendered subregions.


Note: The Wavefront file format is not currently supported.


Using IL to Access an Image

IL allows you to read and write image data in any of the file formats that support the desired mode of access. (Some image file formats do not support writing.) IL provides access to image files using the ilFileImg class. Existing image files can be read and new ones created simply by constructing an ilFileImg object.

Opening an Existing File

The following example opens an existing file for reading:

ilFileImg myFile("anExistingFileName", O_RDONLY);

The first argument to the constructor is the name of the image file to be opened. The second argument is the file access mode. The access mode can be either:

  • O_RDONLY to indicate that the file is to be opened only for reading

  • O_RDWR if the file can be read from and written to. Remember that not all image file formats allow writing. For example, the IFL cannot write images in the Kodak Photo CD format.

The ilFileImg constructor opens the named file. If the named file does not exist, you do not have read permission, or if it is in an unsupported format, the status of the constructed ilFileImg object is set to a value other than ilOKAY.

The filename passed to the ilFileImg constructor is subject to a standard parsing before being used as the name of a file to open. The syntax for filenames is as follows:

filename[#format-name][:image-index][%format-specific]

The filename is the name of the file to open. The format-name, if supplied, specifies the specific image file format to use to access the image file. The format specification is most frequently used when creating a file and the format cannot be discerned from the filename using a standard filename extension for the format. The image-index, if supplied, specifies the index of an image within a multi-image file to access. Image indices start at 0 but not all formats support storing multiple images in a single file. The format-specific string is passed unchanged to the IFL image file module for format-specific interpretation. This string is often used to encode arguments to be used when accessing the image file. For example, the Raw image format uses this string to specify the parameters, such as the dimension and color model of a raw image. IFL provides a utility routine to parse this string as a series of position- and name-qualified values. It is up to the individual format, however, to interpret this string.

After you open an image file, you can read the data, as shown in Example 3-1.

Example 3-1. Opening an Image File and Reading Data


// open the file
ilFileImg* someFile = new ilFileImg("someFileName", O_RDONLY);
// check for errors
if (someFile == NULL || someFile->getStatus() != ilOKAY) {
  printf("file %s could not be opened", fname);
  exit(1);
}
// obtain image attributes
iflDataType theDataType = someFile->getDataType();
int xsize = someFile->getXsize();
int csize = someFile->getNumChans();
// allocate buffer
char* buf = new char[iflDataSize(theDataType, xsize*csize)];
// read data into buffer
someFile->getTile(0, 0, xsize, 1, buf);

In this example:

  1. A file is opened for reading and a corresponding ilFileImg object is created. If the file cannot be opened, the program exits.

  2. The ilFileImg is queried about some of its attributes to determine what size buffer to allocate for holding one row of the image's data.

  3. The buffer is allocated. The iflDataSize() function returns the number of bytes needed for the data type indicated by its first argument, multiplied by the optional second argument. This function is declared in the header file il/iflDataSize.h and described in “Computing the Size of Data Types”.

  4. The getTile() function reads the first row of the image's data into the buffer.

Creating an Image File

To create a new image file, you need to specify the characteristics of the data, such as its data type, and indicate what file format will be used. The ilFileImg constructor creates a new image file, as follows:

iflFileConfig cfg(iflSize(xsize, ysize));
ilFileImg newFile(“newFileName”, NULL, &cfg);

This constructor creates an image file with the requested size; all of the other attributes use the default values. The first argument to the ilFileImg constructor specifies the name of the file to create. The second argument specifies a pointer to an ilImage to use for default image attributes, In this example, NULL is used which means the file format's own preferred defaults are used. The third argument specifies a pointer to an iflFileConfig argument which is used to specify various image file attributes: the x and y size of the image in this example.

Here is a more extensive use of the ilFileImg constructor where many more image parameters are specified. (All of these attributes are discussed in detail in “Image Attributes”, along with the constants that specify particular values for these attributes.)

iflFileConfig cfg(iflSize(xsize, ysize, zsize, csize), datatype,
    dimensionorder, colormodel, orientation, compression,
    iflSize(xpsize, ypsize, zpsize, cpsize));
ilFileImg newFile(“newFileName”, srcImage, &cfg, format);

It is rare that you would specify all of these parameters. In fact, it is likely that such a fully-specified configuration would be in error, for example, the color model and channel size would have to agree with one another. Normally, only a few attributes are specified; the remainder would take default values:

iflFileConfig cfg(iflSize(xsize, ysize), datatype,
    iflOrder(0), colormodel, iflOrientation(0), iflCompression(0),
    iflSize(xpsize, ypsize));
ilFileImg newFile(“newFileName”, NULL, &cfg);

In this example, the dimension order (iflInterleaved, iflSequential, or iflSeparate), the orientation, for example, iflLowerLeftOrigin, and the compression are allowed to default to the format's preferred values: the z size is set implicitly at 1 and the channel size matches that of the specified color model. The only attributes specified for the image are its size, its data type, for example, iflUChar, iflFloat, its color model, for example, iflRGB, iflRGBPalette, and its page size.

The page size argument defines the x, y, z, and c (channel) dimensions of the pages that the image is broken into as it is stored on disk. The x, y, and z dimensions are specified in pixels. Paging in the c dimension is specified in channels and is useful for multi-spectral images with a large number of channels. If no page size is supplied, the default page size for that particular format and image size is used.

The attributes specified when creating an image file must match those supported by the file format being used, for example, TIFF files support any data type except iflDouble, SGI files support only iflUChar and iflUShort, and FIT files can handle any data type. See the reference pages in for the various image formats supported by IFL for more information about what they support.

Once you create a file, you can write data to it. The example shown below assumes the image file, theImg, of size size was previously created. Its data is written to the file, outFile.tif, using copyTile().

ilFileImg tmpFile(“outFile.tif”, theImg);
tmpFile.copyTile(0, 0, size.x, size.y, theImg, 0, 0);

Setting a File's Compression

Often, images stored on disk are compressed to minimize their size. Such images need to be decompressed before you can read them. There are many different compression algorithms and each file format determines which algorithms it supports. From a programmer's point of view, as data is read or written in an IL program, its compression or decompression is handled transparently.

The compression attribute indicates which compression algorithm, if any, is used to compress the data before it is stored on disk. You should not compress files that will be interactively modified. Modifying portions of a compressed, existing file is dangerous because the amount of data written must be the same as what was originally in the file. In general, the size of a file image, once created, is fixed.

To set a file's compression algorithm, you must specify the compression algorithm when the file is created. This can be done either by specifying the compression algorithm explicitly in the iflFileConfig argument that is passed to the ilFileImg constructor or by inheriting the compression algorithm from another image used for the source image attributes. Since the set of compression algorithms supported by formats is highly variable, one of the easiest ways to specify that you want the image compressed is to use iflCompression(0) which specifies the format's preferred compression.

The compression specification used in an iflFileConfig is of type iflCompression. Table 3-1 lists the iflCompression constants currently defined in the header file ifl/iflTypes.h and their corresponding compression algorithms.

Table 3-1. Compression Algorithms Supported for ilTIFFImg Files

iflCompression Constant

Compression Algorithm

iflCompression(0)

use format's preferred compression

iflNoCompression

no compression

iflCCITTFAX3

CCITT Group 3 fax encoding

iflCCITTFAX4

CCITT Group 4 fax encoding

iflLZW

Lempel-Ziv and Welch algorithm

iflPACKBITS

Apple® Computer, Inc., Macintosh® RLE (run-length encoding)

iflSGIRLE

SGI's RLE compression

iflJPEG

Joint Photographic Expert Group

iflZIP

ZIP deflate/inflate

To query an existing file about which compression algorithm it uses, call getCompression():

iflCompression whichCompression = myFile->getCompression();

This function returns a value of type iflCompression corresponding to one of the supported algorithms.

Querying a File Image

Once you create an ilFileImg, you can query its attributes with any of the following functions:

const char* getFileName();
iflFormat* getImageFormat();
const char* getImageFormatName();
int getFileDesc();
int getFileMode();
int getNumImgs();
int getCurrentImg();

Table 3-2 describes each of these functions.

Table 3-2. File Query Functions

Function

Description

getFileName()

Returns the name of the file.

getImageFormat()

Returns the file format—TIFF, SGI, PhotoCD Image Pack, PhotoCD Overview Pack, GIF, or FIT.

getImageFormatName()

Returns the name of the image format.

getFileDesc()

Returns the file descriptor.

getFileMode()

Returns either O_RDWR or O_RDONLY, depending on whether the file was opened for reading and writing or just reading.

getNumImgs()

Returns the number of images stored in the file.


Setting and Getting Special Image Properties

The iflFile member functions, getItem() and setItem(), deal with format-dependent name-value pairs, called items, associated with an image within an image file.

Usage of these functions requires format-specific knowledge of the meaning of the tags for the specific file format, for example, for iflTIFFImg, the meaning of the tags is given in the TIFF specification.

Using getItem()

The getItem() method returns the value of an item associated with the current image in the image file.

virtual iflStatus getItem(int tag, va_list ap);

The tag argument specifies the name of the item to be set. It is interpreted by the specific iflFile subclass. The number and types of the remaining arguments are determined by the particular subclass of iflFile and the tag value.

The return value is iflOKAY if the function succeeds or an appropriate iflStatus error value if it fails.

Using setItem()

The setItem() method sets the value of an item associated with the current image in the image file.

Calling setItem() may change some image attributes. You can check this by calling haveAttributesChanged() after calling setItem().

virtual iflStatus setItem(int tag, va_list ap);

The tag argument specifies the name of the item to be set. It is interpreted by the specific iflFile subclass. The number and types of the remaining arguments are determined by the particular subclass of iflFile and the tag value.

The return value is iflOKAY if the function succeeds, or an appropriate iflStatus error value if it fails.

Using haveAttributesChanged()

You use haveAttributesChanged() to determine whether or not image attributes have changed.

int haveAttributesChanged();

This function returns TRUE if any attribute has changed since the last call to this method, otherwise, the function returns FALSE.

Importing and Exporting Image Data

IL provides a convenient mechanism for importing or exporting raw image data between IL and other libraries or devices. This mechanism is encapsulated in the ilMemoryImg class, which interprets a contiguous array of data residing in memory as an ilImage object. Since ilMemoryImg inherits from ilImage, you can use any of the data access, query, and other functions defined in ilImage. In addition, ilMemoryImg defines a function that returns a pointer to its array of data so that you can read the data (for exporting) or write new data (for importing). The class ilXImage, derived from ilMemoryImg, allows you to convert an XImage (an X Window data structure that defines X's representation of an image) to an ilImage and vice versa.

Images in Memory

The ilMemoryImg class provides four constructors. You can use these constructors to:

  • allocate an array to hold data that will be written

  • use an existing array

  • create an ilMemoryImg object from an ilImage

  • create an empty ilMemoryImg that will be populated later

The first constructor allocates an array large enough to hold size.x*size.y*size.z*size.c pixels of the indicated data type:

ilMemoryImg(const iflSize& size, iflDataType datatype,
    iflOrder order);

This array is deallocated when the ilMemoryImg object is destroyed.

The second constructor allows you to import data. It takes as an argument an existing array of data:

ilMemoryImg(void* data, const iflSize& size, iflDataType datatype,
    iflOrder order);

This constructor creates an ilMemoryImg object and initializes its data array pointer with the value passed in data. The size of the specified array is equal to or larger than size.x*size.y*size.z*size.c pixels of the indicated data type. Since this array was not allocated by ilMemoryImg, it will not be deallocated automatically when the ilMemoryImg object is destroyed.

Both of these constructors set the ilMemoryImg's attributes—size, data type, and order—to the values passed in the constructor so that you can use the query functions defined in ilImage, such as getDataType(). The minimum and maximum allowable pixel values are set by default to the minimum and maximum values allowed for the image's data type. In addition, the coordinate space attribute is set to iflLowerLeftOrigin. The color model is set depending on the number of channels in the image. as shown in Table 3-3.

Table 3-3. Color Models

channels

color model

1

iflLuminance

2

iflLuminanceAlpha

3

iflRGB

4

iflRGBA

5 or more

iflMultiSpectral

The third constructor takes an ilImage as an argument:

ilMemoryImg(ilImage* img);

The ilMemoryImg object has the same attributes as the ilImage. These attributes and the source image data are not changed if the source ilImage changes (thus, you can think of the ilMemoryImg as taking a snapshot of the ilImage). You can explicitly synchronize the ilMemoryImg with its source ilImage by calling the sync() method on the ilMemoryImg.

The fourth constructor returns an ilMemoryImg object with no data or attributes:

ilMemoryImg();

You can use this constructor when you need to create an ilMemoryImg before you can supply its data. Use setDataPtr() to specify the data.

To change the image data residing in an ilMemoryImg object, call setDataPtr() and pass a pointer to the new data. You must call setSize() if the new data is a different size than currently noted for the ilMemoryImg object. Finally, you should also call markDirty() to indicate that the data in the ilMemoryImg object has been altered.

void setDataPtr(void* data);
ilStatus setSize(const iflSize &size);
void markDirty();

To gain direct access to the image data residing in a ilMemoryImg object, call getDataPtr(). This function returns a void pointer to the data, as shown below:

void* getDataPtr();

Because an ilMemoryImg resides in memory, you can use it to hold temporary copies of images that you need to access quickly.


Note: Since the entire image resides in memory, IL's on-demand execution model is not used when an ilMemoryImg is accessed.




[1] The compression algorithm has become the focus of patent infringement litigation which has inspired the creation of a new image format to replace GIF. This new format is the Portable Network Graphics (PNG) image format. It is also supported by IFL.