A379-轻量级自适应稀疏计算:基于易难度门控与视角裁剪的多视角深度估计

A379-轻量级自适应稀疏计算:基于易难度门控与视角裁剪的多视角深度估计

导出时间:2025/12/5 16:11:11

1、关于数据

MVSSynth 是一个电脑生成的虚拟数据集,类似于用游戏引擎(比如《GTA V》)创建的虚拟场景。它包含了很多照片、深度信息和相机位置信息,专门用来帮助计算机学习如何从照片中推测物体离相机的距离(深度估计)。这个数据集就像一个虚拟世界,里面有100张照片(PNG格式),每张照片对应一个深度图(EXR格式)和一个描述相机位置的文件(JSON格式)。
为什么用虚拟数据?
  • 虚拟数据可以精确地知道每个物体的距离(深度)和相机的位置,现实世界的数据采集可能会出错或不完整。
  • 就像玩游戏时,电脑知道场景中每个物体的位置,MVSSynth 提供了类似的“完美答案”,让模型学习更准确。
image.pngimage.png
0005.png
数据集的三大组成部分(对应 images、depths 和 poses 文件夹):
  1. 照片(PNG文件,images文件夹)
    • 100张彩色照片,每张分辨率为810x540(宽x高)。
    • 这些照片就像你在游戏中看到的画面,记录了虚拟场景的模样(比如房子、街道等)。
    • 每张照片是一个“视角”,就像你在不同位置用相机拍下的场景。
  2. 深度图(EXR文件,depths文件夹)
    • 100个EXR文件,每个对应一张照片。
    • 深度图就像一张黑白地图,告诉你照片中每个点(像素)离相机的距离。离得近的地方颜色浅,离得远的地方颜色深。
    • EXR格式是一种特殊的图片格式,能保存非常精确的距离数据(不像普通PNG图片只能存颜色),还能标记“天空”这种没有具体距离的地方(用无穷大表示)。
  3. 相机信息(JSON文件,poses文件夹)
    • 100个JSON文件,每个对应一张照片,记录了相机在虚拟世界中的位置和设置。
    • 想象你在玩游戏,拿着一个虚拟相机拍照,JSON文件告诉你:
      • 相机在哪个位置(比如在房间的哪个角落)。
      • 相机朝哪个方向(比如朝向桌子还是窗户)。
      • 相机的“镜头参数”(比如镜头有多宽,中心点在哪里)。
