Przeglądaj źródła

新增融合图以及修改返回前端缺陷图

袁威 2 tygodni temu
rodzic
commit
0978741f0a

+ 43 - 8
app/api/formate_xy.py

@@ -31,17 +31,21 @@ 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):
+def _process_defects_for_json(card_id: int, img_id: int, img_path: str, json_data: dict, side: str, all_images: list = None):
     if not json_data or "result" not in json_data:
         return
     defect_result = json_data["result"].get("defect_result", {})
     defects = defect_result.get("defects", [])
     
+    is_fusion = side in ("front_fusion", "back_fusion")
+    side_prefix = "front_" if side.startswith("front_") else "back_"
+    
     defect_detail_list = []
     for idx, defect in enumerate(defects, start=1):
         min_rect = defect.get("min_rect")
         defect_img_url = ""
         location_str = ""
+        defect_img_url_list = []
         
         if min_rect and len(min_rect) == 3:
             center_x, center_y = min_rect[0]
@@ -61,9 +65,36 @@ def _process_defects_for_json(card_id: int, img_id: int, img_path: str, json_dat
             except Exception:
                 # 不存在或异常,则执行裁剪并上传
                 defect_img_url = crop_defect_image(img_path, min_rect, filename)
+            
+            # 把同面的其他类型图在同样位置截图(不论是不是融合图都截)
+            if all_images:
+                same_side_images = [img for img in all_images if getattr(img, 'image_type', '').startswith(side_prefix)]
+                for s_img in same_side_images:
+                    s_img_type = getattr(s_img, 'image_type', '')
+                    s_img_path = getattr(s_img, 'image_path', '')
+                    s_img_id = getattr(s_img, 'id', 0)
+                    
+                    s_filename = f"xy_{card_id}_{s_img_id}_{idx}_{rect_hash}.jpg"
+                    s_out_rel_path = f"/DefectImage/{s_filename}"
+                    s_out_object_name = f"{settings.MINIO_BASE_PREFIX}{s_out_rel_path}"
+                    
+                    s_url = ""
+                    try:
+                        minio_client.stat_object(settings.MINIO_BUCKET, s_out_object_name)
+                        s_url = settings.get_full_url(s_out_rel_path)
+                    except Exception:
+                        if s_img_path:
+                            s_url = crop_defect_image(s_img_path, min_rect, s_filename)
+                        
+                    if s_url:
+                        defect_img_url_list.append({
+                            "image_type": s_img_type,
+                            "url": s_url
+                        })
         
         # 1. 给每条缺陷带上 defectImgUrl
         defect["defectImgUrl"] = defect_img_url
+        defect["defectImgUrls"] = defect_img_url_list
         
         # 2. 组装 defectDetailList 元素
         raw_type = f"{defect.get('defect_type', '')}".upper().strip()
@@ -74,15 +105,18 @@ def _process_defects_for_json(card_id: int, img_id: int, img_path: str, json_dat
         }
         type_str = type_str_map.get(raw_type, raw_type)
         
-        defect_detail_list.append({
+        detail_item = {
             "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)
-        })
+            "actual_area": defect.get("actual_area", 0),
+            "defectImgUrls": defect_img_url_list
+        }
+            
+        defect_detail_list.append(detail_item)
         
     defect_result["defectDetailList"] = defect_detail_list
 
@@ -93,14 +127,15 @@ def _process_images_to_xy_format(card_data: dict):
     直接修改传入的 card_data 字典。
     """
     card_id = card_data.get("id")
-    if "images" in card_data and card_data["images"]:
-        for img in card_data["images"]:
+    all_images = card_data.get("images", [])
+    if all_images:
+        for img in all_images:
             # 处理 detection_json
             if img.detection_json:
                 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)
+                _process_defects_for_json(card_id, img.id, img.image_path, d_json, img.image_type, all_images)
                 # *** 转换逻辑 ***
                 img.detection_json = convert_internal_to_xy_format(d_json)
 
@@ -109,7 +144,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)
+                _process_defects_for_json(card_id, img.id, img.image_path, m_json, img.image_type, all_images)
                 # *** 转换逻辑 ***
                 img.modified_json = convert_internal_to_xy_format(m_json)
     return card_data

+ 5 - 3
app/crud/crud_card.py

@@ -176,18 +176,20 @@ def get_card_with_details(db_conn: PooledMySQLConnection, card_id: int) -> Optio
         # 5. 获取分数详情 (只基于主图片计算)
         # 过滤掉灰度图进行分数计算,防止干扰逻辑
         main_images_objs = [img for img in final_images_list if
-                            img.image_type not in [ImageType.front_gray.value, ImageType.back_gray.value]]
+                            img.image_type not in [ImageType.front_gray.value, ImageType.back_gray.value, ImageType.front_fusion.value, ImageType.back_fusion.value]]
         score_details = calculate_scores_from_images(main_images_objs)
 
         # 6. 对图片列表进行自定义排序
-        # 顺序: [front_gray, back_gray, front_ring, back_ring, front_coaxial, back_coaxial]
+        # 顺序: [front_gray, back_gray, front_ring, back_ring, front_coaxial, back_coaxial, front_fusion, back_fusion]
         sort_priority = {
             ImageType.front_gray.value: 0,
             ImageType.back_gray.value: 1,
             ImageType.front_ring.value: 2,
             ImageType.back_ring.value: 3,
             ImageType.front_coaxial.value: 4,
-            ImageType.back_coaxial.value: 5
+            ImageType.back_coaxial.value: 5,
+            ImageType.front_fusion.value: 6,
+            ImageType.back_fusion.value: 7
         }
         final_images_list.sort(key=lambda x: sort_priority.get(x.image_type, 999))
 

+ 13 - 4
app/utils/rating_report_utils.py

@@ -262,6 +262,8 @@ def crop_defect_image(original_image_path_str: str, min_rect: List, output_filen
 
         with Image.open(io.BytesIO(image_bytes)) as img:
             img_w, img_h = img.size
+            if img_w <= 1 or img_h <= 1:
+                return ""
             (center_x, center_y), (rect_w, rect_h), angle = min_rect
             center_x = int(center_x)
             center_y = int(center_y)
@@ -272,10 +274,17 @@ def crop_defect_image(original_image_path_str: str, min_rect: List, output_filen
             side_length = max(max(rect_w, rect_h) * resize_scale, 100)
             half_side = side_length / 2
 
-            left = max(0, center_x - half_side)
-            top = max(0, center_y - half_side)
-            right = min(img_w, center_x + half_side)
-            bottom = min(img_h, center_y + half_side)
+            left = int(max(0, min(img_w - 1, center_x - half_side)))
+            top = int(max(0, min(img_h - 1, center_y - half_side)))
+            right = int(min(img_w, max(left + 1, center_x + half_side)))
+            bottom = int(min(img_h, max(top + 1, center_y + half_side)))
+
+            if right <= left or bottom <= top:
+                logger.warning(
+                    f"裁剪坐标无效,跳过: img_size=({img_w},{img_h}), "
+                    f"crop=({left},{top},{right},{bottom}), min_rect={min_rect}"
+                )
+                return ""
 
             cropped_img = img.crop((left, top, right, bottom))
 

+ 7 - 1
app/utils/scheme.py

@@ -15,6 +15,10 @@ class ImageType(str, Enum):
     # 灰度图类型
     front_gray = "front_gray"
     back_gray = "back_gray"
+    
+    # 融合图类型
+    front_fusion = "front_fusion"
+    back_fusion = "back_fusion"
 
 
 class CardType(str, Enum):
@@ -53,7 +57,9 @@ IMAGE_TYPE_TO_SCORE_TYPE = {
     "front_ring": "front_ring",
     "back_ring": "back_ring",
     "front_gray": None,
-    "back_gray": None
+    "back_gray": None,
+    "front_fusion": None,
+    "back_fusion": None
 }