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  *
20  *  Portions of this file are subject to the VTK Toolkit Version 3 copyright.
21  *
22  *  Copyright (c) Ken Martin, Will Schroeder, Bill Lorensen
23  *
24  *  For complete copyright, license and disclaimer of warranty information
25  *  please refer to the NOTICE file at the top of the ITK source tree.
26  *
27  *=========================================================================*/
28 #ifndef itkImageRegion_h
29 #define itkImageRegion_h
30 
31 #include "itkRegion.h"
32 
33 #include "itkSize.h"
34 #include "itkContinuousIndex.h"
35 #include "itkMath.h"
36 
37 namespace itk
38 {
39 // Forward declaration of ImageBase so it can be declared a friend
40 // (needed for PrintSelf mechanism)
41 template< unsigned int VImageDimension >
42 class ITK_TEMPLATE_EXPORT ImageBase;
43 
44 /** \class ImageRegion
45  * \brief An image region represents a structured region of data.
46  *
47  * ImageRegion is an class that represents some structured portion or
48  * piece of an Image. The ImageRegion is represented with an index and
49  * a size in each of the n-dimensions of the image. (The index is the
50  * corner of the image, the size is the lengths of the image in each of
51  * the topological directions.)
52  *
53  * \sa Region
54  * \sa Index
55  * \sa Size
56  * \sa MeshRegion
57  *
58  * \ingroup ImageObjects
59  * \ingroup ITKCommon
60  *
61  * \wiki
62  * \wikiexample{Images/ImageRegion,An object which holds the index (start) and size of a region of an image}
63  * \wikiexample{SimpleOperations/RegionIntersection,Determine if one region is fully inside another region}
64  * \wikiexample{SimpleOperations/PixelInsideRegion,Determine if a pixel is inside of a region}
65  * \wikiexample{SimpleOperations/RegionOverlap,Determine the overlap of two regions}
66  * \endwiki
67  */
68 template< unsigned int VImageDimension >
69 class ITK_TEMPLATE_EXPORT ImageRegion final: public Region
70 {
71 public:
72   /** Standard class type aliases. */
73   using Self = ImageRegion;
74   using Superclass = Region;
75 
76   /** Standard part of all itk objects. */
77   itkTypeMacro(ImageRegion, Region);
78 
79   /** Dimension of the image available at compile time. */
80   static constexpr unsigned int ImageDimension = VImageDimension;
81 
82   /** Dimension one lower than the image unless the image is one dimensional
83       in which case the SliceDimension is also one dimensional. */
84   static constexpr unsigned int SliceDimension = ImageDimension - ( ImageDimension > 1 );
85 
86   /** Dimension of the image available at run time. */
GetImageDimension()87   static unsigned int GetImageDimension()
88   { return ImageDimension; }
89 
90   /** Index type alias support An index is used to access pixel values. */
91   using IndexType = Index< Self::ImageDimension >;
92   using IndexValueType = typename IndexType::IndexValueType;
93   using OffsetType = typename IndexType::OffsetType;
94   using OffsetValueType = typename OffsetType::OffsetValueType;
95   typedef IndexValueType  IndexValueArrayType[ImageDimension];
96   typedef OffsetValueType OffsetTableType[ImageDimension+1];
97 
98   /** Size type alias support A size is used to define region bounds. */
99   using SizeType = Size< Self::ImageDimension >;
100   using SizeValueType = typename SizeType::SizeValueType;
101 
102   /** Slice region type alias. SliceRegion is one dimension less than Self. */
103   using SliceRegion = ImageRegion< Self::SliceDimension >;
104 
105   /** Return the region type. Images are described with structured regions. */
GetRegionType()106   typename Superclass::RegionType GetRegionType() const override
107   { return Superclass::ITK_STRUCTURED_REGION; }
108 
109   /** Constructor. ImageRegion is a lightweight object that is not reference
110    * counted, so the constructor is public. Its two data members are filled
111    * with zeros (using C++11 default member initializers). */
112   ImageRegion() ITK_NOEXCEPT = default;
113 
114   /** Destructor. ImageRegion is a lightweight object that is not reference
115    * counted, so the destructor is public. */
116   ~ImageRegion() override = default;
117 
118   /** Copy constructor. ImageRegion is a lightweight object that is not
119    * reference counted, so the copy constructor is public. */
120   ImageRegion(const Self & ) ITK_NOEXCEPT = default;
121 
122   /** Constructor that takes an index and size. ImageRegion is a lightweight
123    * object that is not reference counted, so this constructor is public. */
ImageRegion(const IndexType & index,const SizeType & size)124   ImageRegion(const IndexType & index, const SizeType & size) ITK_NOEXCEPT
125     :
126   // Note: Use parentheses instead of curly braces to initialize data members,
127   // to avoid AppleClang 6.0.0.6000056 compile errors, "no viable conversion..."
128   m_Index(index),
129   m_Size(size)
130   {
131   }
132 
133   /** Constructor that takes a size and assumes an index of zeros. ImageRegion
134    * is lightweight object that is not reference counted so this constructor
135    * is public. */
ImageRegion(const SizeType & size)136   ImageRegion(const SizeType & size) ITK_NOEXCEPT
137     :
138   m_Size(size)
139   {
140     // Note: m_Index is initialized by its C++11 default member initializer.
141   }
142 
143   /** operator=. ImageRegion is a lightweight object that is not reference
144    * counted, so operator= is public. */
145   Self& operator=(const Self & ) ITK_NOEXCEPT = default;
146 
147   /** Set the index defining the corner of the region. */
SetIndex(const IndexType & index)148   void SetIndex(const IndexType & index)
149   { m_Index = index; }
150 
151   /** Get index defining the corner of the region. */
GetIndex()152   const IndexType & GetIndex() const { return m_Index; }
GetModifiableIndex()153   IndexType & GetModifiableIndex() { return m_Index; }
154 
155   /** Set the size of the region. This plus the index determines the
156    * rectangular shape, or extent, of the region. */
SetSize(const SizeType & size)157   void SetSize(const SizeType & size)
158   { m_Size = size; }
159 
160   /** Get the size of the region. */
GetSize()161   const SizeType & GetSize() const { return m_Size; }
GetModifiableSize()162   SizeType & GetModifiableSize() { return m_Size; }
163 
164   /** Convenience methods to get and set the size of the particular dimension i.
165     */
SetSize(unsigned int i,SizeValueType sze)166   void SetSize(unsigned int i, SizeValueType sze)
167   { m_Size[i] = sze; }
GetSize(unsigned int i)168   SizeValueType GetSize(unsigned int i) const
169   { return m_Size[i]; }
170 
171   /** Convenience methods to get and set the index of the particular dimension
172     i. */
SetIndex(unsigned int i,IndexValueType sze)173   void SetIndex(unsigned int i, IndexValueType sze)
174   { m_Index[i] = sze; }
GetIndex(unsigned int i)175   IndexValueType GetIndex(unsigned int i) const
176   { return m_Index[i]; }
177 
178   /** Get index defining the upper corner of the region. */
179   IndexType GetUpperIndex() const;
180 
181   /** Modify the Size of the ImageRegion so that the provided index will be the upper corner index. */
182   void SetUpperIndex( const IndexType & idx );
183 
184   /** Compute an offset table based on the Size. */
185   void ComputeOffsetTable(OffsetTableType offsetTable) const;
186 
187   /** Compare two regions. */
188   bool
189   operator==(const Self & region) const ITK_NOEXCEPT
190   {
191     return (m_Index == region.m_Index) && (m_Size == region.m_Size);
192   }
193 
194   /** Compare two regions. */
195   bool
196   operator!=(const Self & region) const ITK_NOEXCEPT
197   {
198     return !(*this == region);
199   }
200 
201   /** Test if an index is inside */
202   bool
IsInside(const IndexType & index)203   IsInside(const IndexType & index) const
204   {
205     for ( unsigned int i = 0; i < ImageDimension; i++ )
206       {
207       if ( index[i] < m_Index[i] )
208         {
209         return false;
210         }
211       if ( index[i] >= ( m_Index[i] + static_cast< IndexValueType >( m_Size[i] ) ) )
212         {
213         return false;
214         }
215       }
216     return true;
217   }
218 
219   /** Test if a continuous index is inside the region.
220    * We take into account the fact that each voxel has its
221    * center at the integer coordinate and extends half way
222    * to the next integer coordinate. */
223   template< typename TCoordRepType >
224   bool
IsInside(const ContinuousIndex<TCoordRepType,VImageDimension> & index)225   IsInside(const ContinuousIndex< TCoordRepType, VImageDimension > & index) const
226   {
227     for ( unsigned int i = 0; i < ImageDimension; i++ )
228       {
229       if ( Math::RoundHalfIntegerUp< IndexValueType >(index[i]) < static_cast< IndexValueType >( m_Index[i] ) )
230         {
231         return false;
232         }
233       // bound is the last valid pixel location
234       const auto bound = static_cast< TCoordRepType >(
235         m_Index[i] + m_Size[i] - 0.5 );
236 
237       /* Note for NaN: test using negation of a positive test in order
238        * to always evaluate to true (and thus return false) when index[i]
239        * is NaN. The cast above to integer via RoundHalfIntegerUp will cast
240        * NaN into a platform-dependent value (large negative, -1 or large
241        * positive, empirically). Thus this test here is relied on
242        * to 'catch' NaN's. */
243       if ( ! (index[i] <= bound) )
244         {
245         return false;
246         }
247       }
248     return true;
249   }
250 
251   /** Test if a region (the argument) is completely inside of this region. If
252    * the region that is passed as argument to this method, has a size of value
253    * zero, then it will not be considered to be inside of the current region,
254    * even its starting index is inside. */
255   bool
IsInside(const Self & region)256   IsInside(const Self & region) const
257   {
258     IndexType beginCorner = region.GetIndex();
259 
260     if ( !this->IsInside(beginCorner) )
261       {
262       return false;
263       }
264     IndexType endCorner;
265     const SizeType & size = region.GetSize();
266     for ( unsigned int i = 0; i < ImageDimension; i++ )
267       {
268       endCorner[i] = beginCorner[i] + static_cast< OffsetValueType >( size[i] ) - 1;
269       }
270     if ( !this->IsInside(endCorner) )
271       {
272       return false;
273       }
274     return true;
275   }
276 
277   /** Get the number of pixels contained in this region. This just
278    * multiplies the size components. */
279   SizeValueType GetNumberOfPixels() const;
280 
281   /** Pad an image region by the specified radius. Region can be padded
282    * uniformly in all dimensions or can be padded by different amounts
283    * in each dimension. */
284   void PadByRadius(OffsetValueType radius);
285 
286   void PadByRadius(const IndexValueArrayType radius);
287 
288   void PadByRadius(const SizeType & radius);
289 
290   /** Shrink an image region by the specified radius.  The region can be shrunk
291    * uniformly in all dimension or can be shink by different amounts in each
292    * direction.  If the shink operation fails because the radius is too large,
293    * this method returns false. */
294   bool ShrinkByRadius(OffsetValueType radius);
295 
296   bool ShrinkByRadius(const IndexValueArrayType radius);
297 
298   bool ShrinkByRadius(const SizeType & radius);
299 
300   /** Crop a region by another region. If this region is outside of the
301    * crop, this method returns false and does not modify the
302    * region. Otherwise, this method returns true and the region is
303    * modified to reflect the crop. */
304   bool Crop(const Self & region);
305 
306   /** Slice a region, producing a region that is one dimension lower
307    * than the current region. Parameter "dim" specifies which dimension
308    * to remove. */
309   SliceRegion Slice(const unsigned int dim) const;
310 
311 protected:
312   /** Methods invoked by Print() to print information about the object
313    * including superclasses. Typically not called by the user (use Print()
314    * instead) but used in the hierarchical print process to combine the
315    * output of several classes.  */
316   void PrintSelf(std::ostream & os, Indent indent) const override;
317 
318 private:
319   IndexType m_Index = {{0}};
320   SizeType  m_Size = {{0}};
321 
322   /** Friends of ImageRegion */
323   friend class ImageBase< VImageDimension >;
324 };
325 
326 template< unsigned int VImageDimension >
327 std::ostream & operator<<(std::ostream & os, const ImageRegion< VImageDimension > & region);
328 } // end namespace itk
329 
330 #ifndef ITK_MANUAL_INSTANTIATION
331 #include "itkImageRegion.hxx"
332 #endif
333 
334 #endif
335