Skip to content

Commit 578ccbc

Browse files
N-Dekkerthewtex
authored andcommitted
ENH: Added policy for constant NeighborhoodRange values outside image
Added ConstantBoundaryImageNeighborhoodPixelAccessPolicy - a policy to get a specified constant value from ShapedImageNeighborhoodRange when a pixel value outside the image is queried. Equivalent to itk::ConstantBoundaryCondition with itk::ConstNeighborhoodIterator. Adapted ShapedImageNeighborhoodRange to allow the user to specify an optional pixel access parameter. This parameter allows specifying the constant value for this policy. Change-Id: I5a1ae9f918a31d03a4dd3d84ceedd4385ab4ec7d
1 parent ed743e2 commit 578ccbc

4 files changed

Lines changed: 376 additions & 19 deletions

File tree

Lines changed: 159 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,159 @@
1+
/*=========================================================================
2+
*
3+
* Copyright Insight Software Consortium
4+
*
5+
* Licensed under the Apache License, Version 2.0 (the "License");
6+
* you may not use this file except in compliance with the License.
7+
* You may obtain a copy of the License at
8+
*
9+
* http://www.apache.org/licenses/LICENSE-2.0.txt
10+
*
11+
* Unless required by applicable law or agreed to in writing, software
12+
* distributed under the License is distributed on an "AS IS" BASIS,
13+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
* See the License for the specific language governing permissions and
15+
* limitations under the License.
16+
*
17+
*=========================================================================*/
18+
19+
#ifndef itkConstantBoundaryImageNeighborhoodPixelAccessPolicy_h
20+
#define itkConstantBoundaryImageNeighborhoodPixelAccessPolicy_h
21+
22+
#include "itkIndex.h"
23+
#include "itkOffset.h"
24+
#include "itkSize.h"
25+
26+
namespace itk
27+
{
28+
namespace Experimental
29+
{
30+
31+
/**
32+
* \class ConstantBoundaryImageNeighborhoodPixelAccessPolicy
33+
* ImageNeighborhoodPixelAccessPolicy class for ShapedImageNeighborhoodRange.
34+
* Allows getting and setting the value of a pixel, located in a specified
35+
* neighborhood location, at a specified offset. Uses a constant as value
36+
* for pixels outside the image border.
37+
*
38+
* \see ShapedNeighborhoodIterator
39+
* \see ConstantBoundaryCondition
40+
* \ingroup ImageIterators
41+
* \ingroup ITKCommon
42+
*/
43+
template <typename TImage>
44+
class ConstantBoundaryImageNeighborhoodPixelAccessPolicy final
45+
{
46+
private:
47+
using NeighborhoodAccessorFunctorType = typename TImage::NeighborhoodAccessorFunctorType;
48+
using PixelType = typename TImage::PixelType;
49+
using InternalPixelType = typename TImage::InternalPixelType;
50+
51+
using ImageDimensionType = typename TImage::ImageDimensionType;
52+
static constexpr ImageDimensionType ImageDimension = TImage::ImageDimension;
53+
54+
using IndexType = Index<ImageDimension>;
55+
using OffsetType = Offset<ImageDimension>;
56+
using ImageSizeType = Size<ImageDimension>;
57+
using ImageSizeValueType = SizeValueType;
58+
59+
// Index value to the image buffer, indexing the current pixel. -1 is used to indicate out-of-bounds.
60+
const IndexValueType m_PixelIndexValue;
61+
62+
// A reference to the accessor of the image.
63+
const NeighborhoodAccessorFunctorType& m_NeighborhoodAccessor;
64+
65+
// The constant whose value is returned a pixel value outside the image is queried.
66+
const PixelType m_Constant;
67+
68+
69+
// Private helper function. Tells whether the pixel at 'pixelIndex' is inside the image.
70+
static bool IsInside(
71+
const IndexType& pixelIndex,
72+
const ImageSizeType& imageSize) ITK_NOEXCEPT
73+
{
74+
bool result = true;
75+
76+
for (ImageDimensionType i = 0; i < ImageDimension; ++i)
77+
{
78+
const IndexValueType indexValue = pixelIndex[i];
79+
80+
// Note: Do not 'quickly' break or return out of the for-loop when the
81+
// result is false! For performance reasons (loop unrolling, etc.) it
82+
// appears preferable to complete the for-loop iteration in this case!
83+
result = result &&
84+
(indexValue >= 0) &&
85+
(static_cast<ImageSizeValueType>(indexValue) < imageSize[i]);
86+
}
87+
return result;
88+
}
89+
90+
91+
// Private helper function. Calculates and returns the index value of the
92+
// current pixel within the image buffer.
93+
static IndexValueType CalculatePixelIndexValue(
94+
const OffsetType& offsetTable,
95+
const IndexType& pixelIndex) ITK_NOEXCEPT
96+
{
97+
IndexValueType result = 0;
98+
99+
for (ImageDimensionType i = 0; i < ImageDimension; ++i)
100+
{
101+
result += pixelIndex[i] * offsetTable[i];
102+
}
103+
return result;
104+
}
105+
106+
public:
107+
// Deleted member functions:
108+
ConstantBoundaryImageNeighborhoodPixelAccessPolicy() = delete;
109+
ConstantBoundaryImageNeighborhoodPixelAccessPolicy& operator=(const ConstantBoundaryImageNeighborhoodPixelAccessPolicy&) = delete;
110+
111+
// Explicitly-defaulted functions:
112+
~ConstantBoundaryImageNeighborhoodPixelAccessPolicy() = default;
113+
ConstantBoundaryImageNeighborhoodPixelAccessPolicy(
114+
const ConstantBoundaryImageNeighborhoodPixelAccessPolicy&) = default;
115+
116+
/** Constructor called directly by the pixel proxy of
117+
* ShapedImageNeighborhoodRange. */
118+
ConstantBoundaryImageNeighborhoodPixelAccessPolicy(
119+
const ImageSizeType& imageSize,
120+
const OffsetType& offsetTable,
121+
const NeighborhoodAccessorFunctorType& neighborhoodAccessor,
122+
const IndexType& pixelIndex,
123+
const PixelType constant = {}) ITK_NOEXCEPT
124+
:
125+
m_PixelIndexValue
126+
{
127+
IsInside(pixelIndex, imageSize) ? CalculatePixelIndexValue(offsetTable, pixelIndex) : -1
128+
},
129+
m_NeighborhoodAccessor(neighborhoodAccessor),
130+
m_Constant{constant}
131+
{
132+
}
133+
134+
135+
/** Retrieves the pixel value from the image buffer, at the current
136+
* index. When the index is out of bounds, it returns the constant
137+
* value specified during construction. */
138+
PixelType GetPixelValue(const InternalPixelType* const imageBufferPointer) const ITK_NOEXCEPT
139+
{
140+
return (m_PixelIndexValue < 0) ?
141+
m_Constant :
142+
m_NeighborhoodAccessor.Get(imageBufferPointer + m_PixelIndexValue);
143+
}
144+
145+
/** Sets the value of the image buffer at the current index value to the
146+
* specified value. */
147+
void SetPixelValue(InternalPixelType* const imageBufferPointer, const PixelType& pixelValue) const ITK_NOEXCEPT
148+
{
149+
if (m_PixelIndexValue >= 0)
150+
{
151+
m_NeighborhoodAccessor.Set(imageBufferPointer + m_PixelIndexValue, pixelValue);
152+
}
153+
}
154+
};
155+
156+
} // namespace Experimental
157+
} // namespace itk
158+
159+
#endif

