Beispiel #1
0
    def sample_and_validate_cam_pose(self, cam, cam_ob, config):
        """ Samples a new camera pose, sets the parameters of the given camera object accordingly and validates it.

        :param cam: The camera which contains only camera specific attributes.
        :param cam_ob: The object linked to the camera which determines general properties like location/orientation
        :param config: The config object describing how to sample
        :return: True, if the sampled pose was valid
        """
        # Sample used floor obj
        floor_obj = random.choice(self.used_floors)

        # Sample/set intrinsics
        self._set_cam_intrinsics(cam, Config(self.config.get_raw_dict("intrinsics", {})))

        # Sample camera extrinsics (we do not set them yet for performance reasons)
        cam2world_matrix = self._cam2world_matrix_from_cam_extrinsics(config)

        # Make sure the sampled location is inside the room => overwrite x and y and add offset to z
        bounding_box = get_bounds(floor_obj)
        min_corner = np.min(bounding_box, axis=0)
        max_corner = np.max(bounding_box, axis=0)

        cam2world_matrix.translation[0] = random.uniform(min_corner[0], max_corner[0])
        cam2world_matrix.translation[1] = random.uniform(min_corner[1], max_corner[1])
        cam2world_matrix.translation[2] += floor_obj.location[2]

        # Check if sampled pose is valid
        if self._is_pose_valid(floor_obj, cam, cam_ob, cam2world_matrix):
            # Set camera extrinsics as the pose is valid
            CameraUtility.add_camera_pose(cam2world_matrix)
            return True
        else:
            return False
Beispiel #2
0
    def drop(self, obj):
        """ Moves object "down" until its bounding box touches the bounding box of the surface. This uses bounding boxes
            which are not aligned optimally, this will cause objects to be placed slightly to high.

        :param obj: Object to move. Type: blender object.
        """
        obj_bounds = get_bounds(obj)
        obj_height = min(
            [self.up_direction.dot(corner) for corner in obj_bounds])

        obj.location -= self.up_direction * (obj_height - self.surface_height)
Beispiel #3
0
    def run(self):
        """ Selects objects, gets a list of appropriate values of objects' attributes, custom properties, or some
            processed custom data and optionally performs some operation of this list.

        :return: List of values (only if `get` was specified or a custom function was called)
                 or a singular int, float, or mathutils.Vector value (if some operation was applied).
        """
        objects = self.config.get_list("entities")
        look_for = self.config.get_string("get")

        cp_search = False
        cf_search = False
        if look_for.startswith('cp_'):
            look_for = look_for[3:]
            cp_search = True
        elif look_for.startswith('cf_'):
            look_for = look_for[3:]
            cf_search = True

        raw_result = []
        for obj in objects:
            if hasattr(obj, look_for) and not cp_search:
                raw_result.append(getattr(obj, look_for))
            elif look_for in obj and cp_search:
                raw_result.append(obj[look_for])
            elif look_for == "bounding_box_means" and cf_search:
                bb_mean = np.mean(get_bounds(obj), axis=0)
                raw_result.append(mathutils.Vector(bb_mean))
            else:
                raise RuntimeError("Unknown parameter name: " + look_for)

        if self.config.has_param("transform_by"):
            transform_by = self.config.get_string("transform_by")
            if self._check_compatibility(raw_result):
                if transform_by == "sum":
                    ref_result = self._sum(raw_result)
                elif transform_by == "avg":
                    ref_result = self._avg(raw_result)
                else:
                    raise RuntimeError("Unknown transform_by: " + transform_by)
            else:
                raise RuntimeError("Performing " + str(transform_by) + " on " +
                                   str(look_for) + " " +
                                   str(type(raw_result[0])) +
                                   " type is not allowed!")
            result = ref_result
        else:
            result = raw_result

        if self.config.has_param("index"):
            result = result[self.config.get_int("index")]

        return result
