def _load_room(node: dict, metadata: dict, material_adjustments: list,
                   transform: Matrix, house_id: str, parent: MeshObject,
                   room_per_object: dict,
                   label_mapping: LabelIdMapping) -> List[MeshObject]:
        """ Load the room specified in the given node.

        :param node: The node dict which contains information from house.json..
        :param metadata: A dict of metadata which will be written into the object's custom data.
        :param material_adjustments: Adjustments to the materials which were specified inside house.json.
        :param transform: The transformation that should be applied to the loaded objects.
        :param house_id: The id of the current house.
        :param parent: The parent object to which the room should be linked
        :param room_per_object: A dict for object -> room lookup (Will be written into)
        :return: The list of loaded mesh objects.
        """
        # Build empty room object which acts as a parent for all objects inside
        room_obj = Entity.create_empty("Room#" + node["id"])
        room_obj.set_cp("type", "Room")
        room_obj.set_cp("bbox", SuncgLoader._correct_bbox_frame(node["bbox"]))
        room_obj.set_cp("roomTypes", node["roomTypes"])
        room_obj.set_parent(parent)
        loaded_objects = [room_obj]

        # Store indices of all contained objects in
        if "nodeIndices" in node:
            for child_id in node["nodeIndices"]:
                room_per_object[child_id] = room_obj

        if "hideFloor" not in node or node["hideFloor"] != 1:
            metadata["type"] = "Floor"
            metadata["category_id"] = label_mapping.id_from_label("floor")
            metadata["fine_grained_class"] = "floor"
            loaded_objects += SuncgLoader._load_obj(
                os.path.join(SuncgLoader._suncg_dir, "room", house_id,
                             node["modelId"] + "f.obj"), metadata,
                material_adjustments, transform, room_obj)

        if "hideCeiling" not in node or node["hideCeiling"] != 1:
            metadata["type"] = "Ceiling"
            metadata["category_id"] = label_mapping.id_from_label("ceiling")
            metadata["fine_grained_class"] = "ceiling"
            loaded_objects += SuncgLoader._load_obj(
                os.path.join(SuncgLoader._suncg_dir, "room", house_id,
                             node["modelId"] + "c.obj"), metadata,
                material_adjustments, transform, room_obj)

        if "hideWalls" not in node or node["hideWalls"] != 1:
            metadata["type"] = "Wall"
            metadata["category_id"] = label_mapping.id_from_label("wall")
            metadata["fine_grained_class"] = "wall"
            loaded_objects += SuncgLoader._load_obj(
                os.path.join(SuncgLoader._suncg_dir, "room", house_id,
                             node["modelId"] + "w.obj"), metadata,
                material_adjustments, transform, room_obj)

        return loaded_objects
