[vtkusers] DICOM to Model Mesh
Harold
boogiedoll at yahoo.com
Tue Nov 26 23:20:16 EST 2013
Hi Miro,
Thank you for the guide. I'm new to vtk so while messing around, I managed to apply the followings to a set of dicom images (in a very crude way) and save it as a .vtk file.
1. Marching Cubes - get mesh
2. Decimation - reduce the number of triangles
3. Smoothing - smooth the mesh
4. Cropping - select the largest region using connectivity filter
I guess my next step is to find out how to do pre-processing and refining of the defected mesh, and also making statistical models (if there are enough training images...).
By the way, here's my crude code based on the the MarchingCubes Example at http://www.vtk.org/Wiki/VTK/Examples/Cxx/Modelling/MarchingCubes". Hopefully it can be a small help to other novice like me out there.
/////////////////////////////////
int main(int argc, char *argv[])
{
vtkSmartPointer<vtkImageData> volume =
vtkSmartPointer<vtkImageData>::New();
double isoValue;
if (argc < 3)
{
// Create a sphere if there's no dicom input
vtkSmartPointer<vtkSphereSource> sphereSource =
vtkSmartPointer<vtkSphereSource>::New();
sphereSource->SetPhiResolution(20);
sphereSource->SetThetaResolution(20);
sphereSource->Update();
double bounds[6];
sphereSource->GetOutput()->GetBounds(bounds);
for (unsigned int i = 0; i < 6; i += 2)
{
double range = bounds[i+1] - bounds[i];
bounds[i] = bounds[i] - .1 * range;
bounds[i+1] = bounds[i+1] + .1 * range;
}
vtkSmartPointer<vtkVoxelModeller> voxelModeller =
vtkSmartPointer<vtkVoxelModeller>::New();
voxelModeller->SetSampleDimensions(50,50,50);
voxelModeller->SetModelBounds(bounds);
voxelModeller->SetScalarTypeToFloat();
voxelModeller->SetMaximumDistance(.1);
voxelModeller->SetInputConnection(sphereSource->GetOutputPort());
voxelModeller->Update();
isoValue = 0.5;
volume->DeepCopy(voxelModeller->GetOutput());
}
else
{
// Read dicom image when dicom image folder path is specified
vtkSmartPointer<vtkDICOMImageReader> reader =
vtkSmartPointer<vtkDICOMImageReader>::New();
reader->SetDirectoryName(argv[1]);
reader->Update();
volume->DeepCopy(reader->GetOutput());
isoValue = atof(argv[2]);
}
// Use MarchingCubes to generate iso-surface
vtkSmartPointer<vtkMarchingCubes> surface =
vtkSmartPointer<vtkMarchingCubes>::New();
#if VTK_MAJOR_VERSION <= 5
surface->SetInput(volume);
#else
surface->SetInputData(volume);
#endif
surface->ComputeNormalsOn();
surface->ComputeScalarsOn();
surface->SetValue(0, isoValue);
std::cout << "Marching Cube finished..." << std::endl;
// Create polydata from iso-surface
vtkSmartPointer<vtkPolyData> marched =
vtkSmartPointer<vtkPolyData>::New();
surface->Update();
marched->DeepCopy(surface->GetOutput());
std::cout<<"Number of points: " << marched->GetNumberOfPoints() << std::endl;
// Decimation to reduce the number of triangles
vtkSmartPointer<vtkDecimatePro> decimator =
vtkDecimatePro::New();
decimator->SetInputData(marched);
decimator->SetTargetReduction(0.5);
decimator->SetPreserveTopology(1);
decimator->Update();
std::cout << "Decimation finished...." << std::endl;
// Smoothing
vtkSmartPointer<vtkSmoothPolyDataFilter> smoother = vtkSmartPointer<vtkSmoothPolyDataFilter>::New();
smoother->SetInputData(decimator->GetOutput());
smoother->SetNumberOfIterations(5);
smoother->SetFeatureAngle(60);
smoother->SetRelaxationFactor(0.05);
smoother->FeatureEdgeSmoothingOff();
std::cout << "Smoothing mesh finished...." << std::endl;
// Select the largest region
vtkSmartPointer<vtkPolyDataConnectivityFilter> connectivityFilter =
vtkSmartPointer<vtkPolyDataConnectivityFilter>::New();
connectivityFilter->SetInputConnection(smoother->GetOutputPort());
connectivityFilter->ScalarConnectivityOff();
connectivityFilter->SetExtractionModeToLargestRegion();
connectivityFilter->Update();
// Create final polygon mesh
vtkSmartPointer<vtkPolyData> mesh =
vtkSmartPointer<vtkPolyData>::New();
mesh->ShallowCopy(connectivityFilter->GetOutput());
std::cout<<"Number of points in the final polygon: " << mesh->GetNumberOfPoints() << std::endl;
// Visualization
vtkSmartPointer<vtkRenderer> renderer =
vtkSmartPointer<vtkRenderer>::New();
renderer->SetBackground(.1, .2, .3);
vtkSmartPointer<vtkRenderWindow> renderWindow =
vtkSmartPointer<vtkRenderWindow>::New();
renderWindow->AddRenderer(renderer);
vtkSmartPointer<vtkRenderWindowInteractor> interactor =
vtkSmartPointer<vtkRenderWindowInteractor>::New();
interactor->SetRenderWindow(renderWindow);
vtkSmartPointer<vtkPolyDataMapper> mapper =
vtkSmartPointer<vtkPolyDataMapper>::New();
mapper->SetInputData(mesh);
mapper->ScalarVisibilityOff();
vtkSmartPointer<vtkActor> actor =
vtkSmartPointer<vtkActor>::New();
actor->SetMapper(mapper);
renderer->AddActor(actor);
renderWindow->Render();
interactor->Start();
// Save the mesh as a .vtk file
vtkSmartPointer<vtkPolyDataWriter> writer = vtkPolyDataWriter::New();
writer->SetFileName("mesh.vtk");
writer->SetInputData(mesh);
writer->SetFileTypeToASCII();
writer->Write();
return EXIT_SUCCESS;
}
Best Regards,
Harold
On Wednesday, November 27, 2013 12:06 PM, Miro Drahos <mdrahos at robodoc.com> wrote:
This is basically a segmentation problem, which in general is a
difficult problem.
One such pipeline could be as follows:
1.) image preprocessing (cropping, smoothing, etc).
2.) thresholding to get a filled binary mask of your object (vertebra)
3.) running Marching Cubes to get a mesh.
This is a very crude and basic approach, but something to get you
started. Note that marching cubes might give you a mesh with topological
defects (holes, handles), from the noise in your data.
There are a variety of other sophisticated algorithms, e.g. using
statistical methods if you have some already segmented data (like
creating a mean shape from the previously segmented data, and using PCA
to do shape analysis, or some machine learning approach).
Another approach would be to use deformable models, i.e. define forces
that would (iteratively) drive an initial mesh to fit the image. The
forces would be based on image intensity, gradient and/or laplacian, and
internal forces to keep the mesh regular and relatively smooth.
There is a lot that has been done in this domain, so look at some
articles on segmentation.
HTH,
Miro
On 11/25/2013 06:20 AM, Boogiedoll wrote:
> Hi,
> I would like to ask the experts about common way in vtk to create a spine bone (e.g L1) mesh from a CT DICOM stack, including the pre-processing.
>
> Thanks & Regards,
> Harold
> _______________________________________________
> Powered by www.kitware.com
>
> Visit other Kitware open-source projects at http://www.kitware.com/opensource/opensource.html
>
> Please keep messages on-topic and check the VTK FAQ at: http://www.vtk.org/Wiki/VTK_FAQ
>
> Follow this link to subscribe/unsubscribe:
> http://www.vtk.org/mailman/listinfo/vtkusers
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://www.vtk.org/pipermail/vtkusers/attachments/20131126/21ded969/attachment.htm>
More information about the vtkusers
mailing list