Modules/Core/Common/include/itkShapedImageNeighborhoodRange.h

Lines changed: 52 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,16 @@ namespace itk
3535
namespace Experimental
3636
{
3737

38+
/**
39+
* \class EmptyPixelAccessParameter
40+
* Empty struct, used by ShapedImageNeighborhoodRange to denote that
41+
* there is no pixel access parameter specified.
42+
*
43+
* \ingroup ImageIterators
44+
* \ingroup ITKCommon
45+
*/
46+
struct EmptyPixelAccessParameter {};
47+
3848
/**
3949
* \class ShapedImageNeighborhoodRange
4050
* Modern C++11 range to iterate over a neighborhood of pixels.
@@ -90,7 +100,8 @@ namespace Experimental
90100
* \ingroup ITKCommon
91101
*/
92102
template<typename TImage,
93-
typename TImageNeighborhoodPixelAccessPolicy = ZeroFluxNeumannImageNeighborhoodPixelAccessPolicy<TImage>>
103+
typename TImageNeighborhoodPixelAccessPolicy = ZeroFluxNeumannImageNeighborhoodPixelAccessPolicy<TImage>,
104+
typename TOptionalPixelAccessParameter = EmptyPixelAccessParameter>
94105
class ShapedImageNeighborhoodRange final
95106
{
96107
private:
@@ -139,13 +150,10 @@ class ShapedImageNeighborhoodRange final
139150
// Constructor, called directly by operator*() of the iterator class.
140151
PixelProxy(
141152
const InternalPixelType* const imageBufferPointer,
142-
const ImageSizeType& imageSize,
143-
const OffsetType& offsetTable,
144-
const NeighborhoodAccessorFunctorType& neighborhoodAccessor,
145-
const IndexType& pixelIndex) ITK_NOEXCEPT
153+
const TImageNeighborhoodPixelAccessPolicy& pixelAccessPolicy) ITK_NOEXCEPT
146154
:
147155
m_ImageBufferPointer{imageBufferPointer},
148-
m_PixelAccessPolicy{ imageSize, offsetTable, neighborhoodAccessor, pixelIndex }
156+
m_PixelAccessPolicy{pixelAccessPolicy}
149157
{
150158
}
151159

@@ -192,13 +200,10 @@ class ShapedImageNeighborhoodRange final
192200
// Constructor, called directly by operator*() of the iterator class.
193201
PixelProxy(
194202
InternalPixelType* const imageBufferPointer,
195-
const ImageSizeType& imageSize,
196-
const OffsetType& offsetTable,
197-
const NeighborhoodAccessorFunctorType& neighborhoodAccessor,
198-
const IndexType& pixelIndex) ITK_NOEXCEPT
203+
const TImageNeighborhoodPixelAccessPolicy& pixelAccessPolicy) ITK_NOEXCEPT
199204
:
200205
m_ImageBufferPointer{imageBufferPointer},
201-
m_PixelAccessPolicy{ imageSize, offsetTable, neighborhoodAccessor, pixelIndex }
206+
m_PixelAccessPolicy{pixelAccessPolicy}
202207
{
203208
}
204209

