Correct way to update a vtkImageData and refresh the render window (renderWindow.Render() not working)

I have created a class that encapsulates the creation of:

  • a vtkImageData referred to here as “mesh”,
  • a lookup table,
  • a poly data set (from vtkImageDataGeometryFilter),
  • a mapper (using vtkPolyDataMapper), that associates the lookup table to it, with appropriate ranges,
  • an actor,
  • a renderer,
  • a render window, referred to here as “renderWindow”,
  • and a render window interactor, referred to as “renderWindowInteractor”,

The constructer finishes with a call to renderWindowInteractor.Initialize().

All of these objects are instance attributes of the class. I’ve skipped a few method calls to try to keep the question succint.

When instantiated, the user must input the initial scalar values of the cells using a numpy array referred to as “values”. An instance attribute called (self.) values is generated. A shallow copy of this numpy array is then taken using numpy_to_vtk (from vtk.utils.numpy_support) yielding a corresponding vtk array referred to as “values_vtk”, which is also an instance attribute of the class. The scalars of the cell data of the vtkImageData object (self.) mesh is set to this vtk array.

The real issue I am having is when updating the vtkImageData of the object when new cell data scalar values are passed.

I created a method as def update(self, values) that:

  1. self.values = values
  2. Re-assignes self.values_vtk to be equal to the vtk equivalent value of this new self.values array.
  3. self.mesh.GetCellData().SetScalars(self.values_vtk) to update mesh.
  4. self.renderWindow.Render() to refresh the window.

Firstly, I would like to know if steps 1 to 3 are okay, performance-wise, in order to update the cell scalar values of an ImageData. I had initially thought that just by changing self.values_vtk (i.e. only step 2 without step 3), the ImageData would automatically change (maybe after a call to .Modified()), but that was not the case in my tests.

Lastly, and most importantly, step 4 is not working - the window does not refresh with the changed ImageData cell scalar values. Or rather, it does so only a couple of times and then stops. When using PyCharm, if I begin the process in debug mode and set a breakpoint anywhere in the code after calling .update(), then the window consistently updates the visualization as desired, when the execution reaches the breakpoint. I tried using time.pause(0.001), but this did not work. I also tried calling self.renderWindowInteractor.ReInitialize() after step 5 but this just crashes the program.


Edit: the written class can be found attached below.

vtkVisualizationPackage.py (11.9 KB)


Edit 2: fixed something in the attached code related to the spacing input.

I am starting to believe the issue has something to do with Windows. In the following MWE, the first call, of quickly changing renders works fine. But the second one stops. I am in Windows 10.

import numpy as np
from vtkVisualizationPackage import VTKImageData2DTopology
import time

def main(n_iter=100, sleep_time=0.01):
    nels = [20, 10]
    design_domain = [1., 0.5]
    types_of_els = 3
    n_tot_els = nels[0]*nels[1]
    values = np.zeros(n_tot_els, dtype=np.int64)
    values[1::3] = 1
    values[2::3] = 2

    imageObject = VTKImageData2DTopology(nels=nels, number_of_vals=types_of_els,
                                         values=values, domain_size=design_domain)
    imageObject.zoom_camera(1.66)

    for i in range(n_iter):
        if (i % 3) == 0:
            values = np.ones(n_tot_els, dtype=np.int64)*2
            values[1::3] = 0
            values[2::3] = 1
        elif (i % 3) == 1:
            values = np.ones(n_tot_els, dtype=np.int64)
            values[1::3] = 2
            values[2::3] = 0
        else:
            values = np.zeros(n_tot_els, dtype=np.int64)
            values[1::3] = 1
            values[2::3] = 2

        imageObject.update(values)
        time.sleep(sleep_time)


if __name__ == "__main__":
    main(sleep_time=0.01)
    print(f"Finished first animation")
    main(n_iter=30, sleep_time=0.3)
    print(f"Finished second animation")


Edit 3: It seems that a quick and dirty fix would be to make the window interactor .Start() or after updating and then somehow trigger a Q click event automatically. Which is not ideal… Or maybe a method that makes the window interactive for a milisecond, after calling the .Render() method.


Edit 4: Edit 3 has been accomplished using renderWindowInteractor.ProcessEvents(). But I hope that there is a better way to accomplish this.

