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
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