VTK Update Position for Multiple Rendering Windows

I had a problem trying to run multiple rendering windows in a Python VTK application that I am writing. The application is an attempt to visualize a three-dimensional model in two separate representations for a stereo application (i.e. left render and right render), but I have a problem updating cameras of each window at the same time. Currently, I have two almost identical pipelines, each with its own vtkCamera , vtkRenderWindow , vtkRenderer and vtkRenderWindowInteractor , with the only difference being that the right camera is positioned 30 units on the X axis.

Each of the interactive windows of the rendering window is updated using the vtkRenderWindowInteractor.AddObserver() method, which calls a simple function to reset the cameras to their original position and orientation. The biggest problem is that it only seems to be happening in one window at a time, in particular the window is in focus at that time. It is as if the interactor timer just went off after the interactor loses focus. In addition, when I hold the mouse (and thus move the camera around), the rendered image starts to β€œdrift”, returning to a more or less correct position, although I hard-coded the coordinates in the function.

Obviously, I am very new to VTK, and a lot of what is happening is rather confusing since there is so much hiding in the backend, so it would be surprising to get some help on this. My code is below. Thanks guys!

 from vtk import* from parse import * import os import time, signal, threading def ParseSIG(signum, stack): print signum return class vtkGyroCallback(): def __init__(self): pass def execute(self, obj, event): #Modified segment to accept input for leftCam position gyro = (raw_input()) xyz = parse("{} {} {}", gyro) #This still prints every 100ms, but camera doesn't update! print xyz #These arguments are updated and the call is made. self.leftCam.SetPosition(float(xyz[0]), float(xyz[1]), float(xyz[2])) self.leftCam.SetFocalPoint(0,0,0) self.leftCam.SetViewUp(0,1,0) self.leftCam.OrthogonalizeViewUp() self.rightCam.SetPosition(10, 40, 100) self.rightCam.SetFocalPoint(0,0,0) self.rightCam.SetViewUp(0,1,0) self.rightCam.OrthogonalizeViewUp() #Just a guess obj.Update() return def main(): # create two cameras cameraR = vtkCamera() cameraR.SetPosition(0,0,200) cameraR.SetFocalPoint(0,0,0) cameraL = vtkCamera() cameraL.SetPosition(40,0,200) cameraL.SetFocalPoint(0,0,0) # create a rendering window and renderer renR = vtkRenderer() renR.SetActiveCamera(cameraR) renL = vtkRenderer() renL.SetActiveCamera(cameraL) # create source reader = vtkPolyDataReader() path = "/home/compilezone/Documents/3DSlicer/SlicerScenes/LegoModel-6_25/Model_5_blood.vtk" reader.SetFileName(path) reader.Update() # create render window renWinR = vtkRenderWindow() renWinR.AddRenderer(renR) renWinR.SetWindowName("Right") renWinL = vtkRenderWindow() renWinL.AddRenderer(renL) renWinL.SetWindowName("Left") # create a render window interactor irenR = vtkRenderWindowInteractor() irenR.SetRenderWindow(renWinR) irenL = vtkRenderWindowInteractor() irenL.SetRenderWindow(renWinL) # mapper mapper = vtkPolyDataMapper() mapper.SetInput(reader.GetOutput()) # actor actor = vtkActor() actor.SetMapper(mapper) # assign actor to the renderer renR.AddActor(actor) renL.AddActor(actor) # enable user interface interactor renWinR.Render() renWinL.Render() irenR.Initialize() irenL.Initialize() #Create callback object for camera manipulation cb = vtkGyroCallback() cb.rightCam = cameraR cb.leftCam = cameraL renWinR.AddObserver('InteractionEvent', cb.execute) renWinL.AddObserver('InteractionEvent', cb.execute) irenR.AddObserver('TimerEvent', cb.execute) irenL.AddObserver('TimerEvent', cb.execute) timerIDR = irenR.CreateRepeatingTimer(100) timerIDL = irenL.CreateRepeatingTimer(100) irenR.Start() irenL.Start() if __name__ == '__main__': main() 

EDIT:

On further viewing, it seems that TimerEvents do not shoot more than once in a row after MouseClickEvent, and I have no idea why.

EDIT 2: Scratch that they work most accurately according to some test outputs built into the code. I changed the code to accept user input for calling self.leftCam.SetPosition() in the self.leftCam.SetPosition() method (thus, replacing the hardcoded parameters "10, 40, 100" with three input variables), then passed the output of the script, which it just displays three random values ​​in my main program. What was about to happen was to have a rendering window that would constantly change position. Instead, nothing happens until I click on the screen, after which the expected functionality starts. All the time, the timer events are still triggered and the inputs are still being received, but the cameras refuse to update until the mouse event occurs within their window. What a deal?

