vigra - Image Input and Output

[ VIGRA Homepage | Function Index | Class Index | Namespaces | File List | Main Page ]

Image Input and Output |

## Section Contents

- Two-Dimensional Images
- Higher Dimensional Arrays

# Two-Dimensional Images

In this section we'll show you how to import and export an image with VIGRA. If you want to import an image from disk and enquire about its properties, you must use an object of `vigra::ImageImportInfo` class. It reads the header of the image file. The constructor expects the file name, the file type will be determined automatically.

The `vigra::ImageImportInfo` class currently recognizes the following file formats:

BMP:
  Microsoft Windows bitmap image file.
EXR:
  OpenEXR high dynamic range image format. (only available if libopenexr is installed)
GIF:
  CompuServe graphics interchange format; 8-bit color.
HDR:
  Radiance RGBE high dynamic range image format.
JPEG:
  Joint Photographic Experts Group JFIF format - compressed 24-bit color (only available if libjpeg is installed).
PNG:
  Portable Network Graphic (only available if libpng is installed).
PBM:
  Portable bitmap format (black and white).
PGM:
  Portable graymap format (gray scale).
PNM:
  Portable anymap.
PPM:
  Portable pixmap format (color).
SUN:
  SUN Rasterfile.
TIFF:
  Tagged Image File Format. (only available if libtiff is installed.)
VIFF:
  Khoros Visualization image file.

In the following example, the image file name is given in the first command line argument, and the most important image metadata are printed:

// read image given as first command-line argument
vigra::ImageImportInfo imageInfo(argv[1]);
// print some information:
std::cout << "Image information:\n";
std::cout << " file format: " << imageInfo.getFileType() << std::endl;
std::cout << " width: " << imageInfo.width() << std::endl;
std::cout << " height: " << imageInfo.height() << std::endl;
std::cout << " pixel type: " << imageInfo.getPixelType() << std::endl;
std::cout << " color image: ";
if (imageInfo.isColor()) std::cout << "yes (";
else std::cout << "no (";
std::cout << "number of channels: " << imageInfo.numBands() << ")\n";

As you can see, the `ImageImportInfo` object contains a lot of information, some of it is printed in the example. Using this image

input file

we get the following output:

    Image information:
      file format: GIF
      width:       154
      height:      145
      pixel type:  UINT8
      color image: no  (number of channels: 1)

To process the image, we must load the actual image data into an array such as the one described in Basic MultiArray Usage. To do so, we create a `vigra::MultiArray` with the appropriate shape and then call vigra::importImage(). This function needs an `ImageImportInfo` object specifying the image to be loaded and a MultiArrayView object of apropriate size to copy the image data in. The code looks like this:

// read image header information from in_filename
ImageImportInfo imageInfo(in_filename);
// instantiate array for image data
MultiArray<2, UInt8> imageArray(imageInfo.shape());
// copy image data from file into array
importImage(imageInfo, imageArray);

If you already know the type of data in the file, you can also just pass the filename and a MultiArray, which will automatically be resized as appropariate:

// if you don't need the information from ImageImportInfo, you can also
// simply pass the filename (this will resize imageArray internally!)
importImage(in_filename, imageArray);

Writing the image data from an array to a file is quite similar. For this purpose, you use the function vigra::exportImage(), which takes an 2D MultiArrayView and an vigra::ImageExportInfo object or a string (the filename). The ImageExportInfo object also needs a file name, but gives you more control over how the image is written. The desired file format is guessed from the file name's extension (but can be overridden with the method `ImageExportInfo::setFileType`. Recognized extensions are: '.bmp', '.exr', '.gif', '.jpeg', '.jpg', '.p7', '.png', '.pbm', '.pgm', '.pnm', '.ppm', '.ras', '.tif', '.tiff', '.xv', '.hdr' (as for reading, '.exr' requires libopenexr, '.jpg' requires libjpeg, '.png' requires libpng and '.tif' requires libtiff). In the following example, we create and save a 160x160 pixels image, where the image is a checkerboard. The image is saved as "testimage.gif" in the same folder as the executed code.

