def convert_internal_to_labelme(image_path: str, internal_json: dict) -> dict: """ 将系统内部的 detection_json/modified_json 转换为 LabelMe 格式 """ result = internal_json.get("result", {}) img_h = result.get("imageHeight", 0) img_w = result.get("imageWidth", 0) labelme_data = { "version": "5.5.0", "flags": {}, "shapes": [], "imagePath": image_path.lstrip("/\\"), # 去除开头的斜杠 "imageData": None, "imageHeight": img_h, "imageWidth": img_w } # 1. 处理框 (Center Result -> Inner/Outer Box) # inner_box 放在第二个,outer_box 放在最上面 center_result = result.get("center_result", {}) box_result = center_result.get("box_result", {}) inner_shape = None outer_shape = None # 提取 Inner Box if "inner_box" in box_result: shapes = box_result["inner_box"].get("shapes", []) if shapes and len(shapes) > 0: points = shapes[0].get("points", []) if points: inner_shape = { "label": "inner_box", "points": points, "group_id": None, "shape_type": "polygon", "flags": {} } # 提取 Outer Box if "outer_box" in box_result: shapes = box_result["outer_box"].get("shapes", []) if shapes and len(shapes) > 0: points = shapes[0].get("points", []) if points: outer_shape = { "label": "outer_box", "points": points, "group_id": None, "shape_type": "polygon", "flags": {} } # 添加到列表头部 if outer_shape: labelme_data["shapes"].append(outer_shape) if inner_shape: labelme_data["shapes"].append(inner_shape) # 2. 处理缺陷 (Defects) defect_result = result.get("defect_result", {}) defects = defect_result.get("defects", []) for defect in defects: shape = { "label": defect.get("label", "unknown"), "points": defect.get("points", []), "group_id": None, "description": "", "shape_type": "polygon", "flags": {}, # 我们将系统特有的字段暂存到 extra_info 中,或者直接放在 shape 根目录下 # LabelMe 打开时会忽略这些额外字段,但保存时通常会保留它们 (取决于版本) # 为了安全,关键信息最好也保留 "system_data": { "confidence": defect.get("confidence"), "defect_type": defect.get("defect_type"), "score": defect.get("score"), "edit_type": defect.get("edit_type", "edit") } } labelme_data["shapes"].append(shape) return labelme_data def convert_labelme_to_internal(original_json: dict, labelme_json: dict) -> dict: """ 将 LabelMe JSON 还原为系统内部格式结构。 注意:我们只构建 '结构',具体的面积/分数计算交给 Project 1 的重计算接口。 我们需要保留 original_json 中的一些元数据 (如 imageHeight, etc.) """ # 基础结构复制 new_internal = { "result": { "imageHeight": labelme_json.get("imageHeight", original_json["result"].get("imageHeight")), "imageWidth": labelme_json.get("imageWidth", original_json["result"].get("imageWidth")), "center_result": {}, "defect_result": {"defects": []} } } # 准备容器 defects_list = [] inner_box_data = None outer_box_data = None # 遍历 LabelMe 的 shapes for shape in labelme_json.get("shapes", []): label = shape.get("label") points = shape.get("points", []) if not points: continue if label == "outer_box": # 重构 outer_box 结构 outer_box_data = { "shapes": [{"label": "outer_box", "points": points, "shape_type": "polygon"}] } elif label == "inner_box": # 重构 inner_box 结构 inner_box_data = { "shapes": [{"label": "inner_box", "points": points, "shape_type": "polygon"}] } else: # 这是一个缺陷 # 尝试从 shape 中读取我们之前暂存的 system_data,如果没有则初始化默认值 sys_data = shape.get("system_data", {}) defect_obj = { "label": label, "points": points, "shape_type": "polygon", # 如果是新加的缺陷,这些可能是 None,重计算服务应该处理它 "confidence": sys_data.get("confidence", 1.0), "defect_type": sys_data.get("defect_type", "face"), # 默认为 face, 服务端会重新校验 "edit_type": "edit", # 标记为编辑过 # score 字段由服务端计算,这里可以不传或者传旧的 "score": sys_data.get("score") } defects_list.append(defect_obj) # 组装 Center Result # 只有当原始数据里有 box_result 或者 labelme 里画了 box 时才构造 # 保持原有的层级结构: center_result -> box_result -> inner_box/outer_box if inner_box_data or outer_box_data: new_internal["result"]["center_result"] = { "box_result": {} } if inner_box_data: new_internal["result"]["center_result"]["box_result"]["inner_box"] = inner_box_data if outer_box_data: new_internal["result"]["center_result"]["box_result"]["outer_box"] = outer_box_data else: # 如果没有框的信息(例如面部图),保持 center_result 为空或复制原始的非 box 信息 new_internal["result"]["center_result"] = {} # 组装 Defect Result new_internal["result"]["defect_result"]["defects"] = defects_list # 复制 statistics 等其他可能的字段,或者留给服务端重新生成 # 建议只传核心数据,让服务端全权处理统计 return new_internal