EDIT 3: I dug something else and found that in the vtkObject::InvokeEvent() method, which is called inside each interaction event, there is a focus loop that overlaps all the observers that are not related to the object in focus. I'm going to investigate if there is a way to remove focus so that instead it bypasses this focus cycle and goes into an unfocused cycle that processes unfocused objects.

+7
python render vtk stereo-3d
source share
1 answer

So, the solution was surprisingly simple, but due to the lack of quality documentation provided by VTK, I had to break through the source to find it. Effectively, all you have to do is pseudo- Render() calls from each of the interactors through any callback method that you use to process your TimerEvent s. I did this using the ID properties added to each interactivity (see the code below). You can see that every time a TimerEvent launched from the internal irenR timer of the interactor ( irenR processes the right eye), the irenL Render() function is irenL and vice versa.

To solve this problem, I first realized that the standard interaction functionalities (mouse events, etc.) work fine. So I dug a source in the vtkRenderWindowInteractor.cxx file and realized that these methods were abstracted with separate implementations of vtkInteractorStyle . After rooting in the vtkInteractorStyleTrackball.cxx source, I found that there is a Render() function in the vtkRenderWindowInteractor class. Go figure! This was not mentioned in the documentation!

Unfortunately, two renders at once are actually very slow. If I do this method with only one window (at which point it becomes unnecessary), it works wonderfully. Drum frames with a second window. OK, what can you do?

Here is my corrected code (Finally, I can start working on what I had to develop):

 from vtk import* from parse import * import os import time, signal, threading def ParseSIG(signum, stack): print signum return class vtkGyroCallback(): def __init__(self): pass def execute(self, obj, event): #Modified segment to accept input for leftCam position gyro = (raw_input()) xyz = parse("{} {} {}", gyro) #print xyz # "Thread" the renders. Left is called on a right TimerEvent and right is called on a left TimerEvent. if obj.ID == 1 and event == 'TimerEvent': self.leftCam.SetPosition(float(xyz[0]), float(xyz[1]), float(xyz[2])) self.irenL.Render() #print "Left" elif obj.ID == 2 and event == 'TimerEvent': self.rightCam.SetPosition(float(xyz[0]), float(xyz[1]), float(xyz[2])) self.irenR.Render() #print "Right" return def main(): # create two cameras cameraR = vtkCamera() cameraR.SetPosition(0,0,200) cameraR.SetFocalPoint(0,0,0) cameraL = vtkCamera() cameraL.SetPosition(40,0,200) cameraL.SetFocalPoint(0,0,0) # create a rendering window and renderer renR = vtkRenderer() renR.SetActiveCamera(cameraR) renL = vtkRenderer() renL.SetActiveCamera(cameraL) # create source reader = vtkPolyDataReader() path = "/home/compilezone/Documents/3DSlicer/SlicerScenes/LegoModel-6_25/Model_5_blood.vtk" reader.SetFileName(path) reader.Update() # create render window renWinR = vtkRenderWindow() renWinR.AddRenderer(renR) renWinR.SetWindowName("Right") renWinL = vtkRenderWindow() renWinL.AddRenderer(renL) renWinL.SetWindowName("Left") # create a render window interactor irenR = vtkRenderWindowInteractor() irenR.SetRenderWindow(renWinR) irenL = vtkRenderWindowInteractor() irenL.SetRenderWindow(renWinL) # mapper mapper = vtkPolyDataMapper() mapper.SetInput(reader.GetOutput()) # actor actor = vtkActor() actor.SetMapper(mapper) # assign actor to the renderer renR.AddActor(actor) renL.AddActor(actor) # enable user interface interactor renWinR.Render() renWinL.Render() irenR.Initialize() irenL.Initialize() #Create callback object for camera manipulation cb = vtkGyroCallback() cb.rightCam = renR.GetActiveCamera()#cameraR cb.leftCam = renL.GetActiveCamera()#cameraL cb.irenR = irenR cb.irenL = irenL irenR.ID = 1 irenL.ID = 2 irenR.AddObserver('TimerEvent', cb.execute) irenL.AddObserver('TimerEvent', cb.execute) timerIDR = irenR.CreateRepeatingTimer(100) timerIDL = irenL.CreateRepeatingTimer(100) irenL.Start() irenR.Start() if __name__ == '__main__': main() 
+5
source share

All Articles