1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238
| from typing import List, Tuple, Dict import numpy as np
class SeatbeltShapeModel: """ 安全带形状建模 论文方法:使用几何形状约束判断正确佩戴 vs 误用 误用类型识别: 1. 未佩戴(无安全带) 2. 插卡但未佩戴(坐在已扣安全带前) 3. 背后佩戴(安全带绕到背后) 4. 手臂下佩戴(安全带在手臂下方) """ def __init__(self): self.correct_template = { "start_region": "upper_right", "end_region": "lower_left", "min_segments": 5, "slope_range": (0.5, 3.0), "curvature_max": 0.5 } def classify_usage(self, belt_path: List[Tuple], body_keypoints: Dict[str, Tuple], buckle_detected: bool) -> Dict: """ 分类安全带使用状态 Args: belt_path: 安全带路径 body_keypoints: 人体关键点 { "shoulder_left": (x, y), "shoulder_right": (x, y), "elbow_left": (x, y), "elbow_right": (x, y), "hip_left": (x, y), "hip_right": (x, y) } buckle_detected: 是否检测到扣锁 Returns: dict: { "usage_type": str, # "correct", "no_belt", "behind", "under_arm", "buckled_only" "confidence": float, "reason": str } """ if not belt_path: if buckle_detected: return { "usage_type": "buckled_only", "confidence": 0.85, "reason": "检测到扣锁但无安全带路径,可能坐在已扣安全带前" } else: return { "usage_type": "no_belt", "confidence": 0.90, "reason": "未检测到安全带和扣锁" } if len(belt_path) < self.correct_template["min_segments"]: return { "usage_type": "no_belt", "confidence": 0.70, "reason": "安全带片段过少,可能误检" } geometry_check = self._check_geometry_constraints(belt_path) if not geometry_check["valid"]: return { "usage_type": "behind", "confidence": 0.75, "reason": geometry_check["reason"] } arm_check = self._check_arm_position(belt_path, body_keypoints) if arm_check["under_arm"]: return { "usage_type": "under_arm", "confidence": 0.80, "reason": "安全带路径在手臂下方,为误用" } if not buckle_detected: return { "usage_type": "no_belt", "confidence": 0.65, "reason": "未检测到扣锁,安全带可能未扣" } return { "usage_type": "correct", "confidence": 0.85, "reason": "几何合理,扣锁正常,正确佩戴" } def _check_geometry_constraints(self, belt_path: List[Tuple]) -> Dict: """检查几何约束""" if len(belt_path) < 2: return {"valid": False, "reason": "路径过短"} start = belt_path[0] end = belt_path[-1] if start[1] <= end[1]: return {"valid": False, "reason": "起点应在右侧"} if start[0] >= end[0]: return {"valid": False, "reason": "终点应在下方"} delta_row = end[0] - start[0] delta_col = start[1] - end[1] slope = delta_row / delta_col if slope < self.correct_template["slope_range"][0]: return {"valid": False, "reason": "斜率过小(过于横向)"} if slope > self.correct_template["slope_range"][1]: return {"valid": False, "reason": "斜率过大(过于垂直)"} if len(belt_path) >= 3: curvature = self._compute_curvature(belt_path) if curvature > self.correct_template["curvature_max"]: return {"valid": False, "reason": "路径过于弯曲"} return {"valid": True, "reason": "几何约束满足"} def _compute_curvature(self, path: List[Tuple]) -> float: """计算路径曲率""" if len(path) < 3: return 0.0 total_curvature = 0.0 for i in range(len(path) - 2): p1 = path[i] p2 = path[i+1] p3 = path[i+2] v1 = np.array([p2[0] - p1[0], p2[1] - p1[1]]) v2 = np.array([p3[0] - p2[0], p3[1] - p2[1]]) if np.linalg.norm(v1) > 0 and np.linalg.norm(v2) > 0: cos_angle = np.dot(v1, v2) / (np.linalg.norm(v1) * np.linalg.norm(v2)) cos_angle = np.clip(cos_angle, -1, 1) angle = np.arccos(cos_angle) total_curvature += angle return total_curvature / (len(path) - 2) def _check_arm_position(self, belt_path: List[Tuple], body_keypoints: Dict) -> Dict: """检查安全带是否在手臂下方""" if not body_keypoints: return {"under_arm": False, "reason": "无人体关键点"} shoulder_right = body_keypoints.get("shoulder_right") elbow_right = body_keypoints.get("elbow_right") if not shoulder_right or not elbow_right: return {"under_arm": False, "reason": "缺少关键点"} upper_belt = belt_path[:len(belt_path)//3] for point in upper_belt: if point[0] > elbow_right[0]: return {"under_arm": True, "reason": "安全带在肘部下方"} return {"under_arm": False, "reason": "安全带位置正常"}
if __main__ == "__main__": shape_model = SeatbeltShapeModel() correct_path = [(1, 6), (2, 5), (3, 5), (4, 4), (5, 3), (6, 2)] body_keypoints = { "shoulder_right": (1, 6), "shoulder_left": (1, 2), "elbow_right": (2, 5), "elbow_left": (2, 2), "hip_right": (6, 3), "hip_left": (6, 1) } result_correct = shape_model.classify_usage( correct_path, body_keypoints, buckle_detected=True ) print("=== 正确佩戴 ===") print(f"使用类型: {result_correct['usage_type']}") print(f"置信度: {result_correct['confidence']:.2f}") print(f"原因: {result_correct['reason']}") under_arm_path = [(2, 5), (3, 5), (4, 4)] result_under_arm = shape_model.classify_usage( under_arm_path, body_keypoints, buckle_detected=True ) print("\n=== 手臂下佩戴 ===") print(f"使用类型: {result_under_arm['usage_type']}") print(f"置信度: {result_under_arm['confidence']:.2f}") print(f"原因: {result_under_arm['reason']}") result_no_belt = shape_model.classify_usage( [], body_keypoints, buckle_detected=False ) print("\n=== 未佩戴 ===") print(f"使用类型: {result_no_belt['usage_type']}") print(f"置信度: {result_no_belt['confidence']:.2f}") print(f"原因: {result_no_belt['reason']}")
|