@@ -284,6 +289,8 @@ class ShapedImageNeighborhoodRange final
284289
// The accessor of the image.
285290
NeighborhoodAccessorFunctorType m_NeighborhoodAccessor;
286291

292+
TOptionalPixelAccessParameter m_OptionalPixelAccessParameter;
293+
287294
// The pixel coordinates of the location of the neighborhood.
288295
// May be outside the image!
289296
IndexType m_Location = { {} };
@@ -297,6 +304,7 @@ class ShapedImageNeighborhoodRange final
297304
const ImageSizeType& imageSize,
298305
const OffsetType& offsetTable,
299306
const NeighborhoodAccessorFunctorType& neighborhoodAccessor,
307+
const TOptionalPixelAccessParameter optionalPixelAccessParameter,
300308
const IndexType& location,
301309
const OffsetType* const offset) ITK_NOEXCEPT
302310
:
@@ -306,12 +314,29 @@ class ShapedImageNeighborhoodRange final
306314
m_ImageSize(imageSize),
307315
m_OffsetTable(offsetTable),
308316
m_NeighborhoodAccessor(neighborhoodAccessor),
317+
m_OptionalPixelAccessParameter(optionalPixelAccessParameter),
309318
m_Location(location),
310319
m_CurrentOffset{offset}
311320
{
312321
assert(m_ImageBufferPointer != nullptr);
313322
}
314323

324+
325+
TImageNeighborhoodPixelAccessPolicy CreatePixelAccessPolicy(EmptyPixelAccessParameter) const
326+
{
327+
return TImageNeighborhoodPixelAccessPolicy{ m_ImageSize, m_OffsetTable, m_NeighborhoodAccessor, m_Location + *m_CurrentOffset };
328+
}
329+
330+
template <typename TPixelAccessParameter>
331+
TImageNeighborhoodPixelAccessPolicy CreatePixelAccessPolicy(const TPixelAccessParameter pixelAccessParameter) const
332+
{
333+
static_assert(std::is_same< TPixelAccessParameter, TOptionalPixelAccessParameter>::value,
334+
"This helper function should only be used for TOptionalPixelAccessParameter!");
335+
static_assert(!std::is_same< TPixelAccessParameter, EmptyPixelAccessParameter>::value,
336+
"EmptyPixelAccessParameter indicates that there is no pixel access parameter specified!");
337+
return TImageNeighborhoodPixelAccessPolicy{ m_ImageSize, m_OffsetTable, m_NeighborhoodAccessor, m_Location + *m_CurrentOffset, pixelAccessParameter };
338+
}
339+
315340
public:
316341
// Types conforming the iterator requirements of the C++ standard library:
317342
using difference_type = std::ptrdiff_t;
@@ -347,7 +372,7 @@ class ShapedImageNeighborhoodRange final
347372
/** Returns a reference to the current pixel. */
348373
reference operator*() const ITK_NOEXCEPT
349374
{
350-
return reference{m_ImageBufferPointer, m_ImageSize, m_OffsetTable, m_NeighborhoodAccessor, m_Location + *m_CurrentOffset};
375+
return reference{m_ImageBufferPointer, CreatePixelAccessPolicy(m_OptionalPixelAccessParameter)};
351376
}
352377