Beispiel #4
0
    def run(self):
        """
        :return: Point of interest in the scene. Type: mathutils Vector.
        """
        # Init matrix for all points of all bounding boxes
        mean_bb_points = []
        # For every object in the scene
        for obj in get_all_mesh_objects():
            # Get bounding box corners
            bb_points = get_bounds(obj)
            # Compute mean coords of bounding box
            mean_bb_points.append(np.mean(bb_points, axis=0))
        # Query point - mean of means
        mean_bb_point = np.mean(mean_bb_points, axis=0)
        # Closest point (from means) to query point (mean of means)
        poi = mathutils.Vector(mean_bb_points[np.argmin(
            np.linalg.norm(mean_bb_points - mean_bb_point, axis=1))])

        return poi
Beispiel #5
0
    def check_above_surface(self, obj):
        """ Check if all corners of the bounding box are "above" the surface

        :param obj: Object for which the check is carried out. Type: blender object.
        :return: True if the bounding box is above the surface, False - if not.
        """
        inv_world_matrix = self.surface.matrix_world.inverted()

        for point in get_bounds(obj):
            ray_start = inv_world_matrix @ (point + self.up_direction)
            ray_direction = inv_world_matrix @ (self.surface.location +
                                                (-1 * self.up_direction))

            is_hit, hit_location, _, _ = self.surface.ray_cast(
                ray_start, ray_direction)

            if not is_hit:
                return False

        return True
    def move_obj_origin_to_bottom_mean_point(objects: [bpy.types.Object]):
        """
        Moves the object center to bottom of the bounding box in Z direction and also in the middle of the X and Y
        plane. So that all objects have a similar origin, which then makes the placement easier.

        :param objects: list of objects, which origin should be moved
        """

        bpy.ops.object.select_all(action='DESELECT')
        for obj in objects:
            # move the object to the center
            obj.select_set(True)
            bpy.context.view_layer.objects.active = obj
            bb = get_bounds(obj)
            bb_center = np.mean(bb, axis=0)
            bb_min_z_value = np.min(bb, axis=0)[2]
            bpy.ops.object.mode_set(mode='EDIT')
            bpy.ops.transform.translate(value=[-bb_center[0], -bb_center[1], -bb_min_z_value])
            bpy.ops.object.mode_set(mode='OBJECT')
            obj.select_set(False)
        bpy.context.view_layer.update()
Beispiel #7
0
    def run(self):
        """
        :return: Point of interest in the scene. Type: mathutils.Vector.
        """
        # Init matrix for all points of all bounding boxes
        mean_bb_points = []
        # For every selected object in the scene
        selected_objects = self.config.get_list("selector",
                                                get_all_blender_mesh_objects())
        if len(selected_objects) == 0:
            raise Exception("No objects were selected!")

        for obj in selected_objects:
            # Get bounding box corners
            bb_points = get_bounds(obj)
            # Compute mean coords of bounding box
            mean_bb_points.append(np.mean(bb_points, axis=0))
        # Query point - mean of means
        mean_bb_point = np.mean(mean_bb_points, axis=0)
        # Closest point (from means) to query point (mean of means)
        poi = mathutils.Vector(mean_bb_points[np.argmin(
            np.linalg.norm(mean_bb_points - mean_bb_point, axis=1))])

        return poi
