CompareExtractSurface
VTKExamples/Cxx/Points/CompareExtractSurface
Description¶
This example compares three surface reconstruction algorithms:
We usually try to keep examples short, but this example teaches several concepts.
- Compare multiple algorithms
- Time multiple algorithms
- Reuse code from other examples
- Use multiple remote modules
- Generate figures for papers
Warning
This example runs longer than most. Be patient.
Info
See PoissonReconstruction Remote Module to configure.
Info
See Powercrust Remote Module to configure.
Code¶
CompareExtractSurface.cxx
#include <vtkSmartPointer.h> #include <vtkBYUReader.h> #include <vtkPLYReader.h> #include <vtkXMLPolyDataReader.h> #include <vtkOBJReader.h> #include <vtkSTLReader.h> #include <vtkPointSource.h> #include <vtkPCANormalEstimation.h> #include <vtkSignedDistance.h> #include <vtkExtractSurface.h> #include <vtkExtractSurface.h> #include <vtkPoissonReconstruction.h> #include <vtkPowerCrustSurfaceReconstruction.h> #include <vtkPointData.h> #include <vtkCoordinate.h> #include <vtkTextMapper.h> #include <vtkTextProperty.h> #include <vtkActor2D.h> #include <vtkPolyLine.h> #include <vtkActor.h> #include <vtkPolyDataMapper2D.h> #include <vtkPolyDataMapper.h> #include <vtkProperty.h> #include <vtkProperty2D.h> #include <vtkRenderWindow.h> #include <vtkRenderWindowInteractor.h> #include <vtkRenderer.h> #include <vtkCamera.h> #include <vtkNamedColors.h> #include <vtkTimerLog.h> #include <vtksys/SystemTools.hxx> #include <sstream> #include <vector> #include <string> namespace { vtkSmartPointer<vtkPolyData> ReadPolyData(const char *fileName); vtkSmartPointer<vtkPolyDataAlgorithm> MakeExtractSurface(vtkPolyData *); vtkSmartPointer<vtkPolyDataAlgorithm> MakePoissonExtractSurface(vtkPolyData *); vtkSmartPointer<vtkPolyDataAlgorithm> MakePowercrustExtractSurface(vtkPolyData *); void MakeViewportGrid( std::vector<vtkSmartPointer<vtkRenderer>> &renderers, unsigned int renderersize, unsigned int xGridDimensions, unsigned int yGridDimensions); void ViewportBorder(vtkSmartPointer<vtkRenderer> &renderer, double *color, bool last = false); } int main (int argc, char *argv[]) { vtkSmartPointer<vtkPolyData> polyData = ReadPolyData(argc > 1 ? argv[1] : "");; std::cout << "# of points: " << polyData->GetNumberOfPoints() << std::endl; vtkSmartPointer<vtkNamedColors> colors = vtkSmartPointer<vtkNamedColors>::New(); vtkSmartPointer<vtkRenderWindow> renderWindow = vtkSmartPointer<vtkRenderWindow>::New(); std::vector<vtkSmartPointer<vtkPolyDataAlgorithm> > surfaceObjects; surfaceObjects.push_back(MakeExtractSurface(polyData.GetPointer())); surfaceObjects.push_back(MakePoissonExtractSurface(polyData.GetPointer())); surfaceObjects.push_back(MakePowercrustExtractSurface(polyData.GetPointer())); std::vector<vtkSmartPointer<vtkRenderer>> renderers; // One camera for all vtkSmartPointer<vtkCamera> camera = vtkSmartPointer<vtkCamera>::New(); for (size_t i = 0; i < surfaceObjects.size(); ++i) { vtkSmartPointer<vtkTimerLog> timer = vtkSmartPointer<vtkTimerLog>::New(); timer->StartTimer(); surfaceObjects[i]->Update(); timer->StopTimer(); vtkSmartPointer<vtkPolyDataMapper> surfaceMapper = vtkSmartPointer<vtkPolyDataMapper>::New(); surfaceMapper->SetInputConnection(surfaceObjects[i]->GetOutputPort()); vtkSmartPointer<vtkProperty> back = vtkSmartPointer<vtkProperty>::New(); back->SetDiffuseColor(colors->GetColor3d("banana").GetData()); back->SetSpecular(.6); back->SetSpecularPower(50.0); vtkSmartPointer<vtkActor> surfaceActor = vtkSmartPointer<vtkActor>::New(); surfaceActor->SetMapper(surfaceMapper); surfaceActor->GetProperty()->SetDiffuseColor(colors->GetColor3d("Tomato").GetData()); surfaceActor->GetProperty()->SetSpecular(.6); surfaceActor->GetProperty()->SetSpecularPower(50.0); surfaceActor->SetBackfaceProperty(back); // Setup renderer vtkSmartPointer<vtkRenderer> renderer = vtkSmartPointer<vtkRenderer>::New(); renderer->AddActor(surfaceActor); renderer->SetBackground(colors->GetColor3d("SlateGray").GetData()); renderer->SetActiveCamera(camera); renderer->GetActiveCamera()->SetPosition (-1, 0, 0); renderer->GetActiveCamera()->SetFocalPoint (0, 1, 0); renderer->GetActiveCamera()->SetViewUp (0, 0, 1); renderer->GetActiveCamera()->Dolly(5); renderer->ResetCamera(); renderer->ResetCameraClippingRange(); renderers.push_back(renderer); renderWindow->AddRenderer(renderer); vtkSmartPointer<vtkTextProperty> textProperty = vtkSmartPointer<vtkTextProperty>::New(); textProperty->SetFontSize(15); textProperty->SetJustificationToCentered(); std::stringstream ss; ss << surfaceObjects[i]->GetClassName() << std::endl; ss << "# of Polys: " << surfaceObjects[i]->GetOutput()->GetNumberOfPolys() << std::endl; ss << "Time: " << timer->GetElapsedTime() << std::endl; vtkSmartPointer<vtkTextMapper> textMapper = vtkSmartPointer<vtkTextMapper>::New(); textMapper->SetInput(ss.str().c_str()); textMapper->SetTextProperty(textProperty); vtkSmartPointer<vtkActor2D> textActor = vtkSmartPointer<vtkActor2D>::New(); textActor->SetMapper(textMapper); textActor->SetPosition(200, 0); textActor->GetProperty()->SetLineWidth(4.0); // Line Width renderer->AddViewProp(textActor); } unsigned int rendererSize = 400; unsigned int xGridDimensions = 3; unsigned int yGridDimensions = 1; renderWindow->SetSize( rendererSize * xGridDimensions, rendererSize * yGridDimensions); MakeViewportGrid(renderers, rendererSize, xGridDimensions, yGridDimensions); for (size_t i = 0; i < renderers.size(); ++i) { ViewportBorder(renderers[i], colors->GetColor3d("Gold").GetData(), i == renderers.size() - 1); } vtkSmartPointer<vtkRenderWindowInteractor> iren = vtkSmartPointer<vtkRenderWindowInteractor>::New(); iren->SetRenderWindow(renderWindow); iren->Initialize(); iren->Start(); return EXIT_SUCCESS; } namespace { vtkSmartPointer<vtkPolyData> ReadPolyData(const char *fileName) { vtkSmartPointer<vtkPolyData> polyData; std::string extension = vtksys::SystemTools::GetFilenameExtension(std::string(fileName)); if (extension == ".ply") { vtkSmartPointer<vtkPLYReader> reader = vtkSmartPointer<vtkPLYReader>::New(); reader->SetFileName (fileName); reader->Update(); polyData = reader->GetOutput(); } else if (extension == ".vtp") { vtkSmartPointer<vtkXMLPolyDataReader> reader = vtkSmartPointer<vtkXMLPolyDataReader>::New(); reader->SetFileName (fileName); reader->Update(); polyData = reader->GetOutput(); } else if (extension == ".obj") { vtkSmartPointer<vtkOBJReader> reader = vtkSmartPointer<vtkOBJReader>::New(); reader->SetFileName (fileName); reader->Update(); polyData = reader->GetOutput(); } else if (extension == ".stl") { vtkSmartPointer<vtkSTLReader> reader = vtkSmartPointer<vtkSTLReader>::New(); reader->SetFileName (fileName); reader->Update(); polyData = reader->GetOutput(); } else if (extension == ".g") { vtkSmartPointer<vtkBYUReader> reader = vtkSmartPointer<vtkBYUReader>::New(); reader->SetGeometryFileName (fileName); reader->Update(); polyData = reader->GetOutput(); } else if (extension == ".g") { vtkSmartPointer<vtkBYUReader> reader = vtkSmartPointer<vtkBYUReader>::New(); reader->SetGeometryFileName (fileName); reader->Update(); polyData = reader->GetOutput(); } else { vtkSmartPointer<vtkPointSource> points = vtkSmartPointer<vtkPointSource>::New(); points->SetNumberOfPoints(1000); points->SetRadius(1.0); points->SetCenter(vtkMath::Random(-1, 1), vtkMath::Random(-1, 1), vtkMath::Random(-1, 1)); points->SetDistributionToShell(); points->Update(); polyData = points->GetOutput(); } return polyData; } vtkSmartPointer<vtkPolyDataAlgorithm> MakeExtractSurface(vtkPolyData *polyData) { double bounds[6]; polyData->GetBounds(bounds); double range[3]; for (int i = 0; i < 3; ++i) { range[i] = bounds[2*i + 1] - bounds[2*i]; } int sampleSize = polyData->GetNumberOfPoints() * .00005; if (sampleSize < 10) { sampleSize = 50; } // Do we need to estimate normals? vtkSmartPointer<vtkSignedDistance> distance = vtkSmartPointer<vtkSignedDistance>::New(); if (polyData->GetPointData()->GetNormals()) { std::cout << "ExtractSurface: Using normals from input file" << std::endl; distance->SetInputData (polyData); } else { std::cout <<" ExtractSurface: Estimating normals using PCANormalEstimation" << std::endl; vtkSmartPointer<vtkPCANormalEstimation> normals = vtkSmartPointer<vtkPCANormalEstimation>::New(); normals->SetInputData (polyData); normals->SetSampleSize(sampleSize); normals->SetNormalOrientationToGraphTraversal(); normals->FlipNormalsOn(); distance->SetInputConnection (normals->GetOutputPort()); } int dimension = 256; double radius; radius = std::max(std::max(range[0], range[1]), range[2]) / static_cast<double>(dimension) * 4; // ~4 voxels distance->SetRadius(radius); distance->SetDimensions(dimension, dimension, dimension); distance->SetBounds( bounds[0] - range[0] * .1, bounds[1] + range[0] * .1, bounds[2] - range[1] * .1, bounds[3] + range[1] * .1, bounds[4] - range[2] * .1, bounds[5] + range[2] * .1); vtkSmartPointer<vtkExtractSurface> surface = vtkSmartPointer<vtkExtractSurface>::New(); surface->SetInputConnection (distance->GetOutputPort()); surface->SetRadius(radius * .99); return surface; } vtkSmartPointer<vtkPolyDataAlgorithm> MakePoissonExtractSurface(vtkPolyData *polyData) { double bounds[6]; polyData->GetBounds(bounds); double range[3]; for (int i = 0; i < 3; ++i) { range[i] = bounds[2*i + 1] - bounds[2*i]; } int sampleSize = polyData->GetNumberOfPoints() * .00005; if (sampleSize < 10) { sampleSize = 10; } vtkSmartPointer<vtkPoissonReconstruction> surface = vtkSmartPointer<vtkPoissonReconstruction>::New(); surface->SetDepth(12); if (polyData->GetPointData()->GetNormals()) { std::cout << "PoissonReconstruction: Using normals from input file" << std::endl; surface->SetInputData (polyData); } else { std::cout << "ExtractSurface: Estimating normals using PCANormalEstimation" << std::endl; vtkSmartPointer<vtkPCANormalEstimation> normals = vtkSmartPointer<vtkPCANormalEstimation>::New(); normals->SetInputData (polyData); normals->SetSampleSize(sampleSize); normals->SetNormalOrientationToGraphTraversal(); normals->FlipNormalsOff(); surface->SetInputConnection(normals->GetOutputPort()); } return surface; } vtkSmartPointer<vtkPolyDataAlgorithm> MakePowercrustExtractSurface(vtkPolyData *polyData) { vtkSmartPointer<vtkPowerCrustSurfaceReconstruction> surface = vtkSmartPointer<vtkPowerCrustSurfaceReconstruction>::New(); surface->SetInputData (polyData); return surface; } void MakeViewportGrid( std::vector<vtkSmartPointer<vtkRenderer>> &renderers, unsigned int rendererSize, unsigned int xGridDimensions, unsigned int yGridDimensions) { // // Setup viewports for the renderers int index = 0; for (int row = 0; row < static_cast<int>(yGridDimensions); row++) { for (int col = 0; col < static_cast<int>(xGridDimensions); col++) { int index = row * xGridDimensions + col; // (xmin, ymin, xmax, ymax) double viewport[4] = { static_cast<double>(col) * rendererSize / (xGridDimensions * rendererSize), static_cast<double>(yGridDimensions - (row + 1)) * rendererSize / (yGridDimensions * rendererSize), static_cast<double>(col + 1) * rendererSize / (xGridDimensions * rendererSize), static_cast<double>(yGridDimensions - row) * rendererSize / (yGridDimensions * rendererSize)}; renderers[index]->SetViewport(viewport); } } } // draw the borders of a renderer's viewport void ViewportBorder(vtkSmartPointer<vtkRenderer> &renderer, double *color, bool last) { // points start at upper right and proceed anti-clockwise vtkSmartPointer<vtkPoints> points = vtkSmartPointer<vtkPoints>::New(); points->SetNumberOfPoints(4); points->InsertPoint(0, 1, 1, 0); points->InsertPoint(1, 0, 1, 0); points->InsertPoint(2, 0, 0, 0); points->InsertPoint(3, 1, 0, 0); // create cells, and lines vtkSmartPointer<vtkCellArray> cells = vtkSmartPointer<vtkCellArray>::New(); cells->Initialize(); vtkSmartPointer<vtkPolyLine> lines = vtkSmartPointer<vtkPolyLine>::New(); // only draw last line if this is the last viewport // this prevents double vertical lines at right border // if different colors are used for each border, then do // not specify last if (last) { lines->GetPointIds()->SetNumberOfIds(5); } else { lines->GetPointIds()->SetNumberOfIds(4); } for(unsigned int i = 0; i < 4; ++i) { lines->GetPointIds()->SetId(i,i); } if (last) { lines->GetPointIds()->SetId(4, 0); } cells->InsertNextCell(lines); // now make the polydata and display it vtkSmartPointer<vtkPolyData> poly = vtkSmartPointer<vtkPolyData>::New(); poly->Initialize(); poly->SetPoints(points); poly->SetLines(cells); // use normalized viewport coordinates since // they are independent of window size vtkSmartPointer<vtkCoordinate> coordinate = vtkSmartPointer<vtkCoordinate>::New(); coordinate->SetCoordinateSystemToNormalizedViewport(); vtkSmartPointer<vtkPolyDataMapper2D> mapper = vtkSmartPointer<vtkPolyDataMapper2D>::New(); mapper->SetInputData(poly); mapper->SetTransformCoordinate(coordinate); vtkSmartPointer<vtkActor2D> actor = vtkSmartPointer<vtkActor2D>::New(); actor->SetMapper(mapper); actor->GetProperty()->SetColor(color); // line width should be at least 2 to be visible at extremes actor->GetProperty()->SetLineWidth(4.0); // Line Width renderer->AddViewProp(actor); } }
CMakeLists.txt¶
cmake_minimum_required(VERSION 2.8) PROJECT(CompareExtractSurface) find_package(VTK REQUIRED) include(${VTK_USE_FILE}) add_executable(CompareExtractSurface MACOSX_BUNDLE CompareExtractSurface.cxx ) target_link_libraries(CompareExtractSurface ${VTK_LIBRARIES})
Download and Build CompareExtractSurface¶
Click here to download CompareExtractSurface and its CMakeLists.txt file. Once the tarball CompareExtractSurface.tar has been downloaded and extracted,
cd CompareExtractSurface/build
If VTK is installed:
cmake ..
If VTK is not installed but compiled on your system, you will need to specify the path to your VTK build:
cmake -DVTK_DIR:PATH=/home/me/vtk_build ..
Build the project:
make
and run it:
./CompareExtractSurface
WINDOWS USERS
Be sure to add the VTK bin directory to your path. This will resolve the VTK dll's at run time.