Răsfoiți Sursa

大改, 去掉边角, 变成环光和同轴的配置

AnlaAnla 3 săptămâni în urmă
părinte
comite
3b4fb4ef9f

BIN
Model/pokemon_back_corner_defect.pth


+ 0 - 0
Model/pokemon_back_face_defect.pth → Model/pokemon_back_face_coaxial_light_defect.pth


BIN
Model/pokemon_front_corner_no_reflect_defect.pth


+ 0 - 0
Model/pokemon_front_face_no_reflect_defect.pth → Model/pokemon_front_face_no_reflect_coaxial_light_defect.pth


BIN
Model/pokemon_front_corner_reflect_defect.pth → Model/pokemon_front_face_no_reflect_ring_light_defect.pth


+ 0 - 0
Model/pokemon_front_face_reflect_defect.pth → Model/pokemon_front_face_reflect_coaxial_light_defect.pth


+ 34 - 50
app/core/config.py

@@ -25,6 +25,7 @@ class Settings:
     # 图片像素与真实图片缩放比例
     PIXEL_RESOLUTION = 24.54
     CORNER_SIZE_MM = 3.0
+    EDGE_SIZE_MM = 1.5
 
     # 使用一个字典来管理所有卡片检测模型
     # key (如 'outer_box') 将成为 API 路径中的 {inference_type}
@@ -62,16 +63,9 @@ class Settings:
             "confidence": 0.5,
             "input_channels": 3,
         },
