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
| import numpy as np from typing import List, Tuple from dataclasses import dataclass
@dataclass class GazeFeature: """眼动特征数据结构""" gaze_x: float gaze_y: float pupil_diameter: float blink_rate: float perclos: float fixation_stability: float
@dataclass class FacialFeature: """面部行为特征数据结构""" au_intensity: np.ndarray head_pitch: float head_yaw: float head_roll: float mouth_openness: float eye_openness_l: float eye_openness_r: float
def compute_impairment_score( gaze_features: List[GazeFeature], facial_features: List[FacialFeature], window_sec: int = 30, fps: int = 30 ) -> float: """ 计算酒精损伤综合评分 基于 Smart Eye AIS 系统的检测原理复现: - 眼动稳定性分析 - 面部行为模式分析 - 头部姿态晃动分析 - PERCLOS 异常检测 Args: gaze_features: 眼动特征序列 facial_features: 面部特征序列 window_sec: 滑动窗口秒数 fps: 帧率 Returns: impairment_score: 损伤评分 (0-1), >0.7 判定为疑似损伤 """ window_size = int(window_sec * fps) if len(gaze_features) < window_size: return 0.0 scores = [] for i in range(len(gaze_features) - window_size): gaze_window = gaze_features[i:i+window_size] facial_window = facial_features[i:i+window_size] gaze_x = np.array([g.gaze_x for g in gaze_window]) gaze_y = np.array([g.gaze_y for g in gaze_window]) gaze_stability = 1.0 - min(np.std(gaze_x) + np.std(gaze_y), 1.0) perclos_vals = np.array([g.perclos for g in gaze_window]) avg_perclos = np.mean(perclos_vals) perclos_score = max(0, (avg_perclos - 0.15) / 0.35) blink_rates = np.array([g.blink_rate for g in gaze_window]) avg_blink = np.mean(blink_rates) blink_score = max(0, (avg_blink - 20) / 20) pitch = np.array([f.head_pitch for f in facial_window]) yaw = np.array([f.head_yaw for f in facial_window]) roll = np.array([f.head_roll for f in facial_window]) pitch_var = np.std(np.diff(pitch)) yaw_var = np.std(np.diff(yaw)) roll_var = np.std(np.diff(roll)) head_instability = min((pitch_var + yaw_var + roll_var) / 10, 1.0) au_intensities = np.array([f.au_intensity for f in facial_window]) avg_au_intensity = np.mean(au_intensities, axis=0) facial_relaxation = 1.0 - np.mean(avg_au_intensity) total_score = ( 0.25 * (1.0 - gaze_stability) + 0.25 * perclos_score + 0.15 * blink_score + 0.20 * head_instability + 0.15 * facial_relaxation ) scores.append(total_score) return float(np.mean(scores[-10:]))
if __name__ == "__main__": np.random.seed(42) normal_gaze = [GazeFeature( gaze_x=np.random.normal(0.5, 0.02), gaze_y=np.random.normal(0.5, 0.02), pupil_diameter=4.0, blink_rate=18, perclos=0.08, fixation_stability=0.95 ) for _ in range(900)] normal_facial = [FacialFeature( au_intensity=np.random.uniform(0.3, 0.7, 17), head_pitch=np.random.normal(0, 0.5), head_yaw=np.random.normal(0, 0.5), head_roll=np.random.normal(0, 0.3), mouth_openness=0.1, eye_openness_l=0.8, eye_openness_r=0.8 ) for _ in range(900)] drunk_gaze = [GazeFeature( gaze_x=np.random.normal(0.5, 0.08), gaze_y=np.random.normal(0.5, 0.08), pupil_diameter=4.5, blink_rate=32, perclos=0.28, fixation_stability=0.65 ) for _ in range(900)] drunk_facial = [FacialFeature( au_intensity=np.random.uniform(0.1, 0.3, 17), head_pitch=np.random.normal(0, 2.0), head_yaw=np.random.normal(0, 2.0), head_roll=np.random.normal(0, 1.5), mouth_openness=0.05, eye_openness_l=0.65, eye_openness_r=0.65 ) for _ in range(900)] normal_score = compute_impairment_score(normal_gaze, normal_facial) drunk_score = compute_impairment_score(drunk_gaze, drunk_facial) print(f"正常驾驶损伤评分: {normal_score:.3f} (阈值: 0.7)") print(f"酒精损伤评分: {drunk_score:.3f} (阈值: 0.7)") print(f"判定结果 - 正常: {'通过' if normal_score < 0.7 else '疑似损伤'}") print(f"判定结果 - 酒精: {'疑似损伤' if drunk_score >= 0.7 else '通过'}")
|