From e8b46a839b86641f48f593d021234566877ac683 Mon Sep 17 00:00:00 2001 From: =?utf8?q?Leonardo=20Fl=C3=B3rez-Valencia?= Date: Mon, 19 Jun 2017 15:02:59 -0500 Subject: [PATCH] ... --- lib/fpa/Image/Skeleton.hxx | 2 +- lib/fpa/Image/SkeletonFilter.hxx | 144 ++++++-------- lib/fpa/Image/SkeletonReader.h | 83 ++++++++ lib/fpa/Image/SkeletonReader.hxx | 209 +++++++++++++++++++++ lib/fpa/Image/SkeletonToPolyDataFilter.h | 68 +++++++ lib/fpa/Image/SkeletonToPolyDataFilter.hxx | 147 +++++++++++++++ lib/fpa/Image/SkeletonWriter.h | 72 +++++++ lib/fpa/Image/SkeletonWriter.hxx | 162 ++++++++++++++++ tests/image/SkeletonFilter.cxx | 30 ++- 9 files changed, 822 insertions(+), 95 deletions(-) create mode 100644 lib/fpa/Image/SkeletonReader.h create mode 100644 lib/fpa/Image/SkeletonReader.hxx create mode 100644 lib/fpa/Image/SkeletonToPolyDataFilter.h create mode 100644 lib/fpa/Image/SkeletonToPolyDataFilter.hxx create mode 100644 lib/fpa/Image/SkeletonWriter.h create mode 100644 lib/fpa/Image/SkeletonWriter.hxx diff --git a/lib/fpa/Image/Skeleton.hxx b/lib/fpa/Image/Skeleton.hxx index 99424e9..57a4184 100644 --- a/lib/fpa/Image/Skeleton.hxx +++ b/lib/fpa/Image/Skeleton.hxx @@ -27,7 +27,7 @@ AddBranch( TPath* path ) this->SetVertex( b, b ); this->AddEdge( a, b, path ); this->AddEdge( b, a, path ); - // TODO: this->Modified( ); + this->Modified( ); } // ------------------------------------------------------------------------- diff --git a/lib/fpa/Image/SkeletonFilter.hxx b/lib/fpa/Image/SkeletonFilter.hxx index 50a0354..224829b 100644 --- a/lib/fpa/Image/SkeletonFilter.hxx +++ b/lib/fpa/Image/SkeletonFilter.hxx @@ -174,24 +174,76 @@ GenerateData( ) mst, dijkstra->GetSkeletonQueue( ) ); - // Create branches - TSkeleton* sk = this->GetOutput( ); + // Compute symbolic branches + typedef std::map< TIndex, TIndex, typename TIndex::LexicographicCompare > _TTags; + _TTags tags, branches; typename std::vector< TIndex >::const_iterator eIt = end_points.begin( ); - std::map< TIndex, unsigned long, typename TIndex::LexicographicCompare > tags; - unsigned long t = 1; for( ; eIt != end_points.end( ); ++eIt ) { // Tag path TIndex it = *eIt; TIndex p = mst->GetParent( it ); - while( it != p ) + typename _TTags::iterator bIt = tags.end( ); + while( it != p && bIt == tags.end( ) ) { - tags[ it ] = t; + typename _TTags::iterator tIt = tags.find( it ); + if( tIt != tags.end( ) ) + { + // Ok, a bifurcation point has been found + // branch1: tIt->second <-> it (ok) + branches[ tIt->second ] = it; + + // branch2: *eit <-> it (ok) + branches[ *eIt ] = it; + + // branch3: it <-> until next bifurcation + bIt = tIt; + } + else + tags[ it ] = *eIt; it = p; p = mst->GetParent( it ); } // elihw - // TODO: More on it + if( bIt != tags.end( ) ) + { + TIndex pTag = bIt->second; + TIndex nTag = bIt->first; + it = bIt->first; + p = it; + while( tags[ it ] == pTag ) + { + tags[ it ] = nTag; + p = it; + it = mst->GetParent( it ); + + } // elihw + branches[ bIt->first ] = p; + } + else + { + tags[ it ] = *eIt; + branches[ *eIt ] = it; + + } // fi + + } // rof + + // Fill full branches + typedef typename _TMST::TVertices _TVertices; + typedef typename TSkeleton::TPath _TPath; + + TSkeleton* sk = this->GetOutput( ); + typename _TTags::const_iterator bIt = branches.begin( ); + for( ; bIt != branches.end( ); ++bIt ) + { + _TVertices v = mst->GetPath( bIt->first, bIt->second ); + typename _TPath::Pointer path = _TPath::New( ); + path->SetReferenceImage( this->GetInput( ) ); + typename _TVertices::const_reverse_iterator vIt = v.rbegin( ); + for( ; vIt != v.rend( ); ++vIt ) + path->AddVertex( *vIt ); + sk->AddBranch( path ); } // rof } @@ -296,84 +348,6 @@ _EndPoints( } // rof } - - -/* TODO - this->m_DistanceMap = TDistanceMap::New( ); - this->m_DistanceMap->InsideIsPositiveOn( ); - this->m_DistanceMap->SquaredDistanceOff( ); - this->m_DistanceMap->UseImageSpacingOn( ); - - template< class _TImage, class _TScalar > - void fpa::Image::SkeletonFilter< _TImage, _TScalar >:: - _Skeleton( const std::vector< TVertex >& end_points, _TAdjacencies& A ) - { - typedef typename TSkeleton::TPath _TPath; - typedef itk::Image< unsigned long, TImage::ImageDimension > _TTagsImage; - - TMST* mst = this->GetMinimumSpanningTree( ); - TSkeleton* skeleton = this->GetSkeleton( ); - - // Tag branches - typename _TTagsImage::Pointer tags = _TTagsImage::New( ); - tags->SetLargestPossibleRegion( mst->GetLargestPossibleRegion( ) ); - tags->SetRequestedRegion( mst->GetRequestedRegion( ) ); - tags->SetBufferedRegion( mst->GetBufferedRegion( ) ); - tags->Allocate( ); - tags->FillBuffer( 0 ); - typename std::vector< TVertex >::const_iterator eIt = end_points.begin( ); - for( ; eIt != end_points.end( ); ++eIt ) - { - TVertex it = *eIt; - TVertex p = mst->GetParent( it ); - while( it != p ) - { - tags->SetPixel( it, tags->GetPixel( it ) + 1 ); - it = p; - p = mst->GetParent( it ); - - } // elihw - tags->SetPixel( it, tags->GetPixel( it ) + 1 ); - - } // rof - - // Build paths (branches) - eIt = end_points.begin( ); - for( ; eIt != end_points.end( ); ++eIt ) - { - TVertex it = *eIt; - TVertex p = mst->GetParent( it ); - TVertex sIdx = it; - typename _TPath::Pointer path = _TPath::New( ); - path->SetReferenceImage( mst ); - while( it != p ) - { - if( tags->GetPixel( sIdx ) != tags->GetPixel( it ) ) - { - // Ok, a new branch has been added - path->AddVertex( it ); - skeleton->AddBranch( path ); - - // Update a new starting index - path = _TPath::New( ); - path->SetReferenceImage( mst ); - sIdx = it; - } - else - path->AddVertex( it ); - it = p; - p = mst->GetParent( it ); - - } // elihw - - // Finally, add last branch - path->AddVertex( it ); - skeleton->AddBranch( path ); - - } // rof - } -*/ - #endif // __fpa__Image__SkeletonFilter__hxx__ // eof - $RCSfile$ diff --git a/lib/fpa/Image/SkeletonReader.h b/lib/fpa/Image/SkeletonReader.h new file mode 100644 index 0000000..2afce07 --- /dev/null +++ b/lib/fpa/Image/SkeletonReader.h @@ -0,0 +1,83 @@ +// ========================================================================= +// @author Leonardo Florez Valencia +// @email florez-l@javeriana.edu.co +// ========================================================================= + +#ifndef __fpa__Image__SkeletonReader__h__ +#define __fpa__Image__SkeletonReader__h__ + +#include + +namespace fpa +{ + namespace Image + { + /** + */ + template< class _TSkeleton > + class SkeletonReader + : public itk::ProcessObject + { + public: + // Basic types + typedef SkeletonReader Self; + typedef itk::ProcessObject Superclass; + typedef itk::SmartPointer< Self > Pointer; + typedef itk::SmartPointer< const Self > ConstPointer; + + typedef _TSkeleton TSkeleton; + + public: + itkNewMacro( Self ); + itkTypeMacro( SkeletonReader, itk::ProcessObject ); + + itkGetConstMacro( FileName, std::string ); + itkSetMacro( FileName, std::string ); + + public: + TSkeleton* GetOutput( ); + TSkeleton* GetOutput( unsigned int i ); + + virtual void GraftOutput( itk::DataObject* out ); + virtual void GraftOutput( + const typename Superclass::DataObjectIdentifierType& key, + itk::DataObject* out + ); + virtual void GraftNthOutput( unsigned int i, itk::DataObject* out ); + virtual itk::DataObject::Pointer MakeOutput( + itk::ProcessObject::DataObjectPointerArraySizeType i + ) override; + + virtual void Update( ) override + { this->GenerateData( ); } + + protected: + SkeletonReader( ); + virtual ~SkeletonReader( ); + + virtual void GenerateData( ) override; + + // Do nothing + virtual void GenerateOutputInformation( ) override + { } + + private: + // Purposely not implemented + SkeletonReader( const Self& ); + void operator=( const Self& ); + + protected: + std::string m_FileName; + }; + + } // ecapseman + +} // ecapseman + +#ifndef ITK_MANUAL_INSTANTIATION +# include +#endif // ITK_MANUAL_INSTANTIATION + +#endif // __fpa__Image__SkeletonReader__h__ + +// eof - $RCSfile$ diff --git a/lib/fpa/Image/SkeletonReader.hxx b/lib/fpa/Image/SkeletonReader.hxx new file mode 100644 index 0000000..95e93fb --- /dev/null +++ b/lib/fpa/Image/SkeletonReader.hxx @@ -0,0 +1,209 @@ +// ========================================================================= +// @author Leonardo Florez Valencia +// @email florez-l@javeriana.edu.co +// ========================================================================= + +#ifndef __fpa__Image__SkeletonReader__hxx__ +#define __fpa__Image__SkeletonReader__hxx__ + +// ------------------------------------------------------------------------- +template< class _TSkeleton > +_TSkeleton* fpa::Image::SkeletonReader< _TSkeleton >:: +GetOutput( ) +{ + return( + itkDynamicCastInDebugMode< TSkeleton* >( this->GetPrimaryOutput( ) ) + ); +} + +// ------------------------------------------------------------------------- +template< class _TSkeleton > +_TSkeleton* fpa::Image::SkeletonReader< _TSkeleton >:: +GetOutput( unsigned int i ) +{ + return( + itkDynamicCastInDebugMode< TSkeleton* >( + this->itk::ProcessObject::GetOutput( i ) + ) + ); +} + +// ------------------------------------------------------------------------- +template< class _TSkeleton > +void fpa::Image::SkeletonReader< _TSkeleton >:: +GraftOutput( itk::DataObject* out ) +{ + this->GraftNthOutput( 0, out ); +} + +// ------------------------------------------------------------------------- +template< class _TSkeleton > +void fpa::Image::SkeletonReader< _TSkeleton >:: +GraftOutput( + const typename Superclass::DataObjectIdentifierType& key, + itk::DataObject* out + ) +{ + if( out == NULL ) + { + itkExceptionMacro( + << "Requested to graft output that is a NULL pointer" + ); + + } // fi + itk::DataObject* output = this->itk::ProcessObject::GetOutput( key ); + output->Graft( out ); +} + +// ------------------------------------------------------------------------- +template< class _TSkeleton > +void fpa::Image::SkeletonReader< _TSkeleton >:: +GraftNthOutput( unsigned int i, itk::DataObject* out ) +{ + if( i >= this->GetNumberOfIndexedOutputs( ) ) + { + itkExceptionMacro( + << "Requested to graft output " << i + << " but this filter only has " + << this->GetNumberOfIndexedOutputs( ) + << " indexed Outputs." + ); + + } // fi + this->GraftOutput( this->MakeNameFromOutputIndex( i ), out ); +} + +// ------------------------------------------------------------------------- +template< class _TSkeleton > +itk::DataObject::Pointer +fpa::Image::SkeletonReader< _TSkeleton >:: +MakeOutput( itk::ProcessObject::DataObjectPointerArraySizeType i ) +{ + return( TSkeleton::New( ).GetPointer( ) ); +} + +// ------------------------------------------------------------------------- +template< class _TSkeleton > +fpa::Image::SkeletonReader< _TSkeleton >:: +SkeletonReader( ) + : Superclass( ) +{ + typename TSkeleton::Pointer out = + static_cast< TSkeleton* >( this->MakeOutput( 0 ).GetPointer( ) ); + this->itk::ProcessObject::SetNumberOfRequiredInputs( 0 ); + this->itk::ProcessObject::SetNumberOfRequiredOutputs( 1 ); + this->itk::ProcessObject::SetNthOutput( 0, out.GetPointer( ) ); +} + +// ------------------------------------------------------------------------- +template< class _TSkeleton > +fpa::Image::SkeletonReader< _TSkeleton >:: +~SkeletonReader( ) +{ +} + +// ------------------------------------------------------------------------- +template< class _TSkeleton > +void fpa::Image::SkeletonReader< _TSkeleton >:: +GenerateData( ) +{ + typedef typename TSkeleton::TPath _TPath; + typedef typename _TPath::TIndex _TIndex; + typedef typename _TPath::TSpacing _TSpacing; + typedef typename _TPath::TPoint _TPoint; + typedef typename _TPath::TDirection _TDirection; + + std::string buffer; + std::ifstream file_stream( this->m_FileName.c_str( ) ); + if( !file_stream ) + { + itkExceptionMacro( + << "Error reading skeleton from \"" << this->m_FileName << "\"" + ); + return; + + } // fi + file_stream.seekg( 0, std::ios::end ); + buffer.reserve( ( unsigned int )( file_stream.tellg( ) ) ); + file_stream.seekg( 0, std::ios::beg ); + buffer.assign( + ( std::istreambuf_iterator< char >( file_stream ) ), + std::istreambuf_iterator< char >( ) + ); + file_stream.close( ); + + std::istringstream in( buffer ); + unsigned int dim; + in >> dim; + if( dim != TSkeleton::Dimension ) + { + itkExceptionMacro( + << "Mismatched skeletons dimension: " << dim + << " != " << TSkeleton::Dimension + ); + return; + + } // fi + + // Read spatial parameters + _TSpacing spa; + _TDirection dir; + _TPoint ori; + for( unsigned int d = 0; d < dim; ++d ) + in >> spa[ d ]; + for( unsigned int d = 0; d < dim; ++d ) + for( unsigned int e = 0; e < dim; ++e ) + in >> dir[ d ][ e ]; + for( unsigned int d = 0; d < dim; ++d ) + in >> ori[ d ]; + + // Read end-points, just to ignore + unsigned int n; + in >> n; + for( unsigned int i = 0; i < n; ++i ) + { + _TIndex idx; + for( unsigned int d = 0; d < dim; ++d ) + in >> idx[ d ]; + + } // rof + + // Read bifurcations, just to ignore + in >> n; + for( unsigned int i = 0; i < n; ++i ) + { + _TIndex idx; + for( unsigned int d = 0; d < dim; ++d ) + in >> idx[ d ]; + + } // rof + + // Read paths + TSkeleton* out = this->GetOutput( ); + unsigned int nPaths; + in >> nPaths; + for( unsigned int p = 0; p < nPaths; ++p ) + { + typename _TPath::Pointer path = _TPath::New( ); + path->SetSpacing( spa ); + path->SetOrigin( ori ); + path->SetDirection( dir ); + + unsigned long pathSize; + in >> pathSize; + for( unsigned long id = 0; id < pathSize; ++id ) + { + _TIndex idx; + for( unsigned int d = 0; d < dim; ++d ) + in >> idx[ d ]; + path->AddVertex( idx ); + + } // rof + out->AddBranch( path ); + + } // rof +} + +#endif // __fpa__Image__SkeletonReader__hxx__ + +// eof - $RCSfile$ diff --git a/lib/fpa/Image/SkeletonToPolyDataFilter.h b/lib/fpa/Image/SkeletonToPolyDataFilter.h new file mode 100644 index 0000000..f98e8bc --- /dev/null +++ b/lib/fpa/Image/SkeletonToPolyDataFilter.h @@ -0,0 +1,68 @@ +// ========================================================================= +// @author Leonardo Florez Valencia +// @email florez-l@javeriana.edu.co +// ========================================================================= + +#ifndef __fpa__Image__SkeletonToPolyDataFilter__h__ +#define __fpa__Image__SkeletonToPolyDataFilter__h__ + +#include + +namespace fpa +{ + namespace Image + { + /** + */ + template< class _TSkeleton > + class SkeletonToPolyDataFilter + : public vtkPolyDataAlgorithm + { + public: + typedef SkeletonToPolyDataFilter Self; + typedef _TSkeleton TSkeleton; + + public: + vtkTypeMacro( SkeletonToPolyDataFilter, vtkPolyDataAlgorithm ); + + public: + static Self* New( ); + + const TSkeleton* GetInput( ) const; + void SetInput( const TSkeleton* sk ); + + protected: + SkeletonToPolyDataFilter( ); + virtual ~SkeletonToPolyDataFilter( ); + + int RequestData( + vtkInformation* information, + vtkInformationVector** input, + vtkInformationVector* output + ); + int RequestInformation( + vtkInformation* information, + vtkInformationVector** input, + vtkInformationVector* output + ); + + private: + // Purposely not implemented + SkeletonToPolyDataFilter( const Self& ); + void operator=( const Self& ); + + protected: + const TSkeleton* m_Skeleton; + }; + + } // ecapseman + +} // ecapseman + +#ifndef ITK_MANUAL_INSTANTIATION +# include +#endif // ITK_MANUAL_INSTANTIATION + +#endif // __fpa__Image__SkeletonToPolyDataFilter__h__ + +// eof - $RCSfile$ diff --git a/lib/fpa/Image/SkeletonToPolyDataFilter.hxx b/lib/fpa/Image/SkeletonToPolyDataFilter.hxx new file mode 100644 index 0000000..9468c4b --- /dev/null +++ b/lib/fpa/Image/SkeletonToPolyDataFilter.hxx @@ -0,0 +1,147 @@ +// ========================================================================= +// @author Leonardo Florez Valencia +// @email florez-l@javeriana.edu.co +// ========================================================================= + +#ifndef __fpa__Image__SkeletonToPolyDataFilter__hxx__ +#define __fpa__Image__SkeletonToPolyDataFilter__hxx__ + +#include +#include +#include +#include + +// ------------------------------------------------------------------------- +template< class _TSkeleton > +typename fpa::Image::SkeletonToPolyDataFilter< _TSkeleton >:: +Self* fpa::Image::SkeletonToPolyDataFilter< _TSkeleton >:: +New( ) +{ + return( new Self( ) ); +} + +// ------------------------------------------------------------------------- +template< class _TSkeleton > +const typename +fpa::Image::SkeletonToPolyDataFilter< _TSkeleton >:: +TSkeleton* fpa::Image::SkeletonToPolyDataFilter< _TSkeleton >:: +GetInput( ) const +{ + return( this->m_Skeleton ); +} + +// ------------------------------------------------------------------------- +template< class _TSkeleton > +void fpa::Image::SkeletonToPolyDataFilter< _TSkeleton >:: +SetInput( const TSkeleton* sk ) +{ + if( this->m_Skeleton != sk ) + { + this->m_Skeleton = sk; + this->Modified( ); + + } // fi +} + +// ------------------------------------------------------------------------- +template< class _TSkeleton > +fpa::Image::SkeletonToPolyDataFilter< _TSkeleton >:: +SkeletonToPolyDataFilter( ) + : vtkPolyDataAlgorithm( ), + m_Skeleton( NULL ) +{ + this->SetNumberOfInputPorts( 0 ); +} + +// ------------------------------------------------------------------------- +template< class _TSkeleton > +fpa::Image::SkeletonToPolyDataFilter< _TSkeleton >:: +~SkeletonToPolyDataFilter( ) +{ +} + +// ------------------------------------------------------------------------- +template< class _TSkeleton > +int fpa::Image::SkeletonToPolyDataFilter< _TSkeleton >:: +RequestData( + vtkInformation* information, + vtkInformationVector** input, + vtkInformationVector* output + ) +{ + typedef typename _TSkeleton::TPath _TPath; + static const unsigned int dim = _TPath::PathDimension; + + if( this->m_Skeleton == NULL ) + return( 0 ); + + // Get output + vtkInformation* info = output->GetInformationObject( 0 ); + vtkPolyData* out = vtkPolyData::SafeDownCast( + info->Get( vtkDataObject::DATA_OBJECT( ) ) + ); + + // Prepare data + out->SetPoints( vtkSmartPointer< vtkPoints >::New( ) ); + out->SetVerts( vtkSmartPointer< vtkCellArray >::New( ) ); + out->SetLines( vtkSmartPointer< vtkCellArray >::New( ) ); + out->SetPolys( vtkSmartPointer< vtkCellArray >::New( ) ); + out->SetStrips( vtkSmartPointer< vtkCellArray >::New( ) ); + vtkPoints* points = out->GetPoints( ); + vtkCellArray* lines = out->GetLines( ); + + // Assign all data + typename TMatrix::const_iterator mIt = this->m_Skeleton->BeginEdgesRows( ); + for( ; mIt != this->m_Skeleton->EndEdgesRows( ); ++mIt ) + { + // TODO: mIt->first; --> this is the row index. <-- + typename TMatrixRow::const_iterator rIt = mIt->second.begin( ); + for( ; rIt != mIt->second.end( ); ++rIt ) + { + // TODO: rIt->first; --> this is the column index. + typename TEdges::const_iterator eIt = rIt->second.begin( ); + for( ; eIt != rIt->second.end( ); ++eIt ) + { + _TPath* path = *eIt; + for( unsigned long i = 0; i < path->GetSize( ); ++i ) + { + auto pnt = path->GetPoint( i ); + if( dim == 1 ) + points->InsertNextPoint( pnt[ 0 ], 0, 0 ); + else if( dim == 2 ) + points->InsertNextPoint( pnt[ 0 ], pnt[ 1 ], 0 ); + else + points->InsertNextPoint( pnt[ 0 ], pnt[ 1 ], pnt[ 2 ] ); + if( i > 0 ) + { + lines->InsertNextCell( 2 ); + lines->InsertCellPoint( points->GetNumberOfPoints( ) - 2 ); + lines->InsertCellPoint( points->GetNumberOfPoints( ) - 1 ); + + } // fi + + } // rof + + } // rof + + } // rof + + } // rof + return( 1 ); +} + +// ------------------------------------------------------------------------- +template< class _TSkeleton > +int fpa::Image::SkeletonToPolyDataFilter< _TSkeleton >:: +RequestInformation( + vtkInformation* information, + vtkInformationVector** input, + vtkInformationVector* output + ) +{ + return( 1 ); +} + +#endif // __fpa__Image__SkeletonToPolyDataFilterFilter__hxx__ + +// eof - $RCSfile$ diff --git a/lib/fpa/Image/SkeletonWriter.h b/lib/fpa/Image/SkeletonWriter.h new file mode 100644 index 0000000..400d6ac --- /dev/null +++ b/lib/fpa/Image/SkeletonWriter.h @@ -0,0 +1,72 @@ +// ========================================================================= +// @author Leonardo Florez Valencia +// @email florez-l@javeriana.edu.co +// ========================================================================= + +#ifndef __fpa__Image__SkeletonWriter__h__ +#define __fpa__Image__SkeletonWriter__h__ + +#include + +namespace fpa +{ + namespace Image + { + /** + */ + template< class _TSkeleton > + class SkeletonWriter + : public itk::ProcessObject + { + public: + // Basic types + typedef SkeletonWriter Self; + typedef itk::ProcessObject Superclass; + typedef itk::SmartPointer< Self > Pointer; + typedef itk::SmartPointer< const Self > ConstPointer; + + typedef _TSkeleton TSkeleton; + typedef typename TSkeleton::TEdges TEdges; + typedef typename TSkeleton::TMatrix TMatrix; + typedef typename TSkeleton::TMatrixRow TMatrixRow; + typedef typename TSkeleton::TPath TPath; + typedef typename TSkeleton::TVertex TVertex; + + public: + itkNewMacro( Self ); + itkTypeMacro( SkeletonWriter, itk::ProcessObject ); + + itkGetConstMacro( FileName, std::string ); + itkSetMacro( FileName, std::string ); + + public: + const TSkeleton* GetInput( ) const; + void SetInput( const TSkeleton* skeleton ); + virtual void Update( ) override; + + protected: + SkeletonWriter( ); + virtual ~SkeletonWriter( ); + + virtual void GenerateData( ) override; + + private: + // Purposely not implemented + SkeletonWriter( const Self& ); + void operator=( const Self& ); + + protected: + std::string m_FileName; + }; + + } // ecapseman + +} // ecapseman + +#ifndef ITK_MANUAL_INSTANTIATION +# include +#endif // ITK_MANUAL_INSTANTIATION + +#endif // __fpa__Image__SkeletonWriter__h__ + +// eof - $RCSfile$ diff --git a/lib/fpa/Image/SkeletonWriter.hxx b/lib/fpa/Image/SkeletonWriter.hxx new file mode 100644 index 0000000..c277dac --- /dev/null +++ b/lib/fpa/Image/SkeletonWriter.hxx @@ -0,0 +1,162 @@ +// ========================================================================= +// @author Leonardo Florez Valencia +// @email florez-l@javeriana.edu.co +// ========================================================================= + +#ifndef __fpa__Image__SkeletonWriter__hxx__ +#define __fpa__Image__SkeletonWriter__hxx__ + +#include + +// ------------------------------------------------------------------------- +template< class _TSkeleton > +const _TSkeleton* fpa::Image::SkeletonWriter< _TSkeleton >:: +GetInput( ) const +{ + return( + dynamic_cast< const TSkeleton* >( + this->itk::ProcessObject::GetInput( 0 ) + ) + ); +} + +// ------------------------------------------------------------------------- +template< class _TSkeleton > +void fpa::Image::SkeletonWriter< _TSkeleton >:: +SetInput( const _TSkeleton* skeleton ) +{ + this->itk::ProcessObject::SetNthInput( + 0, const_cast< TSkeleton* >( skeleton ) + ); +} + +// ------------------------------------------------------------------------- +template< class _TSkeleton > +void fpa::Image::SkeletonWriter< _TSkeleton >:: +Update( ) +{ + TSkeleton* input = const_cast< TSkeleton* >( this->GetInput( ) ); + if( input != NULL ) + { + input->UpdateOutputInformation( ); + input->UpdateOutputData( ); + this->GenerateData( ); + this->ReleaseInputs( ); + + } // fi +} + +// ------------------------------------------------------------------------- +template< class _TSkeleton > +fpa::Image::SkeletonWriter< _TSkeleton >:: +SkeletonWriter( ) + : Superclass( ), + m_FileName( "" ) +{ + this->SetNumberOfRequiredInputs( 1 ); +} + +// ------------------------------------------------------------------------- +template< class _TSkeleton > +fpa::Image::SkeletonWriter< _TSkeleton >:: +~SkeletonWriter( ) +{ +} + +// ------------------------------------------------------------------------- +template< class _TSkeleton > +void fpa::Image::SkeletonWriter< _TSkeleton >:: +GenerateData( ) +{ + const TSkeleton* sk = this->GetInput( ); + typename TMatrix::const_iterator mIt = sk->BeginEdgesRows( ); + typename TMatrixRow::const_iterator rIt = mIt->second.begin( ); + typename TEdges::const_iterator eIt = rIt->second.begin( ); + const TPath* path = *eIt; + + // Write base information + std::stringstream out1, out2; + out1 << TSkeleton::Dimension << std::endl; + typename TPath::TSpacing spa = path->GetSpacing( ); + for( unsigned int d = 0; d < TSkeleton::Dimension; ++d ) + out1 << spa[ d ] << " "; + out1 << std::endl; + typename TPath::TDirection dir = path->GetDirection( ); + for( unsigned int d = 0; d < TSkeleton::Dimension; ++d ) + for( unsigned int e = 0; e < TSkeleton::Dimension; ++e ) + out1 << dir[ d ][ e ] << " "; + out1 << std::endl; + typename TPath::TPoint ori = path->GetOrigin( ); + for( unsigned int d = 0; d < TSkeleton::Dimension; ++d ) + out1 << ori[ d ] << " "; + out1 << std::endl; + + // End points + std::vector< TVertex > end_points = sk->GetEndPoints( ); + out1 << end_points.size( ) << std::endl; + typename std::vector< TVertex >::const_iterator epIt = end_points.begin( ); + for( ; epIt != end_points.end( ); ++epIt ) + { + for( unsigned int d = 0; d < TSkeleton::Dimension; ++d ) + out1 << ( *epIt )[ d ] << " "; + out1 << std::endl; + + } // rof + + // Bifurcations + std::vector< TVertex > bifurcations = sk->GetBifurcations( ); + out1 << bifurcations.size( ) << std::endl; + typename std::vector< TVertex >::const_iterator bIt = bifurcations.begin( ); + for( ; bIt != bifurcations.end( ); ++bIt ) + { + for( unsigned int d = 0; d < TSkeleton::Dimension; ++d ) + out1 << ( *bIt )[ d ] << " "; + out1 << std::endl; + + } // rof + + // Write paths + unsigned long pathCount = 0; + mIt = sk->BeginEdgesRows( ); + for( ; mIt != sk->EndEdgesRows( ); ++mIt ) + { + typename TMatrixRow::const_iterator rIt = mIt->second.begin( ); + for( ; rIt != mIt->second.end( ); ++rIt ) + { + typename TEdges::const_iterator eIt = rIt->second.begin( ); + for( ; eIt != rIt->second.end( ); ++eIt ) + { + TPath* path = *eIt; + pathCount++; + unsigned int size = path->GetSize( ); + out2 << size << std::endl; + for( unsigned int i = 0; i < path->GetSize( ); ++i ) + { + TVertex v = path->GetVertex( i ); + for( unsigned int d = 0; d < TSkeleton::Dimension; ++d ) + out2 << v[ d ] << " "; + + } // rof + out2 << std::endl; + + } // rof + + } // rof + + } // rof + out1 << pathCount << std::endl << out2.str( ); + + // Real write + std::ofstream file_stream( this->m_FileName.c_str( ), std::ofstream::binary ); + if( !file_stream ) + itkExceptionMacro( + << "Unable to write skeleton to \"" + << this->m_FileName + << "\"" + ); + file_stream.write( out1.str( ).c_str( ), out1.str( ).size( ) ); +} + +#endif // __fpa__Image__SkeletonWriter__hxx__ + +// eof - $RCSfile$ diff --git a/tests/image/SkeletonFilter.cxx b/tests/image/SkeletonFilter.cxx index ec0aa74..0cdeb6a 100644 --- a/tests/image/SkeletonFilter.cxx +++ b/tests/image/SkeletonFilter.cxx @@ -1,9 +1,10 @@ #include "BaseFunctions.h" #include #include +#include // ------------------------------------------------------------------------- -const unsigned int Dim = 3; +const unsigned int Dim = 2; typedef short TPixel; typedef itk::Image< TPixel, Dim > TInputImage; @@ -56,18 +57,29 @@ int main( int argc, char* argv[] ) else filter->SeedFromMaximumDistanceOn( ); + // Configure distance map + filter->GetDistanceMap( )->InsideIsPositiveOn( ); + filter->GetDistanceMap( )->SquaredDistanceOff( ); + filter->GetDistanceMap( )->UseImageSpacingOn( ); + // Update filter->Update( ); // Save results - /* TODO - std::string err1 = - fpa::tests::image::Write( filter->GetOutput( ), output_image_filename ); - std::string err2 = - fpa::tests::image::Write( filter->GetMarks( ), output_marks_filename ); - if( err1 != "" ) std::cerr << err1 << std::endl; - if( err2 != "" ) std::cerr << err2 << std::endl; - */ + fpa::Image::SkeletonWriter< TFilter::TSkeleton >::Pointer writer = + fpa::Image::SkeletonWriter< TFilter::TSkeleton >::New( ); + writer->SetInput( filter->GetOutput( ) ); + writer->SetFileName( output_skeleton_filename ); + try + { + writer->Update( ); + } + catch( std::exception& err ) + { + std::cerr << "Error caught: " << err.what( ) << std::endl; + return( 1 ); + + } // yrt return( 0 ); } -- 2.45.1