浏览代码

增加根据json重新计算分数的方法

AnlaAnla 1 月之前
父节点
当前提交
5246bc837d

+ 23 - 1
app/api/score_inference.py

@@ -2,6 +2,7 @@ from fastapi import APIRouter, File, UploadFile, Depends, HTTPException
 from fastapi.responses import FileResponse, JSONResponse
 from fastapi.concurrency import run_in_threadpool
 from enum import Enum
+from typing import Optional, Dict, Any
 from ..core.config import settings
 from app.services.score_service import ScoreService
 import numpy as np
@@ -17,7 +18,7 @@ score_names = settings.SCORE_TYPE
 ScoreType = Enum("InferenceType", {name: name for name in score_names})
 
 
-@router.post("/score_inference", description="输入卡片类型(正反面, 缺陷类型), 是否为反射卡")
+@router.post("/score_inference", summary="输入卡片类型(正反面, 缺陷类型), 是否为反射卡")
 async def card_model_inference(
         score_type: ScoreType,
         is_reflect_card: bool = False,
@@ -52,3 +53,24 @@ async def card_model_inference(
         raise HTTPException(status_code=400, detail=str(e))
     except Exception as e:
         raise HTTPException(status_code=500, detail=f"服务器内部错误: {e}")
+
+
+@router.post("/score_recalculate", summary="输入卡片类型(正反面, 缺陷类型)",
+             description="输入的json数据结构为 "
+                         "{'result': {'center_result':..., 'defect_result':...}}")
+async def score_recalculate(score_type: ScoreType, json_data: Dict[str, Any]):
+    """
+    接收分数推理后的结果, 然后重新根据json数据计算居中和缺陷等分数
+    """
+    service = ScoreService()
+    try:
+        json_result = await run_in_threadpool(
+            service.recalculate_defect_score,
+            score_type=score_type.value,
+            json_data=json_data
+        )
+        return json_result
+    except ValueError as e:
+        raise HTTPException(status_code=400, detail=str(e))
+    except Exception as e:
+        raise HTTPException(status_code=500, detail=f"服务器内部错误: {e}")

+ 42 - 2
app/services/defect_service.py

@@ -142,8 +142,8 @@ class DefectInferenceService:
             outer_points = outer_result['shapes'][0]['points']
             center_result, inner_rect_box, outer_rect_box = analyze_centering_rotated(inner_points, outer_points)
 
-            logger.info(f"inner_rect_box: {type(inner_rect_box)}, {inner_rect_box}")
-            logger.info(f"outer_rect_box: , {outer_rect_box}")
+            # logger.info(f"inner_rect_box: {type(inner_rect_box)}, {inner_rect_box}")
+            # logger.info(f"outer_rect_box: , {outer_rect_box}")
             logger.info("格式化居中数据")
             center_result = formate_center_data(center_result,
                                                 inner_result, outer_result,
@@ -161,5 +161,45 @@ class DefectInferenceService:
         else:
             return {}
 
+    # inference_type: center, face, corner_edge
+    def re_inference_from_json(self, inference_type: str, center_json: dict, defect_json: dict):
+        inference_type_list = ["center", "face", "corner_edge"]
+        if inference_type not in inference_type_list:
+            logger.error(f"inference_type 只能为{inference_type_list}, 输入为{inference_type}")
+            raise ValueError(f"inference_type 只能为{inference_type_list}, 输入为{inference_type}")
+
+        inner_result = center_json['box_result']['inner_box']
+        outer_result = center_json['box_result']['outer_box']
+        processor = DefectProcessor(pixel_resolution=settings.PIXEL_RESOLUTION)
+
+        if inference_type == "center":
+            inner_points = inner_result['shapes'][0]['points']
+            outer_points = outer_result['shapes'][0]['points']
+            center_result, inner_rect_box, outer_rect_box = analyze_centering_rotated(inner_points, outer_points)
+
+            center_result = formate_center_data(center_result,
+                                                inner_result, outer_result,
+                                                inner_rect_box, outer_rect_box)
+            return center_result
+        elif inference_type == "face":
+            area_json = processor.re_analyze_from_json(defect_json)
+            logger.info("面缺陷面积计算结束")
+
+            return area_json
+        elif inference_type == "corner_edge":
+
+            area_json: dict = processor.re_analyze_from_json(defect_json)
+            logger.info("边角缺陷面积计算结束")
+
+            # 根据外框区分边和角
+            classifier = ClassifyEdgeCorner(settings.PIXEL_RESOLUTION, settings.CORNER_SIZE_MM)
+            edge_corner_data = classifier.classify_defects_location(area_json, outer_result)
+
+            logger.info("边角面积计算结束")
+
+            return edge_corner_data
+        else:
+            return {}
+
 # 创建一个单例服务
 # defect_service = DefectInferenceService()

+ 78 - 22
app/services/score_service.py

@@ -14,43 +14,46 @@ logger = get_logger(__name__)
 class ScoreService:
     def __init__(self):
         self.scoring_config_path = settings.SCORE_CONFIG_PATH
+        self.card_scorer = CardScorer(config_path=self.scoring_config_path)
+
+        self.defect_service = DefectInferenceService()
+        self.rectify_center_service = CardRectifyAndCenter()
 
     def score_inference(self, score_type: str, is_reflect_card: bool,
                         img_bgr: np.ndarray) -> dict:
-        defect_service = DefectInferenceService()
-        rectify_center_service = CardRectifyAndCenter()
-        card_scorer = CardScorer(config_path=self.scoring_config_path)
 
         logger.info("开始进行卡片居中和转正")
-        img_bgr = rectify_center_service.rectify_and_center(img_bgr)
+        img_bgr = self.rectify_center_service.rectify_and_center(img_bgr)
 
         logger.info("开始进行卡片分数推理")
         if score_type == 'front_corner_edge' or score_type == 'front_face':
-            center_data = defect_service.defect_inference("pokemon_front_card_center", img_bgr.copy())
+            center_data = self.defect_service.defect_inference("pokemon_front_card_center", img_bgr.copy())
         else:
-            center_data = defect_service.defect_inference("pokemon_back_card_center", img_bgr.copy())
+            center_data = self.defect_service.defect_inference("pokemon_back_card_center", img_bgr.copy())
 
         if is_reflect_card:
             if score_type == 'front_corner_edge':
-                defect_data = defect_service.defect_inference('pokemon_front_corner_reflect_defect', img_bgr.copy())
+                defect_data = self.defect_service.defect_inference('pokemon_front_corner_reflect_defect',
+                                                                   img_bgr.copy())
             elif score_type == 'front_face':
-                defect_data = defect_service.defect_inference('pokemon_front_face_reflect_defect', img_bgr.copy())
+                defect_data = self.defect_service.defect_inference('pokemon_front_face_reflect_defect', img_bgr.copy())
             elif score_type == 'back_corner_edge':
-                defect_data = defect_service.defect_inference('pokemon_back_corner_defect', img_bgr.copy())
+                defect_data = self.defect_service.defect_inference('pokemon_back_corner_defect', img_bgr.copy())
             elif score_type == 'back_face':
-                defect_data = defect_service.defect_inference('pokemon_back_face_defect', img_bgr.copy())
+                defect_data = self.defect_service.defect_inference('pokemon_back_face_defect', img_bgr.copy())
             else:
                 return {}
         else:
             if score_type == 'front_corner_edge':
-                defect_data = defect_service.defect_inference('pokemon_front_corner_no_reflect_defect', img_bgr.copy())
+                defect_data = self.defect_service.defect_inference('pokemon_front_corner_no_reflect_defect',
+                                                                   img_bgr.copy())
             elif score_type == 'front_face':
-                defect_data = defect_service.defect_inference('pokemon_front_face_no_reflect_defect', img_bgr.copy())
+                defect_data = self.defect_service.defect_inference('pokemon_front_face_no_reflect_defect',
+                                                                   img_bgr.copy())
             elif score_type == 'back_corner_edge':
-                defect_data = defect_service.defect_inference('pokemon_back_corner_defect', img_bgr.copy())
+                defect_data = self.defect_service.defect_inference('pokemon_back_corner_defect', img_bgr.copy())
             elif score_type == 'back_face':
-                defect_data = defect_service.defect_inference('pokemon_back_face_defect', img_bgr.copy())
-
+                defect_data = self.defect_service.defect_inference('pokemon_back_face_defect', img_bgr.copy())
             else:
                 return {}
 
@@ -64,23 +67,76 @@ class ScoreService:
         else:
             card_defect_type = "face"
 
-        center_score_data = card_scorer.calculate_centering_score(card_aspect, center_data, True)
+        center_score_data = self.card_scorer.calculate_centering_score(card_aspect, center_data, True)
 
         if card_defect_type == 'corner_edge':
             # 先计算角的分数, 会把分数写入json, 然后传入刚写好的, 继续写边的分数
-            corner_score_data = card_scorer.calculate_defect_score('corner', card_aspect, defect_data, True)
-            defect_score_data = card_scorer.calculate_defect_score('edge', card_aspect, corner_score_data, True)
+            corner_score_data = self.card_scorer.calculate_defect_score('corner', card_aspect, defect_data, True)
+            defect_score_data = self.card_scorer.calculate_defect_score('edge', card_aspect, corner_score_data, True)
         elif card_defect_type == 'face':
-            defect_score_data = card_scorer.calculate_defect_score('face', card_aspect, defect_data, True)
+            defect_score_data = self.card_scorer.calculate_defect_score('face', card_aspect, defect_data, True)
         else:
             return {}
 
-        result_json = card_scorer.formate_one_card_result(center_score_data, defect_score_data,
-                                                          card_defect_type=card_defect_type,
-                                                          card_aspect=card_aspect)
+        result_json = self.card_scorer.formate_one_card_result(center_score_data, defect_score_data,
+                                                               card_defect_type=card_defect_type,
+                                                               card_aspect=card_aspect)
 
         temp_score_json_path = settings.TEMP_WORK_DIR / f'{score_type}_score.json'
         with open(temp_score_json_path, 'w', encoding='utf-8') as f:
             json.dump(result_json, f, ensure_ascii=False, indent=2)
         logger.info("分数推理完成 ")
         return result_json
+
+    def recalculate_defect_score(self, score_type: str, json_data: dict):
+        print(score_type)
+        print(json_data)
+
+        center_json_data = json_data["result"]['center_result']
+        defect_json_data = json_data["result"]['defect_result']
+
+        logger.info("开始进行居中信息重计算")
+        center_data = self.defect_service.re_inference_from_json(inference_type="center",
+                                                                 center_json=center_json_data,
+                                                                 defect_json=defect_json_data)
+
+        logger.info("开始进行缺陷信息重计算")
+        if score_type == 'front_corner_edge' or score_type == 'back_corner_edge':
+            inference_type = "corner_edge"
+        else:
+            inference_type = "face"
+
+        defect_data = self.defect_service.re_inference_from_json(inference_type=inference_type,
+                                                                 center_json=center_json_data,
+                                                                 defect_json=defect_json_data)
+
+        logger.info("开始重新计算分数")
+        if score_type == 'front_corner_edge' or score_type == 'front_face':
+            card_aspect = "front"
+        else:
+            card_aspect = "back"
+        if score_type == 'front_corner_edge' or score_type == 'back_corner_edge':
+            card_defect_type = "corner_edge"
+        else:
+            card_defect_type = "face"
+
+        center_score_data = self.card_scorer.calculate_centering_score(card_aspect, center_data, True)
+
+        if card_defect_type == 'corner_edge':
+            # 先计算角的分数, 会把分数写入json, 然后传入刚写好的, 继续写边的分数
+            corner_score_data = self.card_scorer.calculate_defect_score('corner', card_aspect, defect_data, True)
+            defect_score_data = self.card_scorer.calculate_defect_score('edge', card_aspect, corner_score_data, True)
+        elif card_defect_type == 'face':
+            defect_score_data = self.card_scorer.calculate_defect_score('face', card_aspect, defect_data, True)
+        else:
+            return {}
+
+        result_json = self.card_scorer.formate_one_card_result(center_score_data, defect_score_data,
+                                                               card_defect_type=card_defect_type,
+                                                               card_aspect=card_aspect)
+
+        temp_score_json_path = settings.TEMP_WORK_DIR / f're_{score_type}_score.json'
+        with open(temp_score_json_path, 'w', encoding='utf-8') as f:
+            json.dump(result_json, f, ensure_ascii=False, indent=2)
+        logger.info("分数推理完成 ")
+        return result_json

+ 50 - 1
app/utils/defect_inference/arean_anylize_draw.py

@@ -212,7 +212,7 @@ class DefectProcessor:
     def analyze_from_json(self, json_data: Dict[str, Any],
                           is_return_obj=False) -> Union[dict, AnalysisResult]:
         """
-        [需求 1] 仅根据JSON数据计算缺陷面积并统计,返回包含详细信息的JSON友好对象。
+        仅根据JSON数据计算缺陷面积并统计,返回包含详细信息的JSON友好对象。
 
         Args:
             json_data (Dict[str, Any]): 从labelme JSON文件加载的字典数据。
@@ -267,6 +267,55 @@ class DefectProcessor:
 
         return result_json
 
+    def re_analyze_from_json(self, json_data: Dict[str, Any]) -> dict:
+        """
+        根据已经分析计算过的json数据重新计算面积, 用于前端手动修改缺陷后的重新计算
+        """
+        if not json_data or 'defects' not in json_data:
+            logger.error("重计算缺陷面积时缺少关键字段 defects")
+            raise ValueError("重计算缺陷面积时缺少关键字段 defects")
+        result = AnalysisResult()
+
+        new_defects = []
+
+        for defect in json_data['defects']:
+            label = defect.get('label', 'unlabeled')
+            points = defect.get('points')
+            if not points:
+                continue
+
+            contour = np.array(points, dtype=np.int32)
+            pixel_area, actual_area, width, height, min_rect = self._calculate_metrics(contour, self.pixel_to_mm)
+
+            defect["pixel_area"] = pixel_area
+            defect["actual_area"] = actual_area
+            defect["width"] = width
+            defect["height"] = height
+            defect["min_rect"] = min_rect
+
+            # 更新统计信息
+            result.total_defect_count += 1
+            result.total_pixel_area += pixel_area
+            result.total_defect_area += actual_area
+            result.count_by_label[label] += 1
+            result.area_by_label[label] += actual_area
+
+            new_defects.append(defect)
+
+        statistics = {
+            "total_defect_count": result.total_defect_count,
+            "total_pixel_area": result.total_pixel_area,
+            "total_defect_area_mm2": result.total_defect_area,
+            "area_by_label_mm2": dict(result.area_by_label),
+            "count_by_label": dict(result.count_by_label)
+        }
+        json_data['defects'] = new_defects
+        json_data['statistics'] = statistics
+
+        return json_data
+
+
+
     def analyze_and_draw(self, image: np.ndarray, json_data: Dict[str, Any], drawing_params: DrawingParams) -> Tuple[
         np.ndarray, dict]:
         """