Figure 26.1: The difference between using a CastImageFilter and an ImageAdaptor. ImageAdaptors convertpixel values when they are accessed by iterators. Thus, they do not produces an intermediate image. In theexample illustrated by this figure, the Image Y is not created by the ImageAdaptor; instead, the image issimulated on the fly each time an iterator from the filter downstream attempts to access the image data.

The purpose of an image adaptor is to make one image appear like another image, possibly of a
different pixel type. A typical example is to take an image of pixel type unsigned char and
present it as an image of pixel type float. The motivation for using image adaptors in this
case is to avoid the extra memory resources required by using a casting filter. When we use the
itk::CastImageFilter for the conversion, the filter creates a memory buffer large enough to store
the float image. The float image requires four times the memory of the original image and
contains no useful additional information. Image adaptors, on the other hand, do not require
the extra memory as pixels are converted only when they are read using image iterators (see
Chapter 25).

Image adaptors are particularly useful when there is infrequent pixel access, since the actual conversion
occurs on the fly during the access operation. In such cases the use of image adaptors may reduce overall
computation time as well as reduce memory usage. The use of image adaptors, however, can be
disadvantageous in some situations. For example, when the downstream filter is executed multiple times, a
CastImageFilter will cache its output after the first execution and will not re-execute when
the filter downstream is updated. Conversely, an image adaptor will compute the cast every
time.

Another application for image adaptors is to perform lightweight pixel-wise operations replacing the need
for a filter. In the toolkit, adaptors are defined for many single valued and single parameter functions such as
trigonometric, exponential and logarithmic functions. For example,

The source code for this example can be found in the fileExamples/DataRepresentation/Image/ImageAdaptor1.cxx.

This example illustrates how the itk::ImageAdaptor can be used to cast an image from one pixel type
to another. In particular, we will adapt an unsigned char image to make it appear as an image of pixel
type float.

We begin by including the relevant headers.

#include"otbImage.h"#include"itkImageAdaptor.h"

First, we need to define a pixel accessor class that does the actual conversion. Note that in general, the only
valid operations for pixel accessors are those that only require the value of the input pixel. As such,
neighborhood type operations are not possible. A pixel accessor must provide methods Set() and Get(),
and define the types of InternalPixelType and ExternalPixelType. The InternalPixelType
corresponds to the pixel type of the image to be adapted (unsigned char in this example). The
ExternalPixelType corresponds to the pixel type we wish to emulate with the ImageAdaptor (float in
this case).

Although in this example, we are just performing a simple summation, the key concept is that access to
pixels is performed as if the pixel is of type float. Additionally, it should be noted that the adaptor is used
as if it was an actual image and not as a filter. ImageAdaptors conform to the same API as the otb::Image
class.

The source code for this example can be found in the fileExamples/DataRepresentation/Image/ImageAdaptor2.cxx.

This example illustrates how to use the itk::ImageAdaptor to access the individual components of an
RGB image. In this case, we create an ImageAdaptor that will accept a RGB image as input and presents it
as a scalar image. The pixel data will be taken directly from the red channel of the original
image.

As with the previous example, the bulk of the effort in creating the image adaptor is associated with
the definition of the pixel accessor class. In this case, the accessor converts a RGB vector to a
scalar containing the red channel component. Note that in the following, we do not need to
define the Set() method since we only expect the adaptor to be used for reading data from the
image.

We create an itk::RescaleIntensityImageFilter and an otb::ImageFileWriter to rescale the
dynamic range of the pixel values and send the extracted channel to an image file. Note that the image type
used for the rescaling filter is the ImageAdaptorType itself. That is, the adaptor type is used in the same
context as an image type.

ImageAdaptors for the green and blue channels can easily be implemented by modifying the pixel accessor
of the red channel and then using the new pixel accessor for instantiating the type of an image adaptor. The
following define a green channel pixel accessor.

The source code for this example can be found in the fileExamples/DataRepresentation/Image/ImageAdaptor3.cxx.

This example illustrates the use of itk::ImageAdaptor to obtain access to the components of a vector
image. Specifically, it shows how to manage pixel accessors containing internal parameters. In this example
we create an image of vectors by using a gradient filter. Then, we use an image adaptor to extract
one of the components of the vector image. The vector type used by the gradient filter is the
itk::CovariantVector class.

We start by including the relevant headers.

#include"itkGradientRecursiveGaussianImageFilter.h"

A pixel accessors class may have internal parameters that affect the operations performed on input pixel
data. Image adaptors support parameters in their internal pixel accessor by using the assignment operator.
Any pixel accessor which has internal parameters must therefore implement the assignment operator. The
following defines a pixel accessor for extracting components from a vector pixel. The m_Index member
variable is used to select the vector component to be returned.

The Get() method simply returns the i-th component of the vector as indicated by the index. The
assignment operator transfers the value of the index member variable from one instance of the pixel
accessor to another.

In order to test the pixel accessor, we generate an image of vectors using the
itk::GradientRecursiveGaussianImageFilter . This filter produces an output image of
itk::CovariantVector pixel type. Covariant vectors are the natural representation for gradients since
they are the equivalent of normals to iso-values manifolds.

The index of the component to be extracted is specified from the command line. In the following, we create
the accessor, set the index and connect the accessor to the image adaptor using the SetPixelAccessor()
method.

The Get() method returns one if the input pixel is above the threshold and zero otherwise. The assignment
operator transfers the value of the threshold member variable from one instance of the pixel accessor to
another.

To create an image adaptor, we first instantiate an image type whose pixel type is the same as the internal
pixel type of the pixel accessor.

Figure 26.2: Using ImageAdaptor to perform a simple image computation. An ImageAdaptor is used toperform binary thresholding on the input image on the left. The center image was created using a threshold of100, while the image on the right corresponds to a threshold of 200.

As before, we rescale the emulated scalar image before writing it out to file. Figure 26.2 illustrates
the result of applying the thresholding adaptor to a typical gray scale image using two
different threshold values. Note that the same effect could have been achieved by using the
itk::BinaryThresholdImageFilter but at the price of holding an extra copy of the image in
memory.

Image adaptors will not behave correctly when connected directly to a writer. The reason is that writers tend
to get direct access to the image buffer from their input, since image adaptors do not have a real buffer their
behavior in this circumstances is incorrect. You should avoid instantiating the ImageFileWriter or the
ImageSeriesWriter over an image adaptor type.