ITK  5.0.0
Insight Segmentation and Registration Toolkit
itkConnectedImageNeighborhoodShape.h
Go to the documentation of this file.
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 itkConnectedImageNeighborhoodShape_h
20 #define itkConnectedImageNeighborhoodShape_h
21 
22 #include "itkOffset.h"
23 
24 #include <array>
25 #include <cassert>
26 #include <cstdint> // For uintmax_t
27 #include <limits>
28 
29 // C++11 does not guarantee that assert can be used in constexpr
30 // functions. This is a work-around for GCC 4.8, 4.9. Originating
31 // from Andrzej's C++ blog:
32 // https://akrzemi1.wordpress.com/2017/05/18/asserts-in-constexpr-functions/
33 #if defined NDEBUG
34 # define ITK_X_ASSERT(CHECK) void(0)
35 #else
36 # define ITK_X_ASSERT(CHECK) \
37  ( (CHECK) ? void(0) : []{assert(!#CHECK);}() )
38 #endif
39 
40 namespace itk
41 {
42 namespace Experimental
43 {
44 
98 template <unsigned int VImageDimension>
100 {
101 public:
102  static constexpr unsigned int ImageDimension = VImageDimension;
104 
117  const std::size_t maximumCityblockDistance,
118  const bool includeCenterPixel) ITK_NOEXCEPT
119  :
120  m_MaximumCityblockDistance{ maximumCityblockDistance },
121  m_IncludeCenterPixel{ includeCenterPixel },
122  m_NumberOfOffsets{ CalculateNumberOfOffsets(maximumCityblockDistance, includeCenterPixel) }
123  {
124  }
126 
127 
129  constexpr std::size_t GetNumberOfOffsets() const ITK_NOEXCEPT
130  {
131  return m_NumberOfOffsets;
132  }
133 
134 
137  Offset<ImageDimension>* const offsets) const ITK_NOEXCEPT
138  {
139  if (m_NumberOfOffsets > 0)
140  {
141  assert(offsets != nullptr);
142  Offset<ImageDimension> offset;
143  std::fill_n(offset.begin(), ImageDimension, -1);
145 
146  std::size_t i = 0;
147 
148  while ( i < m_NumberOfOffsets )
149  {
150  const std::size_t numberOfNonZeroOffsetValues = ImageDimension -
151  static_cast<std::size_t>(std::count(offset.begin(), offset.end(), 0));
152 
153  if ((m_IncludeCenterPixel || (numberOfNonZeroOffsetValues > 0)) &&
154  (numberOfNonZeroOffsetValues <= m_MaximumCityblockDistance))
155  {
156  offsets[i] = offset;
157  ++i;
158  }
159 
160  // Go to the next offset:
161  for (unsigned int direction = 0; direction < ImageDimension; ++direction)
162  {
163  auto& offsetValue = offset[direction];
164 
165  ++offsetValue;
166 
167  if (offsetValue <= 1)
168  {
169  break;
170  }
171  offsetValue = -1;
172  }
173  };
174  }
175  }
176 
177 private:
178  // The maximum city-block distance (Manhattan distance) between the center
179  // pixel and each connected neighbor pixel.
181 
182  // Specifies whether or not the center pixel (offset zero) should be included
183  // with the offsets for this shape.
185 
186  // The number of offsets needed to represent this shape.
187  std::size_t m_NumberOfOffsets;
188 
189 
190  // Calculates a + b. Numeric overflow triggers a compilation error in
191  // "constexpr context" and a debug assert failure at run-time.
192  static constexpr std::uintmax_t CalculateSum(
193  const std::uintmax_t a,
194  const std::uintmax_t b) ITK_NOEXCEPT
195  {
196  return ((a + b) >= a) && ((a + b) >= b) ? (a + b) :
197  (ITK_X_ASSERT(!"CalculateSum overflow!"), 0);
198  }
199 
200 
201  // Calculates a * b. Numeric overflow triggers a compilation error in
202  // "constexpr context" and a debug assert failure at run-time.
204  const std::uintmax_t a,
205  const std::uintmax_t b) ITK_NOEXCEPT
206  {
207  return (((a * b) / a) == b) && (((a * b) / b) == a) ? (a * b) :
208  (ITK_X_ASSERT(!"CalculateProduct overflow!"), 0);
209  }
210 
211 
212  // Calculates 2 ^ n. Numeric overflow triggers a compilation error in
213  // "constexpr context" and a debug assert failure at run-time.
215  const std::size_t n) ITK_NOEXCEPT
216  {
217  return (n < std::numeric_limits<std::uintmax_t>::digits) ?
218  (std::uintmax_t{ 1 } << n) :
219  (ITK_X_ASSERT(!"CalculatePowerOfTwo overflow!"), 0);
220  }
221 
222 
223  // Calculates 3 ^ n
225  const std::size_t n) ITK_NOEXCEPT
226  {
227  return (n == 0) ? 1 :
229  }
230 
231 
232  // Calculates the binomial coefficient, 'n' over 'k'.
233  // Inspired by the 'binom' function from Walter, June 23, 2017:
234  // https://stackoverflow.com/questions/44718971/calculate-binomial-coffeficient-very-reliable/44719165#44719165
235  // Optimized for small values of 'k' (k <= n/2).
237  const std::uintmax_t n,
238  const std::uintmax_t k) ITK_NOEXCEPT
239  {
240  return
241  (k > n) ? (ITK_X_ASSERT(!"Out of range!"), 0) :
242  (k == 0) ? 1 :
243  CalculateProduct(n, CalculateBinomialCoefficient(n - 1, k - 1)) / k;
244  }
245 
246 
247  // Calculates the number of m-dimensional hypercubes on the boundary of an
248  // n-cube, as described at https://en.wikipedia.org/wiki/Hypercube#Elements
249  // (Which refers to H.S.M. Coxeter, Regular polytopes, 3rd ed., 1973, p.120.)
250  static constexpr std::size_t CalculateNumberOfHypercubesOnBoundaryOfCube(
251  const std::size_t m,
252  const std::size_t n) ITK_NOEXCEPT
253  {
254  // Calculate 2^(n-m) * BinomialCoefficient(n, m)
255  return CalculateProduct(
256  CalculatePowerOfTwo(n - m),
257  (((2 * m) < n) ?
258  // Calculate either the binomial coefficient of (n, m) or (n, n - m).
259  // Mathematically, both should yield the same number, but the
260  // calculation is optimized for a smaller second argument.
262  CalculateBinomialCoefficient(n, n - m)));
263  }
264 
265 
266  // Iterates recursively from i = ImageDimension-1 down to m (inclusive).
268  const std::size_t i,
269  const std::size_t m) ITK_NOEXCEPT
270  {
271  return ITK_X_ASSERT(i >= m), CalculateSum(
273  ((i <= m)? 0 : CalculateSumOfNumberOfHypercubesOnBoundaryOfCube(i - 1, m)));
274  }
275 
276 
278  static constexpr std::size_t CalculateNumberOfConnectedNeighbors(
279  const std::size_t maximumCityblockDistance) ITK_NOEXCEPT
280  {
281  return (((maximumCityblockDistance == 0) || (ImageDimension == 0)) ? 0 :
282  ((maximumCityblockDistance >= ImageDimension) ? (CalculatePowerOfThree(ImageDimension) - 1) :
284  }
285 
286 
288  static constexpr std::size_t CalculateNumberOfOffsets(
289  const std::size_t maximumCityblockDistance,
290  const bool includeCenterPixel) ITK_NOEXCEPT
291  {
292  return (includeCenterPixel ? 1 : 0) +
293  CalculateNumberOfConnectedNeighbors(maximumCityblockDistance);
294  }
295 
296 
297  template <unsigned int VImageDimensionOfFriend, std::size_t VMaximumCityblockDistance, bool VIncludeCenterPixel>
298  friend
299  std::array<
301  ConnectedImageNeighborhoodShape<VImageDimensionOfFriend>::CalculateNumberOfOffsets(VMaximumCityblockDistance, VIncludeCenterPixel)>
303 };
304 
305 
307 template <unsigned int VImageDimension, std::size_t VMaximumCityblockDistance, bool VIncludeCenterPixel>
308 std::array<
309  Offset<VImageDimension>,
310  ConnectedImageNeighborhoodShape<VImageDimension>::CalculateNumberOfOffsets(VMaximumCityblockDistance, VIncludeCenterPixel)>
312 {
313  constexpr ConnectedImageNeighborhoodShape<VImageDimension> shape{ VMaximumCityblockDistance, VIncludeCenterPixel };
314  std::array<Offset<VImageDimension>, shape.GetNumberOfOffsets()> offsets;
315  shape.FillOffsets(offsets.data());
316  return offsets;
317 }
319 
320 } // namespace Experimental
321 } // namespace itk
322 
323 #undef ITK_X_ASSERT
324 
325 #endif
#define ITK_X_ASSERT(CHECK)
static constexpr std::vcl_size_t CalculateSumOfNumberOfHypercubesOnBoundaryOfCube(const std::vcl_size_t i, const std::vcl_size_t m) noexcept
static constexpr std::uintmax_t CalculateBinomialCoefficient(const std::uintmax_t n, const std::uintmax_t k) noexcept
static constexpr std::uintmax_t CalculateSum(const std::uintmax_t a, const std::uintmax_t b) noexcept
iterator end()
Definition: itkOffset.h:304
static constexpr std::vcl_size_t CalculateNumberOfHypercubesOnBoundaryOfCube(const std::vcl_size_t m, const std::vcl_size_t n) noexcept
static constexpr std::uintmax_t CalculatePowerOfThree(const std::vcl_size_t n) noexcept
static constexpr std::vcl_size_t CalculateNumberOfOffsets(const std::vcl_size_t maximumCityblockDistance, const bool includeCenterPixel) noexcept
static constexpr std::uintmax_t CalculatePowerOfTwo(const std::vcl_size_t n) noexcept
static constexpr std::vcl_size_t CalculateNumberOfConnectedNeighbors(const std::vcl_size_t maximumCityblockDistance) noexcept
::uintmax_t uintmax_t
Definition: itkIntTypes.h:56
void FillOffsets(Offset< ImageDimension > *const offsets) const noexcept
iterator begin()
Definition: itkOffset.h:294
friend std::array< Offset< VImageDimensionOfFriend >, ConnectedImageNeighborhoodShape< VImageDimensionOfFriend >::CalculateNumberOfOffsets(VMaximumCityblockDistance, VIncludeCenterPixel)> GenerateConnectedImageNeighborhoodShapeOffsets() noexcept
constexpr ConnectedImageNeighborhoodShape(const std::vcl_size_t maximumCityblockDistance, const bool includeCenterPixel) noexcept
static constexpr std::uintmax_t CalculateProduct(const std::uintmax_t a, const std::uintmax_t b) noexcept