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
| """ 安全带检测与误用分类网络
目标: 从车内摄像头图像检测安全带状态 分类: 6类 (正常/未系/身后/腋下/未锁扣/儿童干扰)
模型: 轻量级目标检测 + 关键点估计 """
import torch import torch.nn as nn
class SeatbeltDetector(nn.Module): """ 安全带检测器 架构: 1. 骨干网络: MobileNetV3-Small (轻量) 2. 检测头: 安全带区域检测 3. 关键点头: 锚点+锁扣位置 4. 分类头: 6类状态分类 总参数: ~3M, INT8后 ~800KB """ def __init__(self, num_classes=6): super().__init__() self.backbone = MobileNetV3Small(in_channels=3) self.belt_det = nn.Sequential( nn.Conv2d(128, 64, 1), nn.ReLU(), nn.Conv2d(64, 2, 1) ) self.keypoints = nn.Sequential( nn.Conv2d(128, 64, 1), nn.ReLU(), nn.Conv2d(64, 6, 1) ) self.classifier = nn.Sequential( nn.AdaptiveAvgPool2d(1), nn.Flatten(), nn.Linear(128, 64), nn.ReLU(), nn.Dropout(0.2), nn.Linear(64, num_classes) ) def forward(self, x): features = self.backbone(x) belt_boxes = self.belt_det(features) belt_keypoints = self.keypoints(features) logits = self.classifier(features) return logits, belt_boxes, belt_keypoints
class MobileNetV3Small(nn.Module): """轻量骨干网络""" def __init__(self, in_channels=3): super().__init__() self.features = nn.Sequential( nn.Conv2d(in_channels, 16, 3, 2, 1, bias=False), nn.BatchNorm2d(16), nn.Hardswish(inplace=True), nn.Conv2d(16, 32, 3, 2, 1, groups=1, bias=False), nn.BatchNorm2d(32), nn.ReLU6(inplace=True), nn.Conv2d(32, 64, 3, 2, 1, groups=1, bias=False), nn.BatchNorm2d(64), nn.ReLU6(inplace=True), nn.Conv2d(64, 128, 3, 2, 1, bias=False), nn.BatchNorm2d(128), nn.Hardswish(inplace=True), ) def forward(self, x): return self.features(x)
class SeatbeltMisuseClassifier: """ 基于关键点几何关系判断安全带误用 关键点定义: - kp0: 肩带锚点 (B柱上方) - kp1: 锁扣位置 - kp2: 腰带位置 (骨盆附近) """ def classify(self, keypoints, person_pose): """ Args: keypoints: (3, 2) 三个关键点坐标 person_pose: dict with shoulders, hips, chest Returns: category: str, confidence: float """ anchor, buckle, lap = keypoints if self._belt_not_visible(keypoints): return 'unbelted', 0.95 if self._normal_pattern(keypoints, person_pose): return 'normal', 0.90 if self._behind_back_pattern(keypoints, person_pose): return 'behind_back', 0.75 if self._underarm_pattern(keypoints, person_pose): return 'underarm', 0.70 if self._unbuckled_pattern(keypoints): return 'unbuckled', 0.65 return 'uncertain', 0.3 def _belt_not_visible(self, kps): """安全带不可见""" return all(k[0] < 0 for k in kps) def _normal_pattern(self, kps, pose): """正常佩戴: 锚点→胸前→锁扣""" anchor, buckle, lap = kps chest = pose.get('chest', (0.5, 0.4)) return (anchor[1] < chest[1] < buckle[1] and abs(anchor[0] - buckle[0]) < 0.3) def _behind_back_pattern(self, kps, pose): """身后佩戴""" anchor, buckle, lap = kps return anchor[1] < buckle[1] and abs(anchor[0] - buckle[0]) > 0.4 def _underarm_pattern(self, kps, pose): """腋下佩戴""" anchor, buckle, lap = kps armpit = pose.get('right_armpit', (0.6, 0.5)) return abs(anchor[0] - armpit[0]) < 0.1 def _unbuckled_pattern(self, kps): """未锁扣""" anchor, buckle, lap = kps return buckle[0] < 0 or buckle[1] < 0
|