Difference between revisions of "2010 Winter Project Week SegmentationMeshEmbeddedContours"
(4 intermediate revisions by the same user not shown) | |||
Line 9: | Line 9: | ||
==Key Investigators== | ==Key Investigators== | ||
− | * Georgia Tech: Peter Karasev, | + | * Georgia Tech: Peter Karasev, Karol Chudy, Allen Tannenbaum |
* BWH: Ron Kikinis | * BWH: Ron Kikinis | ||
Line 35: | Line 35: | ||
<h3>Progress</h3> | <h3>Progress</h3> | ||
− | A version of this software exists in Slicer3, with a standalone version being updated to encapsulate the functionality as a vtkPolyDataFilter. It has been experimentally verified to give useful segmentation results for meshes involving bone fractures (separating smooth non-fracture surface from jagged break zone). Some of the robustness issues (mesh connectivity, neighborhood sizes, differentiable basis functions) that have been seen to make a user experience inconsistent have been formulated as mathematical problems, allowing provable algorithm improvements to move forward. </div> | + | A version of this software exists in Slicer3, with a standalone version being updated to encapsulate the functionality as a vtkPolyDataFilter. It has been experimentally verified to give useful segmentation results for meshes involving bone fractures (separating smooth non-fracture surface from jagged break zone). Some of the robustness issues (mesh connectivity, neighborhood sizes, differentiable basis functions) that have been seen to make a user experience inconsistent have been formulated as mathematical problems, allowing provable algorithm improvements to move forward. |
+ | |||
+ | As part of the restructuring effort, a data structure outline of the internals of the module has been made. It makes obvious some aspects that should be refactored. | ||
+ | |||
+ | <gallery> | ||
+ | Image:currentform.png|Current Internal Data Structures of Module | ||
+ | </gallery> | ||
+ | |||
+ | |||
+ | However the internals are functional, so it was decided that the priority is the *external* interface, which should look like a pipeline of vtkPolyDataAlgorithms, as shown below. | ||
+ | |||
+ | <gallery> | ||
+ | Image:structure.png|Pipeline of vtkPolyDataAlgorithm subclasses encapsulating module internals. | ||
+ | </gallery> | ||
+ | |||
+ | The result is that the module code SparseFieldLevelSetContour.cxx used to interface with the module goes from including 8 header files and having access to low-level implementation details to including only one header. The included header contains a namespace with non-member non-friend functions with overloaded entry points to the module. | ||
+ | |||
+ | 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. | ||
+ | |||
+ | 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: | ||
+ | |||
+ | <pre> | ||
+ | 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 ); | ||
+ | } | ||
+ | </pre> | ||
+ | The pipeline code; e.g. external interface. Functionality is encapsulated in the vtkPolyDataAlgorithm subclasses. | ||
+ | |||
+ | <pre> | ||
+ | // 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; | ||
+ | } | ||
+ | </pre> | ||
+ | |||
+ | </div> | ||
</div> | </div> | ||
Latest revision as of 02:35, 7 April 2010
Home < 2010 Winter Project Week SegmentationMeshEmbeddedContoursKey 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
Our approach for analyzing surface-embedded segmentation is separated into two parts. First, the notion of surface geometry for a polygonal mesh must be suitably defined. To this end, we study numerically stable and robust methods of estimating the geometry of the surface (mean curvature, tangent plane bases, second fundamental form, etc) and that of the segmenting curve. Second, we explore definitions of functionals whose extrema lead to useful geometric flows on the surface. The techniques for these two tasks must always consider the difficulty imposed by data purely in the form of a polygon mesh. This project makes extensive use of the VTK library to simplify data storage, communication, and visualization.
Our plan for the project week is to first come up with the best encapsulation framework for data storage and persistence, making the existing Slicer3 implementation feature-complete. Secondly, we seek to explore reformulation of existing image volume segmentation and mesh generation such that the numerical stability and accuracy of subsequent surface segmentation is taken into consideration.
Progress
A version of this software exists in Slicer3, with a standalone version being updated to encapsulate the functionality as a vtkPolyDataFilter. It has been experimentally verified to give useful segmentation results for meshes involving bone fractures (separating smooth non-fracture surface from jagged break zone). Some of the robustness issues (mesh connectivity, neighborhood sizes, differentiable basis functions) that have been seen to make a user experience inconsistent have been formulated as mathematical problems, allowing provable algorithm improvements to move forward.
As part of the restructuring effort, a data structure outline of the internals of the module has been made. It makes obvious some aspects that should be refactored.
However the internals are functional, so it was decided that the priority is the *external* interface, which should look like a pipeline of vtkPolyDataAlgorithms, as shown below.
The result is that the module code SparseFieldLevelSetContour.cxx used to interface with the module goes from including 8 header files and having access to low-level implementation details to including only one header. The included header contains a namespace with non-member non-friend functions with overloaded entry points to the module.
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.
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. To appear in SPIE Medical Imaging 2010.