-        "pokemon_back_corner_defect": {
-            "pth_path": "Model/pokemon_back_corner_defect.pth",
-            "class_dict": {"1": "wear", "2": "wear_and_impact", "3": "damaged",
-                           "4": "impact", "5": "wear_and_stain"},
-            "img_size": {'width': 512, 'height': 512},
-            "confidence": 0.5,
-            "input_channels": 3,
-        },
-        "pokemon_back_face_defect": {
-            "pth_path": "Model/pokemon_back_face_defect.pth",
+        # 同轴光模型
+        "pokemon_back_face_coaxial_light_defect": {
+            "pth_path": "Model/pokemon_back_face_coaxial_light_defect.pth",
             "class_dict": {"1": "wear", "2": "scratch", "3": "stain",
                            "4": "scuff", "5": "impact", "6": "damaged",
                            "7": "wear_and_impact"},
@@ -79,39 +73,25 @@ class Settings:
             "confidence": 0.5,
             "input_channels": 3,
         },
-        "pokemon_front_face_reflect_defect": {
-            "pth_path": "Model/pokemon_front_face_reflect_defect.pth",
+        "pokemon_front_face_reflect_coaxial_light_defect": {
+            "pth_path": "Model/pokemon_front_face_reflect_coaxial_light_defect.pth",
             "class_dict": {"1": "stain", "2": "scratch", "3": "impact", "4": "wear", "5": "wear"},
             "img_size": {'width': 512, 'height': 512},
             "confidence": 0.5,
             "input_channels": 3,
         },
-        "pokemon_front_corner_reflect_defect": {
-            "pth_path": "Model/pokemon_front_corner_reflect_defect.pth",
-            "class_dict": {"1": "impact", "2": "wear_and_impact", "3": "wear"},
-            "img_size": {'width': 512, 'height': 512},
-            "confidence": 0.5,
-            "input_channels": 3,
-        },
-        "pokemon_front_corner_no_reflect_defect": {
-            "pth_path": "Model/pokemon_front_corner_no_reflect_defect.pth",
-            "class_dict": {"1": "wear", "2": "wear_and_impact", "3": "impact", "4": "damaged", "5": "stain"},
-            "img_size": {'width': 512, 'height': 512},
-            "confidence": 0.5,
-            "input_channels": 3,
-        },
-        "pokemon_front_face_no_reflect_defect": {
-            "pth_path": "Model/pokemon_front_face_no_reflect_defect.pth",
+        "pokemon_front_face_no_reflect_coaxial_light_defect": {
+            "pth_path": "Model/pokemon_front_face_no_reflect_coaxial_light_defect.pth",
             "class_dict": {"1": "scratch", "2": "wear", "3": "stain", "4": "damaged", "5": "impact", "6": "protrudent",
                            "7": "wear_and_impact"},
             "img_size": {'width': 512, 'height': 512},
             "confidence": 0.5,
             "input_channels": 3,
         },
-        # 2个环光的面模型
+        # 环光模型
         "pokemon_back_face_ring_light_defect": {
             "pth_path": "Model/pokemon_back_face_ring_light_defect.pth",
-            "class_dict": {"1": "wear", "2": "scratch", "3": "stain", "4": "protrudent",
+            "class_dict": {"1": "wear", "2": "scratch", "3": "stain", "4": "impact",
                            "5": "damaged", "6": "wear_and_impact", "7": "impact"},
             "img_size": {'width': 512, 'height': 512},
             "confidence": 0.5,
@@ -120,7 +100,16 @@ class Settings:
         "pokemon_front_face_reflect_ring_light_defect": {
             "pth_path": "Model/pokemon_front_face_reflect_ring_light_defect.pth",
             "class_dict": {"1": "impact", "2": "wear", "3": "scratch", "4": "wear_and_impact",
-                           "5": "stain", "6": "damaged", "7": "protrudent"},
+                           "5": "stain", "6": "damaged", "7": "impact"},
+            "img_size": {'width': 512, 'height': 512},
+            "confidence": 0.5,
+            "input_channels": 3,
+        },
+        "pokemon_front_face_no_reflect_ring_light_defect": {
+            "pth_path": "Model/pokemon_front_face_no_reflect_ring_light_defect.pth",
+            "class_dict": {"1": "damaged", "2": "scratch", "3": "wear", "4": "stain", "5": "impact",
+                           "6": "wear_and_stain", "7": "wear_and_impact"}
+            ,
             "img_size": {'width': 512, 'height': 512},
             "confidence": 0.5,
             "input_channels": 3,
@@ -138,35 +127,30 @@ class Settings:
             "inner_box": "pokemon_back_inner_box",
             "outer_box": "outer_box",
         },
-        "pokemon_back_face_defect": {
-            'model_type': "pokemon_back_face_defect"
-        },
-        "pokemon_back_corner_defect": {
-            'model_type': "pokemon_back_corner_defect"
-        },
-        "pokemon_front_corner_reflect_defect": {
-            "model_type": "pokemon_front_corner_reflect_defect"
+        # 同轴
+        "pokemon_back_face_coaxial_light_defect": {
+            'model_type': "pokemon_back_face_coaxial_light_defect"
         },
-        "pokemon_front_face_reflect_defect": {
-            "model_type": "pokemon_front_face_reflect_defect"
+        "pokemon_front_face_reflect_coaxial_light_defect": {
+            "model_type": "pokemon_front_face_reflect_coaxial_light_defect"
         },
-        "pokemon_front_corner_no_reflect_defect": {
-            "model_type": "pokemon_front_corner_no_reflect_defect",
+        "pokemon_front_face_no_reflect_coaxial_light_defect": {
+            "model_type": "pokemon_front_face_no_reflect_coaxial_light_defect",
         },
-        "pokemon_front_face_no_reflect_defect": {
-            "model_type": "pokemon_front_face_no_reflect_defect",
-        },
-        # 2个环光的
+        # 环光
         "pokemon_back_face_ring_light_defect": {
             "model_type": "pokemon_back_face_ring_light_defect"
         },
         "pokemon_front_face_reflect_ring_light_defect": {
-            "model_type": "pokemon_front_face_reflect_ring_light_defect",
+            "model_type": "pokemon_front_face_reflect_ring_light_defect"
+        },
+        "pokemon_front_face_no_reflect_ring_light_defect": {
+            "model_type": "pokemon_front_face_no_reflect_ring_light_defect"
         }
     }
 
-    SCORE_TYPE: List[str] = ["front_corner_edge", "front_face", "front_face_ring_light",
-                             "back_corner_edge", "back_face", "back_face_ring_light"]
+    SCORE_TYPE: List[str] = ["front_coaxial", "front_ring",
+                             "back_coaxial", "back_ring"]
 
 
 settings = Settings()

+ 186 - 0
app/core/scoring_config.json

@@ -55,6 +55,97 @@
           "max": "inf",
           "deduction": -5
         }
+      ],
+      "pit_area": [
+        {
+          "min": 0,
+          "max": 0.05,
+          "deduction": -0.1
+        },
+        {
+          "min": 0.05,
+          "max": 0.1,
+          "deduction": -0.5
+        },
+        {
+          "min": 0.1,
+          "max": 0.25,
+          "deduction": -1.5
+        },
+        {
+          "min": 0.25,
+          "max": 0.5,
+          "deduction": -3
+        },
+        {
+          "min": 0.5,
+          "max": "inf",
+          "deduction": -5
+        }
+      ],
+      "stain_area": [
+        {
+          "min": 0,
+          "max": 0.05,
+          "deduction": -0.1
+        },
+        {
+          "min": 0.05,
+          "max": 0.1,
+          "deduction": -0.5
+        },
+        {
+          "min": 0.1,
+          "max": 0.25,
+          "deduction": -1.5
+        },
+        {
+          "min": 0.25,
+          "max": 0.5,
+          "deduction": -3
+        },
+        {
+          "min": 0.5,
+          "max": "inf",
+          "deduction": -5
+        }
+      ],
+      "scratch_length": [
+        {
+          "min": 0,
+          "max": 1,
+          "deduction": -0.1
+        },
+        {
+          "min": 1,
+          "max": 2,
+          "deduction": -0.5
+        },
+        {
+          "min": 2,
+          "max": 5,
+          "deduction": -1
+        },
+        {
+          "min": 5,
+          "max": 10,
+          "deduction": -2
+        },
+        {
+          "min": 10,
+          "max": 20,
+          "deduction": -3
+        },
+        {
+          "min": 20,
+          "max": 50,
+          "deduction": -4
+        },
+        {
+          "min": 50,
+          "max": "inf",
+          "deduction": -5
+        }
       ]
     },
     "front_weights": {
@@ -125,6 +216,97 @@
           "max": "inf",
           "deduction": -5
         }
+      ],
+      "pit_area": [
+        {
+          "min": 0,
+          "max": 0.05,
+          "deduction": -0.1
+        },
+        {
+          "min": 0.05,
+          "max": 0.1,
+          "deduction": -0.5
+        },
+        {
+          "min": 0.1,
+          "max": 0.25,
+          "deduction": -1.5
+        },
+        {
+          "min": 0.25,
+          "max": 0.5,
+          "deduction": -3
+        },
+        {
+          "min": 0.5,
+          "max": "inf",
+          "deduction": -5
+        }
+      ],
+      "stain_area": [
+        {
+          "min": 0,
+          "max": 0.05,
+          "deduction": -0.1
+        },
+        {
+          "min": 0.05,
+          "max": 0.1,
+          "deduction": -0.5
+        },
+        {
+          "min": 0.1,
+          "max": 0.25,
+          "deduction": -1.5
+        },
+        {
+          "min": 0.25,
+          "max": 0.5,
+          "deduction": -3
+        },
+        {
+          "min": 0.5,
+          "max": "inf",
+          "deduction": -5
+        }
+      ],
+      "scratch_length": [
+        {
+          "min": 0,
+          "max": 1,
+          "deduction": -0.1
+        },
+        {
+          "min": 1,
+          "max": 2,
+          "deduction": -0.5
+        },
+        {
+          "min": 2,
+          "max": 5,
+          "deduction": -1
+        },
+        {
+          "min": 5,
+          "max": 10,
+          "deduction": -2
+        },
+        {
+          "min": 10,
+          "max": 20,
+          "deduction": -3
+        },
+        {
+          "min": 20,
+          "max": 50,
+          "deduction": -4
+        },
+        {
+          "min": 50,
+          "max": "inf",
+          "deduction": -5
+        }
       ]
     },
     "front_weights": {
@@ -267,6 +449,10 @@
       "dent_area": 0.25,
       "stain_area": 0.25
     },
+    "light_weights": {
+      "ring_weight": 0.8,
+      "coaxial_weight": 0.2
+    },
     "final_weights": {
       "front": 0.75,
       "back": 0.25

+ 4 - 0
app/core/基础备份_scoring_config.json

@@ -267,6 +267,10 @@
       "dent_area": 0.25,
       "stain_area": 0.25
     },
+    "light_weights": {
+      "ring_weight": 0.8,
+      "coaxial_weight": 0.2
+    },
     "final_weights": {
       "front": 0.75,
       "back": 0.25

+ 0 - 440
app/core/新基础备份_scoring_config.json

@@ -1,440 +0,0 @@
-{
-  "base_score": 10,
-  "corner": {
-    "rules": {
-      "wear_area": [
-        {
-          "min": 0,
-          "max": 0.05,
-          "deduction": -0.1
-        },
-        {
-          "min": 0.05,
-          "max": 0.1,
-          "deduction": -0.5
-        },
-        {
-          "min": 0.1,
-          "max": 0.25,
-          "deduction": -1.5
-        },
-        {
-          "min": 0.25,
-          "max": 0.5,
-          "deduction": -3
-        },
-        {
-          "min": 0.5,
-          "max": "inf",
-          "deduction": -5
-        }
-      ],
-      "loss_area": [
-        {
-          "min": 0,
-          "max": 0.05,
-          "deduction": -0.1
-        },
-        {
-          "min": 0.05,
-          "max": 0.1,
-          "deduction": -0.5
-        },
-        {
-          "min": 0.1,
-          "max": 0.25,
-          "deduction": -1.5
-        },
-        {
-          "min": 0.25,
-          "max": 0.5,
-          "deduction": -3
-        },
-        {
-          "min": 0.5,
-          "max": "inf",
-          "deduction": -5
-        }
-      ]
-    },
-    "front_weights": {
-      "wear_area": 0.3,
-      "loss_area": 0.7
-    },
-    "back_weights": {
-      "wear_area": 0.3,
-      "loss_area": 0.7
-    },
-    "final_weights": {
-      "front": 0.7,
-      "back": 0.3
-    }
-  },
-  "edge": {
-    "rules": {
-      "wear_area": [
-        {
-          "min": 0,
-          "max": 0.05,
-          "deduction": -0.1
-        },
-        {
-          "min": 0.05,
-          "max": 0.1,
-          "deduction": -0.5
-        },
-        {
-          "min": 0.1,
-          "max": 0.25,
-          "deduction": -1.5
-        },
-        {
-          "min": 0.25,
-          "max": 0.5,
-          "deduction": -3
-        },
-        {
-          "min": 0.5,
-          "max": "inf",
-          "deduction": -5
-        }
-      ],
-      "loss_area": [
-        {
-          "min": 0,
-          "max": 0.05,
-          "deduction": -0.1
-        },
-        {
-          "min": 0.05,
-          "max": 0.1,
-          "deduction": -0.5
-        },
-        {
-          "min": 0.1,
-          "max": 0.25,
-          "deduction": -1.5
-        },
-        {
-          "min": 0.25,
-          "max": 0.5,
-          "deduction": -3
-        },
-        {
-          "min": 0.5,
-          "max": "inf",
-          "deduction": -5
-        }
-      ]
-    },
-    "front_weights": {
-      "wear_area": 0.4,
-      "loss_area": 0.6
-    },
-    "back_weights": {
-      "wear_area": 0.4,
-      "loss_area": 0.6
-    },
-    "final_weights": {
-      "front": 0.7,
-      "back": 0.3
-    }
-  },
-  "face": {
-    "rules": {
-      "wear_area": [
-        {
-          "min": 0,
-          "max": 0.05,
-          "deduction": -0.1
-        },
-        {
-          "min": 0.05,
-          "max": 0.1,
-          "deduction": -0.5
-        },
-        {
-          "min": 0.1,
-          "max": 0.25,
-          "deduction": -1.5
-        },
-        {
-          "min": 0.25,
-          "max": 0.5,
-          "deduction": -3
-        },
-        {
-          "min": 0.5,
-          "max": "inf",
-          "deduction": -5
-        }
-      ],
-      "pit_area": [
-        {
-          "min": 0,
-          "max": 0.05,
-          "deduction": -0.1
-        },
-        {
-          "min": 0.05,
-          "max": 0.1,
-          "deduction": -0.5
-        },
-        {
-          "min": 0.1,
-          "max": 0.25,
-          "deduction": -1.5
-        },
-        {
-          "min": 0.25,
-          "max": 0.5,
-          "deduction": -3
-        },
-        {
-          "min": 0.5,
-          "max": "inf",
-          "deduction": -5
-        }
-      ],
-      "stain_area": [
-        {
-          "min": 0,
-          "max": 0.05,
-          "deduction": -0.1
-        },
-        {
-          "min": 0.05,
-          "max": 0.1,
-          "deduction": -0.5
-        },
-        {
-          "min": 0.1,
-          "max": 0.25,
-          "deduction": -1.5
-        },
-        {
-          "min": 0.25,
-          "max": 0.5,
-          "deduction": -3
-        },
-        {
-          "min": 0.5,
-          "max": "inf",
-          "deduction": -5
-        }
-      ],
-      "scratch_length": [
-        {
-          "min": 0,
-          "max": 1,
-          "deduction": -0.1
-        },
-        {
-          "min": 1,
-          "max": 2,
-          "deduction": -0.5
-        },
-        {
-          "min": 2,
-          "max": 5,
-          "deduction": -1
-        },
-        {
-          "min": 5,
-          "max": 10,
-          "deduction": -2
-        },
-        {
-          "min": 10,
-          "max": 20,
-          "deduction": -3
-        },
-        {
-          "min": 20,
-          "max": 50,
-          "deduction": -4
-        },
-        {
-          "min": 50,
-          "max": "inf",
-          "deduction": -5
-        }
-      ]
-    },
-    "coefficients": {
-      "wear_area": 0.25,
-      "scratch_length": 0.25,
-      "dent_area": 0.25,
-      "stain_area": 0.25
-    },
-    "final_weights": {
-      "front": 0.75,
-      "back": 0.25
-    }
-  },
-  "centering": {
-    "front": {
-      "rules": [
-        {
-          "min": 0,
-          "max": 52,
-          "deduction": 0
-        },
-        {
-          "min": 52,
-          "max": 55,
-          "deduction": -0.5
-        },
-        {
-          "min": 55,
-          "max": 60,
-          "deduction": -1
-        },
-        {
-          "min": 60,
-          "max": 62.5,
-          "deduction": -1.5
-        },
-        {
-          "min": 62.5,
-          "max": 65,
-          "deduction": -2
-        },
-        {
-          "min": 65,
-          "max": 67.5,
-          "deduction": -2.5
-        },
-        {
-          "min": 67.5,
-          "max": 70,
-          "deduction": -3
-        },
-        {
-          "min": 70,
-          "max": 72.5,
-          "deduction": -3.5
-        },
-        {
-          "min": 72.5,
-          "max": 75,
-          "deduction": -4
-        },
-        {
-          "min": 75,
-          "max": 77.5,
-          "deduction": -4.5
-        },
-        {
-          "min": 77.5,
-          "max": 80,
-          "deduction": -5
-        },
-        {
-          "min": 80,
-          "max": 82.5,
-          "deduction": -5.5
-        },
-        {
-          "min": 82.5,
-          "max": 85,
-          "deduction": -6
-        },
-        {
-          "min": 85,
-          "max": 87.5,
-          "deduction": -6.5
-        },
-        {
-          "min": 87.5,
-          "max": 90,
-          "deduction": -7
-        },
-        {
-          "min": 90,
-          "max": 92.5,
-          "deduction": -7.5
-        },
-        {
-          "min": 92.5,
-          "max": 95,
-          "deduction": -8
-        },
-        {
-          "min": 95,
-          "max": 97.5,
-          "deduction": -8.5
-        },
-        {
-          "min": 97.5,
-          "max": "inf",
-          "deduction": -9
-        }
-      ],
-      "coefficients": {
-        "horizontal": 1.2,
-        "vertical": 0.9
-      }
-    },
-    "back": {
-      "rules": [
-        {
-          "min": 0,
-          "max": 60,
-          "deduction": -0.5
-        },
-        {
-          "min": 60,
-          "max": 70,
-          "deduction": -1
-        },
-        {
-          "min": 70,
-          "max": 75,
-          "deduction": -1.5
-        },
-        {
-          "min": 75,
-          "max": 85,
-          "deduction": -2
-        },
-        {
-          "min": 85,
-          "max": 95,
-          "deduction": -2.5
-        },
-        {
-          "min": 95,
-          "max": "inf",
-          "deduction": -3
-        }
-      ],
-      "coefficients": {
-        "horizontal": 1.2,
-        "vertical": 0.9
-      }
-    },
-    "final_weights": {
-      "front": 0.75,
-      "back": 0.25
-    }
-  },
-  "light_weight": {
-    "ring_weight": 0.5,
-    "coaxial_weight": 0.5
-  },
-  "card": {
-    "PSA": {
-      "face": 0.35,
-      "corner": 0.3,
-      "edge": 0.1,
-      "center": 0.25
-    },
-    "BGS": {
-      "face": 0.3,
-      "corner": 0.25,
-      "edge": 0.1,
-      "center": 0.25
-    }
-  }
-}

+ 25 - 21
app/services/defect_service.py

@@ -53,11 +53,9 @@ class DefectInferenceService:
         outside_filter = FilterOutsideDefects(expansion_pixel=30)
 
         # 面
-        if (inference_type == "pokemon_front_face_no_reflect_defect"
-                or inference_type == "pokemon_front_face_reflect_defect"
-                or inference_type == "pokemon_back_face_defect"
-                or inference_type == "pokemon_back_face_ring_light_defect"
-                or inference_type == "pokemon_front_face_reflect_ring_light_defect"):
+        if (inference_type == "pokemon_back_face_coaxial_light_defect"
+                or inference_type == "pokemon_front_face_reflect_coaxial_light_defect"
+                or inference_type == "pokemon_front_face_no_reflect_coaxial_light_defect"):
             # 1. 获取对应的预测器实例
             predictor = get_predictor(inference_type)
 
@@ -121,10 +119,10 @@ class DefectInferenceService:
 
             return face_json_result
 
-        # 边角
-        elif (inference_type == "pokemon_front_corner_no_reflect_defect"
-              or inference_type == "pokemon_front_corner_reflect_defect"
-              or inference_type == "pokemon_back_corner_defect"):
+        # 存在边角判断的情况
+        elif (inference_type == "pokemon_back_face_ring_light_defect"
+              or inference_type == "pokemon_front_face_reflect_ring_light_defect"
+              or inference_type == "pokemon_front_face_no_reflect_ring_light_defect"):
             predictor = get_predictor(inference_type)
 
             aggregator = CardDefectAggregator(
@@ -135,17 +133,19 @@ class DefectInferenceService:
 
             json_data = aggregator.process_image(
                 image=img_bgr,
-                mode='edge'
+                mode='face'
             )
 
             # 简化点数
+            logger.info("开始进行点数简化")
             for shapes in json_data["shapes"]:
                 points = shapes["points"]
                 num1 = len(points)
                 simplify_points = simplifyPoints.simplify_points(points)
                 shapes["points"] = simplify_points
                 new_num1 = len(simplify_points)
-                logger.info(f"num: {num1}, new_num1: {new_num1}")
+                # logger.info(f"num: {num1}, new_num1: {new_num1}")
+            logger.info("点数简化结束")
 
             # merge_json_path = settings.TEMP_WORK_DIR / f'{inference_type}-merge.json'
             # with open(merge_json_path, 'w', encoding='utf-8') as f:
@@ -175,7 +175,9 @@ class DefectInferenceService:
                 area_json: dict = processor.analyze_from_json(json_data)
             logger.info("边角缺陷面积计算结束")
 
-            classifier = ClassifyEdgeCorner(settings.PIXEL_RESOLUTION, settings.CORNER_SIZE_MM)
+            classifier = ClassifyEdgeCorner(settings.PIXEL_RESOLUTION,
+                                            settings.CORNER_SIZE_MM,
+                                            settings.EDGE_SIZE_MM)
             edge_corner_data = classifier.classify_defects_location(area_json, outer_result)
 
             edge_corner_data = formate_add_edit_type(edge_corner_data)
@@ -229,16 +231,16 @@ class DefectInferenceService:
             return {}
 
     # inference_type: center, face, corner_edge
-    def re_inference_from_json(self, inference_type: str, center_json: dict, defect_json: dict) -> 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}")
+    def re_inference_from_json(self, card_light_type: str, center_json: dict, defect_json: dict) -> dict:
+        light_type_list = ["center", "coaxial", "ring"]
+        if card_light_type not in light_type_list:
+            logger.error(f"inference_type 只能为{light_type_list}, 输入为{card_light_type}")
+            raise ValueError(f"inference_type 只能为{light_type_list}, 输入为{card_light_type}")
 
         processor = DefectProcessor(pixel_resolution=settings.PIXEL_RESOLUTION)
 
         # 对于面的图, 不计算居中相关, 这里得到的center_json 应该为 {}
-        if inference_type == "face":
+        if card_light_type == "coaxial":
             area_json = processor.re_analyze_from_json(defect_json)
             face_json_result = formate_face_data(area_json)
             logger.info("面缺陷面积计算结束")
@@ -248,7 +250,7 @@ class DefectInferenceService:
         inner_result = center_json['box_result']['inner_box']
         outer_result = center_json['box_result']['outer_box']
 
-        if inference_type == "center":
+        if card_light_type == "center":
             logger.info("居中重计算")
             inner_rect = inner_result['shapes'][0]['rect_box']
             outer_rect = outer_result['shapes'][0]['rect_box']
@@ -258,13 +260,15 @@ class DefectInferenceService:
                                                 inner_result, outer_result,
                                                 inner_rect_box, outer_rect_box)
             return center_result
-        elif inference_type == "corner_edge":
+        elif card_light_type == "ring":
 
             area_json: dict = processor.re_analyze_from_json(defect_json)
             logger.info("边角缺陷面积计算结束")
 
             # 根据外框区分边和角
-            classifier = ClassifyEdgeCorner(settings.PIXEL_RESOLUTION, settings.CORNER_SIZE_MM)
+            classifier = ClassifyEdgeCorner(settings.PIXEL_RESOLUTION,
+                                            settings.CORNER_SIZE_MM,
+                                            settings.EDGE_SIZE_MM)
             edge_corner_data = classifier.classify_defects_location(area_json, outer_result)
 
             logger.info("边角面积计算结束")

+ 59 - 52
app/services/score_service.py

@@ -27,73 +27,73 @@ class ScoreService:
         imageHeight, imageWidth = img_bgr.shape[:2]
 
         logger.info("开始进行卡片分数推理")
-        if score_type == 'front_corner_edge' or score_type == 'front_face' or score_type == "front_face_ring_light":
+        if score_type == 'front_ring' or score_type == 'front_coaxial':
             center_data = self.defect_service.defect_inference("pokemon_front_card_center", img_bgr.copy())
         else:
             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 = self.defect_service.defect_inference('pokemon_front_corner_reflect_defect',
-                                                                   img_bgr.copy())
-            elif score_type == 'front_face':
-                defect_data = self.defect_service.defect_inference('pokemon_front_face_reflect_defect', img_bgr.copy())
-            elif score_type == 'back_corner_edge':
-                defect_data = self.defect_service.defect_inference('pokemon_back_corner_defect', img_bgr.copy())
-            elif score_type == 'back_face':
-                defect_data = self.defect_service.defect_inference('pokemon_back_face_defect', img_bgr.copy())
-            elif score_type == 'front_face_ring_light':
+            if score_type == 'front_ring':
                 defect_data = self.defect_service.defect_inference('pokemon_front_face_reflect_ring_light_defect',
                                                                    img_bgr.copy())
-            elif score_type == 'back_face_ring_light':
+            elif score_type == 'front_coaxial':
+                defect_data = self.defect_service.defect_inference('pokemon_front_face_reflect_coaxial_light_defect',
+                                                                   img_bgr.copy())
+            elif score_type == 'back_ring':
                 defect_data = self.defect_service.defect_inference('pokemon_back_face_ring_light_defect',
                                                                    img_bgr.copy())
+            elif score_type == 'back_coaxial':
+                defect_data = self.defect_service.defect_inference('pokemon_back_face_coaxial_light_defect',
+                                                                   img_bgr.copy())
             else:
                 return {}
         else:
-            if score_type == 'front_corner_edge':
-                defect_data = self.defect_service.defect_inference('pokemon_front_corner_no_reflect_defect',
+            if score_type == 'front_ring':
+                defect_data = self.defect_service.defect_inference('pokemon_front_face_no_reflect_ring_light_defect',
                                                                    img_bgr.copy())
-            elif score_type == 'front_face':
-                defect_data = self.defect_service.defect_inference('pokemon_front_face_no_reflect_defect',
-                                                                   img_bgr.copy())
-            elif score_type == 'back_corner_edge':
-                defect_data = self.defect_service.defect_inference('pokemon_back_corner_defect', img_bgr.copy())
-            elif score_type == 'back_face':
-                defect_data = self.defect_service.defect_inference('pokemon_back_face_defect', img_bgr.copy())
-            elif score_type == 'front_face_ring_light':
-                defect_data = self.defect_service.defect_inference('pokemon_front_face_reflect_ring_light_defect',
+            elif score_type == 'front_coaxial':
+                defect_data = self.defect_service.defect_inference('pokemon_front_face_no_reflect_coaxial_light_defect',
                                                                    img_bgr.copy())
-            elif score_type == 'back_face_ring_light':
+            elif score_type == 'back_ring':
                 defect_data = self.defect_service.defect_inference('pokemon_back_face_ring_light_defect',
                                                                    img_bgr.copy())
+            elif score_type == 'back_coaxial':
+                defect_data = self.defect_service.defect_inference('pokemon_back_face_coaxial_light_defect',
+                                                                   img_bgr.copy())
             else:
                 return {}
 
         logger.info("模型推理结束, 开始计算分数")
-        if score_type == 'front_corner_edge' or score_type == 'front_face' or score_type=="front_face_ring_light":
+        if score_type == 'front_ring' or score_type == 'front_coaxial':
             card_aspect = "front"
         else:
             card_aspect = "back"
-        if score_type == 'front_corner_edge' or score_type == 'back_corner_edge':
-            card_defect_type = "corner_edge"
+        if score_type == 'front_ring' or score_type == 'back_ring':
+            card_light_type = "ring"
         else:
-            card_defect_type = "face"
+            card_light_type = "coaxial"
 
-        if card_defect_type == 'corner_edge':
+        if card_light_type == 'ring':
             center_score_data = self.card_scorer.calculate_centering_score(card_aspect, center_data, True)
 
-            # 先计算角的分数, 会把分数写入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':
+            # 计算角, 边, 面的分数, 会把分数写入json, 然后传入刚写好的, 继续写边的分数
+            defect_data = self.card_scorer.calculate_defect_score('corner', card_aspect, card_light_type,
+                                                                  defect_data, True)
+            defect_data = self.card_scorer.calculate_defect_score('edge', card_aspect, card_light_type,
+                                                                  defect_data, True)
+            defect_score_data = self.card_scorer.calculate_defect_score('face', card_aspect, card_light_type,
+                                                                        defect_data, True)
+        elif card_light_type == 'coaxial':
+            # 居中
             center_score_data = {}
-            defect_score_data = self.card_scorer.calculate_defect_score('face', card_aspect, defect_data, True)
+            # 同轴光照片只计算面缺陷
+            defect_score_data = self.card_scorer.calculate_defect_score('face', card_aspect, card_light_type,
+                                                                        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_light_type=card_light_type,
                                                                card_aspect=card_aspect,
                                                                imageHeight=imageHeight,
                                                                imageWidth=imageWidth)
@@ -111,44 +111,51 @@ class ScoreService:
         imageHeight = json_data["result"].get('imageHeight', 0)
         imageWidth = json_data["result"].get('imageWidth', 0)
 
-        # 边角和面 类型分类
-        if score_type == 'front_corner_edge' or score_type == 'back_corner_edge':
-            inference_type = "corner_edge"
-        else:
-            inference_type = "face"
-
         # 正反面类型分类
-        if score_type == 'front_corner_edge' or score_type == 'front_face':
+        if score_type == 'front_ring' or score_type == 'front_coaxial':
             card_aspect = "front"
         else:
             card_aspect = "back"
 
+        if score_type == 'front_ring' or score_type == 'back_ring':
+            card_light_type = "ring"
+        else:
+            card_light_type = "coaxial"
+
         logger.info("开始进行缺陷信息重计算")
-        defect_data = self.defect_service.re_inference_from_json(inference_type=inference_type,
+        defect_data = self.defect_service.re_inference_from_json(card_light_type=card_light_type,
                                                                  center_json=center_json_data,
                                                                  defect_json=defect_json_data)
 
         logger.info("开始重新计算分数")
-        if inference_type == 'corner_edge':
+        if card_light_type == 'ring':
             logger.info("开始进行居中信息重计算")
-            center_data = self.defect_service.re_inference_from_json(inference_type="center",
+            center_data = self.defect_service.re_inference_from_json(card_light_type="center",
                                                                      center_json=center_json_data,
                                                                      defect_json=defect_json_data)
 
             center_score_data = self.card_scorer.calculate_centering_score(card_aspect, center_data, True)
 
-            # 先计算角的分数, 会把分数写入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 inference_type == 'face':
-            # 面类型不计算居中
+            # 计算角, 边, 面的分数, 会把分数写入json, 然后传入刚写好的, 继续写边的分数
+            logger.info("开始重新计算:边角面")
+            defect_data = self.card_scorer.calculate_defect_score('corner', card_aspect, card_light_type,
+                                                                  defect_data, True)
+            defect_data = self.card_scorer.calculate_defect_score('edge', card_aspect, card_light_type,
+                                                                  defect_data, True)
+            defect_score_data = self.card_scorer.calculate_defect_score('face', card_aspect, card_light_type,
+                                                                        defect_data, True)
+        elif card_light_type == 'coaxial':
+            # 居中
             center_score_data = {}
-            defect_score_data = self.card_scorer.calculate_defect_score('face', card_aspect, defect_data, True)
+            # 同轴光照片只计算面缺陷
+            logger.info("开始重新计算:面")
+            defect_score_data = self.card_scorer.calculate_defect_score('face', card_aspect, card_light_type,
+                                                                        defect_data, True)
         else:
             return {}
 
         result_json = self.card_scorer.formate_one_card_result(center_score_data, defect_score_data,
-                                                               card_defect_type=inference_type,
+                                                               card_light_type=card_light_type,
                                                                card_aspect=card_aspect,
                                                                imageHeight=imageHeight,
                                                                imageWidth=imageWidth)

+ 3 - 1
app/utils/defect_inference/CardDefectAggregator.py

@@ -110,10 +110,12 @@ class CardDefectAggregator:
     # _run_prediction_on_tiles 方法保持不变
     def _run_prediction_on_tiles(self, image: np.ndarray, tiles: List[Dict]) -> List[Dict]:
         all_detections = []
+
+        log_print("信息", "开始进行分块预测")
         for i, tile_info in enumerate(tiles):
             x_origin, y_origin = tile_info['origin']
             tile_image = image[y_origin: y_origin + self.tile_size, x_origin: x_origin + self.tile_size]
-            log_print("信息", f"正在预测图块 {i + 1}/{len(tiles)} at ({x_origin}, {y_origin})...")
+            # log_print("信息", f"正在预测图块 {i + 1}/{len(tiles)} at ({x_origin}, {y_origin})...")
             result_dict = self.predictor.predict_from_image(tile_image)
             if not result_dict or not result_dict.get('shapes'):
                 continue

+ 90 - 54
app/utils/defect_inference/ClassifyEdgeCorner.py

@@ -3,17 +3,26 @@ import cv2
 import numpy as np
 import math
 import logging
+
 logger = logging.getLogger('ClassifyEdgeCorner')
 
 
 class ClassifyEdgeCorner:
-    def __init__(self, PIXEL_RESOLUTION_UM: float, PIXEL_CENTER_UM: float):
-        self.PIXEL_RESOLUTION_UM = PIXEL_RESOLUTION_UM  # 单位: μm/pixel
-        # 角区的定义尺寸
+    def __init__(self, PIXEL_RESOLUTION_UM: float, PIXEL_CENTER_UM: float, EDGE_SIZE_MM: float):
+        """
+        Args:
+            PIXEL_RESOLUTION_UM: 像素分辨率 (um/pixel)
+            PIXEL_CENTER_UM: 角区定义的尺寸 (mm) - 对应原本的 CORNER_SIZE_MM
+            EDGE_SIZE_MM: 边区向内缩进的尺寸 (mm)
+        """
+        self.PIXEL_RESOLUTION_UM = PIXEL_RESOLUTION_UM
         self.CORNER_SIZE_MM = PIXEL_CENTER_UM
+        self.EDGE_SIZE_MM = EDGE_SIZE_MM  # 新增:边的宽度
 
-    def get_outer_box_corners(self, outer_box_data):
-        """从外框数据中计算并返回精确的四个角点坐标。"""
+    def get_rotated_rect_data(self, outer_box_data):
+        """
+        从外框数据中提取最小外接矩形的原始数据 ((x,y), (w,h), angle)。
+        """
         if not outer_box_data or 'shapes' not in outer_box_data or not outer_box_data['shapes']:
             raise ValueError("无效的外框数据格式。")
 
@@ -22,118 +31,145 @@ class ClassifyEdgeCorner:
             logger.info("外框计算非第一次, 取 rect_box")
             get_point = outer_box_data['shapes'][0].get('rect_box', None)
             if get_point is None:
-                logger.error("外框数据缺失: rect_box")
                 raise ValueError("外框数据缺失: rect_box")
 
-        # points = outer_box_data['shapes'][0]['points']
         points = get_point
         contour = np.array(points, dtype=np.int32)
 
-        # 计算最小面积旋转矩形
-        rotated_rect = cv2.minAreaRect(contour)
-
-        # 获取矩形的4个角点
-        # boxPoints返回的角点顺序是可预测的,通常是顺时针或逆时针
-        box_corners = cv2.boxPoints(rotated_rect)
-        return box_corners
+        # 返回 ((cx, cy), (w, h), angle)
+        return cv2.minAreaRect(contour)
 
     def create_corner_regions(self, outer_box_corners, corner_size_px):
         """
         根据外框的角点和指定的尺寸(像素),创建四个角区的多边形。
-
-        Args:
-            outer_box_corners (np.array): 外框的4个角点坐标。
-            corner_size_px (float): 角区尺寸(像素单位)。
-
-        Returns:
-            list: 包含四个角区(每个角区都是一个4x2的np.array)的列表。
+        (逻辑保持不变)
         """
         corner_regions = []
         num_corners = len(outer_box_corners)
 
         for i in range(num_corners):
-            # 获取当前角点 p_i 和它的两个相邻角点 p_{i-1} 和 p_{i+1}
             p_current = outer_box_corners[i]
             p_prev = outer_box_corners[(i - 1 + num_corners) % num_corners]
             p_next = outer_box_corners[(i + 1) % num_corners]
 
-            # 计算从当前角点指向相邻角点的向量
             vec1 = p_prev - p_current
             vec2 = p_next - p_current
 
-            # 将向量归一化为单位向量
-            unit_vec1 = vec1 / np.linalg.norm(vec1)
-            unit_vec2 = vec2 / np.linalg.norm(vec2)
+            norm1 = np.linalg.norm(vec1)
+            norm2 = np.linalg.norm(vec2)
+
+            # 防止除以0的保护
+            if norm1 == 0 or norm2 == 0:
+                unit_vec1 = np.array([0, 0])
+                unit_vec2 = np.array([0, 0])
+            else:
+                unit_vec1 = vec1 / norm1
+                unit_vec2 = vec2 / norm2
 
-            # 计算角区正方形的四个顶点
-            # q0: 当前角点
-            # q1: 沿一个边移动指定距离
-            # q2: 沿另一个边移动指定距离
-            # q3: 两个向量相加得到的内部点
             q0 = p_current
             q1 = p_current + corner_size_px * unit_vec1
             q2 = p_current + corner_size_px * unit_vec2
             q3 = q1 + corner_size_px * unit_vec2
 
-            # 将角区作为一个多边形(轮廓)添加到列表中
             corner_poly = np.array([q0, q1, q3, q2], dtype=np.float32)
             corner_regions.append(corner_poly)
 
         return corner_regions
 
-    def classify_defects_location(self, defect_data, outer_box_data):
+    def create_inner_face_polygon(self, rect_data, edge_size_px):
         """
-        主函数,对缺陷数据进行分类并添加 "defect_type" 标签
+        根据外框旋转矩形和边宽,计算内缩后的“面”区域多边形
         """
-        if not defect_data or 'defects' not in defect_data:
-            logger.warn("警告: 缺陷数据为空或格式不正确,跳过处理。")
-            return defect_data
+        (cx, cy), (w, h), angle = rect_data
+
+        # 计算内框的宽和高 (两边各减去 edge_size)
+        inner_w = w - 2 * edge_size_px
+        inner_h = h - 2 * edge_size_px
+
+        # 如果边设置得太宽,导致内框消失,则返回 None 或极小的框
+        if inner_w <= 0 or inner_h <= 0:
+            logger.warning("警告: EDGE_SIZE_MM 设置过大,超过了卡牌尺寸,面(Face)区域将不存在。")
+            return None
+
+        # 生成内框的旋转矩形
+        inner_rect = ((cx, cy), (inner_w, inner_h), angle)
+
+        # 获取内框的四个角点
+        inner_box = cv2.boxPoints(inner_rect)
+        inner_box = np.int0(inner_box)  # 转换为整数点以便于 polygon test
+        return inner_box
 
-        if not defect_data['defects']:
-            logger.warn("没有缺陷数据")
+    def classify_defects_location(self, defect_data, outer_box_data):
+        """
+        主函数:分类为 corner, face, edge
+        """
+        if not defect_data or 'defects' not in defect_data or not defect_data['defects']:
+            # logger.warn("无缺陷数据")
             return defect_data
 
         # 1. 单位转换
         pixel_to_mm = self.PIXEL_RESOLUTION_UM / 1000.0
         corner_size_px = self.CORNER_SIZE_MM / pixel_to_mm
-        logger.info(f"3mm角区尺寸已转换为 {corner_size_px:.2f} 像素。")
+        edge_size_px = self.EDGE_SIZE_MM / pixel_to_mm
+
+        logger.info(
+            f"Param Info: Corner={self.CORNER_SIZE_MM}mm ({corner_size_px:.1f}px), Edge={self.EDGE_SIZE_MM}mm ({edge_size_px:.1f}px)")
 
         # 2. 获取外框几何信息
         try:
-            outer_box_corners = self.get_outer_box_corners(outer_box_data)
+            # 获取旋转矩形完整数据 ((x,y), (w,h), angle)
+            rect_data = self.get_rotated_rect_data(outer_box_data)
+            # 获取外框的四个角点 (用于角区计算)
+            outer_box_corners = cv2.boxPoints(rect_data)
         except ValueError as e:
-            logger.info(f"错误: {e}")
+            logger.error(f"外框数据错误: {e}")
             return None
 
-        # 3. 定义四个角区
+        # 3. 定义区域
+        # A. 定义四个角区
         corner_regions = self.create_corner_regions(outer_box_corners, corner_size_px)
-        logger.info(f"已成功定义 {len(corner_regions)} 个角区。")
+
+        # B. 定义中间的面区 (内缩矩形)
+        face_region = self.create_inner_face_polygon(rect_data, edge_size_px)
 
         # 4. 遍历并分类所有缺陷
         processed_count = 0
         for defect in defect_data['defects']:
-            # 使用JSON中预先计算好的min_rect中心点,更高效
             if 'min_rect' not in defect or not defect['min_rect']:
-                logger.warn(f"警告: 缺陷 '{defect.get('label')}' 缺少 'min_rect' 信息,跳过。")
                 continue
 
+            # 获取缺陷中心点
             center_point = tuple(defect['min_rect'][0])
-            is_in_corner = False
 
-            # 5. 判断缺陷中心点是否在任何一个角区内
+            # --- 判定逻辑优先级 ---
+
+            # 优先级 1: 角 (Corner)
+            is_corner = False
             for region in corner_regions:
-                # pointPolygonTest 返回值: >0 (内部), 0 (边上), <0 (外部)
+                # pointPolygonTest: >0 内部, =0 边界, <0 外部
                 if cv2.pointPolygonTest(region, center_point, False) >= 0:
-                    is_in_corner = True
+                    is_corner = True
                     break
 
-            # 6. 添加新标签
-            if is_in_corner:
+            if is_corner:
                 defect['defect_type'] = 'corner'
             else:
-                defect['defect_type'] = 'edge'
+                # 优先级 2: 面 (Face)
+                # 如果点在内缩矩形内部,且不是角,那就是面
+                is_face = False
+                if face_region is not None:
+                    if cv2.pointPolygonTest(face_region, center_point, False) >= 0:
+                        is_face = True
+
+                if is_face:
+                    defect['defect_type'] = 'face'
+                else:
+                    # 优先级 3: 边 (Edge)
+                    # 既不在角区,也不在面区(内框)里,但还在处理范围内,那就是边
+                    # (注:这里默认缺陷是在外框内的,如果是外框外的噪声,通常也会被算作 Edge 或需要额外过滤)
+                    defect['defect_type'] = 'edge'
 
             processed_count += 1
 
-        logger.info(f"处理完成!共为 {processed_count} 个缺陷添加了 'defect_type' 标签。")
+        logger.info(f"分类完成: {processed_count} 个缺陷处理完毕。")
         return defect_data

+ 22 - 5
app/utils/score_inference/CardScorer.py

@@ -53,6 +53,7 @@ class CardScorer:
     def calculate_defect_score(self,
                                card_defect_type: str,
                                card_aspect: str,
+                               card_light_type: str,
                                defect_data: Dict,
                                is_write_score: bool = True) -> Union[float, dict]:
         """
@@ -65,6 +66,8 @@ class CardScorer:
             raise TypeError("calculate_centering_score:card_type 只能为 'corner', 'edge', 'face'")
         if card_aspect != "front" and card_aspect != "back":
             raise TypeError("calculate_defect_score:card_type 只能为 front 或 back")
+        if card_light_type != "ring" and card_light_type != "coaxial":
+            raise TypeError("calculate_defect_score:card_type 只能为 ring 或 coaxial")
 
         aspect_config = self.config[card_defect_type]
         total_deduction = 0.0
@@ -80,11 +83,17 @@ class CardScorer:
                     defect_type = "wear_area"
                 elif defect['label'] in ['impact', 'damaged']:
                     defect_type = "loss_area"
+                elif defect['label'] in ['scratch', 'scuff']:
+                    defect_type = "scratch_length"
+                elif defect['label'] in ['pit', 'protrudent']:
+                    defect_type = "pit_area"
+                elif defect['label'] in ['stain']:
+                    defect_type = "stain_area"
                 else:
                     logger.error(f"数据缺陷类型不存在: {defect['label']}")
                     raise TypeError(f"数据缺陷类型不存在: {defect['label']}")
             else:
-                if defect['label'] in ['wear', 'wear_and_impact', 'damaged']:
+                if defect['label'] in ['wear', 'wear_and_impact', 'wear_and_stain', 'damaged']:
                     defect_type = "wear_area"
                 elif defect['label'] in ['scratch', 'scuff']:
                     defect_type = "scratch_length"
@@ -159,8 +168,14 @@ class CardScorer:
             total_deduction += score * weights.get(defect_type, 1.0)
 
         final_weights = aspect_config["final_weights"][card_aspect]
+
+        # 对于面的缺陷类型, 根据不同的光类型, 给予不同权重
+        if card_defect_type == "face":
+            light_weights = aspect_config["light_weights"][f"{card_light_type}_weight"]
+            final_weights = final_weights * light_weights
+
         final_score = total_deduction * final_weights
-        logger.info(f"final weights: {final_weights}, final score: {final_score}")
+        logger.info(f"final weights: {final_weights}, final score: {final_score}_weight")
 
         if is_write_score:
             defect_data[f"{card_aspect}_{card_defect_type}_deduct_score"] = final_score
@@ -210,7 +225,7 @@ class CardScorer:
 
     def formate_one_card_result(self, center_result: dict,
                                 defect_result: dict,
-                                card_defect_type: str,
+                                card_light_type: str,
                                 card_aspect: str,
                                 imageHeight: int,
                                 imageWidth: int):
@@ -222,18 +237,20 @@ class CardScorer:
             # 计算居中
             final_center_score = None
 
-            if card_defect_type == "corner_edge":
+            if card_light_type == "ring":
                 center_score = center_result['deduct_score']
                 center_weight = card_config['center']
                 final_center_score = center_score * center_weight
 
                 corner_score = defect_result[f"{card_aspect}_corner_deduct_score"]
                 edge_score = defect_result[f"{card_aspect}_edge_deduct_score"]
+                face_score = defect_result[f"{card_aspect}_face_deduct_score"]
 
                 corner_weight = card_config['corner']
                 edge_weight = card_config['edge']
+                face_weight = card_config['face']
 
-                final_defect_score = corner_score * corner_weight + edge_score * edge_weight
+                final_defect_score = corner_score * corner_weight + edge_score * edge_weight + face_score * face_weight
                 _used_compute_deduct_score = final_center_score + final_defect_score
                 card_score = self.base_score + final_center_score + final_defect_score
             else:

+ 3 - 2
run_defect_score_server.py

@@ -1,5 +1,6 @@
 import uvicorn
 
 if __name__ == "__main__":
-    print("http://127.0.0.1:7744/docs")
-    uvicorn.run("app.main:app", host="0.0.0.0", port=7744, reload=True)
+    port = 7754
+    print(f"http://127.0.0.1:{port}/docs")
+    uvicorn.run("app.main:app", host="0.0.0.0", port=port, reload=True)