convert polydata to image data
#include <vtkVersion.h>
#include <vtkImageData.h>
#include <vtkImageMapper3D.h>
#include <vtkImageStencil.h>
#include <vtkImageStencilData.h>
#include <vtkImageToImageStencil.h>
#include <vtkJPEGReader.h>
#include <vtkPointData.h>
#include <vtkSmartPointer.h>
#include <vtkRenderWindow.h>
#include <vtkRenderWindowInteractor.h>
#include <vtkInteractorStyleImage.h>
#include <vtkRenderer.h>
#include <vtkImageActor.h>
#include <vtkPolyDataToImageStencil.h>
#include <vtkPolyData.h>
#include <vtkSTLReader.h>
#include <vtkImageProperty.h>
#include <vtkSphereSource.h>
#define vSP vtkSmartPointer
#define vSPNew(Var, Type) vSP<Type> Var = vSP<Type>::New();
vSP<vtkImageData> ConvertPolydataToImage(vtkPolyData *polydata)
{
double spacingVal = 0.2;
double bounds[6];
polydata->GetBounds(bounds);
int dim[3];
for (int i = 0; i < 3; i++)
{
dim[i] = static_cast<int>( ceil((bounds[2 * i + 1] - bounds[2 * i]) / spacingVal) );
}
double origin[3];
origin[0] = bounds[0] + spacingVal / 2;
origin[1] = bounds[2] + spacingVal / 2;
origin[2] = bounds[4] + spacingVal / 2;
vSPNew(imageData, vtkImageData)
imageData->SetSpacing(spacingVal, spacingVal, spacingVal);
imageData->SetDimensions(dim);
imageData->SetOrigin(origin);
imageData->AllocateScalars(VTK_UNSIGNED_CHAR, 1);
imageData->SetExtent( 0, dim[0] - 1, 0, dim[1] - 1, 0, dim[2] - 1 );
// fill the imageData with foreground voxels
unsigned char inval = 255;
unsigned char outval = 0;
vtkIdType count = imageData->GetNumberOfPoints();
for (vtkIdType i = 0; i < count; ++i)
{
imageData->GetPointData()->GetScalars()->SetTuple1(i, inval);
}
// polygonal data --> imageData stencil:
vSPNew(pd2stenc, vtkPolyDataToImageStencil);
pd2stenc->SetInputData(polydata);
pd2stenc->SetOutputOrigin(origin);
pd2stenc->SetOutputSpacing(spacingVal, spacingVal, spacingVal);
pd2stenc->SetOutputWholeExtent(imageData->GetExtent());
pd2stenc->Update();
// cut the corresponding white imageData and set the background:
vSPNew(imgstenc, vtkImageStencil);
imgstenc->SetInputData(imageData);
imgstenc->SetStencilConnection(pd2stenc->GetOutputPort());
imgstenc->ReverseStencilOff();
imgstenc->SetBackgroundValue( outval );
imgstenc->Update();
imageData->DeepCopy(imgstenc->GetOutput());
return imageData;
}
int main(int, char *[])
{
setbuf( stdout, nullptr );
vtkSmartPointer<vtkSphereSource> sphereSource =
vtkSmartPointer<vtkSphereSource>::New();
sphereSource->SetRadius(20);
sphereSource->SetPhiResolution(30);
sphereSource->SetThetaResolution(30);
vtkSmartPointer<vtkPolyData> pd = sphereSource->GetOutput();
sphereSource->Update();
vSP<vtkImageData> imageData = ConvertPolydataToImage( pd );
cout << "imageData->GetNumberOfPoints: " << imageData->GetNumberOfPoints() << endl;
cout << "imageData->GetNumberOfCells: " << imageData->GetNumberOfCells() << endl;
// Create an actor
vtkSmartPointer<vtkImageActor> actor = vtkSmartPointer<vtkImageActor>::New();
actor->GetMapper()->SetInputData( imageData );
actor->SetScale( 1.0 );
actor->VisibilityOn();
actor->Update();
// Setup renderer
vtkSmartPointer<vtkRenderer> renderer = vtkSmartPointer<vtkRenderer>::New();
renderer->AddActor(actor);
renderer->ResetCamera();
// Setup render window
vtkSmartPointer<vtkRenderWindow> renderWindow = vtkSmartPointer<vtkRenderWindow>::New();
renderWindow->AddRenderer(renderer);
// Setup render window interactor
vtkSmartPointer<vtkRenderWindowInteractor> renderWindowInteractor =
vtkSmartPointer<vtkRenderWindowInteractor>::New();
// Render and start interaction
renderWindowInteractor->SetRenderWindow(renderWindow);
renderWindowInteractor->Start();
return EXIT_SUCCESS;
}
convert image to polydata
Use vtkImageMarchingCubes to generate isosurface from image.
vSPNew( marchingCubes, vtkImageMarchingCubes );
marchingCubes->SetInputData( imageData );
marchingCubes->SetValue( 0, (inval + outval)/2 );
marchingCubes->SetNumberOfContours( 1 );
marchingCubes->Update();
vSPNew( mapper, vtkPolyDataMapper );
mapper->SetInputData( marchingCubes->GetOutput() );
There are a lot of holes on the new sphere’s surface, so we may need vtkFillHolesFilter to fill these holes. The algorithm class vtkPolyDataNormals is very powerful, it can reorder the model’s internal polygons and to ensure consistent orientation across polygon neighbors.
vSPNew( marchingCubes, vtkImageMarchingCubes );
marchingCubes->SetInputData( imageData );
marchingCubes->SetValue( 0, (inval + outval)/2 );
marchingCubes->SetNumberOfContours( 1 );
marchingCubes->Update();
// Fill the holes
auto fillHoles = vtkSmartPointer<vtkFillHolesFilter>::New();
fillHoles->SetInputConnection( marchingCubes->GetOutputPort() );
fillHoles->SetHoleSize(1000.0);
// Make the triangle winding order consistent
auto normals = vtkSmartPointer<vtkPolyDataNormals>::New();
normals->SetInputConnection(fillHoles->GetOutputPort());
normals->ConsistencyOn();
normals->SplittingOff();
normals->Update();
normals->GetOutput()->GetPointData()->SetNormals(
marchingCubes->GetOutput()->GetPointData()->GetNormals());
vSPNew( mapper, vtkPolyDataMapper );
mapper->SetInputData( normals->GetOutput() );
The class vtkImageDataGeometryFilter is used to extract geometry from points, we can find that the points are not distributed on the sphere’s surface evenly.
vtkSmartPointer<vtkImageDataGeometryFilter> imageDataGeometryFilter =
vtkSmartPointer<vtkImageDataGeometryFilter>::New();
imageDataGeometryFilter->SetInputData( imageData );
imageDataGeometryFilter->Update();
vSPNew( mapper, vtkPolyDataMapper );
mapper->SetInputData( imageDataGeometryFilter->GetOutput() );