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 itkRectangularImageNeighborhoodShape_h
20 #define itkRectangularImageNeighborhoodShape_h
21 
22 #include <algorithm> // For transform.
23 #include <cassert>
24 
25 #include "itkOffset.h"
26 #include "itkSize.h"
27 
28 namespace itk
29 {
30 namespace Experimental
31 {
32 
33 /**
34  * \class RectangularImageNeighborhoodShape
35  * Rectangular (or hyperrectangular) image-neighborhood shape.
36  * Eases creating a sequence of offsets for ShapedImageNeighborhoodRange.
37  * Can also be used for ShapedNeighborhoodIterator.
38  *
39  * The following example creates a 3 x 5 rectangular neighborhood around
40  * pixel location [10, 20], and generates the offsets for a neighborhood range:
41  \code
42   const Index<> location = { 10, 20 };
43   const Size<> radius = { { 1, 2 } };
44   const RectangularImageNeighborhoodShape<2> shape{ radius };
45   const std::vector<Offset<>> offsets = GenerateImageNeighborhoodOffsets(shape);
46   ShapedImageNeighborhoodRange<ImageType> neighborhoodRange{ *image, location, offsets };
47  \endcode
48  *
49  * \see ShapedNeighborhoodIterator
50  * \see ShapedImageNeighborhoodRange
51  * \ingroup ImageIterators
52  * \ingroup ITKCommon
53  */
54 template <unsigned int VImageDimension>
55 class RectangularImageNeighborhoodShape
56 {
57 public:
58   static constexpr unsigned int ImageDimension = VImageDimension;
59 
60   /** Constructs a hyperrectangular shape whose size is specified by the radius */
RectangularImageNeighborhoodShape(const Size<ImageDimension> & radius)61   constexpr explicit RectangularImageNeighborhoodShape(
62     const Size<ImageDimension>& radius) ITK_NOEXCEPT
63   :
64   m_Radius(radius),
65   m_NumberOfOffsets(CalculateNumberOfOffsets(ImageDimension))
66   {
67   }
68 
69 
70   /** Returns the number of offsets needed to represent this shape. */
GetNumberOfOffsets()71   constexpr std::size_t GetNumberOfOffsets() const ITK_NOEXCEPT
72   {
73     return m_NumberOfOffsets;
74   }
75 
76 
77   /** Fills the specified buffer with the offsets for a neighborhood of this shape. */
FillOffsets(Offset<ImageDimension> * const offsets)78   void FillOffsets(Offset<ImageDimension>* const offsets) const ITK_NOEXCEPT
79   {
80     if (m_NumberOfOffsets > 0)
81     {
82       assert(offsets != nullptr);
83       Offset<ImageDimension> offset;
84 
85       std::transform(m_Radius.begin(), m_Radius.end(), offset.begin(), [](const SizeValueType radiusValue)
86       {
87         return -static_cast<OffsetValueType>(radiusValue);
88       });
89 
90       for (std::size_t i = 0; i < m_NumberOfOffsets; ++i)
91       {
92         offsets[i] = offset;
93 
94         for (unsigned dimensionIndex = 0; dimensionIndex < ImageDimension; ++dimensionIndex)
95         {
96           OffsetValueType& offsetValue = offset[dimensionIndex];
97 
98           ++offsetValue;
99 
100           if (offsetValue <= static_cast<OffsetValueType>(m_Radius[dimensionIndex]))
101           {
102             break;
103           }
104           offsetValue = -static_cast<OffsetValueType>(m_Radius[dimensionIndex]);
105         }
106       }
107     }
108   }
109 
110 private:
111   // The radius of the neighborhood along each direction.
112   Size<ImageDimension> m_Radius;
113 
114   // The number of offsets needed to represent this shape.
115   std::size_t m_NumberOfOffsets;
116 
117 
118   // Private helper function to calculate the number of Offsets by a recursive
119   // function call. Recursion is necessary for C++11 constexpr.
CalculateNumberOfOffsets(const unsigned dimension)120   constexpr std::size_t CalculateNumberOfOffsets(const unsigned dimension) const ITK_NOEXCEPT
121   {
122     return (dimension == 0) ? 1 :
123       (2 * m_Radius.m_InternalArray[dimension - 1] + 1) * CalculateNumberOfOffsets(dimension - 1);
124   }
125 
126 };
127 
128 } // namespace Experimental
129 } // namespace itk
130 
131 #endif
132