353378

@@ -535,6 +560,8 @@ class ShapedImageNeighborhoodRange final
535560
// The number of neighborhood pixels.
536561
const std::size_t m_NumberOfNeighborhoodPixels;
537562

563+
const TOptionalPixelAccessParameter m_OptionalPixelAccessParameter;
564+
538565
public:
539566
using const_iterator = QualifiedIterator<true>;
540567
using iterator = QualifiedIterator<false>;
@@ -549,7 +576,8 @@ class ShapedImageNeighborhoodRange final
549576
ImageType& image,
550577
const IndexType& location,
551578
const OffsetType* const shapeOffsets,
552-
const std::size_t numberOfNeigborhoodPixels)
579+
const std::size_t numberOfNeigborhoodPixels,
580+
const TOptionalPixelAccessParameter optionalPixelAccessParameter = {})
553581
:
554582
m_ImageBufferPointer{image.ImageType::GetBufferPointer()},
555583
// Note: Use parentheses instead of curly braces to initialize data members,
@@ -559,7 +587,8 @@ class ShapedImageNeighborhoodRange final
559587
m_NeighborhoodAccessor(image.GetNeighborhoodAccessor()),
560588
m_Location(location),
561589
m_ShapeOffsets{ shapeOffsets },
562-
m_NumberOfNeighborhoodPixels{ numberOfNeigborhoodPixels }
590+
m_NumberOfNeighborhoodPixels{ numberOfNeigborhoodPixels },
591+
m_OptionalPixelAccessParameter(optionalPixelAccessParameter)
563592
{
564593
assert(m_ImageBufferPointer != nullptr);
565594
const OffsetValueType* const offsetTable = image.GetOffsetTable();
@@ -580,13 +609,17 @@ class ShapedImageNeighborhoodRange final
580609
ShapedImageNeighborhoodRange(
581610
ImageType& image,
582611
const IndexType& location,
583-
TContainerOfOffsets&& shapeOffsets)
612+
TContainerOfOffsets&& shapeOffsets,
613+
const TOptionalPixelAccessParameter optionalPixelAccessParameter = {})
584614
:
585-
ShapedImageNeighborhoodRange{
615+
ShapedImageNeighborhoodRange
616+
{
586617
image,
587618
location,
588619
shapeOffsets.data(),
589-
shapeOffsets.size()}
620+
shapeOffsets.size(),
621+
optionalPixelAccessParameter
622+
}
590623
{
591624
static_assert(!std::is_rvalue_reference<decltype(shapeOffsets)>::value,
592625
"The container of offsets should not be a temporary (rvalue) object!");
@@ -596,14 +629,14 @@ class ShapedImageNeighborhoodRange final
596629
iterator begin() const ITK_NOEXCEPT
597630
{
598631
assert(m_ImageBufferPointer != nullptr);
599-
return iterator(m_ImageBufferPointer, m_ImageSize, m_OffsetTable, m_NeighborhoodAccessor, m_Location, m_ShapeOffsets);
632+
return iterator(m_ImageBufferPointer, m_ImageSize, m_OffsetTable, m_NeighborhoodAccessor, m_OptionalPixelAccessParameter, m_Location, m_ShapeOffsets);
600633
}
601634

602635
/** Returns an 'end iterator' for this range. */
603636
iterator end() const ITK_NOEXCEPT
604637
{
605638
assert(m_ImageBufferPointer != nullptr);
606-
return iterator(m_ImageBufferPointer, m_ImageSize, m_OffsetTable, m_NeighborhoodAccessor, m_Location, m_ShapeOffsets + m_NumberOfNeighborhoodPixels);
639+
return iterator(m_ImageBufferPointer, m_ImageSize, m_OffsetTable, m_NeighborhoodAccessor, m_OptionalPixelAccessParameter, m_Location, m_ShapeOffsets + m_NumberOfNeighborhoodPixels);
607640
}
608641

609642
/** Returns a const iterator to the first neighborhood pixel.

Modules/Core/Common/test/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -627,6 +627,7 @@ set(ITKCommonGTests
627627
itkAggregateTypesTest.cxx
628628
itkBuildInformationTest.cxx
629629
itkConnectedImageNeighborhoodShapeGTest.cxx
630+
itkConstantBoundaryImageNeighborhoodPixelAccessPolicyGTest.cxx
630631
itkImageNeighborhoodOffsetsGTest.cxx
631632
itkShapedImageNeighborhoodRangeGTest.cxx
632633
itkSmartPointerGTest.cxx

0 commit comments

Comments
 (0)