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
| import numpy as np from typing import Tuple, List from dataclasses import dataclass
@dataclass class BlinkEvent: """眨眼事件""" start_frame: int end_frame: int duration_ms: float max_closure: float
class HighFrameRateBlinkDetector: """高帧率眨眼检测器""" def __init__(self, fps: int = 100): self.fps = fps self.min_blink_frames = 3 self.max_blink_frames = 40 self.closure_threshold = 0.3 def detect_blinks(self, ear_sequence: np.ndarray, timestamps: np.ndarray) -> List[BlinkEvent]: """ 检测眨眼事件 Args: ear_sequence: (N,) EAR序列 timestamps: (N,) 时间戳(秒) Returns: blinks: 眨眼事件列表 """ blinks = [] is_closed = ear_sequence < self.closure_threshold closed_regions = self._find_closed_regions(is_closed) for start, end in closed_regions: duration_frames = end - start if duration_frames < self.min_blink_frames: continue if duration_frames > self.max_blink_frames: continue duration_ms = duration_frames / self.fps * 1000.0 max_closure = 1.0 - np.min(ear_sequence[start:end]) blinks.append(BlinkEvent( start_frame=start, end_frame=end, duration_ms=duration_ms, max_closure=max_closure )) return blinks def _find_closed_regions(self, is_closed: np.ndarray) -> List[Tuple[int, int]]: """查找闭合区间""" regions = [] in_blink = False start = 0 for i, closed in enumerate(is_closed): if closed and not in_blink: in_blink = True start = i elif not closed and in_blink: in_blink = False regions.append((start, i)) if in_blink: regions.append((start, len(is_closed))) return regions def compute_blink_statistics(self, blinks: List[BlinkEvent], window_sec: float = 60.0) -> Dict: """ 计算眨眼统计 Args: blinks: 眨眼事件列表 window_sec: 统计窗口(秒) Returns: stats: 统计结果 """ if len(blinks) == 0: return { 'blink_rate': 0.0, 'avg_duration': 0.0, 'max_duration': 0.0, 'perclos': 0.0 } blink_rate = len(blinks) / window_sec * 60.0 durations = [b.duration_ms for b in blinks] avg_duration = np.mean(durations) max_duration = np.max(durations) total_closed_ms = sum(durations) window_ms = window_sec * 1000.0 perclos = total_closed_ms / window_ms * 100.0 return { 'blink_rate': blink_rate, 'avg_duration': avg_duration, 'max_duration': max_duration, 'perclos': perclos }
if __name__ == "__main__": detector = HighFrameRateBlinkDetector(fps=100) N = 1000 ear = np.ones(N) * 0.35 for i in range(5): start = 100 + i * 150 ear[start:start+5] = 0.15 timestamps = np.arange(N) / 100.0 blinks = detector.detect_blinks(ear, timestamps) print(f"检测到 {len(blinks)} 次眨眼") for i, blink in enumerate(blinks): print(f" 眨眼{i+1}: 时长={blink.duration_ms:.0f}ms") stats = detector.compute_blink_statistics(blinks, window_sec=10.0) print(f"\n统计结果:") print(f" 眨眼频率: {stats['blink_rate']:.1f} 次/分钟") print(f" 平均时长: {stats['avg_duration']:.0f}ms") print(f" PERCLOS: {stats['perclos']:.1f}%")
|