3D双目感知深度估计之PSMNet解读

这篇具有很好参考价值的文章主要介绍了3D双目感知深度估计之PSMNet解读。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

3D视觉感知之双目深度估计PSMNet: Pyramid Stereo Matching Network

论文地址: [1] Pyramid Stereo Matching Network (arxiv.org)

代码地址: JiaRenChang/PSMNet: Pyramid Stereo Matching Network (CVPR2018) (github.com)

Git链接: 计算机知识汇总

课程来源 : 深蓝学院-环境感知

1. 背景

3D感知任务相比于2D感知任务的情况更为复杂,而相比于单目相机双目相机的感知能力拥有以下几个特点:

  • 优点
    • 双目感知无需依赖强烈的先验知识和几何约束
    • 能够解决透视变化带来的歧义性(通俗的讲就是照片是由3D真实世界投影到2D图像然后再转换成3D,由于深度信息的丢失本身就十分困难)
    • 无需依赖物体检测的结果,对任意障碍物均有效
  • 缺点
    • 硬件: 摄像头需要精确配准,车辆运行过程中同样需要保持
    • 软件: 算法需要同时处理来自两个摄像头的数据,计算复杂度较高



而双目相机是如何实现3D视觉感知的呢?如下图:

B : 两个相机之间的距离

f : 相机的焦距

d: 视差(左右两张图象上同一个3d点之间的距离)

z: 物体相对于相机的深度,也是我们需要求解的值。

基于双目相机深度估计算法,计算机视觉,3d,数码相机,计算机视觉


根据几何的知识我们可以得到 视差d 与 深度z是成反比的,所以双目相机的3D感知其实就是基于视差的估计来的,那么接下来核心来了,我们应该怎么得到每个像素点的视差呢?PSMNet 横空出世,它是一个利用端到端的卷积神经网络学习如何从输入的pair图像中获取每个像素点的视差
基于双目相机深度估计算法,计算机视觉,3d,数码相机,计算机视觉


PSMNet 在原文中提到了以下几个亮点:

  • 端到端无需后处理的双目深度估计方法
  • 利用空间金字塔池化模块(SPP)和空洞卷积有效地整合全局上下文信息,从而提高了深度估计的准确性。
  • 采用三维卷积神经网络(3D CNN)stacked hourglass 对cost map进行正则化处理,进一步提高了深度估计的精度。
  • 使用堆叠多个hourglass网络,并结合中间监督,进一步优化了3D CNN模块的性能。


2. 网络结构

基于双目相机深度估计算法,计算机视觉,3d,数码相机,计算机视觉

整体的网络结构如上图所示,首先网络的输入是成对的双目相机拍摄出来的左右片,通过一系列权重共享的特征提取网络提取特征,然后叠加构建costmap,然后经过一个3D卷积最后通过上采样恢复到原始输入大小的特征图即可。此文将网络结构分成4个模块然后会分别进行介绍


2.1 特征提取

第一个CNN模块,比较简单就是一个带残差模块和空洞卷积的卷积神经网络,将原始的H * W * 3 (kitti数据集里是375 X 1242 * 3)的图像下采样至 H/4 * W/4 * 128。所以整体的分辨率降低了4倍。

基于双目相机深度估计算法,计算机视觉,3d,数码相机,计算机视觉

第二个是SPP模块,这里可以看到下面有4个branch这里用的就是4个不同大小尺度的averagepooling,去收集不同分辨率下的局部的信息,然后通过双线性插值恢复到原始图像的1/4大小,然后与输出进SPP网络的原始输入进行拼接,这样SPP网络最后的输出就整合了全局(CNN网络的输出)以及局部(4个branch的pooling)的信息。

基于双目相机深度估计算法,计算机视觉,3d,数码相机,计算机视觉
PS: 这里查了查相关资料有关于AveragePooling和MaxPooling的区别,主要来说如下:

  • AveragePooling: 更有利于保留图像背景信息,一般用在
    • 当你需要用整合局部的信息的时候就用针对于像素级别的任务比如说分割任务
    • 下采样倍数较大的深层网络
    • 需要保留细节特征,利用全局的信息
  • MaxPooling:更有利于保留图像纹理信息,相当于做了特征选择,选出了辨识度更好的特征,一般用在
    • 需要边缘的信息的任务,比如说分割类的任务


2.2 构建Cost Volume