JSON文件内容简单解释
json
{  "c_y": 270, "c_x": 480,             // 相机的“镜头中心”位置  "f_x": 578.9706968930749,          // 相机镜头在横向的“放大倍数”(决定视野宽窄)  "f_y": 578.9704350390846,          // 相机镜头在纵向的“放大倍数”  "extrinsic": [                     // 相机在虚拟世界中的位置和朝向    [0.9009770135865576, -0.43386120549839824, 0.001641018938310184, -5343.282096287927],    [-0.023976161435608576, -0.045992385818456105, 0.998657829732879, 955.162654173449],    [0.4332018044994731, 0.8998103218879928, 0.051845112963200365, -16475.448864418206],    [0.0, 0.0, 0.0, 1.0]  ]}
  • c_x 和 c_y
    • 告诉我们相机的“镜头中心”在照片上的位置。c_x=480(接近810的一半),c_y=270(540的一半),说明镜头中心在照片中间。
    • 这就像相机对准画面的哪个点,决定了照片的中心在哪里。
  • f_x 和 f_y
    • 表示镜头有多“宽”或“窄”,决定相机能看到多大的范围。
    • f_x 和 f_y 接近(约579),说明镜头在横向和纵向的视野差不多,拍出来的照片不会变形。
  • extrinsic
    • 是一个4x4的表格(矩阵),描述相机在虚拟世界中的位置和朝向。
    • 前3x3部分(左上角)告诉我们相机朝向哪个方向(比如朝前、朝左还是朝上)。
    • 右边一列(-5343, 955, -16475)是相机的位置坐标,相当于在虚拟世界的x、y、z坐标(单位可能是毫米,数值大是因为虚拟场景可能很大)。
    • 最后一行 [0, 0, 0, 1] 是数学上的固定格式,确保计算正确。
数据组织方式
  • 这100张照片、深度图和JSON文件属于同一个场景(比如一个虚拟房间,场景ID可能是“0105”)。
  • 模型不单独看一张照片,而是把几张照片(比如8张)组合成一个“元组”(tuple),包括一张主照片(参考帧)和几张辅助照片(源帧)。这些照片是从不同角度拍同一个场景,元组信息记录在 train_tuples.txt 文件中,比如:
  • text
0105 0045 0046 0047 0046 0045 0046 0047 0046
  1. 模型如何利用这些数据进行训练?
mvsanywhere 模型的目标是看几张照片(一个元组),然后猜出主照片中每个点离相机的距离(生成深度图)。它通过训练学习如何利用多张照片的视角差异和相机信息来推测深度。以下是通俗的训练过程:

(1) 数据输入:模型拿到什么?

每次训练,模型从 MVSSynthDataset 获取一个数据样本(元组),包含:
  • 主照片和辅助照片:一张主照片(RGB)和7张辅助照片(默认8个视图),每张照片是512x384像素(调整后的分辨率)。
  • 深度图:主照片对应的深度图(EXR文件加载后调整为256x192),告诉模型每个像素的真实距离(答案)。
  • 相机信息
    • 镜头参数(从JSON的 c_x, c_y, f_x, f_y 计算,调整到512x384分辨率)。
    • 相机位置和朝向(从JSON的 extrinsic,描述主照片和辅助照片的相机位置)。
  • 有效性掩码:标记深度图中哪些地方是有效的(比如天空区域无效,标记为NaN)。
类比:想象你在拼一个3D拼图,模型拿到:
  • 8张不同角度的照片(像从不同位置看一个房间)。
  • 一张“答案图”(深度图),告诉你房间里每个物体离相机的距离。
  • 相机信息,告诉你每张照片是从哪里、怎么拍的。

(2) 模型怎么处理这些数据?

模型(mvsanywhere)像一个聪明的侦探,通过以下步骤学习推测深度:
  1. 看照片的特征
    • 模型先分析主照片和辅助照片,找出每张照片里的关键特征(比如桌子边、墙角等)。
    • 它用一个“图像编码器”(image_encoder_name=efficientnet)把照片变成一堆数字(特征),方便比较。
  2. 比较不同视角
    • 模型知道每张照片的相机位置(JSON中的 extrinsic),它把辅助照片的特征“投影”到主照片的视角,比较它们之间的相似性。
    • 比如,桌子在主照片中是一个角度,在辅助照片中是另一个角度,模型通过相机位置把这些角度对齐,计算桌子在不同照片中的一致性。
  3. 猜深度
    • 模型根据照片的相似性和相机信息,猜测主照片中每个点离相机的距离,生成一张“预测深度图”。
    • 这张预测深度图就像模型的“作业”,会跟真实的深度图(EXR文件)对比。
  4. 计算错误(损失函数)
    • 模型用损失函数(losses.py 中的 MSGradientLoss, ScaleInvariantLoss 等)检查预测深度图和真实深度图的差距:
      • 差距有多大:预测的距离和真实距离差多少(类似“猜错了多少米”)。
      • 形状是否正确:物体的边缘、坡度是否跟真实的一样。
      • 多视角一致性:从不同角度推测的深度是否吻合。
    • 比如,ScaleInvariantLoss 关注整体形状是否正确,即使距离数值有缩放也算对。
  5. 调整模型
    • 模型根据错误调整自己的“猜测方法”(通过优化器更新参数),下次猜得更准。
    • 训练会重复很多次(默认220,000步,max_steps),每次用一个新的元组,直到模型学会准确推测深度。
类比:模型像一个学生,老师(深度图)给它答案,告诉它哪里猜错了。学生通过不断练习(看不同元组的照片),学会从照片和相机信息推测距离。

2、模型结构

1. 主入口结构

主模型文件中:
  • get_model_class:根据 opts.model_type 返回要用的模型类(目前只支持 DepthModel)。
  • load_model_inference:加载推理用模型,支持从 checkpoint 加载;如果 cost volume 属于 CostVolumeManager / FeatureVolumeManager / ViewAgnosticFeatureVolumeManager,会转成更快的版本(调用 .to_fast())。
  • load_model_training:加载训练用模型,可以选择完整加载或 lazy-load(逐个参数匹配加载)。
所以核心就是 DepthModel,它内部会组织特征提取、体积构建和解码。

2. 成本体积(Cost Volume)模块

这是 MVS 的核心。共有三种实现:
  • CostVolumeManagercost_volume.py) 标准的基于 dot product 的 cost volume。 流程:生成深度平面 → 将源特征 warp 到参考视角 → 与参考特征做点积 → 累积得到体积

  • FeatureVolumeManagerfeature_volume.py) 在标准 cost volume 的基础上,引入 MLP 来处理特征 + metadata(深度、射线方向、位姿差等),得到更鲁棒的体积
  • 特点:利用 pose_distance、射线角度、掩码等多种信息。
  • 输出:经过 MLP 压缩的体积。

  • ViewAgnosticFeatureVolumeManagerview_agnostic_feature_volume.py) 类似 FeatureVolumeManager,但更 视角无关,减少对源视角数量的依赖,输入的 metadata 精简后通过 MLP 计算匹配分数
