From bd0ef351d497debf21da4af475aaec5930b5a77f Mon Sep 17 00:00:00 2001 From: =?utf8?q?Leonardo=20Fl=C3=B3rez-Valencia?= Date: Thu, 23 Mar 2017 16:38:44 -0500 Subject: [PATCH] ... --- data/binary_test_2D_00.png | Bin 0 -> 5192 bytes examples/CMakeLists.txt | 1 + examples/Dijkstra_Maurer.cxx | 120 +++++++++++ libs/fpa/Base/Dijkstra.h | 17 +- libs/fpa/Base/Dijkstra.hxx | 129 +++++++++-- libs/fpa/Base/Functors/InvertValue.h | 80 +++++++ libs/fpa/Base/Functors/VertexParentBase.h | 58 +++++ libs/fpa/Base/MinimumSpanningTree.h | 77 +++++++ libs/fpa/Base/MinimumSpanningTree.hxx | 239 +++++++++++++++++++++ libs/fpa/Image/Dijkstra.h | 91 ++++++++ libs/fpa/Image/Functors/VertexIdentity.h | 67 ++++++ libs/fpa/Image/Functors/VertexParentBase.h | 63 ++++++ libs/fpa/Image/MinimumSpanningTree.h | 71 ++++++ 13 files changed, 993 insertions(+), 20 deletions(-) create mode 100644 data/binary_test_2D_00.png create mode 100644 examples/Dijkstra_Maurer.cxx create mode 100644 libs/fpa/Base/Functors/InvertValue.h create mode 100644 libs/fpa/Base/Functors/VertexParentBase.h create mode 100644 libs/fpa/Base/MinimumSpanningTree.h create mode 100644 libs/fpa/Base/MinimumSpanningTree.hxx create mode 100644 libs/fpa/Image/Dijkstra.h create mode 100644 libs/fpa/Image/Functors/VertexIdentity.h create mode 100644 libs/fpa/Image/Functors/VertexParentBase.h create mode 100644 libs/fpa/Image/MinimumSpanningTree.h diff --git a/data/binary_test_2D_00.png b/data/binary_test_2D_00.png new file mode 100644 index 0000000000000000000000000000000000000000..fbe9d39bd59189ecf7f8ef87e48abde2f3d01953 GIT binary patch literal 5192 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 literal 0 HcmV?d00001 diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt index f0c8826..297746a 100644 --- a/examples/CMakeLists.txt +++ b/examples/CMakeLists.txt @@ -5,6 +5,7 @@ IF(BUILD_EXAMPLES) RegionGrow_Tautology RegionGrow_BinaryThreshold RegionGrow_Mori + Dijkstra_Maurer #CreateMoriInputImage #BronchiiInitialSegmentationWithMori #BronchiiInitialSegmentationWithBinaryThresholdRegionGrow diff --git a/examples/Dijkstra_Maurer.cxx b/examples/Dijkstra_Maurer.cxx new file mode 100644 index 0000000..8875959 --- /dev/null +++ b/examples/Dijkstra_Maurer.cxx @@ -0,0 +1,120 @@ +#include +#include +#include +#include +#include + +#include +#include +#include + +// ------------------------------------------------------------------------- +static const unsigned int VDim = 2; +typedef short TPixel; +typedef double TScalar; +typedef itk::Image< TPixel, VDim > TImage; +typedef itk::Image< TScalar, VDim > TScalarImage; +typedef itk::ImageFileReader< TImage > TReader; +typedef itk::ImageFileWriter< TScalarImage > TWriter; +typedef fpa::Image::Dijkstra< TScalarImage, TScalarImage > TFilter; +typedef itk::MinimumMaximumImageCalculator< TImage > TMinMax; +typedef itk::SignedMaurerDistanceMapImageFilter< TImage, TScalarImage > TDMap; + +typedef fpa::Image::Functors::VertexIdentity< TScalarImage, TScalar > TVertexFunc; +typedef fpa::Base::Functors::InvertValue< TScalar, TScalar > TValueFunc; + +typedef TFilter::TMST TMST; +typedef itk::ImageFileWriter< TMST > TMSTWriter; + +// ------------------------------------------------------------------------- +int main( int argc, char* argv[] ) +{ + // Get arguments + if( argc < 5 + VDim ) + { + std::cerr + << "Usage: " << argv[ 0 ] + << " input_image output_image output_mst stop_at_one_front"; + for( unsigned int i = 0; i < VDim; ++i ) + std::cerr << " s_" << i; + std::cerr << " ..." << std::endl; + return( 1 ); + + } // fi + std::string input_image_filename = argv[ 1 ]; + std::string output_image_filename = argv[ 2 ]; + std::string output_mst_filename = argv[ 3 ]; + bool stop_at_one_front = ( std::atoi( argv[ 4 ] ) == 1 ); + + TReader::Pointer reader = TReader::New( ); + reader->SetFileName( input_image_filename ); + try + { + reader->Update( ); + } + catch( std::exception& err ) + { + std::cerr << "Error caught: " << err.what( ) << std::endl; + return( 1 ); + + } // yrt + + TMinMax::Pointer minmax = TMinMax::New( ); + minmax->SetImage( reader->GetOutput( ) ); + minmax->Compute( ); + + TDMap::Pointer dmap = TDMap::New( ); + dmap->SetInput( reader->GetOutput( ) ); + dmap->SetBackgroundValue( minmax->GetMinimum( ) ); + dmap->InsideIsPositiveOn( ); + dmap->UseImageSpacingOn( ); + dmap->SquaredDistanceOff( ); + + TFilter::Pointer filter = TFilter::New( ); + filter->SetInput( dmap->GetOutput( ) ); + filter->SetStopAtOneFront( stop_at_one_front ); + + for( int i = 5; i < argc; i += VDim ) + { + if( i + VDim <= argc ) + { + TImage::IndexType seed; + for( int j = 0; j < VDim; ++j ) + seed[ j ] = std::atoi( argv[ i + j ] ); + filter->AddSeed( seed ); + + } // fi + + } // rof + + TVertexFunc::Pointer vertex_func = TVertexFunc::New( ); + filter->SetFunctor( vertex_func ); + + TValueFunc::Pointer value_func = TValueFunc::New( ); + value_func->SetAlpha( 1 ); + value_func->SetBeta( 1 ); + filter->SetFunctor( value_func ); + + TWriter::Pointer writer = TWriter::New( ); + writer->SetInput( filter->GetOutput( ) ); + writer->SetFileName( output_image_filename ); + + TMSTWriter::Pointer mst_writer = TMSTWriter::New( ); + mst_writer->SetInput( filter->GetMinimumSpanningTree( ) ); + mst_writer->SetFileName( output_mst_filename ); + + try + { + writer->Update( ); + mst_writer->Update( ); + } + catch( std::exception& err ) + { + std::cerr << "ERROR: " << err.what( ) << std::endl; + return( 1 ); + + } // yrt + return( 0 ); +} + +// eof - $RCSfile$ diff --git a/libs/fpa/Base/Dijkstra.h b/libs/fpa/Base/Dijkstra.h index 2c49c06..d231c7c 100644 --- a/libs/fpa/Base/Dijkstra.h +++ b/libs/fpa/Base/Dijkstra.h @@ -7,6 +7,8 @@ #define __fpa__Base__Dijkstra__h__ #include +#include +#include namespace fpa { @@ -14,7 +16,7 @@ namespace fpa { /** */ - template< class _TFilter, class _TMarksInterface, class _TSeedsInterface > + template< class _TFilter, class _TMarksInterface, class _TSeedsInterface, class _TMST > class Dijkstra : public _TFilter, public _TMarksInterface, @@ -25,6 +27,7 @@ namespace fpa typedef _TFilter Superclass; typedef _TMarksInterface TMarksInterface; typedef _TSeedsInterface TSeedsInterface; + typedef _TMST TMST; typedef itk::SmartPointer< Self > Pointer; typedef itk::SmartPointer< const Self > ConstPointer; @@ -34,7 +37,7 @@ namespace fpa typedef typename Superclass::TVertices TVertices; typedef itk::FunctionBase< TInputValue, TOutputValue > TIntensityFunctor; - typedef itk::FunctionBase< TVertex, TOutputValue > TVertexFunctor; + typedef fpa::Base::Functors::VertexParentBase< TVertex, TOutputValue > TVertexFunctor; protected: struct _TNode @@ -42,15 +45,17 @@ namespace fpa TVertex Vertex; TVertex Parent; TOutputValue Cost; - _TNode( const TVertex& v, const TVertex& p ) + unsigned long FrontId; + _TNode( const TVertex& v, const TVertex& p, const unsigned long& fId ) { this->Vertex = v; this->Parent = p; + this->FrontId = fId; this->Cost = TOutputValue( 0 ); } bool operator<( const _TNode& b ) const { - return( this->Cost < b.Cost ); + return( b.Cost < this->Cost ); } }; @@ -58,6 +63,9 @@ namespace fpa itkTypeMacro( Dijkstra, TFilter ); public: + TMST* GetMinimumSpanningTree( ); + const TMST* GetMinimumSpanningTree( ) const; + const TIntensityFunctor* GetIntensityFunctor( ) const; const TVertexFunctor* GetVertexFunctor( ) const; @@ -77,6 +85,7 @@ namespace fpa protected: typename TIntensityFunctor::Pointer m_IntensityFunctor; typename TVertexFunctor::Pointer m_VertexFunctor; + unsigned long m_MSTIndex; }; } // ecapseman diff --git a/libs/fpa/Base/Dijkstra.hxx b/libs/fpa/Base/Dijkstra.hxx index 9e8e529..c5563c9 100644 --- a/libs/fpa/Base/Dijkstra.hxx +++ b/libs/fpa/Base/Dijkstra.hxx @@ -7,30 +7,58 @@ #define __fpa__Base__Dijkstra__hxx__ // ------------------------------------------------------------------------- -template< class _TFilter, class _TMarksInterface, class _TSeedsInterface > +template< class _TFilter, class _TMarksInterface, class _TSeedsInterface, class _TMST > +typename +fpa::Base::Dijkstra< _TFilter, _TMarksInterface, _TSeedsInterface, _TMST >:: +TMST* fpa::Base::Dijkstra< _TFilter, _TMarksInterface, _TSeedsInterface, _TMST >:: +GetMinimumSpanningTree( ) +{ + return( + dynamic_cast< TMST* >( + this->itk::ProcessObject::GetOutput( this->m_MSTIndex ) + ) + ); +} + +// ------------------------------------------------------------------------- +template< class _TFilter, class _TMarksInterface, class _TSeedsInterface, class _TMST > +const typename +fpa::Base::Dijkstra< _TFilter, _TMarksInterface, _TSeedsInterface, _TMST >:: +TMST* fpa::Base::Dijkstra< _TFilter, _TMarksInterface, _TSeedsInterface, _TMST >:: +GetMinimumSpanningTree( ) const +{ + return( + dynamic_cast< const TMST* >( + this->itk::ProcessObject::GetOutput( this->m_MSTIndex ) + ) + ); +} + +// ------------------------------------------------------------------------- +template< class _TFilter, class _TMarksInterface, class _TSeedsInterface, class _TMST > const typename -fpa::Base::Dijkstra< _TFilter, _TMarksInterface, _TSeedsInterface >:: +fpa::Base::Dijkstra< _TFilter, _TMarksInterface, _TSeedsInterface, _TMST >:: TIntensityFunctor* -fpa::Base::Dijkstra< _TFilter, _TMarksInterface, _TSeedsInterface >:: +fpa::Base::Dijkstra< _TFilter, _TMarksInterface, _TSeedsInterface, _TMST >:: GetIntensityFunctor( ) const { return( this->m_IntensityFunctor ); } // ------------------------------------------------------------------------- -template< class _TFilter, class _TMarksInterface, class _TSeedsInterface > +template< class _TFilter, class _TMarksInterface, class _TSeedsInterface, class _TMST > const typename -fpa::Base::Dijkstra< _TFilter, _TMarksInterface, _TSeedsInterface >:: +fpa::Base::Dijkstra< _TFilter, _TMarksInterface, _TSeedsInterface, _TMST >:: TVertexFunctor* -fpa::Base::Dijkstra< _TFilter, _TMarksInterface, _TSeedsInterface >:: +fpa::Base::Dijkstra< _TFilter, _TMarksInterface, _TSeedsInterface, _TMST >:: GetVertexFunctor( ) const { return( this->m_VertexFunctor ); } // ------------------------------------------------------------------------- -template< class _TFilter, class _TMarksInterface, class _TSeedsInterface > -void fpa::Base::Dijkstra< _TFilter, _TMarksInterface, _TSeedsInterface >:: +template< class _TFilter, class _TMarksInterface, class _TSeedsInterface, class _TMST > +void fpa::Base::Dijkstra< _TFilter, _TMarksInterface, _TSeedsInterface, _TMST >:: SetFunctor( TIntensityFunctor* functor ) { if( this->m_IntensityFunctor.GetPointer( ) != functor ) @@ -42,8 +70,8 @@ SetFunctor( TIntensityFunctor* functor ) } // ------------------------------------------------------------------------- -template< class _TFilter, class _TMarksInterface, class _TSeedsInterface > -void fpa::Base::Dijkstra< _TFilter, _TMarksInterface, _TSeedsInterface >:: +template< class _TFilter, class _TMarksInterface, class _TSeedsInterface, class _TMST > +void fpa::Base::Dijkstra< _TFilter, _TMarksInterface, _TSeedsInterface, _TMST >:: SetFunctor( TVertexFunctor* functor ) { if( this->m_VertexFunctor.GetPointer( ) != functor ) @@ -55,27 +83,96 @@ SetFunctor( TVertexFunctor* functor ) } // ------------------------------------------------------------------------- -template< class _TFilter, class _TMarksInterface, class _TSeedsInterface > -fpa::Base::Dijkstra< _TFilter, _TMarksInterface, _TSeedsInterface >:: +template< class _TFilter, class _TMarksInterface, class _TSeedsInterface, class _TMST > +fpa::Base::Dijkstra< _TFilter, _TMarksInterface, _TSeedsInterface, _TMST >:: Dijkstra( ) : Superclass( ), _TMarksInterface( this ), _TSeedsInterface( this ) { + this->m_MSTIndex = this->GetNumberOfRequiredOutputs( ); + this->SetNumberOfRequiredOutputs( this->m_MSTIndex + 1 ); + this->itk::ProcessObject::SetNthOutput( this->m_MSTIndex, TMST::New( ) ); } // ------------------------------------------------------------------------- -template< class _TFilter, class _TMarksInterface, class _TSeedsInterface > -fpa::Base::Dijkstra< _TFilter, _TMarksInterface, _TSeedsInterface >:: +template< class _TFilter, class _TMarksInterface, class _TSeedsInterface, class _TMST > +fpa::Base::Dijkstra< _TFilter, _TMarksInterface, _TSeedsInterface, _TMST >:: ~Dijkstra( ) { } // ------------------------------------------------------------------------- -template< class _TFilter, class _TMarksInterface, class _TSeedsInterface > -void fpa::Base::Dijkstra< _TFilter, _TMarksInterface, _TSeedsInterface >:: +template< class _TFilter, class _TMarksInterface, class _TSeedsInterface, class _TMST > +void fpa::Base::Dijkstra< _TFilter, _TMarksInterface, _TSeedsInterface, _TMST >:: GenerateData( ) { + // Init objects + this->_ConfigureOutputs( TOutputValue( 0 ) ); + this->_InitMarks( this->GetNumberOfSeeds( ) ); + TMST* mst = this->GetMinimumSpanningTree( ); + + // Init queue + std::vector< _TNode > q; + unsigned long frontId = 1; + for( TVertex seed: this->GetSeeds( ) ) + q.push_back( _TNode( seed, seed, frontId++ ) ); + + // Main loop + while( q.size( ) > 0 ) + { + // Get next candidate + std::pop_heap( q.begin( ), q.end( ) ); + _TNode node = q.back( ); + q.pop_back( ); + if( this->_IsMarked( node.Vertex ) ) + continue; + this->_Mark( node.Vertex, node.FrontId ); + + // Ok, pixel lays inside region + this->_SetOutputValue( node.Vertex, node.Cost ); + mst->SetParent( node.Vertex, node.Parent ); + + // Add neighborhood + TVertices neighbors = this->_GetNeighbors( node.Vertex ); + for( TVertex neigh: neighbors ) + { + if( this->_IsMarked( neigh ) ) + { + // Invoke stop at collisions + unsigned long nColl = this->_Collisions( node.Vertex, neigh ); + if( + this->StopAtOneFront( ) && + this->GetNumberOfSeeds( ) > 1 && + nColl == 1 + ) + q.clear( ); + } + else + { + // Compute new cost + TOutputValue ncost = + this->m_VertexFunctor->Evaluate( neigh, node.Vertex ); + if( this->m_IntensityFunctor.IsNotNull( ) ) + ncost = this->m_IntensityFunctor->Evaluate( ncost ); + + // This algorithm only supports positive values + if( ncost >= TOutputValue( 0 ) ) + { + // Insert new node + _TNode nn( neigh, node.Vertex, node.FrontId ); + nn.Cost = node.Cost + ncost; + q.push_back( nn ); + std::push_heap( q.begin( ), q.end( ) ); + + } // fi + + } // fi + + } // rof + + } // elihw + this->_FreeMarks( ); } #endif // __fpa__Base__Dijkstra__hxx__ diff --git a/libs/fpa/Base/Functors/InvertValue.h b/libs/fpa/Base/Functors/InvertValue.h new file mode 100644 index 0000000..9da3221 --- /dev/null +++ b/libs/fpa/Base/Functors/InvertValue.h @@ -0,0 +1,80 @@ +// ========================================================================= +// @author Leonardo Florez Valencia +// @email florez-l@javeriana.edu.co +// ========================================================================= + +#ifndef __fpa__Base__Functors__InvertValue__h__ +#define __fpa__Base__Functors__InvertValue__h__ + +#include +#include + +namespace fpa +{ + namespace Base + { + namespace Functors + { + /** + */ + template< class _TInputValue, class _TOutputValue > + class InvertValue + : public itk::FunctionBase< _TInputValue, _TOutputValue > + { + public: + typedef InvertValue Self; + typedef itk::FunctionBase< _TInputValue, _TOutputValue > Superclass; + typedef itk::SmartPointer< Self > Pointer; + typedef itk::SmartPointer< const Self > ConstPointer; + + typedef _TInputValue TInputValue; + typedef _TOutputValue TOutputValue; + + public: + itkNewMacro( Self ); + itkTypeMacro( InvertValue, itk::FunctionBase ); + + itkGetConstMacro( Alpha, double ); + itkGetConstMacro( Beta, double ); + itkSetMacro( Alpha, double ); + itkSetMacro( Beta, double ); + + public: + virtual TOutputValue Evaluate( const TInputValue& a ) const override + { + double d = this->m_Alpha; + d += std::pow( double( a ), this->m_Beta ); + double x = -1; + if( std::fabs( d ) > double( 0 ) ) + x = + double( 1 ) / + ( this->m_Alpha + std::pow( double( a ), this->m_Beta ) ); + return( TOutputValue( x ) ); + } + + protected: + InvertValue( ) + : Superclass( ), + m_Alpha( double( 1 ) ), + m_Beta( double( 1 ) ) + { } + virtual ~InvertValue( ) { } + + private: + InvertValue( const Self& other ); + Self& operator=( const Self& other ); + + protected: + double m_Alpha; + double m_Beta; + }; + + } // ecapseman + + } // ecapseman + +} // ecapseman + +#endif // __fpa__Base__Functors__InvertValue__h__ + +// eof - $RCSfile$ diff --git a/libs/fpa/Base/Functors/VertexParentBase.h b/libs/fpa/Base/Functors/VertexParentBase.h new file mode 100644 index 0000000..2725063 --- /dev/null +++ b/libs/fpa/Base/Functors/VertexParentBase.h @@ -0,0 +1,58 @@ +// ========================================================================= +// @author Leonardo Florez Valencia +// @email florez-l@javeriana.edu.co +// ========================================================================= + +#ifndef __fpa__Base__Functors__VertexParentBase__h__ +#define __fpa__Base__Functors__VertexParentBase__h__ + +#include +#include + +namespace fpa +{ + namespace Base + { + namespace Functors + { + /** + */ + template< class _TVertex, class _TOutputValue > + class VertexParentBase + : public itk::Object + { + public: + typedef VertexParentBase Self; + typedef itk::Object Superclass; + typedef itk::SmartPointer< Self > Pointer; + typedef itk::SmartPointer< const Self > ConstPointer; + + typedef _TVertex TVertex; + typedef _TOutputValue TOutputValue; + + public: + itkTypeMacro( VertexParentBase, TFilter ); + + public: + virtual TOutputValue Evaluate( const TVertex& v, const TVertex& p ) const = 0; + + protected: + VertexParentBase( ) + : Superclass( ) + { } + virtual ~VertexParentBase( ) { } + + private: + VertexParentBase( const Self& other ); + Self& operator=( const Self& other ); + }; + + } // ecapseman + + } // ecapseman + +} // ecapseman + +#endif // __fpa__Base__Functors__VertexParentBase__h__ + +// eof - $RCSfile$ diff --git a/libs/fpa/Base/MinimumSpanningTree.h b/libs/fpa/Base/MinimumSpanningTree.h new file mode 100644 index 0000000..35cb6e4 --- /dev/null +++ b/libs/fpa/Base/MinimumSpanningTree.h @@ -0,0 +1,77 @@ +// ========================================================================= +// @author Leonardo Florez Valencia +// @email florez-l@javeriana.edu.co +// ========================================================================= + +#ifndef __fpa__Base__MinimumSpanningTree__h__ +#define __fpa__Base__MinimumSpanningTree__h__ + +#include + +namespace fpa +{ + namespace Base + { + /** + */ + template< class _TVertex, class _Superclass > + class MinimumSpanningTree + : public _Superclass + { + public: + typedef MinimumSpanningTree Self; + typedef _Superclass Superclass; + typedef itk::SmartPointer< Self > Pointer; + typedef itk::SmartPointer< const Self > ConstPointer; + + typedef _TVertex TVertex; + typedef std::pair< TVertex, bool > TCollision; + typedef std::vector< TCollision > TCollisionsRow; + typedef std::vector< TCollisionsRow > TCollisions; + typedef std::vector< TVertex > TVertices; + + protected: + typedef std::vector< unsigned long > _TRow; + typedef std::vector< _TRow > _TMatrix; + + public: + itkTypeMacro( fpa::Base::MinimumSpanningTree, _Superclass ); + + public: + const TCollisions& GetCollisions( ) const; + void SetCollisions( const TCollisions& collisions ); + + void ClearSeeds( ); + void AddSeed( const TVertex& seed ); + + virtual TVertex GetParent( const TVertex& v ) const = 0; + virtual void SetParent( const TVertex& v, const TVertex& p ) = 0; + + virtual TVertices GetPath( const TVertex& a ) const; + virtual TVertices GetPath( const TVertex& a, const TVertex& b ) const; + + protected: + MinimumSpanningTree( ); + virtual ~MinimumSpanningTree( ); + + private: + MinimumSpanningTree( const Self& other ); + Self& operator=( const Self& other ); + + protected: + TCollisions m_Collisions; + _TMatrix m_FrontPaths; + std::vector< TVertex > m_Seeds; + }; + + } // ecapseman + +} // ecapseman + +#ifndef ITK_MANUAL_INSTANTIATION +# include +#endif // ITK_MANUAL_INSTANTIATION + +#endif // __fpa__Base__MinimumSpanningTree__h__ + +// eof - $RCSfile$ diff --git a/libs/fpa/Base/MinimumSpanningTree.hxx b/libs/fpa/Base/MinimumSpanningTree.hxx new file mode 100644 index 0000000..79ddebe --- /dev/null +++ b/libs/fpa/Base/MinimumSpanningTree.hxx @@ -0,0 +1,239 @@ +// ========================================================================= +// @author Leonardo Florez Valencia +// @email florez-l@javeriana.edu.co +// ========================================================================= + +#ifndef __fpa__Base__MinimumSpanningTree__hxx__ +#define __fpa__Base__MinimumSpanningTree__hxx__ + +// ------------------------------------------------------------------------- +template< class _TVertex, class _Superclass > +const typename fpa::Base::MinimumSpanningTree< _TVertex, _Superclass >:: +TCollisions& fpa::Base::MinimumSpanningTree< _TVertex, _Superclass >:: +GetCollisions( ) const +{ + return( this->m_Collisions ); +} + +// ------------------------------------------------------------------------- +template< class _TVertex, class _Superclass > +void fpa::Base::MinimumSpanningTree< _TVertex, _Superclass >:: +SetCollisions( const TCollisions& collisions ) +{ + static const unsigned long _inf = + std::numeric_limits< unsigned long >::max( ); + if( this->m_Collisions == collisions ) + return; + + this->m_Collisions = collisions; + + // Prepare a front graph + unsigned long N = this->m_Collisions.size( ); + _TMatrix dist( N, _TRow( N, _inf ) ); + this->m_FrontPaths = dist; + for( unsigned long i = 0; i < N; ++i ) + { + for( unsigned long j = 0; j < N; ++j ) + { + if( this->m_Collisions[ i ][ j ].second ) + { + dist[ i ][ j ] = 1; + dist[ j ][ i ] = 1; + this->m_FrontPaths[ i ][ j ] = j; + this->m_FrontPaths[ j ][ i ] = i; + + } // fi + + } // rof + dist[ i ][ i ] = 0; + this->m_FrontPaths[ i ][ i ] = i; + + } // rof + + // Use Floyd-Warshall to compute all possible paths between fronts + for( unsigned long k = 0; k < N; ++k ) + { + for( unsigned long i = 0; i < N; ++i ) + { + for( unsigned long j = 0; j < N; ++j ) + { + // WARNING: you don't want a numeric overflow!!! + unsigned long dik = dist[ i ][ k ]; + unsigned long dkj = dist[ k ][ j ]; + unsigned long sum = _inf; + if( dik < _inf && dkj < _inf ) + sum = dik + dkj; + + // Ok, continue Floyd-Warshall + if( sum < dist[ i ][ j ] ) + { + dist[ i ][ j ] = sum; + this->m_FrontPaths[ i ][ j ] = this->m_FrontPaths[ i ][ k ]; + + } // fi + + } // rof + + } // rof + + } // rof + this->Modified( ); +} + +// ------------------------------------------------------------------------- +template< class _TVertex, class _Superclass > +void fpa::Base::MinimumSpanningTree< _TVertex, _Superclass >:: +ClearSeeds( ) +{ + this->m_Seeds.clear( ); + if( this->m_DataObject != NULL ) + this->m_DataObject->Modified( ); +} + +// ------------------------------------------------------------------------- +template< class _TVertex, class _Superclass > +void fpa::Base::MinimumSpanningTree< _TVertex, _Superclass >:: +AddSeed( const _TVertex& seed ) +{ + this->m_Seeds.push_back( seed ); + if( this->m_DataObject != NULL ) + this->m_DataObject->Modified( ); +} + +// ------------------------------------------------------------------------- +template< class _TVertex, class _Superclass > +typename fpa::Base::MinimumSpanningTree< _TVertex, _Superclass >:: +TVertices fpa::Base::MinimumSpanningTree< _TVertex, _Superclass >:: +GetPath( const _TVertex& a ) const +{ + TVertices vertices; + _TVertex it = a; + _TVertex p = this->GetParent( it ); + while( it != p ) + { + vertices.push_back( it ); + it = p; + p = this->GetParent( it ); + + } // elihw + vertices.push_back( it ); + return( vertices ); +} + +// ------------------------------------------------------------------------- +template< class _TVertex, class _Superclass > +typename fpa::Base::MinimumSpanningTree< _TVertex, _Superclass >:: +TVertices fpa::Base::MinimumSpanningTree< _TVertex, _Superclass >:: +GetPath( const _TVertex& a, const _TVertex& b ) const +{ + static const unsigned long _inf = + std::numeric_limits< unsigned long >::max( ); + + TVertices vertices; + TVertices pa = this->GetPath( a ); + TVertices pb = this->GetPath( b ); + if( pa.size( ) > 0 && pb.size( ) > 0 ) + { + // Find front identifiers + unsigned long ia = _inf, ib = _inf; + unsigned long N = this->m_Seeds.size( ); + for( unsigned long i = 0; i < N; ++i ) + { + if( this->m_Seeds[ i ] == pa[ pa.size( ) - 1 ] ) + ia = i; + if( this->m_Seeds[ i ] == pb[ pb.size( ) - 1 ] ) + ib = i; + + } // rof + + // Check if there is a front-jump between given seeds + if( ia != ib ) + { + // Compute front path + std::vector< long > fpath; + fpath.push_back( ia ); + while( ia != ib ) + { + ia = this->m_FrontPaths[ ia ][ ib ]; + fpath.push_back( ia ); + + } // elihw + + // Continue only if both fronts are connected + unsigned int N = fpath.size( ); + if( N > 0 ) + { + // First path: from start vertex to first collision + vertices = this->GetPath( + a, this->m_Collisions[ fpath[ 0 ] ][ fpath[ 1 ] ].first + ); + + // Intermediary paths + for( unsigned int i = 1; i < N - 1; ++i ) + { + TVertices ipath = + this->GetPath( + this->m_Collisions[ fpath[ i ] ][ fpath[ i - 1 ] ].first, + this->m_Collisions[ fpath[ i ] ][ fpath[ i + 1 ] ].first + ); + for( long id = 0; id < ipath.size( ); ++id ) + vertices.push_back( ipath[ id ] ); + + } // rof + + // Final path: from last collision to end point + TVertices lpath = + this->GetPath( + this->m_Collisions[ fpath[ N - 1 ] ][ fpath[ N - 2 ] ].first, b + ); + for( long id = 0; id < lpath.size( ); ++id ) + vertices.push_back( lpath[ id ] ); + + } // fi + } + else + { + // Ignore common part: find common ancestor + long aIt = pa.size( ) - 1; + long bIt = pb.size( ) - 1; + bool cont = true; + while( aIt >= 0 && bIt >= 0 && cont ) + { + cont = ( pa[ aIt ] == pb[ bIt ] ); + aIt--; + bIt--; + + } // elihw + aIt++; + bIt++; + + // Glue both parts + for( long cIt = 0; cIt <= aIt; ++cIt ) + vertices.push_back( pa[ cIt ] ); + for( ; bIt >= 0; --bIt ) + vertices.push_back( pb[ bIt ] ); + + } // fi + + } // fi + return( vertices ); +} + +// ------------------------------------------------------------------------- +template< class _TVertex, class _Superclass > +fpa::Base::MinimumSpanningTree< _TVertex, _Superclass >:: +MinimumSpanningTree( ) + : Superclass( ) +{ +} + +// ------------------------------------------------------------------------- +template< class _TVertex, class _Superclass > +fpa::Base::MinimumSpanningTree< _TVertex, _Superclass >:: +~MinimumSpanningTree( ) +{ +} + +#endif // __fpa__Base__MinimumSpanningTree__hxx__ + +// eof - $RCSfile$ diff --git a/libs/fpa/Image/Dijkstra.h b/libs/fpa/Image/Dijkstra.h new file mode 100644 index 0000000..8516d7b --- /dev/null +++ b/libs/fpa/Image/Dijkstra.h @@ -0,0 +1,91 @@ +// ========================================================================= +// @author Leonardo Florez Valencia +// @email florez-l@javeriana.edu.co +// ========================================================================= + +#ifndef __fpa__Image__Dijkstra__h__ +#define __fpa__Image__Dijkstra__h__ + +#include +#include +#include +#include +#include +#include + +namespace fpa +{ + namespace Image + { + /** + */ + template< class _TInputImage, class _TOutputImage > + class Dijkstra + : public fpa::Base::Dijkstra< fpa::Image::Filter< _TInputImage, _TOutputImage >, fpa::Image::MarksInterface< _TInputImage::ImageDimension >, fpa::Base::SeedsInterface< typename _TInputImage::IndexType, typename _TInputImage::IndexType::LexicographicCompare >, fpa::Image::MinimumSpanningTree< _TInputImage::ImageDimension > > + { + public: + // Interfaces + typedef fpa::Image::Filter< _TInputImage, _TOutputImage > TFilter; + typedef fpa::Image::MarksInterface< _TInputImage::ImageDimension > TMarksInterface; + typedef fpa::Base::SeedsInterface< typename _TInputImage::IndexType, typename _TInputImage::IndexType::LexicographicCompare > TSeedsInterface; + typedef fpa::Image::MinimumSpanningTree< _TInputImage::ImageDimension > TMST; + + // Smart pointers + typedef Dijkstra Self; + typedef fpa::Base::Dijkstra< TFilter, TMarksInterface, TSeedsInterface, TMST > Superclass; + typedef itk::SmartPointer< Self > Pointer; + typedef itk::SmartPointer< const Self > ConstPointer; + + typedef typename TFilter::TInputImage TInputImage; + typedef typename TFilter::TOutputValue TOutputValue; + typedef typename TFilter::TVertex TVertex; + + public: + itkNewMacro( Self ); + itkTypeMacro( fpa::Image::Dijkstra, fpa::Base::Dijkstra ); + + protected: + Dijkstra( ) : Superclass( ) { } + virtual ~Dijkstra( ) { } + + virtual void _ConfigureOutputs( const TOutputValue& init_value ) override + { + this->Superclass::_ConfigureOutputs( init_value ); + + typename TVertex::OffsetType o; + o.Fill( 0 ); + const TInputImage* input = this->GetInput( ); + TMST* mst = this->GetMinimumSpanningTree( ); + mst->CopyInformation( input ); + mst->SetBufferedRegion( input->GetRequestedRegion( ) ); + mst->Allocate( ); + mst->FillBuffer( o ); + } + + virtual void GenerateData( ) override + { + // Configure functors with input image + typedef typename TFilter::TInputImage _TInputIage; + typedef typename TFilter::TOutputValue _TOutputValue; + typedef fpa::Image::Functors::VertexParentBase< _TInputImage, _TOutputValue > _TVFunc; + _TVFunc* vfunc = + dynamic_cast< _TVFunc* >( this->m_VertexFunctor.GetPointer( ) ); + if( vfunc != NULL ) + vfunc->SetImage( this->GetInput( ) ); + + // Ok, continue + this->Superclass::GenerateData( ); + } + + private: + Dijkstra( const Self& other ); + Self& operator=( const Self& other ); + }; + + } // ecapseman + +} // ecapseman + +#endif // __fpa__Image__Dijkstra__h__ + +// eof - $RCSfile$ diff --git a/libs/fpa/Image/Functors/VertexIdentity.h b/libs/fpa/Image/Functors/VertexIdentity.h new file mode 100644 index 0000000..716c2e7 --- /dev/null +++ b/libs/fpa/Image/Functors/VertexIdentity.h @@ -0,0 +1,67 @@ +// ========================================================================= +// @author Leonardo Florez Valencia +// @email florez-l@javeriana.edu.co +// ========================================================================= + +#ifndef __fpa__Image__Functors__VertexIdentity__h__ +#define __fpa__Image__Functors__VertexIdentity__h__ + +#include + +namespace fpa +{ + namespace Image + { + namespace Functors + { + /** + */ + template< class _TInputImage, class _TOutputValue > + class VertexIdentity + : public fpa::Image::Functors::VertexParentBase< _TInputImage, _TOutputValue > + { + public: + typedef _TInputImage TInputImage; + typedef _TOutputValue TOutputValue; + typedef VertexIdentity Self; + typedef itk::SmartPointer< Self > Pointer; + typedef itk::SmartPointer< const Self > ConstPointer; + typedef fpa::Image::Functors::VertexParentBase< TInputImage, TOutputValue > Superclass; + + typedef typename Superclass::TVertex TVertex; + + public: + itkNewMacro( Self ); + itkTypeMacro( + fpa::Image::Functors::VertexIdentity, + fpa::Image::Functors::VertexParentBase + ); + + public: + virtual TOutputValue Evaluate( + const TVertex& a, const TVertex& p + ) const override + { + return( TOutputValue( this->m_Image->GetPixel( a ) ) ); + } + + protected: + VertexIdentity( ) + : Superclass( ) + { } + virtual ~VertexIdentity( ) { } + + private: + VertexIdentity( const Self& other ); + Self& operator=( const Self& other ); + }; + + } // ecapseman + + } // ecapseman + +} // ecapseman + +#endif // __fpa__Image__Functors__VertexIdentity__h__ + +// eof - $RCSfile$ diff --git a/libs/fpa/Image/Functors/VertexParentBase.h b/libs/fpa/Image/Functors/VertexParentBase.h new file mode 100644 index 0000000..20b76e0 --- /dev/null +++ b/libs/fpa/Image/Functors/VertexParentBase.h @@ -0,0 +1,63 @@ +// ========================================================================= +// @author Leonardo Florez Valencia +// @email florez-l@javeriana.edu.co +// ========================================================================= + +#ifndef __fpa__Image__Functors__VertexParentBase__h__ +#define __fpa__Image__Functors__VertexParentBase__h__ + +#include + +namespace fpa +{ + namespace Image + { + namespace Functors + { + /** + */ + template< class _TInputImage, class _TOutputValue > + class VertexParentBase + : public fpa::Base::Functors::VertexParentBase< typename _TInputImage::IndexType, _TOutputValue > + { + public: + typedef _TInputImage TInputImage; + typedef _TOutputValue TOutputValue; + typedef typename TInputImage::IndexType TVertex; + typedef VertexParentBase Self; + typedef itk::SmartPointer< Self > Pointer; + typedef itk::SmartPointer< const Self > ConstPointer; + typedef fpa::Base::Functors::VertexParentBase< TVertex, TOutputValue > Superclass; + + public: + itkTypeMacro( + fpa::Image::Functors::VertexParentBase, + fpa::Base::Functors::VertexParentBase + ); + + itkGetConstObjectMacro( Image, TInputImage ); + itkSetConstObjectMacro( Image, TInputImage ); + + protected: + VertexParentBase( ) + : Superclass( ) + { } + virtual ~VertexParentBase( ) { } + + private: + VertexParentBase( const Self& other ); + Self& operator=( const Self& other ); + + protected: + typename TInputImage::ConstPointer m_Image; + }; + + } // ecapseman + + } // ecapseman + +} // ecapseman + +#endif // __fpa__Image__Functors__VertexParentBase__h__ + +// eof - $RCSfile$ diff --git a/libs/fpa/Image/MinimumSpanningTree.h b/libs/fpa/Image/MinimumSpanningTree.h new file mode 100644 index 0000000..a93157c --- /dev/null +++ b/libs/fpa/Image/MinimumSpanningTree.h @@ -0,0 +1,71 @@ +// ========================================================================= +// @author Leonardo Florez Valencia +// @email florez-l@javeriana.edu.co +// ========================================================================= + +#ifndef __fpa__Image__MinimumSpanningTree__h__ +#define __fpa__Image__MinimumSpanningTree__h__ + +#include +#include + +namespace fpa +{ + namespace Image + { + /** + */ + template< unsigned int _VDim > + class MinimumSpanningTree + : public fpa::Base::MinimumSpanningTree< itk::Index< _VDim >, itk::Image< itk::Offset< _VDim >, _VDim > > + { + public: + typedef itk::Index< _VDim > TVertex; + typedef itk::Image< itk::Offset< _VDim >, _VDim > TBaseImage; + + typedef MinimumSpanningTree Self; + typedef itk::SmartPointer< Self > Pointer; + typedef itk::SmartPointer< const Self > ConstPointer; + typedef fpa::Base::MinimumSpanningTree< TVertex, TBaseImage > Superclass; + + typedef typename Superclass::TCollision TCollision; + typedef typename Superclass::TCollisionsRow TCollisionsRow; + typedef typename Superclass::TCollisions TCollisions; + typedef typename Superclass::TVertices TVertices; + + public: + itkNewMacro( Self ); + itkTypeMacro( + fpa::Image::MinimumSpanningTree, + fpa::Base::MinimumSpanningTree + ); + + public: + virtual TVertex GetParent( const TVertex& v ) const override + { + return( v + this->GetPixel( v ) ); + } + virtual void SetParent( const TVertex& v, const TVertex& p ) override + { + this->SetPixel( v, p - v ); + } + + protected: + MinimumSpanningTree( ) + : Superclass( ) + { } + virtual ~MinimumSpanningTree( ) + { } + + private: + MinimumSpanningTree( const Self& other ); + Self& operator=( const Self& other ); + }; + + } // ecapseman + +} // ecapseman + +#endif // __fpa__Image__MinimumSpanningTree__h__ + +// eof - $RCSfile$ -- 2.45.0