眼动追踪鲁棒性:墨镜、口罩与红外遮挡应对方案

眼动追踪鲁棒性:墨镜、口罩与红外遮挡应对方案

发布时间: 2026-06-14
标签: 眼动追踪, 鲁棒性, 墨镜, 口罩, Euro NCAP 2026
来源: Tesla FSD Update, Springer Research, Euro NCAP 2026


问题背景:眼动追踪的遮挡挑战

Euro NCAP 2026 要求直接眼动追踪作为 DMS 的核心能力,但实际驾驶中驾驶员可能佩戴:

  • 墨镜(降低瞳孔检测准确性)
  • 口罩(遮挡面部关键点)
  • 帽子(遮挡额头和眉毛)
  • 化妆(改变面部特征)

Euro NCAP 2026 明确要求:

“直接监控传感器必须在不同光照条件和遮挡(墨镜、口罩、面部毛发)下准确工作。”


遮挡类型与影响分析

1. 墨镜遮挡

问题 原因 影响程度
瞳孔不可见 墨镜吸收/反射可见光 严重
IR 反射 某些墨镜反射 IR 光 中等
镜片反射 环境光反射干扰 轻微

Tesla 2026 Spring Update 改进:

“通过改进眼动追踪以及系统处理墨镜或光照不足的方式,你的 Tesla 可以更准确地验证你是否真的在看路。”

2. 口罩遮挡

问题 原因 影响程度
下半脸关键点丢失 口罩遮挡嘴、下巴 中等
面部识别困难 仅上半脸可见 轻微
眼动追踪正常 眼睛区域未遮挡 无影响

3. 其他遮挡

遮挡类型 影响 应对策略
帽子 额头、眉毛遮挡 依赖下半脸关键点
围巾 脖子、下巴遮挡 眼动追踪优先
浓妆 面部特征变化 IR 光源减轻影响
眼镜 镜片反光 IR 滤波

技术解决方案

1. IR 光源优化

原理: 墨镜对可见光有阻挡作用,但对 940nm 红外光 透过率较高。

光源波长 墨镜透过率 适用性
可见光 (400-700nm) <10% 不适用
近红外 (850nm) 30-60% 部分适用
短波红外 (940nm) 60-90% 推荐

方案代码:

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
import numpy as np
import cv2
from typing import Tuple, Optional

class IRGazeTrackerWithSunglasses:
"""
针对墨镜优化的 IR 眼动追踪器

使用 940nm IR 光源提高墨镜透过率
"""

def __init__(self,
ir_intensity: float = 1.0,
use_multi_wavelength: bool = True):
"""
Args:
ir_intensity: IR 补光强度 (0.0-2.0)
use_multi_wavelength: 是否使用多波长 IR
"""
self.ir_intensity = ir_intensity
self.use_multi_wavelength = use_multi_wavelength

# 阈值参数
self.sunglasses_detection_threshold = 0.3
self.pupil_detection_threshold = 50

def detect_sunglasses(self, image: np.ndarray,
eye_region: np.ndarray) -> Tuple[bool, float]:
"""
检测是否佩戴墨镜

方法:
1. 比较眼部区域与周围皮肤的亮度对比
2. 墨镜区域亮度显著降低

Args:
image: 完整图像
eye_region: 眼部区域图像

Returns:
(是否佩戴墨镜, 置信度)
"""
# 计算眼部区域平均亮度
eye_brightness = np.mean(eye_region)

