【单目3D目标检测】MonoGround论文精读与代码解析

10 篇文章 27 订阅
订阅专栏

Preface

Qin Z, Li X. MonoGround: Detecting Monocular 3D Objects From the Ground[C]. In Proceedings of the IEEE/CVF Conference on Computer Vision and Pattern Recognition. 2022: 3793-3802.
Paper
Code

Abstract

从单幅二维图像中获取三维信息本质上比较困难,主要有以下三点原因:

  • 一对多的映射关系:从2D到3D的映射是一个不适定(ill-posed)的问题,因为2D图像平面中的一个位置对应于所有共线的3D位置,这些位置存在于从相机中心到2D位置的射线中。如下图中(a)所示,这种一对多的特性使得预测物体的深度非常困难
  • 深度在图片中的表达与物体的实际高度有关:对于两个深度和位置相同的物体,如果高度不同,则在相机平面的投影中心会不同,如下图(b)所示,这样,深度的学习就必须克服对象不相关属性的干扰
  • 基于CenterNet的单目3D目标检测算法,物体的所有信息(包括深度)用一个点来表示,那么在训练过程中,对深度的监督是非常稀疏的,导致深度估计的学习不足(假设一个图像中有两辆车,那么深度图上只有两个点会被训练,而深度图上的所有其他点都被忽略)

 

Contributions

针对上述问题,本文提出在单目3D目标检测任务之前引入地平面先验(Ground Plane Prior)。在地平面先验的情况下,病态的2D到3D映射成为一个具有唯一解的适定问题。唯一解由相机射线与地平面的交点决定。深度表达不再与物体高度相关,如图2所示

本工作的主要贡献可以概括如下:

  • 提出在单目3D目标检测之前引入地平面,可以缓解ill-posed映射,消除深度的不相关关系,同时提供密集的深度监督,而不需要任何额外的数据,如LiDAR、立体和深度图像
  • 提出了一种深度对齐训练策略,以有效地学习和预测密集接地深度
  • 同时,利用稠密预测深度,提出了两阶段深度推断方法,实现了更细粒度的深度估计

单目3D目标检测中的地平面先验:

  • 地平面知识在单目3D目标检测中已经有过多次尝试。Mono3D首先尝试使用地平面来生成3D边界框建议,Ground-Aware在几何映射中引入了地平面,并提出了一个接地感知卷积模块来增强检测。在上述工作中,地平面是根据一个固定的规则定义的,例如KITTI上的摄像机高度为1.65米。这样,三维空间中固定高度为-1.65米的所有位置都将构成地平面
  • 但是本文则提出了一个可学习地平面先验,它基于一个更合理的假设,即物体应该躺在地面上。只要物体在地面上,地平面可以用物体的三维边界框底面代替。这样,所提出的地平面先验于物体的底面

单目3D目标检测中的深度估计:

  • 纯单目3D目标检测中主流的深度估计方法有两种,分别是直接回归方法和基于针孔成像模型的几何投影方法,此外,MonoFlex通过对角成对关键点的几何深度的平均值将几何深度扩展到对角关键点深度。同时,许多研究在深度估计的同时采用了不确定性,得到了较好的结果
  • 本文则在地平面先验的帮助下,提出了一种不同于上述方法的新的深度估计方法。它是一种两阶段深度推理方法,可以得到比几何深度更精确的深度估计。此外,所提出的深度估计还可以采用不确定性和对角成对关键点的扩展
     

Problem Definition

单目3D目标检测任务是从单目RGB图像中检测出3D目标(ill-posed)。除了RGB图像,还可以采用标定信息(即摄像机参数矩阵)。具体来说,对于每个物体,都需要:

  • 三维位置 ( x , y , z ) (x, y, z) (x,y,z)
  • 大小 ( h , w , l ) (h, w, l) (h,w,l)
  • 方向 θ θ θ,在KITTI数据集上,俯仰角和横摇角被视为零,因此只考虑偏航角 θ θ θ

与上述目标相对应,主流方法将整个任务划分为4个子任务,分别是2D位置(根据投影公式计算出3D位置)、深度、尺寸和方向估计任务。而深度估计是单眼三维目标检测的关键瓶颈,本文的工作专注于精确的深度估计
 

Ground Plane Prior

通过3D框的标注信息,来获取密集的地平面表示
整个流程如下:

  1. 首先利用物体的标注坐标信息,获取底面四个角点的坐标
  2. 利用底面四个角点的坐标,在地面随机采样,获得地面的随机采样点 P 3 d P_{3d} P3d(此时为3D坐标)
  3. 将地面的随机采样点坐标,转换为2D图像坐标

In Paper

