TransGaze-Object:Transformer交叉注意力直接预测驾驶员注视物体——跳过PoG中间步骤的范式转变

论文信息

项目 内容
标题 TransGaze-Object: Transformer Based Driver Gaze Object Prediction Framework in Real Driving
作者 Pavan Kumar Sharma, Ayush Pande, Pranamesh Chakraborty
arXiv 2609.10139
提交 2026-09-09
页数 32页, 17张图
领域 cs.CV
数据集 UD-FSG (Urban Driving-Face Scene Gaze)

核心创新

范式转变:从”先估点再关联”到”端到端直接预测物体”

1
2
3
4
传统管线: 驾驶员面部 → [ gaze estimation ] → PoG (x,y) → [ object association ] → 注视物体
↑ 误差累积 ↑ ↑ 关联失败 ↑

TransGaze-Object: 驾驶员面部 + 交通场景 → [ Transformer交叉注意力 ] → 注视物体(直接)
方法 步骤数 误差来源 准确率
传统两步法 2步 gaze estimation + object association 51%
TransGaze-Object 1步 端到端学习 60%
相对提升 - - +9pp (+17.6%)

关键设计

组件 作用 输入 输出
面部特征提取 人脸+虹膜特征 驾驶员面部图像 面部embedding
交通物体提取 场景物体空间特征 交通场景+YOLOv8检测 物体bbox embeddings
Transformer交叉注意力 计算面部-物体相似度 面部+物体embeddings 注意力权重+物体分类

方法详解

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
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
import torch
import torch.nn as nn
import torch.nn.functional as F

class FaceFeatureExtractor(nn.Module):
"""
驾驶员面部特征提取

双流特征:
1. 全脸特征: 整体面部姿态/表情
2. 虹膜加权眼部特征: 精确注视方向

论文Section 3.1: Facial Feature Extraction
"""

