☰
PSMNet全解析:从端到端立体匹配到工程化部署
2026/10/19 10:43:42 网站建设 项目流程

做立体视觉和三维重建这块的老哥,PSMNet 这个名字应该不会陌生。当年我第一次接触它,是在解决双目相机标定后的匹配难题,传统方法在弱纹理和重复纹理区域反复翻车,折腾了很久都提不高精度。后来在调研里看到这篇论文,核心思路就是金字塔立体匹配网络——用空间金字塔池化聚合多尺度上下文信息,再用堆叠沙漏网络对代价体做正则化,最后通过可微的软 argmin 回归视差。说直白点,它让“深度估计”从手工设计相似度度量的老路子,变成了端到端可训练的整体网络。这篇文章我会从架构设计、训练细节、踩坑记录到工程部署,把它们完整过一遍,适合刚入门双目深度估计的实习生,也适合准备把网络落到实际项目里的工程师。

1. 从立体匹配到端到端网络:PSMNet 解决了什么问题

1.1 传统方法为什么难落地

传统立体匹配的老三样是代价计算、代价聚合、视差优化,经典思路包括块匹配、半全局匹配这类手工设计的算法。问题在于,这些方法依赖窗口假设和颜色一致性,一旦碰到无纹理的白墙、周期性重复的栅栏、镜面高光的玻璃幕墙,代价函数就分不清正确匹配和错误匹配了,输出的视差图经常一坨噪点。我当时在实际场景里测试,光照稍微有点变化,误匹配率直接翻两三倍,这种表现根本没法用。

PSMNet 给的解法是换一条赛道:不手工设计相似度度量,而是让网络自己在大量数据里学习“什么样的左右图像素算匹配”。卷积网络天然对光照、纹理这些外观变化有鲁棒性,再加上金字塔池化模块提取的多尺度上下文信息,网络的感受野远大于普通卷积,处理无纹理区域的能力一下子就不一样了。

1.2 两条主线:上下文聚合 + 代价体正则化

理解了痛点,再看网络结构就清楚了。PSMNet 整体由两部分组成:第一部分是特征提取,用共享权值的卷积网络分别处理左右图,再通过空间金字塔池化模块把多尺度特征融合成高维特征描述;第二部分是匹配代价体构建和正则化,把左右特征按视差层级拼接成四维代价体,然后用三维卷积的堆叠沙漏网络对代价体做滤波和正则化,最后用回归算子输出连续视差图。

这两条主线各有分工:特征提取部分是“看懂图像”,让网络理解场景中哪些信息可以用作匹配线索;代价体正则化部分是“判断对应关系”,在可能的多组对应中筛选出空间一致性强、遮挡关系合理的匹配。分开设计的好处是方便做系统性的优化,比如特征部分用残差结构加深网络,正则化部分用沙漏结构做多尺度聚合,哪边有短板就补哪边,而不会像传统方法那样所有环节耦合在一起,牵一发动全身。

2. 网络骨架拆解:四大核心模块

2.1 共享权值特征提取器

网络第一步是对左右视图分别提取特征。这里的关键点是共享权值,也就是说左右图用的是同一套卷积参数。为什么必须共享?因为同一场景下左右图的物理成像规律一致,理想情况下相机拍摄同一物体得到的颜色、纹理统计信息是近似的,如果分开训练两套特征提取器,等于是要求网络额外学习左右相机之间的系统性差异,既增加过拟合风险,又浪费参数。共享权值等于把“空间同态”这个先验直接放进了网络结构里。

实际实现上,特征提取常用基础残差模块堆叠。每层逐渐扩大通道数,特征图尺寸逐步下降,这样可以扩大感受野。我在项目里看到过几种不同深度的标配实现,比较常见的是用前期几个小步长卷积处理原始输入,接着接入若干个尺度变化的残差块,输出特征图的通道数一般在 16、32、64 这样逐级增长。值得留意的是,特征图的空间分辨率不会压得太低,因为后续要构建代价体,视差维度的计算量和输入尺寸直接相关,如果一开始就把特征图缩得太小,亚像素级别的位置信息就丢失了,最终视差图的边缘细节会变糊。

2.2 空间金字塔池化模块

特征提取之后接的是空间金字塔池化模块,这是 PSMNet 网络名字里“金字塔”的由来。它的思路很直白:网络像人看图片一样,既需要近处细节来确定边缘与纹理,也需要远处整体轮廓来判断物体到底是一个平面还是连续曲面。用一组不同池化核大小的分支,分别对特征图做下采样和上采样,再把各分支的结果与原图特征拼接在一起,这样每个输出位置都能同时看到不同范围的上下文信息。