在介绍构建Cost Volume之前,这里还需要估计引入一个概念就是视差的范围:前文提到计算深度就是匹配视差其关键在于计算匹配误差,即对于对于左视图的同一个物体我们只要找到其右视图的水平方向偏移的像素点,我们就可以知道其深度。因此接下来的几点是一个重点:

  • 由于需要感知的深度范围有限,所以我们需要感知的视差的范围也是有限的(eg, 相机的深度范围是1 - 100m,对应的视差范围可能是1-10个pixel)因此对于视差我们就在可能的视差范围内搜寻值就可以了
  • 对于每一个可能的视差(范围有限上一点提到的1-10个pixel),计算匹配误差,因此得到的三维的误差数据称为Cost Volume。
  • 计算匹配误差时考虑像素点附近的局部区域(提取邻域的信息),比如对局部区域内所有对应像素值的差进行求和
  • 通过Cost Volume可以得到每个像素处的视差(对应最小匹配误差的𝑑𝑑),从而得到深度值。



好的有了以上的观点我们就可以继续回到PSMNet的costVolume 的构建了。

基于双目相机深度估计算法,计算机视觉,3d,数码相机,计算机视觉

由2.2部分的输出的左右图像分别大小分别是H’ * W’ * C(其实已经下采样到了1/4) 然后对D(程序中是192)个可能的视差范围将左右的特征图重合的部分拼接,不足的部分padding0,从而得到一个新的4维的特征张量H’ * W’ *D * 2C 。

这里的含义可以看成拼接后的特征图同一个物体的cost比较小,不同的物体差异较大。所以就是计算左右特征图的在left[i:]与right[:-i]的相似度。

基于双目相机深度估计算法,计算机视觉,3d,数码相机,计算机视觉