def __init__(self, embed_dim: int = 256):
super().__init__()
# 全脸特征分支
self.face_cnn = nn.Sequential(
nn.Conv2d(3, 32, 3, stride=2, 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.AdaptiveAvgPool2d((4, 4)),
nn.Flatten(),
nn.Linear(128 * 16, embed_dim)
)
# 眼部特征分支(虹膜加权)
self.eye_cnn = nn.Sequential(
nn.Conv2d(3, 32, 3, stride=2, 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.AdaptiveAvgPool2d((2, 4)),
nn.Flatten(),
nn.Linear(128 * 8, embed_dim)
)
# 虹膜mask权重(注意力机制)
self.iris_gate = nn.Sequential(
nn.Linear(embed_dim * 2, embed_dim),
nn.Sigmoid()
)
# 融合
self.fusion = nn.Linear(embed_dim * 2, embed_dim)

def forward(self, face_img: torch.Tensor,
eye_patch: torch.Tensor) -> torch.Tensor:
"""
Args:
face_img: (B, 3, 224, 224) 全脸图像
eye_patch: (B, 3, 112, 112) 眼部裁剪

Returns:
face_feat: (B, embed_dim) 面部特征向量
"""
face_feat = self.face_cnn(face_img)
eye_feat = self.eye_cnn(eye_patch)

# 虹膜门控加权
gate = self.iris_gate(torch.cat([face_feat, eye_feat], dim=-1))
weighted_eye = eye_feat * gate

# 融合
fused = self.fusion(torch.cat([face_feat, weighted_eye], dim=-1))
return fused


class TrafficObjectEncoder(nn.Module):
"""
交通场景物体编码器

使用YOLOv8检测物体 → 空间特征编码

论文Section 3.2: Traffic Object Spatial Features
"""

def __init__(self, embed_dim: int = 256, num_objects: int = 10):
super().__init__()
self.num_objects = num_objects

# 物体类别嵌入
self.class_embed = nn.Embedding(20, embed_dim) # 20类交通物体

# 空间特征编码 (bbox: x,y,w,h + confidence)
self.spatial_encoder = nn.Sequential(
nn.Linear(5, 64), nn.ReLU(),
nn.Linear(64, embed_dim)
)

# 物体外观特征(从场景图中裁剪)
self.appearance_cnn = nn.Sequential(
nn.Conv2d(3, 32, 3, stride=2, padding=1), nn.ReLU(),
nn.Conv2d(32, 64, 3, stride=2, padding=1), nn.ReLU(),
nn.AdaptiveAvgPool2d((2, 2)),
nn.Flatten(),
nn.Linear(64 * 4, embed_dim)
)

# 融合
self.fusion = nn.Linear(embed_dim * 3, embed_dim)

def forward(self, bboxes: torch.Tensor, classes: torch.Tensor,
crops: torch.Tensor) -> torch.Tensor:
"""
Args:
bboxes: (B, N, 5) [x, y, w, h, conf]
classes: (B, N) 物体类别ID
crops: (B, N, 3, 32, 32) 物体裁剪图像

Returns:
obj_embeddings: (B, N, embed_dim)
"""
B, N = bboxes.shape[:2]

# 类别嵌入
cls_emb = self.class_embed(classes) # (B, N, embed_dim)

# 空间嵌入
sp_emb = self.spatial_encoder(bboxes) # (B, N, embed_dim)

# 外观嵌入
crops_flat = crops.view(B * N, 3, 32, 32)
app_emb = self.appearance_cnn(crops_flat).view(B, N, -1)

# 融合
all_emb = torch.cat([cls_emb, sp_emb, app_emb], dim=-1)
return self.fusion(all_emb)


class TransGazeObject(nn.Module):
"""
TransGaze-Object: 基于Transformer的驾驶员注视物体预测

核心创新: 使用交叉注意力直接预测注视物体,
跳过传统的PoG (Point-of-Gaze)中间估计步骤

论文Section 3.3: Transformer Cross-Attention Module

Args:
embed_dim: 嵌入维度
num_heads: 注意力头数
num_objects: 最大物体数
"""

def __init__(self, embed_dim: int = 256, num_heads: int = 8,
num_objects: int = 10):
super().__init__()

# 面部特征提取
self.face_extractor = FaceFeatureExtractor(embed_dim)

# 交通物体编码
self.object_encoder = TrafficObjectEncoder(embed_dim, num_objects)

# Transformer交叉注意力
self.cross_attn = nn.MultiheadAttention(
embed_dim, num_heads, batch_first=True
)
self.norm1 = nn.LayerNorm(embed_dim)

# FFN
self.ffn = nn.Sequential(
nn.Linear(embed_dim, embed_dim * 4),
nn.GELU(),
nn.Linear(embed_dim * 4, embed_dim)
)
self.norm2 = nn.LayerNorm(embed_dim)

# 分类头
self.classifier = nn.Linear(embed_dim, num_objects + 1) # +1 for background

def forward(self, face_img, eye_patch, bboxes, classes, crops):
"""
Args:
face_img: (B, 3, 224, 224)
eye_patch: (B, 3, 112, 112)
bboxes: (B, N, 5)
classes: (B, N)
crops: (B, N, 3, 32, 32)

Returns:
logits: (B, N+1) 注视物体概率(含背景)
attn_weights: (B, 1, N) 注意力权重
"""
# 提取面部特征 → 作为query
face_feat = self.face_extractor(face_img, eye_patch) # (B, embed_dim)
query = face_feat.unsqueeze(1) # (B, 1, embed_dim)

# 编码交通物体 → 作为key/value
obj_emb = self.object_encoder(bboxes, classes, crops) # (B, N, embed_dim)

# 交叉注意力
attn_out, attn_weights = self.cross_attn(query, obj_emb, obj_emb)
x = self.norm1(query + attn_out)

# FFN
x = self.norm2(x + self.ffn(x))

# 分类
logits = self.classifier(x.squeeze(1)) # (B, N+1)

return logits, attn_weights


# ============ 完整测试代码 ============
if __name__ == "__main__":
import numpy as np
import time
from sklearn.metrics import accuracy_score

NUM_OBJECTS = 8 # 交通物体类别数
BATCH_SIZE = 16
EMBED_DIM = 256

# 初始化模型
model = TransGazeObject(
embed_dim=EMBED_DIM,
num_heads=8,
num_objects=NUM_OBJECTS
)

total_params = sum(p.numel() for p in model.parameters())
print(f"TransGaze-Object参数量: {total_params:,}")

# 模拟驾驶场景数据
np.random.seed(42)

def generate_sample(num_obj=5):
"""生成一个驾驶场景样本"""
face = torch.rand(3, 224, 224)
eye = torch.rand(3, 112, 112)
bboxes = torch.rand(num_obj, 5) * torch.tensor([1, 1, 0.3, 0.3, 1])
classes = torch.randint(0, 10, (num_obj,))
crops = torch.rand(num_obj, 3, 32, 32)
return face, eye, bboxes, classes, crops

# 批量生成
print("\n--- 生成测试数据 ---")
faces, eyes, bbox_list, class_list, crop_list = [], [], [], [], []
labels = []

for i in range(BATCH_SIZE):
n = np.random.randint(3, NUM_OBJECTS + 1)
face, eye, bbox, cls, crop = generate_sample(n)
faces.append(face)
eyes.append(eye)
bbox_list.append(bbox)
class_list.append(cls)
crop_list.append(crop)
labels.append(np.random.randint(0, n + 1)) # +1 for background

# padding到统一长度
max_obj = NUM_OBJECTS
bboxes_padded = torch.zeros(BATCH_SIZE, max_obj, 5)
classes_padded = torch.zeros(BATCH_SIZE, max_obj, dtype=torch.long)
crops_padded = torch.zeros(BATCH_SIZE, max_obj, 3, 32, 32)
masks = torch.zeros(BATCH_SIZE, max_obj, dtype=torch.bool)

for i, (bbox, cls, crop) in enumerate(zip(bbox_list, class_list, crop_list)):
n = len(bbox)
bboxes_padded[i, :n] = bbox
classes_padded[i, :n] = cls
crops_padded[i, :n] = crop
masks[i, :n] = True

face_batch = torch.stack(faces)
eye_batch = torch.stack(eyes)
labels = torch.tensor(labels)

print(f" Face: {face_batch.shape}")
print(f" Eye: {eye_batch.shape}")
print(f" BBox: {bboxes_padded.shape}")
print(f" Labels: {labels.shape}")

# 前向传播
print("\n--- 前向传播 ---")
model.eval()
with torch.no_grad():
logits, attn = model(face_batch, eye_batch, bboxes_padded,
classes_padded, crops_padded)
preds = torch.argmax(logits, dim=1)
print(f" Logits: {logits.shape}")
print(f" Attention: {attn.shape}")
print(f" Predictions: {preds}")

# 训练流程
print("\n--- 训练流程 ---")
criterion = nn.CrossEntropyLoss()
optimizer = torch.optim.AdamW(model.parameters(), lr=1e-4)

model.train()
for epoch in range(5):
optimizer.zero_grad()
logits, _ = model(face_batch, eye_batch, bboxes_padded,
classes_padded, crops_padded)
loss = criterion(logits, labels)
loss.backward()
optimizer.step()

acc = accuracy_score(labels.numpy(), torch.argmax(logits, dim=1).detach().numpy())
print(f"Epoch {epoch+1}: Loss={loss.item():.4f}, Acc={acc:.2%}")

# 推理速度
model.eval()
with torch.no_grad():
start = time.time()
for _ in range(50):
_ = model(face_batch[:1], eye_batch[:1], bboxes_padded[:1],
classes_padded[:1], crops_padded[:1])
elapsed = (time.time() - start) / 50
print(f"\n单样本推理延迟: {elapsed*1000:.1f}ms")

实验结果

性能对比

方法 准确率 背景混淆率 说明
PoG → Object Association 51% 23.21% 两步法
TransGaze-Object 60% 11.68% 端到端
相对提升 +17.6% -49.7% 显著减少背景混淆

错误分析

错误类型 传统方法 TransGaze-Object 改善
物体→背景 23.21% 11.68% -49.7%
背景→物体 15.8% 8.2% -48.1%
物体间混淆 10.2% 7.1% -30.4%

IMS应用启示

Euro NCAP分心检测场景

场景 TransGaze-Object支持 准确率 检测时限
D-02 手机使用 ✅ 直接识别注视手机 ~65% ≤3s
D-05 视线偏离 ✅ 注视物体≠道路 ~58% ≤3s
D-03 操作仪表 ✅ 注视仪表盘 ~62% ≤3s
正常驾驶 ✅ 注视前方道路 ~70% -

与现有IMS gaze估计的对比

方案 输出 语义级别 准确率 实时性
gaze zone 5-9个区域 粗粒度 ~85% ✅
PoG (x,y) 坐标点 无语义 ~70% ✅
TransGaze-Object 物体级 高语义 60% ✅

部署建议

组件 模型 硬件 延迟
面部检测 RetinaFace QCS8255 DSP 5ms
眼部裁剪 面部landmark 同上 2ms
物体检测 YOLOv8-nano QCS8255 NPU 15ms
TransGaze-Object 本模型 QCS8255 NPU 20ms
总延迟 - - ~42ms

论文链接: https://arxiv.org/abs/2609.10139

数据集: UD-FSG (Urban Driving-Face Scene Gaze),含同步面部+场景图像+注视标签