为了定义地平面,本文从一个合理的假设开始,即物体位于地面上。这个假设适用于所有常见的物体,如汽车、行人和骑自行车的人。在此假设下,可以用物体三维边界框的底面代替地平面。对于每个对象,我们首先获得3D空间中的底部键点 k 1 、 k 2 、 k 3 k_1、k_2、k_3 k1k2k3 k 4 k_4 k4。然后对由底部关键点组成的三维底面进行随机采样和插值。将 R ∈ R N × 2 R \in \mathbb{R}^{N \times 2} RRN×2表示为每个元素为 R i , j ∈ [ 0 , 1 ] R_{i, j}∈[0,1] Ri,j[0,1]的随机矩阵。 N N N是采样点的个数,采样的密集点可表示为:
P 3 d = R [ k 2 − k 1 k 4 − k 1 ] + k 1 P_{3 d}=R\left[\begin{array}{l} k_2-k_1 \\ k_4-k_1 \end{array}\right]+k_1 P3d=R[k2k1k4k1]+k1
其中 P 3 d ∈ R N × 3 P_{3d}∈\mathbb{R}^{N \times 3} P3dRN×3包含所有采样点。假设 K 3 × 3 K_{3×3} K3×3为摄像机参数矩阵。采样后将这些点投影回图像空间:
P 2 d T = K 3 × 3 P 3 d T P_{2 d}^T=K_{3 \times 3} P_{3 d}^T P2dT=K3×3P3dT
其中 P 2 d ∈ R N × 3 P_{2d}∈\mathbb{R}^{N \times 3} P2dRN×3 P 2 d P_{2d} P2d的第 i i i行 为 [ u i ⋅ z i , v i ⋅ z i , z i ] [u_i·z_i, v_i·z_i, z_i] [uizi,vizi,zi],其中 u i u_i ui v i v_i vi为图像空间中的投影坐标, z i z_i zi为该点对应的深度。

如上图所示,利用上述公式,可以得到一个密集接地的深度表示,解决了主流方法中深度监督稀疏的问题。对于不适定的2D到3D映射问题,密集的接地深度提供了地平面约束,使其成为一个适定问题,而映射的唯一解是摄像机光线与地平面的相交。此外,由于本文的公式与物体的高度没有关系,因此自然避免了不相关的相关问题。更重要的是,引入的地平面先验和密集接地深度都来自于3D边界框注释,易于访问,不需要LiDAR和深度等其他数据源

In Code

  • 这部分代码的实现,在monoground\data\datasets\kitti.py文件KITTIDataset类的interp_corners3d函数中:
def interp_corners3d(self, ground_corners3d, num_interp_x=100, num_interp_y=100, sampling_method='grid',  
                     sampling_num_type='fixed', ground_corners2d=None, down_ratio=4):  
    '''  
    在目标3D框的底面上进行随机采样,生成ground先验  
    这里默认没有使用num_interp_x和y这两个参数,而是根据底面面积来进行随机采样  
    :param ground_corners3d: 目标底面的四个点的3D坐标 [4, 3]    :param num_interp_x: x方向要采样点的数目 21    :param num_interp_y: y方向要采样点的数目 21    :param sampling_method: 'random'    :param sampling_num_type: 算法使用'area'  
    :param ground_corners2d: 目标底面的四个点的2D坐标 [4, 2]    :param down_ratio: 4    :return:    '''    assert len(ground_corners3d) == 4  
    assert sampling_method in ['grid', 'random']  
    assert sampling_num_type in ['fixed', 'area']  
    if sampling_method == 'grid' and sampling_num_type == 'fixed':  
        sampling_points = np.dstack(  
            np.meshgrid(np.linspace(0, 1, num_interp_x), np.linspace(0, 1, num_interp_y))).reshape(-1, 2)  
    elif sampling_method == 'random' and sampling_num_type == 'fixed':  
        sampling_points = np.random.uniform(size=(num_interp_x * num_interp_y - 5, 2))  
        sampling_points = np.concatenate(  
            [sampling_points, np.array([[0.5, 0.5], [1., 1.], [0., 0.], [1., 0.], [0., 1.], ])], axis=0)  
  
    # 默认运行这里  
    elif sampling_method == 'random' and sampling_num_type == 'area':  
        # 鞋带公式计算不规则矩形面积  
        area = self.ploy_area(ground_corners2d[:, 0], ground_corners2d[:, 1])  
        area /= (down_ratio * down_ratio)  
        if area > self.max_area:  
            area = self.max_area  # 1600 * 928 / 32 / 32  
        if area < 5:  
            sampling_points = np.array([[0.5, 0.5], [1., 1.], [0., 0.], [1., 0.], [0., 1.], ])  
        else:  
            sampling_points = np.random.uniform(size=(ceil(area) - 5, 2))  
            sampling_points = np.concatenate(  
                [sampling_points, np.array([[0.5, 0.5], [1., 1.], [0., 0.], [1., 0.], [0., 1.], ])], axis=0)  
    else:  
        raise NotImplementedError  
    if self.only_five_gdpts:  
        sampling_points = np.array([[0.5, 0.5], [1., 1.], [0., 0.], [1., 0.], [0., 1.], ])  
  
    # num_interp_x * num_interp_y * 2  
    # u、v方向向量  
    x_vector = ground_corners3d[1] - ground_corners3d[0]  
    z_vector = ground_corners3d[3] - ground_corners3d[0]  
  
    # vector from x and z directions  
    # 最终产生地面随机采样点坐标  
    base = np.stack([x_vector, z_vector])  
    sampled_pts = sampling_points @ base + ground_corners3d[0]  
  
    return sampled_pts
  • 计算底面面积:鞋带公式
def ploy_area(self, x, y):  
    '''  
    Shoelace公式,也叫高斯面积公式,是一种数学算法,可求确定区域的一个简单多边形的面积  
    '''    return 0.5 * np.abs(np.dot(x, np.roll(y, 1)) - np.dot(y, np.roll(x, 1)))

 

Pipeline

In Pape

整体网络框架基于CenterNet和MonoFlex,如下图所示

