Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,159 @@
/*=========================================================================
*
* Copyright Insight Software Consortium
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0.txt
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*=========================================================================*/

#ifndef itkConstantBoundaryImageNeighborhoodPixelAccessPolicy_h
#define itkConstantBoundaryImageNeighborhoodPixelAccessPolicy_h

#include "itkIndex.h"
#include "itkOffset.h"
#include "itkSize.h"

namespace itk
{
namespace Experimental
{

/**
* \class ConstantBoundaryImageNeighborhoodPixelAccessPolicy
* ImageNeighborhoodPixelAccessPolicy class for ShapedImageNeighborhoodRange.
* Allows getting and setting the value of a pixel, located in a specified
* neighborhood location, at a specified offset. Uses a constant as value
* for pixels outside the image border.
*
* \see ShapedNeighborhoodIterator
* \see ConstantBoundaryCondition
* \ingroup ImageIterators
* \ingroup ITKCommon
*/
template <typename TImage>
class ConstantBoundaryImageNeighborhoodPixelAccessPolicy final
{
private:
using NeighborhoodAccessorFunctorType = typename TImage::NeighborhoodAccessorFunctorType;
using PixelType = typename TImage::PixelType;
using InternalPixelType = typename TImage::InternalPixelType;

using ImageDimensionType = typename TImage::ImageDimensionType;
static constexpr ImageDimensionType ImageDimension = TImage::ImageDimension;

using IndexType = Index<ImageDimension>;
using OffsetType = Offset<ImageDimension>;
using ImageSizeType = Size<ImageDimension>;
using ImageSizeValueType = SizeValueType;

// Index value to the image buffer, indexing the current pixel. -1 is used to indicate out-of-bounds.
const IndexValueType m_PixelIndexValue;

// A reference to the accessor of the image.
const NeighborhoodAccessorFunctorType& m_NeighborhoodAccessor;

// The constant whose value is returned a pixel value outside the image is queried.
const PixelType m_Constant;


// Private helper function. Tells whether the pixel at 'pixelIndex' is inside the image.
static bool IsInside(
const IndexType& pixelIndex,
const ImageSizeType& imageSize) ITK_NOEXCEPT
{
bool result = true;

for (ImageDimensionType i = 0; i < ImageDimension; ++i)
{
const IndexValueType indexValue = pixelIndex[i];

// Note: Do not 'quickly' break or return out of the for-loop when the
// result is false! For performance reasons (loop unrolling, etc.) it
// appears preferable to complete the for-loop iteration in this case!
result = result &&
(indexValue >= 0) &&
(static_cast<ImageSizeValueType>(indexValue) < imageSize[i]);
}
return result;
}


// Private helper function. Calculates and returns the index value of the
// current pixel within the image buffer.
static IndexValueType CalculatePixelIndexValue(
const OffsetType& offsetTable,
const IndexType& pixelIndex) ITK_NOEXCEPT
{
IndexValueType result = 0;

for (ImageDimensionType i = 0; i < ImageDimension; ++i)
{
result += pixelIndex[i] * offsetTable[i];
}
return result;
}

public:
// Deleted member functions:
ConstantBoundaryImageNeighborhoodPixelAccessPolicy() = delete;
ConstantBoundaryImageNeighborhoodPixelAccessPolicy& operator=(const ConstantBoundaryImageNeighborhoodPixelAccessPolicy&) = delete;

// Explicitly-defaulted functions:
~ConstantBoundaryImageNeighborhoodPixelAccessPolicy() = default;
ConstantBoundaryImageNeighborhoodPixelAccessPolicy(
const ConstantBoundaryImageNeighborhoodPixelAccessPolicy&) = default;

/** Constructor called directly by the pixel proxy of
* ShapedImageNeighborhoodRange. */
ConstantBoundaryImageNeighborhoodPixelAccessPolicy(
const ImageSizeType& imageSize,
const OffsetType& offsetTable,
const NeighborhoodAccessorFunctorType& neighborhoodAccessor,
const IndexType& pixelIndex,
const PixelType constant = {}) ITK_NOEXCEPT
:
m_PixelIndexValue
{
IsInside(pixelIndex, imageSize) ? CalculatePixelIndexValue(offsetTable, pixelIndex) : -1
},
m_NeighborhoodAccessor(neighborhoodAccessor),
m_Constant{constant}
{
}


/** Retrieves the pixel value from the image buffer, at the current
* index. When the index is out of bounds, it returns the constant
* value specified during construction. */
PixelType GetPixelValue(const InternalPixelType* const imageBufferPointer) const ITK_NOEXCEPT
{
return (m_PixelIndexValue < 0) ?
m_Constant :
m_NeighborhoodAccessor.Get(imageBufferPointer + m_PixelIndexValue);
}

/** Sets the value of the image buffer at the current index value to the
* specified value. */
void SetPixelValue(InternalPixelType* const imageBufferPointer, const PixelType& pixelValue) const ITK_NOEXCEPT
{
if (m_PixelIndexValue >= 0)
{
m_NeighborhoodAccessor.Set(imageBufferPointer + m_PixelIndexValue, pixelValue);
}
}
};

} // namespace Experimental
} // namespace itk

