Kaynağa Gözat

分数计算初始开始

AnlaAnla 3 ay önce
ebeveyn
işleme
b61e2ffbfa

+ 2 - 0
app/api/card_inference.py

@@ -64,8 +64,10 @@ async def card_model_inference(
         )
         return json_result
     except ValueError as e:
+        logger.error(e)
         raise HTTPException(status_code=400, detail=str(e))
     except Exception as e:
+        logger.error(e)
         raise HTTPException(status_code=500, detail=f"服务器内部错误: {e}")
 
 

+ 2 - 1
app/core/config.py

@@ -19,7 +19,8 @@ class Settings:
     TEMP_WORK_DIR = BASE_PATH / "_temp_work"
 
     # 图片像素与真实图片缩放比例
-    pixel_resolution = 24.54
+    PIXEL_RESOLUTION = 24.54
+    CORNER_SIZE_MM = 3.0
 
     # 使用一个字典来管理所有卡片检测模型
     # key (如 'outer_box') 将成为 API 路径中的 {inference_type}

+ 0 - 5
app/core/logger.py

@@ -4,11 +4,6 @@ import logging
 logging.basicConfig(
     level=logging.INFO,  # 生产环境通常设置为 INFO 或 WARNING
     format="%(asctime)s - %(name)s - %(levelname)s - %(message)s",
-    # 可以添加 handlers,例如 FileHandler 将日志写入文件
-    handlers=[
-        logging.FileHandler("app.log"),
-        logging.StreamHandler()  # 默认会输出到控制台
-    ]
 )
 
 # 获取一个日志器实例,通常以模块名命名

+ 322 - 0
app/core/scoring_config.json