我在理解这个模块时习惯于用一个类比:把特征图想象成一张地图。你找一条路时,既会看眼前 50 米的路况,也会切换到 1 公里比例尺看整体走向,还需要 5 公里比例尺判断这条路人流量大的原因是什么。空间金字塔池化干的就是这件事,通过不同池化窗口把不同观察尺度的信息“压缩”后塞进同一个特征表示里。到具体实现时,这里面最影响效果的细节是池化后上采样使用的方式——如果直接双线性插值,虽然简单但会丢失部分高频纹理,如果换成转置卷积之类的可学习上采样,拟合能力更强但参数量更高,我在实际项目里会根据训练数据规模在这两者之间权衡。

2.3 代价体构造与视差假设

有了左右图的高维特征,就要开始度量匹配代价了。立体匹配的核心假设是,空间中的同一个三维点在左右图中的位置满足极线约束,而视差就是这个点在左右图横坐标上的差值。网络要做的事情是穷举可能的视差范围,对每个候选视差构造一个匹配代价,最后选出正确的那个。

PSMNet 构造代价体的方式是这样的:预设一个最大视差,把所有视差候选值从 0 到最大值逐层遍历。对每个候选视差,把左图特征向x方向平移对应像素,与右图特征沿通道维度拼接,这样所有视差假设的拼接结果堆叠起来,就形成了一个四维代价体。维度分别是高度、宽度、视差层数和通道数。

这个部分的操作不复杂,难点在于代价体实在太占内存了,这也是很多初学者一训练就爆显存的最直接原因。假设一张输入图的特征图分辨率是四分之一原图大小,最大视差取 192,那么代价体的视差维度就小不了,再加上特征通道往往有几十层,这个张量的形状非常可观,对显存有硬性要求。我在挑选硬件参数时的一个重要经验是,先用小的代价体跑通流程,再逐步加大视差范围和 batch size,避免一开始就把内存通道塞满然后不得不回退设计。

2.4 堆叠沙漏构造与视差回归

代价体虽然携带了大量候选匹配信息,但它本身噪声很大,同一个位置可能存在多个响应相近的视差候选。直接取响应最大值虽然简单,但抖动剧烈,且没有利用空间上下文来约束相邻像素的视差平滑性。PSMNet 使用堆叠的沙漏结构(三小时漏堆叠)作为代价体正则化模块,这个结构在三维空间里做多次下采样和上采样,让网络在不同空间尺度上学到“什么样的视差分布才是合理的”。

堆叠的意义值得多说一句。单个沙漏能把局部信息聚合起来,但只跑一遍往往尺度有限。堆叠多个沙漏相当于让网络反复修正预测,第一遍输出一个粗糙的结果,后一遍基于上一遍的结果做精细校正。这种反复细化的策略,配合中间层深度监督,训练起来梯度可以传到更浅层,有效减缓了梯度消失问题。

正则化后的最终输出是每个视差候选的概率分布,通过一个可微的 soft argmin 操作回归连续视差图。实际做法是把概率分布视为分类得分,对全部视差候选做加权平均得到亚像素精度的视差值。这里有个关键细节:视差回归的输出是连续值,所以可以用平滑 L1 损失作为训练目标,而不是传统分类问题常用的交叉熵损失。平滑 L1 在视差接近真值时梯度较缓,在远离真值时梯度饱和,既能收敛稳定,又能保证边界位置的预测比较锐利。

3. 训练一条 PSMNet 模型:数据、损失、优化

3.1 数据准备与预处理细节

训练立体匹配网络和训练分类网络,数据流程上的差异非常大。分类网络只需要图像和标签,PSMNet 需要的是“左图、右图、视差真值图”三元组。真值视差图一般由激光雷达采样加后处理生成,原始激光点云稀疏,需要做插值、噪声过滤和边界裁剪。我在项目里体会最深的是,真值质量对最终效果的影响往往大于网络结构,在边界处如果真值不准,模型会对边缘区域产生很强的误导,所以宁可牺牲覆盖率也要保证标注区域可靠。

数据增强方面,常用且有效的操作包括左右图一起随机翻转、随机裁剪到同一尺寸、颜色抖动以及亮度扰动。左右图必须保证一致性,裁剪和翻转如果只对单边操作则会破坏极线约束。训练时我一般会保持原图宽高比,把尺寸调整到适合网络输入的目标尺寸,再配合随机裁剪增加样本量。对初学者而言,最容易忽略的是归一化参数要按整个数据集的统计量计算,而不是拍脑袋随便填一个。

3.2 损失函数与评价指标

PSMNet 训练用的主要损失函数是平滑 L1 损失。预测的视差图与真值图在有效像素上算差值,小于阈值时用二次函数,大于阈值时用线性函数,这样兼顾了收敛速度和鲁棒性。由于网络堆叠了三个沙漏模块,训练时会对三个阶段的输出分别计算损失并相加,这属于深度监督——中间阶段也能收到梯度信号,让浅层学习速度更快。

