FYI, Last weekend, @matt.mccormick merged the itk::Experimental::IndexRange template class that I proposed, making it available for ITK 5.0
IndexRange is a modern C++ range of iterators. It should ease iterating over a sequence of consecutive N-dimensional indices, as it can be used as a range-expression of a C++11 range-based for-loop:
for (const Index<Dimension>& index : indexRange)
// Use the 'index' here...!
The order in which the indices are iterated corresponds to the order in which pixels that are stored in a regular itk::Image.
There are two IndexRange type aliases:
ZeroBasedIndexRange<Dimension> has its begin iterator at index zero (thatâs [0, 0] in 2-D, or [0, 0, 0] in 3-D). The range is specified by itk::Size<Dimension>, which is typically the N-dimensional size of an image.
ImageRegionIndexRange<Dimension> can begin at any arbitrary index, as it can be specified by an itk::ImageRegion<Dimension>. (itk::ImageRegion<Dimension>consists of an N-dimensional index and an N-dimensional size.)
The iterators of IndexRange are bidirectional, as they support both forward iteration (++it) and backward iteration (--it). When using ZeroBasedIndexRange, it appears fastest to iterate backward, from the end to the begin of the range:
const ZeroBasedIndexRange<Dimension> range{ imageSize };
const auto begin = range.begin();
auto it = range.end();
while (it != begin)
{
--it;
const auto& index = *it;
// Use the 'index' here...!
}
However, I think that in most cases, the range-based for-loop is fast enough
Do we have any filters that could converted to use this new iterator?
It would be good to come to some kind of recommendation for best practice for performance and style for this type of loop.
I also feel compelled to give a strong word of caution about the ZeroBasedIndexRange this strategy is not suitable for using in most ITK filters. Yes, it will work most of the time but it wonât work all of the time. Filters are expected to work with images or regions that donât have a zero starting index unless they override the VerifyInputInformation with a check. Non-zero based images or regions can occur with streaming, or as a result of filters like the ExtractImageFilter. These complicated features and situations are not well tested in ITK, but I have encountered and fixed them many times.
Actually my first use case was to ease comparing the âold-schoolâ ITK neighborhood iterators with the new ShapedImageNeighborhoodRange, when iterating on an image region.
Old-school ITK neighborhood iteration:
neighborhoodIterator.GoToBegin();
while (!neighborhoodIterator.IsAtEnd())
{
for (itk::SizeValueType i = 0; i < numberOfNeigbors; ++i)
{
auto neighbor = neighborhoodIterator.GetPixel(i);
// Process neighbor pixel here...
}
++neighborhoodIterator;
}
And the corresponding iteration when using ShapedImageNeighborhoodRange + IndexRange:
for (const auto& index : indexRange)
{
shapedImageNeighborhoodRange.SetLocation(index);
for (PixelType neighbor : shapedImageNeighborhoodRange)
{
// Process neighbor pixel here...
}
}
So IndexRange makes it easier to interchange between âold-schoolâ and range-based neighborhood iteration, and it also eases comparing both the performance and the result, for testing purposes.
Still very much Work In Progress: This itk::Experimental::ImageRange should support range-based iteration on the pixels on an itk::Image: https://github.com/N-Dekker/ITK/tree/ImageRange Please tell me what you think!