左右一致性无监督单目深度估计:双目视差与自监督训练原理

📅 发布时间:2026/10/16 4:30:08
左右一致性无监督单目深度估计:双目视差与自监督训练原理
1. 单目深度估计为什么非要走无监督这条路这个系列写到第二篇终于轮到单目深度估计里最经典的双目自监督方案了。今天想拆解的论文标题比较长Unsupervised Monocular Depth Estimation with Left-Right Consistency中文一般叫它左右一致性无监督单目深度估计。这篇论文在2017年前后可以说是自监督深度估计绕不开的起步点直到现在很多新工作仍然在使用它定下的损失函数框架。先说它到底解决了什么问题。如果我们想用一台普通单目相机输出深度图最直接的想法是有监督地训练一个回归网络输入图像输出深度用激光雷达或结构光设备采集的深度图当标签。问题是深度真值非常贵。户外自动驾驶数据集里的激光雷达点云是稀疏的近处有几万个点远处几乎空白室内场景用深度相机采集又会遇到玻璃、黑釉、遮挡带来的无效区域。真值质量不稳定网络学出来的东西也就不稳定。于是人们开始琢磨能不能根本不依赖真值直接让网络从图像自己身上找深度线索。当时有两类思路一类是用视频序列靠相邻帧之间的运动恢复结构来生成监督信号另一类就是这篇论文走的路线利用双目相机左右图像对之间的几何关系。两类思路后来被统称为自监督深度估计但当时“无监督”这个词更流行叫法也没那么统一。这篇论文的聪明之处是选择了一个天然强约束的训练信号——双目图像对。同一个空间点在左图和右图上的成像位置存在水平偏移这个偏移量就是视差视差和深度之间隔着一条简单的几何公式。网络在训练时压根不需要知道真实深度只要能准确估计视差并用视差把左图重建成右图、把右图重建成左图重建得越像说明视差估计越准。推理阶段又完全不需要右图只需要单目图像就能输出视差图换算成深度图即可。论文里真正有价值的创新除了图像重构监督之外还加了一个左右一致性约束。它要求网络同时预测的左视差图和右视差图彼此之间保持几何上的一致。这个约束看着简单却在纹理稀疏、遮挡严重这些难搞的区域里提供了额外的正则信息让深度图的质量明显提升。整篇论文的思想后来被很多工作继承比如用视频时序替代双目的做法本质上就是把它“相邻视角一致性”的思想从空间换到了时间上。2. 左右一致性方法的核心设计拆解2.1 视差与深度之间只差一个简单公式要理解这个方法先要把双目几何的基础捡起来。理想情况下左右相机经过立体校正后成像平面是对齐的同一个空间点只会在水平方向上有坐标差不会产生垂直方向的偏差。这个水平坐标差 d 就叫视差单位是像素。深度 Z 和视差 d 的关系写出来非常简洁Z f * B / d其中 f 是焦距B 是左右相机光心之间的距离也就是基线长度。所以网络预测视差图本质上就等于预测了深度图中间只差一个固定换算。这也是为什么这篇论文把输出定义为视差而不是深度——直接回归深度的话不同场景的尺度变化太大网络很难稳定收敛回归视差则天然落在像素级坐标范围里更容易优化也方便直接用图像重构来做监督。不过要提醒一下这里的 f 和 B 都是标定出来的常量。如果双目相机没有做好内参标定和极线校正f 和 B 不准确算出来的深度自然也不准。很多人在自监督训练时觉得模型效果差其实问题不是网络结构而是相机标定本身就已经带歪了。2.2 图像重构损失L1 和 SSIM 需要的不是二选一有了视差图怎么把它变成监督信号核心操作叫做可微图像重构。以左图为例输入左图 I_left网络预测出左视差图 d_left。对左图上任意像素 (u, v)根据视差找到它在右图上对应的像素位置 (u - d_left, v)然后从右图把这个位置的像素值取回来就能得到一张由右图重构出的左图。如果这张重构左图和原始左图越接近说明视差越好。训练过程就是不断最小化这个重构差异。那重构差异怎么度量只看像素值差距是最直观的但实操里会出问题。L1 或者 L2 损失只照顾单个像素的颜色差异对局部结构不敏感训练到后期很容易把边缘磨平输出一张过度平滑、边界含糊的深度图。只用 SSIM 又会跑向另一个极端它对结构信息很敏感但对像素值全局差异不够敏感容易出现过于锐利的边缘和局部噪点。所以论文采取了一个很实在的做法把两者拧在一起。一个地方重构对了L1 和 SSIM 同时下降一个地方边缘没对齐SSIM 会提供强梯度一个地方颜色偏了L1 会负责拉回来。常见的组合形式是 0.85 倍的 SSIM 损失加上 0.15 倍的 L1 损失这个比例后来几乎成了自监督深度估计的默认配置。我自己的经验是0.8 到 0.9 之间的系数对最终结果影响不算太大真正需要小心的反而是遮挡区域的处理。2.3 遮挡处理别让不存在的对应关系破坏梯度双目图像里有一个特别容易忽略但非常要命的物理现象遮挡。左图里的某些像素在右图里根本找不到对应点。比如一个前景物体挡住了后面的背景物体左侧的边缘区域在右眼里就看不到或者前景物体自身的左右边缘在另一只眼里被自己挡住一部分。这些区域如果强行做图像重构网络会被梯度逼着去“编造”一个颜色来凑损失结果就是深度图上出现一片糊掉的灰雾。论文的思路不是去发明一个更强的损失去硬扛遮挡而是直接把遮挡区域从监督里排除掉。怎么判断哪里被遮挡可以用左右一致性来检查如果一个位置从左到右的视差和从右到左的视差对不上说明这里大概率是遮挡或可信度低的区域就在重构损失里把对应像素的权重置零。这个处理看起来只是一个小细节实际上影响非常大。不加遮挡处理的版本深度图边缘重影很严重加了之后边缘立刻干净一大截。实操中除了用左右一致性算遮挡还可以用“重构图与输入图分歧程度”来算动态掩码。比如把左图按视差重投影回左图如果和原始左图差异太大的像素就说明它处于歧义区域。不同实现里掩码的细节不一样但目标都是同一个不要让错误对应关系参与反向传播。2.4 左右一致性约束让几何信息互相兜底图像重构损失从外观上约束了视差估计但外观相近的区域并不能提供足够的几何信息。比如一面白墙水平移动几个像素颜色差异很小单纯靠重构损失很难把视差固定住。这个时候左右一致性约束就起到关键作用。网络会同时输出左视差图 d_left 和右视差图 d_right。理想情况下左图像素 p 根据 d_left 移动到右图位置 q 后q 处对应的右视差 d_right 也应该能把 p 映射回来。也就是说把 d_right 按左视差重新采样到左图视角之后应该和 d_left 几乎一模一样。左右一致性损失就是度量这两者的差异。这个约束之所以好用是因为它把左右两边的几何信息强制对齐。纹理稀疏区域重构损失缺乏梯度但可以从周围可靠区域把几何信息传播过来因为左视差的预测和右视差的预测共享同一个卷积编码器两边特征相互影响。很多复现实现只用重构损失效果还能看一旦加上左右一致性视差图的连续性、边缘准确性都会上一个台阶。这也是论文标题把 Left-Right Consistency 放在最前面的原因。3. 网络结构与训练配置里的关键细节3.1 预测双视差图的编码解码结构讲完损失再看网络结构。论文采用的是典型的编码器-解码器结构输入是一张左图输出是一张通道数加倍的视差图前半部分是左视差后半部分是右视差。编码器负责从图像里提取语义和结构特征通常使用在图像分类任务上预训练好的卷积骨干网络解码器负责把特征逐步恢复到原图分辨率中间大量使用跳跃连接把编码器低层的边缘细节跳接到解码器对应尺度上。这个结构选择非常有针对性。如果只用普通编解码网络高层语义信息能保留但低层纹理细节会在下采样过程中丢失深度图的边缘就会模糊。加入跳跃连接之后浅层的梯度方向信息和解码层的语义信息拼在一起边缘细节明显改善。也可以理解为先让网络知道“这里是什么物体”再让网络知道“物体的边界在哪里”两条信息缺一条都会让深度图发虚。输出层有一个容易被忽略的细节网络最终输出的是原始数值需要一个激活函数把值约束在合理范围内。论文用了 sigmoid 把输出压到 0 到 1 之间再乘一个预设的视差尺度让视差图落到数据集的合理像素范围内。这个转换看起来简单但对训练稳定性很关键。如果不加约束直接让网络输出任意大小的视差训练前期很容易出现极端大视差导致采样坐标飞出图像边界损失直接变成 NaN训练直接崩掉。3.2 输出层、视差尺度与数值稳定性视差尺度怎么定要看数据集的实际情况。户外自动驾驶双目场景物体距离远视差范围一般可以达到一两百像素甚至更多室内近景双目视差可能只有几十像素。论文在训练时用一个固定的最大视差来缩放 sigmoid 输出相当于给网络设了一个先验上限。这个上限定了之后网络在区间内部自动学习视差的相对分布。复现的时候把这个上限设得太大并不好。因为可视范围虽然变大了但像素级的重构误差对真实大视差值不敏感网络可能只学会区分远近趋势却学不出精细的距离差异。设得太小又会截断真实视差近距离物体会直接溢出。我一般会根据验证集上的真值视差分布选一个大约覆盖 95% 像素的值训练过程中再观察视差直方图是不是顶到边界如果顶到了就说明尺度设置偏小。3.3 三块损失怎么配权重整个训练损失由三块组成图像重构损失、平滑损失、左右一致性损失。重构损失是最主要的监督来源平滑损失负责抑制离散噪点左右一致性负责几何对齐。三者的权重配比会影响最终深度图性格。平滑损失这里需要具体展开。论文用的是边缘感知的正则项公式本质上是对视差图求梯度但在图像边缘强的地方把惩罚力度调小避免把物体边界也一起磨平。可以把它理解成在纹理少的区域希望视差变化缓慢一点因为那些区域大概率是同一个平面在物体边界处图像梯度很大就应该允许视差发生跳变。根据我的复现经验三个损失之间容易打架。平滑权重如果给得太大深度图会呈现出一种“切片蛋糕”式的分层感每一层都平整但层与层之间过渡突兀。重构权重如果压得太低模型会越来越不关心边缘细节深度图看起来像一张做了模糊处理的老照片。一般可以从重构损失为 1.0、平滑损失 0.1~0.5、左右一致性损失 0.5~1.0 这个量级开始调然后看损失曲线和可视化中间结果做调整。4. 从零复现时最值得注意的实操步骤4.1 双目数据采集与极线校正论文实验用的数据集是自动驾驶场景的户外双目数据集左图和右图已经做过立体校正图像行是对齐的。但这件事在实际项目里并不总是成立的。自己采集双目数据时最重要的一步就是极线校正。如果左右相机存在垂直位移没有消除网络要学习的视差就不只是水平分量模型会产生系统性误差怎么调损失函数都救不回来。我的建议是所有自制双目数据在进入训练之前统一用标定板做立体标定标定出左右相机内参、畸变系数和相对外参再用这些参数对每一帧图像做极线校正和重投影。校正之后可以跑一遍双目匹配的可视化验证用简单的特征点匹配检查同一特征点是否落在同一行。如果行号不一致说明标定有问题不能直接拿去做自监督训练。另外还有一个很容易翻车的点是左右相机颜色一致性。同型号摄像头在出厂时也可能存在白平衡差异左右图整体亮度不一致。重构损失无法区分几何误差和颜色误差网络会试图用视差变化去补偿颜色差异结果就会出现深度图上偶发的条纹和伪影。一个低成本解决办法是在训练时把图像转成灰度图或者先做一遍左右颜色直方图匹配让颜色分布接近再进网络。4.2 数据增强里的左右关系双目自监督的数据增强和普通图像分类不太一样因为左右图和视差图之间存在严格的几何对应关系。最常见也最有效的增强是水平翻转但翻转之后左图会变成原来的右图右图变成原来的左图左右视差通道也要跟着交换。如果实现时只翻转了输入图像没有交换输出视差通道相当于给网络喂了一组错乱标签深度图会直接左右颠倒。裁剪增强同样要小心。左右图必须使用完全相同的裁剪位置保证同一空间点还在同一行、同一列对应关系不变。如果左右图裁剪框没有对齐相当于人为制造了校正误差。随机颜色扰动类增强在做单目前向推理时问题不大但在双目光度一致性上要谨慎扰动太强会破坏重构损失的假设前提。4.3 可微采样坐标符号与归一化问题重构损失的核心操作是可微采样。用视差来变形采样网格把右图重新映射到左图视角。几乎所有主流自动微分框架都有现成的双线性采样函数但不同框架对采样坐标的约定不太一样麻烦就出在这里。具体来说采样函数通常接收归一化坐标左上角是 -1右下角是 1而像素坐标是以 0 到 H-1、0 到 W-1 计数。两者之间要做转换。再加上视差的方向到底是“在采样坐标里减去视差”还是“加上视差”取决于你自己对左视差、右视差的定义。复现项目里经常看到深度图整体左右翻转或者整体偏离几个像素的情况绝大多数是坐标系没对齐或者 center offset 设置错了。我的做法是单独写一个 warp 函数里面统一完成像素坐标到归一化坐标的转换再进入采样函数。同时写一个白盒单测用一个人为构造的简单视差图比如全图常数视差验证重构出来的图像是否恰好平移了对应像素数。这个单测跑通之后再去训练可以省掉大量排查时间。4.4 损失函数最小结构伪代码为了方便说明我把训练过程写成一份伪代码。实际实现时用哪个框架关系不大核心逻辑都差不多。# 伪代码仅展示结构具体采样函数名称以所在框架为准 def training_step(left_img, right_img, disp_net): disp_left, disp_right disp_net(left_img) # 输入左图输出左右两张视差图 left_hat warp(right_img, disp_left, directionto_left) right_hat warp(left_img, disp_right, directionto_right) photo_left 0.85 * (1 - ssim(left_img, left_hat)) 0.15 * abs(left_img - left_hat) photo_right 0.85 * (1 - ssim(right_img, right_hat)) 0.15 * abs(right_img - right_hat) photo_loss photo_left.mean() photo_right.mean() smooth_loss edge_aware_smooth(disp_left, left_img) edge_aware_smooth(disp_right, right_img) consistency_loss abs(disp_left - warp_right_to_left(disp_right, disp_left)).mean() # 遮挡掩码把一致性差的像素从重构损失里扣掉 photo_loss apply_occlusion_mask(photo_loss, consistency) return photo_loss 0.1 * smooth_loss 1.0 * consistency_loss这段伪代码里最关键的就是各个 warp 方向最容易写错。我建议把整个训练流程拆成独立的小函数前向、重构、左右一致性分别做单测确认无误后再拼起来否则训练出现异常时根本不知道是哪个环节的问题。5. 复现过程中容易踩的坑与排查思路5.1 遮挡区域扩散成灰雾训练一段时间后我经常遇到一个现象损失曲线在下降重构图像看起来也说得过去但深度图在前景物体边缘附近总有一片灰蒙蒙的雾状区域。排查下来问题基本都出在遮挡掩码上有些实现没有正确地排除遮挡像素或者在计算掩码时把顺序搞反了。遮挡掩码的计算逻辑通常是利用左右一致性|d_left - d_right_warped| 超过一定阈值的像素被视为不可信。但这个阈值设多大很关键。设小了遮挡区域没有被完全排除设大了会把大量正常像素也丢掉导致纹理稀疏区域没有监督信号。调试时最好的办法是直接把这个掩码可视化输出叠加在输入图像上看看它是否恰好盖住了被遮挡的边缘区域。肉眼能直接判断的地方就不要靠猜。5.2 纹理稀疏区域变成无意义的值天空、白墙、大片光滑地面是自监督深度估计的天然难点。以我自己的测试结果来看即使加了平滑损失这些区域也容易出现两种毛病一种是变成过度平滑的平面毫无细节另一种是出现随机噪声和条纹。想进一步改善的话可以尝试把平滑损失做得更强或者在损失里加入图像梯度权重的归一化处理。还有一种思路是把多尺度预测加进来在解码器的每一个分辨率上都做重构损失和视差输出强制网络在低分辨率尺度上先建立起合理的结构再逐步细化。这个做法在不少后续改进工作中都被证明有效。5.3 预测值没有绝对尺度评估前先对齐无监督方法预测的是相对距离关系并不包含真实物理尺度。同一个场景模型可能预测出正确的远近层次但整体深度数值比真值小一半或者大一倍。这个尺度不确定性和相机参数、网络结构、训练数据范围都有关系。所以拿着模型输出和激光雷达真值做定量评估的时候千万不要直接算 RMSE那样误差一定大得离谱。要做对齐后再评估最常见的对齐方式是取预测值和真值有效像素的中值比例做整体缩放或者用最小二乘拟合一个全局缩放系数。对齐之后再看相对误差等指标才能真正反映模型的质量。这不算“作弊”而是无监督方法领域公认的评估规范。5.4 训练震荡和 NaN 问题训练初期出现 NaN 的原因有很多但最典型的是视差输出范围没有限制采样坐标飞出了图像大范围区域导致采样函数在无效位置取到无穷值。解决办法就是第三部分提到的 sigmoid 加固定尺度让视差落在合理范围内。学习率也可能是诱因。双自监督模型的损失曲面相对复杂学习率设太高容易在某个 batch 上产生超大梯度直接冲崩。还有一个隐蔽问题遮挡掩码在计算时出现除零。比如用掩码去加权重构损失时如果某个像素的权重全为零对应位置如果没有做保护就会出现无效值。这个小概率情况在大 batch 下不多见但小 batch 训练时还真能遇到。下表是我自己复现时整理的快速排查清单现象排查方向深度图整体左右颠倒翻转增强时左右视差通道是否交换边缘区域灰雾遮挡掩码是否覆盖被遮挡区域、阈值是否合理大片纹理缺失平滑权重是否过大、是否需要多尺度监督数值普遍偏高或偏低视差尺度上限是否合理、是否做过中位数对齐训练中断出现 NaN视差激活范围、学习率、采样坐标边界左右图亮度差异导致条纹是否做灰度化或颜色直方图匹配6. 这套框架的后续影响与现实落地思路6.1 后续自监督深度估计的继承关系这篇论文定下的很多模块后来几乎成了自监督单目深度估计的标配。图像重构损失、平滑正则、视差一致性这三件套到今天还能在不少最新工作里看到。后续发展最大的变化是有人把输入从双目图像对换成了单目视频序列用相邻两帧图像替代左右图再额外训练一个位姿估计网络来预测相机运动。这个替换听起来很自然但也引入了新的麻烦画面里的动态物体会破坏重投影一致性相机运动估计本身也会有误差。相比之下这篇论文的双目方案反而更干净因为左右相机是刚体固定连接位姿关系已知不需要估计。所以在工程应用中只要能解决双目数据采集问题这个方案依然是风险最低的起点。6.2 在只有一个相机的场景里如何借力如果产品最终形态是单目相机但开发阶段还能拿到双目样机可以分两步走先用双目样机采集数据做自监督训练然后把训练好的网络直接部署到单目相机上。推理时模型只需要左图右图完全不参与计算这一步非常多团队都在用。双目样机的标定、数据采集成本远比激光雷达真值低这也是这个方案最吸引人的地方。我自己在帮一个低成本机器人项目做避障试验时就是这样处理数据的问题。当时拿到的场景里有很多玻璃和镜面激光雷达真值基本无效但双目图像对里这些区域仍然有光度一致性线索训练出来的相对深度虽然不如有监督模型精确但足以支撑基础的障碍物边界判断。6.3 工程落地时的一些替代方向如果连双目数据也拿不到再退一步可以走单目视频序列自监督但需要处理动态物体和位姿估计网络带来的稳定性问题。也可以使用预训练的大模型做单张图像深度估计推理效果不错但模型体积大、计算量大而且大多数面向通用场景训练在垂直场景里不一定非常精准。所以现实里怎么选更多还是看数据条件。手上有高质量双目数据优先考虑这篇论文定义的损失框架只有单目视频再考虑时序方案如果已经确定嵌入式部署又不想自己训练再评估大模型蒸馏和知识迁移的路线。作为系列第二篇的笔记我没有去硬抄论文里的数学细节重点还是把方法拆开讲清楚顺带说一下复现和落地时真正容易绊倒人的地方。如果碰到过和我类似的灰雾、尺度漂移、边缘模糊问题希望上面的排查思路能帮上忙。训练自监督深度模型时损失曲线漂亮不代表深度图真好一定要经常把中间结果可视化出来看眼见为实。