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
| import numpy as np import cv2 from typing import Dict, Tuple, Optional
class SunglassesRobustEyeTracker: """ 墨镜场景下的鲁棒眼动追踪 多层次检测策略: 1. 红外光穿透检测 2. 面部关键点推断 3. 头部姿态估计 """ def __init__(self, config: dict): self.ir_wavelength = config.get('ir_wavelength', 940) self.pupil_detector = PupilDetector() self.landmark_detector = FaceLandmarkDetector() self.head_pose_estimator = HeadPoseEstimator() self.sunglasses_detection_threshold = 0.3 self.ir_penetration_threshold = 50 def detect(self, ir_image: np.ndarray) -> Dict: """ 检测眼睛状态 Args: ir_image: 红外图像 Returns: result: 检测结果 """ landmarks = self.landmark_detector.detect(ir_image) is_sunglasses = self._detect_sunglasses(ir_image, landmarks) if is_sunglasses: pupil_result = self._try_ir_penetration(ir_image, landmarks) if pupil_result is not None: return { 'status': 'IR_PENETRATION_SUCCESS', 'pupil_position': pupil_result, 'confidence': 0.8 } else: estimated_gaze = self._estimate_gaze_from_landmarks(landmarks) return { 'status': 'SUNGLASSES_ESTIMATED', 'estimated_gaze': estimated_gaze, 'confidence': 0.5 } else: pupil_result = self.pupil_detector.detect(ir_image, landmarks) return { 'status': 'NORMAL', 'pupil_position': pupil_result, 'confidence': 0.9 } def _detect_sunglasses(self, image: np.ndarray, landmarks: np.ndarray) -> bool: """ 检测是否佩戴墨镜 方法: 1. 提取眼睛区域 2. 分析灰度分布 3. 墨镜区域通常灰度较低且均匀 """ left_eye_indices = list(range(36, 42)) right_eye_indices = list(range(42, 48)) left_eye_region = self._extract_region(image, landmarks[left_eye_indices]) right_eye_region = self._extract_region(image, landmarks[right_eye_indices]) left_std = np.std(left_eye_region) right_std = np.std(right_eye_region) avg_std = (left_std + right_std) / 2 return avg_std < self.sunglasses_detection_threshold * 255 def _try_ir_penetration(self, image: np.ndarray, landmarks: np.ndarray) -> Optional[Tuple]: """ 尝试红外穿透检测 方法: 1. 提取眼睛区域 2. 检查红外光透过情况 3. 尝试瞳孔检测 """ left_eye_region = self._extract_region(image, landmarks[36:42]) right_eye_region = self._extract_region(image, landmarks[42:48]) left_mean = np.mean(left_eye_region) right_mean = np.mean(right_eye_region) if left_mean > self.ir_penetration_threshold and \ right_mean > self.ir_penetration_threshold: try: left_pupil = self._detect_pupil_in_region(left_eye_region) right_pupil = self._detect_pupil_in_region(right_eye_region) if left_pupil is not None and right_pupil is not None: return ((left_pupil + right_pupil) / 2).astype(int) except: pass return None def _estimate_gaze_from_landmarks(self, landmarks: np.ndarray) -> Tuple[float, float]: """ 从面部关键点推断视线方向 方法: 1. 使用眼角位置推断视线范围 2. 结合头部姿态调整 """ left_outer = landmarks[36] left_inner = landmarks[39] right_inner = landmarks[42] right_outer = landmarks[45] left_center = (left_outer + left_inner) / 2 right_center = (right_inner + right_outer) / 2 left_direction = left_inner - left_outer right_direction = right_outer - right_inner avg_direction = (left_direction + right_direction) / 2 avg_direction = avg_direction / np.linalg.norm(avg_direction) head_pose = self.head_pose_estimator.estimate(landmarks) gaze_pitch = head_pose['pitch'] gaze_yaw = head_pose['yaw'] return (gaze_pitch, gaze_yaw) def _extract_region(self, image: np.ndarray, points: np.ndarray) -> np.ndarray: """提取区域""" x_min, y_min = points.min(axis=0).astype(int) x_max, y_max = points.max(axis=0).astype(int) margin = 5 x_min = max(0, x_min - margin) y_min = max(0, y_min - margin) x_max = min(image.shape[1], x_max + margin) y_max = min(image.shape[0], y_max + margin) return image[y_min:y_max, x_min:x_max] def _detect_pupil_in_region(self, region: np.ndarray) -> Optional[np.ndarray]: """在区域内检测瞳孔""" circles = cv2.HoughCircles( region, cv2.HOUGH_GRADIENT, dp=1, minDist=20, param1=50, param2=30, minRadius=3, maxRadius=15 ) if circles is not None: return circles[0][0][:2] return None
|