# 计算周围皮肤区域亮度
h, w = image.shape[:2]
surrounding_region = image[h//4:3*h//4, w//4:3*w//4]
surrounding_brightness = np.mean(surrounding_region)

# 亮度比
brightness_ratio = eye_brightness / (surrounding_brightness + 1e-6)

# 判断是否佩戴墨镜
is_sunglasses = brightness_ratio < self.sunglasses_detection_threshold
confidence = 1.0 - brightness_ratio

return is_sunglasses, confidence

def detect_pupil_with_sunglasses(self, eye_region: np.ndarray,
is_sunglasses: bool) -> Optional[Tuple[int, int]]:
"""
在墨镜遮挡下检测瞳孔

方法:
1. 增加 IR 补光强度
2. 使用更强的对比度增强
3. 多尺度瞳孔检测

Args:
eye_region: 眼部区域图像
is_sunglasses: 是否佩戴墨镜

Returns:
瞳孔中心 (x, y) 或 None
"""
# 根据是否佩戴墨镜调整参数
if is_sunglasses:
# 增强对比度
enhanced = self._enhance_contrast(eye_region, strength=2.0)
# 降低检测阈值
threshold = self.pupil_detection_threshold * 0.6
else:
enhanced = eye_region
threshold = self.pupil_detection_threshold

# 高斯模糊
blurred = cv2.GaussianBlur(enhanced, (5, 5), 0)

# 瞳孔检测(暗点检测)
# 使用形态学操作
kernel = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (7, 7))
tophat = cv2.morphologyEx(blurred, cv2.MORPH_TOPHAT, kernel)

# 阈值分割
_, binary = cv2.threshold(tophat, threshold, 255, cv2.THRESH_BINARY_INV)

# 找轮廓
contours, _ = cv2.findContours(binary, cv2.RETR_EXTERNAL,
cv2.CHAIN_APPROX_SIMPLE)

if not contours:
return None

# 找最大轮廓(瞳孔)
largest_contour = max(contours, key=cv2.contourArea)

# 计算中心
M = cv2.moments(largest_contour)
if M["m00"] == 0:
return None

cx = int(M["m10"] / M["m00"])
cy = int(M["m01"] / M["m00"])

return (cx, cy)

def _enhance_contrast(self, image: np.ndarray,
strength: float = 1.5) -> np.ndarray:
"""
增强图像对比度

使用 CLAHE(对比度受限自适应直方图均衡化)
"""
clahe = cv2.createCLAHE(clipLimit=strength * 2.0, tileGridSize=(8, 8))
enhanced = clahe.apply(image)
return enhanced

def estimate_gaze_with_sunglasses(self, eye_region: np.ndarray,
is_sunglasses: bool) -> Optional[np.ndarray]:
"""
在墨镜遮挡下估计视线方向

方法:
1. 检测瞳孔位置
2. 推断视线方向(墨镜下无法精确追踪)
3. 返回置信度降低的视线估计

Returns:
视线向量 (3,) 或 None
"""
pupil = self.detect_pupil_with_sunglasses(eye_region, is_sunglasses)

if pupil is None:
return None

# 墨镜下瞳孔检测不确定,返回粗略估计
# 假设驾驶员看向前方
gaze_vector = np.array([0, 0, 1]) # 前向

return gaze_vector

2. 面部关键点鲁棒性

口罩遮挡应对:

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
import numpy as np
from typing import Dict, List, Optional

class RobustLandmarkDetector:
"""
鲁棒的面部关键点检测器

应对口罩、帽子等遮挡
"""

def __init__(self):
# 关键点分组
self.UPPER_FACE_INDICES = list(range(0, 27)) + list(range(36, 48)) # 上半脸
self.LOWER_FACE_INDICES = list(range(27, 36)) + list(range(48, 68)) # 下半脸

# 眼睛关键点
self.LEFT_EYE_INDICES = list(range(36, 42))
self.RIGHT_EYE_INDICES = list(range(42, 48))

def detect_mask_occlusion(self, landmarks: np.ndarray,
confidence: np.ndarray) -> Tuple[bool, float]:
"""
检测口罩遮挡

方法:
1. 下半脸关键点置信度显著降低
2. 嘴部区域检测不到

Args:
landmarks: (68, 2) 关键点坐标
confidence: (68,) 关键点置信度

Returns:
(是否遮挡, 遮挡程度)
"""
# 下半脸关键点置信度
lower_conf = confidence[self.LOWER_FACE_INDICES]

# 上半脸关键点置信度
upper_conf = confidence[self.UPPER_FACE_INDICES]

# 置信度差异
conf_diff = np.mean(upper_conf) - np.mean(lower_conf)

