/*========================================================================= * * Copyright NumFOCUS * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * https://www.apache.org/licenses/LICENSE-2.0.txt * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * *=========================================================================*/ /*========================================================================= * * Portions of this file are subject to the VTK Toolkit Version 3 copyright. * * Copyright (c) Ken Martin, Will Schroeder, Bill Lorensen * * For complete copyright, license and disclaimer of warranty information * please refer to the NOTICE file at the top of the ITK source tree. * *=========================================================================*/ #ifndef itkImageRegion_h #define itkImageRegion_h #include "itkRegion.h" #include "itkSize.h" #include "itkContinuousIndex.h" #include "itkMath.h" #include // For conditional and integral_constant. #include // For tuple_element and tuple_size. // Macro added to each `ImageRegion` member function that overrides a virtual member function of `Region`. In the // future, `ImageRegion` will no longer inherit from `Region`, so then those `ImageRegion` member functions will no // longer override. #ifdef ITK_FUTURE_LEGACY_REMOVE # define itkRegionOverrideMacro // nothing (in the future) #else # define itkRegionOverrideMacro override #endif namespace itk { // Forward declaration of ImageBase so it can be declared a friend // (needed for PrintSelf mechanism) template class ITK_TEMPLATE_EXPORT ImageBase; /** \class ImageRegion * \brief An image region represents a structured region of data. * * ImageRegion is an class that represents some structured portion or * piece of an Image. The ImageRegion is represented with an index and * a size in each of the n-dimensions of the image. (The index is the * corner of the image, the size is the lengths of the image in each of * the topological directions.) * * \sa Region * \sa Index * \sa Size * \sa MeshRegion * * \ingroup ImageObjects * \ingroup ITKCommon * * \sphinx * \sphinxexample{Core/Common/CreateAnImageRegion,An object which holds the index (start) and size of a region of an * image} \sphinxexample{Core/Common/ImageRegionIntersection,Determine image region intersection} * \sphinxexample{Core/Common/IsPixelInsideRegion,Determine if a pixel is inside of a region} * \sphinxexample{Core/Common/ImageRegionOverlap,Determine the overlap of two regions} * \endsphinx */ template class ITK_TEMPLATE_EXPORT ImageRegion final #ifndef ITK_FUTURE_LEGACY_REMOVE // This inheritance is to be removed in the future. : public Region #endif { public: /** Standard class type aliases. */ using Self = ImageRegion; #ifndef ITK_FUTURE_LEGACY_REMOVE using Superclass = Region; #endif /** Standard part of all itk objects. */ const char * GetNameOfClass() const itkRegionOverrideMacro { return "ImageRegion"; } /** Dimension of the image available at compile time. */ static constexpr unsigned int ImageDimension = VImageDimension; /** Dimension one lower than the image unless the image is one dimensional in which case the SliceDimension is also one dimensional. */ static constexpr unsigned int SliceDimension = ImageDimension - (ImageDimension > 1); /** Dimension of the image available at run time. */ static unsigned int GetImageDimension() { return ImageDimension; } /** Index type alias support An index is used to access pixel values. */ using IndexType = Index; using IndexValueType = typename IndexType::IndexValueType; using OffsetType = typename IndexType::OffsetType; using OffsetValueType = typename OffsetType::OffsetValueType; using IndexValueArrayType = IndexValueType[ImageDimension]; using OffsetTableType = OffsetValueType[ImageDimension + 1]; /** Size type alias support A size is used to define region bounds. */ using SizeType = Size; using SizeValueType = typename SizeType::SizeValueType; /** Slice region type alias. SliceRegion is one dimension less than Self. */ using SliceRegion = ImageRegion; /** Return the region type. Images are described with structured regions. */ Region::RegionEnum GetRegionType() const itkRegionOverrideMacro { return Region::RegionEnum::ITK_STRUCTURED_REGION; } /** Print the region. */ void Print(std::ostream & os, Indent indent = 0) const itkRegionOverrideMacro; /** Constructor. ImageRegion is a lightweight object that is not reference * counted, so the constructor is public. Its two data members are filled * with zeros (using C++11 default member initializers). */ ImageRegion() noexcept = default; /** Destructor. ImageRegion is a lightweight object that is not reference * counted, so the destructor is public. */ ~ImageRegion() itkRegionOverrideMacro = default; /** Copy constructor. ImageRegion is a lightweight object that is not * reference counted, so the copy constructor is public. */ ImageRegion(const Self &) noexcept = default; /** Constructor that takes an index and size. ImageRegion is a lightweight * object that is not reference counted, so this constructor is public. * \note This constructor supports class template argument deduction (CTAD). */ ImageRegion(const IndexType & index, const SizeType & size) noexcept : // Note: Use parentheses instead of curly braces to initialize data members, // to avoid AppleClang 6.0.0.6000056 compile errors, "no viable conversion..." m_Index(index) , m_Size(size) {} /** Constructor that takes a size and assumes an index of zeros. ImageRegion * is lightweight object that is not reference counted so this constructor * is public. * \note This constructor supports class template argument deduction (CTAD). */ ImageRegion(const SizeType & size) noexcept : m_Size(size) { // Note: m_Index is initialized by its C++11 default member initializer. } /** operator=. ImageRegion is a lightweight object that is not reference * counted, so operator= is public. */ Self & operator=(const Self &) noexcept = default; /** Set the index defining the corner of the region. */ void SetIndex(const IndexType & index) { m_Index = index; } /** Get index defining the corner of the region. */ const IndexType & GetIndex() const { return m_Index; } IndexType & GetModifiableIndex() { return m_Index; } /** Set the size of the region. This plus the index determines the * rectangular shape, or extent, of the region. */ void SetSize(const SizeType & size) { m_Size = size; } /** Get the size of the region. */ const SizeType & GetSize() const { return m_Size; } SizeType & GetModifiableSize() { return m_Size; } /** Convenience methods to get and set the size of the particular dimension i. */ void SetSize(unsigned int i, SizeValueType sze) { m_Size[i] = sze; } SizeValueType GetSize(unsigned int i) const { return m_Size[i]; } /** Convenience methods to get and set the index of the particular dimension i. */ void SetIndex(unsigned int i, IndexValueType sze) { m_Index[i] = sze; } IndexValueType GetIndex(unsigned int i) const { return m_Index[i]; } /** Get index defining the upper corner of the region. */ IndexType GetUpperIndex() const; /** Modify the Size of the ImageRegion so that the provided index will be the upper corner index. */ void SetUpperIndex(const IndexType & idx); /** Compute an offset table based on the Size. */ void ComputeOffsetTable(OffsetTableType offsetTable) const; /** Compare two regions. */ bool operator==(const Self & region) const noexcept { return (m_Index == region.m_Index) && (m_Size == region.m_Size); } ITK_UNEQUAL_OPERATOR_MEMBER_FUNCTION(Self); /** Test if an index is inside */ bool IsInside(const IndexType & index) const { for (unsigned int i = 0; i < ImageDimension; ++i) { if (index[i] < m_Index[i] || index[i] >= m_Index[i] + static_cast(m_Size[i])) { return false; } } return true; } /** Test if a continuous index is inside the region. * We take into account the fact that each voxel has its * center at the integer coordinate and extends half way * to the next integer coordinate, inclusive on all sides. */ template bool IsInside(const ContinuousIndex & index) const { constexpr TCoordRepType half = 0.5; for (unsigned int i = 0; i < ImageDimension; ++i) { // Use negation of tests so that index[i]==NaN leads to returning false. if (!(index[i] >= m_Index[i] - half && index[i] <= (m_Index[i] + static_cast(m_Size[i])) - half)) { return false; } } return true; } /** Test if a region (the argument) is completely inside of this region. If * the region that is passed as argument to this method, has a size of value * zero, then it will not be considered to be inside of the current region, * even its starting index is inside. */ bool IsInside(const Self & otherRegion) const { const auto & otherIndex = otherRegion.m_Index; const auto & otherSize = otherRegion.m_Size; for (unsigned int i = 0; i < ImageDimension; ++i) { if (otherIndex[i] < m_Index[i] || otherSize[i] == 0 || otherIndex[i] + static_cast(otherSize[i]) > m_Index[i] + static_cast(m_Size[i])) { return false; } } return true; } /** Get the number of pixels contained in this region. This just * multiplies the size components. */ SizeValueType GetNumberOfPixels() const; /** Pad an image region by the specified radius. Region can be padded * uniformly in all dimensions or can be padded by different amounts * in each dimension. */ void PadByRadius(OffsetValueType radius); void PadByRadius(const IndexValueArrayType radius); void PadByRadius(const SizeType & radius); /** Shrink an image region by the specified radius. The region can be shrunk * uniformly in all dimension or can be shrink by different amounts in each * direction. If the shrink operation fails because the radius is too large, * this method returns false. */ bool ShrinkByRadius(OffsetValueType radius); bool ShrinkByRadius(const IndexValueArrayType radius); bool ShrinkByRadius(const SizeType & radius); /** Crop a region by another region. If this region is outside of the * crop, this method returns false and does not modify the * region. Otherwise, this method returns true and the region is * modified to reflect the crop. */ bool Crop(const Self & region); /** Slice a region, producing a region that is one dimension lower * than the current region. Parameter "dim" specifies which dimension * to remove. */ SliceRegion Slice(const unsigned int dim) const; /** Supports tuple-like access: `get<0>()` returns a reference to the index and `get<1>()` returns a reference to the * size of the region. */ template [[nodiscard]] auto & get() { if constexpr (VTupleIndex == 0) { return m_Index; } else { static_assert(VTupleIndex == 1); return m_Size; } } /** Supports tuple-like access. Const overload. */ template [[nodiscard]] const auto & get() const { if constexpr (VTupleIndex == 0) { return m_Index; } else { static_assert(VTupleIndex == 1); return m_Size; } } protected: /** Methods invoked by Print() to print information about the object * including superclasses. Typically not called by the user (use Print() * instead) but used in the hierarchical print process to combine the * output of several classes. */ void PrintSelf(std::ostream & os, Indent indent) const itkRegionOverrideMacro; private: IndexType m_Index = { { 0 } }; SizeType m_Size = { { 0 } }; /** Friends of ImageRegion */ friend class ImageBase; }; // Deduction guide to avoid compiler warnings (-wctad-maybe-unsupported) when using class template argument deduction. template ImageRegion(const Index &, const Size &)->ImageRegion; template std::ostream & operator<<(std::ostream & os, const ImageRegion & region); } // end namespace itk namespace std { #if defined(__clang__) # pragma GCC diagnostic push // Old Clang compiler versions (before Clang 7.0.0) produced some unimportant warnings, like: "warning: 'tuple_size' // defined as a struct template here but previously declared as a class template" # pragma GCC diagnostic ignored "-Wmismatched-tags" #endif // NOLINTBEGIN(cert-dcl58-cpp) // Locally suppressed the following warning from Clang-Tidy (LLVM 17.0.1), as it appears undeserved. // > warning: modification of 'std' namespace can result in undefined behavior [cert-dcl58-cpp] /** `std::tuple_size` specialization, needed for ImageRegion to support C++ structured binding. * * Example, using structured binding to retrieve the index and size of a region: \code auto [index, size] = image.GetRequestedRegion(); \endcode */ template struct tuple_size> : integral_constant {}; /** `std::tuple_element` specialization, needed for ImageRegion to support C++ structured binding. */ template struct tuple_element> : conditional, itk::Size> { static_assert(VTupleIndex < tuple_size_v>); }; // NOLINTEND(cert-dcl58-cpp) #if defined(__clang__) # pragma GCC diagnostic pop #endif } // namespace std #undef itkRegionOverrideMacro #ifndef ITK_MANUAL_INSTANTIATION # include "itkImageRegion.hxx" #endif #endif