Generate the Offsets of a Shaped Image Neighborhood#

Synopsis#

This example demonstrates various ways to create a container of offsets, to specify the shape of a neighborhood of pixels:

  • An arbitrary shape

  • A 4-connected neighborhood shape

  • A rectangular neighborhood shape

These offsets may be used to specify the shape of a ShapedImageNeighborhoodRange (as included with this code example), or a ShapedNeighborhoodIterator.

Results#

Output:

Shape of some arbitrary offsets:

  [0, -1] [0, 1] [1, 1]

  0 0 0 0 0 0 0
  0 0 0 0 0 0 0
  0 0 0 1 0 0 0
  0 0 0 0 0 0 0
  0 0 0 2 3 0 0
  0 0 0 0 0 0 0
  0 0 0 0 0 0 0

4-connected neighborhood shape (excluding the center pixel) with maximumCityblockDistance = 1:

  [0, -1] [-1, 0] [1, 0] [0, 1]

  0 0 0 0 0 0 0
  0 0 0 0 0 0 0
  0 0 0 1 0 0 0
  0 0 2 0 3 0 0
  0 0 0 4 0 0 0
  0 0 0 0 0 0 0
  0 0 0 0 0 0 0

Rectangular shape of radius [1, 2]:

  [-1, -2] [0, -2] [1, -2] [-1, -1] [0, -1] [1, -1] [-1, 0] [0, 0] [1, 0] [-1, 1] [0, 1] [1, 1] [-1, 2] [0, 2] [1, 2]

  0 0 0 0 0 0 0
  0 0 1 2 3 0 0
  0 0 4 5 6 0 0
  0 0 7 8 9 0 0
  0 0 A B C 0 0
  0 0 D E F 0 0
  0 0 0 0 0 0 0

Code#

C++#

#include "itkConnectedImageNeighborhoodShape.h"
#include "itkImage.h"
#include "itkImageBufferRange.h"
#include "itkImageNeighborhoodOffsets.h"
#include "itkRectangularImageNeighborhoodShape.h"
#include "itkShapedImageNeighborhoodRange.h"

#include <array>
#include <cassert>
#include <numeric> // For iota
#include <ios>     // For hex

namespace
{
constexpr unsigned int Dimension{ 2 };
using OffsetType = itk::Offset<Dimension>;

// Print the specified offsets of a neighborhood shape. Also prints the pixel values of a small image for which such a
// shaped neigborhood located at the image center is filled with consecutive values, 1, 2, 3, ..., N.
template <typename TOffsets>
void
PrintImageNeighborhoodShape(const TOffsets & offsets)
{
  std::cout << "  ";

  for (const OffsetType & offset : offsets)
  {
    std::cout << offset << ' ';
  }

  using ImageType = itk::Image<int>;
  const auto             image = ImageType::New();
  constexpr unsigned int imageSize{ 7 };
  image->SetRegions(ImageType::SizeType::Filled(imageSize));
  image->Allocate(true);

  const auto                                         centerIndex = ImageType::IndexType::Filled(imageSize / 2);
  const itk::ShapedImageNeighborhoodRange<ImageType> shapedImageNeighborhoodRange(*image, centerIndex, offsets);

  // Set the values of the pixels in the "shaped neighborhood" of the image center to 1, 2, 3, ..., N, consecutively.
  std::iota(shapedImageNeighborhoodRange.begin(), shapedImageNeighborhoodRange.end(), 1);

  std::cout << "\n\n";
  const std::ios_base::fmtflags flags(std::cout.flags());
  std::cout << std::hex << std::uppercase;

  const itk::ImageBufferRange<const ImageType> imageBufferRange(*image);
  auto                                         imageBufferIterator = imageBufferRange.cbegin();

  for (int y{ 0 }; y < imageSize; ++y)
  {
    std::cout << "  ";

    for (int x{ 0 }; x < imageSize; ++x)
    {
      std::cout << *imageBufferIterator << ' ';
      ++imageBufferIterator;
    }
    std::cout << '\n';
  }
  std::cout.flags(flags);
  std::cout << '\n';
}

} // namespace


