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