]> Creatis software - FrontAlgorithms.git/commitdiff
...
authorLeonardo Flórez-Valencia <leonardo.florez@gmail.com>
Mon, 19 Jun 2017 20:02:59 +0000 (15:02 -0500)
committerLeonardo Flórez-Valencia <leonardo.florez@gmail.com>
Mon, 19 Jun 2017 20:02:59 +0000 (15:02 -0500)
lib/fpa/Image/Skeleton.hxx
lib/fpa/Image/SkeletonFilter.hxx
lib/fpa/Image/SkeletonReader.h [new file with mode: 0644]
lib/fpa/Image/SkeletonReader.hxx [new file with mode: 0644]
lib/fpa/Image/SkeletonToPolyDataFilter.h [new file with mode: 0644]
lib/fpa/Image/SkeletonToPolyDataFilter.hxx [new file with mode: 0644]
lib/fpa/Image/SkeletonWriter.h [new file with mode: 0644]
lib/fpa/Image/SkeletonWriter.hxx [new file with mode: 0644]
tests/image/SkeletonFilter.cxx

index 99424e9072355788be8e1420b8c41bb04dbbec88..57a418441cd9505fb0cf45bf1752eb80cda27040 100644 (file)
@@ -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( );
 }
 
 // -------------------------------------------------------------------------
index 50a035453cfd8660aec6cb4f6a2d19c485d0043e..224829b93f5a37119a5ceea5f3421b26814b9f43 100644 (file)
@@ -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 (file)
index 0000000..2afce07
--- /dev/null
@@ -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 <itkProcessObject.h>
+
+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 <fpa/Image/SkeletonReader.hxx>
+#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 (file)
index 0000000..95e93fb
--- /dev/null
@@ -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 (file)
index 0000000..f98e8bc
--- /dev/null
@@ -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 <vtkPolyDataAlgorithm.h>
+
+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 <fpa/Image/SkeletonToPolyDataFilter.hxx>
+#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 (file)
index 0000000..9468c4b
--- /dev/null
@@ -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 <vtkCellArray.h>
+#include <vtkInformation.h>
+#include <vtkInformationVector.h>
+#include <vtkSmartPointer.h>
+
+// -------------------------------------------------------------------------
+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 (file)
index 0000000..400d6ac
--- /dev/null
@@ -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 <itkProcessObject.h>
+
+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 <fpa/Image/SkeletonWriter.hxx>
+#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 (file)
index 0000000..c277dac
--- /dev/null
@@ -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 <fstream>
+
+// -------------------------------------------------------------------------
+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$
index ec0aa74aa1c20e850e2ccc71d5232ce42e7437e1..0cdeb6a8a97c948171f98f9566e4640f0f54d704 100644 (file)
@@ -1,9 +1,10 @@
 #include "BaseFunctions.h"
 #include <itkImage.h>
 #include <fpa/Image/SkeletonFilter.h>
+#include <fpa/Image/SkeletonWriter.h>
 
 // -------------------------------------------------------------------------
-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 );
 }