评价指标方面,工程上最关心的是端点误差,也就是预测视差与真值视差之间的平均绝对差值,单位是像素。另一个高频指标是误差率,通常把视差误差大于某阈值或大于真值一定比例的像素视为错误像素,错误像素占比就是误差率。不同场景对这两个指标的容忍度差异很大,比如高精度工业测量可能要求端点误差在亚像素级别,而辅助驾驶避障只需在低频上保持误差较低即可。我在复现实验时,强烈建议在训练期间用验证集实时监控这两个指标,不要只盯着损失曲线——损失和业务指标有时并不完全同步。

3.3 训练策略:分批训练与学习率调整

PSMNet 尝试训练时候比较常见的坑是直接全量训练一批报错,原因是显存被庞大的代价体吃满了。我常用的做法是视差范围从小到大渐进训练:先在较小视差范围内训练几十个周期让网络学到稳定的基础匹配能力,再逐步扩大到完整视差范围,这样显存压力小,训练过程也稳。batch size 不要一开始就调到很大,通常先试用小 batch 跑通前向反向链路,再用梯度累积的方式模拟大 batch 效果。

学习率调整我用的是分阶段下降策略:前一部分用较大的学习率让损失快速下降,后面分别在总训练进度过半和接近尾声时把学习率调低一个数量级。相比余弦退火,这种手工分段策略在立体匹配任务上更可控。优化器一般选择带权重衰减的变种优化器,权重衰减不要设太大,否则网络拟合能力会大打折扣。训练过程中如果发现验证误差在某一段反复震荡不降,不妨把学习率临时调到更低然后重新拉起来,这个操作在我这里经常能续命。

4. 实操中的常见坑与排查经验

4.1 显存爆掉:从 batch size 到梯度检查点

显存不足是 PSMNet 新手训练时遇到概率最高的问题,这句话我说得毫不夸张。四维代价体把张量体积撑得很大,再加上堆叠沙漏里的三维卷积中间变量也不少,单卡训练大分辨率图像几乎是不可能的。

排查思路分几步。第一步看 batch size,先降到 1 看看能不能跑通,如果 1 也爆,说明是模型本身占用的显存超出了单卡容量;第二步看代价体的分辨率,把最大视差从 192 降到 96,显存占用大约能少一半,这是非常直接的手段;第三步如果还想保住大视差范围,可以考虑开启梯度检查点功能,用计算换显存,在反向传播时不保存所有中间激活值,而是重新计算一遍,这样对训练速度有一定影响,但显存会明显降下来。

4.2 训练发散:数值不稳定怎么办

我遇到过几次训练发散的情况,典型表现是损失在某个 epoch 突然跳到极大值,再之后就再也回不来了。仔细排查后,发现原因往往不是代码逻辑错误,而是数值稳定性问题。

第一个常见根源是学习率过大,导致更新量迅速放大。这类问题多见于使用大 batch 之后,如果学习率没有同步调整就会失控。处理方案是降低学习率并用梯度裁剪,我一般将梯度裁剪阈值设在 10 左右,虽然看起来宽松,但在大部分情况下足够阻止爆炸。第二个常见根源是代价体中的视差范围与实际图像不匹配,如果图像里物体的真实视差远小于预设最大值,代价体在视差方向大部分区域都是空白,soft argmin 对噪声敏感,训练起来就容易抖。这种情况下,重新标定图像或把视差范围收紧,效果往往立竿见影。

4.3 边缘噪点多:后处理与损失设计

即使训练收敛得很好,视差图上仍然可能有一些细碎的噪声点,尤其是在遮挡边界、反射表面和透明物体区域。我一开始以为是模型容量不足,后来才发现是损失函数对整个图像一视同仁。实际上,遮挡区域的像素根本没有正确匹配,真值标签通常也是无效的,这些区域不该被计入损失。在数据预处理阶段,把无效像素从损失计算中排除,是提升边缘质量最有效的一步。

另一个处理手段是左右一致性检测。在推理阶段,同时计算左图和右图的视差,然后检查左右视差是否互相一致,如果不一致则判定为遮挡或不可靠区域,将其置为无效。这个过程放在部署阶段做后处理非常有效。还有一些项目会直接在损失里添加边缘感知权重,让网络更重视梯度较大的边界区域。我在实践中认为,正确排除无效像素是必须做的,边缘感知权重则是锦上添花,如果你训练时间紧张,优先保证第一点。

5. 部署与工程化落地经验

5.1 模型裁剪与量化

