/*========================================================================= * * 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 itkKLMSegmentationBorder_h #define itkKLMSegmentationBorder_h #include "itkSegmentationBorder.h" #include "itkKLMSegmentationRegion.h" #include "itkMacro.h" #include "ITKKLMRegionGrowingExport.h" #include "itkMath.h" #include "vnl/vnl_vector.h" namespace itk { /** * \class KLMDynamicBorderArray * \brief Object maintaining a reference to a list of borders associated * with a region. * * This is a tiny class similar to smart pointers that maintains a reference * to a list of borders pointed by a region. * * \ingroup RegionGrowingSegmentation * \ingroup ITKKLMRegionGrowing */ template class KLMDynamicBorderArray { public: /** Greater than operators defined to work with both static objects * or pointer to objects. In the degenerate * case of an image where all (or many) Lambda's are equal to some * constant value, this operator will ensure that the future * merged regions do not gain more borders than other regions, * thus avoiding pathologically slow behavior. */ bool operator>(const KLMDynamicBorderArray & rhs) const { if (Math::ExactlyEquals(m_Pointer->GetLambda(), rhs.m_Pointer->GetLambda())) { if (m_Pointer->GetLambda() < 0) { return (m_Pointer > rhs.m_Pointer); } else { // The purpose of this comparison is to not let any one region // get more borders than another region. In the degenerate // case of an image where the Lambdas are always equal to some // constant C, allowing a single region to be repeatedly // merged so that it gains many borders will result in // pathologically slow behavior. double v1 = std::max(static_cast(m_Pointer->GetRegion1()->GetRegionBorderSize()), static_cast(m_Pointer->GetRegion2()->GetRegionBorderSize())); double v2 = std::max(static_cast(rhs.m_Pointer->GetRegion1()->GetRegionBorderSize()), static_cast(rhs.m_Pointer->GetRegion2()->GetRegionBorderSize())); return (v1 > v2); } } return (m_Pointer->GetLambda() > rhs.m_Pointer->GetLambda()); } bool operator>(const KLMDynamicBorderArray * rhs) const { if (m_Pointer->GetLambda() == rhs->m_Pointer->GetLambda()) { if (m_Pointer->GetLambda() < 0) { return (m_Pointer > rhs->m_Pointer); } else { // The purpose of this comparison is to not let any one region // get more borders than another region. In the degenerate // case of an image where the Lambdas are always equal to some // constant C, allowing a single region to be repeatedly // merged so that it gains many borders will result in // pathologically slow behavior. double v1 = std::max(static_cast(m_Pointer->GetRegion1()->GetRegionBorderSize()), static_cast(m_Pointer->GetRegion2()->GetRegionBorderSize())); double v2 = std::max(static_cast(rhs->m_Pointer->GetRegion1()->GetRegionBorderSize()), static_cast(rhs->m_Pointer->GetRegion2()->GetRegionBorderSize())); return (v1 > v2); } } return (m_Pointer->GetLambda() > rhs->m_Pointer->GetLambda()); } TBorder * m_Pointer; }; /** * \class KLMSegmentationBorder * \brief Base class for KLMSegmentationBorder object * * itkKLMSegmentationBorder is the base class for the KLMSegmentationBorder * objects. It provides the basic function definitions that are inherent to a * KLMSegmentationBorder objects. * * This class implements the border object that is used in particular with * the KLM algorithm (see also KLMRegionGrowImageFilter). The border is * defined by the adjacency of two regions. The parameter Lambda ascertains * the importance of the border in defining the regions. The higher the * values of Lambda the more dominant is its presence in the a region. In * case of removal of a border during the region growing process the one with * least Lambda value is eliminated. * * \ingroup RegionGrowingSegmentation * \ingroup ITKKLMRegionGrowing */ // Forward reference because of circular dependencies class ITK_FORWARD_EXPORT KLMSegmentationRegion; class ITKKLMRegionGrowing_EXPORT KLMSegmentationBorder : public SegmentationBorder { public: ITK_DISALLOW_COPY_AND_MOVE(KLMSegmentationBorder); /** Standard class type aliases. */ using Self = KLMSegmentationBorder; using Superclass = SegmentationBorder; using Pointer = SmartPointer; using ConstPointer = SmartPointer; /** Method for creation through the object factory. */ itkNewMacro(Self); /** \see LightObject::GetNameOfClass() */ itkOverrideGetNameOfClassMacro(KLMSegmentationBorder); /** Set the region 1 associated with the border */ void SetRegion1(KLMSegmentationRegion * Region1); /** Get the region 1 associated with the border. */ KLMSegmentationRegion * GetRegion1(); /** Set the region 2 associated with the border. */ void SetRegion2(KLMSegmentationRegion * Region2); /** Get the region 2 associated with the border. */ KLMSegmentationRegion * GetRegion2(); /** Set/Get the Lambda parameter associate with the borders * in the KLM algorithm */ itkSetMacro(Lambda, double); itkGetConstReferenceMacro(Lambda, double); /** Evaluate the Lambda for a given border. */ void EvaluateLambda(); /** Print the data associated with each border. */ void PrintBorderInfo(); protected: /** Constructor. */ KLMSegmentationBorder(); /** Destructor. */ ~KLMSegmentationBorder() override; /** Print self identity */ void PrintSelf(std::ostream & os, Indent indent) const override; private: double m_Lambda{}; KLMSegmentationRegion * m_Region1{}; KLMSegmentationRegion * m_Region2{}; }; } // end namespace itk #endif