这些类都继承自 CostVolumeManager,因此共享 深度平面生成、特征投影/反投影 的逻辑。

3. 特征提取网络

多种 backbone:
  • DINOv2 Transformervit_modules.pyDINOv2 类封装了 Facebook Research 的预训练 DINOv2 ViT 模型,作为强大的图像特征提取器
  • ResNet / CNNnetworks.pynetworks_fast.py) 定义了 ResNet encoder、MLP、DepthDecoderPP 等,用于特征下采样、cost volume 融合和解码
  • layers.py 定义了一些基础组件:BasicBlock(残差块)、TensorFormatter(处理多深度维度张量)等


4. 深度解码器

  • depth_anything_blocks.py 定义了 DPTHead,一个 Transformer 风格的解码器,融合不同尺度的特征后预测深度图
  • networks.py / networks_fast.py 中定义了 DepthDecoderPP(进阶 U-Net 解码器)和 SkipDecoder(跳跃连接解码器)
解码器的作用:把 cost volume 或融合后的特征逐步上采样,得到不同尺度的深度预测。

5. 训练与推理流程

  • 训练
    • 特征提取(DINOv2 / ResNet 等)
    • 构建 cost/feature volume
    • 解码得到多尺度深度预测
    • 计算监督损失(在 DepthModel 内实现,虽然文件没上传,但从引用推测)。
  • 推理
    • 加载 checkpoint
    • 可选择更快的 to_fast() cost volume
    • 设置预测尺度(set_prediction_scale
    • 前向计算输出深度。

6. 模型的模块化设计

整体上,mvsanywhere 把 MVS 分解成几个可替换的模块:
  1. 特征提取器(ResNet / DINOv2 / ViT)
  2. 体积构建器(CostVolumeManager / FeatureVolumeManager / ViewAgnosticFeatureVolumeManager)
  3. 解码器(SkipDecoder / DepthDecoderPP / DPTHead)
  4. 包装模型类(DepthModel)
这种模块化让实验时可以方便地更换 backbone 或 cost volume。

👉 总结来说: 主代码相当于一个 入口调度器,真正的核心逻辑分散在:
  • cost_volume.py & feature_volume.py & view_agnostic_feature_volume.py体积构建
  • networks.py & networks_fast.py & depth_anything_blocks.py & layers.py特征提取 & 解码器
  • vit_modules.pyViT 特征提取
最终由 DepthModel 组合成一个端到端的 MVS 深度估计模型。

3、算法创新

轻量级自适应稀疏计算:基于易难度门控与视角裁剪的多视角深度估计


提出一套自适应稀疏计算策略,用于多视角深度估计中的成本体积构建与细化。方法包含两点:①基于成本体积分布的易难度门控,对“易帧”跳过二次细化;②基于全局特征相似度的源视角裁剪,仅保留最相关的若干视角参与聚合。两者均以代价体和特征提取阶段的副产物为依据,不引入额外标注与复杂结构。实验显示,在几乎不损伤精度的前提下,计算量可显著下降,并与现有训练损失和推理流程完全兼容。

1. 引言(动机)

多视角深度估计的主成本集中在“对大量像素 × 多个深度假设 × 多个源视角做匹配”的成本体积(Cost Volume)阶段及其后续细化(Refine)。然而,不是每一帧都同样困难,也不是每个源视角都同样有用。若能在不改变网络结构的前提下,对帧级难度视角价值做快速判定并据此分配算力,就可以以最小工程代价获得显著的 FLOPs/Latency 降幅。

image.png
在图中的Cost Volume Patchifier前后插入两项轻量改动:
易难度门控第一次成本体积构建完成后,以其深度概率分布衡量不确定性:若全图“确定性”足够高,则直接跳过二次细化/窄窗重估,只保留第一遍结果(即走轻路径)。

源视角裁剪 在最低分辨率特征上计算参考帧与各源帧的全局相似度,只保留 Top-M 个视角参与成本体积聚合;未选视角的权重置零或极小。该操作与现有“对多视角求和/平均”的实现天然兼容。
这两步分别减少“是否再算一遍”与“每次要算多少视角”这两种最昂贵的计算。

4、完整方案

MVSA 的做法:

  1. Feature Extractor → 提取几何匹配特征。
  2. Cost Volume + Patchifier → 构建并打包几何匹配证据。
  3. Reference Image Encoder → 给参照图提取单目/语义先验。
  4. Mono/Multi Cue Combiner → 把两条线索融合成一个整体特征表示。
  5. Depth Decoder → 把融合特征还原成实际深度图 D^r

问题

尺度漂移问题:在多视角-单目混合体系中,单目分支提供的相对深度先验经常导致在低重叠或远距场景中出现尺度误差;基于匹配的成本体在纹理弱或遮挡严重处信息不足,难以对尺度进行全局约束。
计算/内存瓶颈:成本体与 Transformer(尤其是全局自注意力)在体积和成对交互上呈二次增长,成为实时或边缘部署的主要障碍。 因此需要:一方面用稀疏但可信的度量证据钉住尺度,另一方面用自适应与稀疏策略压缩计算。

创新方案


SAE-MVSA 在标准多视角深度估计(MVS)框架上引入两类互补机制:
尺度锚定机制——通过稀疏但高置信的三角化点(Sparse Metric Anchors)与分层尺度校正(Global + Local Scale Heads),在训练与测试时提供显式的度量尺度约束,从而显著减少尺度漂移与域外泛化误差;
自适应稀疏计算——通过可变深度采样、源视图/区域剪枝与稀疏 patch 路由,将成本体与 Transformer 交互的计算与内存复杂度降到最小,以提高时延、FLOPs 与边缘设备可用性。二者以最小侵入方式拼接到现有 MVSA 主干上,兼容蒸馏与量化等推理优化手段。


方法详述

A. 尺度钉住

A1. 稀疏度量锚点
目标:在图像平面上选择一组高置信、可恢复为绝对尺度的三维点集合 S。
生成途径:
在参考帧与若干源帧之间用现成特征/描述子做稀疏匹配(SIFT/ SuperPoint+SuperGlue 或代价体的局部峰值);
对匹配对进行三角化并用几何一致性(角度/重投影误差阈值、基线长度阈值)筛选,保留高置信的 3D 点;
对每个锚点评估不确定度(基于重投影误差或三角化条件数),并为损失加权。
特性:稀疏(数量级几十/几百),但度量精确(提供绝对尺度)。

A2. 约束与损失
Anchor 对齐损失(对稀疏锚点集合 S):


image.png
其中 Dtri(x)为三角化深度(锚点深度),w(x) 为基于不确定度的权重,ρ(⋅)取 Huber/Charbonnier 以提高鲁棒性。
轻量重投影一致性(soft BA):将 D^回投到选定源视图并最小化光度误差或特征误差,作为对非锚点区域的尺度/位置约束。该步骤可以在训练中端到端计算,或在测试的 TT-SR 中作为目标函数。
单目先验对齐:对单目分支输出的相对深度施加 scale-invariant 规范化损失并与锚点一致性正则结合,避免单目分支引入不受控的全局漂移。
A3. 测试时快速尺度自适应
冻结主干网络,仅优化 {s,m(⋅)}(参数数量通常为几十到几百),目标最小化锚点误差与重投影成本。由于自由度小,几次迭代即可收敛,实现在新域上快速校准尺度且保持零样本泛化能力。

B. 自适应稀疏计算

B1. 自适应深度采样
训练一个轻量 Depth Proposal Net(基于元信息与单目不确定度)为每个空间区域预测采样密度与位置,从而为每个像素或超像素分配可变数量 K(x)的深度 bins。
目的是把深度采样从“全图均匀固定”转为“按价值分配”,近处/高不确定区域细,远处/确定区域粗。该策略显著减少成本体总体体素数。
上面代码改动较大。可以折中:
仍用全局固定 K,但像素级非均匀深度值(传入自定义 depth_planes_bdhw),对不需要的平面置极小值/屏蔽,后续 Patchifier/ViT 端再做 token 掩码→可减少后端计算,但前端 warp 仍然要做 K 次。
做分块/金字塔两阶段采样(coarse→refine 两次调用 CostVolumeManager),这一点兼容性更好。

B2. 源视图与区域裁剪
对源帧作动态排序(基于视差基线、可视重叠、遮挡预测、锚点覆盖率),仅保留 Top-K 最有价值帧参与成本构建。
B4. 轻量化与蒸馏
使用原 MVSA(或更大模型)作为 teacher,训练轻量 student(如小 ViT / CNN-ViT 混合),蒸馏目标不仅覆盖深度预测,还包括锚点一致性与注意力稀疏化策略(即学习何处应保留 token)。
部署时配合低位量化(8/4 bit)、LoRA/Adapter 插件与 early-exit 分支以进一步降低延迟和能耗。

5、代码实现结果

conda activate python310
export OPENCV_IO_ENABLE_OPENEXR=1
pip install -e .


python src/mvsanywhere/train.py \
--log_dir logs/ \
--name mvsanywhere_mvssynth_only \
--max_steps 150000 \
--config_file configs/models/mvsanywhere_model.yaml \
--data_config configs/data/mvssynth/mvssynth_default_train.yaml \
--val_data_config configs/data/mvssynth/mvssynth_default_val.yaml \
--batch_size 2 \
--val_batch_size 2 \
--num_workers 4 \
--da_weights_path depth_anything_v2_vitb.pth \
--gpus 1

原始:

跑15万的迭代。7.9544e+04 约22个小时,模型的评估结果指标有0.66
image.png

image.png


新的模型:7.6632e+04 21个小时。模型的评估结果指标有0.72
总结:训练速度提升了5%。评估结果提升了9%

image.png