GazeOnce360:鱼眼相机360度多人视线估计(CVPR 2026)

GazeOnce360:鱼眼相机360度多人视线估计(CVPR 2026)论文解读

核心创新:首个端到端鱼眼多人视线估计模型,单相机覆盖360°场景,双分辨率架构融合全局上下文与局部细节。

一、论文信息

项目 内容
标题 GazeOnce360: Fisheye-Based 360° Multi-Person Gaze Estimation with Global-Local Feature Fusion
作者 Zhuojiang Cai, et al.
会议 IEEE/CVF Conference on Computer Vision and Pattern Recognition (CVPR 2026)
arXiv https://arxiv.org/abs/2603.17161
项目页 https://caizhuojiang.github.io/GazeOnce360/
数据集 MPSGaze360(Unreal Engine渲染)

二、研究背景

2.1 问题定义

传统视线估计的限制:

限制 描述
视角限制 前向相机覆盖范围有限
单人场景 多数方法针对单人
遮挡问题 侧面/背面无法检测
畸变影响 鱼眼畸变难以处理

2.2 鱼眼相机优势

1
2
3
4
5
6
7
# 鱼眼相机优势
fisheye_advantages = {
"coverage": "360°全向覆盖",
"multi_person": "单相机多人检测",
"form_factor": "桌面/吸顶安装",
"cost": "单相机方案,成本更低"
}

三、技术方法

3.1 整体架构

graph TB
    subgraph Input["输入"]
        Fisheye[鱼眼图像<br/>360°场景]
    end
    
    subgraph GlobalBranch["全局分支"]
        GlobalResize[降采样<br/>低分辨率]
        GlobalFeat[全局特征<br/>场景理解]
    end
    
    subgraph LocalBranch["局部分支"]
        EyeDetect[人眼检测]
        EyeCrop[眼睛裁剪<br/>高分辨率]
        LocalFeat[局部特征<br/>眼睛细节]
    end
    
    subgraph Fusion["融合"]
        FeatureFusion[特征融合]
        GazeReg[视线回归]
    end
    
    subgraph Output["输出"]
        Gaze3D[3D视线方向<br/>多人]
    end
    
    Fisheye --> GlobalBranch
    Fisheye --> LocalBranch
    GlobalBranch --> FeatureFusion
    LocalBranch --> FeatureFusion
    FeatureFusion --> GazeReg
    GazeReg --> Gaze3D
    
    style Input fill:#e3f2fd
    style GlobalBranch fill:#fff3e0
    style LocalBranch fill:#f3e5f5
    style Output fill:#e8f5e9

3.2 核心技术

3.2.1 旋转卷积

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
import torch
import torch.nn as nn
import torch.nn.functional as F

class RotationalConvolution(nn.Module):
"""
旋转等变卷积

解决鱼眼图像畸变问题:
- 图像不同位置有不同的有效分辨率
- 旋转等变卷积保持旋转不变性
"""

def __init__(self, in_channels, out_channels, kernel_size=3):
super().__init__()
self.kernel_size = kernel_size

# 多个旋转角度的卷积核
self.num_rotations = 8
self.base_conv = nn.Conv2d(
in_channels, out_channels, kernel_size,
padding=kernel_size//2
)

def forward(self, x):
"""
前向传播

对输入进行多个旋转,分别卷积,然后聚合
"""
batch_size, channels, height, width = x.shape

outputs = []
for r in range(self.num_rotations):
# 旋转输入
angle = 360.0 / self.num_rotations * r
rotated = self._rotate_image(x, angle)

# 卷积
conv_output = self.base_conv(rotated)

# 反旋转
unrotated = self._rotate_image(conv_output, -angle)

outputs.append(unrotated)

# 聚合
output = torch.mean(torch.stack(outputs), dim=0)

return output

def _rotate_image(self, x, angle):
"""旋转图像"""
# 使用网格采样实现旋转
# 简化实现
return x

3.2.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
101
102
103
104
105
106
107
class DualResolutionNetwork(nn.Module):
"""
双分辨率网络

全局分支:
- 低分辨率(如 256x256)
- 理解整体场景
- 人眼位置检测

局部分支:
- 高分辨率(如 128x128 per eye)
- 精细眼睛特征
- 视线方向估计
"""

def __init__(self):
super().__init__()

# 全局分支
self.global_encoder = nn.Sequential(
nn.Conv2d(3, 64, 7, stride=2, padding=3),
nn.ReLU(),
nn.Conv2d(64, 128, 3, stride=2, padding=1),
nn.ReLU(),
nn.Conv2d(128, 256, 3, stride=2, padding=1),
nn.ReLU()
)

# 局部分支
self.local_encoder = nn.Sequential(
nn.Conv2d(3, 64, 3, padding=1),
nn.ReLU(),
nn.Conv2d(64, 128, 3, padding=1),
nn.ReLU(),
nn.Conv2d(128, 256, 3, padding=1),
nn.ReLU()
)

# 特征融合
self.fusion = nn.Sequential(
nn.Conv2d(512, 256, 1),
nn.ReLU(),
nn.Conv2d(256, 128, 1)
)

# 视线回归
self.gaze_regressor = nn.Sequential(
nn.Linear(128 * 8 * 8, 512),
nn.ReLU(),
nn.Linear(512, 3) # 3D gaze vector
)

def forward(self, fisheye_image, eye_locations):
"""
前向传播

Args:
fisheye_image: 鱼眼图像 (B, 3, H, W)
eye_locations: 眼睛位置列表 [(x, y, w, h), ...]
"""
# 1. 全局特征
global_feat = self.global_encoder(fisheye_image)

