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
Exemplo n.º 3
0
    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
Exemplo n.º 6
0
    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()
Exemplo n.º 7
0
    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]
Exemplo n.º 8
0
    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()
Exemplo n.º 9
0
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
Exemplo n.º 10
0
 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
Exemplo n.º 11
0
    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
Exemplo n.º 13
0
    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)
Exemplo n.º 14
0
 def wrapper(*args, **kwargs):
     try:
         func(*args, **kwargs)
     except Exception as err:
         logger = get_logger()
         logger.warning(str(err))
Exemplo n.º 15
0
    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()
Exemplo n.º 17
0
            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")