Beispiel #8
0
    def run(self):
        """ Samples the selected objects poses on a selected surface. """
        max_tries = self.config.get_int("max_iterations", 100)
        objects = self.config.get_list("objects_to_sample")
        self.surface = self.config.get_list("surface")
        if len(self.surface) > 1:
            raise Exception(
                "This module operates with only one `surface` object while more than one was returned by "
                "the Provider. Please, configure the corresponding Provider's `conditions` accordingly such "
                "that it returns only one object! Tip: use getter.Entity's 'index' parameter."
            )
        else:
            self.surface = self.surface[0]

        surface_bounds = get_bounds(self.surface)
        self.surface_height = max(
            [self.up_direction.dot(corner) for corner in surface_bounds])

        for obj in objects:
            if obj.type == "MESH":

                print("Trying to put ", obj.name)

                placed_successfully = False

                for i in range(max_tries):
                    position = self.config.get_vector3d("pos_sampler")
                    rotation = self.config.get_vector3d("rot_sampler")

                    obj.location = position
                    obj.rotation_euler = rotation

                    if not self.check_collision_free(obj):
                        print("Collision detected, retrying!")
                        continue

                    if not self.check_above_surface(obj):
                        print("Not above surface, retrying!")
                        continue

                    self.drop(obj)

                    if not self.check_above_surface(obj):
                        print("Not above surface after drop, retrying!")
                        continue

                    if not self.check_spacing(obj):
                        print("Bad spacing after drop, retrying!")
                        continue

                    if not self.check_collision_free(obj):
                        print("Collision detected after drop, retrying!")
                        continue

                    print(
                        "Placed object \"{}\" successfully at {} after {} iterations!"
                        .format(obj.name, obj.location, i + 1))
                    self.placed_objects.append(obj)

                    placed_successfully = True
                    break

                # Add a tiny displacement to make sure it is above surface
                obj.location += mathutils.Vector([0.0, 0.0, 0.0003])

                if not placed_successfully:
                    print("Giving up on {}, deleting...".format(obj.name))
                    bpy.ops.object.select_all(action='DESELECT')
                    obj.select_set(True)
                    bpy.ops.object.delete()

        bpy.context.view_layer.update()
Beispiel #9
0
    def __init__(self, config):
        Provider.__init__(self, config)
        self._regions = []

        # invoke a Getter, get a list of objects to manipulate
        self._objects = config.get_list("to_sample_on")
        if len(self._objects) == 0:
            raise Exception(
                "The used selector returns an empty list, check the config value: \"to_sample_on\""
            )

        # relative area on selected face where to sample points
        self._face_sample_range = config.get_vector2d("face_sample_range",
                                                      [0.0, 1.0])

        # min and max distance to the bounding box
        self._min_height = config.get_float("min_height", 0.0)
        self._max_height = config.get_float("max_height", 1.0)
        if self._max_height < self._min_height:
            raise Exception("The minimum height ({}) must be smaller "
                            "than the maximum height ({})!".format(
                                self._min_height, self._max_height))
        self._use_ray_trace_check = config.get_bool('use_ray_trace_check',
                                                    False)

        # the upper direction, to define what is up in the scene
        # is used to selected the correct face
        self._upper_dir = config.get_vector3d("upper_dir", [0.0, 0.0, 1.0])
        self._upper_dir.normalize()
        # if this is true the up direction is determined by the upper_dir vector, if it is false the
        # face normal is used
        self._use_upper_dir = config.get_bool("use_upper_dir", True)

        def calc_vec_and_normals(face):
            """ Calculates the two vectors, which lie in the plane of the face and the normal of the face.

            :param face: Four corner coordinates of a face. Type: [4x[3xfloat]].
            :return: (two vectors in the plane), and the normal.
            """
            vec1 = face[1] - face[0]
            vec2 = face[3] - face[0]
            normal = vec1.cross(vec2)
            normal.normalize()
            return (vec1, vec2), normal

        # determine for each object in objects the region, where to sample on
        for obj in self._objects:
            bb = get_bounds(obj)
            faces = []
            faces.append([bb[0], bb[1], bb[2], bb[3]])
            faces.append([bb[0], bb[4], bb[5], bb[1]])
            faces.append([bb[1], bb[5], bb[6], bb[2]])
            faces.append([bb[6], bb[7], bb[3], bb[2]])
            faces.append([bb[3], bb[7], bb[4], bb[0]])
            faces.append([bb[7], bb[6], bb[5], bb[4]])
            # select the face, which has the smallest angle to the upper direction
            min_diff_angle = 2 * math.pi
            selected_face = None
            for face in faces:
                # calc the normal of all faces
                _, normal = calc_vec_and_normals(face)
                diff_angle = math.acos(normal.dot(self._upper_dir))
                if diff_angle < min_diff_angle:
                    min_diff_angle = diff_angle
                    selected_face = face
            # save the selected face values
            if selected_face is not None:
                vectors, normal = calc_vec_and_normals(selected_face)
                base_point = mathutils.Vector(selected_face[0])
                self._regions.append(Region2D(vectors, normal, base_point))
            else:
                raise Exception(
                    "Couldn't find a face, for this obj: {}".format(obj.name))