在这里插入图片描述

  1. Backbone采用DLA34,之后分为两个branch
  2. Ground Branch负责预测Grounded Depth,并且采用了Dilated Coord Conv,原因如下:
  • 首先,Grunded Depth的估计涉及到视觉特征和位置特征,而普通卷积只能提供视觉特征,坐标卷积可以整合视觉和位置特征
  • 其次,3D坐标 ( x , y , z ) (x, y, z) (x,y,z) z z z为深度)是2D坐标的线性变换(相机投影模型)。坐标卷积可以显式地将卷积中的二维坐标串联起来,使其成为一个外部提示,从而更好地估计深度
  1. 3D Branch基于MonoFlex,负责预测五类Attributes:
  • Heatmap [ C × H S × W S ] [C\times\frac{H}{S}\times\frac{W}{S}] [C×SH×SW]表示每个物体的中心坐标,热图越接近1,表示某类物体在该位置存在的可能性越大
  • Offsets [ 22 × H S × W S ] [22\times\frac{H}{S}\times\frac{W}{S}] [22×SH×SW]表示11个点(3个中心点+8个立方体顶点)在2D图像中的偏移,如下图所示:

本文中定义Offsets为:
δ p = p S − ⌊ c 2 d S ⌋  s.t.  p ∈ { c 2 d , k 1 2 d , ⋯   , k 8 2 d , b 2 d , t 2 d } \begin{gathered} \delta_p=\frac{p}{S}-\left\lfloor\frac{c^{2 d}}{S}\right\rfloor \\ \text { s.t. } p \in\left\{c^{2 d}, k_1^{2 d}, \cdots, k_8^{2 d}, b^{2 d}, t^{2 d}\right\} \end{gathered} δp=SpSc2d s.t. p{c2d,k12d,,k82d,b2d,t2d}
其中, S S S为Backbone的下采样率, ⌊ ⋅ ⌋ \left\lfloor · \right\rfloor 为向下取整,假设 δ p ∗ \delta_p^* δp为GT真值,那么Offsets的损失如下:
L offset  = ∑ p ∈ { c 2 d , k 1 2 d , ⋯   , k 8 2 d , b 2 d , t 2 d } ∣ δ p − δ p ∗ ∣ L_{\text {offset }}=\sum_{p \in\left\{c^{2 d}, k_1^{2 d}, \cdots, k_8^{2 d}, b^{2 d}, t^{2 d}\right\}}\left|\delta_p-\delta_p^*\right| Loffset =p{c2d,k12d,,k82d,b2d,t2d}δpδp

  • Size [ 3 × H S × W S ] [3\times\frac{H}{S}\times\frac{W}{S}] [3×SH×SW]表示物体3D的长宽高预测值
  • Orientation [ 8 × H S × W S ] [8\times\frac{H}{S}\times\frac{W}{S}] [8×SH×SW]表示物体的偏航角(yaw),分为8部分,在KITTI数据集中,俯仰角(pitch)和旋转角(roll)均设置为0
  • Direct Depth [ 1 × H S × W S ] [1\times\frac{H}{S}\times\frac{W}{S}] [1×SH×SW]表示物体的直接深度
  1. 在后处理阶段:分别预测两个Depth(Grounded Depth、Geometry Depth)
  • 利用预测的Offsets,计算Geometry Depth:
    z = f ⋅ h h 2 d z=\frac{f \cdot h}{h_{2 d}} z=h2dfh
    其中, f f f为相机焦距, h h h物体的3D高度, h 2 d h_{2d} h2d b 2 d − t 2 d b^{2 d}-t^{2 d} b2dt2d,即物体的2D图像像素高度。此外,本文还通过平均对角关键点的几何深度来扩展几何深度(借鉴MonoFlex)
  • 最后,通过从七个深度 z p r e d z_{pred} zpred(1 direct depth, 3 geometry depths, 3 grounded depths)中基于不确定性的投票得到最终深度(借鉴MonoFlex):
    z final  = ( ∑ i = 1 7 z pred  i σ i ) / ( ∑ i = 1 7 1 σ i ) z_{\text {final }}=\left(\sum_{i=1}^7 \frac{z_{\text {pred }}^i}{\sigma_i}\right) /\left(\sum_{i=1}^7 \frac{1}{\sigma_i}\right) zfinal =(i=17σizpred i)/(i=17σi1)
  • 3 geometry depths:物体 h 2 d h_{2d} h2d的表示有三种,即顶部中心和底部中心之间的距离,两对对角线的距离,如下图所示:
  • 3 grounded depths:物体底部中心 b 2 d b^{2d} b2d的深度、底部两对对角顶点的深度 k 1 2 d , k 3 2 d {k^{2d}_1,k^{2d}_3} k12d,k32d k 2 2 d , k 4 2 d {k^{2d}_2,k^{2d}_4} k22d,k42d

In Code

这里要明确两个问题:
【第一个问题】:矩形框的8个顶点坐标如何产生,以及顺序如何?

  • 根据目标底面中心坐标和偏航角,我们很容易计算出 k 1 k_1 k1的坐标(只计算x方向和z方向即可),如下图所示:
  • 这样我们就得出一个转换矩阵(下图中蓝色矩阵R),但实际代码中的转换矩阵确实下图中的红色矩阵R,区别就在于,偏航角 r y r_y ry的正负问题:在KITTI中相机坐标系下,偏航角的方向定位为:X正半轴为0°,Z正半轴为-90°
  • 因此,要在 s i n ( ) sin() sin()函数前面加个负号
  • 对应代码位于monoground\data\datasets\kitti_utils.pygenerate_corners3d函数中:
