/*========================================================================= * * 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. * *=========================================================================*/ #ifndef itkWatershedBoundary_h #define itkWatershedBoundary_h #include #include #include "itkImage.h" #include "itkProcessObject.h" #include namespace itk { namespace watershed { /** \class Boundary * \par * A data object for used by watershed segmentation process objects in * streaming applications. A "boundary" represents the single-pixel wide * surface of an image chunk. This class is used to store information needed to * resolve processing at chunk boundaries after data streaming of the * watershed segmentation algorithm. * * \par * This is an itkDataObject. It contains a matrix of "faces" of an * N-dimensional hypercube. A chunk of a volume with \f$ N \f$ dimensions * has \f$ 2N \f$ faces of dimension \f$ N-1 \f$. Some examples: A * 2-dimensional image has 4 faces that are lines. A 3-dimensional image has 6 * faces that are planes. A 4-dimensional image has 8 faces which are * cubes. Faces are indexed as \f$ N \f$ sets of pairs \f$ (low, high) \f$. * * \sa WatershedSegmenter * \sa WatershedBoundaryResolver * \ingroup WatershedSegmentation * \ingroup ITKWatersheds */ template class ITK_TEMPLATE_EXPORT Boundary : public DataObject { public: ITK_DISALLOW_COPY_AND_MOVE(Boundary); /** The dimensionality of this boundary. For example, if the boundary * of a set of planes, it has dimensionality 2. If the boundary is * a set of lines, it has dimensionality 1. Dimensionality is one less * than the image chunks from which the boundary is derived. */ static constexpr unsigned int Dimension = TDimension; /** A pair of values used to index into the boundary data structure. * The IndexType.first is the dimension of the face and IndexType.second is a * binary value 0 or 1 indicating the LOW face or the HIGH face, * respectively. */ using IndexType = std::pair; using ImageType = Image; using ImageIndexType = typename ImageType::IndexType; using ScalarType = TScalar; /** Data type stored at each pixel in a face. */ struct face_pixel_t { /**Index of the direction of watershed flow through this pixel. * A negative value indicates that the flow does not move out * of the region. A positive value is the index into the * pixel neighborhood of the facing chunk boundary into which * flow moves. * * Note that the range of values of the index depends on the * the connectivity used by the watershed segmentation algorithm. * If the WS algorithm uses city-block style connectivity (4-connectivity * in 2D, 6-connectivity in 3D, etc) this could actually be a boolean * value indicating inward or outward flow since there is only one * valid neighbor to reference. For extensibility to other * connectivities, this flow value can be used to index a number of * different neighbors. */ short flow; /** The label associated with this pixel. */ IdentifierType label; }; /** */ struct flat_region_t { /** Indices into the associated Face containing boundary pixels. These * give access to spatial information, label and flow associated with * this boundary pixel connection. */ std::list offset_list; /** The value of the lowest point (indicating the steepest descent) along * the boundary of the flat region of which this pixel is a member. */ ScalarType bounds_min; /** The label associated with the lowest point * point along this flat region boundary. */ IdentifierType min_label; /** The value of this flat region */ ScalarType value; }; /** The face data structure. This is just an Image of face pixel types. */ using face_t = Image; /** A hash table holding flat region data structures. */ using flat_hash_t = std::unordered_map; using FlatHashValueType = typename flat_hash_t::value_type; /** Itk type alias and macros defining smart pointer and type identification. */ using Self = Boundary; using Superclass = DataObject; using Pointer = SmartPointer; using ConstPointer = SmartPointer; itkNewMacro(Self); itkOverrideGetNameOfClassMacro(Boundary); /** The following averts an internal compiler error on microsoft compilers */ using FacePointer = typename face_t::Pointer; /** Returns the face at the specified index */ FacePointer GetFace(const IndexType & idx) { return this->GetFace(idx.first, idx.second); } /** Returns the face at the specified index, where dimension is * the number of the axial dimension and highlow is 0 for the LOW * face and 1 for the HIGH face. */ FacePointer GetFace(unsigned int dimension, unsigned int highlow) { if (highlow == 0) { return m_Faces[dimension].first; } else { return m_Faces[dimension].second; } } void SetFace(FacePointer f, const IndexType & idx) { this->SetFace(f, idx.first, idx.second); } void SetFace(FacePointer f, unsigned int dimension, unsigned int highlow) { if (highlow == 0) { m_Faces[dimension].first = f; } else { m_Faces[dimension].second = f; } this->Modified(); } /** Get/Set the table of flat region connections specified by the index. */ flat_hash_t * GetFlatHash(const IndexType & idx) { return this->GetFlatHash(idx.first, idx.second); } flat_hash_t * GetFlatHash(unsigned int dimension, unsigned int highlow) { if (highlow == 0) { return &(m_FlatHashes[dimension].first); } else { return &(m_FlatHashes[dimension].second); } } void SetFlatHash(flat_hash_t & l, const IndexType & idx) { this->SetFlatHash(l, idx.first, idx.second); } void SetFlatHash(flat_hash_t & l, unsigned int dimension, unsigned int highlow) { if (highlow == 0) { m_FlatHashes[dimension].first = l; } else { m_FlatHashes[dimension].second = l; } this->Modified(); } /** Marks a face in the boundary object as either valid (true) or * invalid (false). A valid face is assumed to be initialized * and contain information. No assumptions are made about an * invalid face. */ void SetValid(bool & l, const IndexType & idx) { this->SetValid(l, idx.first, idx.second); } void SetValid(bool b, unsigned int dimension, unsigned int highlow) { if (highlow == 0) { m_Valid[dimension].first = b; } else { m_Valid[dimension].second = b; } this->Modified(); } bool GetValid(const IndexType & idx) const { return this->GetValid(idx.first, idx.second); } bool GetValid(unsigned int dimension, unsigned int highlow) const { if (highlow == 0) { return m_Valid[dimension].first; } else { return m_Valid[dimension].second; } } protected: Boundary(); ~Boundary() override = default; void PrintSelf(std::ostream & os, Indent indent) const override; /** The Nx2 matrix of faces of this boundary. */ std::vector> m_Faces{}; /** The Nx2 matrix flat region connections associated with * this boundary. */ std::vector> m_FlatHashes{}; /** The Nx2 matrix of boolean flags indicating which faces contain * information. */ std::vector> m_Valid{}; }; } // end namespace watershed } // end namespace itk #ifndef ITK_MANUAL_INSTANTIATION # include "itkWatershedBoundary.hxx" #endif #endif