Source code for blendersynth.blender.compositor.render_result

import os.path
from shutil import copyfile
from pathlib import Path


[docs] class RenderResult: """Result of any form of render. Treated as a series of paths to the output files. Handles different output types, cameras, and frame counts. """ def __init__(self, render_paths: dict[tuple, Path]): """Expects dictionary of: (output_key, camera_name, frame_number) -> Path. """ self.render_paths = render_paths
[docs] def add( self, output_key: str, camera_name: str, frame_number: int, path: Path, overwrite=False, ): """Add a new RenderResult.""" key = (output_key, camera_name, frame_number) if key in self.render_paths and not overwrite: raise ValueError( f"Render path already exists for {output_key}, {camera_name}, {frame_number}." ) self.render_paths[key] = path
@property def output_types(self) -> list[str]: """List of output types.""" return list(set([k[0] for k in self.render_paths.keys()])) @property def camera_names(self) -> list[str]: """List of camera names.""" return list(set([k[1] for k in self.render_paths.keys()])) @property def frames(self) -> list[int]: """List of frame numbers.""" return list(set([k[2] for k in self.render_paths.keys()])) @property def num_cameras(self) -> int: """Number of cameras in the render.""" return len(self.camera_names) @property def num_frames(self) -> int: """Number of frames in the render.""" return len(self.frames) @property def num_output_types(self) -> int: """Number of output types in the render.""" return len(self.output_types)
[docs] def get_render_path( self, output_type: str, camera_name: str = None, frame_number: int = None ) -> Path: """Get the path to a specific render output. :param output_type: Type of output - `name` returned from :meth:`~blendersynth.blender.compositor.compositor.Compositor.define_output`. :param camera_name: Name of camera to get render for. Only required if multiple cameras used. :param frame_number: Frame number to get render for. Only required if multiple frames used. :return: Path to the render output. """ if camera_name is None: if self.num_cameras != 1: raise ValueError( "Must specify camera name if more than one camera. Cameras: ", self.camera_names, ) camera_name = self.camera_names[0] if frame_number is None: if self.num_frames != 1: raise ValueError( "Must specify frame number if more than one frame. Frames: ", self.frames, ) frame_number = self.frames[0] return self.render_paths[(output_type, camera_name, frame_number)]
[docs] def save_all( self, output_directory: str, suppress_camera_name: bool = True, suppress_frame_number: bool = True, ): """Save all the files to a directory. By default, saves files in format {data_type}_{camera_name}_{frame_number}.{ext}. :param output_directory: Directory to save files to. :param suppress_camera_name: If True, suppress camera name in filename (only works if single camera). :param suppress_frame_number: If True, suppress frame number in filename (only works if single frame). """ os.makedirs(output_directory, exist_ok=True) suppress_camera_name = suppress_camera_name and self.num_cameras == 1 suppress_frame_number = suppress_frame_number and self.num_frames == 1 for (data_type, camera, frame), path in self.render_paths.items(): fname = f"{data_type}" if not suppress_camera_name: fname += f"_{camera}" if not suppress_frame_number: fname += f"_{frame:06d}" fname += os.path.splitext(path)[1] copyfile(path, os.path.join(output_directory, fname))
[docs] def save_file( self, output_path: str, output_type: str, camera_name: str = None, frame_number: int = None, ): """Save a single file to a path. :param output_path: Path to save file to. :param output_type: Type of output - `name` returned from :meth:`~blendersynth.blender.compositor.compositor.Compositor.define_output`. :param camera_name: Name of camera to get render for. Only required if multiple cameras used. :param frame_number: Frame number to get render for. Only required if multiple frames used. """ if os.path.dirname(output_path): os.makedirs(os.path.dirname(output_path), exist_ok=True) path = self.get_render_path(output_type, camera_name, frame_number) target_ext = os.path.splitext(output_path)[1] rendered_ext = os.path.splitext(path)[1] if target_ext != rendered_ext: raise ValueError( f"Output path must have same extension as rendered path. Got {target_ext}, expected {rendered_ext}." ) copyfile(path, output_path)