def generate_corners3d(self):  
    """ 前四个为底部四个点:左前-->右前-->右后-->左后  
    generate corners3d representation for this object    self.l, self.h, self.w: 目标的3D长、高、宽  
    self.t: 目标底面的3D坐标(x, y, z)  
    self.ry: 目标的偏航角r_y
    :return corners_3d: [8, 3] corners of box3d in camera coord    """    l, h, w = self.l, self.h, self.w  
  
    # 设置8个顶点相对于底面中心点的初始偏移  
    x_corners = [l / 2, l / 2, -l / 2, -l / 2, l / 2, l / 2, -l / 2, -l / 2]  
    y_corners = [0, 0, 0, 0, -h, -h, -h, -h]  
    z_corners = [w / 2, -w / 2, -w / 2, w / 2, w / 2, -w / 2, -w / 2, w / 2]  
  
    # 定义转换矩阵  
    R = np.array([[np.cos(self.ry), 0, np.sin(self.ry)],  
                  [0, 1, 0],  
                  [-np.sin(self.ry), 0, np.cos(self.ry)]])  
  
    corners3d = np.vstack([x_corners, y_corners, z_corners])  # (3, 8)  
  
    # 8个顶点相对于底面中心点的实际偏移  
    corners3d = np.dot(R, corners3d).T  
    corners3d = corners3d + self.t  
  
    return corners3d

【第二个问题】:目标的geometry depth如何产生?

  • 利用目标的2D高度和3D高度之间的相对比例来计算目标深度
    z l = f × H h l h l = h t − h b \begin{aligned} & z_l=\frac{f \times H}{h_l} \\ & h_l=h_t-h_b \end{aligned} zl=hlf×Hhl=hthb
  • 对应代码位于monoground\model\anno_encoder.py中的decode_depth_from_keypoints_batch函数中:
def decode_depth_from_keypoints_batch(self, pred_keypoints, pred_dimensions, calibs, batch_idxs=None):  
    ''' generate geometry depth  
    :param pred_keypoints: 一个batch中所有目标8个顶点,在2D图像中的坐标信息 [N, 10, 2]    :param pred_dimensions: 一个batch中所有目标的长高宽 [N, 3]    :param calibs: 校准信息,会用到u方向的焦距f_u  
    :param batch_idxs:    :return:  
    '''    # pred_keypoints: K x 10 x 2, 8 vertices, bottom center and top center  
    pred_height_3D = pred_dimensions[:, 1].clone()  
    batch_size = len(calibs)  
    if batch_size == 1:  
        batch_idxs = pred_dimensions.new_zeros(pred_dimensions.shape[0])  
  
    # 产生三个目标在2D图像上的高度:  
    # center_height: 底面中心的y坐标值-顶面中心的y坐标值 [N, ]    # corner_02_height: 目标框两条相对棱的长度[N, 2]  
    # corner_13_height: 目标框两条相对棱的长度[N, 2]  
    center_height = pred_keypoints[:, -2, 1] - pred_keypoints[:, -1, 1]  
    corner_02_height = pred_keypoints[:, [0, 2], 1] - pred_keypoints[:, [4, 6], 1]  
    corner_13_height = pred_keypoints[:, [1, 3], 1] - pred_keypoints[:, [5, 7], 1]  
  
    pred_keypoint_depths = {'center': [], 'corner_02': [], 'corner_13': []}  
  
    for idx, gt_idx in enumerate(torch.unique(batch_idxs, sorted=True).tolist()):  
        calib = calibs[idx]  
        corr_pts_idx = torch.nonzero(batch_idxs == gt_idx).squeeze(-1)  
        # 利用投影矩阵,计算三组几何深度  
        center_depth = calib.f_u * pred_height_3D[corr_pts_idx] / (  
                    F.relu(center_height[corr_pts_idx]) * self.down_ratio + self.EPS)  
        corner_02_depth = calib.f_u * pred_height_3D[corr_pts_idx].unsqueeze(-1) / (  
                    F.relu(corner_02_height[corr_pts_idx]) * self.down_ratio + self.EPS)  
        corner_13_depth = calib.f_u * pred_height_3D[corr_pts_idx].unsqueeze(-1) / (  
                    F.relu(corner_13_height[corr_pts_idx]) * self.down_ratio + self.EPS)  
  
        # 取平均  
        corner_02_depth = corner_02_depth.mean(dim=1)  
        corner_13_depth = corner_13_depth.mean(dim=1)  
  
        pred_keypoint_depths['center'].append(center_depth)  
        pred_keypoint_depths['corner_02'].append(corner_02_depth)  
        pred_keypoint_depths['corner_13'].append(corner_13_depth)  
  
    for key, depths in pred_keypoint_depths.items():  
        pred_keypoint_depths[key] = torch.clamp(torch.cat(depths), min=self.depth_range[0], max=self.depth_range[1])  
  
    pred_depths = torch.stack([depth for depth in pred_keypoint_depths.values()], dim=1)  
  
    return pred_depths

 

Depth-Align Training

网络预测的grounded points,由均匀的网格组成,处在整数位置
而由标注信息转换的投影grounded points,则是随机分散的,即非整数位置
那么二者存在不对齐的现象
那么,本文的思路是:对投影的每个随机采样grounded points,使用其周围4个网格点的深度进行损失函数的计算,来优化非整数位置的密集深度

In Paper

