1.准备知识

参考:带你玩转 3D 检测和分割 (二):核心组件分析之坐标系和 Box - 知乎

1.1 坐标系介绍

        激光雷达坐标系通常定义如下左图所示,其中x指向前方,y指向左方,z指向上方。

 相机坐标系通常定义如上右图所示,其中x指向右方,y指向下方,z指向前方。

1.2 3D边界框的定义

一般来说,对于自动驾驶目标检测任务而言,一个3D边界框可以由7个参数决定:位置(x,y,z)、尺寸(x  siza,y  siza,z  siza)以及朝向角/偏航角/旋转角θ。

一般将与物体朝向平行的棱的长度记为边界框长度l,竖直方向棱的长度记为边界框高度h,余下一组棱的长度记为边界框宽度w,如下左图所示。

 将边界框绕高度轴(对于激光雷达坐标系,高度轴为Z轴;对于相机坐标系,高度轴为Y轴)旋转到朝向与X轴正方向相同时,边界框与x,y,z坐标轴平行的3组棱对应的长度分别记为(x  siza,y  siza,z  siza)

        对于激光雷达坐标系下的边界框,其(x  siza,y  siza,z  siza)即为(l,w,h)(如上中图所示);则对于相机坐标系下的边界框,其(x  siza,y  siza,z  siza)即(l,w,h)(如上右图所示)。 

  朝向角为边界框绕高度轴的旋转角度(从X轴正向与边界框朝向之间的角度),其正负按照右手螺旋定则确定。

1.3 边界框的坐标变换

 从激光雷达坐标系到相机坐标系:

  • 位置变换直接按照点的坐标变换方法进行(需要坐标变换矩阵);
  • 尺寸上,x_size不变,y_size和z_size交换(原因见1.2节中灰底内容);
  • 旋转角先取相反数,再减去90度。

角度的变换是由于两个坐标系中,θ=0的基准轴X轴并不是同一个(相差90度),且由于相机坐标系高度轴正方向向下, 故角度增大的方向从上向下看是顺时针,与激光雷达坐标系中相反。

  注意:上述旋转角的变换的前提条件是,相机坐标系Y轴与激光雷达坐标系—Z轴朝向相同(多数情况下均符合此条件)。

后续内容是MMDetection3D的相关概念、函数和数据类型的介绍。若只是想了解3D目标检测的坐标系和边界框,则无需继续往下看。 

1.4 origin

在MMDetection3D一些边界框相关的函数中(如2.3节的函数),存在origin参数,这个origin表示边界框位置坐标对应的那个点在边界框中的相对位置。

 具体来说,在坐标系下先旋转3D边界框到朝向角为0,然后将其缩放为单位正方体,并平移到第一象限,使其一个角点位于坐标系原点,与角点相邻的三条棱分别与三个坐标轴重合。此时边界框位置坐标也相应地被变换到某处,其变换后的坐标即为origin。

  举例来说,一般的数据集会使用边界框底面中心坐标作为边界框的位置坐标,则在激光雷达坐标系下,origin应设为(0.5,0.5,0),如下左图所示:

而在相机坐标系下,origin应设为(0.5,1,0.5),如上右图所示。

2. mmdet3d/core/bbox/box_np_ops.py中的常用函数

 mmdet3d/core/bbox/box_np_ops.py中有很多与边界框相关的函数,这些函数通常会在数据准备的相关代码中看到。

2.1 remove_outside_points

目的是去除图像视锥外的点云,仅保留能投影到图像范围内的那些点。

输入

  • 原始点云(N×(3+dins));
  • 相机修正矩阵(3×3扩维成4×4);
  • 点云坐标系到相机坐标系的坐标变换阵(3×4扩维成4×4);
  • 相机内参矩阵(3×4扩维成4×4);
  • 图像大小(H,W)。

