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 #ifndef itkImageMaskSpatialObject_hxx
19 #define itkImageMaskSpatialObject_hxx
20
21 #include "itkMath.h"
22 #include "itkImageMaskSpatialObject.h"
23 #include "itkImageRegionConstIterator.h"
24 #include "itkImageRegionConstIteratorWithIndex.h"
25
26 #include <cstdint> // For uintmax_t.
27
28 namespace itk
29 {
30 /** Constructor */
31 template< unsigned int TDimension, typename TPixel >
32 ImageMaskSpatialObject< TDimension, TPixel >
ImageMaskSpatialObject()33 ::ImageMaskSpatialObject()
34 {
35 this->SetTypeName("ImageMaskSpatialObject");
36 }
37
38 /** Test whether a point is inside or outside the object
39 * For computational speed purposes, it is faster if the method does not
40 * check the name of the class and the current depth */
41 template< unsigned int TDimension, typename TPixel >
42 bool
43 ImageMaskSpatialObject< TDimension, TPixel >
IsInsideInObjectSpace(const PointType & point) const44 ::IsInsideInObjectSpace(const PointType & point) const
45 {
46 typename Superclass::InterpolatorType::ContinuousIndexType index;
47 if( this->GetImage()->TransformPhysicalPointToContinuousIndex( point,
48 index ) )
49 {
50 using InterpolatorOutputType = typename InterpolatorType::OutputType;
51 bool insideMask = (
52 Math::NotExactlyEquals(
53 DefaultConvertPixelTraits<InterpolatorOutputType>::GetScalarValue(
54 this->GetInterpolator()->EvaluateAtContinuousIndex(index)),
55 NumericTraits<PixelType>::ZeroValue() ) );
56 if( insideMask )
57 {
58 return true;
59 }
60 }
61
62 return false;
63 }
64
65
66 template< unsigned int TDimension, typename TPixel >
67 void
68 ImageMaskSpatialObject< TDimension, TPixel >
ComputeMyBoundingBox()69 ::ComputeMyBoundingBox()
70 {
71 const ImageType* const image = this->GetImage();
72 itkAssertOrThrowMacro(image != nullptr, "Ensure that SetImage has been called!");
73
74 const RegionType boundingBoxInIndexSpace{ this->ComputeMyBoundingBoxInIndexSpace() };
75
76 BoundingBoxType* const boundingBoxInObjectSpace = this->GetModifiableMyBoundingBoxInObjectSpace();
77
78 // Assert should never fail as SpatialObject takes care of creating the BoundingBox.
79 assert(boundingBoxInObjectSpace != nullptr);
80
81 if (boundingBoxInIndexSpace.GetNumberOfPixels() == 0)
82 {
83 boundingBoxInObjectSpace->SetMinimum(PointType());
84 boundingBoxInObjectSpace->SetMaximum(PointType());
85 }
86 else
87 {
88 using ContinuousIndexType = typename Superclass::ContinuousIndexType;
89 using VectorType = typename SpatialObject<TDimension>::VectorType;
90
91 const IndexType minIndex = boundingBoxInIndexSpace.GetIndex();
92
93 ContinuousIndexType minContinuousIndex{ minIndex };
94 ContinuousIndexType maxContinuousIndex{ minIndex + boundingBoxInIndexSpace.GetSize() };
95
96 // Allow a margin of half a pixel in each direction.
97 minContinuousIndex -= VectorType{ 0.5 };
98 maxContinuousIndex -= VectorType{ 0.5 };
99
100 // Initially set the corner point corresponding to the minimum index as
101 // both the minimum and maximum of the bounding box (in object space).
102 // Afterwards, all other corners are considered.
103 PointType firstPoint;
104 image->TransformContinuousIndexToPhysicalPoint(minContinuousIndex, firstPoint);
105 boundingBoxInObjectSpace->SetMinimum(firstPoint);
106 boundingBoxInObjectSpace->SetMaximum(firstPoint);
107
108 // The total number of corner points of the bounding box.
109 constexpr auto numberOfCorners = std::uintmax_t{ 1 } << TDimension;
110
111 for (std::uintmax_t cornerNumber{1}; cornerNumber < numberOfCorners; ++cornerNumber)
112 {
113 // For each corner, estimate the n-dimensional index.
114
115 auto continuousIndex = minContinuousIndex;
116
117 for (unsigned dim{}; dim < TDimension; ++dim)
118 {
119 const std::uintmax_t bitMask{ std::uintmax_t{ 1 } << dim };
120
121 if ((cornerNumber & bitMask) != 0)
122 {
123 continuousIndex[dim] = maxContinuousIndex[dim];
124 }
125 }
126
127 // Consider the corner point that corresponds to this n-dimensional index.
128 PointType cornerPoint;
129 image->TransformContinuousIndexToPhysicalPoint(continuousIndex, cornerPoint);
130 boundingBoxInObjectSpace->ConsiderPoint(cornerPoint);
131 }
132 }
133 }
134
135
136 /** InternalClone */
137 template< unsigned int TDimension, typename TPixel >
138 typename LightObject::Pointer
139 ImageMaskSpatialObject< TDimension, TPixel >
InternalClone() const140 ::InternalClone() const
141 {
142 // Default implementation just copies the parameters from
143 // this to new transform.
144 typename LightObject::Pointer loPtr = Superclass::InternalClone();
145
146 typename Self::Pointer rval =
147 dynamic_cast<Self *>(loPtr.GetPointer());
148 if(rval.IsNull())
149 {
150 itkExceptionMacro(<< "downcast to type "
151 << this->GetNameOfClass()
152 << " failed.");
153 }
154
155 return loPtr;
156 }
157
158 /** Print the object */
159 template< unsigned int TDimension, typename TPixel >
160 void
161 ImageMaskSpatialObject< TDimension, TPixel >
PrintSelf(std::ostream & os,Indent indent) const162 ::PrintSelf(std::ostream & os, Indent indent) const
163 {
164 Superclass::PrintSelf(os, indent);
165 }
166
167
168 template< unsigned int TDimension, typename TPixel >
169 typename ImageMaskSpatialObject< TDimension, TPixel >::RegionType
170 ImageMaskSpatialObject< TDimension, TPixel >
ComputeMyBoundingBoxInIndexSpace() const171 ::ComputeMyBoundingBoxInIndexSpace() const
172 {
173 const ImagePointer imagePointer = this->GetImage();
174
175 if (imagePointer == nullptr)
176 {
177 return {};
178 }
179
180 const ImageType& image = *imagePointer;
181
182 const auto HasForegroundPixels = [&image](const RegionType& region)
183 {
184 for (ImageRegionConstIterator<ImageType> it{ &image, region }; !it.IsAtEnd(); ++it)
185 {
186 constexpr auto zeroValue = NumericTraits<PixelType>::ZeroValue();
187
188 if (it.Get() != zeroValue)
189 {
190 return true;
191 }
192 }
193 return false;
194 };
195
196 const auto CreateRegion = [](const IndexType& minIndex, const IndexType& maxIndex)
197 {
198 SizeType regionSize;
199
200 for (unsigned dim = 0; dim < SizeType::Dimension; ++dim)
201 {
202 regionSize[dim] = static_cast<SizeValueType>(maxIndex[dim] + 1 - minIndex[dim]);
203 }
204 return RegionType{ minIndex, regionSize };
205 };
206
207 const RegionType requestedRegion = image.GetRequestedRegion();
208
209 if (requestedRegion.GetNumberOfPixels() == 0)
210 {
211 return {};
212 }
213
214 const SizeType imageSize = requestedRegion.GetSize();
215
216 IndexType minIndex = requestedRegion.GetIndex();
217 IndexType maxIndex = minIndex + imageSize;
218
219 for (auto& maxIndexValue : maxIndex)
220 {
221 --maxIndexValue;
222 }
223
224 // Iterate from high to low (for significant performance reasons).
225 for (int dim = TDimension - 1; dim >= 0; --dim)
226 {
227 auto subregion = CreateRegion(minIndex, maxIndex);
228 subregion.SetSize(dim, 1);
229 const auto initialMaxIndexValue = maxIndex[dim];
230
231 // Estimate minIndex[dim]
232 while (!HasForegroundPixels(subregion))
233 {
234 const auto indexValue = subregion.GetIndex(dim) + 1;
235
236 if (indexValue > initialMaxIndexValue)
237 {
238 // The requested image region has only zero-valued pixels.
239 return {};
240 }
241 subregion.SetIndex(dim, indexValue);
242 }
243 minIndex[dim] = subregion.GetIndex(dim);
244
245 // Estimate maxIndex[dim]
246 subregion.SetIndex(dim, initialMaxIndexValue);
247 while (!HasForegroundPixels(subregion))
248 {
249 subregion.SetIndex(dim, subregion.GetIndex(dim) - 1);
250 }
251 maxIndex[dim] = subregion.GetIndex(dim);
252 }
253 return CreateRegion(minIndex, maxIndex);
254 }
255
256
257 #if ! defined ( ITK_LEGACY_REMOVE )
258 template< unsigned int TDimension, typename TPixel >
259 typename ImageMaskSpatialObject< TDimension, TPixel >::RegionType
260 ImageMaskSpatialObject< TDimension, TPixel >
GetAxisAlignedBoundingBoxRegion() const261 ::GetAxisAlignedBoundingBoxRegion() const
262 {
263 return ComputeMyBoundingBoxInIndexSpace();
264 }
265 #endif //ITK_LEGACY_REMOVE
266 } // end namespace itk
267
268 #endif //__ImageMaskSpatialObject_hxx
269