#endif
72 changes: 52 additions & 20 deletions Modules/Core/Common/include/itkShapedImageNeighborhoodRange.h
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,16 @@ namespace itk
namespace Experimental
{

/**
* \class EmptyPixelAccessParameter
* Empty struct, used by ShapedImageNeighborhoodRange to denote that
* there is no pixel access parameter specified.
*
* \ingroup ImageIterators
* \ingroup ITKCommon
*/
struct EmptyPixelAccessParameter {};

/**
* \class ShapedImageNeighborhoodRange
* Modern C++11 range to iterate over a neighborhood of pixels.
Expand Down Expand Up @@ -90,7 +100,8 @@ namespace Experimental
* \ingroup ITKCommon
*/
template<typename TImage,
typename TImageNeighborhoodPixelAccessPolicy = ZeroFluxNeumannImageNeighborhoodPixelAccessPolicy<TImage>>
typename TImageNeighborhoodPixelAccessPolicy = ZeroFluxNeumannImageNeighborhoodPixelAccessPolicy<TImage>,
typename TOptionalPixelAccessParameter = EmptyPixelAccessParameter>
class ShapedImageNeighborhoodRange final
{
private:
Expand Down Expand Up @@ -139,13 +150,10 @@ class ShapedImageNeighborhoodRange final
// Constructor, called directly by operator*() of the iterator class.
PixelProxy(
const InternalPixelType* const imageBufferPointer,
const ImageSizeType& imageSize,
const OffsetType& offsetTable,
const NeighborhoodAccessorFunctorType& neighborhoodAccessor,
const IndexType& pixelIndex) ITK_NOEXCEPT
const TImageNeighborhoodPixelAccessPolicy& pixelAccessPolicy) ITK_NOEXCEPT
:
m_ImageBufferPointer{imageBufferPointer},
m_PixelAccessPolicy{ imageSize, offsetTable, neighborhoodAccessor, pixelIndex }
m_PixelAccessPolicy{pixelAccessPolicy}
{
}

Expand Down Expand Up @@ -192,13 +200,10 @@ class ShapedImageNeighborhoodRange final
// Constructor, called directly by operator*() of the iterator class.
PixelProxy(
InternalPixelType* const imageBufferPointer,
const ImageSizeType& imageSize,
const OffsetType& offsetTable,
const NeighborhoodAccessorFunctorType& neighborhoodAccessor,
const IndexType& pixelIndex) ITK_NOEXCEPT
const TImageNeighborhoodPixelAccessPolicy& pixelAccessPolicy) ITK_NOEXCEPT
:
m_ImageBufferPointer{imageBufferPointer},
m_PixelAccessPolicy{ imageSize, offsetTable, neighborhoodAccessor, pixelIndex }
m_PixelAccessPolicy{pixelAccessPolicy}
{
}

Expand Down Expand Up @@ -284,6 +289,8 @@ class ShapedImageNeighborhoodRange final
// The accessor of the image.
NeighborhoodAccessorFunctorType m_NeighborhoodAccessor;

TOptionalPixelAccessParameter m_OptionalPixelAccessParameter;

// The pixel coordinates of the location of the neighborhood.
// May be outside the image!
IndexType m_Location = { {} };
Expand All @@ -297,6 +304,7 @@ class ShapedImageNeighborhoodRange final
const ImageSizeType& imageSize,
const OffsetType& offsetTable,
const NeighborhoodAccessorFunctorType& neighborhoodAccessor,
const TOptionalPixelAccessParameter optionalPixelAccessParameter,
const IndexType& location,
const OffsetType* const offset) ITK_NOEXCEPT
:
Expand All @@ -306,12 +314,29 @@ class ShapedImageNeighborhoodRange final
m_ImageSize(imageSize),
m_OffsetTable(offsetTable),
m_NeighborhoodAccessor(neighborhoodAccessor),
m_OptionalPixelAccessParameter(optionalPixelAccessParameter),
m_Location(location),
m_CurrentOffset{offset}
{
assert(m_ImageBufferPointer != nullptr);
}


TImageNeighborhoodPixelAccessPolicy CreatePixelAccessPolicy(EmptyPixelAccessParameter) const
{
return TImageNeighborhoodPixelAccessPolicy{ m_ImageSize, m_OffsetTable, m_NeighborhoodAccessor, m_Location + *m_CurrentOffset };
}

template <typename TPixelAccessParameter>
TImageNeighborhoodPixelAccessPolicy CreatePixelAccessPolicy(const TPixelAccessParameter pixelAccessParameter) const
{
static_assert(std::is_same< TPixelAccessParameter, TOptionalPixelAccessParameter>::value,
"This helper function should only be used for TOptionalPixelAccessParameter!");
static_assert(!std::is_same< TPixelAccessParameter, EmptyPixelAccessParameter>::value,
"EmptyPixelAccessParameter indicates that there is no pixel access parameter specified!");
return TImageNeighborhoodPixelAccessPolicy{ m_ImageSize, m_OffsetTable, m_NeighborhoodAccessor, m_Location + *m_CurrentOffset, pixelAccessParameter };
}

public:
// Types conforming the iterator requirements of the C++ standard library:
using difference_type = std::ptrdiff_t;
Expand Down Expand Up @@ -347,8 +372,7 @@ class ShapedImageNeighborhoodRange final
/** Returns a reference to the current pixel. */
reference operator*() const ITK_NOEXCEPT
{
return reference{m_ImageBufferPointer, m_ImageSize, m_OffsetTable, m_NeighborhoodAccessor, m_Location + *m_CurrentOffset};
}
return {m_ImageBufferPointer, CreatePixelAccessPolicy(m_OptionalPixelAccessParameter)};


/** Prefix increment ('++it'). */
Expand Down Expand Up @@ -535,6 +559,8 @@ class ShapedImageNeighborhoodRange final
// The number of neighborhood pixels.
const std::size_t m_NumberOfNeighborhoodPixels;

const TOptionalPixelAccessParameter m_OptionalPixelAccessParameter;

public:
using const_iterator = QualifiedIterator<true>;
using iterator = QualifiedIterator<false>;
Expand All @@ -549,7 +575,8 @@ class ShapedImageNeighborhoodRange final
ImageType& image,
const IndexType& location,
const OffsetType* const shapeOffsets,
const std::size_t numberOfNeigborhoodPixels)
const std::size_t numberOfNeigborhoodPixels,
const TOptionalPixelAccessParameter optionalPixelAccessParameter = {})
:
m_ImageBufferPointer{image.ImageType::GetBufferPointer()},
// Note: Use parentheses instead of curly braces to initialize data members,
Expand All @@ -559,7 +586,8 @@ class ShapedImageNeighborhoodRange final
m_NeighborhoodAccessor(image.GetNeighborhoodAccessor()),
m_Location(location),
m_ShapeOffsets{ shapeOffsets },
m_NumberOfNeighborhoodPixels{ numberOfNeigborhoodPixels }
m_NumberOfNeighborhoodPixels{ numberOfNeigborhoodPixels },
m_OptionalPixelAccessParameter(optionalPixelAccessParameter)
{
assert(m_ImageBufferPointer != nullptr);
const OffsetValueType* const offsetTable = image.GetOffsetTable();
Expand All @@ -580,13 +608,17 @@ class ShapedImageNeighborhoodRange final
ShapedImageNeighborhoodRange(
ImageType& image,
const IndexType& location,
TContainerOfOffsets&& shapeOffsets)
TContainerOfOffsets&& shapeOffsets,
const TOptionalPixelAccessParameter optionalPixelAccessParameter = {})
:
ShapedImageNeighborhoodRange{
ShapedImageNeighborhoodRange
{
image,
location,
shapeOffsets.data(),
shapeOffsets.size()}
shapeOffsets.size(),
optionalPixelAccessParameter
}
{
static_assert(!std::is_rvalue_reference<decltype(shapeOffsets)>::value,
"The container of offsets should not be a temporary (rvalue) object!");
Expand All @@ -596,14 +628,14 @@ class ShapedImageNeighborhoodRange final
iterator begin() const ITK_NOEXCEPT
{
assert(m_ImageBufferPointer != nullptr);
return iterator(m_ImageBufferPointer, m_ImageSize, m_OffsetTable, m_NeighborhoodAccessor, m_Location, m_ShapeOffsets);
return iterator(m_ImageBufferPointer, m_ImageSize, m_OffsetTable, m_NeighborhoodAccessor, m_OptionalPixelAccessParameter, m_Location, m_ShapeOffsets);
}

/** Returns an 'end iterator' for this range. */
iterator end() const ITK_NOEXCEPT
{
assert(m_ImageBufferPointer != nullptr);
return iterator(m_ImageBufferPointer, m_ImageSize, m_OffsetTable, m_NeighborhoodAccessor, m_Location, m_ShapeOffsets + m_NumberOfNeighborhoodPixels);
return iterator(m_ImageBufferPointer, m_ImageSize, m_OffsetTable, m_NeighborhoodAccessor, m_OptionalPixelAccessParameter, m_Location, m_ShapeOffsets + m_NumberOfNeighborhoodPixels);
}

/** Returns a const iterator to the first neighborhood pixel.
Expand Down
1 change: 1 addition & 0 deletions Modules/Core/Common/test/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -627,6 +627,7 @@ set(ITKCommonGTests
itkAggregateTypesTest.cxx
itkBuildInformationTest.cxx
itkConnectedImageNeighborhoodShapeGTest.cxx
itkConstantBoundaryImageNeighborhoodPixelAccessPolicyGTest.cxx
itkImageNeighborhoodOffsetsGTest.cxx
itkIndexRangeGTest.cxx
itkShapedImageNeighborhoodRangeGTest.cxx
Expand Down
Loading