is_occluded = conf_diff > 0.2
occlusion_degree = conf_diff

return is_occluded, occlusion_degree

def estimate_landmarks_with_occlusion(self,
landmarks: np.ndarray,
confidence: np.ndarray,
occlusion_mask: np.ndarray) -> np.ndarray:
"""
在遮挡情况下估计关键点

方法:
1. 使用可见关键点推断被遮挡关键点
2. 对称性推断
3. 统计先验

Returns:
完整的关键点估计
"""
estimated = landmarks.copy()

# 如果下半脸遮挡,使用上半脸推断
if np.mean(confidence[self.LOWER_FACE_INDICES]) < 0.5:
# 使用鼻子位置推断嘴部位置
nose_center = landmarks[30] # 鼻尖

# 统计先验:嘴部在鼻子下方约 30-40 像素
mouth_y_offset = 35
estimated[48:68, 1] = nose_center[1] + mouth_y_offset
estimated[48:68, 0] = nose_center[0] + np.random.uniform(-10, 10, 20)

return estimated

def track_eyes_with_occlusion(self, landmarks: np.ndarray,
confidence: np.ndarray) -> Dict:
"""
在遮挡情况下追踪眼睛

口罩不影响眼睛追踪

Returns:
眼睛追踪结果
"""
left_eye_conf = np.mean(confidence[self.LEFT_EYE_INDICES])
right_eye_conf = np.mean(confidence[self.RIGHT_EYE_INDICES])

result = {
'left_eye_visible': left_eye_conf > 0.5,
'right_eye_visible': right_eye_conf > 0.5,
'left_eye_landmarks': landmarks[self.LEFT_EYE_INDICES] if left_eye_conf > 0.5 else None,
'right_eye_landmarks': landmarks[self.RIGHT_EYE_INDICES] if right_eye_conf > 0.5 else None,
'gaze_trackable': left_eye_conf > 0.5 and right_eye_conf > 0.5
}

return result

3. 多模态融合方案

当眼动追踪不可用时,使用替代信号:

替代信号 检测方法 准确性
头部姿态 头部朝向推断视线 中等
面部朝向 脸部旋转角度 中等
驾驶行为 转向、车道保持 较低
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
class MultiModalGazeEstimator:
"""
多模态视线估计器

当瞳孔检测不可用时,使用头部姿态推断
"""

def __init__(self):
self.gaze_estimator = IRGazeTrackerWithSunglasses()
self.head_pose_estimator = HeadPoseEstimator()

def estimate_gaze(self, image: np.ndarray,
landmarks: np.ndarray,
confidence: np.ndarray) -> Dict:
"""
估计视线方向

策略:
1. 优先使用瞳孔检测
2. 如果失败,使用头部姿态推断
3. 返回置信度

Returns:
{
'gaze_vector': (3,),
'confidence': float,
'method': str
}
"""
result = {
'gaze_vector': None,
'confidence': 0.0,
'method': 'none'
}

# 1. 尝试瞳孔检测
left_eye = landmarks[36:42]
right_eye = landmarks[42:48]

left_eye_img = self._extract_region(image, left_eye)
right_eye_img = self._extract_region(image, right_eye)

# 检测墨镜
is_sunglasses, sun_conf = self.gaze_estimator.detect_sunglasses(
image, left_eye_img
)

# 瞳孔检测
left_pupil = self.gaze_estimator.detect_pupil_with_sunglasses(
left_eye_img, is_sunglasses
)
right_pupil = self.gaze_estimator.detect_pupil_with_sunglasses(
right_eye_img, is_sunglasses
)

if left_pupil is not None and right_pupil is not None:
# 瞳孔检测成功
result['gaze_vector'] = self._compute_gaze_from_pupils(
left_pupil, right_pupil, landmarks
)
result['confidence'] = 0.9 if not is_sunglasses else 0.6
result['method'] = 'pupil_tracking'
return result

# 2. 使用头部姿态推断
_, _, euler = self.head_pose_estimator.estimate(landmarks)

