I’m looking for a way to effectively capture the frame buffer during interaction in pyvista
so that I can produce a video afterwards of the model moving around on the screen.
The problem I’ve encountered is that when I click the screen to interact with the viewer/plotter, no frames are written while the mouse button is pressed and the model is moving to its next position. This results in ‘jerky’ movements in the video.
Is there a way around this behavior to effectively do something like a screen capture of the plotter window, even when being manipulated? Maybe through direct access to the frame buffer or something similar?
#!/usr/bin/env python3 # -*- coding: utf-8 -*- import pyvista as pv import numpy as np from pyvista import examples import matplotlib as mpl import matplotlib.pyplot as plt px = int(round(1920*0.4)) py = int(round(1000*0.4)) mesh = examples.download_st_helens().warp_by_scalar() p = pv.Plotter() p.set_background(color='k') cmap = mpl.cm.get_cmap('viridis') p.add_mesh(mesh, lighting=True, texture=False, cmap=cmap, smooth_shading=True) p.show_grid() p.show(window_size=[px,py], auto_close=False, interactive_update=True) p.render() p.open_movie('anim.mp4',framerate=60) i=0 while (i<100): i+=1 p.write_frame() print(i) p.close()
Advertisement
Answer
I have added an example in pure VTK, where a cube is rotated and a smooth video is captured with the rotation. Afterwards, the interactor is started and the user can interact with the scene afterwards.
import os import vtk import numpy as np def vtkRotationMovie(renderWindow, filename='c:/test.avi'): global degrees degrees = 0 windowToImageFilter = vtk.vtkWindowToImageFilter() windowToImageFilter.SetInput(renderWindow) windowToImageFilter.SetInputBufferTypeToRGB() windowToImageFilter.ReadFrontBufferOff() windowToImageFilter.Update() if os.name == 'nt': writer = vtk.vtkAVIWriter() else: writer = vtk.vtkOggTheoraWriter() writer.SetInputConnection(windowToImageFilter.GetOutputPort()) writer.SetRate(10) # Not needed for Ogg try: os.remove(filename) except OSError: pass writer.SetFileName(filename) writer.Start() timerId = renderWindow.GetInteractor().CreateRepeatingTimer(50) def cb(interactor, event): global degrees step = 5 if (degrees > 359): interactor.DestroyTimer(timerId) writer.End() return interactor.GetRenderWindow().Render() cam = interactor.GetRenderWindow().GetRenderers().GetFirstRenderer().GetActiveCamera() cam.Azimuth(step) cam.OrthogonalizeViewUp() windowToImageFilter.Modified() writer.Write() degrees = degrees + step renderWindow.GetInteractor().AddObserver('TimerEvent', cb) renderWindow.GetInteractor().Start() # create a rendering window and renderer ren = vtk.vtkRenderer() renWin = vtk.vtkRenderWindow() renWin.AddRenderer(ren) # create a renderwindowinteractor iren = vtk.vtkRenderWindowInteractor() iren.SetRenderWindow(renWin) # create cube cube = vtk.vtkCubeSource() # mapper cubeMapper = vtk.vtkPolyDataMapper() cubeMapper.SetInputConnection(cube.GetOutputPort()) # actor cubeActor = vtk.vtkActor() cubeActor.SetMapper(cubeMapper) # assign actor to the renderer ren.AddActor(cubeActor) ren.SetBackground(.3,.2,.1) # enable user interface interactor iren.Initialize() renWin.Render() vtkRotationMovie(renWin, filename='./test.avi') iren.Start()