对应的代码如下:

        #matching
        cost = Variable(torch.FloatTensor(refimg_fea.size()[0], refimg_fea.size()[1]*2, self.maxdisp//4,  refimg_fea.size()[2],  refimg_fea.size()[3]).zero_()).cuda()

        for i in range(self.maxdisp//4): # 0 - 47 
            if i > 0 :
             cost[:, :refimg_fea.size()[1], i, :,i:]   = refimg_fea[:,:,:,i:] # LEFT 
             cost[:, refimg_fea.size()[1]:, i, :,i:] = targetimg_fea[:,:,:,:-i] #RIGHT 
            else: # i == 0
             cost[:, :refimg_fea.size()[1], i, :,:]   = refimg_fea
             cost[:, refimg_fea.size()[1]:, i, :,:]   = targetimg_fea
        cost = cost.contiguous()


2.3 三维卷积

对于一个4D(H’ * W’ *D * 2C)的输入,作者采用了两种三维卷积的机制分别是basic 和Stacked hourglass,且其作用均为对比左右特征图的在同一个位置的视差差异。

2.3.1 Basic

基于双目相机深度估计算法,计算机视觉,3d,数码相机,计算机视觉

这个结构就是多层跨链接的3D卷积,且卷积核为(3 * 3 * 3),可以看出其利用到了每个像素点周围邻域的信息(空间信息)也利用到了多个视差的信息,所以相比于只对比一个视差更加鲁棒。最后同一个线性插补上采样恢复原始分辨率,从而计算每个像素的深度值。下列是basic模块的代码实现


2.3.2 Stacked hourglass

基于双目相机深度估计算法,计算机视觉,3d,数码相机,计算机视觉

这是作者提出一个相比于basic更加复杂的结构,是一个堆叠3次的hourglass结构,同样的这种hourglass的结构有3个好处:

  • 能获取不同感受野的信息
  • 利用skip连接可以在不同以及自身的结构内传递信息,更加鲁棒
  • 与basic只有一个输出不同,stacked hourglass 在每个hourglass 结构都接了一个单独的输出,且在训练阶段加权求和得到总loss(具体权重参考第3部分)
        if self.training: # training 需要加上所有的cost 
            cost1 = F.upsample(cost1, [self.maxdisp,left.size()[2],left.size()[3]], mode='trilinear')
            cost2 = F.upsample(cost2, [self.maxdisp,left.size()[2],left.size()[3]], mode='trilinear')

            cost1 = torch.squeeze(cost1,1)
            pred1 = F.softmax(cost1,dim=1)
            pred1 = disparityregression(self.maxdisp)(pred1)

            cost2 = torch.squeeze(cost2,1)
            pred2 = F.softmax(cost2,dim=1)
            pred2 = disparityregression(self.maxdisp)(pred2)

        cost3 = F.upsample(cost3, [self.maxdisp,left.size()[2],left.size()[3]], mode='trilinear')
        cost3 = torch.squeeze(cost3,1)
        pred3 = F.softmax(cost3,dim=1)
        pred3 = disparityregression(self.maxdisp)(pred3)


2.4 视差匹配

这里有两种做法:

  • 硬分类: 直接取cost最小的视差值作为输出,但是这样有个缺点就是如果实际中最小值与第二小的值差别特别小,那么真实的视差应该处于二者之间,所以作者采用了一种软分类的机制。
  • 软分类: 对网络输出的d个cost的值进行加权求和,权重就是输出的cost值,cost值越大权重越小。

基于双目相机深度估计算法,计算机视觉,3d,数码相机,计算机视觉

class disparityregression(nn.Module):
    def __init__(self, maxdisp):
        super(disparityregression, self).__init__()
        self.disp = torch.Tensor(np.reshape(np.array(range(maxdisp)),[1, maxdisp,1,1])).cuda()

    def forward(self, x):
        out = torch.sum(x*self.disp.data,1, keepdim=True) # 加权求和
        return out


3. 损失函数

这里用的就是回归模型里比较常用的smoothL1,其是一种结合了L1和L2 的结合体,不会像L2对离群点敏感且容易梯度爆炸也不会像L1一样在0处不可导。

这里还要highligh一点就是之前提到的stacked hourglass操作里有三个预测头,在训练的时候这三个输出也对应着不同的loss,作者对其进行了调参结果如下:

基于双目相机深度估计算法,计算机视觉,3d,数码相机,计算机视觉

在源代码里体现如下:

        if args.model == 'stackhourglass':
            output1, output2, output3 = model(imgL,imgR)
            output1 = torch.squeeze(output1,1)
            output2 = torch.squeeze(output2,1)
            output3 = torch.squeeze(output3,1)
            # 三个loss 是加权平均 分别是 0.5 / 0.7 / 1.0
            loss =  0.5*F.smooth_l1_loss(output1[mask], disp_true[mask], size_average=True) + \
                    0.7*F.smooth_l1_loss(output2[mask], disp_true[mask], size_average=True) + \
                    F.smooth_l1_loss(output3[mask], disp_true[mask], size_average=True) 


4. 效果

下图是原论文中截至于2018年3月,在kitti2015数据集上的效果,其中All 和 Noc 分别代表了所有像素点和未遮挡像素点的误差。 D1-bg / D1-fg / D1-all 分别代表的是背景/前景/所有点的误差百分比。可以看出效果还是很不错的,效率上由于引入3D卷积的操作时间上可能有待提供高。

基于双目相机深度估计算法,计算机视觉,3d,数码相机,计算机视觉




5. 失败case与提升

基于双目相机深度估计算法,计算机视觉,3d,数码相机,计算机视觉

原因 & 改进:

虽然考虑了邻域的信息但没用考虑高层的语义信息,无法理解场景 -> 用物体检测和语义分割的结果进行后处理,或者多个任务同时进行训练。或者增加注意力机制增加网络对纹理信息的理解提高深度的一致性能。



基于双目相机深度估计算法,计算机视觉,3d,数码相机,计算机视觉

原因 & 改进:

远距离的视差值较小,在离散的图像像素上难以区分 -> 提高图像的空间分辨率,使得远距离物体也有较多的 像素覆盖;增加基线长度,从而增加视差的范围



基于双目相机深度估计算法,计算机视觉,3d,数码相机,计算机视觉

原因 & 改进:

低纹理或者低光照的区域内无法有效提取特征,用于计算匹配误差 -> 提高摄像头的动态范围,或者采用可以测距的传感器





改进方向总结:文章来源地址https://www.toymoban.com/news/detail-807846.html

  • 针对3D卷积的 stacked hourglass 和 深层次的SPP结构 , 会影响整体的效率 -> 一种基于 PSMNet 改进的立体匹配算法,作者提出一种浅层的ASPP和替代stacked hourglass的3个级联的残差结构 从而提高效率。
  • 针对cost volume 的建立,只是直接concat,并没有考虑到相关性 -> Group-wise Correlation Stereo Network , 利用了相互之间的关系,将左特征和右特征沿着通道维度分成多组,在视差水平上对每组之间计算相关图,然后打包所有相关图以形成4D cost,这样一来,便可为后续的3D聚合网络提供更好的相似性度量,



原作者给的代码里生成的深度图是灰度图,不利于肉眼对比效果,需要将灰度图转换成自设定的彩色图,对应的代码可以参考。
def disp_map(disp):
    map = np.array([
        [0, 0, 0, 114],
        [0, 0, 1, 185],
        [1, 0, 0, 114],
        [1, 0, 1, 174],
        [0, 1, 0, 114],
        [0, 1, 1, 185],
        [1, 1, 0, 114],
        [1, 1, 1, 0]
    ])
    # grab the last element of each column and convert into float type, e.g. 114 -> 114.0
    # the final result: [114.0, 185.0, 114.0, 174.0, 114.0, 185.0, 114.0]
    bins = map[0:map.shape[0] - 1, map.shape[1] - 1].astype(float)

    # reshape the bins from [7] into [7,1]
    bins = bins.reshape((bins.shape[0], 1))

    # accumulate element in bins, and get [114.0, 299.0, 413.0, 587.0, 701.0, 886.0, 1000.0]
    cbins = np.cumsum(bins)

    # divide the last element in cbins, e.g. 1000.0
    bins = bins / cbins[cbins.shape[0] - 1]

    # divide the last element of cbins, e.g. 1000.0, and reshape it, final shape [6,1]
    cbins = cbins[0:cbins.shape[0] - 1] / cbins[cbins.shape[0] - 1]
    cbins = cbins.reshape((cbins.shape[0], 1))

    # transpose disp array, and repeat disp 6 times in axis-0, 1 times in axis-1, final shape=[6, Height*Width]
    ind = np.tile(disp.T, (6, 1))
    tmp = np.tile(cbins, (1, disp.size))

    # get the number of disp's elements bigger than  each value in cbins, and sum up the 6 numbers
    b = (ind > tmp).astype(int)
    s = np.sum(b, axis=0)

    bins = 1 / bins

    # add an element 0 ahead of cbins, [0, cbins]
    t = cbins
    cbins = np.zeros((cbins.size + 1, 1))
    cbins[1:] = t

    # get the ratio and interpolate it
    disp = (disp - cbins[s]) * bins[s]
    disp = map[s, 0:3] * np.tile(1 - disp, (1, 3)) + map[s + 1, 0:3] * np.tile(disp, (1, 3))

    return disp


def disp_to_color(disp, max_disp=None):

    # grab the disp shape(Height, Width)
    h, w = disp.shape

    # if max_disp not provided, set as the max value in disp
    if max_disp is None:
        max_disp = np.max(disp)

    # scale the disp to [0,1] by max_disp
    disp = disp / max_disp

    # reshape the disparity to [Height*Width, 1]
    disp = disp.reshape((h * w, 1))

    # convert to color map, with shape [Height*Width, 3]
    disp = disp_map(disp)

    # convert to RGB-mode
    disp = disp.reshape((h, w, 3))
    disp = disp * 255.0

    return disp


def tensor_to_color(disp_tensor, max_disp=192):
    """
    The main target is to convert the tensor to image format
      so that we can load it into tensor-board.add_image()
    Args:
        disp_tensor (Tensor): disparity map
            in (BatchSize, Channel, Height, Width) or (BatchSize, Height, Width) layout
        max_disp (int): the max disparity value
    Returns:
        tensor_color (numpy.array): the converted disparity color map
            in (3, Height, Width) layout, value range [0,1]
    """
    if disp_tensor.ndimension() == 4:
        disp_tensor = disp_tensor[0, 0, :, :].detach().cpu()
    elif disp_tensor.ndimension() == 3:
        disp_tensor = disp_tensor[0, :, :].detach().cpu()
    else:
        disp_tensor = disp_tensor.detach().cpu()

    disp = disp_tensor.numpy()

    disp_color = disp_to_color(disp, max_disp) / 255.0
    disp_color = disp_color.transpose((2, 0, 1))

    return disp_color

到了这里,关于3D双目感知深度估计之PSMNet解读的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 深度相机介绍(TOF、RGB双目、结构光参数对比)

    一、深度相机的介绍         随着计算机视觉与人工智能技术的飞速发展,采用深度相机进行场景三维重建、目标检测、环境感知等应用越来越广泛,与传统的2D相机不同,深度相机可以通过拍摄空间来获得景深信息,从而获得目标的3D信息,构建3D模型,这也是与普通相机最

    2024年02月05日
    浏览(80)
  • 基于Matlab的双目相机标定

    第一步,打开matlab 输入stereoCameraCalibrator ,进入工具箱  第二步:点击add images 第三步:添加图片路径,并且修改尺寸(根据格子边长) 工具箱会弹窗告诉你一共识别到多少组照片,多少组可以用,多少组被工具箱拒绝了。 我这里一共20张图片,都可以  第四步:点击 运行完

    2024年02月04日
    浏览(44)
  • 双目、结构光、tof,三种深度相机的原理区别看这一篇就够了!

    编辑:OAK中国 首发:oakchina.cn 喜欢的话,请多多👍⭐️✍ 内容可能会不定期更新,官网内容都是最新的,请查看首发地址链接。 Hello,大家好,这里是OAK中国,我是助手君。 最近刷知乎看到这样一个问题👇 碰巧B站也有朋友在问这种类似的问题,我寻思刚开始接触深度相机

    2024年02月09日
    浏览(41)
  • 【双目相机】基于matlab的参数标定2-使用matlab标定

    使用双目相机拍照并分割图片: 【双目相机】基于matlab的参数标定1-使用双目相机拍照 照片拍摄好后,进入matlab标定工具箱,如下图所示。可以使用matlab2020a版本。 进入工具箱以后,选择Add Images。 选择左右相机照片的路径,Size of checkerboard square为棋盘中每一个方格的长度,

    2024年02月15日
    浏览(41)
  • 一.基于压缩感知(CS)的DOA估计方法-OMP-CS算法

    阅读须知: 1.本文为本人原创作品仅供学习参考,未经过本人同意禁止转载和抄袭。 2.要想无障碍阅读本文需要一定的压缩感知理论以及压缩感知信号重构算法基础。 3.话不多说,直接开搞。         假设有K个远场窄带信号入射到有M个天线的均匀线阵上,第k个信号的入

    2024年02月11日
    浏览(50)
  • 关于机器人状态估计(15)-VIO与VSLAM精度答疑、融合前端、主流深度相机说明与近期工程汇总

    VIOBOT种子用户有了一定的数量,日常大家也会进行交流,整理总结一下近期的交流与答疑。 VIO-SLAM(作为三维SLAM,相对于Lidar-SLAM和LIO-SLAM)在工程上落地的长期障碍,不仅在算法精度本身,还有相对严重的鲁棒性问题,尺度问题,世界观问题和沉重的开销/成本问题。 这些我在

    2024年02月16日
    浏览(35)
  • Python基于OpenCV的双目视觉深度图算法(源码&教程)

    对于双目视觉立体匹配算法,先验的视差范围估计是影响算法匹配效果和运行时间的重要因素。在双目视觉系统的实际应用中,匹配视图之间的视差范围通常随场景的变化而不断改变,因此需要对视图间的视差范围进行有效的自动估计。针对此问题,开发了一种双模相机,可

    2024年02月03日
    浏览(42)
  • 深度相机(3D相机)

    传统的RGB彩色相机称为2D相机, 只能得到2D的图像信息, 无法得到物体与相机的距离信息,也就是深度信息。 顾名思义, 深度相机除了获取2D信息,还能得到深度信息,也叫RGBD相机, 或3D相机。 顺便提一句, 当前也有很多工作基于2D相机, 通过算法的方式估计深度, 但一

    2024年04月27日
    浏览(29)
  • DSGN: Deep Stereo Geometry Network for 3D Object Detection---基于双目视觉的3D目标检测(1)

    为了弥合2D图像和3D空间之间的差距,在平面扫描体中建立立体对应关系,然后将其转换为3DGV(3D geometric volume),以便能够对3D几何体和语义线索进行编码,并能在世界坐标系中进行目标检测。 设计了一条端到端的pipeline,用于提取像素级特征以进行立体匹配,并提取高级特

    2024年02月12日
    浏览(41)
  • 自动驾驶环境感知之基于深度学习的毫米波雷达感知算法

    (1)基本的数据形式 ADC(数模转换)数据块:由Chirp采样N、每帧内Chirp个数M和天线K组成的三维数据块的中频信号 Range-Azimuth-Doppler数据块:将中频信号数据块分别在距离、速度、角度三个维度上进行FFT操作,得到距离-角度-速度表征的RAD数据块。其中,角度是指水平方向的旋

    2024年01月25日
    浏览(48)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包