def activate_cuda_devices(): """This function tries to activate all CUDA devices for rendering""" # get cycles preferences cycles = bpy.context.preferences.addons['cycles'] prefs = cycles.preferences # set CUDA enabled, and activate all GPUs we have access to prefs.compute_device_type = 'CUDA' # determine if we have a GPU available cuda_available = False for d in prefs.get_devices()[0]: cuda_available = cuda_available or d.type == 'CUDA' # if we don't have a GPU available, then print a warning if not cuda_available: get_logger().warn("No CUDA compute device available, will use CPU") else: # device_set = False # FIXME: unused variable for d in prefs.devices: if d.type == 'CUDA': get_logger().info(f"Using CUDA device '{d.name}' ({d.id})") d.use = True else: d.use = False # using the current scene, enable GPU Compute for rendering bpy.context.scene.cycles.device = 'GPU'
def select_object(obj_name: str): """Select and activate an object given its name""" if obj_name not in bpy.data.objects: get_logger().warn(f"Could not find object {obj_name}") return # we first deselect all, then select and activate the target object bpy.ops.object.select_all(action='DESELECT') obj = bpy.data.objects[obj_name] obj.select_set(True) bpy.context.view_layer.objects.active = obj
def __init__(self, **kwargs): super(StaticScene, self).__init__() self.logger = get_logger() # we do composition here, not inheritance anymore because it is too # limiting in its capabilities. Using a render manager is a better way # to handle compositor nodes self.renderman = abr_scenes.RenderManager() # extract configuration, then build and activate a split config self.config = kwargs.get('config', StaticSceneConfiguration()) # this check that the given configuration is (or inherits from) of the correct type if not isinstance(self.config, StaticSceneConfiguration): raise RuntimeError( f"Invalid configuration of type {type(self.config)} for class {_scene_name}" ) # determine if we are rendering in multiview mode self.render_mode = kwargs.get('render_mode', 'default') if self.render_mode not in ['default', 'multiview']: self.logger.warn( f'render mode "{self.render_mode}" not supported. Falling back to "default"' ) self.render_mode = 'default' # we might have to post-process the configuration self.postprocess_config() # setup directory information for each camera self.setup_dirinfo() # setup the scene, i.e. load it from file self.setup_scene() # setup the renderer. do this _AFTER_ the file was loaded during # setup_scene(), because otherwise the information will be taken from # the file, and changes made by setup_renderer ignored self.renderman.setup_renderer(self.config.render_setup.integrator, self.config.render_setup.denoising, self.config.render_setup.samples, self.config.render_setup.motion_blur) # grab environment textures self.setup_environment_textures() # setup objects for which the user want to randomize the texture self.setup_textured_objects() # setup all camera information according to the configuration self.setup_cameras() # setup global render output configuration self.setup_render_output() # populate the scene with objects (target and non) self.objs = self.setup_objects( self.config.scenario_setup.target_objects) # finally, setup the compositor self.setup_compositor()
def delete_object(object_name): """Delete an object given its name. This will also remove the meshes associated with the object. Args: object_name (str, bpy.types.Object): Either pass the object's name, or the object """ # try to get the object name logger = get_logger() if not isinstance(object_name, str): try: object_name = object_name.name except AttributeError as err: logger.error("Expecting name string but got: {}".format(object_name)) raise err if object_name not in bpy.data.objects: logger.warning(f"Could not find object {object_name}") return select_object(object_name) meshes_to_remove = list() for ob in bpy.context.selected_objects: meshes_to_remove.append(ob.data) bpy.ops.object.delete() for mesh in meshes_to_remove: try: # might data be a non-mesh ? bpy.data.meshes.remove(mesh) except Exception as err: logger.error(str(err))
def get_mesh_bounding_box(mesh): """Returns Bounding-Box of Mesh-Object at zero position (Edit mode)""" try: xyz = mesh.data.vertices[0].co except AttributeError as err: get_logger().error('expecting a mesh object, but no data.vertices attribute in object {}'.format(mesh)) raise err bb = [[xyz[0], xyz[0]], [xyz[1], xyz[1]], [xyz[2], xyz[2]]] for v in mesh.data.vertices: xyz = v.co for i in range(3): bb[i][0] = min(bb[i][0], xyz[i]) bb[i][1] = max(bb[i][1], xyz[i]) bounding_box = BoundingBox3D(bb[0][0], bb[0][1], bb[1][0], bb[1][1], bb[2][0], bb[2][1]) return bounding_box
def __init__(self, **kwargs): super(WorkstationScenarios, self).__init__() self.logger = get_logger() add_file_handler(self.logger) # we do composition here, not inheritance anymore because it is too # limiting in its capabilities. Using a render manager is a better way # to handle compositor nodes self.renderman = abr_scenes.RenderManager() # extract configuration, then build and activate a split config self.config = kwargs.get('config', WorkstationScenariosConfiguration()) if self.config.dataset.scene_type.lower() != 'WorkstationScenarios'.lower(): raise RuntimeError( f"Invalid configuration of scene type {self.config.dataset.scene_type} for class WorkstationScenarios") # determine if we are rendering in multiview mode self.render_mode = kwargs.get('render_mode', 'default') if self.render_mode not in ['default', 'multiview']: self.logger.warn(f'render mode "{self.render_mode}" not supported. Falling back to "default"') self.render_mode = 'default' # we might have to post-process the configuration self.postprocess_config() # setup directory information for each camera self.setup_dirinfo() # setup the scene, i.e. load it from file self.setup_scene() # setup the renderer. do this _AFTER_ the file was loaded during # setup_scene(), because otherwise the information will be taken from # the file, and changes made by setup_renderer ignored self.renderman.setup_renderer(self.config.render_setup.integrator, self.config.render_setup.denoising, self.config.render_setup.samples, self.config.render_setup.motion_blur) # grab environment textures self.setup_environment_textures() # setup all camera information according to the configuration self.setup_cameras() # setup global render output configuration self.setup_render_output() # populate the scene with objects self.objs = self.setup_objects(self.config.scenario_setup.target_objects, bpy_collection='TargetObjects', abc_objects=self.config.scenario_setup.abc_objects, abc_bpy_collection='ABCObjects') self.distractors = self.setup_objects(self.config.scenario_setup.distractor_objects, bpy_collection='DistractorObjects') # finally, setup the compositor self.setup_compositor()
def postprocess(self): """Postprocessing: Repair all filenames and make mask filenames accessible to each corresponding object. Blender adds the frame number in filenames. It is not possible to change this behavior, even in file output nodes in the compositor. The workaround that we use here is to store everything as desired-filename.ext0001, and then, within this function, rename these files to desired-filename.ext. """ # TODO: all TODO items from the _update function above apply! # turn the frame number into a string. given the update function, # blender will write files with the framenumber as four trailing digits frame_number = int(bpy.context.scene.frame_current) frame_number_str = f"{frame_number:04}" # get file names self.fname_render = os.path.join( self.dirinfo.images.rgb, f'{self.base_filename}.png{frame_number_str}') self.fname_range = os.path.join( self.dirinfo.images.range, f'{self.base_filename}.exr{frame_number_str}') self.fname_backdrop = os.path.join( self.dirinfo.images.base_path, 'backdrop', f'{self.base_filename}.png{frame_number_str}') for f in (self.fname_render, self.fname_range, self.fname_backdrop): if not os.path.exists(f): get_logger().error(f"File {f} expected, but does not exist") else: os.rename(f, f[:-4]) # store mask filename for other users that currently need the mask for obj in self.objs: fname_mask = os.path.join( self.dirinfo.images.mask, f'{self.base_filename}{obj["id_mask"]}.png{frame_number_str}') os.rename(fname_mask, fname_mask[:-4]) # store name of mask file into dict of corresponding obj # TODO: not sure is good to modify the dict but I like more than the list of fname_masks obj['fname_mask'] = fname_mask[:-4]
def __init__(self, data_dir=None): """Dataloader for ABC dataset Args: data_dir (str, optional): fullpath to ABC dataset parent directory. Defaults to None. """ self._logger = log_utils.get_logger() log_utils.add_file_handler(self._logger) self._parent = self._get_abc_parent_dir(data_dir) self._object_types_map = self._get_object_types_map()
def rotation_matrix(alpha, axis, homogeneous=False): """Euler rotation matrices Args: alpha (float): angle in radians axis (str): x/y/z homogeneous (bool): output homogeneous coordinates Returns: rotation matrix """ # make sure axis is lower case axis = axis.lower() if axis == 'x': # rotation around x rot = euler_x_to_matrix(alpha) elif axis == 'y': # rotation around y rot = euler_y_to_matrix(alpha) elif axis == 'z': # rotation around z rot = euler_z_to_matrix(alpha) else: get_logger().error('Axis needs to be x/y/z!') raise ValueError # create homogeneous matrix if homogeneous is True: h = np.eye(4) h[:3, :3] = rot return h else: return rot
def __init__(self, material_generator, units="METERS", enable_physics=True, collision_margin=0.0001, density=8000): self._logger = log_utils.get_logger() log_utils.add_file_handler(self._logger) self._mat_gen = material_generator self._units = units self._physhics = enable_physics self._collision_margin = collision_margin self._density = density # kg/m^3, Steel ~ 8000 self._mass_top_limit = 1.0 # [kg] self._mass_bottom_limit = 0.01
def __init__(self, **kwargs): super(SimpleObject, self).__init__() self.logger = get_logger() # we make use of the RenderManager self.renderman = abr_scenes.RenderManager() # get the configuration, if one was passed in self.config = kwargs.get('config', SimpleObjectConfiguration()) # determine if we are rendering in multiview mode self.render_mode = kwargs.get('render_mode', 'default') if not self.render_mode == 'default': self.logger.warn( f'{self.__class__} scene supports only "default" render mode. Falling back to "default"' ) self.render_mode = 'default' # we might have to post-process the configuration self.postprocess_config() # set up directory information that will be used self.setup_dirinfo() # set up anything that we need for the scene before doing anything else. # For instance, removing all default objects self.setup_scene() # now that we have setup the scene, let's set up the render manager self.renderman.setup_renderer(self.config.render_setup.integrator, self.config.render_setup.denoising, self.config.render_setup.samples, self.config.render_setup.motion_blur) # setup environment texture information self.setup_environment_textures() # setup the camera that we wish to use self.setup_cameras() # setup render / output settings self.setup_render_output() # setup the object that we want to render self.setup_objects() # finally, let's setup the compositor self.setup_compositor()
def check_default_material(material: bpy.types.Material): """This function checks if, given a material, the default nodes are present. If not, they will be set up. Args: material(bpy.types.Material): material to check Returns tuple containing the output node, and the bsdf node """ logger = get_logger() tree = material.node_tree nodes = tree.nodes # check if default principles bsdf + metarial output exist if len(nodes) != 2: logger.warn("More shader nodes in material than expected!") # find if the material output node is available. If not, create it if 'Material Output' not in nodes: logger.warn("Node 'Material Output' not found in material node-tree") n_output = nodes.new('ShaderNodeOutputMaterial') else: n_output = nodes['Material Output'] # find if the principled bsdf node is available. If not, create it if 'Principled BSDF' not in nodes: logger.warn("Node 'Principled BSDF' not found in material node-tree") n_bsdf = nodes.new('ShaderNodeBsdfPrincipled') else: n_bsdf = nodes['Principled BSDF'] # check if link from BSDF to output is available link_exists = False for lnk in tree.links: if (lnk.from_node == n_bsdf) and (lnk.to_node == n_output): link_exists = True break if not link_exists: tree.links.new(n_bsdf.outputs['BSDF'], n_output.inputs['Surface']) return n_output, n_bsdf
def __init__(self, data_dir=None, n_materials=3, collision_margin=0.0001, density=8000): """Configuration Args: data_dir (str, optional): fullpath to ABC dataset parent directory. Defaults to None. n_materials (int, optional): Number of random materials to generate. Defaults to 3. density (float, optional): density in [kg/m^3]. Default to 8000, for Steel. collision_margin (float, optional): collision_margin in [m]. Defaults to 0.0001. Physics simulation params are necessary for randomized object placement. """ self._logger = log_utils.get_logger() log_utils.add_file_handler(self._logger) self._dataloader = ABCDataLoader(data_dir=data_dir) material_generator = MetallicMaterialGenerator() material_generator.make_random_material(n=n_materials) self._stl_importer = STLImporter(material_generator, units="METERS", enable_physics=True, collision_margin=collision_margin, density=density)
def wrapper(*args, **kwargs): try: func(*args, **kwargs) except Exception as err: logger = get_logger() logger.warning(str(err))
import json import amira_blender_rendering.utils.camera as camera_utils import amira_blender_rendering.utils.blender as blnd import amira_blender_rendering.nodes as abr_nodes import amira_blender_rendering.scenes as abr_scenes import amira_blender_rendering.math.geometry as abr_geom from amira_blender_rendering.math.conversions import bu_to_mm # import things from AMIRA Perception Subsystem that are required from amira_blender_rendering.interfaces import PoseRenderResult, ResultsCollection from amira_blender_rendering.postprocessing import boundingbox_from_mask from amira_blender_rendering.utils.logging import get_logger # from amira_blender_rendering.utils.converters import to_PASCAL_VOC logger = get_logger() class RenderManager(abr_scenes.BaseSceneManager): # NOTE: you must call setup_compositor manually when using this class! def __init__(self, unit_conversion=bu_to_mm): # this will initialize a BaseSceneManager, which is used for setting # environment textures, to reset blender, or to initialize default # blender settings super(RenderManager, self).__init__() self.unit_conversion = unit_conversion def postprocess(self, dirinfo, base_filename, camera, objs, zeroing, **kwargs): """Postprocessing the scene.
def __init__(self): super(BaseSceneManager, self).__init__() self.init_default_blender_config() self.logger = get_logger()
mass=mass, collision_margin=collision_margin) if not rescale_success: bpy.ops.object.select_all(action="DESELECT") # bpy.context.scene.objects.active = None obj_handle.select_set(True) bpy.ops.object.delete() return None, None return obj_handle, object_type if __name__ == "__main__": logger = log_utils.get_logger() log_utils.add_file_handler(logger) logger.info("starting __main__") n_rand = 7 n_per_type = 4 step = 0.3 out_dir = osp.join(os.environ["HOME"], "Desktop", "stl_import_demo") try: shutil.rmtree(out_dir) except FileNotFoundError: pass os.makedirs(out_dir, exist_ok=True) abc_importer = ABCImporter() logger.info("instantiated an ABCImporter for STL files")