Custom border extrapolation of ShapedImageNeighborhoodRange by ImageNeighborhoodPixelAccessPolicy

@matt.mccormick, @phcerdan I just did a performance comparison between “old-school” and new range-based neighborhood iteration, both doing zero-padding (constant boundary). Using Visual C++ 2017 64-bit Release, I observed that the range-based neighborhood iteration implemented by http://review.source.kitware.com/#/c/23795/7 can be more than twice as fast as the “old-school” iteration :grinning:

The new range-based neighborhood iteration was tested by the following function. It calculates a number, sumOfNeighbors, just so that the optimizer cannot eliminate all the work.

unsigned TestRangeBasedIteration(const ImageType& image, const SizeType& radius)
{
  using namespace itk::Experimental;
  using RangeType = ShapedImageNeighborhoodRange<const ImageType,
    ConstantBoundaryImageNeighborhoodPixelAccessPolicy<ImageType>>;

  const auto region = image.GetBufferedRegion();
  const auto imageSize = region.GetSize();
  const auto offsets = GenerateRectangularImageNeighborhoodOffsets(radius);

  RangeType range{ image, IndexType{}, offsets };
  IndexType index{};
  unsigned sumOfNeighbors = 0;

  for (auto i = region.GetNumberOfPixels(); i > 0; --i)
  {
    range.SetLocation(index);

    for (const PixelType neighbor : range)
    {
      sumOfNeighbors += neighbor;
    }
    GoToNextPixelIndex(index, imageSize);
  }
  return sumOfNeighbors;
}

And the old-school neighborhood iteration was tested by the following function:

unsigned TestOldSchoolIteration(const ImageType& image, const SizeType& radius)
{
  using NeighborhoodIteratorType = itk::ConstNeighborhoodIterator<ImageType,
    itk::ConstantBoundaryCondition<ImageType>>;

  const auto region = image.GetBufferedRegion();
  const auto imageSize = region.GetSize();

  NeighborhoodIteratorType neighborhoodIterator(radius, &image, region);
  const auto numberOfNeigbors = neighborhoodIterator.Size();
  unsigned sumOfNeighbors = 0;

  while (!neighborhoodIterator.IsAtEnd())
  {
    for (itk::SizeValueType i = 0; i < numberOfNeigbors; ++i)
    {
      sumOfNeighbors += neighborhoodIterator.GetPixel(i);
    }
    ++neighborhoodIterator;
  }
  return sumOfNeighbors;
}

The full test is at https://gist.github.com/N-Dekker/eb94ba817b2a16e75d5e7c3fe54ec66f

Please check if you find it a fair comparison!

1 Like