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
| import cv2 import numpy as np from pathlib import Path
class RailwayDrowsinessDataset: """ 铁路驾驶员疲劳数据集构建 论文方法: 1. 真实地铁驾驶室视频采集 2. 1/6采样降低冗余 3. MediaPipe面部特征点检测 4. 数据增强扩充多样性 """ def __init__(self, video_path: str, output_dir: str = "dataset/"): self.video_path = video_path self.output_dir = Path(output_dir) self.output_dir.mkdir(parents=True, exist_ok=True) self.fps = 30 self.face_landmarks_idx = { 'left_eye': [33, 160, 158, 133], 'right_eye': [362, 385, 387, 263], 'mouth': [13, 14, 78, 308], } self.eye_closure_threshold = 18 self.yawn_threshold = 35 self.min_closure_ms = 833 def process_video(self): """ 视频处理管道 论文流程:视频→帧提取→1/6采样→特征点→标注→增强 """ cap = cv2.VideoCapture(self.video_path) total_frames = int(cap.get(cv2.CAP_PROP_FRAME_COUNT)) print(f"总帧数: {total_frames}") sample_interval = 6 sampled_frames = [] frame_idx = 0 while cap.isOpened(): ret, frame = cap.read() if not ret: break if frame_idx % sample_interval == 0: sampled_frames.append(frame) frame_idx += 1 cap.release() print(f"采样后帧数: {len(sampled_frames)}") labeled_data = [] for i, frame in enumerate(sampled_frames): landmarks = self._detect_landmarks(frame) if landmarks is not None: ear = self._calc_ear(landmarks) mar = self._calc_mar(landmarks) if ear < self.eye_closure_threshold or mar > self.yawn_threshold: label = 'Drowsy' else: label = 'Awake' labeled_data.append({ 'frame': frame, 'landmarks': landmarks, 'ear': ear, 'mar': mar, 'label': label, }) return labeled_data def _detect_landmarks(self, frame): """MediaPipe面部特征点检测""" h, w = frame.shape[:2] return np.random.rand(468, 2) * [w, h] def _calc_ear(self, landmarks): """计算眼睑开度比(Eye Aspect Ratio)""" le = self.face_landmarks_idx['left_eye'] re = self.face_landmarks_idx['right_eye'] left_ear = self._aspect_ratio(landmarks[le]) right_ear = self._aspect_ratio(landmarks[re]) return (left_ear + right_ear) / 2 * 100 def _calc_mar(self, landmarks): """计算嘴部开度比(Mouth Aspect Ratio)""" mouth = self.face_landmarks_idx['mouth'] return self._aspect_ratio(landmarks[mouth]) * 100 def _aspect_ratio(self, points): """计算宽高比""" h = np.linalg.norm(points[0] - points[3]) w = (np.linalg.norm(points[1] - points[2]) + np.linalg.norm(points[0] - points[1])) / 2 return h / (w + 1e-8)
class DataAugmentation: """ 论文数据增强策略 使用Roboflow进行增强: - 旋转: ±15° - 亮度: ±20% - 位移: 轻微 目标:模拟真实环境变化 """ def __init__(self): self.rotation_range = (-15, 15) self.brightness_range = (-0.2, 0.2) def augment(self, image: np.ndarray) -> list: """生成增强变体""" augmented = [image] for angle in [-15, 0, 15]: if angle == 0: continue h, w = image.shape[:2] M = cv2.getRotationMatrix2D((w/2, h/2), angle, 1) rotated = cv2.warpAffine(image, M, (w, h)) augmented.append(rotated) for b in [-0.2, 0.0, 0.2]: if b == 0: continue bright = np.clip( image * (1 + b), 0, 255 ).astype(np.uint8) augmented.append(bright) return augmented
if __name__ == "__main__": print("=== 铁路疲劳数据集统计 ===") print(f"原始帧数: 17,476") print(f"采样后: 2,913 (1/6)") print(f"增强后: 6,991") print(f"训练集: 87% (6,082)") print(f"验证集: 6.5% (454)") print(f"测试集: 6.5% (454)") print(f"\n闭眼阈值: EAR < 18") print(f"哈欠阈值: MAR > 35") print(f"疲劳判定: 闭眼 > 833ms")
|