def snapshot(scene, fname=None, size=(300, 300), offscreen=True, order_transparent=False, stereo='off', multi_samples=8, max_peels=4, occlusion_ratio=0.0): """Save a snapshot of the scene in a file or in memory. Parameters ----------- scene : Scene() or vtkRenderer Scene instance fname : str or None Save PNG file. If None return only an array without saving PNG. size : (int, int) ``(width, height)`` of the window. Default is (300, 300). offscreen : bool Default True. Go stealth mode no window should appear. order_transparent : bool Default False. Use depth peeling to sort transparent objects. If True also enables anti-aliasing. stereo: string Set the stereo type. Default is 'off'. Other types include: * 'opengl': OpenGL frame-sequential stereo. Referred to as 'CrystalEyes' by VTK. * 'anaglyph': For use with red/blue glasses. See VTK docs to use different colors. * 'interlaced': Line interlaced. * 'checkerboard': Checkerboard interlaced. * 'left': Left eye only. * 'right': Right eye only. * 'horizontal': Side-by-side. multi_samples : int Number of samples for anti-aliazing (Default 8). For no anti-aliasing use 0. max_peels : int Maximum number of peels for depth peeling (Default 4). occlusion_ratio : float Occlusion ration for depth peeling (Default 0 - exact image). Returns ------- arr : ndarray Color array of size (width, height, 3) where the last dimension holds the RGB values. """ width, height = size render_window = RenderWindow() if offscreen: render_window.SetOffScreenRendering(1) if stereo.lower() != 'off': enable_stereo(render_window, stereo) render_window.AddRenderer(scene) render_window.SetSize(width, height) if order_transparent: antialiasing(scene, render_window, multi_samples, max_peels, occlusion_ratio) render_window.Render() window_to_image_filter = WindowToImageFilter() window_to_image_filter.SetInput(render_window) window_to_image_filter.Update() vtk_image = window_to_image_filter.GetOutput() h, w, _ = vtk_image.GetDimensions() vtk_array = vtk_image.GetPointData().GetScalars() components = vtk_array.GetNumberOfComponents() arr = numpy_support.vtk_to_numpy(vtk_array).reshape(w, h, components) if fname is None: return arr save_image(arr, fname) return arr
def __init__(self, scene=None, title='FURY', size=(300, 300), png_magnify=1, reset_camera=True, order_transparent=False, interactor_style='custom', stereo='off', multi_samples=8, max_peels=4, occlusion_ratio=0.0): """Manage the visualization pipeline. Parameters ---------- scene : Scene() or vtkRenderer() The scene that holds all the actors. title : string A string for the window title bar. size : (int, int) ``(width, height)`` of the window. Default is (300, 300). png_magnify : int Number of times to magnify the screenshot. This can be used to save high resolution screenshots when pressing 's' inside the window. reset_camera : bool Default is True. You can change this option to False if you want to keep the camera as set before calling this function. order_transparent : bool True is useful when you want to order transparent actors according to their relative position to the camera. The default option which is False will order the actors according to the order of their addition to the Scene(). interactor_style : str or vtkInteractorStyle If str then if 'trackball' then vtkInteractorStyleTrackballCamera() is used, if 'image' then vtkInteractorStyleImage() is used (no rotation) or if 'custom' then CustomInteractorStyle is used. Otherwise you can input your own interactor style. stereo: string Set the stereo type. Default is 'off'. Other types include: * 'opengl': OpenGL frame-sequential stereo. Referred to as 'CrystalEyes' by VTK. * 'anaglyph': For use with red/blue glasses. See VTK docs to use different colors. * 'interlaced': Line interlaced. * 'checkerboard': Checkerboard interlaced. * 'left': Left eye only. * 'right': Right eye only. * 'horizontal': Side-by-side. multi_samples : int Number of samples for anti-aliazing (Default 8). For no anti-aliasing use 0. max_peels : int Maximum number of peels for depth peeling (Default 4). occlusion_ratio : float Occlusion ration for depth peeling (Default 0 - exact image). Attributes ---------- scene : Scene() or vtkRenderer() iren : vtkRenderWindowInteractor() style : vtkInteractorStyle() window : vtkRenderWindow() Methods ------- initialize() render() start() add_window_callback() Examples -------- >>> from fury import actor, window >>> scene = window.Scene() >>> scene.add(actor.axes()) >>> showm = window.ShowManager(scene) >>> # showm.initialize() >>> # showm.render() >>> # showm.start() """ if scene is None: scene = Scene() self.scene = scene self.title = title self.size = size self.png_magnify = png_magnify self.reset_camera = reset_camera self.order_transparent = order_transparent self.interactor_style = interactor_style self.stereo = stereo self.timers = [] if self.reset_camera: self.scene.ResetCamera() self.window = RenderWindow() if self.stereo.lower() != 'off': enable_stereo(self.window, self.stereo) self.window.AddRenderer(scene) self.window.SetSize(size[0], size[1]) if self.order_transparent: occlusion_ratio = occlusion_ratio or 0.1 antialiasing(self.scene, self.window, multi_samples=0, max_peels=max_peels, occlusion_ratio=occlusion_ratio) if self.interactor_style == 'image': self.style = InteractorStyleImage() elif self.interactor_style == 'trackball': self.style = InteractorStyleTrackballCamera() elif self.interactor_style == 'custom': self.style = CustomInteractorStyle() else: self.style = interactor_style self.iren = RenderWindowInteractor() self.style.SetCurrentRenderer(self.scene) # Hack: below, we explicitly call the Python version of SetInteractor. self.style.SetInteractor(self.iren) self.iren.SetInteractorStyle(self.style) self.iren.SetRenderWindow(self.window)
def record(scene=None, cam_pos=None, cam_focal=None, cam_view=None, out_path=None, path_numbering=False, n_frames=1, az_ang=10, magnification=1, size=(300, 300), reset_camera=True, screen_clip=False, stereo='off', verbose=False): """Record a video of your scene. Records a video as a series of ``.png`` files of your scene by rotating the azimuth angle az_angle in every frame. Parameters ----------- scene : Scene() or vtkRenderer() object Scene instance cam_pos : None or sequence (3,), optional Camera's position. If None then default camera's position is used. cam_focal : None or sequence (3,), optional Camera's focal point. If None then default camera's focal point is used. cam_view : None or sequence (3,), optional Camera's view up direction. If None then default camera's view up vector is used. out_path : str, optional Output path for the frames. If None a default fury.png is created. path_numbering : bool When recording it changes out_path to out_path + str(frame number) n_frames : int, optional Number of frames to save, default 1 az_ang : float, optional Azimuthal angle of camera rotation. magnification : int, optional How much to magnify the saved frame. Default is 1. A value greater than 1 increases the quality of the image. However, the output size will be larger. For example, 200x200 image with magnification of 2 will be a 400x400 image. size : (int, int) ``(width, height)`` of the window. Default is (300, 300). screen_clip: bool Clip the png based on screen resolution. Default is False. reset_camera : bool If True Call ``scene.reset_camera()``. Otherwise you need to set the camera before calling this function. stereo: string Set the stereo type. Default is 'off'. Other types include: * 'opengl': OpenGL frame-sequential stereo. Referred to as 'CrystalEyes' by VTK. * 'anaglyph': For use with red/blue glasses. See VTK docs to use different colors. * 'interlaced': Line interlaced. * 'checkerboard': Checkerboard interlaced. * 'left': Left eye only. * 'right': Right eye only. * 'horizontal': Side-by-side. verbose : bool print information about the camera. Default is False. Examples --------- >>> from fury import window, actor >>> scene = window.Scene() >>> a = actor.axes() >>> scene.add(a) >>> # uncomment below to record >>> # window.record(scene) >>> # check for new images in current directory """ if scene is None: scene = Scene() renWin = RenderWindow() renWin.SetOffScreenRendering(1) renWin.SetBorders(screen_clip) renWin.AddRenderer(scene) renWin.SetSize(size[0], size[1]) iren = RenderWindowInteractor() iren.SetRenderWindow(renWin) # scene.GetActiveCamera().Azimuth(180) if reset_camera: scene.ResetCamera() if stereo.lower() != 'off': enable_stereo(renWin, stereo) renderLarge = RenderLargeImage() renderLarge.SetInput(scene) renderLarge.SetMagnification(magnification) renderLarge.Update() ang = 0 if cam_pos is not None: cx, cy, cz = cam_pos scene.GetActiveCamera().SetPosition(cx, cy, cz) if cam_focal is not None: fx, fy, fz = cam_focal scene.GetActiveCamera().SetFocalPoint(fx, fy, fz) if cam_view is not None: ux, uy, uz = cam_view scene.GetActiveCamera().SetViewUp(ux, uy, uz) cam = scene.GetActiveCamera() if verbose: print('Camera Position (%.2f, %.2f, %.2f)' % cam.GetPosition()) print('Camera Focal Point (%.2f, %.2f, %.2f)' % cam.GetFocalPoint()) print('Camera View Up (%.2f, %.2f, %.2f)' % cam.GetViewUp()) for i in range(n_frames): scene.GetActiveCamera().Azimuth(ang) renderLarge = RenderLargeImage() renderLarge.SetInput(scene) renderLarge.SetMagnification(magnification) renderLarge.Update() if path_numbering: if out_path is None: filename = str(i).zfill(6) + '.png' else: filename = out_path + str(i).zfill(6) + '.png' else: if out_path is None: filename = 'fury.png' else: filename = out_path arr = numpy_support.vtk_to_numpy(renderLarge.GetOutput().GetPointData() .GetScalars()) w, h, _ = renderLarge.GetOutput().GetDimensions() components = renderLarge.GetOutput().GetNumberOfScalarComponents() arr = arr.reshape((h, w, components)) save_image(arr, filename) ang = +az_ang
class ShowManager(object): """Class interface between the scene, the window and the interactor.""" def __init__(self, scene=None, title='FURY', size=(300, 300), png_magnify=1, reset_camera=True, order_transparent=False, interactor_style='custom', stereo='off', multi_samples=8, max_peels=4, occlusion_ratio=0.0): """Manage the visualization pipeline. Parameters ---------- scene : Scene() or vtkRenderer() The scene that holds all the actors. title : string A string for the window title bar. size : (int, int) ``(width, height)`` of the window. Default is (300, 300). png_magnify : int Number of times to magnify the screenshot. This can be used to save high resolution screenshots when pressing 's' inside the window. reset_camera : bool Default is True. You can change this option to False if you want to keep the camera as set before calling this function. order_transparent : bool True is useful when you want to order transparent actors according to their relative position to the camera. The default option which is False will order the actors according to the order of their addition to the Scene(). interactor_style : str or vtkInteractorStyle If str then if 'trackball' then vtkInteractorStyleTrackballCamera() is used, if 'image' then vtkInteractorStyleImage() is used (no rotation) or if 'custom' then CustomInteractorStyle is used. Otherwise you can input your own interactor style. stereo: string Set the stereo type. Default is 'off'. Other types include: * 'opengl': OpenGL frame-sequential stereo. Referred to as 'CrystalEyes' by VTK. * 'anaglyph': For use with red/blue glasses. See VTK docs to use different colors. * 'interlaced': Line interlaced. * 'checkerboard': Checkerboard interlaced. * 'left': Left eye only. * 'right': Right eye only. * 'horizontal': Side-by-side. multi_samples : int Number of samples for anti-aliazing (Default 8). For no anti-aliasing use 0. max_peels : int Maximum number of peels for depth peeling (Default 4). occlusion_ratio : float Occlusion ration for depth peeling (Default 0 - exact image). Attributes ---------- scene : Scene() or vtkRenderer() iren : vtkRenderWindowInteractor() style : vtkInteractorStyle() window : vtkRenderWindow() Methods ------- initialize() render() start() add_window_callback() Examples -------- >>> from fury import actor, window >>> scene = window.Scene() >>> scene.add(actor.axes()) >>> showm = window.ShowManager(scene) >>> # showm.initialize() >>> # showm.render() >>> # showm.start() """ if scene is None: scene = Scene() self.scene = scene self.title = title self.size = size self.png_magnify = png_magnify self.reset_camera = reset_camera self.order_transparent = order_transparent self.interactor_style = interactor_style self.stereo = stereo self.timers = [] if self.reset_camera: self.scene.ResetCamera() self.window = RenderWindow() if self.stereo.lower() != 'off': enable_stereo(self.window, self.stereo) self.window.AddRenderer(scene) self.window.SetSize(size[0], size[1]) if self.order_transparent: occlusion_ratio = occlusion_ratio or 0.1 antialiasing(self.scene, self.window, multi_samples=0, max_peels=max_peels, occlusion_ratio=occlusion_ratio) if self.interactor_style == 'image': self.style = InteractorStyleImage() elif self.interactor_style == 'trackball': self.style = InteractorStyleTrackballCamera() elif self.interactor_style == 'custom': self.style = CustomInteractorStyle() else: self.style = interactor_style self.iren = RenderWindowInteractor() self.style.SetCurrentRenderer(self.scene) # Hack: below, we explicitly call the Python version of SetInteractor. self.style.SetInteractor(self.iren) self.iren.SetInteractorStyle(self.style) self.iren.SetRenderWindow(self.window) def initialize(self): """Initialize interaction.""" self.iren.Initialize() def render(self): """Render only once.""" self.window.Render() def start(self): """Start interaction.""" try: self.render() if self.title.upper() == "FURY": self.window.SetWindowName(self.title + " " + fury_version) else: self.window.SetWindowName(self.title) self.iren.Start() except AttributeError: self.__init__(self.scene, self.title, size=self.size, png_magnify=self.png_magnify, reset_camera=self.reset_camera, order_transparent=self.order_transparent, interactor_style=self.interactor_style) self.initialize() self.render() if self.title.upper() == "FURY": self.window.SetWindowName(self.title + " " + fury_version) else: self.window.SetWindowName(self.title) self.iren.Start() self.window.RemoveRenderer(self.scene) self.scene.SetRenderWindow(None) self.window.Finalize() del self.iren del self.window def record_events(self): """Record events during the interaction. The recording is represented as a list of VTK events that happened during the interaction. The recorded events are then returned. Returns ------- events : str Recorded events (one per line). Notes ----- Since VTK only allows recording events to a file, we use a temporary file from which we then read the events. """ with InTemporaryDirectory(): filename = "recorded_events.log" recorder = InteractorEventRecorder() recorder.SetInteractor(self.iren) recorder.SetFileName(filename) def _stop_recording_and_close(_obj, _evt): if recorder: recorder.Stop() self.iren.TerminateApp() self.iren.AddObserver("ExitEvent", _stop_recording_and_close) recorder.EnabledOn() recorder.Record() self.initialize() self.render() self.iren.Start() # Deleting this object is the unique way # to close the file. recorder = None # Retrieved recorded events. with open(filename, 'r') as f: events = f.read() return events def record_events_to_file(self, filename="record.log"): """Record events during the interaction. The recording is represented as a list of VTK events that happened during the interaction. The recording is going to be saved into `filename`. Parameters ---------- filename : str Name of the file that will contain the recording (.log|.log.gz). """ events = self.record_events() # Compress file if needed if filename.endswith(".gz"): with gzip.open(filename, 'wb') as fgz: fgz.write(asbytes(events)) else: with open(filename, 'w') as f: f.write(events) def play_events(self, events): """Play recorded events of a past interaction. The VTK events that happened during the recorded interaction will be played back. Parameters ---------- events : str Recorded events (one per line). """ recorder = InteractorEventRecorder() recorder.SetInteractor(self.iren) recorder.SetInputString(events) recorder.ReadFromInputStringOn() self.initialize() # self.render() recorder.Play() # self.window.RemoveRenderer(self.scene) # self.scene.SetRenderWindow(None) # Finalize seems very important otherwise # the recording window will not close. self.window.Finalize() self.exit() # print('After Finalize and Exit') # del self.iren # del self.window def play_events_from_file(self, filename): """Play recorded events of a past interaction. The VTK events that happened during the recorded interaction will be played back from `filename`. Parameters ---------- filename : str Name of the file containing the recorded events (.log|.log.gz). """ # Uncompress file if needed. if filename.endswith(".gz"): with gzip.open(filename, 'r') as f: events = f.read() else: with open(filename) as f: events = f.read() self.play_events(events) def add_window_callback(self, win_callback, event=Command.ModifiedEvent): """Add window callbacks.""" self.window.AddObserver(event, win_callback) self.window.Render() def add_timer_callback(self, repeat, duration, timer_callback): self.iren.AddObserver("TimerEvent", timer_callback) if repeat: timer_id = self.iren.CreateRepeatingTimer(duration) else: timer_id = self.iren.CreateOneShotTimer(duration) self.timers.append(timer_id) def add_iren_callback(self, iren_callback, event="MouseMoveEvent"): self.iren.AddObserver(event, iren_callback) def destroy_timer(self, timer_id): self.iren.DestroyTimer(timer_id) del self.timers[self.timers.index(timer_id)] def destroy_timers(self): for timer_id in self.timers: self.destroy_timer(timer_id) def exit(self): """Close window and terminate interactor.""" # if is_osx and self.timers: # OSX seems to not destroy correctly timers # segfault 11 appears sometimes if we do not do it manually. # self.iren.GetRenderWindow().Finalize() self.iren.TerminateApp() self.destroy_timers() self.timers.clear() def save_screenshot(self, fname, magnification=1, size=None, stereo=None): """Save a screenshot of the current window in the specified filename. Parameters ---------- fname : str or None File name where to save the screenshot. magnification : int, optional Applies a magnification factor to the scene before taking the screenshot which improves the quality. A value greater than 1 increases the quality of the image. However, the output size will be larger. For example, 200x200 image with magnification of 2 will result in a 400x400 image. Default is 1. size : tuple of 2 ints, optional Size of the output image in pixels. If None, the size of the scene will be used. If magnification > 1, then the size will be determined by the magnification factor. Default is None. stereo : str, optional Set the type of stereo for the screenshot. Supported values are: * 'opengl': OpenGL frame-sequential stereo. Referred to as 'CrystalEyes' by VTK. * 'anaglyph': For use with red/blue glasses. See VTK docs to use different colors. * 'interlaced': Line interlaced. * 'checkerboard': Checkerboard interlaced. * 'left': Left eye only. * 'right': Right eye only. * 'horizontal': Side-by-side. """ if size is None: size = self.size if stereo is None: stereo = self.stereo.lower() record(scene=self.scene, out_path=fname, magnification=magnification, size=size, stereo=stereo)