Search code examples
c++rendervtkprojectioncontour

vtk c++ update contour from contourfilter


I have a 3D .vtk model that I render and I extract the contour from the resulting image using a vtkContourFilter (using vtk version 7.0.0 on Ubuntu 16.04).

I would like to project it from different perspectives, but as I loop over the different camera positions (I checked that the camera positions are indeed changed) the interactive viewer that launches with each iteration always shows the contour from the first image.

When I output the first few coordinates of the found contour points (which I store as a vtkPolyData) I also noticed that the content in my set of contour points does not change.

I have tried some online suggestions that worked for others, such as adding:

ContFilter->Modified();
ContFilter->Update();

and

polyData->Modified(); // This is the 3D vtkPolyData that I project

and

ContFilter->SetValue(0, 10);
ContFilter->SetValue(0, 255);

As a wild guess I also tried adding:

polyData->Modified();

// Remove old links
renderWindow->RemoveRenderer(renderer);
mapper->RemoveAllInputs();


// Set new links
renderer->SetActiveCamera(camera);
renderWindow->AddRenderer(renderer);
renderer->Modified();
renderer->ResetCameraClippingRange();

renderWindow->Modified();

mapper->SetInputData(polyData);
renderWindow->Render();

within the for loop, before using the ContourFilter, but it still does not update. With that I tried everything I could think of and find online.

This is the relevant code:

   // Prepare the rendering environment to project the 3D model to an image from different perspectives
   vtkSmartPointer<vtkDataSetMapper> mapper = vtkSmartPointer<vtkDataSetMapper>::New();
   mapper->SetInputData(polyData);
   mapper->ScalarVisibilityOff();

   vtkSmartPointer<vtkActor> actor = vtkSmartPointer<vtkActor>::New();
   actor->SetMapper(mapper);
   actor->GetProperty()->SetInterpolationToFlat();

   vtkSmartPointer<vtkRenderer> renderer = vtkSmartPointer<vtkRenderer>::New();
   renderer->SetBackground(1,1,1);
   renderer->AddActor(actor);

   vtkSmartPointer<vtkCamera> camera = vtkSmartPointer<vtkCamera>::New();
   vtkSmartPointer<vtkRenderWindow> renderWindow = vtkSmartPointer<vtkRenderWindow>::New();
   renderWindow->SetOffScreenRendering(1);
   vtkSmartPointer<vtkWindowToImageFilter> windowToImageFilter = vtkSmartPointer<vtkWindowToImageFilter>::New();
   vtkSmartPointer<vtkContourFilter> ContFilter = vtkSmartPointer<vtkContourFilter>::New();
   vtkSmartPointer<vtkPolyData> contour = vtkSmartPointer<vtkPolyData>::New();

   // Loop over the camera positions. At each iteration render/project, 
   // extract the contour and finally render the 3D model and the found 
   // contour
   double * iPoint;
   double * camPos;
   double * contourStart;
   int nContours;
   for(int i=0; i<positions->GetNumberOfPoints(); i++){
      // Print the camera position
      iPoint = positions->GetPoint(i);
      std::cout << iPoint[0] << " " << iPoint[1] << " " << iPoint[2] << std::endl;

      //Move camera
      camera->SetPosition(iPoint[0], iPoint[1], iPoint[2]);
      camera->SetFocalPoint(focalPointOrig[0], focalPointOrig[1], focalPointOrig[2]);
      camera->SetViewAngle(viewAngle);
      camera->Modified();
      camera->SetRoll(90);

      // Does this help to update the view?
      polyData->Modified();

      // Remove old links and set them again
      renderWindow->RemoveRenderer(renderer);
      mapper->RemoveAllInputs();
      renderer->SetActiveCamera(camera);
      renderWindow->AddRenderer(renderer);
      renderer->Modified();
      renderer->ResetCameraClippingRange();
      renderWindow->Modified();

      // Render/project the data
      mapper->SetInputData(polyData);
      renderWindow->Render();

      // Print camera position for debugging
      camera->GetPosition(camPos);
      std::cout << camPos[0] << " " << camPos[1] << " " << camPos[2] << std::endl;

      // Get the image and apply a contourfilter
      windowToImageFilter->SetInput(renderWindow);
      windowToImageFilter->Update();
      ContFilter->SetInputConnection(windowToImageFilter->GetOutputPort());

      // Saw someone do this as a workaround for updating the view
      ContFilter->SetValue(0, 10);
      ContFilter->SetValue(0, 255);

      // Does this help to update the view?
      ContFilter->Modified();

      //Get the contour from the contourfilter
      ContFilter->Update();
      contour = ContFilter->GetOutput();

      // Print the first points coordinates to see if they changed
      contourStart = contour->GetPoint(1);
      std::cout << contourStart[0] << " " << contourStart[1] << " " << std::endl;

      // Print the number of contours to see if it may be stored as an additional contour
      nContours = ContFilter->GetNumberOfContours();
      std::cout << nContours << std::endl;


      // Render the 3D model and the found contour
      actor->GetProperty()->SetColor(0.9,0.9,0.8);

      // Create a mapper and actor of the silhouette
      vtkSmartPointer<vtkPolyDataMapper> mapper_contour = vtkSmartPointer<vtkPolyDataMapper>::New();
      mapper_contour->SetInputData(contour);

      // Try this again here
      polyData->Modified();

      vtkSmartPointer<vtkActor> actor_contour = vtkSmartPointer<vtkActor>::New();
      actor_contour->SetMapper(mapper_contour);
      actor_contour->GetProperty()->SetLineWidth(2.);

      // 2 renderers and a render window
      vtkSmartPointer<vtkRenderer> renderer1 = vtkSmartPointer<vtkRenderer>::New();
      renderer1->AddActor(actor);
      vtkSmartPointer<vtkRenderer> renderer2 = vtkSmartPointer<vtkRenderer>::New();
      renderer2->AddActor(actor_contour);

      // Set the 3D model renderer to the same perspective but don't change the camera perspective of the contour
      renderer1->SetActiveCamera(camera);

      // Setup the window
      vtkSmartPointer<vtkRenderWindow> renderwindow = vtkSmartPointer<vtkRenderWindow>::New();
      renderwindow->SetSize(1600, 800);
      renderwindow->AddRenderer(renderer1);
      renderer1->SetViewport(0., 0., 0.5, 1.);
      renderwindow->AddRenderer(renderer2);
      renderer2->SetViewport(0.5, 0., 1., 1.);

      // Setup the interactor
      vtkSmartPointer<vtkInteractorStyleTrackballCamera> style = vtkSmartPointer<vtkInteractorStyleTrackballCamera>::New();
      vtkSmartPointer<vtkRenderWindowInteractor> iren = vtkSmartPointer<vtkRenderWindowInteractor>::New();
      iren->SetRenderWindow( renderwindow);
      iren->SetInteractorStyle(style);

      // Display the coordinate system axes
      vtkSmartPointer<vtkAxesActor> axes = vtkSmartPointer<vtkAxesActor>::New();
      vtkSmartPointer<vtkOrientationMarkerWidget> widget = vtkSmartPointer<vtkOrientationMarkerWidget>::New();
      widget->SetOutlineColor( 0.9300, 0.5700, 0.1300 );
      widget->SetOrientationMarker( axes );
      widget->SetInteractor( iren );
      widget->SetViewport( 0.0, 0.0, 0.4, 0.4 );
      widget->SetEnabled( 1 );
      widget->InteractiveOn();

      // Render the 3D model and the found contour
      renderwindow->Render();
      iren->Start();

   }