训练完的 PSMNet 模型直接部署到实际产品里,往往会面临算力和功耗的压力。我常用的优化思路是先做结构裁剪,再考虑量化加速。结构裁剪不是临时设计,而是从训练阶段就预留剪枝空间。比如把沙漏模块的通道数按比例缩小,用更窄的特征宽度换取更高的推理帧率,这样甚至不需要重新训练,而是在训练时就训练一个更小的结构。

量化是另一种有效做法。把网络里的权重从浮点数缩到较低位宽的整数,可以有效降低模型体积和计算量。但直接量化常常会导致视差精度明显下降,尤其是边缘区域,因为匹配代价体对数值精度很敏感。我踩过这个坑,解决方案是先做校准,用少量训练样本统计激活值范围,再对敏感层做混合精度处理,保留关键层的浮点计算,只量化卷积部分,这么折中下来推理速度提升明显,精度损失能控制在可接受范围内。

5.2 推理优化与硬件适配

工程落地上,最常见的场景是配合嵌入式平台或者专用加速芯片运行。这些设备对内存带宽和并行度都有严格要求,直接跑标准网络通常帧率不够。我调整过的方式包括:把输入图像缩小后先算一次低分辨率视差,再用这个结果做先验,在高分辨率阶段只在小范围内搜索精细化视差。这个两级方案能让运算量大幅下降,同时尽量保住最终视差图的精度。

硬件适配时还要注意算子和实现库的版本差异,同一个卷积在桌面级独立显卡上表现很好,搬到专用加速芯片上可能因为算子没优化而变慢。如果发现某个模块是瓶颈,可以考虑用等效结构替换掉,比如把某些三维卷积改成低秩近似,或者利用硬件擅长的并行算子来重写。这些优化往往要结合运行时的性能分析工具逐层看耗时,不要一开始就整体重写网络。

5.3 应用场景:3D 重建与避障

PSMNet 的典型应用场景是双目视觉测距与三维重建。在移动机器人和辅助驾驶场景里,利用双目相机获取视差图,再根据相机内外参数把视差投影为深度图,就能得到环境的三维点云。与激光雷达相比,双目方案成本低、可以获得稠密深度,弱点是深度精度受基线和分辨率限制。我做过的一个模拟项目里,使用 PSMNet 输出视差图再融合生成点云,后续用于地面凹凸检测,在光照稳定的室内环境下效果相当不错,改到室外强光下就需要加入曝光对齐预处理。

另一个常见的延伸是三维场景理解,比如把视差图当作额外通道输入给目标检测或语义分割网络。多一个深度的几何信息,下游算法对遮挡目标的识别能力确实有提升。我也见过一些工业机械臂抓取场景,使用 PSMNet 估计目标物体的三维形状,精度要求不高、但要求实时性,这时候一般会对模型做较大幅度的轻量化,把标准 PSMNet 的通道数减半甚至减到四分之一,再配合专用推理框架,帧率就能跑到实时可用。

6. 后续扩展与个人体会

6.1 PSMNet 的变体思路

PSMNet 面世之后,后续很多工作都围绕它的几个痛点做改进。比较大的方向是代价体压缩,用分组相关等操作替代拼接方式来构建代价体,显著降低内存占用,同时保持精度。另外一种是使用更高效的沙漏替代结构,比如用注意力机制替代部分三维卷积,减少计算量。这些变体的思路普遍都是打基础网络的瓶颈,如果你已经熟悉了 PSMNet 本体,再去接触这些变体时就不会觉得是全新的网络,很多组件都能一眼认出来,学习成本会低很多。

针对特定场景,我还会自己改输入结构,比如在原有左右图两个输入通道之外,额外加入一个经过边缘增强的预处理通道。这个小改动在工业零件边缘匹配的项目中精度提升了几个百分点。所以学习和使用 PSMNet 时,没必要刻板照搬原版,把每个延展模块理解清楚,改成适合自己的才是最有价值的部分。

6.2 我的一点经验之谈

做了一年多立体匹配相关项目之后,我最大的感受是,PSMNet 真正的贡献不在于刷了几个指标,而是它把传统立体匹配里的瓶颈模块全部替换成了可学习结构,并为后续一系列基于代价体的方法打下了框架基础。无论做研究还是做应用,理解四维代价体在显存和精度之间的权衡,理解空间金字塔池化对无纹理区域的提升,理解堆叠沙漏对边缘一致性的作用,这些认知都能迁移到很多其他视觉任务里。

如果你现在正准备选型一个立体匹配网络来落地,我建议不要一上来就追求完美复现论文,先跑一个简化版本,确认数据和评测流程没问题,再逐步叠加模块。这个思路速度快、调试容易,也适合团队里帮新人快速进入状态。踩过的坑早一点踩透,后面的路就顺很多。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询