2010 Summer Project Week SegmentationMeshEmbeddedContours

From NAMIC
Jump to: navigation, search
Home < 2010 Summer Project Week SegmentationMeshEmbeddedContours

Key Investigators

  • Georgia Tech: Peter Karasev, Karol Chudy, Allen Tannenbaum
  • BWH: Ron Kikinis

Objective

We are developing methods for segmentation of surfaces defined by polygonal meshes. In image or volume-slice segmentation, 'information' comes from image intensity, while in mesh surface segmentation the information is purely geometric. The goal of this project is to develop algorithms allowing a user to quickly and robustly segment one or several regions on a surface. Specifically, it is required to reconcile some concepts in classical differential geometry and embedded curves with the numerical and discrete-space constraints of polygon meshes.


Approach, Plan

Surface Geometry Computation

Closest Path Between Initial Points

Fast Level Set Implementation for Unstructured Mesh

Support 'global geometry' estimation for statistical analysis versus local-only for fast segmentation

Extend Functionality in VTK and ITK to perform needed operations


The module pipeline now contains three classes that extend vtkPolyDataAlgorithm. It now makes use of vtkSmartPointer.

  • vtkInitClosedPath : take in poly data and set of fiducial points, create a closed path including the closest points on the mesh to the given fiducials. Searches for pre-existence of geometry information, pass-through if it already exists.
  • vtkComputeLocalGeometry : take in poly data, compute curvature, its derivatives, and related differential geometric quantities. Uses its own version of curvature computation- vtk versions are not useable because they only consider 1-neighborhood. Searches for pre-existence of geometry information, pass-through if it already exists.
  • vtkLevelSetMeshEvolver : take in poly data with a scalar array defining the current region boundaries. Update the boundaries with curve evolution and return poly data with updated scalar array.

Progress

1. Setup Slicer4 "superbuild", verified module consistency therein

2. Signed up for ITK write access as there is some partial overlap and functions in this module perform tasks that ITK doesn't do:

   i. large neighborhood curvature computation via paraboloid fit
   ii. unstructured graph sparse field level sets )

3. From discussion with Ron Kikinis and Karl Fritscher, going forward must branch the functionality:

   i. don't compute 'global geometry' in Slicer, must look up neighbors as needed as user is only segmenting a small piece of large mesh
   ii. allow more geometric information to be returned and stored for statistical analysis and registration of surfaces

4. Curvature overlay in Slicer:

5. Implemented a helpful re-factor, convert a beefy struct with bunch of global functions to a class with member functions plus non-member non-friend helper functions in anonymous namespace


/* NEW */
namespace {
  TestFunc1( const MeshData& data ) { ... }
}

class MeshData{
public:
functionA( );
functionB( );
...
}





/* OLD */ 
struct MeshData{

member1;
member2;
....
}

functionA( MeshData* ) { ... }
functionB( MeshData* ) { ... }
...

6. Learn about and setup qtcreator, continue development of stand-alone frontend and module there (it blows Eclipse away for C/C++)


Some implementation specifics

Development is much easier now for two reasons:

1. After the reformulation of external interface, the standalone application which can be built easily in windows has virtually identical interface to the module as the Slicer CLI entry point. This allows rapid development in the standalone demo in windows to be copied directly to Slicer in linux.

2. Much improved knowledge of linux development, particularly being able to execute the various scripts for Slicer without doing getbuildtest.tcl; saves time.

The namespace:

namespace MeshContourEvolver {
// Input: mesh and indices of vertices for initialization
vtkPolyData* entry_main( vtkPolyData* inputMesh, const vector<int>& initPointVertexIndices );

// Input: mesh and 3D points for initialization. This is what you get
// when inputting 'fiducials' in Slicer GUI. The 3D points
// are not on the mesh, you need to first find closest points on the mesh.
vtkPolyData* entry_main( vtkPolyData* inputMesh, const vector< vector<float> >& initPoints3D );

// Input: mesh only. No initialization of points; either continue
// evolution of existing curve or only pre-compute geometry!
vtkPolyData* entry_main( vtkPolyData* inputMesh );
}

The pipeline code; e.g. external interface. Functionality is encapsulated in the vtkPolyDataAlgorithm subclasses.

// Input: mesh and indices of vertices for initialization
vtkPolyData* MeshContourEvolver::entry_main( vtkPolyData* inputMesh, 
    const vector<int>& initPointVertexIndices )
{
	// instantiate output mesh
	vtkPolyData* outputMesh = vtkPolyData::New();

	// algorithm #1: initialization of path
	vtkSmartPointer<vtkInitClosedPath> initPath = vtkSmartPointer<vtkInitClosedPath>::New();
	initPath->SetInput( inputMesh );
    
	// algorithm #2: computing the geometry (if the input data 
	// does NOT already have a scalar data set containing it)
	vtkSmartPointer<vtkComputeLocalGeometry> computeGeometry = vtkSmartPointer<vtkComputeLocalGeometry>::New();
	computeGeometry->SetInputConnection( initPath->GetOutputPort() );
    
	// algorithm #3: run curve evolution and update the scalar dataset for display
	vtkSmartPointer<vtkLevelSetMeshEvolver> evolver = vtkSmartPointer<vtkLevelSetMeshEvolver>::New();
	evolver->SetInputConnection( computeGeometry->GetOutputPort() );
    evolver->Update();

	// register and return the result of the pipeline of algorithms
	vtkSmartPointer<vtkPolyData> result = evolver->GetOutput();
	outputMesh->DeepCopy( result );

	return outputMesh;
}

References

P.A. Karasev, J.G. Malcolm, M. Niethammer, R. Kikinis, A. Tannenbaum. User-Driven 3D Mesh Region Targeting. SPIE Medical Imaging 2010.