1
votes

TL;DR

I have a pipeline which reads an image and displays a mesh using VTK; upon changing the input to the image reader and updating the pipeline, the mesh doesn't update until I interact with the window.

The Long Version

I have a directory with a sequence of segmentation files (i.e., 3D image volumes where pixel values correspond to structures in a corresponding image) which show how a structure changes over time. I've written a utility in VTK which allows me to load the first image in the directory, visualize a label as a mesh, and "step" forwards or backwards using the arrow keys by changing the file name of the input image and updating the pipeline. This very nearly works--the only issue is that, after updating the pipeline, the meshes don't update until I interact with the window (simply clicking anywhere in the window causes the mesh to update, for example).

What I've Tried:

  1. Calling Update() on the reader:

    this->reader->Update();
    
  2. Calling Modified() on the actors:

    const auto actors = this->GetCurrentRenderer()->GetActors();
    actors->InitTraversal();
    for (vtkIdType i = 0; i < actors->GetNumberOfItems(); ++i)
      {
      actors->GetNextActor()->Modified();
      }
    
  3. Calling Render() on the current render window:

    this->GetCurrentRenderer()->Render();
    

MCVE:

NOTE: the reader is updated in KeyPressInteractorStyle::UpdateReader().

// VTK
#include <vtkSmartPointer.h>
#include <vtkNIFTIImageReader.h>
#include <vtkPolyDataMapper.h>
#include <vtkActor.h>
#include <vtkRenderer.h>
#include <vtkRenderWindow.h>
#include <vtkRenderWindowInteractor.h>
#include <vtkCamera.h>
#include <vtkDiscreteMarchingCubes.h>
#include <vtkProperty.h>
#include <vtkInteractorStyleTrackballCamera.h>

// Define interaction style
class KeyPressInteractorStyle : public vtkInteractorStyleTrackballCamera
{
  public:
    static KeyPressInteractorStyle* New();
    vtkTypeMacro(KeyPressInteractorStyle, vtkInteractorStyleTrackballCamera);

    virtual void OnKeyPress() 
    {
      // Get the keypress
      vtkRenderWindowInteractor *rwi = this->Interactor;
      std::string key = rwi->GetKeySym();

      // Output the key that was pressed
      std::cout << "Pressed " << key << std::endl;

      // Handle an arrow key
      if(key == "Down" || key == "Right")
        {
        this->index += 1;
        this->UpdateReader();
        }

      // Handle an arrow key
      if(key == "Up" || key == "Left")
        {
        this->index -= 1;
        this->UpdateReader();
        }

      // Forward events
      vtkInteractorStyleTrackballCamera::OnKeyPress();
    }

  void UpdateReader()
    {
    std::cout << "Frame: " << this->index << std::endl;
    const auto fn = this->directory + std::to_string(this->index) + ".nii.gz";
    std::cout << fn << std::endl;
    this->reader->SetFileName( fn.c_str() );
    this->reader->Update();
    const auto actors = this->GetCurrentRenderer()->GetActors();
    actors->InitTraversal();
    for (vtkIdType i = 0; i < actors->GetNumberOfItems(); ++i)
      {
      actors->GetNextActor()->Modified();
      }
    this->GetCurrentRenderer()->Render();
    }
  unsigned int index = 0;
  std::string directory;
  vtkSmartPointer<vtkNIFTIImageReader> reader;
};
vtkStandardNewMacro(KeyPressInteractorStyle);

int
main( int argc, char ** argv )
{

  std::string dn = argv[1];

  const auto renderer = vtkSmartPointer<vtkRenderer>::New();

  unsigned int frameid = 0;

  const auto reader = vtkSmartPointer<vtkNIFTIImageReader>::New();
  reader->SetFileName( (dn + std::to_string(frameid) + ".nii.gz").c_str() );

  const auto cubes = vtkSmartPointer<vtkDiscreteMarchingCubes>::New();
  cubes->SetInputConnection( reader->GetOutputPort() );

  cubes->SetValue( 0, 1 );

  const auto mapper = vtkSmartPointer<vtkPolyDataMapper>::New();
  mapper->SetInputConnection( cubes->GetOutputPort() );
  mapper->ScalarVisibilityOff();
  const auto actor = vtkSmartPointer<vtkActor>::New();
  actor->SetMapper( mapper );
  renderer->AddActor( actor );

  const auto window = vtkSmartPointer<vtkRenderWindow>::New();
  window->AddRenderer( renderer );
  const auto interactor = vtkSmartPointer<vtkRenderWindowInteractor>::New();

  const auto style = vtkSmartPointer<KeyPressInteractorStyle>::New();
  style->reader = reader;
  style->directory = dn;
  interactor->SetInteractorStyle( style );
  style->SetCurrentRenderer( renderer );

  interactor->SetRenderWindow( window );

  window->Render();

  interactor->Start();

  return EXIT_SUCCESS;

}
1

1 Answers

3
votes

You have actually not tried to call Render() on the current render window, you are calling it on the current renderer, which are two different things. As the documentation of vtkRenderer::Renderer() states,

CALLED BY vtkRenderWindow ONLY.

End-user pass your way and call vtkRenderWindow::Render(). Create an image. This is a superclass method which will in turn call the DeviceRender method of Subclasses of vtkRenderer.

So change this->GetCurrentRenderer()->Render(); to this->GetCurrentRenderer()->GetRenderWindow()->Render();