]> Creatis software - FrontAlgorithms.git/commitdiff
...
authorLeonardo Flórez-Valencia <leonardo.florez@gmail.com>
Mon, 15 May 2017 01:51:22 +0000 (20:51 -0500)
committerLeonardo Flórez-Valencia <leonardo.florez@gmail.com>
Mon, 15 May 2017 01:51:22 +0000 (20:51 -0500)
examples/CMakeLists.txt
examples/SkeletonFilter.cxx [new file with mode: 0644]
lib/fpa/Base/Graph.h [new file with mode: 0644]
lib/fpa/Base/Graph.hxx [new file with mode: 0644]
lib/fpa/Base/PolyLineParametricPath.h [new file with mode: 0644]
lib/fpa/Base/PolyLineParametricPath.hxx [new file with mode: 0644]
lib/fpa/Base/SeedsInterface.h
lib/fpa/Base/Skeleton.h [new file with mode: 0644]
lib/fpa/Base/Skeleton.hxx [new file with mode: 0644]
lib/fpa/Image/SkeletonFilter.h [new file with mode: 0644]
lib/fpa/Image/SkeletonFilter.hxx [new file with mode: 0644]

index 4feb556f0db0c20ac5c29385713dcd31e7c34ff3..72d9ad2c64aae2fc9e57083ebd496fd63ba3adf5 100644 (file)
@@ -6,6 +6,7 @@ if(BUILD_EXAMPLES)
     RegionGrow_BinaryThreshold
     RegionGrow_Mori
     Dijkstra_Maurer
+    SkeletonFilter
     #CreateMoriInputImage
     #BronchiiInitialSegmentationWithMori
     #BronchiiInitialSegmentationWithBinaryThresholdRegionGrow
