VTK обновляет положение нескольких окон рендеринга

#python #render #vtk #stereo-3d

#python #визуализация #vtk #стерео-3d

Вопрос:

Я столкнулся с небольшой проблемой при попытке запустить несколько окон рендеринга в приложении Python VTK, которое я пишу. Приложение представляет собой попытку рендеринга 3D-модели в двух отдельных представлениях для стереоприложения (т. Е. левого рендеринга и правого рендеринга), но у меня возникла проблема с одновременным обновлением камер каждого окна. В настоящее время у меня настроено два почти идентичных конвейера, каждый со своими vtkCamera , vtkRenderWindow , vtkRenderer и vtkRenderWindowInteractor , с той лишь разницей, что правая камера позиционно сдвинута на 30 единиц вдоль оси X.

Каждый из взаимодействующих элементов окна рендеринга обновляется с помощью vtkRenderWindowInteractor.AddObserver() метода, который вызывает простую функцию для сброса камер в их исходные положения и ориентации. Самая большая проблема заключается в том, что, похоже, это происходит только в одном окне одновременно, в частности, в окне, находящемся в фокусе в данный момент. Это как если бы таймер взаимодействия просто отключался, как только взаимодействие теряет фокус. Кроме того, когда я удерживаю мышь нажатой (и, таким образом, перемещаю камеру), визуализированное изображение начинает «дрейфовать», возвращаясь во все менее и менее правильное положение, даже несмотря на то, что я жестко закодировал координаты в функцию.

Очевидно, что я очень новичок в VTK, и многое из того, что происходит, довольно запутанно, поскольку многое скрыто в серверной части, поэтому было бы замечательно получить некоторую помощь по этому вопросу. Мой код приведен ниже. Спасибо, ребята!

 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()
  

Редактировать:

При дальнейшем просмотре кажется, что TimerEvents запускаются не более одного раза подряд после MouseClickEvent, и я понятия не имею, почему.

РЕДАКТИРОВАТЬ 2: Поцарапайте это, они, безусловно, срабатывают в соответствии с некоторыми тестовыми выводами, которые я встроил в код. Я изменил код, чтобы принимать пользовательский ввод для self.leftCam.SetPosition() вызова внутри vtkGyroCallback.execute() метода (таким образом, заменив жестко заданные параметры «10, 40, 100» тремя входными переменными), затем передал вывод скрипта, который просто отображал три случайных значения в моей основной программе. Чего это должно было достичь, так это наличия окна рендеринга, которое постоянно меняло бы положение. Вместо этого ничего не происходит, пока я не нажму на экран, после чего начнется ожидаемая функциональность. Все это время события таймера все еще срабатывают, и входные данные все еще принимаются, однако камеры отказываются обновляться, пока событие мыши не произойдет в пределах их окна. В чем заключается сделка?

ПРАВКА 3: Я покопался еще немного и обнаружил, что в vtkObject::InvokeEvent() методе, который вызывается в каждом событии взаимодействия, есть цикл фокусировки, который переопределяет всех наблюдателей, которые не относятся к объекту в фокусе. Я собираюсь исследовать, есть ли способ убрать фокус, чтобы вместо этого он обходил этот цикл фокусировки и переходил к несфокусированному циклу, который обрабатывает не сфокусированные объекты.

Комментарии:

1. Все исправлено! Если вы хотите вознаграждение, скажите мне, как заставить его работать плавно (частота кадров в настоящее время составляет 2-3 кадра в секунду, решение показано в моем комментарии ниже): D

2. Поцарапайте это, решите и эту проблему тоже! Я должен заплатить сам за это 😉 Мой метод использования двух отдельных vtkRenderWindow ов был медленным и громоздким. Я переключился на использование двух vtkRenderer файлов внутри одного vtkRenderWindow , и частота кадров легко возросла до 60 кадров в секунду. Кроме того, Oculus Rift не требует нескольких окон, поскольку технически каждый экран eye является частью одного монитора, поэтому все ваше приложение может находиться в одном окне.

Ответ №1:

Итак, решение было на удивление простым, но из-за отсутствия качественной документации, предоставленной VTK, мне пришлось покопаться в исходном коде, чтобы найти его. Фактически все, что вам нужно сделать, это псевдопотоковые Render() вызовы от каждого из взаимодействующих через любой метод обратного вызова, который вы используете для обработки ваших TimerEvent файлов. Я сделал это, используя свойства идентификатора, добавленные к каждому взаимодействующему (см. приведенный ниже код). Вы можете видеть, что каждый раз, когда TimerEvent запускается внутренний таймер irenR взаимодействия ( irenR обрабатывает правый глаз), вызывается функция irenL ‘s Render() , и наоборот.

Чтобы решить эту проблему, я сначала понял, что стандартные функциональные возможности взаимодействия (события мыши и тому подобное) Работают нормально. Итак, я покопался в исходном коде в vtkRenderWindowInteractor.cxx и понял, что эти методы были абстрагированы для отдельных vtkInteractorStyle реализаций. Покопавшись в исходном коде vtkInteractorStyleTrackball.cxx, я обнаружил, что в Render() классе действительно существует vtkRenderWindowInteractor функция. Поди разберись! В документации это точно не упоминалось!

К сожалению, два рендеринга одновременно на самом деле очень медленные. Если я выполняю этот метод только с одним окном (в этот момент это становится ненужным), он работает замечательно. Однако частота кадров зависит от второго окна. Ну и что ты можешь сделать?

Вот мой исправленный код (наконец-то я могу начать работать над тем, что я должен был разрабатывать):

 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()
  

Комментарии:

1. Забавный факт более двух лет спустя: вы можете просто использовать одно окно с несколькими средствами рендеринга внутри него, используя функцию setViewport. У меня нет с собой кода (вероятно, он все еще находится на каком-нибудь школьном жестком диске в 700 милях отсюда), но при выполнении этого способа частота кадров не является проблемой. Ознакомьтесь с официальным примером от ребят из VTK, чтобы лучше понять, что я имею в виду.