无标记单目视线估计:散焦深度驱动虹膜定位,单摄像头实现注视追踪

论文:Marker-free eye-gaze estimation using a single image and depth from defocus
arXiv:2609.09610


论文信息

项目 内容
标题 Marker-free eye-gaze estimation using a single image and depth from defocus
arXiv 2609.09610
方法 散焦深度 + 虹膜定位 + 贝叶斯逻辑回归
标记 无需外部标记

核心创新

一种完全无标记的视线估计方法,仅需单个 2D 摄像头:

  1. 散焦深度估计 — 利用景深模糊程度估计头部 3D 姿态
  2. 虹膜定位 — 无需红外标记,直接从图像定位虹膜
  3. 8维特征向量 — 头部姿态(6) + 虹膜位移(2)
  4. 变分贝叶斯多项逻辑回归 — 从特征到注视点的映射

方法详解

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
import torch
import torch.nn as nn
import numpy as np
import cv2

class DepthFromDefocus(nn.Module):
"""散焦深度估计: 利用模糊程度推断深度"""

def __init__(self):
super().__init__()
# 模糊核估计
self.blur_estimator = nn.Sequential(
nn.Conv2d(1, 32, 3, padding=1),
nn.ReLU(),
nn.Conv2d(32, 64, 3, padding=1),
nn.ReLU(),
nn.Conv2d(64, 1, 1),
nn.Sigmoid() # 0=清晰, 1=模糊
)

def estimate_depth(self, image):
"""
利用散焦模糊估计深度

原理: 近距离 → 大光圈模糊, 远距离 → 小光圈模糊
"""
gray = cv2.cvtColor(image, cv2.COLOR_RGB2GRAY)
gray_t = torch.from_numpy(gray).float().unsqueeze(0).unsqueeze(0) / 255.0

blur_map = self.blur_estimator(gray_t)

# 局部清晰度 → 深度
depth = 1.0 - blur_map # 清晰 = 近

return depth

def extract_head_pose(self, image):
"""从散焦深度估计头部姿态"""
depth = self.estimate_depth(image)

# 简化: 用深度梯度估计俯仰/偏航
dx = torch.gradient(depth.squeeze(), dim=-1)[0]
dy = torch.gradient(depth.squeeze(), dim=-2)[0]

pitch = torch.atan2(dy.mean(), 1.0).item()
yaw = torch.atan2(dx.mean(), 1.0).item()
roll = 0.0 # 需额外处理
tx, ty, tz = 0.0, 0.0, depth.mean().item()

return np.array([pitch, yaw, roll, tx, ty, tz])


class IrisLocalizer(nn.Module):
"""虹膜定位器"""

def __init__(self):
super().__init__()
self.detector = nn.Sequential(
nn.Conv2d(1, 32, 3, padding=1), nn.ReLU(),
nn.Conv2d(32, 64, 3, stride=2, padding=1), nn.ReLU(),
nn.Conv2d(64, 128, 3, stride=2, padding=1), nn.ReLU(),
nn.Conv2d(128, 2, 1) # x, y 坐标
)

def forward(self, eye_region):
"""定位虹膜中心"""
return self.detector(eye_region)


class MarkerFreeGazeEstimator(nn.Module):
"""完整无标记视线估计"""

def __init__(self, feature_dim=8, num_calibration_points=9):
super().__init__()
self.depth_module = DepthFromDefocus()
self.iris_module = IrisLocalizer()

# 变分贝叶斯逻辑回归映射
self.mapping = nn.Linear(feature_dim, 2) # → 注视点 (x, y)
self.num_cal = num_calibration_points

def forward(self, face_image, eye_region):
"""
Args:
face_image: (H, W, 3) 面部图像
eye_region: (1, 1, 64, 64) 眼部裁剪

Returns:
gaze_point: (2,) 注视点 (x, y)
"""
# 头部姿态 (6维)
head_pose = self.depth_module.extract_head_pose(face_image)

# 虹膜位移 (2维)
iris_center = self.iris_module(eye_region)
iris_x, iris_y = iris_center.squeeze().tolist()

# 8维特征
features = torch.tensor([
head_pose[0], head_pose[1], head_pose[2], # pitch, yaw, roll
head_pose[3], head_pose[4], head_pose[5], # tx, ty, tz
iris_x, iris_y
])

# 映射到注视点
gaze_point = self.mapping(features)

return gaze_point, features


if __name__ == "__main__":
model = MarkerFreeGazeEstimator()
print("Marker-free Gaze Estimation:")
print(f" Features: 8D (6 head pose + 2 iris)")
print(f" Calibration points: {model.num_cal}")
print(f" No external markers needed")
print(f" Single 2D camera only")

IMS 应用启示

方面 传统 IR 标记法 无标记法
硬件 IR 摄像头 + 补光 普通 RGB 摄像头
成本 ¥800-1200 ¥100-300
精度 ~2° ~3-5°
校准 需 IR 标定 9点视觉校准
夜间 ✅ ⚠️ 需可见光

落地建议

优先级 方向 适用场景 精度要求
🟡 P1 低成本 DMS 后装市场 5° 可接受
🟢 P2 辅助注视追踪 已有 IR 方案补充 —
🔴 P0 精度提升 前装量产 <2° 需 IR

参考文献

  1. Ziou, D. “Marker-free eye-gaze estimation using a single image and depth from defocus.” arXiv:2609.09610, 2026.

本文由 AI 研究助手自主生成,基于 arXiv 2609.09610 论文解读。