@@ -0,0 +1,322 @@
+{
+  "base_score": 10.0,
+  "corners": {
+    "rules": {
+      "wear_area": [
+        [
+          0.05,
+          -0.1
+        ],
+        [
+          0.1,
+          -0.5
+        ],
+        [
+          0.25,
+          -1.5
+        ],
+        [
+          0.5,
+          -3.0
+        ],
+        [
+          "inf",
+          -5.0
+        ]
+      ],
+      "sharp_loss_area": [
+        [
+          0.05,
+          -0.1
+        ],
+        [
+          0.1,
+          -0.5
+        ],
+        [
+          0.25,
+          -1.5
+        ],
+        [
+          0.5,
+          -3.0
+        ],
+        [
+          "inf",
+          -5.0
+        ]
+      ]
+    },
+    "front_weights": {
+      "wear_area": 0.3,
+      "sharp_loss_area": 0.7
+    },
+    "back_weights": {
+      "wear_area": 0.3,
+      "sharp_loss_area": 0.7
+    },
+    "final_weights": {
+      "front": 0.7,
+      "back": 0.3
+    }
+  },
+  "edges": {
+    "rules": {
+      "wear_area": [
+        [
+          0.05,
+          -0.1
+        ],
+        [
+          0.1,
+          -0.5
+        ],
+        [
+          0.25,
+          -1.5
+        ],
+        [
+          0.5,
+          -3.0
+        ],
+        [
+          "inf",
+          -5.0
+        ]
+      ],
+      "block_loss_area": [
+        [
+          0.05,
+          -0.1
+        ],
+        [
+          0.1,
+          -0.5
+        ],
+        [
+          0.25,
+          -1.5
+        ],
+        [
+          0.5,
+          -3.0
+        ],
+        [
+          "inf",
+          -5.0
+        ]
+      ]
+    },
+    "front_weights": {
+      "wear_area": 0.4,
+      "block_loss_area": 0.6
+    },
+    "back_weights": {
+      "wear_area": 0.4,
+      "block_loss_area": 0.6
+    },
+    "final_weights": {
+      "front": 0.7,
+      "back": 0.3
+    }
+  },
+  "face": {
+    "rules": {
+      "dent_area": [
+        [
+          0.05,
+          -0.1
+        ],
+        [
+          0.1,
+          -0.5
+        ],
+        [
+          0.25,
+          -1.5
+        ],
+        [
+          0.5,
+          -3.0
+        ],
+        [
+          "inf",
+          -5.0
+        ]
+      ],
+      "stain_area": [
+        [
+          0.05,
+          -0.1
+        ],
+        [
+          0.1,
+          -0.5
+        ],
+        [
+          0.25,
+          -1.5
+        ],
+        [
+          0.5,
+          -3.0
+        ],
+        [
+          "inf",
+          -5.0
+        ]
+      ],
+      "scratch_length": [
+        [
+          1.0,
+          -0.5
+        ],
+        [
+          3.0,
+          -1.5
+        ],
+        [
+          5.0,
+          -3.0
+        ],
+        [
+          "inf",
+          -5.0
+        ]
+      ]
+    },
+    "coefficients": {
+      "scratch_length": 1.0,
+      "dent_area": 1.0,
+      "stain_area": 1.0
+    },
+    "final_weights": {
+      "front": 0.75,
+      "back": 0.25
+    }
+  },
+  "centering": {
+    "front": {
+      "rules": [
+        [
+          52,
+          0
+        ],
+        [
+          55,
+          -0.5
+        ],
+        [
+          60,
+          -1.0
+        ],
+        [
+          62.5,
+          -1.5
+        ],
+        [
+          65,
+          -2.0
+        ],
+        [
+          67.5,
+          -2.5
+        ],
+        [
+          70,
+          -3.0
+        ],
+        [
+          72.5,
+          -3.5
+        ],
+        [
+          75,
+          -4.0
+        ],
+        [
+          77.5,
+          -4.5
+        ],
+        [
+          80,
+          -5.0
+        ],
+        [
+          82.5,
+          -5.5
+        ],
+        [
+          85,
+          -6.0
+        ],
+        [
+          87.5,
+          -6.5
+        ],
+        [
+          90,
+          -7.0
+        ],
+        [
+          92.5,
+          -7.5
+        ],
+        [
+          95,
+          -8.0
+        ],
+        [
+          97.5,
+          -8.5
+        ],
+        [
+          "inf",
+          -9.0
+        ]
+      ],
+      "coefficients": {
+        "horizontal": 1.2,
+        "vertical": 0.9
+      }
+    },
+    "back": {
+      "rules": [
+        [
+          60,
+          -0.5
+        ],
+        [
+          70,
+          -1.0
+        ],
+        [
+          75,
+          -1.5
+        ],
+        [
+          85,
+          -2.0
+        ],
+        [
+          95,
+          -2.5
+        ],
+        [
+          "inf",
+          -3.0
+        ]
+      ],
+      "coefficients": {
+        "horizontal": 1.2,
+        "vertical": 0.9
+      }
+    },
+    "tilt_penalty": -1.5,
+    "tilt_threshold_degrees": 0.5,
+    "final_weights": {
+      "front": 0.75,
+      "back": 0.25
+    }
+  }
+}

+ 23 - 13
app/services/defect_service.py

@@ -4,7 +4,8 @@ from ..core.model_loader import get_predictor
 from app.utils.CardDefectAggregator import CardDefectAggregator
 from app.utils.arean_anylize_draw import DefectProcessor, DrawingParams
 from app.utils.AnalyzeCenter import analyze_centering_rotated
-from app.utils.json_data_formate import formate_center_data
+from app.utils.ClassifyEdgeCorner import ClassifyEdgeCorner
+from app.utils.json_data_formate import formate_center_data, formate_face_data
 from app.core.config import settings
 from app.core.logger import logger
 import json
@@ -59,22 +60,24 @@ class DefectInferenceService:
             #     json.dump(json_data, f, ensure_ascii=False, indent=4)
             # logger.info(f"合并结束")
 
-            processor = DefectProcessor(pixel_resolution=settings.pixel_resolution)
-            area_json_path = settings.TEMP_WORK_DIR / f'{inference_type}-area_result.json'
+            processor = DefectProcessor(pixel_resolution=settings.PIXEL_RESOLUTION)
+            area_json_path = settings.TEMP_WORK_DIR / f'{inference_type}-face_result.json'
             if is_draw_image:
                 drawing_params_with_rect = DrawingParams(draw_min_rect=True)
                 drawn_image, area_json = processor.analyze_and_draw(img_bgr, json_data,
                                                                     drawing_params_with_rect)
