diff --git a/Examples/Segmentation/HoughTransform2DCirclesImageFilter.cxx b/Examples/Segmentation/HoughTransform2DCirclesImageFilter.cxx index 6f92403b1a4..64dc099b92e 100644 --- a/Examples/Segmentation/HoughTransform2DCirclesImageFilter.cxx +++ b/Examples/Segmentation/HoughTransform2DCirclesImageFilter.cxx @@ -215,9 +215,9 @@ int main( int argc, char *argv[] ) while( itCircles != circles.end() ) { std::cout << "Center: "; - std::cout << (*itCircles)->GetObjectToParentTransform()->GetOffset() + std::cout << itCircles->GetCenter() << std::endl; - std::cout << "Radius: " << (*itCircles)->GetRadius()[0] << std::endl; + std::cout << "Radius: " << itCircles->GetRadius() << std::endl; // Software Guide : EndCodeSnippet // Software Guide : BeginLatex @@ -231,16 +231,14 @@ int main( int argc, char *argv[] ) angle <= itk::Math::twopi; angle += itk::Math::pi/60.0 ) { - using TransformType = HoughTransformFilterType::CircleType::TransformType; - using OffsetType = TransformType::OutputVectorType; - const OffsetType offset = - (*itCircles)->GetObjectToParentTransform()->GetOffset(); + const HoughTransformFilterType::Circle::CenterType center = itCircles->GetCenter(); + using IndexValueType = HoughTransformFilterType::IndexType::IndexValueType; localIndex[0] = - itk::Math::Round(offset[0] - + (*itCircles)->GetRadius()[0]*std::cos(angle)); + itk::Math::Round(center[0] + + itCircles->GetRadius()*std::cos(angle)); localIndex[1] = - itk::Math::Round(offset[1] - + (*itCircles)->GetRadius()[0]*std::sin(angle)); + itk::Math::Round(center[1] + + itCircles->GetRadius()*std::sin(angle)); OutputImageType::RegionType outputRegion = localOutputImage->GetLargestPossibleRegion(); diff --git a/Modules/Filtering/ImageFeature/include/itkHoughTransform2DCirclesImageFilter.h b/Modules/Filtering/ImageFeature/include/itkHoughTransform2DCirclesImageFilter.h index 7d0e4ec898d..f106c362ea0 100644 --- a/Modules/Filtering/ImageFeature/include/itkHoughTransform2DCirclesImageFilter.h +++ b/Modules/Filtering/ImageFeature/include/itkHoughTransform2DCirclesImageFilter.h @@ -21,6 +21,9 @@ #include "itkImageToImageFilter.h" #include "itkEllipseSpatialObject.h" +#include "itkIndex.h" +#include +#include namespace itk { @@ -66,6 +69,42 @@ class ITK_TEMPLATE_EXPORT HoughTransform2DCirclesImageFilter: { public: + /** + * \class Circle + * \brief Represents a circle by its center and its radius. + * + * \ingroup ITKImageFeature + */ + class Circle + { + public: + using CenterType = Index<2>; + using RadiusType = TRadiusPixelType; + + Circle(); + + Circle(const CenterType&, RadiusType); + + RadiusType GetRadius() const; + + void SetRadius(RadiusType); + + CenterType GetCenter() const; + + void SetCenter(const CenterType&); + + void ToEllipseSpatialObject(EllipseSpatialObject<2>&) const; + + private: + CenterType m_Center; + RadiusType m_Radius; + + friend std::ostream & operator<<(std::ostream & os, const Circle & circle) + { + return os << circle.m_Center << ' ' << circle.m_Radius; + } + }; + /** Standard class type aliases. */ using Self = HoughTransform2DCirclesImageFilter; using Superclass = ImageToImageFilter< Image< TInputPixelType, 2 >, @@ -96,9 +135,7 @@ class ITK_TEMPLATE_EXPORT HoughTransform2DCirclesImageFilter: using OutputImageRegionType = typename InputImageType::RegionType; /** Circle type alias. */ - using CircleType = EllipseSpatialObject< 2 >; - using CirclePointer = typename CircleType::Pointer; - using CirclesListType = std::list< CirclePointer >; + using CirclesListType = std::vector< Circle >; using CirclesListSizeType = typename CirclesListType::size_type; diff --git a/Modules/Filtering/ImageFeature/include/itkHoughTransform2DCirclesImageFilter.hxx b/Modules/Filtering/ImageFeature/include/itkHoughTransform2DCirclesImageFilter.hxx index e195ddd5e1e..0541c18fdc6 100644 --- a/Modules/Filtering/ImageFeature/include/itkHoughTransform2DCirclesImageFilter.hxx +++ b/Modules/Filtering/ImageFeature/include/itkHoughTransform2DCirclesImageFilter.hxx @@ -24,9 +24,90 @@ #include "itkGaussianDerivativeImageFunction.h" #include "itkMinimumMaximumImageCalculator.h" #include "itkMath.h" +#include namespace itk { +template< typename TInputPixelType, typename TOutputPixelType, typename TRadiusPixelType > +HoughTransform2DCirclesImageFilter< TInputPixelType, TOutputPixelType, TRadiusPixelType > +::Circle::Circle() + : + m_Center{}, + m_Radius{} +{ +} + +template< typename TInputPixelType, typename TOutputPixelType, typename TRadiusPixelType > +HoughTransform2DCirclesImageFilter< TInputPixelType, TOutputPixelType, TRadiusPixelType > +::Circle::Circle(const CenterType& center, const RadiusType radius) + : + // Note: Use parentheses instead of curly braces to initialize m_Center, to avoid + // AppleClang 6.0.0.6000056 compilation error, "no viable conversion from + // 'const CenterType' (aka 'const Index<2>') to 'IndexValueType' (aka 'long'). + m_Center( center ), + m_Radius{ radius } +{ + assert(radius >= 0.0); +} + +template< typename TInputPixelType, typename TOutputPixelType, typename TRadiusPixelType > +auto +HoughTransform2DCirclesImageFilter< TInputPixelType, TOutputPixelType, TRadiusPixelType > +::Circle::GetCenter() const +-> CenterType +{ + return m_Center; +} + +template< typename TInputPixelType, typename TOutputPixelType, typename TRadiusPixelType > +void +HoughTransform2DCirclesImageFilter< TInputPixelType, TOutputPixelType, TRadiusPixelType > +::Circle::SetCenter(const CenterType& center) +{ + m_Center = center; +} + +template< typename TInputPixelType, typename TOutputPixelType, typename TRadiusPixelType > +auto +HoughTransform2DCirclesImageFilter< TInputPixelType, TOutputPixelType, TRadiusPixelType > +::Circle::GetRadius() const +-> RadiusType +{ + return m_Radius; +} + +template< typename TInputPixelType, typename TOutputPixelType, typename TRadiusPixelType > +void +HoughTransform2DCirclesImageFilter< TInputPixelType, TOutputPixelType, TRadiusPixelType > +::Circle::SetRadius(const RadiusType radius) +{ + assert(radius >= 0.0); + m_Radius = radius; +} + +template< typename TInputPixelType, typename TOutputPixelType, typename TRadiusPixelType > +void +HoughTransform2DCirclesImageFilter< TInputPixelType, TOutputPixelType, TRadiusPixelType > +::Circle::ToEllipseSpatialObject(EllipseSpatialObject<2>& ellipseSpatialObject) const +{ + auto* const transform = ellipseSpatialObject.GetObjectToParentTransform(); + + if (transform == nullptr) + { + assert(!"ellipseSpatialObject.GetObjectToParentTransform() should not return nullptr!"); + } + else + { + EllipseSpatialObject<2>::TransformType::OffsetType offset; + offset[0] = m_Center[0]; + offset[1] = m_Center[1]; + + transform->SetOffset(offset); + } + ellipseSpatialObject.SetRadius(m_Radius); +} + + template< typename TInputPixelType, typename TOutputPixelType, typename TRadiusPixelType > HoughTransform2DCirclesImageFilter< TInputPixelType, TOutputPixelType, TRadiusPixelType > ::HoughTransform2DCirclesImageFilter() : @@ -223,8 +304,6 @@ HoughTransform2DCirclesImageFilter< TInputPixelType, TOutputPixelType, TRadiusPi Index< 2 > index; - CirclesListSizeType circles = 0; - // Find maxima // Break out of "forever loop" as soon as the requested number of circles is found. for(;;) @@ -243,26 +322,17 @@ HoughTransform2DCirclesImageFilter< TInputPixelType, TOutputPixelType, TRadiusPi const InternalImageType::IndexType indexOfMaximum = minMaxCalculator->GetIndexOfMaximum(); - // Create a Circle Spatial Object - CirclePointer Circle = CircleType::New(); - Circle->SetId(static_cast( circles )); - Circle->SetRadius( m_RadiusImage->GetPixel( indexOfMaximum ) ); - - CircleType::VectorType center; - center[0] = indexOfMaximum[0]; - center[1] = indexOfMaximum[1]; - Circle->GetObjectToParentTransform()->SetOffset(center); - Circle->ComputeBoundingBox(); + // Create a Circle + const TRadiusPixelType radius = m_RadiusImage->GetPixel(indexOfMaximum); - m_CirclesList.push_back(Circle); + m_CirclesList.push_back(Circle{ indexOfMaximum, radius }); - circles++; - if ( circles >= m_NumberOfCircles ) { break; } + if ( m_CirclesList.size() >= m_NumberOfCircles ) { break; } // Remove a black disc from the Hough space domain for ( double angle = 0; angle <= 2 * itk::Math::pi; angle += itk::Math::pi / 1000 ) { - for ( double length = 0; length < m_DiscRadiusRatio * Circle->GetRadius()[0]; length += 1 ) + for ( double length = 0; length < m_DiscRadiusRatio * radius; length += 1 ) { index[0] = Math::Round( indexOfMaximum[0] + length * std::cos(angle) ); index[1] = Math::Round( indexOfMaximum[1] + length * std::sin(angle) ); diff --git a/Modules/Filtering/ImageFeature/test/itkHoughTransform2DCirclesImageTest.cxx b/Modules/Filtering/ImageFeature/test/itkHoughTransform2DCirclesImageTest.cxx index dd9a2a1c171..7c9b0819dd7 100644 --- a/Modules/Filtering/ImageFeature/test/itkHoughTransform2DCirclesImageTest.cxx +++ b/Modules/Filtering/ImageFeature/test/itkHoughTransform2DCirclesImageTest.cxx @@ -168,10 +168,8 @@ namespace return false; } - using CirclesListType = FilterType1::CirclesListType; - - const CirclesListType& circles1 = filter1->GetCircles(); - const CirclesListType& circles2 = filter2->GetCircles(); + const auto& circles1 = filter1->GetCircles(); + const auto& circles2 = filter2->GetCircles(); if ( circles1.empty() || circles2.empty() ) { @@ -187,30 +185,13 @@ namespace return false; } - using CircleType = FilterType1::CircleType; - - const CircleType* const circle1 = circles1.front().GetPointer(); - const CircleType* const circle2 = circles2.front().GetPointer(); - - if ( (circle1 == nullptr) || (circle2 == nullptr) ) - { - std::cout << "A Circle pointer appears to be incorrect!" << std::endl; - return false; - } - - const CircleType::TransformType* const transform1 = circle1->GetObjectToParentTransform(); - const CircleType::TransformType* const transform2 = circle2->GetObjectToParentTransform(); - - if ( (transform1 == nullptr) || (transform2 == nullptr) ) - { - std::cout << "A GetObjectToParentTransform() call appears to be incorrect!" << std::endl; - return false; - } + const auto circle1 = circles1.front(); + const auto circle2 = circles2.front(); bool success = true; - const itk::Vector& center1 = transform1->GetOffset(); - const itk::Vector& center2 = transform2->GetOffset(); + const auto center1 = circle1.GetCenter(); + const auto center2 = circle2.GetCenter(); if (center1 != center2) { @@ -220,8 +201,8 @@ namespace success = false; } - const double radius1 = circle1->GetRadius()[0]; - const double radius2 = circle2->GetRadius()[0]; + const double radius1 = circle1.GetRadius(); + const double radius2 = circle2.GetRadius(); if ( radius2 < radius1 ) { @@ -381,18 +362,18 @@ int itkHoughTransform2DCirclesImageTest( int, char* [] ) unsigned int i = 0; while( it != circleList.end() ) { - if( !itk::Math::FloatAlmostEqual( (double)( it->GetPointer()->GetRadius()[0] ), + if( !itk::Math::FloatAlmostEqual( (double)( it->GetRadius() ), radius[i], 10, radiusTolerance ) && - !itk::Math::FloatAlmostEqual( (double)( it->GetPointer()->GetRadius()[0] ), + !itk::Math::FloatAlmostEqual( (double)( it->GetRadius() ), radius[i] * discRadiusRatio, 10, radiusTolerance ) ) { std::cout << "Failure for circle #" << i << std::endl; - std::cout << "Expected radius: " << radius[i] << ", found " << it->GetPointer()->GetRadius() << std::endl; + std::cout << "Expected radius: " << radius[i] << ", found " << it->GetRadius() << std::endl; success = false; } else { - std::cout << "Circle #" << i << " radius: " << it->GetPointer()->GetRadius() << std::endl; + std::cout << "Circle #" << i << " radius: " << it->GetRadius() << std::endl; } ++it; ++i;