diff --git a/examples/SkeletonFilter.cxx b/examples/SkeletonFilter.cxx
new file mode 100644 (file)
index 0000000..25d54c4
--- /dev/null
@@ -0,0 +1,125 @@
+
+#include <chrono>
+#include <iomanip>
+#include <itkImage.h>
+#include <itkImageFileReader.h>
+
+#include <fpa/Image/SkeletonFilter.h>
+
+/* TODO
+   #include <itkCommand.h>
+   #include <itkImageFileWriter.h>
+   #include <itkBinaryThresholdImageFilter.h>
+   #include <fpa/Image/MoriRegionGrow.h>
+*/
+
+// -------------------------------------------------------------------------
+static const unsigned int VDim = 3;
+typedef unsigned char                                 TPixel;
+typedef double                                        TScalar;
+typedef itk::Image< TPixel, VDim >                    TImage;
+typedef itk::ImageFileReader< TImage >                TReader;
+typedef fpa::Image::SkeletonFilter< TImage, TScalar > TFilter;
+
+/* TODO
+   typedef itk::ImageFileWriter< TImage >                    TWriter;
+   typedef fpa::Image::MoriRegionGrow< TImage, TImage >      TFilter;
+   typedef itk::BinaryThresholdImageFilter< TImage, TImage > TThreshold;
+*/
+
+// -------------------------------------------------------------------------
+int main( int argc, char* argv[] )
+{
+  // Get arguments
+  if( argc != 3 && argc != 3 + VDim )
+  {
+    std::cerr
+      << "Usage: " << argv[ 0 ]
+      << " input_image output_skeleton [";
+    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_skeleton_filename = argv[ 2 ];
+
+  TReader::Pointer reader = TReader::New( );
+  reader->SetFileName( input_image_filename );
+  try
+  {
+    reader->Update( );
+  }
+  catch( std::exception& err )
+  {
+    std::cerr << "ERROR: " << err.what( ) << std::endl;
+    return( 1 );
+
+  } // yrt
+
+  TFilter::Pointer filter = TFilter::New( );
+  filter->SetInput( reader->GetOutput( ) );
+
+  /* TODO
+     filter->SetThresholdRange( lower, upper, delta );
+     TImage::PointType pnt;
+     for( int i = 0; i < VDim; ++i )
+     pnt[ i ] = std::atof( argv[ i + 6 ] );
+
+     TImage::IndexType seed;
+     if( !( reader->GetOutput( )->TransformPhysicalPointToIndex( pnt, seed ) ) )
+     {
+     std::cerr << "ERROR: seed outside image." << std::endl;
+     return( 1 );
+
+     } // fi
+     filter->AddSeed( seed );
+
+     std::chrono::time_point< std::chrono::system_clock > start, end;
+     start = std::chrono::system_clock::now( );
+     filter->Update( );
+     end = std::chrono::system_clock::now( );
+     std::chrono::duration< double > elapsed_seconds = end - start;
+
+     TThreshold::Pointer threshold = TThreshold::New( );
+     threshold->SetInput( filter->GetOutput( ) );
+     threshold->SetInsideValue( 255 );
+     threshold->SetOutsideValue( 0 );
+     threshold->SetLowerThreshold( 0 );
+     threshold->SetUpperThreshold( filter->GetOptimumThreshold( ) );
+  
+     TWriter::Pointer writer = TWriter::New( );
+     writer->SetInput( threshold->GetOutput( ) );
+     writer->SetFileName( output_image_filename );
+     try
+     {
+     writer->Update( );
+     }
+     catch( std::exception& err )
+     {
+     std::cerr << "ERROR: " << err.what( ) << std::endl;
+     return( 1 );
+
+     } // yrt
+
+     // Show data
+     TFilter::TCurve curve = filter->GetCurve( );
+     for( TFilter::TCurveData data: curve )
+     {
+     std::cout << data.XValue << " " << data.YValue << " " << data.Diff1 << std::endl;
+     }
+     std::cout
+     << std::endl
+     << "# Opt: "
+     << curve[ filter->GetOptimumThreshold( ) ].XValue
+     << "("
+     << filter->GetOptimumThreshold( )
+     << ")"
+     << std::endl;
+     std::cout << "Time: " << elapsed_seconds.count( ) << "s" << std::endl;
+  */
+  return( 0 );
+}
+
+// eof - $RCSfile$
diff --git a/lib/fpa/Base/Graph.h b/lib/fpa/Base/Graph.h
new file mode 100644 (file)
index 0000000..41b2fb9
--- /dev/null
@@ -0,0 +1,170 @@
+// =========================================================================
+// @author Leonardo Florez Valencia
+// @email florez-l@javeriana.edu.co
+// =========================================================================
+
+#ifndef __fpa__Base__Graph__h__
+#define __fpa__Base__Graph__h__
+
+#include <map>
+#include <set>
+#include <vector>
+#include <itkDataObject.h>
+#include <itkObjectFactory.h>
+
+namespace fpa
+{
+  namespace Base
+  {
+    /** \brief A generic graph with templated index types.
+     *
+     *  @param _TVertex Vertex type.
+     *  @param _TCost   Cost type.
+     *  @param _TIndex  Index type (it should be a strict weak ordering type).
+     */
+    template< class _TVertex, class _TCost, class _TIndex = unsigned long, class _TIndexCompare = std::less< _TIndex > >
+    class Graph
+      : public itk::DataObject
+    {
+    public:
+      typedef Graph                           Self;
+      typedef itk::DataObject                 Superclass;
+      typedef itk::SmartPointer< Self >       Pointer;
+      typedef itk::SmartPointer< const Self > ConstPointer;
+
+      typedef _TVertex       TVertex;
+      typedef _TCost         TCost;
+      typedef _TIndex        TIndex;
+      typedef _TIndexCompare TIndexCompare;
+
+      // Base types
+      typedef std::map< TIndex, TVertex, TIndexCompare >    TVertices;
+      typedef std::vector< TCost >                          TEdges;
+      typedef std::map< TIndex, TEdges, TIndexCompare >     TMatrixRow;
+      typedef std::map< TIndex, TMatrixRow, TIndexCompare > TMatrix;
+
+    public:
+      itkNewMacro( Self );
+      itkTypeMacro( Graph, itk::DataObject );
+
+    public:
+      /*! \brief Iterators over vertices.
+       *         These allow you to iterate over all of graph's vertices.
+       *
+       * Typical iteration should be done as:
+       *
+       *  TGraph g;
+       *  ...
+       *  TGraph::TVertices::[const_]iterator vIt = g.BeginVertices( );
+       *  for( ; vIt != g.EndVertices( ); ++vIt )
+       *  {
+       *    vIt->first;  --> this is the vertex's index <--
+       *    vIt->second; --> this is the vertex's value <--
+       *  }
+       */
+      inline typename TVertices::iterator BeginVertices( )
+        { return( this->m_Vertices.begin( ) ); }
+      inline typename TVertices::iterator EndVertices( )
+        { return( this->m_Vertices.end( ) ); }
+      inline typename TVertices::const_iterator BeginVertices( ) const
+        { return( this->m_Vertices.begin( ) ); }
+      inline typename TVertices::const_iterator EndVertices( ) const
+        { return( this->m_Vertices.end( ) ); }
+
+      /*! \brief Iterators over edges.
+       *         These allow you to iterate over all of graph's edges.
+       *
+       * Typical iteration should be done as:
+       *
+       *  TGraph g;
+       *  ...
+       *  TGraph::TMatrix::[const_]iterator mIt = g.BeginEdgesRows( );
+       *  for( ; mIt != g.EndEdgesRows( ); ++mIt )
+       *  {
+       *    mIt->first; --> this is the row index. <--
+       *    TGraph::TMatrixRow::[const_]iterator rIt = mIt->second.begin( );
+       *    for( ; rIt != mIt->second.end( ); ++rIt )
+       *    {
+       *      rIt->first;  --> this is the column index.
+       *      TGraph::TEdges::[const_]iterator eIt = rIt->second.begin( );
+       *      for( ; eIt != rIt->second.end( ); ++eIt )
+       *        *eIt; --> this is the cost between mIt->first and rIt->first
+       *    }
+       *  }
+       */
+      inline typename TMatrix::iterator BeginEdgesRows( )
+        { return( this->m_Matrix.begin( ) ); }
+      inline typename TMatrix::iterator EndEdgetsRows( )
+        { return( this->m_Matrix.end( ) ); }
+      inline typename TMatrix::const_iterator BeginEdgesRows( ) const
+        { return( this->m_Matrix.begin( ) ); }
+      inline typename TMatrix::const_iterator EndEdgesRows( ) const
+        { return( this->m_Matrix.end( ) ); }
+
+      /*! \brief Clear all vertices and edges.
+       */
+      void Clear( );
+
+      /*! \brief Clear all edges.
+       */
+      inline void ClearEdges( )
+        { this->m_Matrix.clear( ); }
+
+      /*! \brief Vertex manipulation methods.
+       *         Names are self-explanatory.
+       */
+      inline bool HasVertexIndex( const TIndex& i ) const
+        { return( this->m_Vertices.find( i ) != this->m_Vertices.end( ) ); }
+      inline void SetVertex( const TIndex& index, TVertex& vertex )
+        { this->m_Vertices[ index ] = vertex; }
+      inline TVertex& GetVertex( const TIndex& index )
+        { return( this->m_Vertices[ index ] ); }
+      inline const TVertex& GetVertex( const TIndex& index ) const
+        { return( this->m_Vertices[ index ] ); }
+      bool RenameVertex( const TIndex& old_index, const TIndex& new_index );
+      void RemoveVertex( const TIndex& index );
+
+      /*! \brief Edge manipulation methods.
+       *         Names are self-explanatory.
+       */
+      inline void AddEdge( const TIndex& orig, const TIndex& dest, const TCost& cost )
+        { this->m_Matrix[ orig ][ dest ].push_back( cost ); }
+      TEdges& GetEdges( const TIndex& orig, const TIndex& dest );
+      const TEdges& GetEdges( const TIndex& orig, const TIndex& dest ) const;
+      bool HasEdge( const TIndex& orig, const TIndex& dest ) const;
+      void RemoveEdge( const TIndex& orig, const TIndex& dest, const TCost& cost );
+      void RemoveEdges( const TIndex& orig, const TIndex& dest );
+
+      /*! \brief Returns graph's sinks.
+       *
+       *  A sink is a special vertex which does not have any "exiting" edges.
+       *
+       *  @return Sinks ordered by their index.
+       */
+      std::set< TIndex, TIndexCompare > GetSinks( ) const;
+
+    protected:
+      Graph( );
+      virtual ~Graph( );
+
+    private:
+      // Purposely not implemented
+      Graph( const Self& other );
+      Self& operator=( const Self& other );
+
+    protected:
+      TVertices m_Vertices;
+      TMatrix   m_Matrix;
+    };
+
+  } // ecapseman
+
+} // ecapseman
+
+#ifndef ITK_MANUAL_INSTANTIATION
+#  include <fpa/Base/Graph.hxx>
+#endif // ITK_MANUAL_INSTANTIATION
+
+#endif // __fpa__Base__Graph__h__
+
+// eof - $RCSfile$
diff --git a/lib/fpa/Base/Graph.hxx b/lib/fpa/Base/Graph.hxx
new file mode 100644 (file)
index 0000000..89fec7b
--- /dev/null
@@ -0,0 +1,272 @@
+// =========================================================================
+// @author Leonardo Florez Valencia
+// @email florez-l@javeriana.edu.co
+// =========================================================================
+
+#ifndef __fpa__Base__Graph__hxx__
+#define __fpa__Base__Graph__hxx__
+
+// -------------------------------------------------------------------------
+template< class _TVertex, class _TCost, class _TIndex, class _TIndexCompare >
+void fpa::Base::Graph< _TVertex, _TCost, _TIndex, _TIndexCompare >::
+Clear( )
+{
+  this->m_Vertices.clear( );
+  this->m_Matrix.clear( );
+}
+
+// -------------------------------------------------------------------------
+template< class _TVertex, class _TCost, class _TIndex, class _TIndexCompare >
+bool fpa::Base::Graph< _TVertex, _TCost, _TIndex, _TIndexCompare >::
+RenameVertex( const TIndex& old_index, const TIndex& new_index )
+{
+  auto old_v = this->m_Vertices.find( old_index );
+  auto new_v = this->m_Vertices.find( new_index );
+  if( old_v != this->m_Vertices.end( ) && new_v == this->m_Vertices.end( ) )
+  {
+    // Replace vertex
+    this->m_Vertices[ new_index ] = old_v->second;
+    this->m_Vertices.erase( old_index );
+
+    // Duplicate edges
+    auto mIt = this->m_Matrix.begin( );
+    auto found_row = this->m_Matrix.end( );
+    for( ; mIt != this->m_Matrix.end( ); ++mIt )
+    {
+      if( mIt->first == old_index )
+        found_row = mIt;
+
+      auto rIt = mIt->second.begin( );
+      for( ; rIt != mIt->second.end( ); ++rIt )
+      {
+        if( mIt->first == old_index )
+          this->m_Matrix[ new_index ][ rIt->first ] = rIt->second;
+        else if( rIt->first == old_index )
+          this->m_Matrix[ mIt->first ][ new_index ] = rIt->second;
+
+      } // rof
+
+    } // rof
+
+    // Delete old edges
+    if( found_row != this->m_Matrix.end( ) )
+      this->m_Matrix.erase( found_row );
+
+    mIt = this->m_Matrix.begin( );
+    for( ; mIt != this->m_Matrix.end( ); ++mIt )
+    {
+      auto rIt = mIt->second.begin( );
+      while( rIt != mIt->second.end( ) )
+      {
+        if( rIt->first == old_index )
+        {
+          mIt->second.erase( rIt );
+          rIt = mIt->second.begin( );
+        }
+        else
+          ++rIt;
+
+      } // elihw
+
+    } // rof
+    return( true );
+  }
+  else
+    return( false );
+}
+
+// -------------------------------------------------------------------------
+template< class _TVertex, class _TCost, class _TIndex, class _TIndexCompare >
+void fpa::Base::Graph< _TVertex, _TCost, _TIndex, _TIndexCompare >::
+RemoveVertex( const TIndex& index )
+{
+  auto i = this->m_Vertices.find( index );
+  if( i != this->m_Vertices.end( ) )
+  {
+    // Delete vertex
+    this->m_Vertices.erase( i );
+
+    // Delete edges starting from given vertex
+    auto mIt = this->m_Matrix.find( index );
+    if( mIt != this->m_Matrix.end( ) )
+      this->m_Matrix.erase( mIt );
+
+    // Delete edges arriving to given vertex
+    mIt = this->m_Matrix.begin( );
+    for( ; mIt != this->m_Matrix.end( ); ++mIt )
+    {
+      auto rIt = mIt->second.begin( );
+      while( rIt != mIt->second.end( ) )
+      {
+        if( rIt->first == index )
+        {
+          mIt->second.erase( rIt );
+          rIt = mIt->second.begin( );
+        }
+        else
+          ++rIt;
+
+      } // elihw
+
+    } // rof
+
+  } // fi
+}
+
+// -------------------------------------------------------------------------
+template< class _TVertex, class _TCost, class _TIndex, class _TIndexCompare >
+typename
+fpa::Base::Graph< _TVertex, _TCost, _TIndex, _TIndexCompare >::
+TEdges&
+fpa::Base::Graph< _TVertex, _TCost, _TIndex, _TIndexCompare >::
+GetEdges( const TIndex& orig, const TIndex& dest )
+{
+  static TEdges null_edges;
+  auto o = this->m_Matrix.find( orig );
+  if( o != this->m_Matrix.find( orig ) )
+  {
+    auto d = o->second.find( dest );
+    if( d == o->second.end( ) )
+    {
+      null_edges.clear( );
+      return( null_edges );
+    }
+    else
+      return( d->second );
+  }
+  else
+  {
+    null_edges.clear( );
+    return( null_edges );
+
+  } // fi
+}
+
+// -------------------------------------------------------------------------
+template< class _TVertex, class _TCost, class _TIndex, class _TIndexCompare >
+const typename 
+fpa::Base::Graph< _TVertex, _TCost, _TIndex, _TIndexCompare >::
+TEdges&
+fpa::Base::Graph< _TVertex, _TCost, _TIndex, _TIndexCompare >::
+GetEdges( const TIndex& orig, const TIndex& dest ) const
+{
+  static const TEdges null_edges;
+  auto o = this->m_Matrix.find( orig );
+  if( o != this->m_Matrix.find( orig ) )
+  {
+    auto d = o->second.find( dest );
+    if( d == o->second.end( ) )
+      return( null_edges );
+    else
+      return( d->second );
+  }
+  else
+    return( null_edges );
+}
+
+// -------------------------------------------------------------------------
+template< class _TVertex, class _TCost, class _TIndex, class _TIndexCompare >
+bool fpa::Base::Graph< _TVertex, _TCost, _TIndex, _TIndexCompare >::
+HasEdge( const TIndex& orig, const TIndex& dest ) const
+{
+  auto mIt = this->m_Matrix.find( orig );
+  if( mIt != this->m_Matrix.end( ) )
+    return( mIt->second.find( dest ) != mIt->second.end( ) );
+  else
+    return( false );
+}
+
+// -------------------------------------------------------------------------
+template< class _TVertex, class _TCost, class _TIndex, class _TIndexCompare >
+void fpa::Base::Graph< _TVertex, _TCost, _TIndex, _TIndexCompare >::
+RemoveEdge( const TIndex& orig, const TIndex& dest, const TCost& cost )
+{
+  auto m = this->m_Matrix.find( orig );
+  if( m != this->m_Matrix.end( ) )
+  {
+    auto r = m->second.find( dest );
+    if( r != m->second.end( ) )
+    {
+      auto e = r->second.end( );
+      for(
+        auto i = r->second.begin( );
+        i != r->second.end( ) && e == r->second.end( );
+        ++i
+        )
+        if( *i == cost )
+          e = i;
+      if( e != r->second.end( ) )
+      {
+        r->second.erase( e );
+        if( r->second.size( ) == 0 )
+        {
+          m->second.erase( r );
+          if( m->second.size( ) == 0 )
+            this->m_Matrix.erase( m );
+
+        } // fi
+
+      } // fi
+
+    } // fi
+
+  } // fi
+}
+
+// -------------------------------------------------------------------------
+template< class _TVertex, class _TCost, class _TIndex, class _TIndexCompare >
+void fpa::Base::Graph< _TVertex, _TCost, _TIndex, _TIndexCompare >::
+RemoveEdges( const TIndex& orig, const TIndex& dest )
+{
+  auto m = this->m_Matrix.find( orig );
+  if( m != this->m_Matrix.end( ) )
+  {
+    auto r = m->second.find( dest );
+    if( r != m->second.end( ) )
+    {
+      m->second.erase( r );
+      if( m->second.size( ) == 0 )
+        this->m_Matrix.erase( m );
+
+    } // fi
+
+  } // fi
+
+}
+
+// -------------------------------------------------------------------------
+template< class _TVertex, class _TCost, class _TIndex, class _TIndexCompare >
+std::set< _TIndex, _TIndexCompare >
+fpa::Base::Graph< _TVertex, _TCost, _TIndex, _TIndexCompare >::
+GetSinks( ) const
+{
+  std::set< _TIndex, _TIndexCompare > sinks;
+
+  auto vIt = this->m_Vertices.begin( );
+  for( ; vIt != this->m_Vertices.end( ); ++vIt )
+    sinks.insert( vIt->first );
+  auto mIt = this->m_Matrix.begin( );
+  for( ; mIt != this->m_Matrix.end( ); ++mIt )
+    sinks.erase( mIt->first );
+
+  return( sinks );
+}
+
+// -------------------------------------------------------------------------
+template< class _TVertex, class _TCost, class _TIndex, class _TIndexCompare >
+fpa::Base::Graph< _TVertex, _TCost, _TIndex, _TIndexCompare >::
+Graph( )
+  : Superclass( )
+{
+}
+
+// -------------------------------------------------------------------------
+template< class _TVertex, class _TCost, class _TIndex, class _TIndexCompare >
+fpa::Base::Graph< _TVertex, _TCost, _TIndex, _TIndexCompare >::
+~Graph( )
+{
+}
+
+#endif // __fpa__Base__Graph__hxx__
+
+// eof - $RCSfile$
diff --git a/lib/fpa/Base/PolyLineParametricPath.h b/lib/fpa/Base/PolyLineParametricPath.h
new file mode 100644 (file)
index 0000000..79eb530
--- /dev/null
@@ -0,0 +1,97 @@
+// =========================================================================
+// @author Leonardo Florez Valencia
+// @email florez-l@javeriana.edu.co
+// =========================================================================
+
+#ifndef __fpa__Base__PolyLineParametricPath__h__
+#define __fpa__Base__PolyLineParametricPath__h__
+
+#include <itkPolyLineParametricPath.h>
+#include <itkImageBase.h>
+
+namespace fpa
+{
+  namespace Base
+  {
+    /**
+     */
+    template< unsigned int _VDim >
+    class PolyLineParametricPath
+      : public itk::PolyLineParametricPath< _VDim >
+    {
+    public:
+      typedef PolyLineParametricPath               Self;
+      typedef itk::PolyLineParametricPath< _VDim > Superclass;
+      typedef itk::SmartPointer< Self >            Pointer;
+      typedef itk::SmartPointer< const Self >      ConstPointer;
+
+      typedef itk::ImageBase< _VDim >                  TImageBase;
+      typedef typename TImageBase::SpacingType         TSpacing;
+      typedef typename TImageBase::PointType           TPoint;
+      typedef typename TImageBase::DirectionType       TDirection;
+      typedef typename Superclass::ContinuousIndexType TContinuousIndex;
+      typedef typename TContinuousIndex::IndexType     TIndex;
+
+    public:
+      itkNewMacro( Self );
+      itkTypeMacro( PolyLineParametricPath, itk::PolyLineParametricPath );
+
+      itkGetConstReferenceMacro( Spacing, TSpacing );
+      itkGetConstReferenceMacro( Origin, TPoint );
+      itkGetConstReferenceMacro( Direction, TDirection );
+      itkGetConstReferenceMacro( InverseDirection, TDirection );
+
+      itkSetMacro( Origin, TPoint );
+
+    public:
+      unsigned long GetSize( ) const;
+      TContinuousIndex GetContinuousVertex( unsigned long i ) const;
+      TIndex GetVertex( unsigned long i ) const;
+      TPoint GetPoint( unsigned long i ) const;
+
+      virtual void SetSpacing( const TSpacing& spac );
+      virtual void SetSpacing( const double spac[ _VDim ] );
+      virtual void SetSpacing( const float spac[ _VDim ] );
+      virtual void SetOrigin( const double ori[ _VDim ] );
+      virtual void SetOrigin( const float ori[ _VDim ] );
+      virtual void SetDirection( const TDirection& dir );
+
+      template< class _TRefImage >
+      inline void SetReferenceImage( const _TRefImage* image )
+        {
+          this->SetSpacing( image->GetSpacing( ) );
+          this->SetOrigin( image->GetOrigin( ) );
+          this->SetDirection( image->GetDirection( ) );
+        }
+
+    protected:
+      PolyLineParametricPath( );
+      virtual ~PolyLineParametricPath( );
+
+      virtual void _ComputeIndexToPhysicalPointMatrices( );
+
+    private:
+      // Purposely not implemented
+      PolyLineParametricPath( const Self& other );
+      Self& operator=( const Self& other );
+
+    protected:
+      TSpacing   m_Spacing;
+      TPoint     m_Origin;
+      TDirection m_Direction;
+      TDirection m_InverseDirection;
+      TDirection m_IndexToPhysicalPoint;
+      TDirection m_PhysicalPointToIndex;
+    };
+
+  } // ecapseman
+
+} // ecapseman
+
+#ifndef ITK_MANUAL_INSTANTIATION
+#  include <fpa/Base/PolyLineParametricPath.hxx>
+#endif // ITK_MANUAL_INSTANTIATION
+
+#endif // __fpa__Base__PolyLineParametricPath__h__
+
+// eof - $RCSfile$
diff --git a/lib/fpa/Base/PolyLineParametricPath.hxx b/lib/fpa/Base/PolyLineParametricPath.hxx
new file mode 100644 (file)
index 0000000..18732d5
--- /dev/null
@@ -0,0 +1,194 @@
+// =========================================================================
+// @author Leonardo Florez Valencia
+// @email florez-l@javeriana.edu.co
+// =========================================================================
+
+#ifndef __fpa__Base__PolyLineParametricPath__hxx__
+#define __fpa__Base__PolyLineParametricPath__hxx__
+
+#include <itkMath.h>
+#include <itkNumericTraits.h>
+
+// -------------------------------------------------------------------------
+template< unsigned int _VDim >
+unsigned long fpa::Base::PolyLineParametricPath< _VDim >::
+GetSize( ) const
+{
+  return( this->GetVertexList( )->Size( ) );
+}
+
+// -------------------------------------------------------------------------
+template< unsigned int _VDim >
+typename fpa::Base::PolyLineParametricPath< _VDim >::
+TContinuousIndex
+fpa::Base::PolyLineParametricPath< _VDim >::
+GetContinuousVertex( unsigned long i ) const
+{
+  return( this->GetVertexList( )->GetElement( i ) );
+}
+
+// -------------------------------------------------------------------------
+template< unsigned int _VDim >
+typename fpa::Base::PolyLineParametricPath< _VDim >::
+TIndex fpa::Base::PolyLineParametricPath< _VDim >::
+GetVertex( unsigned long i ) const
+{
+  TContinuousIndex cidx = this->GetContinuousVertex( i );
+  TIndex idx;
+  for( unsigned int d = 0; d < _VDim; ++d )
+    idx[ d ] = cidx[ d ];
+  return( idx );
+}
+
+// -------------------------------------------------------------------------
+template< unsigned int _VDim >
+typename fpa::Base::PolyLineParametricPath< _VDim >::
+TPoint fpa::Base::PolyLineParametricPath< _VDim >::
+GetPoint( unsigned long i ) const
+{
+  typedef typename TPoint::CoordRepType _TCoordRep;
+  TPoint pnt;
+  TContinuousIndex idx = this->GetVertex( i );
+  for( unsigned int r = 0; r < _VDim; ++r )
+  {
+    _TCoordRep sum = itk::NumericTraits< _TCoordRep >::ZeroValue( );
+    for( unsigned int c = 0; c < _VDim; ++c )
+      sum += this->m_IndexToPhysicalPoint( r, c ) * idx[ c ];
+    pnt[ r ] = sum + this->m_Origin[ r ];
+
+  } // rof
+  return( pnt );
+}
+
+// -------------------------------------------------------------------------
+template< unsigned int _VDim >
+void fpa::Base::PolyLineParametricPath< _VDim >::
+SetSpacing( const TSpacing& spac )
+{
+  if( this->m_Spacing != spac )
+  {
+    this->m_Spacing = spac;
+    this->_ComputeIndexToPhysicalPointMatrices( );
+    this->Modified( );
+
+  } // fi
+}
+
+// -------------------------------------------------------------------------
+template< unsigned int _VDim >
+void fpa::Base::PolyLineParametricPath< _VDim >::
+SetSpacing( const double spac[ _VDim ] )
+{
+  this->SetSpacing( TSpacing( spac ) );
+}
+
+// -------------------------------------------------------------------------
+template< unsigned int _VDim >
+void fpa::Base::PolyLineParametricPath< _VDim >::
+SetSpacing( const float spac[ _VDim ] )
+{
+  TSpacing s;
+  for( unsigned int d = 0; d < _VDim; ++d )
+    s[ d ] = spac[ d ];
+  this->SetSpacing( s );
+}
+
+// -------------------------------------------------------------------------
+template< unsigned int _VDim >
+void fpa::Base::PolyLineParametricPath< _VDim >::
+SetOrigin( const double ori[ _VDim ] )
+{
+  this->SetOrigin( TPoint( ori ) );
+}
+
+// -------------------------------------------------------------------------
+template< unsigned int _VDim >
+void fpa::Base::PolyLineParametricPath< _VDim >::
+SetOrigin( const float ori[ _VDim ] )
+{
+  this->SetOrigin( TPoint( ori ) );
+}
+
+// -------------------------------------------------------------------------
+template< unsigned int _VDim >
+void fpa::Base::PolyLineParametricPath< _VDim >::
+SetDirection( const TDirection& dir )
+{
+  bool modified = false;
+  for( unsigned int r = 0; r < _VDim; r++ )
+  {
+    for( unsigned int c = 0; c < _VDim; c++ )
+    {
+      if(
+        itk::Math::NotExactlyEquals(
+          this->m_Direction[ r ][ c ], dir[ r ][ c ]
+          )
+        )
+      {
+        this->m_Direction[ r ][ c ] = dir[ r ][ c ];
+        modified = true;
+      } // fi
+
+    } // rof
+
+  } // rof
+  if( modified )
+  {
+    this->_ComputeIndexToPhysicalPointMatrices( );
+    this->m_InverseDirection = this->m_Direction.GetInverse( );
+    this->Modified( );
+
+  } // fi
+}
+
+// -------------------------------------------------------------------------
+template< unsigned int _VDim >
+fpa::Base::PolyLineParametricPath< _VDim >::
+PolyLineParametricPath( )
+  : Superclass( )
+{
+  this->m_Spacing.Fill( 1.0 );
+  this->m_Origin.Fill( 0.0 );
+  this->m_Direction.SetIdentity( );
+  this->m_InverseDirection.SetIdentity( );
+  this->m_IndexToPhysicalPoint.SetIdentity( );
+  this->m_PhysicalPointToIndex.SetIdentity( );
+}
+
+// -------------------------------------------------------------------------
+template< unsigned int _VDim >
+fpa::Base::PolyLineParametricPath< _VDim >::
+~PolyLineParametricPath( )
+{
+}
+
+// -------------------------------------------------------------------------
+template< unsigned int _VDim >
+void fpa::Base::PolyLineParametricPath< _VDim >::
+_ComputeIndexToPhysicalPointMatrices( )
+{
+  TDirection scale;
+  scale.Fill( 0.0 );
+  for( unsigned int i = 0; i < _VDim; i++ )
+  {
+    if( this->m_Spacing[ i ] == 0.0 )
+      itkExceptionMacro(
+        "A spacing of 0 is not allowed: Spacing is " << this->m_Spacing
+        );
+    scale[ i ][ i ] = this->m_Spacing[ i ];
+
+  } // rof
+
+  if( vnl_determinant( this->m_Direction.GetVnlMatrix( ) ) == 0.0 )
+    itkExceptionMacro(
+      << "Bad direction, determinant is 0. Direction is "
+      << this->m_Direction
+      );
+  this->m_IndexToPhysicalPoint = this->m_Direction * scale;
+  this->m_PhysicalPointToIndex = this->m_IndexToPhysicalPoint.GetInverse( );
+  this->Modified( );
+}
+
+#endif // __fpa__Base__PolyLineParametricPath__hxx__
+
+// eof - $RCSfile$
index a994125e3ef46445dd744a5257c0def6013dfe27..cfc1bd568e505af2aba29ef173dccf8bc70d9a0a 100644 (file)
@@ -38,7 +38,7 @@ namespace fpa
       SeedsInterface( itk::ProcessObject* filter );
       virtual ~SeedsInterface( );
 
-    private:
+    protected:
       TSeeds m_Seeds;
       itk::ProcessObject* m_Filter;
     };