-                temp_img_path = settings.TEMP_WORK_DIR / f'{inference_type}-area_result.jpg'
+                temp_img_path = settings.TEMP_WORK_DIR / f'{inference_type}-face_result.jpg'
                 cv2.imwrite(temp_img_path, drawn_image)
             else:
                 area_json = processor.analyze_from_json(json_data)
 
+            face_json_result = formate_face_data(area_json)
+
             with open(area_json_path, 'w', encoding='utf-8') as f:
-                json.dump(area_json, f, ensure_ascii=False, indent=4)
-            logger.info("面积计算结束")
+                json.dump(face_json_result, f, ensure_ascii=False, indent=2)
+            logger.info("面的面积计算结束")
 
-            return area_json
+            return face_json_result
 
         # 边角
         elif (inference_type == "pokemon_front_corner_no_reflect_defect"
@@ -98,22 +101,29 @@ class DefectInferenceService:
             #     json.dump(json_data, f, ensure_ascii=False, indent=4)
             # logger.info(f"合并结束")
 
-            processor = DefectProcessor(pixel_resolution=settings.pixel_resolution)
-            area_json_path = settings.TEMP_WORK_DIR / f'{inference_type}-area_result.json'
+            processor = DefectProcessor(pixel_resolution=settings.PIXEL_RESOLUTION)
+            area_json_path = settings.TEMP_WORK_DIR / f'{inference_type}-corner_result.json'
             if is_draw_image:
                 drawing_params_with_rect = DrawingParams(draw_min_rect=True)
                 drawn_image, area_json = processor.analyze_and_draw(img_bgr, json_data,
                                                                     drawing_params_with_rect)
-                temp_img_path = settings.TEMP_WORK_DIR / f'{inference_type}-area_result.jpg'
+                temp_img_path = settings.TEMP_WORK_DIR / f'{inference_type}-corner_result.jpg'
                 cv2.imwrite(temp_img_path, drawn_image)
             else:
                 area_json = processor.analyze_from_json(json_data)
 
+            # 推理外框
+            predictor_outer = get_predictor("pokemon_outer_box")
+            outer_result = predictor_outer.predict_from_image(img_bgr)
+
+            classifier = ClassifyEdgeCorner(settings.PIXEL_RESOLUTION, settings.CORNER_SIZE_MM)
+            edge_corner_data = classifier.classify_defects_location(area_json, outer_result)
+
             with open(area_json_path, 'w', encoding='utf-8') as f:
-                json.dump(area_json, f, ensure_ascii=False, indent=4)
-            logger.info("面积计算结束")
+                json.dump(edge_corner_data, f, ensure_ascii=False, indent=2)
+            logger.info("边角面积计算结束")
 
-            return area_json
+            return edge_corner_data
 
         elif inference_type == "pokemon_card_center":
             predictor_inner = get_predictor(settings.DEFECT_TYPE[inference_type]['inner_box'])

+ 1 - 1
app/utils/CardDefectAggregator.py

@@ -246,7 +246,7 @@ class CardDefectAggregator:
             output_json_path (str): 输出合并后JSON文件的路径。
             mode (str): 处理模式,'face' (全图) 或 'edge' (仅边缘)。
         """
-        log_print("组开始", f"开始处理图片: {image},模式: {mode}")
+        # log_print("组开始", f"开始处理图片: {image},模式: {mode}")
 
         if isinstance(image, str):
             image = fry_cv2_imread(image)

+ 96 - 0
app/utils/CardScorer.py

@@ -0,0 +1,96 @@
+import json
+from typing import List, Dict, Any, Tuple
+
+
+class CardScorer:
+    """
+    它从一个JSON配置文件加载评分规则,并根据输入的卡片数据计算分数。
+    """
+
+    def __init__(self, config_path: str):
+        try:
+            with open(config_path, 'r', encoding='utf-8') as f:
+                self.config = json.load(f)
+            self.base_score = self.config.get("base_score", 10.0)
+        except FileNotFoundError:
+            raise ValueError(f"配置文件未找到: {config_path}")
+        except json.JSONDecodeError:
+            raise ValueError(f"配置文件格式错误: {config_path}")
+
+    def _get_score_from_tiers(self, value: float, rules: List[List[Any]]) -> float:
+        """
+        一个通用的辅助函数,根据分层规则查找值对应的分数。
+        """
+        for tier in rules:
+            threshold, score = tier
+            if threshold == "inf" or value < threshold:
+                return float(score)
+        return 0.0  # 如果没有匹配的规则,不扣分
+
+    def calculate_defect_score(self, aspect: str, defect_data: Dict[str, List[float]]) -> float:
+        """
+        一个通用的缺陷计分函数,用于计算角、边、表面的加权分数。
+        'corners', 'edges', 'face'
+        """
+        aspect_config = self.config[aspect]
+        total_deduction = 0.0
+        weighted_scores = {}
+
+        # 1. 计算每种缺陷类型的总扣分
+        for defect_type, values in defect_data.items():
+            if defect_type not in aspect_config['rules']:
+                continue
+
+            rules = aspect_config['rules'][defect_type]
+            # 累加所有同类型缺陷的扣分
+            type_deduction = sum(self._get_score_from_tiers(v, rules) for v in values)
+            weighted_scores[defect_type] = type_deduction
+
+        # 2. 根据权重/系数计算最终扣分
+        weights = aspect_config.get(f"{self.current_face}_weights") or aspect_config.get("coefficients")
+        if not weights:
+            raise ValueError(f"在配置中未找到 '{aspect}' 的权重/系数")
+
+        for defect_type, score in weighted_scores.items():
+            total_deduction += score * weights.get(defect_type, 1.0)
+
+        return total_deduction
+
+    def calculate_centering_score(self, card_type: str, center_data: dict) -> float:
+        """
+        计算居中度分数。
+        card_type 为 front或 back
+        """
+        if card_type != "front" and card_type != "back":
+            raise TypeError("calculate_centering_score:card_type 只能为 front 或 back")
+
+        centering_config = self.config['centering'][card_type]
+        rules = centering_config['rules']
+        coefficients = centering_config['coefficients']
+
+        center_left = center_data['inference_result']['center_inference']['center_left']
+        center_right = center_data['inference_result']['center_inference']['center_right']
+        center_top = center_data['inference_result']['center_inference']['center_top']
+        center_bottom = center_data['inference_result']['center_inference']['center_bottom']
+
+        # 将比例转换为用于查找规则的单个最大值
+        h_lookup_val = 51
+        v_lookup_val = 52
+
+        h_deduction = self._get_score_from_tiers(h_lookup_val, rules) * coefficients['horizontal']
+        v_deduction = self._get_score_from_tiers(v_lookup_val, rules) * coefficients['vertical']
+
+        print(h_deduction, v_deduction)
+        return h_deduction + v_deduction
+
+
+if __name__ == '__main__':
+    # 1. 初始化评分器,加载规则
+    scorer = CardScorer(r"C:\Code\ML\Project\CheckCardBoxAndDefectServer\app\core\scoring_config.json")
+
+    center_data_path = r"C:\Code\ML\Project\CheckCardBoxAndDefectServer\_temp_work\pokemon_card_center-center_result.json"
+    with open(center_data_path, 'r', encoding='utf-8') as f:
+        center_data = json.load(f)
+    center_score = scorer.calculate_centering_score("front", center_data)
+
+    print(center_score)

+ 0 - 5
app/utils/ScoreCalculate.py

@@ -1,5 +0,0 @@
-
-class ScoreCalculate:
-    def __init__(self, card_data_type:str):
-        # face, corner, edge, center
-        self.card_data_type = card_data_type

+ 15 - 2
app/utils/json_data_formate.py

@@ -1,3 +1,6 @@
+import json
+
+
 def formate_center_data(center_result, inner_data: dict, outer_data: dict):
     data = {
         "inference_result": {
@@ -15,5 +18,15 @@ def formate_center_data(center_result, inner_data: dict, outer_data: dict):
     return data
 
 
-def formate_face_data():
-    pass
+def formate_face_data(area_json: dict):
+    for defect in area_json['defects']:
+        # 添加新标签
+        defect['defect_type'] = "face"
+    return area_json
+
+# if __name__ == '__main__':
+#     json_path = r"C:\Code\ML\Project\CheckCardBoxAndDefectServer\_temp_work\pokemon_front_face_no_reflect_defect-area_result.json"
+#     with open(json_path, 'r') as f:
+#         area_json = json.load(f)
+#     result = formate_face_data(area_json)
+#     print(result)