Exemple #2
0
    def _set_category_ids(loaded_objects: List[MeshObject],
                          label_mapping: LabelIdMapping):
        """
        Set the category ids for the objs based on the .csv file loaded in LabelIdMapping

        Each object will have a custom property with a label, can be used by the SegMapRenderer.

        :param loaded_objects: objects loaded from the .obj file
        """

        #  Some category names in scenenet objects are written differently than in nyu_idset.csv
        normalize_name = {
            "floor-mat": "floor_mat",
            "refrigerator": "refridgerator",
            "shower-curtain": "shower_curtain",
            "nightstand": "night_stand",
            "Other-structure": "otherstructure",
            "Other-furniture": "otherfurniture",
            "Other-prop": "otherprop",
            "floor_tiles_floor_tiles_0125": "floor",
            "ground": "floor",
            "floor_enclose": "floor",
            "floor_enclose2": "floor",
            "floor_base_object01_56": "floor",
            "walls1_line01_12": "wall",
            "room_skeleton": "wall",
            "ceilingwall": "ceiling"
        }

        for obj in loaded_objects:
            obj_name = obj.get_name().lower().split(".")[0]

            # If it's one of the cases that the category have different names in both idsets.
            if obj_name in normalize_name:
                obj_name = normalize_name[obj_name]  # Then normalize it.

            if label_mapping.has_label(obj_name):
                obj.set_cp("category_id",
                           label_mapping.id_from_label(obj_name))
            # Check whether the object's name without suffixes like 's', '1' or '2' exist in the mapping.
            elif label_mapping.has_label(obj_name[:-1]):
                obj.set_cp("category_id",
                           label_mapping.id_from_label(obj_name[:-1]))
            elif "painting" in obj_name:
                obj.set_cp("category_id",
                           label_mapping.id_from_label("picture"))
            else:
                print("This object was not specified: {} use objects for it.".
                      format(obj_name))
                obj.set_cp(
                    "category_id",
                    label_mapping.id_from_label("otherstructure".lower()))

            # Correct names of floor and ceiling objects to make them later easier to identify (e.g. by the FloorExtractor)
            if obj.get_cp("category_id") == label_mapping.id_from_label(
                    "floor"):
                obj.set_name("floor")
            elif obj.get_cp("category_id") == label_mapping.id_from_label(
                    "ceiling"):
                obj.set_name("ceiling")
    def _load_ground(node: dict, metadata: dict, material_adjustments: list,
                     transform: Matrix, house_id: str, parent: MeshObject,
                     label_mapping: LabelIdMapping) -> List[MeshObject]:
        """ Load the ground specified in the given node.

        :param node: The node dict which contains information from house.json..
        :param metadata: A dict of metadata which will be written into the object's custom data.
        :param material_adjustments: Adjustments to the materials which were specified inside house.json.
        :param transform: The transformation that should be applied to the loaded objects.
        :param house_id: The id of the current house.
        :param parent: The parent object to which the ground should be linked
        :return: The list of loaded mesh objects.
        """
        metadata["type"] = "Ground"
        metadata["category_id"] = label_mapping.id_from_label("floor")
        metadata["fine_grained_class"] = "ground"
        return SuncgLoader._load_obj(
            os.path.join(SuncgLoader._suncg_dir, "room", house_id,
                         node["modelId"] + "f.obj"), metadata,
            material_adjustments, transform, parent)
    def _load_box(node: dict, material_adjustments: list, transform: Matrix,
                  parent: MeshObject,
                  label_mapping: LabelIdMapping) -> List[MeshObject]:
        """ Creates a cube inside blender which follows the specifications of the given node.

        :param node: The node dict which contains information from house.json..
        :param material_adjustments: Adjustments to the materials which were specified inside house.json.
        :param transform: The transformation that should be applied to the loaded objects.
        :param parent: The parent object to which the ground should be linked
        :return: The list of loaded mesh objects.
        """
        box = MeshObject.create_primitive("CUBE")
        box.set_name("Box#" + node["id"])
        # Scale the cube to the required dimensions
        box.set_local2world_mat(
            Matrix.Scale(node["dimensions"][0] / 2, 4, (1.0, 0.0, 0.0))
            @ Matrix.Scale(node["dimensions"][1] / 2, 4,
                           (0.0, 1.0, 0.0)) @ Matrix.Scale(
                               node["dimensions"][2] / 2, 4, (0.0, 0.0, 1.0)))

        # Create UV mapping (beforehand we apply the scaling from the previous step, such that the resulting uv mapping has the correct aspect)
        bpy.ops.object.transform_apply(scale=True)
        bpy.ops.object.editmode_toggle()
        bpy.ops.uv.cube_project()
        bpy.ops.object.editmode_toggle()

        # Create an empty material which is filled in the next step
        box.new_material("material_0")

        SuncgLoader._transform_and_colorize_object(box, material_adjustments,
                                                   transform, parent)
        # set class to void
        box.set_cp("category_id", label_mapping.id_from_label("void"))
        # Rotate cube to match objects loaded from .obj, has to be done after transformations have been applied
        box.set_local2world_mat(
            Matrix.Rotation(math.radians(90), 4, "X") @ Matrix(
                box.get_local2world_mat()))

        return [box]
    def load(house_path: str,
             label_mapping: LabelIdMapping,
             suncg_dir: str = None) -> List[MeshObject]:
        """ Loads a house.json file into blender.

        - Loads all objects files specified in the house.json file.
        - Orders them hierarchically (level -> room -> object)
        - Writes metadata into the custom properties of each object

        :param house_path: The path to the house.json file which should be loaded.
        :param suncg_dir: The path to the suncg root directory which should be used for loading objects, rooms, textures etc.
        :return: The list of loaded mesh objects.
        """
        # If not suncg root directory has been given, determine it via the given house directory.
        if suncg_dir is None:
            suncg_dir = os.path.join(os.path.dirname(house_path), "../..")

        SuncgLoader._suncg_dir = suncg_dir
        SuncgLoader._collection_of_loaded_objs = {}
        # there are only two types of materials, textures and diffuse
        SuncgLoader._collection_of_loaded_mats = {"texture": {}, "diffuse": {}}

        with open(Utility.resolve_path(house_path), "r") as f:
            config = json.load(f)

        object_label_map, object_fine_grained_label_map, object_coarse_grained_label_map = SuncgLoader._read_model_category_mapping(
            os.path.join('resources', 'suncg', 'Better_labeling_for_NYU.csv'))

        house_id = config["id"]
        loaded_objects = []

        for level in config["levels"]:
            # Build empty level object which acts as a parent for all rooms on the level
            level_obj = Entity.create_empty("Level#" + level["id"])
            level_obj.set_cp("type", "Level")
            if "bbox" in level:
                level_obj.set_cp(
                    "bbox", SuncgLoader._correct_bbox_frame(level["bbox"]))
            else:
                print(
                    "Warning: The level with id " + level["id"] +
                    " is missing the bounding box attribute in the given house.json file!"
                )
            loaded_objects.append(level_obj)

            room_per_object = {}

            for node in level["nodes"]:
                # Skip invalid nodes (This is the same behavior as in the SUNCG Toolbox)
                if "valid" in node and node["valid"] == 0:
                    continue

                # Metadata is directly stored in the objects custom data
                metadata = {"type": node["type"], "is_suncg": True}

                if "modelId" in node:
                    metadata["modelId"] = node["modelId"]

                    if node["modelId"] in object_fine_grained_label_map:
                        metadata[
                            "fine_grained_class"] = object_fine_grained_label_map[
                                node["modelId"]]
                        metadata[
                            "coarse_grained_class"] = object_coarse_grained_label_map[
                                node["modelId"]]
                        metadata["category_id"] = label_mapping.id_from_label(
                            object_label_map[node["modelId"]])

                if "bbox" in node:
                    metadata["bbox"] = SuncgLoader._correct_bbox_frame(
                        node["bbox"])

                if "transform" in node:
                    transform = Matrix([
                        node["transform"][i * 4:(i + 1) * 4] for i in range(4)
                    ])
                    # Transpose, as given transform matrix was col-wise, but blender expects row-wise
                    transform.transpose()
                else:
                    transform = None

                if "materials" in node:
                    material_adjustments = node["materials"]
                else:
                    material_adjustments = []

                # Lookup if the object belongs to a room
                object_id = int(node["id"].split("_")[-1])
                if object_id in room_per_object:
                    parent = room_per_object[object_id]
                else:
                    parent = level_obj

                if node["type"] == "Room":
                    loaded_objects += SuncgLoader._load_room(
                        node, metadata, material_adjustments, transform,
                        house_id, level_obj, room_per_object, label_mapping)
                elif node["type"] == "Ground":
                    loaded_objects += SuncgLoader._load_ground(
                        node, metadata, material_adjustments, transform,
                        house_id, parent, label_mapping)
                elif node["type"] == "Object":
                    loaded_objects += SuncgLoader._load_object(
                        node, metadata, material_adjustments, transform,
                        parent)
                elif node["type"] == "Box":
                    loaded_objects += SuncgLoader._load_box(
                        node, material_adjustments, transform, parent,
                        label_mapping)
        SuncgLoader._rename_materials()
        return loaded_objects