My best guess is that the timestamp of the data array is not being updated. You can update the timestamp manually before calling Render():

self.values_vtk.Modified()

In general, the VTK rendering pipeline won’t re-upload a VTK data array to the GPU unless the timestamp of the array has changed. If you modify a VTK data set yourself, then it is your responsibility to ensure that the timestamps of all modified arrays are updated.

Thank you for the suggestion! Though, I tried it out and it did not work.
I also attempted

self.mesh.Modified()

but that also does not refresh the window.

Maybe I need to manually create a timestamp? Or is one automatically created at first render?

Every object derived from vtkObjectBase has a timestamp, so timestamps do not have to be created, they only have to be updated.

It’s often the data arrays that need a Modified() call, rather than the data set, but of course it doesn’t hurt to do both.

Can you show the code you use to get the mesh into the renderer? What mapper are you using? Ah, I see that it goes through vtkImageDataGeometryFilter and then vtkPolyDataMapper.

I have added the python implementation of the class to the question.

I am using a vtkPolyDataMapper. It is only called once in the constructer. I’ve tried adding .Modified() and .Update() to it in my .update(values) method, but it doesn’t work.

I’m not sure if vtkImageDataGeometryFilter is designed to work with cell data, unfortunately it’s a filter that I’m not familiar with. In general a “vtkImageData” is used to store the data values for a regular grid of points (e.g. via GetPointData().SetScalars()), and then other filters and mappers will interpolate to get the values beween the points. This is e.g. how contouring and volume rendering works with images in VTK.

I looked at the vtkImageDataGeometryFilter and yes, it uses both the cell data and the point data of the input. So my next guess is that this filter itself isn’t modifying the timestamps for the arrays that it produces (which would be a bug, of course).

You can try debugging by getting the polydata that it produces and checking that the timestamps are modified when they should be.

Before that, though, you can try changing the pipeline time to see if it helps:

self.mesh.DataHasBeenGenerated()

Thank you for the suggestions David!

I tried adding both of the following before self.renderWindow.Render() in the .update(values) method:

self.mesh.DataHasBeenGenerated()
self.meshPolyData.Modified()

But that did not work.
I’ll try adding a “print(timestamps)” to check what you’ve said.

It’s difficult to debug this in debug mode in PyCharm, because when the execution is paused in a breakpoint, the window gets updated as is expected.

Rather than using breakpoints, you can add an observer for the EndEvent of the vtkImageDataGeometryFilter. I can’t remember the exact syntax, but it’s something like this:

def callback(caller, event):
    print(caller.GetOutput().GetPointData().GetMTime())

filter.AddObserver('EndEvent', callback)

Thank you David. I’ll try out what you’ve written.

In the meantime, I’ve added a Minimum Working Example to the question. I am starting to think the issue has something to do with Windows ignoring update calls since it “thinks” the window is unresponsive.

For quick updates the .update() method seems to be working. Only after the visualization window has been opened for a while, it stops refreshing and, after clicking on it, I get a “not responding” from Windows.

I’ve tested the observer to track modifications to both the PointData and the CellData of the filter.

Increasing integer numbers are being printed.

You mentioned that you are running this in PyCharm… maybe PyCharm is running Python within the PyCharm process instead of spawning a new process? This could interfere with the handling of GUI events by vtkWin32RenderWindow and vtkWin32RenderWindowInteractor. Maybe other PyCharm users (I’m not one myself) have relevant experience to share…

Thank you for the suggestion!

Though I tested running the MWE directly from windows powershell and it also has the same effect. The initial seconds or so work, then the window becomes unresponsive and stops updating.

I’ve found a Quick & Dirty fix.

After calling “self.renderWindow.Render()”, do

self.renderWindowInteractor.ProcessEvents()

at the end of the `.update(values)’ method.

Since no events are actually programmed, the window becomes interactive for a few miliseconds and it never freezes, consistently showing the updated visuals.

I’m not marking this as a solution, though. I need a shower after implementing this.


Edit: marked this as the solution, for now.

You are most likely blocking the event loop in the GUI by calling sleep(). What happens in the second call to main() if the sleep time is again set to 0.01?

Try making the update() method asynchronous.