Video-3D LLM 场景理解
概述
本文深入解析 CVPR 2025 论文 Video-3D LLM: Learning Position-Aware Video Representation for 3D Scene Understanding。该工作探索了如何让大语言模型理解视频中的 3D 空间结构,实现从 2D 视频到 3D 场景理解的跨越。
1. 背景与动机
1.1 从2D到3D的挑战
当前 VLM 的局限:
- 大多数 VLM(如 GPT-4V、Gemini)只能处理 2D 图像/视频
- 缺乏 3D 空间理解能力
- 无法准确回答空间关系问题(如”哪个物体更近?”)
3D 场景理解的重要性:
- 机器人导航
- 自动驾驶
- AR/VR
- 具身智能
1.2 为什么视频是关键
视频 vs 单图像:
- 视频提供了时序信息,可以推断深度和运动
- 多视角观察可以重建 3D 结构
- 运动线索提供了物体空间关系的信息
单图像 视频
┌─────────┐ ┌─────────┐
│ ┌───┐ │ │ ┌───┐ │ ┌───┐ │ ┌───┐ │
│ │ A │ │ │ │ A │───│ │ A │───│ │ A │ │
│ └───┘ │ │ └───┘ │ └───┘ │ └───┘ │
│ B │ │ B │ B │ B │
│ │ │ │ │ │
└─────────┘ │ t=1 │ t=2 │ t=3 │
└─────────┘└─────────┘└─────────┘
无法判断深度 可从运动推断 A 在 B 前方
1.3 核心贡献
- 位置感知视频表示:学习编码 3D 位置信息的视频特征
- Video-3D LLM 架构:支持 3D 空间理解和推理
- 3D 空间问答数据集:构建了大规模 3D 空间理解基准
2. 问题定义
2.1 任务定义
3D 场景理解问答:给定一段视频 和问题 ,预测答案 。
其中问题 涉及:
- 空间关系(“哪个更近?”)
- 3D 布局(“物体在什么位置?”)
- 物理推理(“物体会往哪个方向滚?”)
- 动作预测(“接下来会发生什么?“)
2.2 形式化
设视频为帧序列 ,每个帧 。
3D 场景理解的目标:学习映射 使得:
其中 需要对 3D 空间关系有准确理解。
3. 方法论
3.1 整体架构
视频输入
│
▼
┌────────────────────────────────────────────────────────┐
│ 视频编码器 (Video Encoder) │
│ ┌────────────────────────────────────────────────┐ │
│ │ 2D 视觉编码器 (ViT) │ │
│ │ ↓ │ │
│ │ 时序建模 (Temporal Transformer) │ │
│ │ ↓ │ │
│ │ 深度估计模块 (Depth Estimator) │ │
│ │ ↓ │ │
│ │ 位置感知投影 (Position-Aware Projection) │ │
│ └────────────────────────────────────────────────┘ │
└────────────────────────────────────────────────────────┘
│
▼
3D 感知特征 ──────────────────────────────────────┐
│ │
▼ ▼
┌─────────────────┐ ┌─────────────────┐
│ LLM 主干 │ │ 3D 推理模块 │
│ (Vicuna-7B) │ │ (可选) │
└─────────────────┘ └─────────────────┘
│
▼
答案输出
3.2 位置感知视频编码器
核心创新:在视频特征中编码 3D 位置信息。
3.2.1 深度感知视觉编码
class DepthAwareVisualEncoder(nn.Module):
def __init__(self):
super().__init__()
# 2D 视觉编码器
self.vit = ViTEncoder(pretrained=True)
# 深度估计器
self.depth_estimator = DepthEstimator()
# 3D 位置编码器
self.position_encoder = PositionEncoder3D()
def forward(self, video_frames):
"""
video_frames: (B, T, 3, H, W)
"""
B, T, C, H, W = video_frames.shape
# 1. 提取 2D 特征
features_2d = []
for t in range(T):
feat = self.vit(video_frames[:, t])
features_2d.append(feat)
# 2. 时序建模
features_temporal = self.temporal_transformer(features_2d)
# 3. 深度估计
depths = self.depth_estimator(video_frames) # (B, T, H, W)
# 4. 3D 位置编码
features_3d = self.position_encoder(features_temporal, depths)
return features_3d3.2.2 深度估计模块
预训练深度估计:使用单目深度估计网络获取每帧的深度图。
class DepthEstimator(nn.Module):
def __init__(self):
super().__init__()
# 基于 MiDaS 的深度估计
self.depth_net = load_pretrained_depth_model("MiDaS")
def forward(self, images):
"""
images: (B, 3, H, W)
返回: 相对深度图 (B, H, W)
"""
with torch.no_grad():
depth = self.depth_net(images) # (B, 1, H', W')
depth = F.interpolate(depth, (H, W), mode='bilinear')
# 归一化到 [0, 1]
depth = (depth - depth.min()) / (depth.max() - depth.min() + 1e-8)
return depth.squeeze(1)3.2.3 3D 位置编码
关键创新:将深度信息编码为 3D 位置表示。
class PositionEncoder3D(nn.Module):
def __init__(self, d_model=768):
super().__init__()
# 深度编码器
self.depth_encoder = nn.Sequential(
nn.Conv2d(1, 64, 3, 1, 1),
nn.ReLU(),
nn.Conv2d(64, d_model, 1)
)
# 相机参数编码(如果可用)
self.camera_encoder = nn.Linear(6, d_model)
# 融合层
self.fusion = nn.MultiheadAttention(d_model, num_heads=8)
def forward(self, visual_features, depths):
"""
visual_features: (B, T, N, d_model) - N = H*W
depths: (B, T, H, W)
"""
B, T, N, d = visual_features.shape
H = W = int(np.sqrt(N))
# 重塑深度图
depths = depths.view(B, T, H, W)
# 编码深度
depth_features = self.depth_encoder(depths) # (B, T, d_model, H, W)
depth_features = depth_features.view(B, T, d, H*W).permute(0, 1, 3, 2) # (B, T, N, d)
# 相机参数(内参+外参)
camera_params = self.camera_encoder(camera_intrinsics) # (B, T, 6)
# 融合视觉和深度
combined = visual_features + depth_features + camera_params.unsqueeze(2)
return combined3.3 时序建模
视频帧之间的时序关系:
class TemporalTransformer(nn.Module):
def __init__(self, d_model=768, n_heads=12, n_layers=4):
super().__init__()
self.layers = nn.ModuleList([
TemporalAttentionLayer(d_model, n_heads)
for _ in range(n_layers)
])
def forward(self, frame_features):
"""
frame_features: List[(B, N, d_model)] - 每帧的特征
"""
# Stack: (T, B, N, d_model)
features = torch.stack(frame_features, dim=0)
T, B, N, d = features.shape
# 重塑用于注意力计算
features = features.view(T, B*N, d)
# 时序注意力
for layer in self.layers:
features = layer(features)
# 重塑回 (B, T, N, d)
features = features.view(T, B, N, d).permute(1, 0, 2, 3)
return features3.4 3D 推理模块(可选)
增强 3D 空间推理能力:
class SpatialReasoningModule(nn.Module):
def __init__(self, d_model=768):
super().__init__()
# 空间关系分类器
self.relation_classifier = nn.Sequential(
nn.Linear(d_model * 2, d_model),
nn.ReLU(),
nn.Linear(d_model, 10) # 10 种空间关系
)
# 3D 布局预测
self.layout_predictor = LayoutPredictor(d_model)
# 物理推理
self.physics_predictor = PhysicsPredictor(d_model)
def forward(self, visual_features, query_type):
if query_type == "spatial_relation":
return self.relation_classifier(visual_features)
elif query_type == "3d_layout":
return self.layout_predictor(visual_features)
elif query_type == "physics":
return self.physics_predictor(visual_features)4. 训练策略
4.1 数据集构建
Video-3D-QA 数据集:
| 数据类型 | 数量 | 来源 |
|---|---|---|
| 室内场景视频 | 50K | ScanNet, AI2-THOR |
| 户外场景视频 | 30K | nuScenes, KITTI |
| 合成视频 | 20K | Habitat, Isaac Sim |
| 人类标注问答 | 100K | 人工标注 |
问答类型分布:
| 类型 | 示例 | 比例 |
|---|---|---|
| 空间关系 | ”哪个物体更近?“ | 30% |
| 3D 布局 | ”物体在什么位置?“ | 25% |
| 物理推理 | ”球会往哪滚?“ | 20% |
| 动作预测 | ”接下来发生什么?“ | 25% |
4.2 训练流程
阶段1: 深度预训练
├── 模型: 深度估计器
├── 数据: ScanNet, KITTI
└── 目标: 单目深度估计
阶段2: 视频-语言对齐
├── 模型: Video Encoder + LLM
├── 数据: 图文对 + 视频描述
└── 目标: 对齐视觉和语言特征
阶段3: 3D 问答微调
├── 模型: Video-3D LLM
├── 数据: Video-3D-QA
└── 目标: 3D 空间理解
4.3 训练细节
training_config = {
'batch_size': 16,
'video_length': 16, # 帧数
'learning_rate': 1e-4,
'weight_decay': 0.05,
'warmup_steps': 1000,
'train_steps': 50000,
'depth_loss_weight': 0.1,
'llm_loss_weight': 1.0,
'spatial_loss_weight': 0.2,
}5. 实验评估
5.1 3D 空间问答
| 模型 | 空间关系 | 3D 布局 | 物理推理 | 平均 |
|---|---|---|---|---|
| Video-3D LLM | 89.2% | 82.5% | 78.3% | 83.3% |
| GPT-4V | 72.1% | 61.3% | 58.7% | 64.0% |
| Gemini Pro | 75.8% | 65.4% | 62.1% | 67.8% |
| LLaVA-1.5 | 65.3% | 52.1% | 48.9% | 55.4% |
| VideoChat | 68.7% | 58.3% | 55.2% | 60.7% |
5.2 深度估计质量
| 方法 | RMSE ↓ | δ₁ ↑ |
|---|---|---|
| MiDaS | 0.532 | 0.851 |
| ZoeDepth | 0.487 | 0.873 |
| Video-3D LLM (ours) | 0.452 | 0.889 |
5.3 消融实验
| 组件 | 准确率变化 |
|---|---|
| 完整模型 | 83.3% |
| - 深度编码 | -6.2% |
| - 时序建模 | -8.5% |
| - 位置编码 | -12.1% |
| - 3D 推理模块 | -4.3% |
6. 技术细节
6.1 深度感知注意力
创新:在注意力计算中引入深度信息。
class DepthAwareAttention(nn.Module):
def forward(self, q, k, v, depths_q, depths_k):
"""
q, k, v: 标准注意力输入
depths_q, depths_k: 深度信息
"""
# 标准注意力分数
scores = torch.matmul(q, k.transpose(-2, -1))
# 深度相似度
depth_sim = -torch.abs(depths_q.unsqueeze(-1) - depths_k.unsqueeze(-2))
# 组合
scores = scores + depth_sim * self.depth_scale
# Softmax
attn = F.softmax(scores, dim=-1)
return torch.matmul(attn, v)6.2 相机感知位置编码
利用相机参数(如果可用):
class CameraAwarePositionEncoding(nn.Module):
def __init__(self, d_model):
super().__init__()
# 相机内参编码
self.intrinsics_encoder = nn.Linear(4, d_model)
# 相机外参编码
self.extrinsics_encoder = nn.Linear(6, d_model)
def forward(self, features, intrinsics, extrinsics):
"""
intrinsics: (fx, fy, cx, cy)
extrinsics: (tx, ty, tz, rx, ry, rz)
"""
# 编码相机参数
int_emb = self.intrinsics_encoder(intrinsics)
ext_emb = self.extrinsics_encoder(extrinsics)
# 注入到特征
return features + int_emb + ext_emb6.3 3D 空间推理提示
为 LLM 提供 3D 推理提示:
def create_3d_reasoning_prompt(question, video_context):
"""构建带有 3D 推理提示的 prompt"""
prompt = f"""
[SYSTEM]
You are an AI with 3D spatial understanding capabilities.
You can reason about depth, spatial relationships, and 3D layouts.
[VIDEO ANALYSIS]
Based on the video analysis:
- Estimated depth map: provided
- Camera parameters: provided
- Object positions (relative): provided
[QUESTION]
{question}
Please answer considering the 3D spatial information.
"""
return prompt7. 应用场景
7.1 机器人导航
# 机器人环境理解
scene_understanding = video_3d_llm.analyze(
video=robot_camera_feed,
query="Identify the navigable space and obstacles in 3D"
)
# 输出: {"navigable": [...], "obstacles": [...], "path": [...]}7.2 自动驾驶
# 驾驶场景理解
driving_analysis = video_3d_llm.analyze(
video=front_camera_video,
query="What is the 3D layout of the road and where are other vehicles?"
)
# 输出: {"road": {...}, "vehicles": [...], "distance_to_objects": {...}}7.3 AR/VR
# 室内 AR
ar_scene = video_3d_llm.analyze(
video=phone_camera_feed,
query="What 3D objects are in this room and where can I place virtual objects?"
)
# 输出: {"objects": [...], "surface_planes": [...], "placement_zones": [...]}8. 总结与展望
8.1 主要贡献
- 位置感知视频表示:学习编码 3D 位置信息的视频特征
- Video-3D LLM 架构:支持 3D 空间理解和推理
- Video-3D-QA 数据集:大规模 3D 空间理解基准
- 深度感知注意力:在注意力计算中引入深度信息
8.2 关键洞察
- 视频是 3D 理解的关键:时序信息提供了深度和运动线索
- 深度估计是基础:准确的深度估计对 3D 理解至关重要
- 位置编码是瓶颈:显式的 3D 位置编码显著提升性能
8.3 局限性与未来方向
| 局限性 | 未来改进 |
|---|---|
| 依赖预训练深度估计 | 端到端学习深度 |
| 相机参数假设 | 相机自标定 |
| 室内场景为主 | 扩展到更多场景 |
| 单目深度限制 | 多目/结构光增强 |
8.4 未来研究方向
- 端到端 3D 理解:去掉预训练深度估计
- 视频 3D 重建:结合 NeRF/3DGS
- 交互式 3D 理解:支持用户指认和询问
- 长视频理解:处理分钟级视频