#include <iostream>
#include <vigra/multi_array.hxx>
#include <vigra/stdimage.hxx>
#include <vigra/impex.hxx>
using namespace vigra;
int main(int argc, char ** argv)
{
// instantiate array for image data of size 160x160 pixels
MultiArray<2, UInt8> imageArray(Shape2(160,160));
// create a black (0) and white (255) checker-board image
for (int y = 0; y < imageArray.shape(1); y++)
{
// note that the inner loop should go over the first (x-) axis
// because elements along the first axis are consecutive in memory
for (int x = 0; x < imageArray.shape(0); x++)
{
if ((x%20)/10 == (y%20)/10)
imageArray(x,y) = 0;
else
imageArray(x,y) = 255;
}
}
// write image data to "testimage.jpg" and set compression to 70%
exportImage(imageArray, ImageExportInfo("testimage.jpg").setCompression("JPEG QUALITY=70"));
// if you don't want to set any options in ImageExportInfo, you can simply write
exportImage(imageArray, "testimage.gif");
return 0;
}

The resulting images are the following:

testimage.jpg | testimage.gif

Finally, we give a complete example of importing, editing and exporting an image. After importing, we set every other horizontal line to black. This can be done with the `bind<M>(i)` method explained in bind<M>(i) and bindAt(M, i). Input and output file names are specified via command line arguments.

#include <iostream>
#include <vigra/multi_array.hxx>
#include <vigra/stdimage.hxx>
#include <vigra/impex.hxx>
using namespace vigra;
int main(int argc, char ** argv)
{
if(argc != 3)
{
std::cout << "Usage: " << argv[0] << " infile outfile" << std::endl;
std::cout << "(grayscale only, supported formats: " << impexListFormats() << ")" << std::endl;
return 1;
}
try
{
char * in_filename = argv[1];
char * out_filename = argv[2];
// read image header information from in_filename
ImageImportInfo imageInfo(in_filename);
// instantiate array for image data
MultiArray<2, UInt8> imageArray(imageInfo.shape());
// copy image data from file into array
importImage(imageInfo, imageArray);
// if you don't need the information from ImageImportInfo, you can also
// simply pass the filename (this will resize imageArray internally!)
importImage(in_filename, imageArray);
// set every second horizontal line to black
for (int i = 0; i<imageInfo.height(); i+=2)
{
imageArray.bind<1>(i) = 0;
}
// write image data to the file given as second argument
exportImage(imageArray, ImageExportInfo(out_filename));
}
catch (std::exception & e)
{
// catch any errors that might have occurred and print their reason
std::cout << e.what() << std::endl;
return 1;
}
return 0;
}

The input image and the resulting output image are:

input image | output image

The handling of color images is exactly the same, but instead of instantiating a `vigra::MultiArray<2, UInt8>` you need a `vigra::MultiArray<2, vigra::RGBValue<UInt8> >` array as described in Basic MultiArray Usage. Images with alpha channel are supported by importImageAlpha() and exportImageAlpha().

Note that image processing often requires more complicated calculations than in these examples. In this case, it is better to import and convert the data into a `float` array (i.e. `vigra::MultiArray<2, float>`) instead of the simple `unsigned char` type in order to minimize rounding errors. When a file is imported into such an array, the conversion is automatically performed by the importImage() function. When an array is to be exported, the handling of `float` depends on the file format: If the file format supports float (currently: TIFF and VIFF), the data are written verbatim (unless this is explicitly overridden, see below). Otherwise, the data are mapped to `unsigned char` via a linear transform of the orginal range, followed by rounding (use vigra::linearRangeMapping() to override this behavior by an explicit user-defined mapping).

The `ImageExportInfo` class provides a number of additional methods to customize data export, including:

setCompression():
  Request compressed storage if the file format supports it.
setFileType():
  Provide the file format explicitly instead of guessing from the filename extension.
setPixelType():
  Request pixels to be stored as the given type (results in an exception if the type is unsupported by the file format).
setXResolution(), setYResolution:
  Store resolution information for the two axes (ignored if unsupported by the file format).

See vigra::ImageExportInfo for a complete list and more details.

# Higher Dimensional Arrays

The recommended file format for arrays of arbitrary dimension is HDF5. It supports all possible pixel types, arbitrary dimensions, on-the-fly compression, arbitrary many arrays per file, and flexible metadata storage along with arrays. See Import/Export of Images and Arrays in HDF5 Format for more information.

The functions importVolume() and exportVolume() support three additional methods to read and write 3D volume data:

- Multipage TIFF (read/write) and Andor SIF (read only) file formats.
- Image stacks (consisting of one 2D image per slice - read/write).
- Raw data if accompanied with an additional text file providing the necessary metadata (shape, pixel type etc.) - read only.

© Ullrich Köthe (ullrich.koethe@iwr.uni-heidelberg.de) Heidelberg Collaboratory for Image Processing, University of Heidelberg, Germany | html generated using doxygen and Python vigra 1.10.0 (Thu Jan 8 2015)