You probably learned image processing with the classical access to the image data using "for loops" like:
const int nx = 200; const int ny = 100; ImageType image(nx,ny); for(int x=0; x<nx; x++) // for all Columns { for(int y=0; y<ny; y++) // for all Rows { image(x,y) = 10; } }
When what you really mean is:
ForAllThePixels p in image Do p = 100
ImageIterators gets you closer to this algorithmic abstraction. They abstract the low-level processing of images from the particular implementation of the image class.
Here is how an image iterator is used in ITK:
ImageType::Pointer im = GetAnImageSomeHow(); ImageIterator it( im, im->GetRequestedRegion() ); it.GoToBegin(); while( !it.IsAtEnd() ) { it.Set( 10 ); ++it; }
This code can also be written as:
ImageType::Pointer im = GetAnImageSomeHow(); ImageIterator it( im, im->GetRequestedRegion() ); for (it = it.Begin(); !it.IsAtEnd(); ++it) { it.Set( 10 ); }
One important advantage of ImageIterators is that they provide support for the N-Dimensional images in ITK. Otherwise it would be impossible (or at least very hard) to write algorithms that work independent of the image dimension.
Another advantage of ImageIterators is that they support walking a region of an image. In fact, one argument of an ImageIterator's constructor defines the region or portion of an image to traverse.
Iterators know a lot about the internal composition of the image, relieving the user from these details. Your algorithm can go through all the pixels of an image without ever knowing the dimension of the image.
To address these diverse requirements, ITK implements a set of ImageIterators, always following the "C" philosophy of :
"You only pay for what you use"
Here is a list of some of the different ImageIterators implemented in ITK:
Region iterators don't define any specific order to walk over the pixels on the image. The user can be sure though, that all the pixels inside the region will be visited.
The following iterators allow to walk the image in specific directions
Iterators in general can have Read/Write access to image pixels. A family of iterators provides Read Only access, in order to preserve the image content. These iterators are equivalent to "C" const pointers :
const * PixelType iterator;
or to STL const_iterators:
vector<PixelType>::const_iterator it;
The class name of the iterator makes clears if it provides const access or not. Some of the const
iterators available are
for (it = vec.begin(); it != vec.end(); ++it)
{}
ImageIterators, unfortunately, are more complicated than STL iterators. ImageIterators need to store more state information than STL iterators. As one example, ImageIterators can walk a region of an image and an image can have multiple ImageIterators traversing different regions simultaneously. Thus, each ImageIterator must maintain which region it traverses. This results in a fairly heavyweight iterator, where comparing two ImageIterators and constructing iterators is an expensive operation. To address this issue, ImageIterators have a slightly different API than STL iterators.
First, you do not ask the container (the image) for an iterator. Instead, you construct an iterator and tell it which image to traverse. Here is a snippet of code to construct an iterator that will walk a region of an image:
ImageType::Pointer im = GetAnImageSomeHow(); ImageIterator it( im, im->GetRequestedRegion() );
Second, since constructing and comparing ImageIterators is expensive, ImageIterators know the beginning and end of the region. So you ask the iterator rather than the container whether the iterator is at the end of a region.
for (it = it.Begin(); !it.IsAtEnd(); ++it)
{
it.Set( 10 );
}
PixelType * p; // creation of the pointer *p = 100; // write access to a data PixelType a = *p; // read access to data
Iterators dereference data using Set()
and Get()
imageIterator.Set( 100 ); PixelType a = imageIterator.Get();
if (it.IsAtBegin()) {} // Fast if (it == it.Begin()) {} // Slow
if (it.IsAtEnd()) {} // Fast if (it == it.End()) {} // Slow
Before starting to write code that use iterators, users should consider to verify if the particular operation they intend to apply to the image is not already defined in the form of an existing image filter.