Unverified Commit d7067e44 authored by Wenwei Zhang's avatar Wenwei Zhang Committed by GitHub
Browse files

Bump version to v1.1.0rc2

Bump to v1.1.0rc2
parents 28fe73d2 fb0e57e5
# 常见问题解答 # 常见问题解答
我们列出了一些用户和开发者在开发过程中会遇到的常见问题以及对应的解决方案,如果您发现了任何频繁出现的问题,请随时扩充本列表,非常欢迎您提出的任何解决方案。如果您在环境配置、模型训练等工作中遇到任何的问题,请使用[问题模板](https://github.com/open-mmlab/mmdetection3d/blob/master/.github/ISSUE_TEMPLATE/error-report.md/)来创建相应的 issue,并将所需的所有信息填入到问题模板中,我们会尽快解决您的问题。 我们列出了一些用户和开发者在开发过程中会遇到的常见问题以及对应的解决方案,如果您发现了任何频繁出现的问题,请随时扩充本列表,非常欢迎您提出的任何解决方案。如果您在环境配置、模型训练等工作中遇到任何的问题,请使用[问题模板](https://github.com/open-mmlab/mmdetection3d/blob/master/.github/ISSUE_TEMPLATE/error-report.md)来创建相应的 issue,并将所需的所有信息填入到问题模板中,我们会尽快解决您的问题。
## MMCV/MMDet/MMDet3D Installation ## MMEngine/MMCV/MMDet/MMDet3D 安装
- 跟 MMCV, MMDetection, MMSegmentation 和 MMDetection3D 相关的编译问题; "ConvWS is already registered in conv layer"; "AssertionError: MMCV==xxx is used but incompatible. Please install mmcv>=xxx, \<=xxx." - 跟 MMEngine, MMCV, MMDetection 和 MMDetection3D 相关的编译问题; "ConvWS is already registered in conv layer"; "AssertionError: MMCV==xxx is used but incompatible. Please install mmcv>=xxx, \<=xxx."
MMDetection3D 需要的 MMCV, MMDetection 和 MMSegmentation 的版本列在了下面。请安装正确版本的 MMCV、MMDetection 和 MMSegmentation 以避免相关的安装问题。 - MMDetection3D 需要的 MMEngine, MMCV 和 MMDetection 的版本列在了下面。请安装正确版本的 MMEngine、MMCV 和 MMDetection 以避免相关的安装问题。
| MMDetection3D version | MMDetection version | MMSegmentation version | MMCV version | | MMDetection3D 版本 | MMEngine 版本 | MMCV 版本 | MMDetection 版本 |
| :-------------------: | :----------------------: | :---------------------: | :-------------------------: | | ---------------- | :----------------------: | :---------------------: | :----------------------: |
| master | mmdet>=2.24.0, \<=3.0.0 | mmseg>=0.20.0, \<=1.0.0 | mmcv-full>=1.5.2, \<=1.7.0 | | dev-1.x | mmengine>=0.1.0, \<1.0.0 | mmcv>=2.0.0rc0, \<2.1.0 | mmdet>=3.0.0rc0, \<3.1.0 |
| v1.0.0rc4 | mmdet>=2.24.0, \<=3.0.0 | mmseg>=0.20.0, \<=1.0.0 | mmcv-full>=1.5.2, \<=1.7.0 | | v1.1.0rc1 | mmengine>=0.1.0, \<1.0.0 | mmcv>=2.0.0rc0, \<2.1.0 | mmdet>=3.0.0rc0, \<3.1.0 |
| v1.0.0rc3 | mmdet>=2.24.0, \<=3.0.0 | mmseg>=0.20.0, \<=1.0.0 | mmcv-full>=1.4.8, \<=1.6.0 | | v1.1.0rc0 | mmengine>=0.1.0, \<1.0.0 | mmcv>=2.0.0rc0, \<2.1.0 | mmdet>=3.0.0rc0, \<3.1.0 |
| v1.0.0rc2 | mmdet>=2.24.0, \<=3.0.0 | mmseg>=0.20.0, \<=1.0.0 | mmcv-full>=1.4.8, \<=1.6.0 |
| v1.0.0rc1 | mmdet>=2.19.0, \<=3.0.0 | mmseg>=0.20.0, \<=1.0.0 | mmcv-full>=1.4.8, \<=1.5.0 | **注意**:如果你想安装 mmdet3d-v1.0.0rcx,可以在[此处](https://mmdetection3d.readthedocs.io/en/latest/faq.html#mmcv-mmdet-mmdet3d-installation)找到 MMDetection,MMSegmentation 和 MMCV 的兼容版本。请选择正确版本的 MMCV、MMDetection 和 MMSegmentation 以避免安装问题。
| v1.0.0rc0 | mmdet>=2.19.0, \<=3.0.0 | mmseg>=0.20.0, \<=1.0.0 | mmcv-full>=1.3.17, \<=1.5.0 |
| 0.18.1 | mmdet>=2.19.0, \<=3.0.0 | mmseg>=0.20.0, \<=1.0.0 | mmcv-full>=1.3.17, \<=1.5.0 |
| 0.18.0 | mmdet>=2.19.0, \<=3.0.0 | mmseg>=0.20.0, \<=1.0.0 | mmcv-full>=1.3.17, \<=1.5.0 |
| 0.17.3 | mmdet>=2.14.0, \<=3.0.0 | mmseg>=0.14.1, \<=1.0.0 | mmcv-full>=1.3.8, \<=1.4.0 |
| 0.17.2 | mmdet>=2.14.0, \<=3.0.0 | mmseg>=0.14.1, \<=1.0.0 | mmcv-full>=1.3.8, \<=1.4.0 |
| 0.17.1 | mmdet>=2.14.0, \<=3.0.0 | mmseg>=0.14.1, \<=1.0.0 | mmcv-full>=1.3.8, \<=1.4.0 |
| 0.17.0 | mmdet>=2.14.0, \<=3.0.0 | mmseg>=0.14.1, \<=1.0.0 | mmcv-full>=1.3.8, \<=1.4.0 |
| 0.16.0 | mmdet>=2.14.0, \<=3.0.0 | mmseg>=0.14.1, \<=1.0.0 | mmcv-full>=1.3.8, \<=1.4.0 |
| 0.15.0 | mmdet>=2.14.0, \<=3.0.0 | mmseg>=0.14.1, \<=1.0.0 | mmcv-full>=1.3.8, \<=1.4.0 |
| 0.14.0 | mmdet>=2.10.0, \<=2.11.0 | mmseg==0.14.0 | mmcv-full>=1.3.1, \<=1.4.0 |
| 0.13.0 | mmdet>=2.10.0, \<=2.11.0 | Not required | mmcv-full>=1.2.4, \<=1.4.0 |
| 0.12.0 | mmdet>=2.5.0, \<=2.11.0 | Not required | mmcv-full>=1.2.4, \<=1.4.0 |
| 0.11.0 | mmdet>=2.5.0, \<=2.11.0 | Not required | mmcv-full>=1.2.4, \<=1.3.0 |
| 0.10.0 | mmdet>=2.5.0, \<=2.11.0 | Not required | mmcv-full>=1.2.4, \<=1.3.0 |
| 0.9.0 | mmdet>=2.5.0, \<=2.11.0 | Not required | mmcv-full>=1.2.4, \<=1.3.0 |
| 0.8.0 | mmdet>=2.5.0, \<=2.11.0 | Not required | mmcv-full>=1.1.5, \<=1.3.0 |
| 0.7.0 | mmdet>=2.5.0, \<=2.11.0 | Not required | mmcv-full>=1.1.5, \<=1.3.0 |
| 0.6.0 | mmdet>=2.4.0, \<=2.11.0 | Not required | mmcv-full>=1.1.3, \<=1.2.0 |
| 0.5.0 | 2.3.0 | Not required | mmcv-full==1.0.5 |
- 如果您在 `import open3d` 时遇到下面的问题: - 如果您在 `import open3d` 时遇到下面的问题:
...@@ -69,4 +50,4 @@ MMDetection3D 不支持点云标注。我们提供一些开源的标注工具供 ...@@ -69,4 +50,4 @@ MMDetection3D 不支持点云标注。我们提供一些开源的标注工具供
- [SUSTechPOINTS](https://github.com/naurril/SUSTechPOINTS) - [SUSTechPOINTS](https://github.com/naurril/SUSTechPOINTS)
- [LATTE](https://github.com/bernwang/latte) - [LATTE](https://github.com/bernwang/latte)
此外,我们改进了 [LATTE](https://github.com/bernwang/latte) 以便更方便的标注。 更多的细节请参考 [这里](https://arxiv.org/abs/2011.10174) 此外,我们改进了 [LATTE](https://github.com/bernwang/latte) 以便更方便的标注。更多的细节请参考[这里](https://arxiv.org/abs/2011.10174)
...@@ -2,6 +2,7 @@ ...@@ -2,6 +2,7 @@
:maxdepth: 3 :maxdepth: 3
benchmarks.md benchmarks.md
changelog_v1.0.x.md
changelog.md changelog.md
compatibility.md compatibility.md
faq.md faq.md
...@@ -18,7 +18,7 @@ ...@@ -18,7 +18,7 @@
由于前两种方式比第三种方式更加容易,我们更加建议采用前两种方式来自定义数据集。 由于前两种方式比第三种方式更加容易,我们更加建议采用前两种方式来自定义数据集。
在本文中,我们采用第一种方式来将 Waymo 数据集重新组织成 KITTI 数据集的数据格式 在本文中,我们给出示例将数据转换成 KITTI 数据集的数据格式,你可以参考此处将你的数据集重新组织成 KITTI 格式。关于标准格式的数据集,你可以参考[自定义数据集文档](https://github.com/open-mmlab/mmdetection3d/blob/dev-1.x/docs/zh_cn/advanced_guides/customize_dataset.md)
**注意**:考虑到 Waymo 数据集的格式与现有的其他数据集的格式的差别较大,因此本文以该数据集为例来讲解如何自定义数据集,从而方便理解数据集自定义的过程。若需要创建的新数据集与现有的数据集的组织格式较为相似,如 Lyft 数据集和 nuScenes 数据集,采用对数据集的中间格式进行转换的方式(第二种方式)相比于采用对数据格式进行转换的方式(第一种方式)会更加简单易行。 **注意**:考虑到 Waymo 数据集的格式与现有的其他数据集的格式的差别较大,因此本文以该数据集为例来讲解如何自定义数据集,从而方便理解数据集自定义的过程。若需要创建的新数据集与现有的数据集的组织格式较为相似,如 Lyft 数据集和 nuScenes 数据集,采用对数据集的中间格式进行转换的方式(第二种方式)相比于采用对数据格式进行转换的方式(第一种方式)会更加简单易行。
...@@ -65,10 +65,11 @@ KITTI 官方提供的目标检测开发[工具包](https://s3.eu-central-1.amazo ...@@ -65,10 +65,11 @@ KITTI 官方提供的目标检测开发[工具包](https://s3.eu-central-1.amazo
1 得分  仅在计算结果时使用,检测中表示置信度的浮点数,用于生成 p/r 曲线,在p/r 图中,越高的曲线表示结果越好。 1 得分  仅在计算结果时使用,检测中表示置信度的浮点数,用于生成 p/r 曲线,在p/r 图中,越高的曲线表示结果越好。
``` ```
接下来本文将对 Waymo 数据集原始格式进行转换。 假定我们使用 Waymo 数据集。
首先需要将下载的 Waymo 数据集的数据文件和标注文件转换到 KITTI 数据集的格式,接着定义一个从 KittiDataset 类继承而来的 WaymoDataset 类,来帮助数据的加载、模型的训练和评估。
具体来说,首先使用[数据转换器](https://github.com/open-mmlab/mmdetection3d/blob/master/tools/data_converter/waymo_converter.py)将 Waymo 数据集转换成 KITTI 数据集的格式,并定义 [Waymo 类](https://github.com/open-mmlab/mmdetection3d/blob/master/mmdet3d/datasets/waymo_dataset.py)对转换的数据进行处理。因为我们将 Waymo 原始数据集进行预处理并重新组织成 KITTI 数据集的格式,因此可以比较容易通过继承 KittiDataset 类来实现 WaymoDataset 类。需要注意的是,由于 Waymo 数据集有相应的官方评估方法,我们需要在定义新数据类的过程中引入官方评估方法,此时用户可以顺利的转换 Waymo 数据的格式,并使用 `WaymoDataset` 数据类进行模型的训练和评估。 在下载好数据集后,我们需要实现一个函数用来将输入数据和标注文件转换成 KITTI 风格。然后我们可以通过继承 `KittiDataset` 实现 `WaymoDataset`,用来加载数据以及训练模型,通过继承 `KittiMetric` 实现 `WaymoMetric` 来做模型的评估。
具体来说,首先使用[数据转换器](https://github.com/open-mmlab/mmdetection3d/blob/dev-1.x/tools/dataset_converters/waymo_converter.py)将 Waymo 数据集转换成 KITTI 数据集的格式,并定义 [Waymo 类](https://github.com/open-mmlab/mmdetection3d/blob/dev-1.x/mmdet3d/datasets/waymo_dataset.py)对转换的数据进行处理。此外需要添加 waymo [评估类](https://github.com/open-mmlab/mmdetection3d/blob/dev-1.x/mmdet3d/evaluation/metrics/waymo_metric.py)来评估结果。因为我们将 Waymo 原始数据集进行预处理并重新组织成 KITTI 数据集的格式,因此可以比较容易通过继承 KittiDataset 类来实现 WaymoDataset 类。需要注意的是,由于 Waymo 数据集有相应的官方评估方法,我们需要进一步实现新的 Waymo 评估方法,更多关于评估方法参考[评估文档](https://github.com/open-mmlab/mmengine/blob/main/docs/en/tutorials/metric_and_evaluator.md)。最后,用户可以成功地转换数据并使用 `WaymoDataset` 训练以及 `WaymoMetric` 评估模型。
更多关于 Waymo 数据集预处理的中间结果的细节,请参照对应的[说明文档](https://mmdetection3d.readthedocs.io/zh_CN/latest/datasets/waymo_det.html) 更多关于 Waymo 数据集预处理的中间结果的细节,请参照对应的[说明文档](https://mmdetection3d.readthedocs.io/zh_CN/latest/datasets/waymo_det.html)
...@@ -76,14 +77,14 @@ KITTI 官方提供的目标检测开发[工具包](https://s3.eu-central-1.amazo ...@@ -76,14 +77,14 @@ KITTI 官方提供的目标检测开发[工具包](https://s3.eu-central-1.amazo
第二步是准备配置文件来帮助数据集的读取和使用,另外,为了在 3D 检测中获得不错的性能,调整超参数通常是必要的。 第二步是准备配置文件来帮助数据集的读取和使用,另外,为了在 3D 检测中获得不错的性能,调整超参数通常是必要的。
假设我们想要使用 PointPillars 模型在 Waymo 数据集上实现三类的 3D 目标检测:vehicle、cyclist、pedestrian,参照 KITTI 数据集[配置文件](https://github.com/open-mmlab/mmdetection3d/blob/master/configs/_base_/datasets/kitti-3d-3class.py)、模型[配置文件](https://github.com/open-mmlab/mmdetection3d/blob/master/configs/_base_/models/hv_pointpillars_secfpn_kitti.py)[整体配置文件](https://github.com/open-mmlab/mmdetection3d/blob/master/configs/pointpillars/hv_pointpillars_secfpn_6x8_160e_kitti-3d-3class.py),我们需要准备[数据集配置文件](https://github.com/open-mmlab/mmdetection3d/blob/master/configs/_base_/datasets/waymoD5-3d-3class.py)[模型配置文件](https://github.com/open-mmlab/mmdetection3d/blob/master/configs/_base_/models/hv_pointpillars_secfpn_waymo.py),并将这两种文件进行结合得到[整体配置文件](https://github.com/open-mmlab/mmdetection3d/blob/master/configs/pointpillars/hv_pointpillars_secfpn_sbn_2x16_2x_waymoD5-3d-3class.py) 假设我们想要使用 PointPillars 模型在 Waymo 数据集上实现三类的 3D 目标检测:vehicle、cyclist、pedestrian,参照 KITTI 数据集[配置文件](https://github.com/open-mmlab/mmdetection3d/blob/dev-1.x/configs/_base_/datasets/kitti-3d-3class.py)、模型[配置文件](https://github.com/open-mmlab/mmdetection3d/blob/dev-1.x/configs/_base_/models/pointpillars_hv_secfpn_kitti.py)[整体配置文件](https://github.com/open-mmlab/mmdetection3d/blob/dev-1.x/configs/pointpillars/pointpillars_hv_secfpn_8xb6-160e_kitti-3d-3class.py),我们需要准备[数据集配置文件](https://github.com/open-mmlab/mmdetection3d/blob/dev-1.x/configs/_base_/datasets/waymoD5-3d-3class.py)[模型配置文件](https://github.com/open-mmlab/mmdetection3d/blob/dev-1.x/configs/_base_/models/pointpillars_hv_secfpn_waymo.py),并将这两种文件进行结合得到[整体配置文件](https://github.com/open-mmlab/mmdetection3d/blob/dev-1.x/configs/pointpillars/pointpillars_hv_secfpn_sbn-all_16xb2-2x_waymoD5-3d-3class.py)
## 训练一个新的模型 ## 训练一个新的模型
为了使用一个新的配置文件来训练模型,可以通过下面的命令来实现: 为了使用一个新的配置文件来训练模型,可以通过下面的命令来实现:
```shell ```shell
python tools/train.py configs/pointpillars/hv_pointpillars_secfpn_sbn_2x16_2x_waymoD5-3d-3class.py python tools/train.py configs/pointpillars/pointpillars_hv_secfpn_sbn-all_16xb2-2x_waymoD5-3d-3class.py
``` ```
更多的使用细节,请参考[案例 1](https://mmdetection3d.readthedocs.io/zh_CN/latest/1_exist_data_model.html) 更多的使用细节,请参考[案例 1](https://mmdetection3d.readthedocs.io/zh_CN/latest/1_exist_data_model.html)
...@@ -93,7 +94,7 @@ python tools/train.py configs/pointpillars/hv_pointpillars_secfpn_sbn_2x16_2x_wa ...@@ -93,7 +94,7 @@ python tools/train.py configs/pointpillars/hv_pointpillars_secfpn_sbn_2x16_2x_wa
为了测试已经训练好的模型的性能,可以通过下面的命令来实现: 为了测试已经训练好的模型的性能,可以通过下面的命令来实现:
```shell ```shell
python tools/test.py configs/pointpillars/hv_pointpillars_secfpn_sbn_2x16_2x_waymoD5-3d-3class.py work_dirs/hv_pointpillars_secfpn_sbn_2x16_2x_waymoD5-3d-3class/latest.pth --eval waymo python tools/test.py configs/pointpillars/pointpillars_hv_secfpn_sbn-all_16xb2-2x_waymoD5-3d-3class.py work_dirs/pointpillars_hv_secfpn_sbn-all_16xb2-2x_waymoD5-3d-3class/latest.pth
``` ```
**注意**:为了使用 Waymo 数据集的评估方法,需要参考[说明文档](https://mmdetection3d.readthedocs.io/zh_CN/latest/datasets/waymo_det.html)并按照官方指导来准备与评估相关联的文件。 **注意**:为了使用 Waymo 数据集的评估方法,需要参考[说明文档](https://mmdetection3d.readthedocs.io/zh_CN/latest/datasets/waymo_det.html)并按照官方指导来准备与评估相关联的文件。
......
# 教程 7: 后端支持 # 后端支持
我们支持不同的文件客户端后端:磁盘、Ceph 和 LMDB 等。下面是修改配置使之从 Ceph 加载和保存数据的示例。 我们支持不同的文件客户端后端:磁盘、Ceph 和 LMDB 等。下面是修改配置使之从 Ceph 加载和保存数据的示例。
......
# 教程 1: 学习配置文件 # 学习配置文件
我们在配置文件中支持了继承和模块化来方便进行各种实验。 MMDetection3D 和其他 OpenMMLab 仓库使用[MMEngine 配置系统](https://mmengine.readthedocs.io/zh_CN/latest/tutorials/config.html)。它具有模块化和继承性设计,便于进行各种实验。如果希望检查配置文件,可以通过运行 `python tools/misc/print_config.py /PATH/TO/CONFIG` 来查看完整的配置。
如果需要检查配置文件,可以通过运行 `python tools/misc/print_config.py /PATH/TO/CONFIG` 来查看完整的配置。
你也可以传入 `--options xxx.yyy=zzz` 参数来查看更新后的配置。
## 配置文件结构 ## 配置文件内容
`config/_base_` 文件夹下有 4 个基本组件类型,分别是:数据集 (dataset),模型 (model),训练策略 (schedule) 和运行时的默认设置 (default runtime)。 MMDetection3D 使用模块化的设计,所有具有不同功能的模块都可以通过配置文件进行配置。以 PointPillars 为例,我们将根据不同的功能模块介绍配置文件的每一个字段。
通过从上述每个文件夹中选取一个组件进行组合,许多方法如 SECOND、PointPillars、PartA2 和 VoteNet 都能够很容易地构建出来。
`_base_` 下的组件组成的配置,被我们称为 _原始配置 (primitive)_。
对于同一文件夹下的所有配置,推荐**只有一个**对应的 _原始配置_ 文件,所有其他的配置文件都应该继承自这个 _原始配置_ 文件,这样就能保证配置文件的最大继承深度为 3。 ### 模型配置
为了便于理解,我们建议贡献者继承现有方法。 在 mmdetection3d 配置中,我们使用 `model` 设置检测算法组件。除了神经网络组件,如 `voxel_encoder``backbone` 等,它还需要 `data_preprocessor``train_cfg``test_cfg``data_preprocessor` 负责处理 dataloader 输出的一个批次的数据。模型配置中的 `train_cfg``test_cfg` 用于设置训练和测试组件的超参数。
例如,如果在 PointPillars 的基础上做了一些修改,用户首先可以通过指定 `_base_ = ../pointpillars/hv_pointpillars_fpn_sbn-all_4x8_2x_nus-3d.py` 来继承基础的 PointPillars 结构,然后修改配置文件中的必要参数以完成继承。
如果你在构建一个与任何现有方法不共享结构的全新方法,可以在 `configs` 文件夹下创建一个新的例如 `xxx_rcnn` 文件夹。
更多细节请参考 [MMCV](https://mmcv.readthedocs.io/en/latest/understand_mmcv/config.html) 文档。
## 配置文件名称风格
我们遵循以下样式来命名配置文件,并建议贡献者遵循相同的风格。
```
{model}_[model setting]_{backbone}_{neck}_[norm setting]_[misc]_[gpu x batch_per_gpu]_{schedule}_{dataset}
```
`{xxx}` 是被要求填写的字段而 `[yyy]` 是可选的。
- `{model}`:模型种类,例如 `hv_pointpillars` (Hard Voxelization PointPillars)、`VoteNet` 等。
- `[model setting]`:某些模型的特殊设定。
- `{backbone}`: 主干网络种类例如 `regnet-400mf``regnet-1.6gf` 等。
- `{neck}`:模型颈部的种类包括 `fpn``secfpn` 等。
- `[norm_setting]`:如无特殊声明,默认使用 `bn` (Batch Normalization),其他类型可以有 `gn` (Group Normalization)、`sbn` (Synchronized Batch Normalization) 等。
`gn-head`/`gn-neck` 表示 GN 仅应用于网络的头部或颈部,而 `gn-all` 表示 GN 用于整个模型,例如主干网络、颈部和头部。
- `[misc]`:模型中各式各样的设置/插件,例如 `strong-aug` 意味着在训练过程中使用更强的数据增广策略。
- `[batch_per_gpu x gpu]`:每个 GPU 的样本数和 GPU 数量,默认使用 `4x8`
- `{schedule}`:训练方案,选项是 `1x``2x``20e` 等。
`1x``2x` 分别代表训练 12 和 24 轮。
`20e` 在级联模型中使用,表示训练 20 轮。
对于 `1x`/`2x`,初始学习率在第 8/16 和第 11/22 轮衰减 10 倍;对于 `20e`,初始学习率在第 16 和第 19 轮衰减 10 倍。
- `{dataset}`:数据集,例如 `nus-3d``kitti-3d``lyft-3d``scannet-3d``sunrgbd-3d` 等。
当某一数据集存在多种设定时,我们也标记下所使用的类别数量,例如 `kitti-3d-3class``kitti-3d-car` 分别意味着在 KITTI 的所有三类上和单独车这一类上进行训练。
## 弃用的 train_cfg/test_cfg
遵循 MMDetection 的做法,我们在配置文件中弃用 `train_cfg``test_cfg`,请在模型配置中指定它们。
原始的配置结构如下:
```python ```python
# 已经弃用的形式
model = dict( model = dict(
type=..., type='VoxelNet',
... data_preprocessor=dict(
) type='Det3DDataPreprocessor',
train_cfg=dict(...) voxel=True,
test_cfg=dict(...) voxel_layer=dict(
max_num_points=32,
point_cloud_range=[0, -39.68, -3, 69.12, 39.68, 1],
voxel_size=[0.16, 0.16, 4],
max_voxels=(16000, 40000))),
voxel_encoder=dict(
type='PillarFeatureNet',
in_channels=4,
feat_channels=[64],
with_distance=False,
voxel_size=[0.16, 0.16, 4],
point_cloud_range=[0, -39.68, -3, 69.12, 39.68, 1]),
middle_encoder=dict(
type='PointPillarsScatter', in_channels=64, output_shape=[496, 432]),
backbone=dict(
type='SECOND',
in_channels=64,
layer_nums=[3, 5, 5],
layer_strides=[2, 2, 2],
out_channels=[64, 128, 256]),
neck=dict(
type='SECONDFPN',
in_channels=[64, 128, 256],
upsample_strides=[1, 2, 4],
out_channels=[128, 128, 128]),
bbox_head=dict(
type='Anchor3DHead',
num_classes=3,
in_channels=384,
feat_channels=384,
use_direction_classifier=True,
assign_per_class=True,
anchor_generator=dict(
type='AlignedAnchor3DRangeGenerator',
ranges=[[0, -39.68, -0.6, 69.12, 39.68, -0.6],
[0, -39.68, -0.6, 69.12, 39.68, -0.6],
[0, -39.68, -1.78, 69.12, 39.68, -1.78]],
sizes=[[0.8, 0.6, 1.73], [1.76, 0.6, 1.73], [3.9, 1.6, 1.56]],
rotations=[0, 1.57],
reshape_out=False),
diff_rad_by_sin=True,
bbox_coder=dict(type='DeltaXYZWLHRBBoxCoder'),
loss_cls=dict(
type='mmdet.FocalLoss',
use_sigmoid=True,
gamma=2.0,
alpha=0.25,
loss_weight=1.0),
loss_bbox=dict(
type='mmdet.SmoothL1Loss',
beta=0.1111111111111111,
loss_weight=2.0),
loss_dir=dict(
type='mmdet.CrossEntropyLoss', use_sigmoid=False,
loss_weight=0.2)),
train_cfg=dict(
assigner=[
dict(
type='Max3DIoUAssigner',
iou_calculator=dict(type='mmdet3d.BboxOverlapsNearest3D'),
pos_iou_thr=0.5,
neg_iou_thr=0.35,
min_pos_iou=0.35,
ignore_iof_thr=-1),
dict(
type='Max3DIoUAssigner',
iou_calculator=dict(type='mmdet3d.BboxOverlapsNearest3D'),
pos_iou_thr=0.5,
neg_iou_thr=0.35,
min_pos_iou=0.35,
ignore_iof_thr=-1),
dict(
type='Max3DIoUAssigner',
iou_calculator=dict(type='mmdet3d.BboxOverlapsNearest3D'),
pos_iou_thr=0.6,
neg_iou_thr=0.45,
min_pos_iou=0.45,
ignore_iof_thr=-1)
],
allowed_border=0,
pos_weight=-1,
debug=False),
test_cfg=dict(
use_rotate_nms=True,
nms_across_levels=False,
nms_thr=0.01,
score_thr=0.1,
min_bbox_size=0,
nms_pre=100,
max_num=50))
``` ```
迁移后的配置结构如下: ### 数据集和评估器配置
```python [执行器(Runner)](https://mmengine.readthedocs.io/zh_CN/latest/tutorials/runner.html)的训练,验证和测试需要[数据加载器(dataloaders)](https://pytorch.org/docs/stable/data.html?highlight=data%20loader#torch.utils.data.DataLoader)。构建数据加载器需要数据集和数据流水线。由于这一部分的复杂性,我们使用中间变量简化数据加载配置文件的编写。
# 推荐的形式
model = dict(
type=...,
...
train_cfg=dict(...),
test_cfg=dict(...),
)
```
## VoteNet 配置文件示例
```python ```python
model = dict( dataset_type = 'KittiDataset'
type='VoteNet', # 检测器的类型,更多细节请参考 mmdet3d.models.detectors data_root = 'data/kitti/'
backbone=dict( class_names = ['Pedestrian', 'Cyclist', 'Car']
type='PointNet2SASSG', # 主干网络的类型,更多细节请参考 mmdet3d.models.backbones point_cloud_range = [0, -39.68, -3, 69.12, 39.68, 1]
in_channels=4, # 点云输入通道数 input_modality = dict(use_lidar=True, use_camera=False)
num_points=(2048, 1024, 512, 256), # 每个 SA 模块采样的中心点的数量 metainfo = dict(classes=['Pedestrian', 'Cyclist', 'Car'])
radius=(0.2, 0.4, 0.8, 1.2), # 每个 SA 层的半径 db_sampler = dict(
num_samples=(64, 32, 16, 16), # 每个 SA 层聚集的点的数量 data_root='data/kitti/',
sa_channels=((64, 64, 128), (128, 128, 256), (128, 128, 256), info_path='data/kitti/kitti_dbinfos_train.pkl',
(128, 128, 256)), # SA 模块中每个多层感知器的输出通道数 rate=1.0,
fp_channels=((256, 256), (256, 256)), # FP 模块中每个多层感知器的输出通道数 prepare=dict(
norm_cfg=dict(type='BN2d'), # 归一化层的配置 filter_by_difficulty=[-1],
sa_cfg=dict( # 点集抽象 (SA) 模块的配置 filter_by_min_points=dict(Car=5, Pedestrian=5, Cyclist=5)),
type='PointSAModule', # SA 模块的类型 classes=['Pedestrian', 'Cyclist', 'Car'],
pool_mod='max', # SA 模块的池化方法 (最大池化或平均池化) sample_groups=dict(Car=15, Pedestrian=15, Cyclist=15),
use_xyz=True, # 在特征聚合中是否使用 xyz 坐标 points_loader=dict(
normalize_xyz=True)), # 在特征聚合中是否使用标准化的 xyz 坐标 type='LoadPointsFromFile', coord_type='LIDAR', load_dim=4, use_dim=4))
bbox_head=dict( train_pipeline = [
type='VoteHead', # 检测框头的类型,更多细节请参考 mmdet3d.models.dense_heads dict(type='LoadPointsFromFile', coord_type='LIDAR', load_dim=4, use_dim=4),
num_classes=18, # 分类的类别数量 dict(type='LoadAnnotations3D', with_bbox_3d=True, with_label_3d=True),
bbox_coder=dict(
type='PartialBinBasedBBoxCoder', # 框编码层的类型,更多细节请参考 mmdet3d.core.bbox.coders
num_sizes=18, # 尺寸聚类的数量
num_dir_bins=1, # 编码方向角的间隔数
with_rot=False, # 框是否带有旋转角度
mean_sizes=[[0.76966727, 0.8116021, 0.92573744],
[1.876858, 1.8425595, 1.1931566],
[0.61328, 0.6148609, 0.7182701],
[1.3955007, 1.5121545, 0.83443564],
[0.97949594, 1.0675149, 0.6329687],
[0.531663, 0.5955577, 1.7500148],
[0.9624706, 0.72462326, 1.1481868],
[0.83221924, 1.0490936, 1.6875663],
[0.21132214, 0.4206159, 0.5372846],
[1.4440073, 1.8970833, 0.26985747],
[1.0294262, 1.4040797, 0.87554324],
[1.3766412, 0.65521795, 1.6813129],
[0.6650819, 0.71111923, 1.298853],
[0.41999173, 0.37906948, 1.7513971],
[0.59359556, 0.5912492, 0.73919016],
[0.50867593, 0.50656086, 0.30136237],
[1.1511526, 1.0546296, 0.49706793],
[0.47535285, 0.49249494, 0.5802117]]), # 每一类的平均尺寸,其顺序与类名顺序相同
vote_moudule_cfg=dict( # 投票 (vote) 模块的配置,更多细节请参考 mmdet3d.models.model_utils
in_channels=256, # 投票模块的输入通道数
vote_per_seed=1, # 对于每个种子点生成的投票数
gt_per_seed=3, # 每个种子点的真实标签个数
conv_channels=(256, 256), # 卷积通道数
conv_cfg=dict(type='Conv1d'), # 卷积配置
norm_cfg=dict(type='BN1d'), # 归一化层配置
norm_feats=True, # 是否标准化特征
vote_loss=dict( # 投票分支的损失函数配置
type='ChamferDistance', # 投票分支的损失函数类型
mode='l1', # 投票分支的损失函数模式
reduction='none', # 设置对损失函数输出的聚合方法
loss_dst_weight=10.0)), # 投票分支的目标损失权重
vote_aggregation_cfg=dict( # 投票聚合分支的配置
type='PointSAModule', # 投票聚合模块的类型
num_point=256, # 投票聚合分支中 SA 模块的点的数量
radius=0.3, # 投票聚合分支中 SA 模块的半径
num_sample=16, # 投票聚合分支中 SA 模块的采样点的数量
mlp_channels=[256, 128, 128, 128], # 投票聚合分支中 SA 模块的多层感知器的通道数
use_xyz=True, # 是否使用 xyz 坐标
normalize_xyz=True), # 是否使用标准化后的 xyz 坐标
feat_channels=(128, 128), # 特征卷积的通道数
conv_cfg=dict(type='Conv1d'), # 卷积的配置
norm_cfg=dict(type='BN1d'), # 归一化层的配置
objectness_loss=dict( # 物体性 (objectness) 损失函数的配置
type='CrossEntropyLoss', # 损失函数类型
class_weight=[0.2, 0.8], # 损失函数对每一类的权重
reduction='sum', # 设置损失函数输出的聚合方法
loss_weight=5.0), # 损失函数权重
center_loss=dict( # 中心 (center) 损失函数的配置
type='ChamferDistance', # 损失函数类型
mode='l2', # 损失函数模式
reduction='sum', # 设置损失函数输出的聚合方法
loss_src_weight=10.0, # 源损失权重
loss_dst_weight=10.0), # 目标损失权重
dir_class_loss=dict( # 方向分类损失函数的配置
type='CrossEntropyLoss', # 损失函数类型
reduction='sum', # 设置损失函数输出的聚合方法
loss_weight=1.0), # 损失函数权重
dir_res_loss=dict( # 方向残差 (residual) 损失函数的配置
type='SmoothL1Loss', # 损失函数类型
reduction='sum', # 设置损失函数输出的聚合方法
loss_weight=10.0), # 损失函数权重
size_class_loss=dict( # 尺寸分类损失函数的配置
type='CrossEntropyLoss', # 损失函数类型
reduction='sum', # 设置损失函数输出的聚合方法
loss_weight=1.0), # 损失函数权重
size_res_loss=dict( # 尺寸残差损失函数的配置
type='SmoothL1Loss', # 损失函数类型
reduction='sum', # 设置损失函数输出的聚合方法
loss_weight=3.3333333333333335), # 损失函数权重
semantic_loss=dict( # 语义损失函数的配置
type='CrossEntropyLoss', # 损失函数类型
reduction='sum', # 设置损失函数输出的聚合方法
loss_weight=1.0)), # 损失函数权重
train_cfg = dict( # VoteNet 训练的超参数配置
pos_distance_thr=0.3, # 距离 >= 0.3 阈值的样本将被视为正样本
neg_distance_thr=0.6, # 距离 < 0.6 阈值的样本将被视为负样本
sample_mod='vote'), # 采样方法的模式
test_cfg = dict( # VoteNet 测试的超参数配置
sample_mod='seed', # 采样方法的模式
nms_thr=0.25, # NMS 中使用的阈值
score_thr=0.8, # 剔除框的阈值
per_class_proposal=False)) # 是否使用逐类提议框 (proposal)
dataset_type = 'ScanNetDataset' # 数据集类型
data_root = './data/scannet/' # 数据路径
class_names = ('cabinet', 'bed', 'chair', 'sofa', 'table', 'door', 'window',
'bookshelf', 'picture', 'counter', 'desk', 'curtain',
'refrigerator', 'showercurtrain', 'toilet', 'sink', 'bathtub',
'garbagebin') # 类的名称
train_pipeline = [ # 训练流水线,更多细节请参考 mmdet3d.datasets.pipelines
dict(
type='LoadPointsFromFile', # 第一个流程,用于读取点,更多细节请参考 mmdet3d.datasets.pipelines.indoor_loading
shift_height=True, # 是否使用变换高度
load_dim=6, # 读取的点的维度
use_dim=[0, 1, 2]), # 使用所读取点的哪些维度
dict( dict(
type='LoadAnnotations3D', # 第二个流程,用于读取标注,更多细节请参考 mmdet3d.datasets.pipelines.indoor_loading type='ObjectSample',
with_bbox_3d=True, # 是否读取 3D 框 db_sampler=dict(
with_label_3d=True, # 是否读取 3D 框对应的类别标签 data_root='data/kitti/',
with_mask_3d=True, # 是否读取 3D 实例分割掩码 info_path='data/kitti/kitti_dbinfos_train.pkl',
with_seg_3d=True), # 是否读取 3D 语义分割掩码 rate=1.0,
prepare=dict(
filter_by_difficulty=[-1],
filter_by_min_points=dict(Car=5, Pedestrian=5, Cyclist=5)),
classes=['Pedestrian', 'Cyclist', 'Car'],
sample_groups=dict(Car=15, Pedestrian=15, Cyclist=15),
points_loader=dict(
type='LoadPointsFromFile',
coord_type='LIDAR',
load_dim=4,
use_dim=4)),
use_ground_plane=True),
dict(type='RandomFlip3D', flip_ratio_bev_horizontal=0.5),
dict( dict(
type='PointSegClassMapping', # 选取有效的类别,更多细节请参考 mmdet3d.datasets.pipelines.point_seg_class_mapping type='GlobalRotScaleTrans',
valid_cat_ids=(3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 14, 16, 24, 28, 33, 34, rot_range=[-0.78539816, 0.78539816],
36, 39), # 所有有效类别的编号 scale_ratio_range=[0.95, 1.05]),
max_cat_id=40), # 输入语义分割掩码中可能存在的最大类别编号
dict(type='PointSample', # 室内点采样,更多细节请参考 mmdet3d.datasets.pipelines.indoor_sample
num_points=40000), # 采样的点的数量
dict(type='IndoorFlipData', # 数据增广流程,随机翻转点和 3D 框
flip_ratio_yz=0.5, # 沿着 yz 平面被翻转的概率
flip_ratio_xz=0.5), # 沿着 xz 平面被翻转的概率
dict( dict(
type='IndoorGlobalRotScale', # 数据增广流程,旋转并放缩点和 3D 框,更多细节请参考 mmdet3d.datasets.pipelines.indoor_augment type='PointsRangeFilter',
shift_height=True, # 读取的点是否有高度这一属性 point_cloud_range=[0, -39.68, -3, 69.12, 39.68, 1]),
rot_range=[-0.027777777777777776, 0.027777777777777776], # 旋转角范围
scale_range=None), # 缩放尺寸范围
dict( dict(
type='DefaultFormatBundle3D', # 默认格式打包以收集读取的所有数据,更多细节请参考 mmdet3d.datasets.pipelines.formatting type='ObjectRangeFilter',
class_names=('cabinet', 'bed', 'chair', 'sofa', 'table', 'door', point_cloud_range=[0, -39.68, -3, 69.12, 39.68, 1]),
'window', 'bookshelf', 'picture', 'counter', 'desk', dict(type='PointShuffle'),
'curtain', 'refrigerator', 'showercurtrain', 'toilet',
'sink', 'bathtub', 'garbagebin')),
dict( dict(
type='Collect3D', # 最后一个流程,决定哪些键值对应的数据会被输入给检测器,更多细节请参考 mmdet3d.datasets.pipelines.formatting type='Pack3DDetInputs',
keys=[ keys=['points', 'gt_labels_3d', 'gt_bboxes_3d'])
'points', 'gt_bboxes_3d', 'gt_labels_3d', 'pts_semantic_mask',
'pts_instance_mask'
])
] ]
test_pipeline = [ # 测试流水线,更多细节请参考 mmdet3d.datasets.pipelines test_pipeline = [
dict( dict(type='LoadPointsFromFile', coord_type='LIDAR', load_dim=4, use_dim=4),
type='LoadPointsFromFile', # 第一个流程,用于读取点,更多细节请参考 mmdet3d.datasets.pipelines.indoor_loading
shift_height=True, # 是否使用变换高度
load_dim=6, # 读取的点的维度
use_dim=[0, 1, 2]), # 使用所读取点的哪些维度
dict(type='PointSample', # 室内点采样,更多细节请参考 mmdet3d.datasets.pipelines.indoor_sample
num_points=40000), # 采样的点的数量
dict( dict(
type='DefaultFormatBundle3D', # 默认格式打包以收集读取的所有数据,更多细节请参考 mmdet3d.datasets.pipelines.formatting type='MultiScaleFlipAug3D',
class_names=('cabinet', 'bed', 'chair', 'sofa', 'table', 'door', img_scale=(1333, 800),
'window', 'bookshelf', 'picture', 'counter', 'desk', pts_scale_ratio=1,
'curtain', 'refrigerator', 'showercurtrain', 'toilet', flip=False,
'sink', 'bathtub', 'garbagebin')), transforms=[
dict(type='Collect3D', # 最后一个流程,决定哪些键值对应的数据会被输入给检测器,更多细节请参考 mmdet3d.datasets.pipelines.formatting dict(
keys=['points']) type='GlobalRotScaleTrans',
rot_range=[0, 0],
scale_ratio_range=[1.0, 1.0],
translation_std=[0, 0, 0]),
dict(type='RandomFlip3D'),
dict(
type='PointsRangeFilter',
point_cloud_range=[0, -39.68, -3, 69.12, 39.68, 1])
]),
dict(type='Pack3DDetInputs', keys=['points'])
] ]
eval_pipeline = [ # 模型验证或可视化所使用的流水线,更多细节请参考 mmdet3d.datasets.pipelines eval_pipeline = [
dict( dict(type='LoadPointsFromFile', coord_type='LIDAR', load_dim=4, use_dim=4),
type='LoadPointsFromFile', # 第一个流程,用于读取点,更多细节请参考 mmdet3d.datasets.pipelines.indoor_loading dict(type='Pack3DDetInputs', keys=['points'])
shift_height=True, # 是否使用变换高度
load_dim=6, # 读取的点的维度
use_dim=[0, 1, 2]), # 使用所读取点的哪些维度
dict(
type='DefaultFormatBundle3D', # 默认格式打包以收集读取的所有数据,更多细节请参考 mmdet3d.datasets.pipelines.formatting
class_names=('cabinet', 'bed', 'chair', 'sofa', 'table', 'door',
'window', 'bookshelf', 'picture', 'counter', 'desk',
'curtain', 'refrigerator', 'showercurtrain', 'toilet',
'sink', 'bathtub', 'garbagebin')),
with_label=False),
dict(type='Collect3D', # 最后一个流程,决定哪些键值对应的数据会被输入给检测器,更多细节请参考 mmdet3d.datasets.pipelines.formatting
keys=['points'])
] ]
data = dict( train_dataloader = dict(
samples_per_gpu=8, # 单张 GPU 上的样本数 batch_size=6,
workers_per_gpu=4, # 每张 GPU 上用于读取数据的进程数 num_workers=4,
train=dict( # 训练数据集配置 persistent_workers=True,
type='RepeatDataset', # 数据集嵌套,更多细节请参考 https://github.com/open-mmlab/mmdetection/blob/master/mmdet/datasets/dataset_wrappers.py sampler=dict(type='DefaultSampler', shuffle=True),
times=5, # 重复次数 dataset=dict(
type='RepeatDataset',
times=2,
dataset=dict( dataset=dict(
type='ScanNetDataset', # 数据集类型 type='KittiDataset',
data_root='./data/scannet/', # 数据路径 data_root='data/kitti/',
ann_file='./data/scannet/scannet_infos_train.pkl', # 数据标注文件的路径 ann_file='kitti_infos_train.pkl',
pipeline=[ # 流水线,这里传入的就是上面创建的训练流水线变量 data_prefix=dict(pts='training/velodyne_reduced'),
pipeline=[
dict( dict(
type='LoadPointsFromFile', type='LoadPointsFromFile',
shift_height=True, coord_type='LIDAR',
load_dim=6, load_dim=4,
use_dim=[0, 1, 2]), use_dim=4),
dict( dict(
type='LoadAnnotations3D', type='LoadAnnotations3D',
with_bbox_3d=True, with_bbox_3d=True,
with_label_3d=True, with_label_3d=True),
with_mask_3d=True,
with_seg_3d=True),
dict( dict(
type='PointSegClassMapping'), type='ObjectSample',
dict(type='PointSample', num_points=40000), db_sampler=dict(
data_root='data/kitti/',
info_path='data/kitti/kitti_dbinfos_train.pkl',
rate=1.0,
prepare=dict(
filter_by_difficulty=[-1],
filter_by_min_points=dict(
Car=5, Pedestrian=5, Cyclist=5)),
classes=['Pedestrian', 'Cyclist', 'Car'],
sample_groups=dict(Car=15, Pedestrian=15, Cyclist=15),
points_loader=dict(
type='LoadPointsFromFile',
coord_type='LIDAR',
load_dim=4,
use_dim=4)),
use_ground_plane=True),
dict(type='RandomFlip3D', flip_ratio_bev_horizontal=0.5),
dict( dict(
type='IndoorFlipData', type='GlobalRotScaleTrans',
flip_ratio_yz=0.5, rot_range=[-0.78539816, 0.78539816],
flip_ratio_xz=0.5), scale_ratio_range=[0.95, 1.05]),
dict( dict(
type='IndoorGlobalRotScale', type='PointsRangeFilter',
shift_height=True, point_cloud_range=[0, -39.68, -3, 69.12, 39.68, 1]),
rot_range=[-0.027777777777777776, 0.027777777777777776],
scale_range=None),
dict( dict(
type='DefaultFormatBundle3D', type='ObjectRangeFilter',
class_names=('cabinet', 'bed', 'chair', 'sofa', 'table', point_cloud_range=[0, -39.68, -3, 69.12, 39.68, 1]),
'door', 'window', 'bookshelf', 'picture', dict(type='PointShuffle'),
'counter', 'desk', 'curtain', 'refrigerator',
'showercurtrain', 'toilet', 'sink', 'bathtub',
'garbagebin')),
dict( dict(
type='Collect3D', type='Pack3DDetInputs',
keys=[ keys=['points', 'gt_labels_3d', 'gt_bboxes_3d'])
'points', 'gt_bboxes_3d', 'gt_labels_3d',
'pts_semantic_mask', 'pts_instance_mask'
])
], ],
filter_empty_gt=False, # 是否过滤掉空的标签框 modality=dict(use_lidar=True, use_camera=False),
classes=('cabinet', 'bed', 'chair', 'sofa', 'table', 'door', test_mode=False,
'window', 'bookshelf', 'picture', 'counter', 'desk', metainfo=dict(classes=['Pedestrian', 'Cyclist', 'Car']),
'curtain', 'refrigerator', 'showercurtrain', 'toilet', box_type_3d='LiDAR')))
'sink', 'bathtub', 'garbagebin'))), # 类别名称 val_dataloader = dict(
val=dict( # 验证数据集配置 batch_size=1,
type='ScanNetDataset', # 数据集类型 num_workers=1,
data_root='./data/scannet/', # 数据路径 persistent_workers=True,
ann_file='./data/scannet/scannet_infos_val.pkl', # 数据标注文件的路径 drop_last=False,
pipeline=[ # 流水线,这里传入的就是上面创建的测试流水线变量 sampler=dict(type='DefaultSampler', shuffle=False),
dataset=dict(
type='KittiDataset',
data_root='data/kitti/',
data_prefix=dict(pts='training/velodyne_reduced'),
ann_file='kitti_infos_val.pkl',
pipeline=[
dict( dict(
type='LoadPointsFromFile', type='LoadPointsFromFile',
shift_height=True, coord_type='LIDAR',
load_dim=6, load_dim=4,
use_dim=[0, 1, 2]), use_dim=4),
dict(type='PointSample', num_points=40000),
dict( dict(
type='DefaultFormatBundle3D', type='MultiScaleFlipAug3D',
class_names=('cabinet', 'bed', 'chair', 'sofa', 'table', img_scale=(1333, 800),
'door', 'window', 'bookshelf', 'picture', pts_scale_ratio=1,
'counter', 'desk', 'curtain', 'refrigerator', flip=False,
'showercurtrain', 'toilet', 'sink', 'bathtub', transforms=[
'garbagebin')), dict(
dict(type='Collect3D', keys=['points']) type='GlobalRotScaleTrans',
rot_range=[0, 0],
scale_ratio_range=[1.0, 1.0],
translation_std=[0, 0, 0]),
dict(type='RandomFlip3D'),
dict(
type='PointsRangeFilter',
point_cloud_range=[0, -39.68, -3, 69.12, 39.68, 1])
]),
dict(type='Pack3DDetInputs', keys=['points'])
], ],
classes=('cabinet', 'bed', 'chair', 'sofa', 'table', 'door', 'window', modality=dict(use_lidar=True, use_camera=False),
'bookshelf', 'picture', 'counter', 'desk', 'curtain', test_mode=True,
'refrigerator', 'showercurtrain', 'toilet', 'sink', 'bathtub', metainfo=dict(classes=['Pedestrian', 'Cyclist', 'Car']),
'garbagebin'), # 类别名称 box_type_3d='LiDAR'))
test_mode=True), # 是否开启测试模式 test_dataloader = dict(
test=dict( # 测试数据集配置 batch_size=1,
type='ScanNetDataset', # 数据集类型 num_workers=1,
data_root='./data/scannet/', # 数据路径 persistent_workers=True,
ann_file='./data/scannet/scannet_infos_val.pkl', # 数据标注文件的路径 drop_last=False,
pipeline=[ # 流水线,这里传入的就是上面创建的测试流水线变量 sampler=dict(type='DefaultSampler', shuffle=False),
dataset=dict(
type='KittiDataset',
data_root='data/kitti/',
data_prefix=dict(pts='training/velodyne_reduced'),
ann_file='kitti_infos_val.pkl',
pipeline=[
dict( dict(
type='LoadPointsFromFile', type='LoadPointsFromFile',
shift_height=True, coord_type='LIDAR',
load_dim=6, load_dim=4,
use_dim=[0, 1, 2]), use_dim=4),
dict(type='PointSample', num_points=40000),
dict( dict(
type='DefaultFormatBundle3D', type='MultiScaleFlipAug3D',
class_names=('cabinet', 'bed', 'chair', 'sofa', 'table', img_scale=(1333, 800),
'door', 'window', 'bookshelf', 'picture', pts_scale_ratio=1,
'counter', 'desk', 'curtain', 'refrigerator', flip=False,
'showercurtrain', 'toilet', 'sink', 'bathtub', transforms=[
'garbagebin')), dict(
dict(type='Collect3D', keys=['points']) type='GlobalRotScaleTrans',
rot_range=[0, 0],
scale_ratio_range=[1.0, 1.0],
translation_std=[0, 0, 0]),
dict(type='RandomFlip3D'),
dict(
type='PointsRangeFilter',
point_cloud_range=[0, -39.68, -3, 69.12, 39.68, 1])
]),
dict(type='Pack3DDetInputs', keys=['points'])
], ],
classes=('cabinet', 'bed', 'chair', 'sofa', 'table', 'door', 'window', modality=dict(use_lidar=True, use_camera=False),
'bookshelf', 'picture', 'counter', 'desk', 'curtain', test_mode=True,
'refrigerator', 'showercurtrain', 'toilet', 'sink', 'bathtub', metainfo=dict(classes=['Pedestrian', 'Cyclist', 'Car']),
'garbagebin'), # 类别名称 box_type_3d='LiDAR'))
test_mode=True)) # 是否开启测试模式 ```
evaluation = dict(pipeline=[ # 流水线,这里传入的就是上面创建的验证流水线变量
[评估器](https://mmengine.readthedocs.io/zh_CN/latest/tutorials/metric_and_evaluator.html)用来计算训练模型在验证集和测试集上的评价指标。评估器的配置包含一个或一系列指标配置:
```python
val_evaluator = dict(
type='KittiMetric',
ann_file='data/kitti/kitti_infos_val.pkl',
metric='bbox')
test_evaluator = dict(
type='KittiMetric',
ann_file='data/kitti/kitti_infos_val.pkl',
metric='bbox')
```
### 训练和测试配置
MMEngine 的执行器使用循环(Loop)来控制训练、验证以及测试过程。用户可以用以下字段设置最大训练周期以及验证间隔。
```python
train_cfg = dict(
type='EpochBasedTrainLoop',
max_epochs=80,
val_interval=2)
val_cfg = dict(type='ValLoop')
test_cfg = dict(type='TestLoop')
```
### 优化器配置
`optim_wrapper` 字段用来配置优化器相关设置。优化器包装器不仅提供优化器的功能,用时也支持其它功能,如梯度裁剪、混合精度训练等。更多细节请参考[优化器封装教程](https://mmengine.readthedocs.io/zh_CN/latest/tutorials/optimizer.html)
```python
optim_wrapper = dict( # 优化器封装配置
type='OptimWrapper', # 优化器封装类型,切换成 AmpOptimWrapper 使用混合精度训练
optimizer=dict( # 优化器配置。支持 PyTorch 中所有类型的优化器。参考 https://pytorch.org/docs/stable/optim.html#algorithms
type='AdamW', lr=0.001, betas=(0.95, 0.99), weight_decay=0.01),
clip_grad=dict(max_norm=35, norm_type=2)) # 梯度裁剪选项。设置 None 禁用梯度裁剪。用法请参考 https://mmengine.readthedocs.io/zh_CN/latest/tutorials
```
`param_scheduler` 字段用来配置调整优化器超参数,例如学习率和动量。用户可以组合多个调度器来创建所需要的参数调整策略。更多细节请参考[参数调度器教程](https://mmengine.readthedocs.io/zh_CN/latest/tutorials/param_scheduler.html)[参数调度器 API 文档](TODO)
```python
param_scheduler = [
dict( dict(
type='LoadPointsFromFile', type='CosineAnnealingLR',
coord_type='DEPTH', T_max=32.0,
shift_height=False, eta_min=0.01,
load_dim=6, begin=0,
use_dim=[0, 1, 2]), end=32.0,
by_epoch=True,
convert_to_iter_based=True),
dict(
type='CosineAnnealingLR',
T_max=48.0,
eta_min=1.0000000000000001e-07,
begin=32.0,
end=80,
by_epoch=True,
convert_to_iter_based=True),
dict(
type='CosineAnnealingMomentum',
T_max=32.0,
eta_min=0.8947368421052632,
begin=0,
end=32.0,
by_epoch=True,
convert_to_iter_based=True),
dict( dict(
type='DefaultFormatBundle3D', type='CosineAnnealingMomentum',
class_names=('cabinet', 'bed', 'chair', 'sofa', 'table', 'door', T_max=48.0,
'window', 'bookshelf', 'picture', 'counter', 'desk', eta_min=1,
'curtain', 'refrigerator', 'showercurtrain', 'toilet', begin=32.0,
'sink', 'bathtub', 'garbagebin'), end=80,
with_label=False), convert_to_iter_based=True)
dict(type='Collect3D', keys=['points']) ]
])
lr = 0.008 # 优化器的学习率
optimizer = dict( # 构建优化器所使用的配置,我们支持所有 PyTorch 中支持的优化器,并且拥有相同的参数名称
type='Adam', # 优化器类型,更多细节请参考 https://github.com/open-mmlab/mmcv/blob/v1.3.7/mmcv/runner/optimizer/default_constructor.py#L12
lr=0.008) # 优化器的学习率,用户可以在 PyTorch 文档中查看这些参数的详细使用方法
optimizer_config = dict( # 构建优化器钩子的配置,更多实现细节可参考 https://github.com/open-mmlab/mmcv/blob/v1.3.7/mmcv/runner/hooks/optimizer.py#L22
grad_clip=dict( # 梯度裁剪的配置
max_norm=10, # 梯度的最大模长
norm_type=2)) # 所使用的 p-范数的类型,可以设置成 'inf' 则指代无穷范数
lr_config = dict( # 学习率策略配置,用于注册学习率更新的钩子
policy='step', # 学习率调整的策略,支持 CosineAnnealing、Cyclic 等,更多支持的种类请参考 https://github.com/open-mmlab/mmcv/blob/v1.3.7/mmcv/runner/hooks/lr_updater.py#L9
warmup=None, # Warmup 策略,同时也支持 `exp` 和 `constant`
step=[24, 32]) # 学习率衰减的步数
checkpoint_config = dict( # 设置保存模型权重钩子的配置,具体实现请参考 https://github.com/open-mmlab/mmcv/blob/master/mmcv/runner/hooks/checkpoint.py
interval=1) # 保存模型权重的间隔是 1 轮
log_config = dict( # 用于注册输出记录信息钩子的配置
interval=50, # 输出记录信息的间隔
hooks=[dict(type='TextLoggerHook'),
dict(type='TensorboardLoggerHook')]) # 用于记录训练过程的信息记录机制
runner = dict(type='EpochBasedRunner', max_epochs=36) # 程序运行器,将会运行 `workflow` `max_epochs` 次
dist_params = dict(backend='nccl') # 设置分布式训练的配置,通讯端口值也可被设置
log_level = 'INFO' # 输出记录信息的等级
find_unused_parameters = True # 是否查找模型中未使用的参数
work_dir = None # 当前实验存储模型权重和输出信息的路径
load_from = None # 从指定路径读取一个预训练的模型权重,这将不会继续 (resume) 训练
resume_from = None # 从一个指定路径读入模型权重并继续训练,这意味着训练轮数、优化器状态等都将被读取
workflow = [('train', 1)] # 要运行的工作流。[('train', 1)] 意味着只有一个名为 'train' 的工作流,它只会被执行一次。这一工作流依据 `max_epochs` 的值将会训练模型 36 轮。
gpu_ids = range(0, 1) # 所使用的 GPU 编号
``` ```
## 常问问题 (FAQ) ### 钩子配置
用户可以将钩子连接到训练、验证和测试中,以便在运行期间插入一些操作。有两个不同的钩子字段:`default_hooks``custom_hooks`
### 忽略基础配置文件里的部分内容 `default_hooks` 是一个钩子配置字典。`default_hooks` 里的钩子是在运行时所需要的。它们有默认的优先级,是不需要修改的。如果没有设置,执行器会使用默认值。如果需要禁用默认的钩子,用户可以将其配置设置成 `None`
有时,您也许会需要通过设置 `_delete_=True` 来忽略基础配置文件里的一些域内容。 ```python
请参照 [mmcv](https://mmcv.readthedocs.io/en/latest/understand_mmcv/config.html#inherit-from-base-config-with-ignored-fields) 来获得一些简单的指导。 default_hooks = dict(
timer=dict(type='IterTimerHook'),
logger=dict(type='LoggerHook', interval=50),
param_scheduler=dict(type='ParamSchedulerHook'),
checkpoint=dict(type='CheckpointHook', interval=1),
sampler_seed=dict(type='DistSamplerSeedHook'))
```
例如在 MMDetection3D 中,为了改变如下所示 PointPillars FPN 模块的某些配置: ### 运行配置
```python
default_scope = 'mmdet3d' # 寻找模块的默认注册域。参考 https://mmengine.readthedocs.io/zh_CN/latest/tutorials/registry.html
env_cfg = dict(
cudnn_benchmark=False, # 是否使用 cudnn benchmark
mp_cfg=dict(mp_start_method='fork', opencv_num_threads=0), # 使用 fork 开启多线程。'fork' 通常比 'spawn' 快,但可能不安全。可参考 https://github.com/pytorch/pytorch/issues/1355
dist_cfg=dict(backend='nccl')) # 分布式配置
vis_backends = [dict(type='LocalVisBackend')] # 可视化后端
visualizer = dict(
type='Det3DLocalVisualizer',
vis_backends=[dict(type='LocalVisBackend')],
name='visualizer')
log_processor = dict(type='LogProcessor', window_size=50, by_epoch=True)
log_level = 'INFO'
load_from = None
resume = False
```
## 配置文件继承性
`configs/_base_` 文件夹下有 4 个基本组件类型,分别是:数据集(dataset),模型(model),训练策略(schedule)和运行时的默认设置(default runtime)。通过从上述文件夹中选取一个组件进行组合,许多方法如 SECOND、PointPillars、PartA2 和 VoteNet 都能够很容易地构建出来。由 `_base_` 下的组件组成的配置,被我们称为 _原始配置(primitive)_。
对于同一文件夹下的配置,推荐**只有一个**对应的 _原始配置_ 文件,所有其他的配置文件都应该继承自这个 _原始配置_ 文件,这样就能保证配置文件的最大继承深度为 3。
为了便于理解,我们建议贡献者继承现有方法。例如,如果在 PointPillars 的基础上做了一些修改,用户首先可以通过指定 `_base_ = ../pointpillars/pointpillars_hv_fpn_sbn-all_8xb4_2x_nus-3d.py` 来继承基础的 PointPillars 结构,然后修改配置文件中的必要参数以完成继承。
如果你在构建一个与任何现有方法不共享结构的全新方法,可以在 `configs` 文件夹下创建一个新的例如 `xxx_rcnn` 文件夹。
更多细节请参考 [mmengine 配置文件教程](https://mmengine.readthedocs.io/zh_CN/latest/tutorials/config.html)
### 忽略基础配置中的某些字段
有时候,你需要设置 `_delete_=True` 来忽略基础配置中的某些字段。你可以参考 [mmcv](https://mmcv.readthedocs.io/en/latest/utils.html#inherit-from-base-config-with-ignored-fields) 做简单了解。
在 MMDetection3D 中,例如,修改以下 PointPillars 配置中的 FPN 瓶颈网络。
```python ```python
model = dict( model = dict(
type='MVXFasterRCNN', type='MVXFasterRCNN',
pts_voxel_layer=dict(...), data_preprocessor=dict(voxel_layer=dict(...)),
pts_voxel_encoder=dict(...), pts_voxel_encoder=dict(...),
pts_middle_encoder=dict(...), pts_middle_encoder=dict(...),
pts_backbone=dict(...), pts_backbone=dict(...),
...@@ -435,7 +478,7 @@ model = dict( ...@@ -435,7 +478,7 @@ model = dict(
pts_bbox_head=dict(...)) pts_bbox_head=dict(...))
``` ```
`FPN``SECONDFPN` 使用不同的关键来构建。 `FPN``SECONDFPN` 使用不同的关键来构建。
```python ```python
_base_ = '../_base_/models/pointpillars_hv_fpn_nus.py' _base_ = '../_base_/models/pointpillars_hv_fpn_nus.py'
...@@ -450,13 +493,11 @@ model = dict( ...@@ -450,13 +493,11 @@ model = dict(
pts_bbox_head=dict(...)) pts_bbox_head=dict(...))
``` ```
`_delete_=True` 的标识将会使用新的键值覆盖掉 `pts_neck` 中的所有旧键值。 `_delete_=True` 将会使用新的键值替换 `pts_neck` 中的旧键值。
### 使用配置文件里的中间变量 ### 在配置中使用中间变量
配置文件里会使用一些中间变量,例如数据集中的 `train_pipeline`/`test_pipeline` 在配置文件中通常会使用一些中间变量,例如数据集中 `train_pipeline`/`test_pipeline`。需要注意的是当在子配置中修改中间变量,用户需要再次将中间变量传递到相应的字段中。例如,我们想要使用多尺度策略训练和测试 PointPillars。`train_pipeline`/`test_pipeline` 是我们需要修改的中间变量。
值得注意的是,当修改子配置文件中的中间变量后,用户还需再次将其传入相应字段。
例如,我们想在训练和测试中,对 PointPillars 使用多尺度策略 (multi scale strategy),那么 `train_pipeline`/`test_pipeline` 就是我们想要修改的中间变量。
```python ```python
_base_ = './nus-3d.py' _base_ = './nus-3d.py'
...@@ -481,8 +522,9 @@ train_pipeline = [ ...@@ -481,8 +522,9 @@ train_pipeline = [
dict(type='ObjectRangeFilter', point_cloud_range=point_cloud_range), dict(type='ObjectRangeFilter', point_cloud_range=point_cloud_range),
dict(type='ObjectNameFilter', classes=class_names), dict(type='ObjectNameFilter', classes=class_names),
dict(type='PointShuffle'), dict(type='PointShuffle'),
dict(type='DefaultFormatBundle3D', class_names=class_names), dict(
dict(type='Collect3D', keys=['points', 'gt_bboxes_3d', 'gt_labels_3d']) type='Pack3DDetInputs',
keys=['points', 'gt_labels_3d', 'gt_bboxes_3d'])
] ]
test_pipeline = [ test_pipeline = [
dict( dict(
...@@ -507,18 +549,55 @@ test_pipeline = [ ...@@ -507,18 +549,55 @@ test_pipeline = [
translation_std=[0, 0, 0]), translation_std=[0, 0, 0]),
dict(type='RandomFlip3D'), dict(type='RandomFlip3D'),
dict( dict(
type='PointsRangeFilter', point_cloud_range=point_cloud_range), type='PointsRangeFilter', point_cloud_range=point_cloud_range)
dict( ]),
type='DefaultFormatBundle3D', dict(type='Pack3DDetInputs', keys=['points'])
class_names=class_names,
with_label=False),
dict(type='Collect3D', keys=['points'])
])
] ]
data = dict( train_dataloader = dict(dataset=dict(pipeline=train_pipeline))
train=dict(pipeline=train_pipeline), test_dataloader = dict(dataset=dict(pipeline=test_pipeline))
val=dict(pipeline=test_pipeline), val_dataloader = dict(dataset=dict(pipeline=test_pipeline))
test=dict(pipeline=test_pipeline)) ```
### 重使用 \_base\_ 文件中的变量
如果用户想重新使用基础文件中的变量,可以通过使用 `{{_base_.xxx}}` 拷贝相应的变量。例如:
```python
_base_ = './pointpillars_hv_secfpn_8xb6_160e_kitti-3d-3class.py'
a = {{_base_.model}} # 变量 `a` 和 `_base_` 中定义的 `model` 相同
```
### 通过脚本参数修改配置
当使用 "tools/train.py" 或者 "tools/test.py" 提交工作任务时,可以指定 `--cfg-options` 来修改配置。
- 更新配置字典的键值
可以按照原始配置中字典的键值顺序指定配置选项。例如,`--cfg-options model.backbone.norm_eval=False` 改变模型骨干网络中的 BN 模块为 `train` 模式。
- 更新配置列表中的键值
配置中一些配置字典是由列表组成。例如,训练流水线 `train_dataloader.dataset.pipeline` 通常是一个列表,例如 `[dict(type='LoadImageFromFile'), ...]`。如果你想将流水线中的 `LoadImageFromFile` 改为 `LoadImageFromNDArray`,你可以指定 `--cfg-options data.train.pipeline.0.type=LoadImageFromNDArray`
- 更新列表/元组值
如果更新的值是列表或元组。例如,配置文件中通常设置 `model.data_preprocessor.mean=[123.675, 116.28, 103.53]`。如果你想改变这个均值,你可以指定 `--cfg-options model.data_preprocessor.mean="[127,127,127]"`。注意引用符号 `"` 是用来支持列表/元组数据类型所必要的,并且在指定值的引用符号内**没有**空格。
## 配置文件名称风格
我们遵循以下样式来命名配置文件,并建议贡献者遵循相同的风格。
```
{algorithm name}_{model component names [component1]_[component2]_[...]}_{training settings}_{training dataset information}_{testing dataset information}.py
``` ```
这里,我们首先定义了新的 `train_pipeline`/`test_pipeline`,然后将其传入 `data` 文件名分为五个部分。所有部分和组件通过 `_` 连接,每个部分或组件单词用 `-` 连接。
- `{algorithm name}`:算法名。这应该是检测器的名字例如 `pointpillars``fcos3d` 等。
- `{model component names}`:算法中使用的组件名,例如体素编码器,骨干网络,瓶颈网络等。例如 `second_secfpn_head-dcn-circlenms` 意味着使用 SECOND's SparseEncoder,SECONDFPN 以及使用 DCN 和 circle NMS 的检测头。
- `{training settings}`:训练设置信息,例如批量大小,数据增强,损失函数策略,调度器以及 epoch/迭代等。例如:`8xb4-tta-cyclic-20e` 意味着使用 8 个 GPUs,每个 GPU 有 4 个数据样本,测试增强,余弦退火学习率以及训练 20 个 epoch。一些缩写:
- `{gpu x batch_per_gpu}`:GPUs 数以及每块 GPU 的样本数。`bN` 表示 每块 GPU 的批量大小为 N。例如 `4xb4` 是 4-gpus x 4-samples-per-gpu 的简写。
- `{schedule}`:训练调度,可选项为 `schedule-2x``schedule-3x``cyclic-20e`等。`schedule-2x``schedule-3x` 分别表示训练 24 和 36 个 epoch。`cyclic-20e` 表示训练 20 个 epoch。
- `{training dataset information}`:训练数据集名如 `kitti-3d-3class``nus-3d``s3dis-seg``scannet-seg``waymoD5-3d-car`。此处 `3d` 表示数据集用于 3d 目标检测,`seg` 表示数据集用于点云分割。
- `{testing dataset information}`(可选):当模型在一个数据集上训练,在另一个数据集上测试时的测试数据集名。如果没有指定,意味着模型在同一数据类型上训练和测试。
# 教程 6: 坐标系 # 坐标系
## 概述 ## 概述
......
# 教程 3: 自定义数据预处理流程 # 自定义数据预处理流程
## 数据预处理流程的设计 ## 数据预处理流程的设计
...@@ -7,6 +7,7 @@ ...@@ -7,6 +7,7 @@
数据预处理流程和数据集之间是互相分离的两个部分,通常数据集定义了如何处理标注信息,而数据预处理流程定义了准备数据项字典的所有步骤。数据集预处理流程包含一系列的操作,每个操作将一个字典作为输入,并输出应用于下一个转换的一个新的字典。 数据预处理流程和数据集之间是互相分离的两个部分,通常数据集定义了如何处理标注信息,而数据预处理流程定义了准备数据项字典的所有步骤。数据集预处理流程包含一系列的操作,每个操作将一个字典作为输入,并输出应用于下一个转换的一个新的字典。
我们将在下图中展示一个最经典的数据集预处理流程,其中蓝色框表示预处理流程中的各项操作。随着预处理的进行,每一个操作都会添加新的键值(图中标记为绿色)到输出字典中,或者更新当前存在的键值(图中标记为橙色)。 我们将在下图中展示一个最经典的数据集预处理流程,其中蓝色框表示预处理流程中的各项操作。随着预处理的进行,每一个操作都会添加新的键值(图中标记为绿色)到输出字典中,或者更新当前存在的键值(图中标记为橙色)。
![](../../../resources/data_pipeline.png) ![](../../../resources/data_pipeline.png)
预处理流程中的各项操作主要分为数据加载、预处理、格式化、测试时的数据增强。 预处理流程中的各项操作主要分为数据加载、预处理、格式化、测试时的数据增强。
......
...@@ -2,8 +2,7 @@ ...@@ -2,8 +2,7 @@
## 在数据预处理前 ## 在数据预处理前
我们推荐用户将数据集的路径软链接到 `$MMDETECTION3D/data` 我们推荐用户将数据集的路径软链接到 `$MMDETECTION3D/data`。如果你的文件夹结构和以下所展示的结构不一致,你可能需要改变配置文件中相应的数据路径。
如果你的文件夹结构和以下所展示的结构相异,你可能需要改变配置文件中相应的数据路径。
``` ```
mmdetection3d mmdetection3d
...@@ -88,24 +87,47 @@ wget -c https://raw.githubusercontent.com/traveller59/second.pytorch/master/sec ...@@ -88,24 +87,47 @@ wget -c https://raw.githubusercontent.com/traveller59/second.pytorch/master/sec
wget -c https://raw.githubusercontent.com/traveller59/second.pytorch/master/second/data/ImageSets/train.txt --no-check-certificate --content-disposition -O ./data/kitti/ImageSets/train.txt wget -c https://raw.githubusercontent.com/traveller59/second.pytorch/master/second/data/ImageSets/train.txt --no-check-certificate --content-disposition -O ./data/kitti/ImageSets/train.txt
wget -c https://raw.githubusercontent.com/traveller59/second.pytorch/master/second/data/ImageSets/val.txt --no-check-certificate --content-disposition -O ./data/kitti/ImageSets/val.txt wget -c https://raw.githubusercontent.com/traveller59/second.pytorch/master/second/data/ImageSets/val.txt --no-check-certificate --content-disposition -O ./data/kitti/ImageSets/val.txt
wget -c https://raw.githubusercontent.com/traveller59/second.pytorch/master/second/data/ImageSets/trainval.txt --no-check-certificate --content-disposition -O ./data/kitti/ImageSets/trainval.txt wget -c https://raw.githubusercontent.com/traveller59/second.pytorch/master/second/data/ImageSets/trainval.txt --no-check-certificate --content-disposition -O ./data/kitti/ImageSets/trainval.txt
```
然后通过运行以下指令生成信息文件:
```bash
python tools/create_data.py kitti --root-path ./data/kitti --out-dir ./data/kitti --extra-tag kitti python tools/create_data.py kitti --root-path ./data/kitti --out-dir ./data/kitti --extra-tag kitti
``` ```
在使用 slurm 的环境下,用户需要使用下面的指令:
```bash
sh tools/create_data.sh <partition> kitti
```
### Waymo ### Waymo
[这里](https://waymo.com/open/download/)下载 Waymo 公开数据集1.2版本,在[这里](https://drive.google.com/drive/folders/18BVuF_RYJF0NjZpt8SnfzANiakoRMf0o?usp=sharing)下载其数据划分文件。 [这里](https://waymo.com/open/download/)下载 Waymo 公开数据集 1.2 版本,在[这里](https://drive.google.com/drive/folders/18BVuF_RYJF0NjZpt8SnfzANiakoRMf0o?usp=sharing)下载其数据划分文件。然后,将 `.tfrecord` 文件置于 `data/waymo/waymo_format/` 目录下的相应位置,并将数据划分的 `.txt` 文件置于 `data/waymo/kitti_format/ImageSets` 目录下。在[这里](https://console.cloud.google.com/storage/browser/waymo_open_dataset_v_1_2_0/validation/ground_truth_objects)下载验证集的真实标签(`.bin` 文件)并将其置于 `data/waymo/waymo_format/`。提示:你可以使用 `gsutil` 来用命令下载大规模的数据集。更多细节请参考此[工具](https://github.com/RalphMao/Waymo-Dataset-Tool)。完成以上各步后,可以通过运行以下指令对 Waymo 数据进行预处理:
然后,将 tfrecord 文件置于 `data/waymo/waymo_format/` 目录下的相应位置,并将数据划分的 txt 文件置于 `data/waymo/kitti_format/ImageSets` 目录下。
[这里](https://console.cloud.google.com/storage/browser/waymo_open_dataset_v_1_2_0/validation/ground_truth_objects)下载验证集的真实标签 (bin 文件) 并将其置于 `data/waymo/waymo_format/`
提示,你可以使用 `gsutil` 来用命令下载大规模的数据集。你可以参考这个[工具](https://github.com/RalphMao/Waymo-Dataset-Tool)来获取更多实现细节。
完成以上各步后,可以通过运行以下指令对 Waymo 数据进行预处理:
```bash ```bash
python tools/create_data.py waymo --root-path ./data/waymo/ --out-dir ./data/waymo/ --workers 128 --extra-tag waymo python tools/create_data.py waymo --root-path ./data/waymo/ --out-dir ./data/waymo/ --workers 128 --extra-tag waymo
``` ```
注意,如果你的硬盘空间大小不足以存储转换后的数据,你可以将 `out-dir` 参数设定为别的路径。 注意:
你只需要记得在那个路径下创建文件夹并下载数据,然后在数据预处理完成后将其链接回 `data/waymo/kitti_format` 即可。
- 如果你的硬盘空间大小不足以存储转换后的数据,你可以将 `--out-dir` 参数设定为别的路径。你只需要记得在那个路径下创建文件夹并下载数据,然后在数据预处理完成后将其链接回 `data/waymo/kitti_format` 即可。
- 如果你想在 Waymo 上进行更快的评估,你可以下载已经预处理好的[元信息文件](https://download.openmmlab.com/mmdetection3d/data/waymo/idx2metainfo.pkl)并将其放置在 `data/waymo/waymo_format/` 目录下。接着,你可以按照以下来修改数据集的配置:
```python
val_evaluator = dict(
type='WaymoMetric',
ann_file='./data/waymo/kitti_format/waymo_infos_val.pkl',
waymo_bin_file='./data/waymo/waymo_format/gt.bin',
data_root='./data/waymo/waymo_format',
file_client_args=file_client_args,
convert_kitti_format=True,
idx2metainfo='data/waymo/waymo_format/idx2metainfo.pkl'
)
```
目前这种方式仅限于纯点云检测任务。
### NuScenes ### NuScenes
...@@ -124,17 +146,34 @@ python tools/create_data.py lyft --root-path ./data/lyft --out-dir ./data/lyft - ...@@ -124,17 +146,34 @@ python tools/create_data.py lyft --root-path ./data/lyft --out-dir ./data/lyft -
python tools/data_converter/lyft_data_fixer.py --version v1.01 --root-folder ./data/lyft python tools/data_converter/lyft_data_fixer.py --version v1.01 --root-folder ./data/lyft
``` ```
注意,为了文件结构的清晰性,我们遵从了 Lyft 数据原先的文件夹名称。请按照上面展示出的文件结构对原始文件夹进行重命名。 注意,为了文件结构的清晰性,我们遵从了 Lyft 数据原先的文件夹名称。请按照上面展示出的文件结构对原始文件夹进行重命名。同样值得注意的是,第二行命令的目的是为了修复一个损坏的激光雷达数据文件。更多细节请参考[该讨论](https://www.kaggle.com/c/3d-object-detection-for-autonomous-vehicles/discussion/110000)
同样值得注意的是,第二行命令的目的是为了修复一个损坏的激光雷达数据文件。请参考[这一](https://www.kaggle.com/c/3d-object-detection-for-autonomous-vehicles/discussion/110000)讨论来获取更多细节。
### S3DIS、ScanNet 和 SUN RGB-D ### S3DIS、ScanNet 和 SUN RGB-D
请参考 S3DIS [README](https://github.com/open-mmlab/mmdetection3d/blob/master/data/s3dis/README.md/) 文件以对其进行数据预处理。 请参考 S3DIS [README](https://github.com/open-mmlab/mmdetection3d/blob/dev-1.x/data/s3dis/README.md) 文件以对其进行数据预处理。
请参考 ScanNet [README](https://github.com/open-mmlab/mmdetection3d/blob/master/data/scannet/README.md/) 文件以对其进行数据预处理。 请参考 ScanNet [README](https://github.com/open-mmlab/mmdetection3d/blob/dev-1.x/data/scannet/README.md) 文件以对其进行数据预处理。
请参考 SUN RGB-D [README](https://github.com/open-mmlab/mmdetection3d/blob/master/data/sunrgbd/README.md/) 文件以对其进行数据预处理。 请参考 SUN RGB-D [README](https://github.com/open-mmlab/mmdetection3d/blob/dev-1.x/data/sunrgbd/README.md) 文件以对其进行数据预处理。
### 自定义数据集 ### 自定义数据集
关于如何使用自定义数据集,请参考[教程 2: 自定义数据集](https://mmdetection3d.readthedocs.io/zh_CN/latest/tutorials/customize_dataset.html) 关于如何使用自定义数据集,请参考[自定义数据集](https://github.com/open-mmlab/mmdetection3d/blob/dev-1.x/docs/zh_cn/advanced_guides/customize_dataset.md)
### 更新数据信息
如果你之前已经使用 v1.0.0rc1-v1.0.0rc4 版的 mmdetection3d 创建数据信息,现在你想使用最新的 v1.1.0 版 mmdetection3d,你需要更新数据信息文件。
```bash
python tools/dataset_converters/update_infos_to_v2.py --dataset ${DATA_SET} --pkl-path ${PKL_PATH} --out-dir ${OUT_DIR}
```
- `--dataset`:数据集名。
- `--pkl-path`:指定数据信息 pkl 文件路径。
- `--out-dir`:输出数据信息 pkl 文件目录。
例如:
```bash
python tools/dataset_converters/update_infos_to_v2.py --dataset kitti --pkl-path ./data/kitti/kitti_infos_trainval.pkl --out-dir ./data/kitti
```
.. toctree:: .. toctree::
:maxdepth: 3 :maxdepth: 3
1_exist_data_model.md
2_new_data_model.md 2_new_data_model.md
backends_support.md backends_support.md
config.md config.md
coord_sys_tutorial.md coord_sys_tutorial.md
data_pipeline.md data_pipeline.md
data_prepare.md dataset_prepare.md
demo.md inference.md
index.rst index.rst
model_deployment.md model_deployment.md
train_test.md
useful_tools.md useful_tools.md
visualization.md
# 样例 # 推理
# 介绍 ## 介绍
我们提供了多模态/单模态(基于激光雷达/图像)、室内/室外场景的 3D 检测和 3D 语义分割样例的脚本,预训练模型可以从 [Model Zoo](https://github.com/open-mmlab/mmdetection3d/blob/master/docs_zh-CN/model_zoo.md/) 下载。我们也提供了 KITTI、SUN RGB-D、nuScenes 和 ScanNet 数据集的预处理样本数据,你可以根据我们的预处理步骤使用任何其它数据。 我们提供了多模态/单模态(基于激光雷达/图像)、室内/室外场景的 3D 检测和 3D 语义分割样例的脚本,预训练模型可以从 [Model Zoo](https://github.com/open-mmlab/mmdetection3d/blob/dev-1.x/docs/zh_cn/model_zoo.md) 下载。我们也提供了 KITTI、SUN RGB-D、nuScenes 和 ScanNet 数据集的预处理样本数据,你可以根据我们的预处理步骤使用任何其它数据。
## 测试 ## 测试
...@@ -21,13 +21,13 @@ python demo/pcd_demo.py ${PCD_FILE} ${CONFIG_FILE} ${CHECKPOINT_FILE} [--device ...@@ -21,13 +21,13 @@ python demo/pcd_demo.py ${PCD_FILE} ${CONFIG_FILE} ${CHECKPOINT_FILE} [--device
在 KITTI 数据上测试 [SECOND](https://github.com/open-mmlab/mmdetection3d/tree/master/configs/second) 模型: 在 KITTI 数据上测试 [SECOND](https://github.com/open-mmlab/mmdetection3d/tree/master/configs/second) 模型:
```shell ```shell
python demo/pcd_demo.py demo/data/kitti/kitti_000008.bin configs/second/hv_second_secfpn_6x8_80e_kitti-3d-car.py checkpoints/hv_second_secfpn_6x8_80e_kitti-3d-car_20200620_230238-393f000c.pth python demo/pcd_demo.py demo/data/kitti/000008.bin configs/second/second_hv-secfpn_8xb6-80e_kitti-3d-car.py checkpoints/second_hv-secfpn_8xb6-80e_kitti-3d-car_20200620_230238-393f000c.pth
``` ```
在 SUN RGB-D 数据上测试 [VoteNet](https://github.com/open-mmlab/mmdetection3d/tree/master/configs/votenet) 模型: 在 SUN RGB-D 数据上测试 [VoteNet](https://github.com/open-mmlab/mmdetection3d/tree/master/configs/votenet) 模型:
```shell ```shell
python demo/pcd_demo.py demo/data/sunrgbd/sunrgbd_000017.bin configs/votenet/votenet_8xb16_sunrgbd-3d.py checkpoints/votenet_16x8_sunrgbd-3d-10class_20200620_230238-4483c0c0.pth python demo/pcd_demo.py demo/data/sunrgbd/sunrgbd_000017.bin configs/votenet/votenet_8xb16_sunrgbd-3d.py checkpoints/votenet_8xb16_sunrgbd-3d_20200620_230238-4483c0c0.pth
``` ```
如果你正在使用的 mmdetection3d 版本 >= 0.6.0,记住转换 VoteNet 的模型权重文件,查看 [README](https://github.com/open-mmlab/mmdetection3d/blob/master/configs/votenet/README.md/) 来获取转换模型权重文件的详细说明。 如果你正在使用的 mmdetection3d 版本 >= 0.6.0,记住转换 VoteNet 的模型权重文件,查看 [README](https://github.com/open-mmlab/mmdetection3d/blob/master/configs/votenet/README.md/) 来获取转换模型权重文件的详细说明。
...@@ -45,13 +45,13 @@ python demo/multi_modality_demo.py ${PCD_FILE} ${IMAGE_FILE} ${ANNOTATION_FILE} ...@@ -45,13 +45,13 @@ python demo/multi_modality_demo.py ${PCD_FILE} ${IMAGE_FILE} ${ANNOTATION_FILE}
在 KITTI 数据上测试 [MVX-Net](https://github.com/open-mmlab/mmdetection3d/tree/master/configs/mvxnet) 模型: 在 KITTI 数据上测试 [MVX-Net](https://github.com/open-mmlab/mmdetection3d/tree/master/configs/mvxnet) 模型:
```shell ```shell
python demo/multi_modality_demo.py demo/data/kitti/kitti_000008.bin demo/data/kitti/kitti_000008.png demo/data/kitti/kitti_000008_infos.pkl configs/mvxnet/mvxnet_fpn_dv_second_secfpn_8xb2-80e_kitti-3d-3class.py checkpoints/dv_mvx-fpn_second_secfpn_adamw_2x8_80e_kitti-3d-3class_20200621_003904-10140f2d.pth python demo/multi_modality_demo.py demo/data/kitti/000008.bin demo/data/kitti/000008.png demo/data/kitti/000008.pkl configs/mvxnet/mvx_fpn-dv-second-secfpn_8xb2-80e_kitti-3d-3class.py checkpoints/mvx_fpn-dv-second-secfpn_8xb2-80e_kitti-3d-3class_20200621_003904-10140f2d.pth
``` ```
在 SUN RGB-D 数据上测试 [ImVoteNet](https://github.com/open-mmlab/mmdetection3d/tree/master/configs/imvotenet) 模型: 在 SUN RGB-D 数据上测试 [ImVoteNet](https://github.com/open-mmlab/mmdetection3d/tree/master/configs/imvotenet) 模型:
```shell ```shell
python demo/multi_modality_demo.py demo/data/sunrgbd/sunrgbd_000017.bin demo/data/sunrgbd/sunrgbd_000017.jpg demo/data/sunrgbd/sunrgbd_000017_infos.pkl configs/imvotenet/imvotenet_stage2_8xb16_sunrgbd-3d.py checkpoints/imvotenet_stage2_16x8_sunrgbd-3d-10class_20210323_184021-d44dcb66.pth python demo/multi_modality_demo.py demo/data/sunrgbd/sunrgbd_000017.bin demo/data/sunrgbd/sunrgbd_000017.jpg demo/data/sunrgbd/sunrgbd_000017_infos.pkl configs/imvotenet/imvotenet_stage2_8xb16_sunrgbd.py checkpoints/imvotenet_stage2_8xb16_sunrgbd_20210323_184021-d44dcb66.pth
``` ```
### 单目 3D 检测 ### 单目 3D 检测
...@@ -67,9 +67,11 @@ python demo/mono_det_demo.py ${IMAGE_FILE} ${ANNOTATION_FILE} ${CONFIG_FILE} ${C ...@@ -67,9 +67,11 @@ python demo/mono_det_demo.py ${IMAGE_FILE} ${ANNOTATION_FILE} ${CONFIG_FILE} ${C
在 nuScenes 数据上测试 [FCOS3D](https://github.com/open-mmlab/mmdetection3d/tree/master/configs/fcos3d) 模型: 在 nuScenes 数据上测试 [FCOS3D](https://github.com/open-mmlab/mmdetection3d/tree/master/configs/fcos3d) 模型:
```shell ```shell
python demo/mono_det_demo.py demo/data/nuscenes/n015-2018-07-24-11-22-45+0800__CAM_BACK__1532402927637525.jpg demo/data/nuscenes/n015-2018-07-24-11-22-45+0800__CAM_BACK__1532402927637525_mono3d.pkl configs/fcos3d/fcos3d_r101-caffe-dcn_fpn_head-gn_8xb2-1x_nus-mono3d_finetune.py checkpoints/fcos3d_r101_caffe_fpn_gn-head_dcn_2x8_1x_nus-mono3d_finetune_20210717_095645-8d806dc2.pth python demo/mono_det_demo.py demo/data/nuscenes/n015-2018-07-24-11-22-45+0800__CAM_BACK__1532402927637525.jpg demo/data/nuscenes/n015-2018-07-24-11-22-45+0800__CAM_BACK__1532402927637525.pkl configs/fcos3d/fcos3d_r101-caffe-dcn-fpn-head-gn_8xb2-1x_nus-mono3d_finetune.py checkpoints/fcos3d_r101-caffe-dcn-fpn-head-gn_8xb2-1x_nus-mono3d_finetune_20210717_095645-8d806dc2.pth
``` ```
注意当对翻转图像可视化单目 3D 检测结果是,相机内参矩阵也应该相应修改。在 PR [#744](https://github.com/open-mmlab/mmdetection3d/pull/744) 中可以了解更多细节和示例。
### 3D 分割 ### 3D 分割
在点云数据上测试 3D 分割器,运行: 在点云数据上测试 3D 分割器,运行:
...@@ -83,5 +85,5 @@ python demo/pc_seg_demo.py ${PCD_FILE} ${CONFIG_FILE} ${CHECKPOINT_FILE} [--devi ...@@ -83,5 +85,5 @@ python demo/pc_seg_demo.py ${PCD_FILE} ${CONFIG_FILE} ${CHECKPOINT_FILE} [--devi
在 ScanNet 数据上测试 [PointNet++ (SSG)](https://github.com/open-mmlab/mmdetection3d/tree/master/configs/pointnet2) 模型: 在 ScanNet 数据上测试 [PointNet++ (SSG)](https://github.com/open-mmlab/mmdetection3d/tree/master/configs/pointnet2) 模型:
```shell ```shell
python demo/pc_seg_demo.py demo/data/scannet/scene0000_00.bin configs/pointnet2/pointnet2_ssg_2xb16-cosine-200e_scannet-seg.py checkpoints/pointnet2_ssg_16x2_cosine_200e_scannet_seg-3d-20class_20210514_143644-ee73704a.pth python demo/pc_seg_demo.py demo/data/scannet/scene0000_00.bin configs/pointnet2/pointnet2_ssg_2xb16-cosine-200e_scannet-seg.py checkpoints/pointnet2_ssg_2xb16-cosine-200e_scannet-seg_20210514_143644-ee73704a.pth
``` ```
# 1: 使用已有模型在标准数据集上进行推理和训练 # 使用已有模型在标准数据集上进行推理和训练
## 使用已有模型进行推理 ## 使用已有模型进行推理
这里我们提供了评测 SUNRGBD、ScanNet、KITTI 等多个数据集的测试脚本。 这里我们提供了评测 SUNRGBD、ScanNet、KITTI 等多个数据集的测试脚本。
请参考[开始](https://mmdetection3d.readthedocs.io/zh_CN/latest/getting_started.html)下的验证/样例来获取更容易集成到其它项目和基本样例的高级接口。 请参考[开始](https://mmdetection3d.readthedocs.io/zh_CN/dev-1.x/inference.html)下的验证/样例来获取更容易集成到其它项目和基本样例的高级接口。
### 在标准数据集上测试已有模型 ### 在标准数据集上测试已有模型
...@@ -33,10 +33,17 @@ python tools/test.py ${CONFIG_FILE} ${CHECKPOINT_FILE} [--out ${RESULT_FILE}] [- ...@@ -33,10 +33,17 @@ python tools/test.py ${CONFIG_FILE} ${CHECKPOINT_FILE} [--out ${RESULT_FILE}] [-
可选参数: 可选参数:
- `RESULT_FILE`:输出结果(pickle 格式)的文件名,如果未指定,结果不会被保存。 - `--show`:如果被指定,检测结果会在静默模式下被保存,用于调试和可视化,但只在单块 GPU 测试的情况下生效,和 `--show-dir` 搭配使用。
- `EVAL_METRICS`:在结果上评测的项,不同的数据集有不同的合法值。具体来说,我们默认对不同的数据集都使用各自的官方度量方法进行评测,所以对 nuScenes、Lyft、ScanNet 和 SUNRGBD 这些数据集来说在检测任务上可以简单设置为 `mAP`;对 KITTI 数据集来说,如果我们只想评测 2D 检测效果,可以将度量方法设置为 `img_bbox`;对于 Waymo 数据集,我们提供了 KITTI 风格(不稳定)和 Waymo 官方风格这两种评测方法,分别对应 `kitti``waymo`,我们推荐使用默认的官方度量方法,它的性能稳定而且可以与其它算法公平比较;同样地,对 S3DIS、ScanNet 这些数据集来说,在分割任务上的度量方法可以设置为 `mIoU` - `--show-dir`:如果被指定,检测结果会被保存在指定文件夹下的 `***_points.obj``***_pred.obj` 文件中,用于调试和可视化,但只在单块 GPU 测试的情况下生效,对于这个选项,图形化界面在你的环境中不是必需的。
- `--show`:如果被指定,检测结果会在静默模式下被保存,用于调试和可视化,但只在单块GPU测试的情况下生效,和 `--show-dir` 搭配使用。
- `--show-dir`:如果被指定,检测结果会被保存在指定文件夹下的 `***_points.obj``***_pred.obj` 文件中,用于调试和可视化,但只在单块GPU测试的情况下生效,对于这个选项,图形化界面在你的环境中不是必需的。 所有和评估相关的参数在相应的数据集配置的 `test_evaluator` 中设置。例如 `test_evaluator = dict(type='KittiMetric', ann_file=data_root + 'kitti_infos_val.pkl', pklfile_prefix=None, submission_prefix=None)`
参数:
- `type`:相对应的评价指标名,通常和数据集相关联。
- `ann_file`:标注文件路径。
- `pklfile_prefix`:可选参数。输出结果保存成 pickle 格式的文件名。如果没有指定,结果将不会保存成文件。
- `submission_prefix`:可选参数。结果将被保存到文件中,然后你可以将它上传到官方评估服务器中。
示例: 示例:
...@@ -55,77 +62,75 @@ python tools/test.py ${CONFIG_FILE} ${CHECKPOINT_FILE} [--out ${RESULT_FILE}] [- ...@@ -55,77 +62,75 @@ python tools/test.py ${CONFIG_FILE} ${CHECKPOINT_FILE} [--out ${RESULT_FILE}] [-
```shell ```shell
python tools/test.py configs/votenet/votenet_8xb8_scannet-3d.py \ python tools/test.py configs/votenet/votenet_8xb8_scannet-3d.py \
checkpoints/votenet_8x8_scannet-3d-18class_20200620_230238-2cea9c3a.pth \ checkpoints/votenet_8x8_scannet-3d-18class_20200620_230238-2cea9c3a.pth \
--eval mAP --show --show-dir ./data/scannet/show_results
--eval-options 'show=True' 'out_dir=./data/scannet/show_results'
``` ```
3. 在 ScanNet 数据集上测试 VoteNet(不保存测试结果),计算 mAP 3. 在 ScanNet 数据集上测试 VoteNet(不保存测试结果),计算 mAP
```shell ```shell
python tools/test.py configs/votenet/votenet_8xb8_scannet-3d.py \ python tools/test.py configs/votenet/votenet_8xb8_scannet-3d.py \
checkpoints/votenet_8x8_scannet-3d-18class_20200620_230238-2cea9c3a.pth \ checkpoints/votenet_8x8_scannet-3d-18class_20200620_230238-2cea9c3a.pth
--eval mAP
``` ```
4. 使用8块显卡在 KITTI 数据集上测试 SECOND,计算 mAP 4. 使用 8 块显卡在 KITTI 数据集上测试 SECOND,计算 mAP
```shell ```shell
./tools/slurm_test.sh ${PARTITION} ${JOB_NAME} configs/second/hv_second_secfpn_6x8_80e_kitti-3d-3class.py \ ./tools/slurm_test.sh ${PARTITION} ${JOB_NAME} configs/second/second_hv_secfpn_8xb6-80e_kitti-3d-3class.py \
checkpoints/hv_second_secfpn_6x8_80e_kitti-3d-3class_20200620_230238-9208083a.pth \ checkpoints/hv_second_secfpn_6x8_80e_kitti-3d-3class_20200620_230238-9208083a.pth
--out results.pkl --eval mAP
``` ```
5. 使用8块显卡在 nuScenes 数据集上测试 PointPillars,生成提交给官方评测服务器的 json 文件 5. 使用 8 块显卡在 nuScenes 数据集上测试 PointPillars,生成提交给官方评测服务器的 json 文件
```shell ```shell
./tools/slurm_test.sh ${PARTITION} ${JOB_NAME} configs/pointpillars/pointpillars_hv_fpn_sbn-all_8xb4-2x_nus-3d.py \ ./tools/slurm_test.sh ${PARTITION} ${JOB_NAME} configs/pointpillars/pointpillars_hv_secfpn_sbn-all_8xb4-2x_nus-3d.py \
checkpoints/hv_pointpillars_fpn_sbn-all_4x8_2x_nus-3d_20200620_230405-2fa62f3d.pth \ checkpoints/hv_pointpillars_fpn_sbn-all_4x8_2x_nus-3d_20200620_230405-2fa62f3d.pth \
--format-only --eval-options 'jsonfile_prefix=./pointpillars_nuscenes_results' --cfg-options 'test_evaluator.jsonfile_prefix=./pointpillars_nuscenes_results'
``` ```
生成的结果会保存在 `./pointpillars_nuscenes_results` 目录。 生成的结果会保存在 `./pointpillars_nuscenes_results` 目录。
6. 使用8块显卡在 KITTI 数据集上测试 SECOND,生成提交给官方评测服务器的 txt 文件 6. 使用 8 块显卡在 KITTI 数据集上测试 SECOND,生成提交给官方评测服务器的 txt 文件
```shell ```shell
./tools/slurm_test.sh ${PARTITION} ${JOB_NAME} configs/second/hv_second_secfpn_6x8_80e_kitti-3d-3class.py \ ./tools/slurm_test.sh ${PARTITION} ${JOB_NAME} configs/second/second_hv_secfpn_8xb6-80e_kitti-3d-3class.py \
checkpoints/hv_second_secfpn_6x8_80e_kitti-3d-3class_20200620_230238-9208083a.pth \ checkpoints/hv_second_secfpn_6x8_80e_kitti-3d-3class_20200620_230238-9208083a.pth \
--format-only --eval-options 'pklfile_prefix=./second_kitti_results' 'submission_prefix=./second_kitti_results' --cfg-options 'test_evaluator.pklfile_prefix=./second_kitti_results' 'submission_prefix=./second_kitti_results'
``` ```
生成的结果会保存在 `./second_kitti_results` 目录。 生成的结果会保存在 `./second_kitti_results` 目录。
7. 使用8块显卡在 Lyft 数据集上测试 PointPillars,生成提交给排行榜的 pkl 文件 7. 使用 8 块显卡在 Lyft 数据集上测试 PointPillars,生成提交给排行榜的 pkl 文件
```shell ```shell
./tools/slurm_test.sh ${PARTITION} ${JOB_NAME} configs/pointpillars/hv_pointpillars_fpn_sbn-2x8_2x_lyft-3d.py \ ./tools/slurm_test.sh ${PARTITION} ${JOB_NAME} configs/pointpillars/hv_pointpillars_fpn_sbn-2x8_2x_lyft-3d.py \
checkpoints/hv_pointpillars_fpn_sbn-2x8_2x_lyft-3d_latest.pth --out results/pp_lyft/results_challenge.pkl \ checkpoints/hv_pointpillars_fpn_sbn-2x8_2x_lyft-3d_latest.pth \
--format-only --eval-options 'jsonfile_prefix=results/pp_lyft/results_challenge' \ --cfg-options 'test_evaluator.jsonfile_prefix=results/pp_lyft/results_challenge' \
'csv_savepath=results/pp_lyft/results_challenge.csv' 'test_evaluator.csv_savepath=results/pp_lyft/results_challenge.csv' \
'test_evaluator.pklfile_prefix=results/pp_lyft/results_challenge.pkl'
``` ```
**注意**:为了生成 Lyft 数据集的提交结果,`--eval-options` 必须指定 `csv_savepath`。生成 csv 文件后,你可以使用[网站](https://www.kaggle.com/c/3d-object-detection-for-autonomous-vehicles/submit)上给出的 kaggle 命令提交结果。 **注意**:为了生成 Lyft 数据集的提交结果,`--eval-options` 必须指定 `csv_savepath`。生成 csv 文件后,你可以使用[网站](https://www.kaggle.com/c/3d-object-detection-for-autonomous-vehicles/submit)上给出的 kaggle 命令提交结果。
注意在 [Lyft 数据集的配置文件](../../configs/_base_/datasets/lyft-3d.py)`test` 中的 `ann_file` 值为 `lyft_infos_test.pkl`,是没有标注的 Lyft 官方测试集。要在验证数据集上测试,请把它改为 `lyft_infos_val.pkl` 注意在 [Lyft 数据集的配置文件](../../configs/_base_/datasets/lyft-3d.py)`test` 中的 `ann_file` 值为 `lyft_infos_test.pkl`,是没有标注的 Lyft 官方测试集。要在验证数据集上测试,请把它改为 `lyft_infos_val.pkl`
8. 使用8块显卡在 waymo 数据集上测试 PointPillars,使用 waymo 度量方法计算 mAP 8. 使用 8 块显卡在 waymo 数据集上测试 PointPillars,使用 waymo 度量方法计算 mAP
```shell ```shell
./tools/slurm_test.sh ${PARTITION} ${JOB_NAME} configs/pointpillars/hv_pointpillars_secfpn_sbn-2x16_2x_waymo-3d-car.py \ ./tools/slurm_test.sh ${PARTITION} ${JOB_NAME} configs/pointpillars/pointpillars_hv_secfpn_sbn-all_16xb2-2x_waymo-3d-car.py \
checkpoints/hv_pointpillars_secfpn_sbn-2x16_2x_waymo-3d-car_latest.pth --out results/waymo-car/results_eval.pkl \ checkpoints/hv_pointpillars_secfpn_sbn-2x16_2x_waymo-3d-car_latest.pth \
--eval waymo --eval-options 'pklfile_prefix=results/waymo-car/kitti_results' \ --cfg-options 'test_evaluator.pklfile_prefix=results/waymo-car/kitti_results' \
'submission_prefix=results/waymo-car/kitti_results' 'test_evaluator.submission_prefix=results/waymo-car/kitti_results'
``` ```
**注意**:对于 waymo 数据集上的评估,请根据[说明](https://github.com/waymo-research/waymo-open-dataset/blob/master/docs/quick_start.md/)构建二进制文件 `compute_detection_metrics_main` 来做度量计算,并把它放在 `mmdet3d/core/evaluation/waymo_utils/`。(在使用 bazel 构建 `compute_detection_metrics_main` 时,有时会出现 `'round' is not a member of 'std'` 的错误,我们只需要把那个文件中 `round` 前的 `std::` 去掉。)二进制文件生成时需要在 `--eval-options` 中给定 `pklfile_prefix`。对于度量方法,`waymo` 是推荐的官方评估策略,目前 `kitti` 评估是依照 KITTI 而来的,每个难度的结果和 KITTI 的定义并不完全一致。目前大多数物体都被标记为0难度,会在未来修复。它的不稳定原因包括评估的计算大、转换后的数据缺乏遮挡和截断、难度的定义不同以及平均精度的计算方法不同。 **注意**:对于 waymo 数据集上的评估,请根据[说明](https://github.com/waymo-research/waymo-open-dataset/blob/master/docs/quick_start.md/)构建二进制文件 `compute_detection_metrics_main` 来做度量计算,并把它放在 `mmdet3d/core/evaluation/waymo_utils/`。(在使用 bazel 构建 `compute_detection_metrics_main` 时,有时会出现 `'round' is not a member of 'std'` 的错误,我们只需要把那个文件中 `round` 前的 `std::` 去掉。)二进制文件生成时需要在 `--eval-options` 中给定 `pklfile_prefix`。对于度量方法,`waymo` 是推荐的官方评估策略,目前 `kitti` 评估是依照 KITTI 而来的,每个难度的结果和 KITTI 的定义并不完全一致。目前大多数物体都被标记为0难度,会在未来修复。它的不稳定原因包括评估的计算大、转换后的数据缺乏遮挡和截断、难度的定义不同以及平均精度的计算方法不同。
9. 使用8块显卡在 waymo 数据集上测试 PointPillars,生成 bin 文件并提交到排行榜 9. 使用 8 块显卡在 waymo 数据集上测试 PointPillars,生成 bin 文件并提交到排行榜
```shell ```shell
./tools/slurm_test.sh ${PARTITION} ${JOB_NAME} configs/pointpillars/hv_pointpillars_secfpn_sbn-2x16_2x_waymo-3d-car.py \ ./tools/slurm_test.sh ${PARTITION} ${JOB_NAME} configs/pointpillars/pointpillars_hv_secfpn_sbn-all_16xb2-2x_waymo-3d-car.py \
checkpoints/hv_pointpillars_secfpn_sbn-2x16_2x_waymo-3d-car_latest.pth --out results/waymo-car/results_eval.pkl \ checkpoints/hv_pointpillars_secfpn_sbn-2x16_2x_waymo-3d-car_latest.pth \
--format-only --eval-options 'pklfile_prefix=results/waymo-car/kitti_results' \ --cfg-options 'test_evaluator.pklfile_prefix=results/waymo-car/kitti_results' \
'submission_prefix=results/waymo-car/kitti_results' 'test_evaluator.submission_prefix=results/waymo-car/kitti_results'
``` ```
**注意**:生成 bin 文件后,你可以简单地构建二进制文件 `create_submission`,并根据[说明](https://github.com/waymo-research/waymo-open-dataset/blob/master/docs/quick_start.md/)创建提交的文件。要在验证服务器上评测验证数据集,你也可以用同样的方式生成提交的文件。 **注意**:生成 bin 文件后,你可以简单地构建二进制文件 `create_submission`,并根据[说明](https://github.com/waymo-research/waymo-open-dataset/blob/master/docs/quick_start.md/)创建提交的文件。要在验证服务器上评测验证数据集,你也可以用同样的方式生成提交的文件。
...@@ -139,11 +144,11 @@ MMDetection3D 分别用 `MMDistributedDataParallel` and `MMDataParallel` 实现 ...@@ -139,11 +144,11 @@ MMDetection3D 分别用 `MMDistributedDataParallel` and `MMDataParallel` 实现
默认我们每过一个周期都在验证数据集上评测模型,你可以通过在训练配置里添加间隔参数来改变评测的时间间隔: 默认我们每过一个周期都在验证数据集上评测模型,你可以通过在训练配置里添加间隔参数来改变评测的时间间隔:
```python ```python
evaluation = dict(interval=12) # 每12个周期评估一次模型 train_cfg = dict(type='EpochBasedTrainLoop', val_interval=1) # 每12个周期评估一次模型
``` ```
**重要**:配置文件中的默认学习率对应8块显卡,配置文件名里有具体的批量大小,比如'2x8'表示一共8块显卡,每块显卡2个样本。 **重要**:配置文件中的默认学习率对应 8 块显卡,配置文件名里有具体的批量大小,比如 '2xb8' 表示一共 8 块显卡,每块显卡 2 个样本。
根据 [Linear Scaling Rule](https://arxiv.org/abs/1706.02677),当你使用不同数量的显卡或每块显卡有不同数量的图像时,需要依批量大小按比例调整学习率。如果用4块显卡、每块显卡2幅图像时学习率为0.01,那么用16块显卡、每块显卡4幅图像时学习率应设为0.08。然而,由于大多数模型使用 ADAM 而不是 SGD 进行优化,上述规则可能并不适用,用户需要自己调整学习率。 根据 [Linear Scaling Rule](https://arxiv.org/abs/1706.02677),当你使用不同数量的显卡或每块显卡有不同数量的图像时,需要依批量大小按比例调整学习率。如果用 4 块显卡、每块显卡 2 幅图像时学习率为 0.01,那么用 16 块显卡、每块显卡 4 幅图像时学习率应设为 0.08。然而,由于大多数模型使用 ADAM 而不是 SGD 进行优化,上述规则可能并不适用,用户需要自己调整学习率。
### 使用单块显卡进行训练 ### 使用单块显卡进行训练
...@@ -175,15 +180,7 @@ export CUDA_VISIBLE_DEVICES=-1 ...@@ -175,15 +180,7 @@ export CUDA_VISIBLE_DEVICES=-1
可选参数: 可选参数:
- `--no-validate`**不推荐**):默认情况下,代码在训练阶段每 k(默认值是1,可以像[这里](https://github.com/open-mmlab/mmdetection3d/blob/master/configs/fcos3d/fcos3d_r101_caffe_fpn_gn-head_dcn_2x8_1x_nus-mono3d.py#L75)一样修改)个周期做一次评测,如果要取消评测,使用 `--no-validate` - `--cfg-options 'Key=value'`:覆盖使用的配置中的一些设定。
- `--work-dir ${WORK_DIR}`:覆盖配置文件中的指定工作目录。
- `--resume-from ${CHECKPOINT_FILE}`:从之前的模型权重文件中恢复。
- `--options 'Key=value'`:覆盖使用的配置中的一些设定。
`resume-from``load-from` 的不同点:
- `resume-from` 加载模型权重和优化器状态,同时周期数也从特定的模型权重文件中继承,通常用于恢复偶然中断的训练过程。
- `load-from` 仅加载模型权重,训练周期从0开始,通常用于微调。
### 使用多个机器进行训练 ### 使用多个机器进行训练
...@@ -193,10 +190,10 @@ export CUDA_VISIBLE_DEVICES=-1 ...@@ -193,10 +190,10 @@ export CUDA_VISIBLE_DEVICES=-1
[GPUS=${GPUS}] ./tools/slurm_train.sh ${PARTITION} ${JOB_NAME} ${CONFIG_FILE} ${WORK_DIR} [GPUS=${GPUS}] ./tools/slurm_train.sh ${PARTITION} ${JOB_NAME} ${CONFIG_FILE} ${WORK_DIR}
``` ```
下面是一个使用16块显卡在 dev 分区上训练 Mask R-CNN 的示例: 下面是一个使用 16 块显卡在 dev 分区上训练 Mask R-CNN 的示例:
```shell ```shell
GPUS=16 ./tools/slurm_train.sh dev pp_kitti_3class pointpillars_hv_secfpn_8xb6-160e_kitti-3d-3class.py /nfs/xxxx/pp_kitti_3class GPUS=16 ./tools/slurm_train.sh dev pp_kitti_3class configs/pointpillars/pointpillars_hv_secfpn_8xb6-160e_kitti-3d-3class.py /nfs/xxxx/pp_kitti_3class
``` ```
你可以查看 [slurm_train.sh](https://github.com/open-mmlab/mmdetection/blob/master/tools/slurm_train.sh) 来获取所有的参数和环境变量。 你可以查看 [slurm_train.sh](https://github.com/open-mmlab/mmdetection/blob/master/tools/slurm_train.sh) 来获取所有的参数和环境变量。
...@@ -230,11 +227,11 @@ CUDA_VISIBLE_DEVICES=4,5,6,7 PORT=29501 ./tools/dist_train.sh ${CONFIG_FILE} 4 ...@@ -230,11 +227,11 @@ CUDA_VISIBLE_DEVICES=4,5,6,7 PORT=29501 ./tools/dist_train.sh ${CONFIG_FILE} 4
如果你使用 Slurm 启动训练任务,有两种方式指定端口: 如果你使用 Slurm 启动训练任务,有两种方式指定端口:
1. 通过 `--options` 设置端口,这是更推荐的,因为它不改变原来的配置 1. 通过 `--cfg-options` 设置端口,这是更推荐的,因为它不改变原来的配置
```shell ```shell
CUDA_VISIBLE_DEVICES=0,1,2,3 GPUS=4 ./tools/slurm_train.sh ${PARTITION} ${JOB_NAME} config1.py ${WORK_DIR} --options 'dist_params.port=29500' CUDA_VISIBLE_DEVICES=0,1,2,3 GPUS=4 ./tools/slurm_train.sh ${PARTITION} ${JOB_NAME} config1.py ${WORK_DIR} --cfg-options 'env_cfg.dist_cfg.port=29500'
CUDA_VISIBLE_DEVICES=4,5,6,7 GPUS=4 ./tools/slurm_train.sh ${PARTITION} ${JOB_NAME} config2.py ${WORK_DIR} --options 'dist_params.port=29501' CUDA_VISIBLE_DEVICES=4,5,6,7 GPUS=4 ./tools/slurm_train.sh ${PARTITION} ${JOB_NAME} config2.py ${WORK_DIR} --cfg-options 'env_cfg.dist_cfg.port=29501'
``` ```
2. 修改配置文件(通常在配置文件的倒数第6行)来设置不同的通信端口 2. 修改配置文件(通常在配置文件的倒数第6行)来设置不同的通信端口
...@@ -242,13 +239,17 @@ CUDA_VISIBLE_DEVICES=4,5,6,7 PORT=29501 ./tools/dist_train.sh ${CONFIG_FILE} 4 ...@@ -242,13 +239,17 @@ CUDA_VISIBLE_DEVICES=4,5,6,7 PORT=29501 ./tools/dist_train.sh ${CONFIG_FILE} 4
`config1.py` 中, `config1.py` 中,
```python ```python
dist_params = dict(backend='nccl', port=29500) env_cfg = dict(
dist_cfg=dict(backend='nccl', port=29500)
)
``` ```
`config2.py` 中, `config2.py` 中,
```python ```python
dist_params = dict(backend='nccl', port=29501) env_cfg = dict(
dist_cfg=dict(backend='nccl', port=29501)
)
``` ```
然后,你可以使用 `config1.py` and `config2.py` 启动两个任务 然后,你可以使用 `config1.py` and `config2.py` 启动两个任务
......
# 有用的工具
我们在 `tools/` 文件夹路径下提供了许多有用的工具。 我们在 `tools/` 文件夹路径下提供了许多有用的工具。
# 日志分析 ## 日志分析
给定一个训练的日志文件,您可以绘制出 loss/mAP 曲线。首先需要运行 `pip install seaborn` 安装依赖包。 给定一个训练的日志文件,您可以绘制出 loss/mAP 曲线。首先需要运行 `pip install seaborn` 安装依赖包。
![loss曲线图](../../resources/loss_curve.png) ![loss曲线图](../../../resources/loss_curve.png)
```shell ```shell
python tools/analysis_tools/analyze_logs.py plot_curve [--keys ${KEYS}] [--title ${TITLE}] [--legend ${LEGEND}] [--backend ${BACKEND}] [--style ${STYLE}] [--out ${OUT_FILE}] [--mode ${MODE}] [--interval ${INTERVAL}] python tools/analysis_tools/analyze_logs.py plot_curve [--keys ${KEYS}] [--title ${TITLE}] [--legend ${LEGEND}] [--backend ${BACKEND}] [--style ${STYLE}] [--out ${OUT_FILE}] [--mode ${MODE}] [--interval ${INTERVAL}]
...@@ -53,88 +55,13 @@ average iter time: 1.1959 s/iter ...@@ -53,88 +55,13 @@ average iter time: 1.1959 s/iter
&#8195; &#8195;
# 可视化 ## 模型部署
## 结果
为了观察模型的预测结果,您可以运行下面的指令
```bash
python tools/test.py ${CONFIG_FILE} ${CKPT_PATH} --show --show-dir ${SHOW_DIR}
```
在运行这个指令后,所有的绘制结果包括输入数据,以及在输入数据基础上可视化的网络输出和真值(例如: 3D 单模态检测任务中的 `***_points.obj``***_pred.obj`),将会被保存在 `${SHOW_DIR}`
要在评估期间看见预测结果,您可以运行下面的指令
```bash
python tools/test.py ${CONFIG_FILE} ${CKPT_PATH} --eval 'mAP' --eval-options 'show=True' 'out_dir=${SHOW_DIR}'
```
在运行这个指令后,您将会在 `${SHOW_DIR}` 获得输入数据、可视化在输入上的网络输出和真值标签(例如:在多模态检测任务中的`***_points.obj``***_pred.obj``***_gt.obj``***_img.png``***_pred.png` )。当 `show` 被激活,[Open3D](http://www.open3d.org/) 将会被用来在线可视化结果。当您在没有 GUI 的远程服务器上运行测试的时候,无法进行在线可视化,您可以设定 `show=False` 将输出结果保存在 `{SHOW_DIR}`
至于离线可视化,您将有两个选择。
利用 `Open3D` 后端可视化结果,您可以运行下面的指令
```bash
python tools/misc/visualize_results.py ${CONFIG_FILE} --result ${RESULTS_PATH} --show-dir ${SHOW_DIR}
```
![](../../resources/open3d_visual.*)
或者您可以使用 3D 可视化软件,例如 [MeshLab](http://www.meshlab.net/) 来打开这些在 `${SHOW_DIR}` 目录下的文件,从而查看 3D 检测输出。具体来说,打开 `***_points.obj` 查看输入点云,打开 `***_pred.obj` 查看预测的 3D 边界框。这允许推理和结果生成在远程服务器中完成,用户可以使用 GUI 在他们的主机上打开它们。
**注意**:可视化接口有一些不稳定,我们将计划和 MMDetection 一起重构这一部分。
## 数据集
我们也提供脚本用来可视化数据集,而无需推理。您可以使用 `tools/misc/browse_dataset.py` 来在线显示载入的数据和真值标签,并且保存进磁盘。现在我们支持所有数据集上的单模态 3D 检测和 3D 分割,支持 KITTI 和 SUN RGB-D 数据集上的多模态 3D 检测,同时支持 nuScenes 数据集上的单目 3D 检测。为了浏览 KITTI 数据集,您可以运行下面的指令
```shell
python tools/misc/browse_dataset.py configs/_base_/datasets/kitti-3d-3class.py --task det --output-dir ${OUTPUT_DIR} --online
```
**注意**:一旦指定 `--output-dir` ,当按下 open3d 窗口的 `_ESC_`,用户指定的视图图像将被保存。如果您没有显示器,您可以移除 `--online` 标志,从而仅仅保存可视化结果并且进行离线浏览。
为了验证数据的一致性和数据增强的效果,您还可以使用以下命令添加 `--aug` 标志来可视化数据增强后的数据:
```shell
python tools/misc/browse_dataset.py configs/_base_/datasets/kitti-3d-3class.py --task det --aug --output-dir ${OUTPUT_DIR} --online
```
如果您还想显示 2D 图像以及投影的 3D 边界框,则需要找到支持多模态数据加载的配置文件,然后将 `--task` 参数更改为 `multi-modality_det`。一个例子如下所示
```shell
python tools/misc/browse_dataset.py configs/mvxnet/mvxnet_fpn_dv_second_secfpn_8xb2-80e_kitti-3d-3class.py --task multi-modality_det --output-dir ${OUTPUT_DIR} --online
```
![](../../resources/browse_dataset_multi_modality.png)
您可以简单的使用不同的配置文件,浏览不同的数据集,例如:在 3D 语义分割任务中可视化 ScanNet 数据集
```shell
python tools/misc/browse_dataset.py configs/_base_/datasets/scannet-seg.py --task seg --output-dir ${OUTPUT_DIR} --online
```
![](../../resources/browse_dataset_seg.png)
在单目 3D 检测任务中浏览 nuScenes 数据集
```shell
python tools/misc/browse_dataset.py configs/_base_/datasets/nus-mono3d.py --task mono_det --output-dir ${OUTPUT_DIR} --online
```
![](../../resources/browse_dataset_mono.png)
&#8195;
# 模型部署
**Note**: 此工具仍然处于试验阶段,目前只有 SECOND 支持用 [`TorchServe`](https://pytorch.org/serve/) 部署,我们将会在未来支持更多的模型。 **注意**此工具仍然处于试验阶段,目前只有 SECOND 支持用 [`TorchServe`](https://pytorch.org/serve/) 部署,我们将会在未来支持更多的模型。
为了使用 [`TorchServe`](https://pytorch.org/serve/) 部署 `MMDetection3D` 模型,您可以遵循以下步骤: 为了使用 [`TorchServe`](https://pytorch.org/serve/) 部署 `MMDetection3D` 模型,您可以遵循以下步骤:
## 1. 将模型从 MMDetection3D 转换到 TorchServe ### 1. 将模型从 MMDetection3D 转换到 TorchServe
```shell ```shell
python tools/deployment/mmdet3d2torchserve.py ${CONFIG_FILE} ${CHECKPOINT_FILE} \ python tools/deployment/mmdet3d2torchserve.py ${CONFIG_FILE} ${CHECKPOINT_FILE} \
...@@ -144,13 +71,13 @@ python tools/deployment/mmdet3d2torchserve.py ${CONFIG_FILE} ${CHECKPOINT_FILE} ...@@ -144,13 +71,13 @@ python tools/deployment/mmdet3d2torchserve.py ${CONFIG_FILE} ${CHECKPOINT_FILE}
**Note**: ${MODEL_STORE} 需要为文件夹的绝对路径。 **Note**: ${MODEL_STORE} 需要为文件夹的绝对路径。
## 2. 构建 `mmdet3d-serve` 镜像 ### 2. 构建 `mmdet3d-serve` 镜像
```shell ```shell
docker build -t mmdet3d-serve:latest docker/serve/ docker build -t mmdet3d-serve:latest docker/serve/
``` ```
## 3. 运行 `mmdet3d-serve` ### 3. 运行 `mmdet3d-serve`
查看官网文档来 [使用 docker 运行 TorchServe](https://github.com/pytorch/serve/blob/master/docker/README.md#running-torchserve-in-a-production-docker-environment) 查看官网文档来 [使用 docker 运行 TorchServe](https://github.com/pytorch/serve/blob/master/docker/README.md#running-torchserve-in-a-production-docker-environment)
...@@ -169,7 +96,7 @@ mmdet3d-serve:latest ...@@ -169,7 +96,7 @@ mmdet3d-serve:latest
[阅读文档](https://github.com/pytorch/serve/blob/072f5d088cce9bb64b2a18af065886c9b01b317b/docs/rest_api.md/) 关于 Inference (8080), Management (8081) and Metrics (8082) 接口。 [阅读文档](https://github.com/pytorch/serve/blob/072f5d088cce9bb64b2a18af065886c9b01b317b/docs/rest_api.md/) 关于 Inference (8080), Management (8081) and Metrics (8082) 接口。
## 4. 测试部署 ### 4. 测试部署
您可以使用 `test_torchserver.py` 进行部署, 同时比较 torchserver 和 pytorch 的结果。 您可以使用 `test_torchserver.py` 进行部署, 同时比较 torchserver 和 pytorch 的结果。
...@@ -204,7 +131,7 @@ Params: 953.83 k ...@@ -204,7 +131,7 @@ Params: 953.83 k
============================== ==============================
``` ```
**注意**: 此工具仍然处于试验阶段,我们不能保证数值是绝对正确的。您可以将结果用于简单的比较,但在写技术文档报告或者论文之前您需要再次确认一下。 **注意**此工具仍然处于试验阶段,我们不能保证数值是绝对正确的。您可以将结果用于简单的比较,但在写技术文档报告或者论文之前您需要再次确认一下。
1. 计算量 (FLOPs) 和输入形状有关,但是参数量 (params) 则和输入形状无关。默认的输入形状为 (1, 40000, 4)。 1. 计算量 (FLOPs) 和输入形状有关,但是参数量 (params) 则和输入形状无关。默认的输入形状为 (1, 40000, 4)。
2. 一些运算操作不计入计算量 (FLOPs),比如说像GN和定制的运算操作,详细细节请参考 [`mmcv.cnn.get_model_complexity_info()`](https://github.com/open-mmlab/mmcv/blob/master/mmcv/cnn/utils/flops_counter.py)。 2. 一些运算操作不计入计算量 (FLOPs),比如说像GN和定制的运算操作,详细细节请参考 [`mmcv.cnn.get_model_complexity_info()`](https://github.com/open-mmlab/mmcv/blob/master/mmcv/cnn/utils/flops_counter.py)。
...@@ -212,9 +139,9 @@ Params: 953.83 k ...@@ -212,9 +139,9 @@ Params: 953.83 k
&#8195; &#8195;
# 模型转换 ## 模型转换
## RegNet 模型转换到 MMDetection ### RegNet 模型转换到 MMDetection
`tools/model_converters/regnet2mmdet.py` 将 pycls 预训练 RegNet 模型中的键转换为 MMDetection 风格。 `tools/model_converters/regnet2mmdet.py` 将 pycls 预训练 RegNet 模型中的键转换为 MMDetection 风格。
...@@ -222,7 +149,7 @@ Params: 953.83 k ...@@ -222,7 +149,7 @@ Params: 953.83 k
python tools/model_converters/regnet2mmdet.py ${SRC} ${DST} [-h] python tools/model_converters/regnet2mmdet.py ${SRC} ${DST} [-h]
``` ```
## Detectron ResNet 转换到 Pytorch ### Detectron ResNet 转换到 Pytorch
MMDetection 中的 `tools/detectron2pytorch.py` 能够把原始的 detectron 中预训练的 ResNet 模型的键转换为 PyTorch 风格。 MMDetection 中的 `tools/detectron2pytorch.py` 能够把原始的 detectron 中预训练的 ResNet 模型的键转换为 PyTorch 风格。
...@@ -230,7 +157,7 @@ MMDetection 中的 `tools/detectron2pytorch.py` 能够把原始的 detectron 中 ...@@ -230,7 +157,7 @@ MMDetection 中的 `tools/detectron2pytorch.py` 能够把原始的 detectron 中
python tools/detectron2pytorch.py ${SRC} ${DST} ${DEPTH} [-h] python tools/detectron2pytorch.py ${SRC} ${DST} ${DEPTH} [-h]
``` ```
## 准备要发布的模型 ### 准备要发布的模型
`tools/model_converters/publish_model.py` 帮助用户准备他们用于发布的模型。 `tools/model_converters/publish_model.py` 帮助用户准备他们用于发布的模型。
...@@ -256,12 +183,12 @@ python tools/model_converters/publish_model.py work_dirs/faster_rcnn/latest.pth ...@@ -256,12 +183,12 @@ python tools/model_converters/publish_model.py work_dirs/faster_rcnn/latest.pth
# 数据集转换 # 数据集转换
`tools/data_converter/` 包含转换数据集为其他格式的一些工具。其中大多数转换数据集为基于 pickle 的信息文件,比如 KITTI,nuscense 和 lyft。Waymo 转换器被用来重新组织 waymo 原始数据为 KITTI 风格。用户能够参考它们了解我们转换数据格式的方法。将它们修改为 nuImages 转换器等脚本也很方便。 `tools/dataset_converters/` 包含转换数据集为其他格式的一些工具。其中大多数转换数据集为基于 pickle 的信息文件,比如 KITTI,nuscense 和 lyft。Waymo 转换器被用来重新组织 waymo 原始数据为 KITTI 风格。用户能够参考它们了解我们转换数据格式的方法。将它们修改为 nuImages 转换器等脚本也很方便。
为了转换 nuImages 数据集为 COCO 格式,请使用下面的指令: 为了转换 nuImages 数据集为 COCO 格式,请使用下面的指令:
```shell ```shell
python -u tools/data_converter/nuimage_converter.py --data-root ${DATA_ROOT} --version ${VERSIONS} \ python -u tools/dataset_converters/nuimage_converter.py --data-root ${DATA_ROOT} --version ${VERSIONS} \
--out-dir ${OUT_DIR} --nproc ${NUM_WORKERS} --extra-tag ${TAG} --out-dir ${OUT_DIR} --nproc ${NUM_WORKERS} --extra-tag ${TAG}
``` ```
......
# 可视化
MMDetection3D 提供了 `Det3DLocalVisualizer` 用来在训练及测试阶段可视化和存储模型的状态以及结果,其具有以下特性:
1. 支持多模态数据和多任务的基本绘图界面。
2. 支持多个后端(如 local,TensorBoard),将训练状态(如 `loss``lr`)或模型评估指标写入指定的一个或多个后端中。
3. 支持多模态数据真实标签的可视化,3D 检测结果的跨模态可视化。
## 基本绘制界面
继承自 `DetLocalVisualizer``Det3DLocalVisualizer` 提供了在 2D 图像上绘制常见目标的界面,例如绘制检测框、点、文本、线、圆、多边形、二进制掩码等。关于 2D 绘制的更多细节,请参考 MMDetection 中的[可视化文档](https://mmengine.readthedocs.io/zh_CN/latest/advanced_tutorials/visualization.html)。这里我们介绍 3D 绘制界面。
### 在图像上绘制点云
通过使用 `draw_points_on_image`,我们支持在图像上绘制点云。
```python
import mmcv
import numpy as np
from mmengine import load
from mmdet3d.visualization import Det3DLocalVisualizer
info_file = load('demo/data/kitti/000008.pkl')
points = np.fromfile('demo/data/kitti/000008.bin', dtype=np.float32)
points = points.reshape(-1, 4)[:, :3]
lidar2img = np.array(info_file['data_list'][0]['images']['CAM2']['lidar2img'], dtype=np.float32)
visualizer = Det3DLocalVisualizer()
img = mmcv.imread('demo/data/kitti/000008.png')
img = mmcv.imconvert(img, 'bgr', 'rgb')
visualizer.set_image(img)
visualizer.draw_points_on_image(points, lidar2img)
visualizer.show()
```
![points_on_image](../../../resources/points_on_image.png)
### 在点云上绘制 3D 框
通过使用 `draw_bboxes_3d`,我们支持在点云上绘制 3D 框。
```python
import torch
from mmdet3d.visualization import Det3DLocalVisualizer
from mmdet3d.structures import LiDARInstance3DBoxes
points = np.fromfile('tests/data/kitti/training/velodyne/000000.bin', dtype=np.float32)
points = points.reshape(-1, 4)
visualizer = Det3DLocalVisualizer()
# set point cloud in visualizer
visualizer.set_points(points)
bboxes_3d = LiDARInstance3DBoxes(torch.tensor(
[[8.7314, -1.8559, -1.5997, 1.2000, 0.4800, 1.8900,
-1.5808]])),
# Draw 3D bboxes
visualizer.draw_bboxes_3d(bboxes_3d)
visualizer.show()
```
![mono3d](../../../resources/pcd.png)
### 在图像上绘制投影的 3D 框
通过使用 `draw_proj_bboxes_3d`,我们支持在图像上绘制投影的 3D 框。
```python
import mmcv
import numpy as np
from mmengine import load
from mmdet3d.visualization import Det3DLocalVisualizer
from mmdet3d.structures import CameraInstance3DBoxes
info_file = load('demo/data/kitti/000008.pkl')
cam2img = np.array(info_file['data_list'][0]['images']['CAM2']['cam2img'], dtype=np.float32)
bboxes_3d = []
for instance in info_file['data_list'][0]['instances']:
bboxes_3d.append(instance['bbox_3d'])
gt_bboxes_3d = np.array(bboxes_3d, dtype=np.float32)
gt_bboxes_3d = CameraInstance3DBoxes(gt_bboxes_3d)
input_meta = {'cam2img': cam2img}
visualizer = Det3DLocalVisualizer()
img = mmcv.imread('demo/data/kitti/000008.png')
img = mmcv.imconvert(img, 'bgr', 'rgb')
visualizer.set_image(img)
# project 3D bboxes to image
visualizer.draw_proj_bboxes_3d(gt_bboxes_3d, input_meta)
visualizer.show()
```
![mono3d](../../../resources/mono3d.png)
### 绘制 BEV 视角的框
通过使用 `draw_bev_bboxes`,我们支持绘制 BEV 视角下的框。
```python
import numpy as np
from mmengine import load
from mmdet3d.visualization import Det3DLocalVisualizer
from mmdet3d.structures import CameraInstance3DBoxes
info_file = load('demo/data/kitti/000008.pkl')
bboxes_3d = []
for instance in info_file['data_list'][0]['instances']:
bboxes_3d.append(instance['bbox_3d'])
gt_bboxes_3d = np.array(bboxes_3d, dtype=np.float32)
gt_bboxes_3d = CameraInstance3DBoxes(gt_bboxes_3d)
visualizer = Det3DLocalVisualizer()
# set bev image in visualizer
visualizer.set_bev_image()
# draw bev bboxes
visualizer.draw_bev_bboxes(gt_bboxes_3d, edge_colors='orange')
visualizer.show()
```
<img src="../../../resources/bev.png" width = "50%" />
### 绘制 3D 分割掩码
通过使用 `draw_seg_mask`,我们支持通过逐点着色来绘制分割掩码。
```python
import torch
from mmdet3d.visualization import Det3DLocalVisualizer
points = np.fromfile('tests/data/s3dis/points/Area_1_office_2.bin', dtype=np.float32)
points = points.reshape(-1, 3)
visualizer = Det3DLocalVisualizer()
mask = np.random.rand(points.shape[0], 3)
points_with_mask = np.concatenate((points, mask), axis=-1)
# Draw 3D points with mask
visualizer.draw_seg_mask(points_with_mask)
visualizer.show()
```
## 结果
如果想要可视化训练模型的预测结果,你可以运行如下指令:
```bash
python tools/test.py ${CONFIG_FILE} ${CKPT_PATH} --show --show-dir ${SHOW_DIR}
```
运行该指令后,绘制的结果(包括输入数据和网络输出在输入上的可视化)将会被保存在 `${SHOW_DIR}` 中。
运行该指令后,你将在 `${SHOW_DIR}` 中获得输入数据,网络输出和真是标签在输入上的可视化(如在多模态检测任务和基于视觉的检测任务中的 `***_gt.png``***_pred.png`)。当启用 `show` 时,[Open3D](http://www.open3d.org/) 将会用于在线可视化结果。如果你是在没有 GUI 的远程服务器上测试时,在线可视化是不被支持的。你可以从远程服务器中下载 `results.pkl`,并在本地机器上离线可视化预测结果。
使用 `Open3D` 后端离线可视化结果,你可以运行如下指令:
```bash
python tools/misc/visualize_results.py ${CONFIG_FILE} --result ${RESULTS_PATH} --show-dir ${SHOW_DIR}
```
![](../../../resources/open3d_visual.gif)
这需要在远程服务器中能够推理并生成结果,然后用户在主机中使用 GUI 打开。
## 数据集
我们也提供了脚本来可视化数据集而无需推理。你可以使用 `tools/misc/browse_dataset.py` 来在线可视化加载的数据的真实标签,并保存在硬盘中。目前我们支持所有数据集的单模态 3D 检测和 3D 分割,KITTI 和 SUN RGB-D 的多模态 3D 检测,以及 nuScenes 的单目 3D 检测。如果想要浏览 KITTI 数据集,你可以运行如下指令:
```shell
python tools/misc/browse_dataset.py configs/_base_/datasets/kitti-3d-3class.py --task lidar_det --output-dir ${OUTPUT_DIR}
```
**注意**:一旦指定了 `--output-dir`,当在 open3d 窗口中按下 `_ESC_` 时,用户指定的视图图像将会被保存下来。
为了验证数据的一致性和数据增强的效果,你可以加上 `--aug` 来可视化数据增强后的数据,指令如下所示:
```shell
python tools/misc/browse_dataset.py configs/_base_/datasets/kitti-3d-3class.py --task det --aug --output-dir ${OUTPUT_DIR}
```
如果你想显示带有投影的 3D 边界框的 2D 图像,你需要一个支持多模态数据加载的配置文件,并将 `--task` 参数改为 `multi-modality_det`。示例如下:
```shell
python tools/misc/browse_dataset.py configs/mvxnet/dv_mvx-fpn_second_secfpn_adamw_2x8_80e_kitti-3d-3class.py --task multi-modality_det --output-dir ${OUTPUT_DIR}
```
![](../../../resources/browse_dataset_multi_modality.png)
你可以使用不同的配置浏览不同的数据集,例如在 3D 语义分割任务中可视化 ScanNet 数据集:
```shell
python tools/misc/browse_dataset.py configs/_base_/datasets/scannet_seg-3d-20class.py --task lidar_seg --output-dir ${OUTPUT_DIR} --online
```
![](../../../resources/browse_dataset_seg.png)
在单目 3D 检测任务中浏览 nuScenes 数据集:
```shell
python tools/misc/browse_dataset.py configs/_base_/datasets/nus-mono3d.py --task mono_det --output-dir ${OUTPUT_DIR} --online
```
![](../../../resources/browse_dataset_mono.png)
# Copyright (c) OpenMMLab. All rights reserved. # Copyright (c) OpenMMLab. All rights reserved.
import mmcv import mmcv
import mmdet import mmdet
from .version import __version__, short_version import mmengine
from mmengine.utils import digit_version
def digit_version(version_str):
digit_version = []
for x in version_str.split('.'):
if x.isdigit():
digit_version.append(int(x))
elif x.find('rc') != -1:
patch_version = x.split('rc')
digit_version.append(int(patch_version[0]) - 1)
digit_version.append(int(patch_version[1]))
return digit_version
from .version import __version__, version_info
mmcv_minimum_version = '2.0.0rc0' mmcv_minimum_version = '2.0.0rc0'
mmcv_maximum_version = '2.1.0' mmcv_maximum_version = '2.1.0'
mmcv_version = digit_version(mmcv.__version__) mmcv_version = digit_version(mmcv.__version__)
mmengine_minimum_version = '0.1.0'
mmengine_maximum_version = '1.0.0'
mmengine_version = digit_version(mmengine.__version__)
mmdet_minimum_version = '3.0.0rc0'
mmdet_maximum_version = '3.1.0'
mmdet_version = digit_version(mmdet.__version__)
assert (mmcv_version >= digit_version(mmcv_minimum_version) assert (mmcv_version >= digit_version(mmcv_minimum_version)
and mmcv_version < digit_version(mmcv_maximum_version)), \ and mmcv_version < digit_version(mmcv_maximum_version)), \
f'MMCV=={mmcv.__version__} is used but incompatible. ' \ f'MMCV=={mmcv.__version__} is used but incompatible. ' \
f'Please install mmcv>={mmcv_minimum_version}, <={mmcv_maximum_version}.' f'Please install mmcv>={mmcv_minimum_version}, <{mmcv_maximum_version}.'
assert (mmengine_version >= digit_version(mmengine_minimum_version)
and mmengine_version < digit_version(mmengine_maximum_version)), \
f'MMEngine=={mmengine.__version__} is used but incompatible. ' \
f'Please install mmengine>={mmengine_minimum_version}, ' \
f'<{mmengine_maximum_version}.'
mmdet_minimum_version = '3.0.0rc0'
mmdet_maximum_version = '3.1.0'
mmdet_version = digit_version(mmdet.__version__)
assert (mmdet_version >= digit_version(mmdet_minimum_version) assert (mmdet_version >= digit_version(mmdet_minimum_version)
and mmdet_version < digit_version(mmdet_maximum_version)), \ and mmdet_version < digit_version(mmdet_maximum_version)), \
f'MMDET=={mmdet.__version__} is used but incompatible. ' \ f'MMDET=={mmdet.__version__} is used but incompatible. ' \
f'Please install mmdet>={mmdet_minimum_version}, ' \ f'Please install mmdet>={mmdet_minimum_version}, ' \
f'<={mmdet_maximum_version}.' f'<{mmdet_maximum_version}.'
__all__ = ['__version__', 'short_version'] __all__ = ['__version__', 'version_info', 'digit_version']
...@@ -67,7 +67,6 @@ def init_model(config: Union[str, Path, Config], ...@@ -67,7 +67,6 @@ def init_model(config: Union[str, Path, Config],
if checkpoint is not None: if checkpoint is not None:
checkpoint = load_checkpoint(model, checkpoint, map_location='cpu') checkpoint = load_checkpoint(model, checkpoint, map_location='cpu')
dataset_meta = checkpoint['meta'].get('dataset_meta', None) dataset_meta = checkpoint['meta'].get('dataset_meta', None)
# save the dataset_meta in the model for convenience # save the dataset_meta in the model for convenience
if 'dataset_meta' in checkpoint.get('meta', {}): if 'dataset_meta' in checkpoint.get('meta', {}):
......
# Copyright (c) OpenMMLab. All rights reserved. # Copyright (c) OpenMMLab. All rights reserved.
import platform import platform
from mmdet3d.registry import DATASETS, TRANSFORMS
from mmdet.datasets.builder import _concat_dataset from mmdet.datasets.builder import _concat_dataset
from mmdet3d.registry import DATASETS, TRANSFORMS
if platform.system() != 'Windows': if platform.system() != 'Windows':
# https://github.com/pytorch/pytorch/issues/973 # https://github.com/pytorch/pytorch/issues/973
import resource import resource
......
# Copyright (c) OpenMMLab. All rights reserved. # Copyright (c) OpenMMLab. All rights reserved.
import copy import copy
from collections import OrderedDict
from typing import List, Optional, Tuple, Union from typing import List, Optional, Tuple, Union
import numpy as np import numpy as np
from nuscenes import NuScenes
from nuscenes.utils.geometry_utils import view_points from nuscenes.utils.geometry_utils import view_points
from pyquaternion import Quaternion from pyquaternion import Quaternion
from shapely.geometry import MultiPoint, box from shapely.geometry import MultiPoint, box
...@@ -53,19 +53,20 @@ LyftNameMapping = { ...@@ -53,19 +53,20 @@ LyftNameMapping = {
} }
def get_nuscenes_2d_boxes(nusc, sample_data_token: str, def get_nuscenes_2d_boxes(nusc: NuScenes, sample_data_token: str,
visibilities: List[str]): visibilities: List[str]) -> List[dict]:
"""Get the 2d / mono3d annotation records for a given `sample_data_token of """Get the 2d / mono3d annotation records for a given `sample_data_token`
nuscenes dataset. of nuscenes dataset.
Args: Args:
nusc (:obj:`NuScenes`): NuScenes class.
sample_data_token (str): Sample data token belonging to a camera sample_data_token (str): Sample data token belonging to a camera
keyframe. keyframe.
visibilities (list[str]): Visibility filter. visibilities (List[str]): Visibility filter.
Return: Return:
list[dict]: List of 2d annotation record that belongs to the input List[dict]: List of 2d annotation record that belongs to the input
`sample_data_token`. `sample_data_token`.
""" """
# Get the sample data and the sample corresponding to that sample data. # Get the sample data and the sample corresponding to that sample data.
...@@ -190,7 +191,7 @@ def get_kitti_style_2d_boxes(info: dict, ...@@ -190,7 +191,7 @@ def get_kitti_style_2d_boxes(info: dict,
occluded: Tuple[int] = (0, 1, 2, 3), occluded: Tuple[int] = (0, 1, 2, 3),
annos: Optional[dict] = None, annos: Optional[dict] = None,
mono3d: bool = True, mono3d: bool = True,
dataset: str = 'kitti'): dataset: str = 'kitti') -> List[dict]:
"""Get the 2d / mono3d annotation records for a given info. """Get the 2d / mono3d annotation records for a given info.
This function is used to get 2D/Mono3D annotations when loading annotations This function is used to get 2D/Mono3D annotations when loading annotations
...@@ -202,19 +203,19 @@ def get_kitti_style_2d_boxes(info: dict, ...@@ -202,19 +203,19 @@ def get_kitti_style_2d_boxes(info: dict,
belong to. In KITTI, typically only CAM 2 will be used, belong to. In KITTI, typically only CAM 2 will be used,
and in Waymo, multi cameras could be used. and in Waymo, multi cameras could be used.
Defaults to 2. Defaults to 2.
occluded (tuple[int]): Integer (0, 1, 2, 3) indicating occlusion state: occluded (Tuple[int]): Integer (0, 1, 2, 3) indicating occlusion state:
0 = fully visible, 1 = partly occluded, 2 = largely occluded, 0 = fully visible, 1 = partly occluded, 2 = largely occluded,
3 = unknown, -1 = DontCare. 3 = unknown, -1 = DontCare.
Defaults to (0, 1, 2, 3). Defaults to (0, 1, 2, 3).
annos (dict, optional): Original annotations. annos (dict, optional): Original annotations. Defaults to None.
mono3d (bool): Whether to get boxes with mono3d annotation. mono3d (bool): Whether to get boxes with mono3d annotation.
Defaults to True. Defaults to True.
dataset (str): Dataset name of getting 2d bboxes. dataset (str): Dataset name of getting 2d bboxes.
Defaults to `kitti`. Defaults to 'kitti'.
Return: Return:
list[dict]: List of 2d / mono3d annotation record that List[dict]: List of 2d / mono3d annotation record that
belongs to the input camera id. belongs to the input camera id.
""" """
# Get calibration information # Get calibration information
camera_intrinsic = info['calib'][f'P{cam_idx}'] camera_intrinsic = info['calib'][f'P{cam_idx}']
...@@ -296,7 +297,7 @@ def get_kitti_style_2d_boxes(info: dict, ...@@ -296,7 +297,7 @@ def get_kitti_style_2d_boxes(info: dict,
repro_rec['velocity'] = -1 # no velocity in KITTI repro_rec['velocity'] = -1 # no velocity in KITTI
center_3d = np.array(loc).reshape([1, 3]) center_3d = np.array(loc).reshape([1, 3])
center_2d_with_depth = box_np_ops.points_cam2img( center_2d_with_depth = points_cam2img(
center_3d, camera_intrinsic, with_depth=True) center_3d, camera_intrinsic, with_depth=True)
center_2d_with_depth = center_2d_with_depth.squeeze().tolist() center_2d_with_depth = center_2d_with_depth.squeeze().tolist()
...@@ -336,19 +337,20 @@ def convert_annos(info: dict, cam_idx: int) -> dict: ...@@ -336,19 +337,20 @@ def convert_annos(info: dict, cam_idx: int) -> dict:
def post_process_coords( def post_process_coords(
corner_coords: List, imsize: Tuple[int, int] = (1600, 900) corner_coords: List[int], imsize: Tuple[int] = (1600, 900)
) -> Union[Tuple[float, float, float, float], None]: ) -> Union[Tuple[float], None]:
"""Get the intersection of the convex hull of the reprojected bbox corners """Get the intersection of the convex hull of the reprojected bbox corners
and the image canvas, return None if no intersection. and the image canvas, return None if no intersection.
Args: Args:
corner_coords (list[int]): Corner coordinates of reprojected corner_coords (List[int]): Corner coordinates of reprojected
bounding box. bounding box.
imsize (tuple[int]): Size of the image canvas. imsize (Tuple[int]): Size of the image canvas.
Defaults to (1600, 900).
Return: Return:
tuple [float]: Intersection of the convex hull of the 2D box Tuple[float] or None: Intersection of the convex hull of the 2D box
corners and the image canvas. corners and the image canvas.
""" """
polygon_from_2d_box = MultiPoint(corner_coords).convex_hull polygon_from_2d_box = MultiPoint(corner_coords).convex_hull
img_canvas = box(0, 0, imsize[0], imsize[1]) img_canvas = box(0, 0, imsize[0], imsize[1])
...@@ -369,7 +371,7 @@ def post_process_coords( ...@@ -369,7 +371,7 @@ def post_process_coords(
def generate_record(ann_rec: dict, x1: float, y1: float, x2: float, y2: float, def generate_record(ann_rec: dict, x1: float, y1: float, x2: float, y2: float,
dataset: str) -> OrderedDict: dataset: str) -> Union[dict, None]:
"""Generate one 2D annotation record given various information on top of """Generate one 2D annotation record given various information on top of
the 2D bounding box coordinates. the 2D bounding box coordinates.
...@@ -382,12 +384,12 @@ def generate_record(ann_rec: dict, x1: float, y1: float, x2: float, y2: float, ...@@ -382,12 +384,12 @@ def generate_record(ann_rec: dict, x1: float, y1: float, x2: float, y2: float,
dataset (str): Name of dataset. dataset (str): Name of dataset.
Returns: Returns:
dict: A sample 2d annotation record. dict or None: A sample 2d annotation record.
- bbox_label (int): 2d box label id
- bbox_label_3d (int): 3d box label id - bbox_label (int): 2d box label id
- bbox (list[float]): left x, top y, right x, bottom y - bbox_label_3d (int): 3d box label id
of 2d box - bbox (List[float]): left x, top y, right x, bottom y of 2d box
- bbox_3d_isvalid (bool): whether the box is valid - bbox_3d_isvalid (bool): whether the box is valid
""" """
if dataset == 'nuscenes': if dataset == 'nuscenes':
...@@ -398,10 +400,6 @@ def generate_record(ann_rec: dict, x1: float, y1: float, x2: float, y2: float, ...@@ -398,10 +400,6 @@ def generate_record(ann_rec: dict, x1: float, y1: float, x2: float, y2: float,
cat_name = NuScenesNameMapping[cat_name] cat_name = NuScenesNameMapping[cat_name]
categories = nus_categories categories = nus_categories
else: else:
cat_name = ann_rec['name']
if cat_name not in categories:
return None
if dataset == 'kitti': if dataset == 'kitti':
categories = kitti_categories categories = kitti_categories
elif dataset == 'waymo': elif dataset == 'waymo':
...@@ -409,6 +407,10 @@ def generate_record(ann_rec: dict, x1: float, y1: float, x2: float, y2: float, ...@@ -409,6 +407,10 @@ def generate_record(ann_rec: dict, x1: float, y1: float, x2: float, y2: float,
else: else:
raise NotImplementedError('Unsupported dataset!') raise NotImplementedError('Unsupported dataset!')
cat_name = ann_rec['name']
if cat_name not in categories:
return None
rec = dict() rec = dict()
rec['bbox_label'] = categories.index(cat_name) rec['bbox_label'] = categories.index(cat_name)
rec['bbox_label_3d'] = rec['bbox_label'] rec['bbox_label_3d'] = rec['bbox_label']
......
...@@ -19,8 +19,8 @@ class CBGSDataset(object): ...@@ -19,8 +19,8 @@ class CBGSDataset(object):
def __init__(self, dataset): def __init__(self, dataset):
self.dataset = DATASETS.build(dataset) self.dataset = DATASETS.build(dataset)
self.metainfo = self.dataset.metainfo self.metainfo = self.dataset.metainfo
self.CLASSES = self.metainfo['CLASSES'] self.classes = self.metainfo['classes']
self.cat2id = {name: i for i, name in enumerate(self.CLASSES)} self.cat2id = {name: i for i, name in enumerate(self.classes)}
self.sample_indices = self._get_sample_indices() self.sample_indices = self._get_sample_indices()
# self.dataset.data_infos = self.data_infos # self.dataset.data_infos = self.data_infos
if hasattr(self.dataset, 'flag'): if hasattr(self.dataset, 'flag'):
...@@ -54,7 +54,7 @@ class CBGSDataset(object): ...@@ -54,7 +54,7 @@ class CBGSDataset(object):
sample_indices = [] sample_indices = []
frac = 1.0 / len(self.CLASSES) frac = 1.0 / len(self.classes)
ratios = [frac / v for v in class_distribution.values()] ratios = [frac / v for v in class_distribution.values()]
for cls_inds, ratio in zip(list(class_sample_idxs.values()), ratios): for cls_inds, ratio in zip(list(class_sample_idxs.values()), ratios):
sample_indices += np.random.choice(cls_inds, sample_indices += np.random.choice(cls_inds,
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment