Skip to content
Merged
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,163 @@
/*=========================================================================
*
* 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:
/** This type is necessary to tell the ShapedImageNeighborhoodRange that the
* constructor accepts a pixel value as (optional) extra parameter. */
using PixelAccessParameterType = PixelType;

// 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
102 changes: 84 additions & 18 deletions Modules/Core/Common/include/itkShapedImageNeighborhoodRange.h
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,47 @@ template<typename TImage,
class ShapedImageNeighborhoodRange final
{
private:

// Empty struct, used internally to denote that there is no pixel access parameter specified.
struct EmptyPixelAccessParameter {};


// Helper class to estimate whether the policy has nested type PixelAccessParameterType.
class CheckPolicy
{
private:
// The Test function has two overloads whose return type is different.
// One of the overloads is only available for overload resolution when
// the policy T has a nested type PixelAccessParameterType (using SFINAE).

template <typename T>
static int Test(typename T::PixelAccessParameterType*);

template <typename T>
static void Test(...);

public:
// This constant tells whether the policy has a PixelAccessParameterType:
static constexpr bool HasPixelAccessParameterType = ! std::is_same<
decltype(Test<TImageNeighborhoodPixelAccessPolicy>(nullptr)),
decltype(Test<TImageNeighborhoodPixelAccessPolicy>())>::value;
};


template <typename TPolicy, bool VPolicyHasPixelAccessParameterType = CheckPolicy::HasPixelAccessParameterType>
struct OptionalPixelAccessParameter
{
using Type = typename TPolicy::PixelAccessParameterType;
};

// Specialization for when the policy does not have PixelAccessParameterType.
template <typename TPolicy>
struct OptionalPixelAccessParameter<TPolicy, false>
{
using Type = EmptyPixelAccessParameter;
};


using ImageType = TImage;
using ImageDimensionType = typename TImage::ImageDimensionType;
using ImageSizeType = typename TImage::SizeType;
Expand All @@ -105,6 +146,9 @@ class ShapedImageNeighborhoodRange final
using IndexType = typename TImage::IndexType;
using IndexValueType = typename TImage::IndexValueType;
using OffsetType = Offset<ImageDimension>;
using OptionalPixelAccessParameterType =
typename OptionalPixelAccessParameter<TImageNeighborhoodPixelAccessPolicy>::Type;


// PixelProxy: internal class that aims to act like a reference to a pixel:
// It acts either like 'PixelType &' or like 'const PixelType &', depending
Expand Down Expand Up @@ -139,13 +183,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 +233,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 +322,8 @@ class ShapedImageNeighborhoodRange final
// The accessor of the image.
NeighborhoodAccessorFunctorType m_NeighborhoodAccessor;

OptionalPixelAccessParameterType m_OptionalPixelAccessParameter;

// The pixel coordinates of the location of the neighborhood.
// May be outside the image!
IndexType m_Location = { {} };
Expand All @@ -297,6 +337,7 @@ class ShapedImageNeighborhoodRange final
const ImageSizeType& imageSize,
const OffsetType& offsetTable,
const NeighborhoodAccessorFunctorType& neighborhoodAccessor,
const OptionalPixelAccessParameterType optionalPixelAccessParameter,
const IndexType& location,
const OffsetType* const offset) ITK_NOEXCEPT
:
Expand All @@ -306,12 +347,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, OptionalPixelAccessParameterType>::value,
"This helper function should only be used for OptionalPixelAccessParameterType!");
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,7 +405,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 reference{m_ImageBufferPointer, CreatePixelAccessPolicy(m_OptionalPixelAccessParameter)};
}


Expand Down Expand Up @@ -535,6 +593,8 @@ class ShapedImageNeighborhoodRange final
// The number of neighborhood pixels.
const std::size_t m_NumberOfNeighborhoodPixels;

const OptionalPixelAccessParameterType m_OptionalPixelAccessParameter;

public:
using const_iterator = QualifiedIterator<true>;
using iterator = QualifiedIterator<false>;
Expand All @@ -552,7 +612,8 @@ class ShapedImageNeighborhoodRange final
ImageType& image,
const IndexType& location,
const OffsetType* const shapeOffsets,
const std::size_t numberOfNeigborhoodPixels)
const std::size_t numberOfNeigborhoodPixels,
const OptionalPixelAccessParameterType optionalPixelAccessParameter = {})
:
m_ImageBufferPointer{image.ImageType::GetBufferPointer()},
// Note: Use parentheses instead of curly braces to initialize data members,
Expand All @@ -562,7 +623,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 @@ -586,28 +648,32 @@ class ShapedImageNeighborhoodRange final
ShapedImageNeighborhoodRange(
ImageType& image,
const IndexType& location,
const TContainerOfOffsets& shapeOffsets)
const TContainerOfOffsets& shapeOffsets,
const OptionalPixelAccessParameterType optionalPixelAccessParameter = {})
:
ShapedImageNeighborhoodRange{
ShapedImageNeighborhoodRange
{
image,
location,
shapeOffsets.data(),
shapeOffsets.size()}
shapeOffsets.size(),
optionalPixelAccessParameter
}
{
}

/** Returns an iterator to the first neighborhood pixel. */
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