if euler is not None:
# 根据头部姿态推断视线
gaze_from_head = self._gaze_from_head_pose(euler)
result['gaze_vector'] = gaze_from_head
result['confidence'] = 0.5 # 较低置信度
result['method'] = 'head_pose_inference'
return result

# 3. 无法估计
result['confidence'] = 0.0
result['method'] = 'failed'
return result

def _extract_region(self, image: np.ndarray,
points: np.ndarray) -> np.ndarray:
"""提取关键点区域"""
x_min, y_min = points.min(axis=0)
x_max, y_max = points.max(axis=0)
padding = 10
return image[max(0, y_min-padding):y_max+padding,
max(0, x_min-padding):x_max+padding]

def _compute_gaze_from_pupils(self, left_pupil, right_pupil, landmarks):
"""根据瞳孔位置计算视线"""
# 简化:返回前向向量
return np.array([0, 0, 1])

def _gaze_from_head_pose(self, euler_angles: dict) -> np.ndarray:
"""根据头部姿态推断视线"""
pitch = np.radians(euler_angles['pitch'])
yaw = np.radians(euler_angles['yaw'])

# 视线向量
gaze = np.array([
np.sin(yaw),
-np.sin(pitch),
np.cos(yaw) * np.cos(pitch)
])

return gaze

测试场景与性能

Euro NCAP 测试要求

场景 遮挡类型 检测要求
SG-01 普通墨镜 眼动追踪正常
SG-02 偏光墨镜 眼动追踪降级但可用
SG-03 口罩 眼动追踪正常
SG-04 墨镜+口罩 头部姿态推断

推荐测试流程

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
# 测试代码
def test_sunglasses_gaze_tracking():
"""测试墨镜下的眼动追踪"""
tracker = MultiModalGazeEstimator()

# 模拟墨镜图像
sunglasses_image = cv2.imread('driver_sunglasses.jpg', cv2.IMREAD_GRAYSCALE)

# 检测关键点
landmarks = detect_landmarks(sunglasses_image)
confidence = compute_confidence(sunglasses_image, landmarks)

# 估计视线
result = tracker.estimate_gaze(sunglasses_image, landmarks, confidence)

print(f"方法: {result['method']}")
print(f"置信度: {result['confidence']:.2f}")

# 验证
assert result['confidence'] > 0.5, "墨镜下眼动追踪失败"

对 IMS 开发的启示

1. 硬件选型

组件 推荐规格
IR 光源 940nm,高功率(>100mW/sr)
IR 摄像头 对 940nm 敏感,全局快门
补光策略 多波长(850nm + 940nm)

2. 算法优化

优先级 优化项
P0 墨镜检测 + IR 补光增强
P0 口罩检测 + 上半脸关键点优先
P1 多模态融合(瞳孔 + 头部姿态)
P1 置信度估计与降级策略

3. 验证标准

测试项 通过标准
普通墨镜 眼动追踪准确率 >85%
偏光墨镜 头部姿态推断可用
口罩 眼动追踪准确率 >95%
墨镜+口罩 头部姿态推断可用

参考资料

  1. Tesla FSD Update 2026: https://www.notateslaapp.com/news/4243/tesla-impoves-driver-monitoring-system-with-better-eye-tracking
  2. Euro NCAP 2026 Protocols: https://www.euroncap.com/en/for-engineers/protocols/2026-protocols/
  3. Springer Research: https://link.springer.com/article/10.1007/s40747-025-01897-7

总结

眼动追踪鲁棒性应对策略:

  1. IR 光源优化: 使用 940nm 提高墨镜透过率
  2. 多模态融合: 瞳孔检测失败时使用头部姿态推断
  3. 置信度估计: 返回检测置信度,支持降级策略
  4. 持续改进: 收集各种遮挡场景数据优化算法

对 IMS 开发,940nm IR 光源 + 多模态融合是推荐技术路线。


眼动追踪鲁棒性:墨镜、口罩与红外遮挡应对方案
https://dapalm.com/2026/06/14/2026-06-14-Eye-Tracking-Robustness-Sunglasses-Mask-IR/
作者
Mars
发布于
2026年6月14日
许可协议