|
|
@@ -16,6 +16,9 @@ from app.utils.scheme import (
|
|
|
)
|
|
|
from app.crud import crud_card
|
|
|
from app.utils.xy_process import convert_internal_to_xy_format, convert_xy_to_internal_format
|
|
|
+import hashlib
|
|
|
+from app.core.minio_client import minio_client
|
|
|
+from app.utils.rating_report_utils import crop_defect_image
|
|
|
|
|
|
logger = get_logger(__name__)
|
|
|
router = APIRouter()
|
|
|
@@ -28,11 +31,68 @@ class QueryMode(str, Enum):
|
|
|
prev = "prev"
|
|
|
|
|
|
|
|
|
+def _process_defects_for_json(card_id: int, img_id: int, img_path: str, json_data: dict, side: str):
|
|
|
+ if not json_data or "result" not in json_data:
|
|
|
+ return
|
|
|
+ defect_result = json_data["result"].get("defect_result", {})
|
|
|
+ defects = defect_result.get("defects", [])
|
|
|
+
|
|
|
+ defect_detail_list = []
|
|
|
+ for idx, defect in enumerate(defects, start=1):
|
|
|
+ min_rect = defect.get("min_rect")
|
|
|
+ defect_img_url = ""
|
|
|
+ location_str = ""
|
|
|
+
|
|
|
+ if min_rect and len(min_rect) == 3:
|
|
|
+ center_x, center_y = min_rect[0]
|
|
|
+ location_str = f"{int(center_x)},{int(center_y)}"
|
|
|
+
|
|
|
+ # 使用坐标哈希作为缓存文件名,避免重复裁剪
|
|
|
+ rect_str = str(min_rect)
|
|
|
+ rect_hash = hashlib.md5(rect_str.encode('utf-8')).hexdigest()[:8]
|
|
|
+ filename = f"xy_{card_id}_{img_id}_{idx}_{rect_hash}.jpg"
|
|
|
+
|
|
|
+ out_rel_path = f"/DefectImage/{filename}"
|
|
|
+ out_object_name = f"{settings.MINIO_BASE_PREFIX}{out_rel_path}"
|
|
|
+ try:
|
|
|
+ # 检查 MinIO 中是否已有该截图,有则直接使用
|
|
|
+ minio_client.stat_object(settings.MINIO_BUCKET, out_object_name)
|
|
|
+ defect_img_url = settings.get_full_url(out_rel_path)
|
|
|
+ except Exception:
|
|
|
+ # 不存在或异常,则执行裁剪并上传
|
|
|
+ defect_img_url = crop_defect_image(img_path, min_rect, filename)
|
|
|
+
|
|
|
+ # 1. 给每条缺陷带上 defectImgUrl
|
|
|
+ defect["defectImgUrl"] = defect_img_url
|
|
|
+
|
|
|
+ # 2. 组装 defectDetailList 元素
|
|
|
+ raw_type = f"{defect.get('defect_type', '')}".upper().strip()
|
|
|
+ type_str_map = {
|
|
|
+ "CORNER": "CORNER",
|
|
|
+ "EDGE": "SIDE",
|
|
|
+ "FACE": "SURFACE"
|
|
|
+ }
|
|
|
+ type_str = type_str_map.get(raw_type, raw_type)
|
|
|
+
|
|
|
+ defect_detail_list.append({
|
|
|
+ "id": defect.get("id", idx),
|
|
|
+ "side": side,
|
|
|
+ "location": location_str,
|
|
|
+ "type": type_str,
|
|
|
+ "defectImgUrl": defect_img_url,
|
|
|
+ "label": defect.get("label", ""),
|
|
|
+ "actual_area": defect.get("actual_area", 0)
|
|
|
+ })
|
|
|
+
|
|
|
+ defect_result["defectDetailList"] = defect_detail_list
|
|
|
+
|
|
|
+
|
|
|
def _process_images_to_xy_format(card_data: dict):
|
|
|
"""
|
|
|
内部辅助函数:遍历卡牌数据中的图片,将 JSON 格式转换为前端需要的 XY 格式。
|
|
|
直接修改传入的 card_data 字典。
|
|
|
"""
|
|
|
+ card_id = card_data.get("id")
|
|
|
if "images" in card_data and card_data["images"]:
|
|
|
for img in card_data["images"]:
|
|
|
# 处理 detection_json
|
|
|
@@ -40,6 +100,7 @@ def _process_images_to_xy_format(card_data: dict):
|
|
|
d_json = img.detection_json
|
|
|
if isinstance(d_json, str):
|
|
|
d_json = json.loads(d_json)
|
|
|
+ _process_defects_for_json(card_id, img.id, img.image_path, d_json, img.image_type)
|
|
|
# *** 转换逻辑 ***
|
|
|
img.detection_json = convert_internal_to_xy_format(d_json)
|
|
|
|
|
|
@@ -48,6 +109,7 @@ def _process_images_to_xy_format(card_data: dict):
|
|
|
m_json = img.modified_json
|
|
|
if isinstance(m_json, str):
|
|
|
m_json = json.loads(m_json)
|
|
|
+ _process_defects_for_json(card_id, img.id, img.image_path, m_json, img.image_type)
|
|
|
# *** 转换逻辑 ***
|
|
|
img.modified_json = convert_internal_to_xy_format(m_json)
|
|
|
return card_data
|