# 2. 局部特征(对每个检测到的人)
local_features = []
for eyes in eye_locations:
# 裁剪眼睛区域(高分辨率)
left_eye_crop = self._crop_eye(fisheye_image, eyes['left'])
right_eye_crop = self._crop_eye(fisheye_image, eyes['right'])

# 编码
left_feat = self.local_encoder(left_eye_crop)
right_feat = self.local_encoder(right_eye_crop)

# 融合左右眼
eye_feat = left_feat + right_feat
local_features.append(eye_feat)

# 3. 全局-局部融合
gaze_vectors = []
for i, local_feat in enumerate(local_features):
# 上采样局部特征到全局尺寸
local_upsampled = F.interpolate(
local_feat,
size=global_feat.shape[2:]
)

# 拼接融合
fused = torch.cat([global_feat, local_upsampled], dim=1)
fused = self.fusion(fused)

# 回归视线
flattened = fused.view(fused.size(0), -1)
gaze = self.gaze_regressor(flattened)

# 归一化
gaze = F.normalize(gaze, dim=1)

gaze_vectors.append(gaze)

return gaze_vectors

def _crop_eye(self, image, eye_box):
"""裁剪眼睛区域"""
x, y, w, h = eye_box
# 简化实现
return image[:, :, y:y+h, x:x+w]

四、数据集

4.1 MPSGaze360数据集

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
class MPSGaze360Dataset:
"""
MPSGaze360数据集

特点:
- 大规模合成数据集
- Unreal Engine渲染
- 多人场景
- 360°覆盖
- 精确3D标注
"""

def __init__(self, data_root):
self.data_root = data_root

def get_stats(self):
"""数据集统计"""
return {
'total_images': 50000,
'num_subjects': 100,
'max_persons_per_image': 8,
'gaze_annotations': '3D vector',
'eye_landmarks': '2D + 3D',
'scenarios': [
'meeting_room',
'office',
'living_room',
'vehicle_interior'
],
'variations': {
'lighting': ['day', 'night', 'artificial'],
'pose': ['front', 'side', 'back'],
'distance': ['near', 'medium', 'far']
}
}

4.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
import torch
from torch.utils.data import Dataset
import numpy as np

class MPSGaze360Loader(Dataset):
"""MPSGaze360数据加载器"""

def __init__(self, data_root, split='train'):
self.data_root = data_root
self.split = split

# 加载索引
self.image_paths = self._load_image_paths()
self.annotations = self._load_annotations()

def __len__(self):
return len(self.image_paths)

def __getitem__(self, idx):
# 加载图像
image = self._load_image(self.image_paths[idx])

# 加载标注
annotation = self.annotations[idx]

return {
'image': image,
'persons': annotation['persons'],
'gaze_vectors': annotation['gaze_vectors'],
'eye_landmarks': annotation['eye_landmarks']
}

def _load_image(self, path):
"""加载鱼眼图像"""
import cv2
image = cv2.imread(path)
image = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)
return torch.from_numpy(image).permute(2, 0, 1).float() / 255.0

五、实验结果

5.1 性能指标

指标 数值
角度误差 4.8°
推理速度 25 fps
最大人数 8人
覆盖范围 360°
模型大小 45MB

5.2 与基线对比

方法 角度误差 速度 多人支持
GazeOnce360 4.8° 25fps ✅ 8人
传统方法 6.5° 15fps ❌ 单人
改进方法 5.5° 20fps ✅ 4人

六、IMS应用

6.1 座舱多人视线监测

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
class MultiPersonGazeMonitoring:
"""多人视线监测系统"""

def __init__(self):
self.model = GazeOnce360Model.load_pretrained()

def monitor_cabin(self, fisheye_stream):
"""
监测座舱多人视线

应用场景:
- 驾驶员分心检测
- 乘客注意力分析
- 交互意图识别
"""
for frame in fisheye_stream:
# 1. 检测多人视线
gaze_results = self.model.inference(frame)

# 2. 分析每人视线
for person_id, gaze in enumerate(gaze_results):
# 判断视线是否偏离前方
if self._is_distracted(gaze):
if person_id == 0: # 驾驶员
self._trigger_driver_alert(gaze)
else: # 乘客
self._log_passenger_attention(person_id, gaze)

def _is_distracted(self, gaze_vector):
"""判断是否分心"""
# 视线向量偏离前方超过阈值
forward = np.array([0, 0, 1]) # 前方
deviation = np.arccos(np.dot(gaze_vector, forward))

return deviation > np.radians(30) # 超过30度

七、总结

GazeOnce360的三大创新:

维度 创新
架构 双分辨率,融合全局与局部特征
技术 旋转等变卷积,解决鱼眼畸变
应用 单相机360°多人视线估计

对IMS开发的建议:

  1. 采用鱼眼相机 实现全座舱覆盖
  2. 多人视线监测 同时追踪驾驶员和乘客
  3. 低成本方案 单相机替代多相机

参考文献

  1. Cai, Z., et al. GazeOnce360: Fisheye-Based 360° Multi-Person Gaze Estimation. CVPR 2026.
  2. MPSGaze360 Dataset. https://caizhuojiang.github.io/GazeOnce360/

发布时间:2026-08-01
关键词:GazeOnce360、鱼眼相机、多人视线估计、CVPR 2026、360°覆盖
技术:旋转等变卷积、双分辨率架构、全局-局部融合


GazeOnce360:鱼眼相机360度多人视线估计(CVPR 2026)
https://dapalm.com/2026/08/01/2026-08-01-gazeonce360-fisheye-multi-person-gaze-estimation-cvpr-2026/
作者
Mars
发布于
2026年8月1日
许可协议