在得到Grounded Depth预测图后,如何训练这种预测图是一个重要的问题,因为预测的Grounded Depth与利用标签得到的投影Grounded Depth存在不对齐的问题,即投影密集接地点分布在预测图中任意位置,而预测图由均匀的网格组成。也就是说,预测图只能用于整数位置,而投影的密集接地点分布在非整数位置。为了解决这个问题,本文提出了一种深度对齐训练方法,如下图所示:

  • 深度对齐训练的前向计算与双线性插值相同,设投影接地点为 ( u i , v i ) (u_i, v_i) (ui,vi),深度为 z i z_i zi { g 1 , g 2 , g 3 , g 4 } \{g_1, g_2, g_3, g_4\} {g1,g2,g3,g4}为从左上角开始顺时针环绕的四个网格点,则第 i i i个点的深度预测值可以写成:
    λ 1 = u i − ⌊ u i ⌋ , λ 2 = ⌈ u i ⌉ − u i , λ 3 = v i − ⌊ v i ⌋ , λ 4 = ⌈ v i ⌉ − v i ,  pred  i = λ 2 λ 4 g 1 + λ 1 λ 4 g 2 + λ 1 λ 3 g 3 + λ 2 λ 3 g 4 , \begin{gathered} \lambda_1=u_i-\left\lfloor u_i\right\rfloor, \lambda_2=\left\lceil u_i\right\rceil-u_i, \\ \lambda_3=v_i-\left\lfloor v_i\right\rfloor, \quad \lambda_4=\left\lceil v_i\right\rceil-v_i, \\ \text { pred }_i=\lambda_2 \lambda_4 g_1+\lambda_1 \lambda_4 g_2+\lambda_1 \lambda_3 g_3+\lambda_2 \lambda_3 g_4, \end{gathered} λ1=uiui,λ2=uiui,λ3=vivi,λ4=vivi, pred i=λ2λ4g1+λ1λ4g2+λ1λ3g3+λ2λ3g4,
  • 那么接地点的总深度损失为:
    L d a = ∑ i = 1 N ∣ z i − pred ⁡ i ∣ L_{d a}=\sum_{i=1}^N\left|z_i-\operatorname{pred}_i\right| Lda=i=1Nzipredi
  • 这样,我们可以直接优化非整数位置的密集深度,反向传播是根据双线性插值的权重将梯度传递给周围的网格点
  • 【疑问1】:为什么要用投影点周围的四个网格点来进行双线性插值呢?
    • 【解答1】: 计算该非整数位置的深度值和深度不确定值,便于和GT值进行loss计算
  • 【疑问2】: g g g是什么意思呢?每个网格点的深度值?
    • 【解答2】 :深度值和深度不确定值

In Code

  • 对应代码位置:monoground\model\layers\utils.py中的soft_get函数
def soft_get(reg, pts, force_get=False, force_get_uncertainty=False):  
    '''  
    深度对齐,考虑到随机采样的底面真值点的坐标不是整数  
    因此无法直接与网络预测产生的网格点的深度进行损失函数的计算    :param reg: 网络预测的ground深度值  
    :param pts: 随机采样的底面真值坐标  
    :return:  
        rop: 随机采样底面真值点,对应于网络输出预测特征图的值(深度值和深度不确定性值)  
        valid_idx: 随机采样地面真值点的有效索引  
    '''    pts_x = pts[:, 0]  
    pts_y = pts[:, 1]  
  
    h, w = reg.shape[-2], reg.shape[-1]  
    # if force_get:  
    #     pts_x = pts_x.clamp(0, w-1)    #     pts_y = pts_y.clamp(0, h-1)  
    # 获取随机采样点周围的四个网格点坐标  
    pts_x_low = pts_x.floor().long()  
    pts_x_high = pts_x.ceil().long()  
  
    pts_y_low = pts_y.floor().long()  
    pts_y_high = pts_y.ceil().long()  
  
    # 是否超出特征图  
    valid_idx = (pts_y_low >= 0) & (pts_y_high < h) & (pts_x_low >= 0) & (pts_x_high < w)  
  
    # 四个网格点的有效坐标  
    pts_x_low_valid = pts_x_low[valid_idx]  
    pts_x_high_valid = pts_x_high[valid_idx]  
    pts_y_low_valid = pts_y_low[valid_idx]  
    pts_y_high_valid = pts_y_high[valid_idx]  
  
    # 随机采样点的有效坐标  
    pts_x = pts_x[valid_idx]  
    pts_y = pts_y[valid_idx]  
  
    # 四个有效网格点的深度值  
    rop_lt = reg[..., pts_y_low_valid, pts_x_low_valid]  
    rop_rt = reg[..., pts_y_low_valid, pts_x_high_valid]  
    rop_ld = reg[..., pts_y_high_valid, pts_x_low_valid]  
    rop_rd = reg[..., pts_y_high_valid, pts_x_high_valid]  
  
    rop_t = (1 - pts_x + pts_x_low_valid) * rop_lt + (1 - pts_x_high_valid + pts_x) * rop_rt  
    rop_d = (1 - pts_x + pts_x_low_valid) * rop_ld + (1 - pts_x_high_valid + pts_x) * rop_rd  
  
    rop = (1 - pts_y + pts_y_low_valid) * rop_t + (1 - pts_y_high_valid + pts_y) * rop_d  
  
    # False  
    # torch.all(valid_idx) 全部为True时返回True  
    if force_get and not torch.all(valid_idx):  
        shape = list(rop.shape)  
        shape[-1] = len(valid_idx)  
        rop_force = torch.zeros(*shape, dtype=rop.dtype, device=rop.device)  
        rop_force[..., valid_idx] = rop  
  
        pts_invalid = pts[~valid_idx]  
  
        grid = generate_grid(w, h).to(pts_invalid.device)  
        reg_invalid_all = []  
        for pts_in in pts_invalid:  
            diff = (pts_in - grid)  
            dis = diff[:, 0] ** 2 + diff[:, 1] ** 2  
            closest_idx = dis.argmin()  
  
            reg_invalid_all.append(reg[..., grid[closest_idx, 1].long(), grid[closest_idx, 0].long()])  
        reg_invalid_all = torch.stack(reg_invalid_all, dim=-1)  
        rop_force[..., ~valid_idx] = reg_invalid_all.detach()  
        if force_get_uncertainty:  
            # import pdb; pdb.set_trace()  
            # assert rop_force.shape[0] == 2            if rop_force.shape[0] == 2:  
                rop_force[1, ~valid_idx] = 30  
            elif rop_force.shape[0] == 6:  
                rop_force[1, ~valid_idx] = 30  
                rop_force[3, ~valid_idx] = 30  
                rop_force[5, ~valid_idx] = 30  
            else:  
                raise NotImplementedError  
        return rop_force, valid_idx  
  
    return rop, valid_idx

 