diff --git a/lib/fpa/Base/Skeleton.h b/lib/fpa/Base/Skeleton.h
new file mode 100644 (file)
index 0000000..223d74e
--- /dev/null
@@ -0,0 +1,67 @@
+// =========================================================================
+// @author Leonardo Florez Valencia
+// @email florez-l@javeriana.edu.co
+// =========================================================================
+
+#ifndef __fpa__Base__Skeleton__h__
+#define __fpa__Base__Skeleton__h__
+
+#include <vector>
+#include <fpa/Base/Graph.h>
+#include <fpa/Base/PolyLineParametricPath.h>
+
+namespace fpa
+{
+  namespace Base
+  {
+    /**
+     */
+    template< unsigned int _VDim >
+    class Skeleton
+      : public fpa::Base::Graph< typename fpa::Base::PolyLineParametricPath< _VDim >::TIndex, typename fpa::Base::PolyLineParametricPath< _VDim >::Pointer, typename fpa::Base::PolyLineParametricPath< _VDim >::TIndex, typename fpa::Base::PolyLineParametricPath< _VDim >::TIndex::LexicographicCompare >
+    {
+    public:
+      typedef fpa::Base::PolyLineParametricPath< _VDim > TPath;
+      typedef typename TPath::TIndex                     TIndex;
+      typedef typename TIndex::LexicographicCompare      TIndexCompare;
+      typedef typename TPath::Pointer                    TPathPointer;
+
+      itkStaticConstMacro( Dimension, unsigned int, _VDim );
+
+      typedef fpa::Base::Graph< TIndex, TPathPointer, TIndex, TIndexCompare > Superclass;
+      typedef Skeleton                        Self;
+      typedef itk::SmartPointer< Self >       Pointer;
+      typedef itk::SmartPointer< const Self > ConstPointer;
+
+    public:
+      itkNewMacro( Self );
+      itkTypeMacro( Skeleton, fpa::Base::Graph );
+
+    public:
+      void AddBranch( TPath* path );
+      const TPath* GetBranch( const TIndex& a, const TIndex& b ) const;
+
+      std::vector< TIndex > GetEndPoints( ) const;
+      std::vector< TIndex > GetBifurcations( ) const;
+
+    protected:
+      Skeleton( );
+      virtual ~Skeleton( );
+
+    private:
+      // Purposely not implemented
+      Skeleton( const Self& other );
+      Self& operator=( const Self& other );
+    };
+
+  } // ecapseman
+
+} // ecapseman
+
+#ifndef ITK_MANUAL_INSTANTIATION
+#  include <fpa/Base/Skeleton.hxx>
+#endif // ITK_MANUAL_INSTANTIATION
+
+#endif // __fpa__Base__Skeleton__h__
+
+// eof - $RCSfile$
diff --git a/lib/fpa/Base/Skeleton.hxx b/lib/fpa/Base/Skeleton.hxx
new file mode 100644 (file)
index 0000000..91ec404
--- /dev/null
@@ -0,0 +1,99 @@
+// =========================================================================
+// @author Leonardo Florez Valencia
+// @email florez-l@javeriana.edu.co
+// =========================================================================
+
+#ifndef __fpa__Base__Skeleton__hxx__
+#define __fpa__Base__Skeleton__hxx__
+
+// -------------------------------------------------------------------------
+template< unsigned int _VDim >
+void fpa::Base::Skeleton< _VDim >::
+AddBranch( TPath* path )
+{
+  // Check inputs
+  if( path == NULL )
+    return;
+  unsigned long size = path->GetSize( );
+  if( size == 0 )
+    return;
+  TIndex a = path->GetVertex( 0 );
+  TIndex b = path->GetVertex( size - 1 );
+  if( this->HasEdge( a, b ) )
+    return;
+
+  // Add path
+  this->SetVertex( a, a );
+  this->SetVertex( b, b );
+  this->AddEdge( a, b, path );
+  this->AddEdge( b, a, path );
+  // TODO: this->Modified( );
+}
+
+// -------------------------------------------------------------------------
+template< unsigned int _VDim >
+const typename fpa::Base::Skeleton< _VDim >::
+TPath* fpa::Base::Skeleton< _VDim >::
+GetBranch( const TIndex& a, const TIndex& b ) const
+{
+  static const TPath* null_path = NULL;
+  if( this->HasEdge( a, b ) )
+    return( this->GetEdges( a, b ).front( ) );
+  else
+    return( null_path );
+}
+
+// -------------------------------------------------------------------------
+template< unsigned int _VDim >
+std::vector< typename fpa::Base::Skeleton< _VDim >::TIndex >
+fpa::Base::Skeleton< _VDim >::
+GetEndPoints( ) const
+{
+  std::vector< TIndex > res;
+  auto mIt = this->BeginEdgesRows( );
+  for( ; mIt != this->EndEdgesRows( ); ++mIt )
+  {
+    unsigned long count = mIt->second.size( );
+    if( count == 1 )
+      res.push_back( mIt->first );
+
+  } // rof
+  return( res );
+}
+
+// -------------------------------------------------------------------------
+template< unsigned int _VDim >
+std::vector< typename fpa::Base::Skeleton< _VDim >::TIndex >
+fpa::Base::Skeleton< _VDim >::
+GetBifurcations( ) const
+{
+  std::vector< TIndex > res;
+  auto mIt = this->BeginEdgesRows( );
+  for( ; mIt != this->EndEdgesRows( ); ++mIt )
+  {
+    unsigned long count = mIt->second.size( );
+    if( count > 1 )
+      res.push_back( mIt->first );
+
+  } // rof
+  return( res );
+}
+
+// -------------------------------------------------------------------------
+template< unsigned int _VDim >
+fpa::Base::Skeleton< _VDim >::
+Skeleton( )
+  : Superclass( )
+{
+}
+
+// -------------------------------------------------------------------------
+template< unsigned int _VDim >
+fpa::Base::Skeleton< _VDim >::
+~Skeleton( )
+{
+}
+
+#endif // __fpa__Base__Skeleton__hxx__
+
+// eof - $RCSfile$
diff --git a/lib/fpa/Image/SkeletonFilter.h b/lib/fpa/Image/SkeletonFilter.h
new file mode 100644 (file)
index 0000000..9f0542f
--- /dev/null
@@ -0,0 +1,96 @@
+// =========================================================================
+// @author Leonardo Florez Valencia
+// @email florez-l@javeriana.edu.co
+// =========================================================================
+
+#ifndef __fpa__Image__SkeletonFilter__h__
+#define __fpa__Image__SkeletonFilter__h__
+
+#include <fpa/Image/Dijkstra.h>
+
+#include <map>
+#include <itkSignedMaurerDistanceMapImageFilter.h>
+#include <fpa/Base/Skeleton.h>
+
+namespace fpa
+{
+  namespace Image
+  {
+    /**
+     */
+    template< class _TImage, class _TScalar = double >
+    class SkeletonFilter
+      : public fpa::Image::Dijkstra< itk::Image< _TScalar, _TImage::ImageDimension >, itk::Image< _TScalar, _TImage::ImageDimension > >
+    {
+    public:
+      // Smart pointers
+      typedef _TImage  TImage;
+      typedef _TScalar TScalar;
+      typedef itk::Image< TScalar, TImage::ImageDimension > TScalarImage;
+      typedef SkeletonFilter Self;
+      typedef fpa::Image::Dijkstra< TScalarImage, TScalarImage > Superclass;
+      typedef itk::SmartPointer< Self > Pointer;
+      typedef itk::SmartPointer< const Self > ConstPointer;
+
+      typedef typename Superclass::TVertex TVertex;
+      typedef typename Superclass::TOutputValue TOutputValue;
+
+      typedef itk::SignedMaurerDistanceMapImageFilter< TImage, TScalarImage > TDistanceMap;
+      typedef itk::Image< unsigned char, TImage::ImageDimension > TMarks;
+      typedef fpa::Base::Skeleton< TImage::ImageDimension > TSkeleton;
+
+    protected:
+      typedef std::multimap< TScalar, TVertex, std::greater< TScalar > > _TSkeletonQueue;
+      typedef std::map< TVertex, TVertex, typename TVertex::LexicographicCompare > _TAdjacencies;
+      
+    public:
+      itkNewMacro( Self );
+      itkTypeMacro( fpa::Image::SkeletonFilter, fpa::Image::Dijkstra );
+
+      itkBooleanMacro( SeedFromMaximumDistance );
+      itkGetConstMacro( SeedFromMaximumDistance, bool );
+      itkSetMacro( SeedFromMaximumDistance, bool );
+
+    public:
+      virtual void SetInput( const TScalarImage* image ) override;
+      void SetInput( const TImage* image );
+      TImage* GetInput( );
+      const TImage* GetInput( ) const;
+
+      TSkeleton* GetSkeleton( );
+      TMarks* GetMarks( );
+
+    protected:
+      SkeletonFilter( );
+      virtual ~SkeletonFilter( );
+
+      virtual void GenerateData( ) override;
+      virtual void _SetOutputValue( const TVertex& vertex, const TOutputValue& value ) override;
+
+      void _EndPoints( std::vector< TVertex >& end_points, _TAdjacencies& A );
+      void _Skeleton( const std::vector< TVertex >& end_points, _TAdjacencies& A );
+      void _MarkSphere( const TVertex& idx );
+
+    private:
+      SkeletonFilter( const Self& other );
+      Self& operator=( const Self& other );
+
+    protected:
+      bool m_SeedFromMaximumDistance;
+      typename TDistanceMap::Pointer m_DistanceMap;
+      _TSkeletonQueue m_SkeletonQueue;
+      unsigned long m_SkeletonIdx;
+      unsigned long m_MarksIdx;
+    };
+
+  } // ecapseman
+
+} // ecapseman
+
+#ifndef ITK_MANUAL_INSTANTIATION
+#  include <fpa/Image/SkeletonFilter.hxx>
+#endif // ITK_MANUAL_INSTANTIATION
+
+#endif // __fpa__Image__SkeletonFilter__h__
+
+// eof - $RCSfile$
diff --git a/lib/fpa/Image/SkeletonFilter.hxx b/lib/fpa/Image/SkeletonFilter.hxx
new file mode 100644 (file)
index 0000000..2b1cb65
--- /dev/null
@@ -0,0 +1,336 @@
+// =========================================================================
+// @author Leonardo Florez Valencia
+// @email florez-l@javeriana.edu.co
+// =========================================================================
+
+#ifndef __fpa__Image__SkeletonFilter__hxx__
+#define __fpa__Image__SkeletonFilter__hxx__
+
+#include <itkMinimumMaximumImageCalculator.h>
+
+// -------------------------------------------------------------------------
+template< class _TImage, class _TScalar >
+void fpa::Image::SkeletonFilter< _TImage, _TScalar >::
+SetInput( const TScalarImage* image )
+{
+  // Do nothing
+}
+
+// -------------------------------------------------------------------------
+template< class _TImage, class _TScalar >
+void fpa::Image::SkeletonFilter< _TImage, _TScalar >::
+SetInput( const TImage* image )
+{
+  this->m_DistanceMap->SetInput( image );
+  this->Superclass::SetInput( this->m_DistanceMap->GetOutput( ) );
+}
+
+// -------------------------------------------------------------------------
+template< class _TImage, class _TScalar >
+_TImage* fpa::Image::SkeletonFilter< _TImage, _TScalar >::
+GetInput( )
+{
+  return( this->m_DistanceMap->GetInput( ) );
+}
+
+// -------------------------------------------------------------------------
+template< class _TImage, class _TScalar >
+const _TImage* fpa::Image::SkeletonFilter< _TImage, _TScalar >::
+GetInput( ) const
+{
+  return( this->m_DistanceMap->GetInput( ) );
+}
+
+// -------------------------------------------------------------------------
+template< class _TImage, class _TScalar >
+typename fpa::Image::SkeletonFilter< _TImage, _TScalar >::
+TSkeleton* fpa::Image::SkeletonFilter< _TImage, _TScalar >::
+GetSkeleton( )
+{
+  return(
+    dynamic_cast< TSkeleton* >(
+      this->itk::ProcessObject::GetOutput( this->m_SkeletonIdx )
+      )
+    );
+}
+
+// -------------------------------------------------------------------------
+template< class _TImage, class _TScalar >
+typename fpa::Image::SkeletonFilter< _TImage, _TScalar >::
+TMarks* fpa::Image::SkeletonFilter< _TImage, _TScalar >::
+GetMarks( )
+{
+  return(
+    dynamic_cast< TMarks* >(
+      this->itk::ProcessObject::GetOutput( this->m_MarksIdx )
+      )
+    );
+}
+
+// -------------------------------------------------------------------------
+template< class _TImage, class _TScalar >
+fpa::Image::SkeletonFilter< _TImage, _TScalar >::
+SkeletonFilter( )
+  : Superclass( ),
+    m_SeedFromMaximumDistance( false )
+{
+  this->m_DistanceMap = TDistanceMap::New( );
+  this->m_DistanceMap->InsideIsPositiveOn( );
+  this->m_DistanceMap->SquaredDistanceOff( );
+  this->m_DistanceMap->UseImageSpacingOn( );
+
+  unsigned int nOutputs = this->GetNumberOfRequiredOutputs( );
+  this->SetNumberOfRequiredOutputs( nOutputs + 2 );
+  this->m_SkeletonIdx = nOutputs;
+  this->m_MarksIdx = nOutputs + 1;
+
+  typename TSkeleton::Pointer skeleton = TSkeleton::New( );
+  typename TMarks::Pointer marks = TMarks::New( );
+  this->SetNthOutput( this->m_SkeletonIdx, skeleton.GetPointer( ) );
+  this->SetNthOutput( this->m_MarksIdx, marks.GetPointer( ) );
+}
+
+// -------------------------------------------------------------------------
+template< class _TImage, class _TScalar >
+fpa::Image::SkeletonFilter< _TImage, _TScalar >::
+~SkeletonFilter( )
+{
+}
+
+// -------------------------------------------------------------------------
+template< class _TImage, class _TScalar >
+void fpa::Image::SkeletonFilter< _TImage, _TScalar >::
+GenerateData( )
+{
+  // Prepare distance map
+  this->m_DistanceMap->Update( );
+
+  // Update start seed
+  TVertex seed;
+  if( this->m_SeedFromMaximumDistance )
+  {
+    typedef itk::MinimumMaximumImageCalculator< TScalarImage > _TMinMax;
+    typename _TMinMax::Pointer minmax = _TMinMax::New( );
+    minmax->SetImage( this->m_DistanceMap->GetOutput( ) );
+    minmax->Compute( );
+    seed = minmax->GetIndexOfMaximum( );
+  }
+  else
+    seed = *( this->BeginSeeds( ) );
+  this->m_Seeds.clear( );
+  this->m_Seeds.insert( seed );
+
+  // Prepare skeleton candidates queue
+  this->m_SkeletonQueue.clear( );
+
+  // Go!
+  this->Superclass::GenerateData( );
+
+  // Backtracking
+  _TAdjacencies A;
+  std::vector< TVertex > end_points;
+  this->_EndPoints( end_points, A );
+  this->_Skeleton( end_points, A );
+}
+
+// -------------------------------------------------------------------------
+template< class _TImage, class _TScalar >
+void fpa::Image::SkeletonFilter< _TImage, _TScalar >::
+_SetOutputValue( const TVertex& vertex, const TOutputValue& value )
+{
+  /* TODO
+     typedef typename _TSkeletonQueue::value_type _TSkeletonQueueValue;
+
+     this->Superclass::_UpdateResult( n );
+     double d = double( this->GetInput( )->GetPixel( n.Vertex ) );
+     if( d >= double( 0 ) )
+     {
+     // Update skeleton candidates
+     d += double( 1e-5 );
+     double v = double( n.Result ) / ( d * d );
+     this->m_SkeletonQueue.insert( _TSkeletonQueueValue( v, n.Vertex ) );
+
+     } // fi
+  */
+}
+
+// -------------------------------------------------------------------------
+template< class _TImage, class _TScalar >
+void fpa::Image::SkeletonFilter< _TImage, _TScalar >::
+_EndPoints( std::vector< TVertex >& end_points, _TAdjacencies& A )
+{
+  /* TODO
+     auto marks = this->GetMarks( );
+     auto mst = this->GetMinimumSpanningTree( );
+     auto spac = marks->GetSpacing( );
+
+     // Some values
+     marks->SetLargestPossibleRegion( mst->GetLargestPossibleRegion( ) );
+     marks->SetRequestedRegion( mst->GetRequestedRegion( ) );
+     marks->SetBufferedRegion( mst->GetBufferedRegion( ) );
+     marks->SetSpacing( mst->GetSpacing( ) );
+     marks->SetOrigin( mst->GetOrigin( ) );
+     marks->SetDirection( mst->GetDirection( ) );
+     marks->Allocate( );
+     marks->FillBuffer( 0 );
+
+     // BFS from maximum queue
+     while( this->m_SkeletonQueue.size( ) > 0 )
+     {
+     // Get node
+     auto nIt = this->m_SkeletonQueue.begin( );
+     auto n = *nIt;
+     this->m_SkeletonQueue.erase( nIt );
+
+     // Mark it and update end-points
+     unsigned char m = marks->GetPixel( n.second );
+     if( m != 0 )
+     continue;
+     marks->SetPixel( n.second, 1 );
+     end_points.push_back( n.second );
+
+     // Mark path
+     TVertex it = n.second;
+     TVertex p = mst->GetParent( it );
+     while( it != p )
+     {
+     this->_MarkSphere( it );
+     it = p;
+     p = mst->GetParent( it );
+
+     } // elihw
+     this->_MarkSphere( it );
+     A[ n.second ] = it;
+
+     } // elihw
+  */
+}
+
+// -------------------------------------------------------------------------
+template< class _TImage, class _TScalar >
+void fpa::Image::SkeletonFilter< _TImage, _TScalar >::
+_Skeleton( const std::vector< TVertex >& end_points, _TAdjacencies& A )
+{
+  /* TODO
+     typedef itk::Image< unsigned long, Self::Dimension > _TTagsImage;
+     typedef typename TMST::TPath _TPath;
+
+     auto mst = this->GetMinimumSpanningTree( );
+     auto 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 );
+     for( auto eIt = end_points.begin( ); 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)
+     for( auto eIt = end_points.begin( ); eIt != end_points.end( ); ++eIt )
+     {
+     TVertex it = *eIt;
+     TVertex p = mst->GetParent( it );
+     TVertex sIdx = *eIt;
+     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
+  */
+}
+
+// -------------------------------------------------------------------------
+template< class _TImage, class _TScalar >
+void fpa::Image::SkeletonFilter< _TImage, _TScalar >::
+_MarkSphere( const TVertex& idx )
+{
+  /* TODO
+     typedef itk::ImageRegionIteratorWithIndex< TMarks > _TMarksIt;
+
+     static const double _eps = std::sqrt( double( Self::Dimension + 1 ) );
+     auto input = this->GetInput( );
+     auto marks = this->GetMarks( );
+     auto spac = input->GetSpacing( );
+     auto region = input->GetRequestedRegion( );
+
+     typename _TImage::PointType cnt;
+     input->TransformIndexToPhysicalPoint( idx, cnt );
+     double r = double( input->GetPixel( idx ) ) * _eps;
+
+     TVertex i0, i1;
+     for( unsigned int d = 0; d < Self::Dimension; ++d )
+     {
+     long off = long( std::ceil( r / double( spac[ d ] ) ) );
+     if( off < 3 )
+     off = 3;
+     i0[ d ] = idx[ d ] - off;
+     i1[ d ] = idx[ d ] + off;
+
+     if( i0[ d ] < region.GetIndex( )[ d ] )
+     i0[ d ] = region.GetIndex( )[ d ];
+
+     if( i1[ d ] >= region.GetIndex( )[ d ] + region.GetSize( )[ d ] )
+     i1[ d ] = region.GetIndex( )[ d ] + region.GetSize( )[ d ] - 1;
+
+     } // rof
+
+     typename _TImage::SizeType size;
+     for( unsigned int d = 0; d < Self::Dimension; ++d )
+     size[ d ] = i1[ d ] - i0[ d ] + 1;
+
+     typename _TImage::RegionType neighRegion;
+     neighRegion.SetIndex( i0 );
+     neighRegion.SetSize( size );
+
+     _TMarksIt mIt( marks, neighRegion );
+     for( mIt.GoToBegin( ); !mIt.IsAtEnd( ); ++mIt )
+     {
+     TVertex w = mIt.GetIndex( );
+     typename _TImage::PointType p;
+     marks->TransformIndexToPhysicalPoint( w, p );
+     mIt.Set( 1 );
+
+     } // rof
+  */
+}
+
+#endif // __fpa__Image__SkeletonFilter__hxx__
+
+// eof - $RCSfile$