输出

  • 能投影到图像范围内的点云子集(N'×(3+dims))。

2.2 box_camera_to_lidar

 目的是将相机坐标系下的边界框参数变换为激光雷达坐标系下的参数。

输入

  • 相机坐标系下的3D边界框(M×7);
  • 相机修正矩阵(3×3扩维成4×4);
  • 点云坐标系到相机坐标系的坐标变换阵(3×4扩维成4×4)。

输出

  • 激光雷达坐标系下的3D边界框(M×7)。

        该函数的原理见1.3节。

        注意:该函数默认相机坐标系Y轴和激光雷达坐标系—Z轴同向;若坐标系不完全对齐(如View-of-Delft数据集),若输入该函数的旋转角定义为绕激光雷达—Z轴的旋转角(朝向激光雷达—Y轴时角度为0),则也可使用该函数。

2.3 points_in_bbox

目的是统计各点是否落在每个边界框内。

输入

  • 点云 (N×(3+dims));
  • 边界框( M×7 ,其与点云定义于同一坐标系下);
  • 旋转轴序号(0,1,2之一,表示高度轴是第几个坐标轴;对于图像坐标系,应设置为1;对于激光雷达坐标系,应设置为2);
  • origin(表示边界框位置坐标在边界框中的相对位置,详见1.4节)。

输出

  • 二值矩阵(N×M,其第i行第j列为True表示第i个点位于第j个边界框内) 。

2.4 center_to_corner_box3d

目的是将边界框的位置、尺寸、朝向角参数转换为边界框的8个角点坐标。

输入

  • 各边界框位置(N×3);
  • 各边界框尺寸(N×3);
  • 各边界框朝向角(N);
  • origin(表示边界框位置坐标在边界框中的相对位置,详见1.4节);
  • 旋转轴序号(0,1,2之一,表示高度轴是第几个坐标轴;对于图像坐标系,应设置为1;对于激光雷达坐标系,应设置为2)。

输出

  • 各边界框的8个角点坐标(N×8×3)。

  注意:所有边界框参数、origin以及旋转轴应对应同一坐标系。

3.点云在不同坐标系下的变换

3.1 view_points(位于nuscenes/utils/geometry_utils.py

        目的是将相机坐标系下的点云坐标转换到图像坐标系下。

输入

  • 相机坐标系下的点云(3×N);
  • 相机内参矩阵(3×4;可扩维成4×4);
  • 归一化选择(True则输出像素坐标,False则输出像平面上的坐标)。

输出

  • 图像坐标系下的点云(N×3,归一化时前两维为像素坐标,最后一维恒为1;否则最后一维为深度d,前两维为)。

3.2 points_cam2img(位于mmdet3d/core/bbox/structures/utils.py)

        目的是将相机坐标系下的点云坐标转换到图像坐标系下。

输入

  • 相机坐标系下的点云(×3);
  • 相机内参矩阵(3×3,可扩维成3×4或4×4);
  • 是否包含深度信息的选择(True则输出包含深度信息)。

输出

  • 图像坐标系下的点云(若输出包含深度信息则为×3,前两维为像素坐标(u,v),最一维为深度d;否则输出为,为像素坐标

注:上述维度中的 “  ” 代表任意多个维度,如可以为N×3等。

3.3 点云数据类型及其坐标变换

mmdet3d提供了定义好的边界框数据类型LiDARPoints(位于mmdet3d/core/points/lidar_points.py下)和CameraPoints(位于mmdet3d/core/points/camera_points.py下),分别是激光雷达坐标系下的点云以及相机坐标系下的点云数据类型。一些方法已经内置于数据结构中,可直接调用。

  以LiDARPoints为例,首先需要创建该类的实例:

# 设pts为(N,C)的Tensor,表示N个C维的点(C>=3),其中前三个维度分别是x,y,z坐标
pts = LiDARPoints(pts, points_dim=C)

然后可以直接调用flip/rotate等数据增广方法以及坐标变换方法。

convert_to:坐标变换方法。

输入:

  • dst:目标坐标系,为Coord3DMode.LIDARCoord3DMode.CAM
  • rt_mat:坐标变换矩阵。

若输入dst=Coord3dMode.LIDAR,则输出数据类型为LiDARPoints;若输入dst=Coord3dMode.CAM,则输出数据类型为CameraPoints。

4.边界框的数据类型及其在不同坐标系下的变换

 mmdet3d提供了定义好的边界框数据类型LiDARInstance3DBoxes(位于mmdet3d/core/bbox/structure/lidar_box3d.py下)以及CameraInstance3DBoxes(位于mmdet3d/core/bbox/structure/cam_box3d.py下),分别是激光雷达坐标系下的3D边界框以及相机坐标系下的3D边界框数据类型,许多方法已经内置于数据结构中,可直接调用。

   以LiDARInstance3DBoxes类为例,首先需要创建该类的实例:

# 设bboxes为大小(M,7)的Tensor,其中M为边界框数量,7代表x,y,z坐标,x,y,z尺寸以及朝向角,若有速度等其它参数,需放置在最后
bboxes = LiDARInstance3DBoxes(bboxes)
# 另有三项输入box_dim(每个边界框的参数数量),with_yaw(包含朝向角为True)和origin(含义见前文介绍,指示3D坐标在边界框中的相对位置)
# 可使用默认值box_dim=7, with_yaw=True, origin=(0.5, 0.5, 0),

然后可以直接调用如下方法(没有全部介绍):

  • gravity_center:取边界框几何中心(M,3);
  • corners:取边界框8个角点(M,8,3)
  • bev:取BEV下的边界框参数(x,y坐标、x,y尺寸以及朝向角(M,5);
  • in_range_bev:检查中心在BEV范围内的边界框。 
  • 输入:BEV范围(x_min,y_min,x_max,y_max);
  • 输出:M维bool值向量,若第i个元素为True则表示第i个边界框中心在该范围内。
  • limit_yaw:限制朝向角范围。
  • 输入:
    • offset:原点在角度区间中的相对位置;
    • period:角度范围的大小。
  • 例如,要将朝向角限制在(—π,π)的范围内,period需取2π,offset取0.5(表示原点在区间正中)。
  • 该方法的实现实际上是调用了mmdet3d/core/bboxes/structures/utils.py中的limit_period函数,因此在单独限制角度范围时可直接调用limit_period函数(输入依次为角度张量、offset和period)。

  • ​​​​​​​convert_to:坐标系变换方法。
  • 输入:
  • dst:目标坐标系,为Box3dMode.LIDARBox3dMode.CAM(Box3dMode类位于mmdet3d/core/bbox/structure/box_3d_mode.py下);
  • rt_mat:源坐标系到目标坐标系的坐标变换矩阵(4×4)。
  • 若输入dst=Box3dMode.LIDAR,则输出数据类型为LiDARInstance3DBoxes;若输入dst=Box3dMode.CAM,则输出数据类型为CameraInstance3DBoxes。
  • 注意:当相机坐标系Y轴与激光雷达坐标系—Z轴朝向不完全相同时,朝向角的变换是不正确的。

调用上述方法的代码如下(以gravity_center为例):

bboxes_centers = bboxes.gravity_center