Two-stage Depth Inference

目前理解:Ground Branch就是用来预测接地点,每一个接地点的深度都是唯一的,并且理论上接地点的深度,等于该点向上延伸中所有点的深度值,例如:底部中心点的深度=物体中心点的深度=顶部中心点的深度。那么,就可以利用Ground Branch来获得接地点的深度值,来辅助最终深度的生成(在Loss计算中加入地面随机采样点的预测)
其中两阶段的含义:在深度估计之前使用3D分支的回归结果

在CenterNet-Style的推理中,对象由特征图上的粗网格点表示,所有其他属性都由点上的特征一次性预测,称之为单阶段推理。但是粗网格点只能反映物体的粗位置,这就是为什么我们需要进一步回归GT位置和粗网格位置之间的差异
在密训练的接地深度图的帮助下,我们使用回归的细粒度位置代替粗糙的网格位置,可以实现比原来的CenterNet式深度推断更精确的深度估计,二者的区别如下图所示:

  • 第一个Ground Depth:假设 ⌊ c 2 d / S ⌋ \left\lfloor c^{\mathbf{2 d}} / S\right\rfloor c2d/S是热图预测的中心。我们首先对中心进行回归偏移,得到物体底部中心点的精细位置 b 2 d / S = ⌊ c 2 d / S ⌋ + δ b 2 d b^{\mathbf{2 d}} / S=\left\lfloor c^{\mathbf{2 d}} / S\right\rfloor+\delta_{b^{\mathbf{2 d}}} b2d/S=c2d/S+δb2d,然后对地面支路预测采用深度对齐的前向计算公式进行插值,得到精确深度。由于 b 2 d b^\mathbf{2d} b2d为物体的底部中心,因此 b 2 d b^\mathbf{2d} b2d的接地深度与 c 2 d c^\mathbf{2d} c2d的深度完全相同,即物体的中心深度
  • 第二、三个Ground Depth:利用物体底部框的四个角点,根据三维几何结构,中心深度等于对角线关键点的平均深度。这样,可以利用得到的对角线结果求平均值,从关键点 k 1 2 d , k 3 2 d 和 k 2 2 d , k 4 2 d k^{2d}_1, k^{2d}_3和k^{2d}_2, k^{2d}_4 k12d,k32dk22d,k42d得到另外两个深度估计