int
main()
{
  const std::array<OffsetType, 3> offsets = { { { { 0, -1 } }, { { 0, 1 } }, { { 1, 1 } } } };
  std::cout << "Shape of some arbitrary offsets:\n\n";
  PrintImageNeighborhoodShape(offsets);

  const bool   includeCenterPixel = false;
  const size_t maximumCityblockDistance = 1;
  std::cout << "4-connected neighborhood shape (excluding the center pixel) with maximumCityblockDistance = "
            << maximumCityblockDistance << ":\n\n";

  // GenerateConnectedImageNeighborhoodShapeOffsets returns an std::array of offsets.
  const auto connectedImageNeighborhoodShapeOffsets =
    itk::GenerateConnectedImageNeighborhoodShapeOffsets<Dimension, maximumCityblockDistance, includeCenterPixel>();
  PrintImageNeighborhoodShape(connectedImageNeighborhoodShapeOffsets);

  const itk::Size<Dimension> radius = { { 1, 2 } };
  std::cout << "Rectangular shape of radius " << radius << ":\n\n";

  // GenerateRectangularImageNeighborhoodOffsets returns an std::vector of offsets.
  const auto rectangularImageNeighborhoodOffsets = itk::GenerateRectangularImageNeighborhoodOffsets(radius);
  PrintImageNeighborhoodShape(rectangularImageNeighborhoodOffsets);
}

Classes demonstrated#

template<unsigned int VImageDimension>
class ConnectedImageNeighborhoodShape

Connected image-neighborhood shape, based on the topological property of pixel connectivity. Eases creating a sequence of offsets to construct a ShapedImageNeighborhoodRange object. Can also be used to specify the shape of a ShapedNeighborhoodIterator, using its ActivateOffset member function.

This shape class supports generating offsets in colexicographic order. Which means that, for example, a sequence of generated offsets for a 2-dimensional shape will have offset {1, 0} before offset {0, 1}. This order was chosen because it is usually in agreement with the order of the corresponding neighbor pixels, as stored in the internal image buffer.

The following example generates the offsets for a 3-dimensional 18-connected neighborhood shape, including the center pixel, and asserts that the result is as expected:

std::size_t maximumCityblockDistance = 2;
bool includeCenterPixel = true;
ConnectedImageNeighborhoodShape<3> shape{ maximumCityblockDistance, includeCenterPixel };
std::vector<Offset<3>> offsets = GenerateImageNeighborhoodOffsets(shape);
assert(offsets.size() == 19);
assert(offsets == std::vector<Offset<3>>(
{
  {{0, -1, -1}}, {{-1, 0, -1}}, {{0, 0, -1}},
  {{1, 0, -1}}, {{0, 1, -1}}, {{-1, -1, 0}},
  {{0, -1, 0}}, {{1, -1, 0}}, {{-1, 0, 0}},
  {{0, 0, 0}},
  {{1, 0, 0}}, {{-1, 1, 0}}, {{0, 1, 0}},
  {{1, 1, 0}}, {{0, -1, 1}}, {{-1, 0, 1}},
  {{0, 0, 1}}, {{1, 0, 1}}, {{0, 1, 1}}
}));

The following code shows how to create 4-connected, 8-connected, 6-connected, 18-connected, and 26-connected neighborhood shapes:

// 2-dimensional:
ConnectedImageNeighborhoodShape<2> _4_connectedShape{ 1, includeCenterPixel };
ConnectedImageNeighborhoodShape<2> _8_connectedShape{ 2, includeCenterPixel };
// 3-dimensional:
ConnectedImageNeighborhoodShape<3> _6_connectedShape{ 1, includeCenterPixel };
ConnectedImageNeighborhoodShape<3> _18_connectedShape{ 2, includeCenterPixel };
ConnectedImageNeighborhoodShape<3> _26_connectedShape{ 3, includeCenterPixel };

Author

Niels Dekker, LKEB, Leiden University Medical Center

See

ShapedImageNeighborhoodRange

See

ShapedNeighborhoodIterator

See itk::ConnectedImageNeighborhoodShape for additional documentation.
template<unsigned int VImageDimension>
class RectangularImageNeighborhoodShape

Rectangular (or hyperrectangular) image-neighborhood shape. Eases creating a sequence of offsets for ShapedImageNeighborhoodRange. Can also be used for ShapedNeighborhoodIterator.

The following example creates a 3 x 5 rectangular neighborhood around pixel location [10, 20], and generates the offsets for a neighborhood range:

const Index<> location = { 10, 20 };
const Size<> radius = { { 1, 2 } };
const RectangularImageNeighborhoodShape<2> shape{ radius };
const std::vector<Offset<>> offsets = GenerateImageNeighborhoodOffsets(shape);
ShapedImageNeighborhoodRange<ImageType> neighborhoodRange{ *image, location, offsets };

See

ShapedNeighborhoodIterator

See

ShapedImageNeighborhoodRange

See itk::RectangularImageNeighborhoodShape for additional documentation.