Solution

  • Just found the answer.

    As mentioned in the warning in the detailed description of the vtkWindowToImageFilter class reference webpage (https://www.vtk.org/doc/nightly/html/classvtkWindowToImageFilter.html), vtkWindows generally do not rerender unless you call their Modified() function. Now my projected views are updated like I wanted.

    So I changed

    // Get the image and apply a contourfilter
    windowToImageFilter->SetInput(renderWindow);
    windowToImageFilter->Update();
    

    to

    // Get the image and apply a contourfilter
    windowToImageFilter->Modified();
    windowToImageFilter->SetInput(renderWindow);
    windowToImageFilter->Update();
    

    See here the warning text in case the link above ever stops working:

    Warning: A vtkWindow doesn't behave like other parts of the VTK pipeline: its modification time doesn't get updated when an image is rendered. As a result, naive use of vtkWindowToImageFilter will produce an image of the first image that the window rendered, but which is never updated on subsequent window updates. This behavior is unexpected and in general undesirable. To force an update of the output image, call vtkWindowToImageFilter's Modified method after rendering to the window. In VTK versions 4 and later, this filter is part of the canonical way to output an image of a window to a file (replacing the obsolete SaveImageAsPPM method for vtkRenderWindows that existed in 3.2 and earlier). Connect this filter to the output of the window, and filter's output to a writer such as vtkPNGWriter. Reading back alpha planes is dependent on the correct operation of the render window's GetRGBACharPixelData method, which in turn is dependent on the configuration of the window's alpha planes. As of VTK 4.4+, machine-independent behavior is not automatically assured because of these dependencies.