【图像拼接/深度单应】论文精读:Deep Image Homography Estimation(HomographyNet)
畅游计算机视觉的海洋
04-09 3446
我们提出了一个深度卷积神经网络来估计一对图像之间的相对单应性。我们的前馈网络有 10 层,将两个堆叠的灰度图像作为输入,并产生 8 个自由度单应性,可用于将第一个图像中的像素映射到第二个图像。我们为 HomographyNet 提出了两种卷积神经网络架构:一个直接估计实值单应性参数的回归网络,以及一个在量化单应性上产生分布的分类网络。我们使用 4 点单应性参数化,它将一张图像的四个角映射到第二张图像。我们的网络使用扭曲的 MS-COCO 图像以端到端的方式进行训练。我们的方法无需单独的局部特征检测和变换估计
【AI面试】目标检测中one-stage、two-stage算法的内容和优缺点对比汇总
钱多多先森
04-30 9333
目标检测算法one stage和two stage的对比
单目3D车辆检测全流程实战分享-附完整代码
一拳小超人
06-16 1644
基于M3D-RPN全流程实现单目3D检测,从数据处理到优化和部署的全流程实战分享
单目3D目标检测——MonoDLE 模型训练 | 模型推理
黎国溥
10-15 1693
本文分享 MonoDLE 的模型训练、模型推理、可视化3D检测结果。
3D目标检测(教程+代码
2301_78240361的博客
12-31 2511
随着计算机视觉技术的不断发展,3D目标检测成为了一个备受关注的研究领域。与传统的2D目标检测相比,3D目标检测可以在三维空间中对物体进行定位和识别,具有更高的准确性和适用性。本文将介绍3D目标检测的相关概念、方法和代码实现。
论文MonoFusion: Real-time 3D Reconstruction of Small Scenes with a Single Web Camera》学习
Orange_Wu的博客
12-12 879
Abstract (2013 SCI会议) MonoFusion允许用户实时构建密集的三维重建环境,只使用一个现成的网络摄像头作为输入传感器。这款相机可能已经可以在平板电脑、手机或独立设备上使用。不需要额外的输入硬件。这就消除了在自然室外照明中不能稳定工作的高功率有源传感器的需求。利用相机的输入流,我们首先利用稀疏跟踪方法估计了6自由度相机的姿态。这些姿态然后用于输入帧和关键帧之间的高效密集立体匹...
3D目标检测MonoDIS
weixin_38132142的博客
07-27 1227
在今年 CVPR 2019 WAD Workshop nuScenes Detection Challenge 中,Mapillary 使用本文介绍的 MonoDIS 达到了目前 SOTA 的 image-only 3D Detection Performance(NDS 38.4%);虽然不及官方基于 lidar 的 pointpillars baseline,但也已经是基于单目非常高的精度了,...
yolov3论文精读代码注释
最新发布
01-14
YOLOv3是YOLO(You Only Look Once)系列目标检测算法的第三个版本,由Joseph Redmon、Ali Farhadi等人在2018年的...通过对论文精读代码的注释,你可以掌握目标检测的核心技术,并有可能将其应用到自己的项目中。
目标检测论文精读(1)- YOLOv1
zhli99的博客
04-07 1495
YOLOv1阅读重点IntroductionThe YOLO Detection SystemThe ModelThe architectureLoss functionInnovationResultsReal-Time Systems on Pascal VOC 2007Error analysisGeneralization results on Picasso and People-Art...
目标检测论文精读(3)- SPP
zhli99的博客
04-17 779
SPP阅读重点IntroductionThe stepsThe architectureSingle-size trainingMulti-size trainingBaseline network architecturesInnovationResultDetection results on Pascal VOC 2007Detection results on Pascal VOC 200...
目标检测代码
11-01
该文件为目标检测代码以及训练好的模型,tensorflow实现,可以直接运行,也可以在自己需要的基础上进行二次训练
Mono3D:“单目视频单声道化”的源代码(SIGGRAPH Asia 2020)
01-31
单眼双目视频 《 ACM图形交易》(SIGGRAPH亚洲2020年发行),第1卷。 2020年12月,第6号,第39卷,第228:1--228:16页。 [] [ ] [] Mono3D是将双目视频单声道化为具有隐式编码的立体声信息的常规单眼视频的实现,从而可以高质量地恢复原始的双目视频。 在线演示 环境 请参考 。 数据集 由于版权问题,我们无法发布整个3D电影数据集。 但是本文中使用的双目图像数据集和部分双目视频数据集是公开可用的: 和 。 准备Flickr1024以训练图像版本模型 从网站下载Flickr1024: ://yingqianwang.github.io/Flickr1024/ 从下载数据列表 如下组织数据集($ {DATASET是用于维护我们的数据集的根目录}): ${DATASET} |-- Flickr1024 | |-- Train | |-- |-- 0001_L.png | | |-- 0001_R.png | | |-- 0002_L.png | | |-- 0002_R.png |
yolo3d目标检测识别,使用opencv技术基于深度学习
03-09
基于OpenCV的使用YOLOv3进行目标检测,使用YOLO神经网络的实时目标检测代码
点云3D目标检测之——尝试SFD代码跑通(超详细!!)
weixin_47343723的博客
09-28 3982
在kitti数据集上目前排名前三,文章通俗易懂(也可能是我没看懂)(这部分后面再补吧)
Python 实现二叉树前序,中序,后序,零基础也能看得懂
m0_67621628的博客
03-17 815
self.element = element self.l_child = l_child self.r_child = r_child class Tree(object): “”“树类”"" def init(self): self.root = Node() self.queue = [] def add_node(self, element): “”“为树添加节点”"" node = Node(element) 如果树是空的,则对根节点赋值 if self.root.element == -1: s
单目3D目标检测】FCOS3D + PGD论文解析代码复现
热门推荐
weixin_43799388的博客
11-08 1万+
本文对OpenMMLab在Monocular 3D detection领域做的两项工作FCOS3D和PGD(也被称作FCOS3D++)进行介绍。
单目3D检测】Monoflex论文阅读
qq_44876051的博客
08-22 4063
1. 引言 随着深度学习的浪潮从 2D 图像席卷至更加广泛的数据模态和应用场景,3D 物体检测作为现实应用场景中极其重要的一个任务被越来越多的研究者所关注。在这一波浪潮中,最早取得成功和巨大现实影响力的当属基于 LiDAR 点云的针对室外驾驶场景的检测方案们(最早如 MV3D, VoxelNet, Frustum PointNet 等)。而单目3D检测由于其低成本的感知方案,近年来受到越来越多的学者关注,但纯视觉方案的检测性能远不如基于 LiDAR 的一众方法,这是符合直觉的,因为二维图像终究是二维信息,
单目3D目标检测MonoCon论文精读代码解析
weixin_43799388的博客
12-27 2064
MonoCon:在MonoDLE的基础上增加辅助学习模块,效果非常work
任务感知单目深度估计的3D目标检测
3D视觉工坊
05-06 2141
标题:Task-Aware Monocular Depth Estimation for 3D Object Detection作者:Xinlong Wang1∗, Wei Yin1, T...
写文章

热门文章

  • 【魔改YOLOv5-6.x(上)】结合轻量化网络Shufflenetv2、Mobilenetv3和Ghostnet 60287
  • 【魔改YOLOv5-6.x(中)】加入ACON激活函数、CBAM和CA注意力机制、加权双向特征金字塔BiFPN 46978
  • 【YOLOv7】主要改进点详解 40832
  • 【YOLOv5-6.x】模型参数量param及计算量FLOPs解析 40421
  • 【魔改YOLOv5-6.x(下)】YOLOv5s+Ghostconv+BiFPN+CA 36527

分类专栏

  • AI大模型技术积累 14篇
  • 技术分享 14篇
  • Pytorch 1篇
  • 好用的开发工具 2篇
  • Paddle
  • 3D目标检测 10篇
  • 常用命令合集 2篇
  • 模型部署 1篇
  • 视频剪辑 1篇
  • 该死的环境安装
  • 又怎么不能解决呢? 2篇
  • 目标检测 19篇
  • YOLOv5 15篇
  • mmdetection 1篇
  • YOLOv7 3篇
  • 深度学习 8篇
  • Python 5篇
  • 实用技能 2篇
  • Office 1篇
  • Latex
  • LeetCode
  • Markdown 2篇
  • 保研 3篇

最新评论

  • 【Python】OpenCV读取视频帧并保存为图片

    sinat_37903980: 谢谢大佬,非常好用。下好库,改好路径,直接就能运行

  • 【YOLOv5-6.x】输出中文标签、修改标签框的位置和大小

    weixin_44837284: en_to_ch函数怎么调用啊

  • 【YOLOv5】结合GradCAM热力图可视化

    死的却是狗: from utils.datasets import letterbox ModuleNotFoundError: No module named 'utils.datasets'

  • 【MMDetection3D】环境搭建,使用PointPillers训练&测试&可视化KITTI数据集

    浅若夏沫444: 博主,请问Converting prediction to KITTI format不能利用GPU加速,运行速度巨慢是怎么回事。

  • 【MMDetection3D】环境搭建,使用PointPillers训练&测试&可视化KITTI数据集

    weixin_51941475: 我也是这个问题,你解决了吗?

大家在看

  • 【快速上手】linux环境下Neo4j的安装与使用
  • 黑神话:悟空 后话 189
  • 简单生活的快乐 337

最新文章

  • 【多模态大模型】Qwen2-VL基本原理和推理部署实战
  • 【大模型训练】Flash Attention详解
  • 【大模型推理】vLLM推理库介绍及部署qwen2实战教程
2024年14篇
2023年2篇
2022年47篇
2020年1篇

目录

目录

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

嗜睡的篠龙

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或 充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值

玻璃钢生产厂家柳州玻璃钢人物雕塑玻璃钢生蚝雕塑开封锻铜校园玻璃钢雕塑公司玻璃钢道教雕塑价格商场的美陈带来的效果意义玻璃钢雕塑老化松江区拉丝玻璃钢雕塑品牌企业五华区设计玻璃钢雕塑哪里好玻璃钢雕塑龙玻璃钢瓜果雕塑厂商北京通道商场美陈制造灯市口商场美陈商场内部美陈吉林玻璃钢仿真雕塑北京景观玻璃钢雕塑方法玻璃钢卡通雕塑厂家供应合肥人物玻璃钢雕塑多少钱河北百货商场美陈玻璃钢雕塑雪山福建节庆商场美陈供货商雨花春季商场美陈城市标志玻璃钢雕塑报价南宁玻璃钢革命人物雕塑门头玻璃钢雕塑哪家好河北主题商场美陈市场报价广东超市商场美陈销售公司平度玻璃钢卡通雕塑沅江玻璃钢人物雕塑南京欧式玻璃钢雕塑优势广州特色玻璃钢雕塑价位香港通过《维护国家安全条例》两大学生合买彩票中奖一人不认账让美丽中国“从细节出发”19岁小伙救下5人后溺亡 多方发声单亲妈妈陷入热恋 14岁儿子报警汪小菲曝离婚始末遭遇山火的松茸之乡雅江山火三名扑火人员牺牲系谣言何赛飞追着代拍打萧美琴窜访捷克 外交部回应卫健委通报少年有偿捐血浆16次猝死手机成瘾是影响睡眠质量重要因素高校汽车撞人致3死16伤 司机系学生315晚会后胖东来又人满为患了小米汽车超级工厂正式揭幕中国拥有亿元资产的家庭达13.3万户周杰伦一审败诉网易男孩8年未见母亲被告知被遗忘许家印被限制高消费饲养员用铁锨驱打大熊猫被辞退男子被猫抓伤后确诊“猫抓病”特朗普无法缴纳4.54亿美元罚金倪萍分享减重40斤方法联合利华开始重组张家界的山上“长”满了韩国人?张立群任西安交通大学校长杨倩无缘巴黎奥运“重生之我在北大当嫡校长”黑马情侣提车了专访95后高颜值猪保姆考生莫言也上北大硕士复试名单了网友洛杉矶偶遇贾玲专家建议不必谈骨泥色变沉迷短剧的人就像掉进了杀猪盘奥巴马现身唐宁街 黑色着装引猜测七年后宇文玥被薅头发捞上岸事业单位女子向同事水杯投不明物质凯特王妃现身!外出购物视频曝光河南驻马店通报西平中学跳楼事件王树国卸任西安交大校长 师生送别恒大被罚41.75亿到底怎么缴男子被流浪猫绊倒 投喂者赔24万房客欠租失踪 房东直发愁西双版纳热带植物园回应蜉蝣大爆发钱人豪晒法院裁定实锤抄袭外国人感慨凌晨的中国很安全胖东来员工每周单休无小长假白宫:哈马斯三号人物被杀测试车高速逃费 小米:已补缴老人退休金被冒领16年 金额超20万

玻璃钢生产厂家 XML地图 TXT地图 虚拟主机 SEO 网站制作 网站优化