"docs/vscode:/vscode.git/clone" did not exist on "98cc6d05e478533b09620192ab44fb752f563c2f"
Commit e585f0d5 authored by Xiangxu-0103's avatar Xiangxu-0103 Committed by ZwwWayne
Browse files

[Docs] Update Chinese documentation (#1891)

parent 0be27ffb
.. toctree:: .. toctree::
:maxdepth: 3 :maxdepth: 3
1_exist_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 visualization.md
...@@ -2,7 +2,7 @@ ...@@ -2,7 +2,7 @@
## Introduction ## Introduction
We provide scripts for multi-modality/single-modality (LiDAR-based/vision-based), indoor/outdoor 3D detection and 3D semantic segmentation demos. The pre-trained models can be downloaded from [model zoo](https://github.com/open-mmlab/mmdetection3d/blob/master/docs/en/model_zoo.md/). We provide pre-processed sample data from KITTI, SUN RGB-D, nuScenes and ScanNet dataset. You can use any other data following our pre-processing steps. We provide scripts for multi-modality/single-modality (LiDAR-based/vision-based), indoor/outdoor 3D detection and 3D semantic segmentation demos. The pre-trained models can be downloaded from [model zoo](https://github.com/open-mmlab/mmdetection3d/blob/dev-1.x/docs/en/model_zoo.md). We provide pre-processed sample data from KITTI, SUN RGB-D, nuScenes and ScanNet dataset. You can use any other data following our pre-processing steps.
## Testing ## Testing
......
...@@ -44,7 +44,7 @@ The arguments: ...@@ -44,7 +44,7 @@ The arguments:
- `type`: The name of the corresponding metric, usually associated with the dataset. - `type`: The name of the corresponding metric, usually associated with the dataset.
- `ann_file`: The path of annotation file. - `ann_file`: The path of annotation file.
- `pklfile_prefix`: An optional argument. The filename of the output results in pickle format. If not specified, the results will not be saved to a file. - `pklfile_prefix`: An optional argument. The filename of the output results in pickle format. If not specified, the results will not be saved to a file.
- `submission_prefix`: An optional argument. The the results will not be saved to a file then you can upload it to do the official evaluation. - `submission_prefix`: An optional argument. The results will be saved to a file then you can upload it to do the official evaluation.
Examples: Examples:
...@@ -70,7 +70,7 @@ Assume that you have already downloaded the checkpoints to the directory `checkp ...@@ -70,7 +70,7 @@ Assume that you have already downloaded the checkpoints to the directory `checkp
```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
``` ```
4. Test SECOND on KITTI with 8 GPUs, and evaluate the mAP. 4. Test SECOND on KITTI with 8 GPUs, and evaluate the mAP.
...@@ -243,17 +243,17 @@ If you use launch training jobs with Slurm, there are two ways to specify the po ...@@ -243,17 +243,17 @@ If you use launch training jobs with Slurm, there are two ways to specify the po
In `config1.py`, In `config1.py`,
```python ```python
env_cfg = dict( env_cfg = dict(
dist_cfg=dict(backend='nccl', port=29500) dist_cfg=dict(backend='nccl', port=29500)
) )
``` ```
In `config2.py`, In `config2.py`,
```python ```python
env_cfg = dict( env_cfg = dict(
dist_cfg=dict(backend='nccl', port=29500) dist_cfg=dict(backend='nccl', port=29501)
) )
``` ```
Then you can launch two jobs with `config1.py` and `config2.py`. Then you can launch two jobs with `config1.py` and `config2.py`.
......
...@@ -6,7 +6,7 @@ We provide lots of useful tools under `tools/` directory. ...@@ -6,7 +6,7 @@ We provide lots of useful tools under `tools/` directory.
You can plot loss/mAP curves given a training log file. Run `pip install seaborn` first to install the dependency. You can plot loss/mAP curves given a training log file. Run `pip install seaborn` first to install the dependency.
![loss curve image](../../resources/loss_curve.png) ![loss curve image](../../../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}]
......
...@@ -16,9 +16,11 @@ We support drawing point cloud on the image by using `draw_points_on_image`. ...@@ -16,9 +16,11 @@ We support drawing point cloud on the image by using `draw_points_on_image`.
```python ```python
import mmcv import mmcv
import numpy as np
from mmengine import load from mmengine import load
from mmdet3d.visualization import Det3DLocalVisualizer from mmdet3d.visualization import Det3DLocalVisualizer
import numpy as np
info_file = load('demo/data/kitti/000008.pkl') info_file = load('demo/data/kitti/000008.pkl')
points = np.fromfile('demo/data/kitti/000008.bin', dtype=np.float32) points = np.fromfile('demo/data/kitti/000008.bin', dtype=np.float32)
points = points.reshape(-1, 4)[:, :3] points = points.reshape(-1, 4)[:, :3]
...@@ -40,6 +42,7 @@ We support drawing 3D boxes on point cloud by using `draw_bboxes_3d`. ...@@ -40,6 +42,7 @@ We support drawing 3D boxes on point cloud by using `draw_bboxes_3d`.
```python ```python
import torch import torch
from mmdet3d.visualization import Det3DLocalVisualizer from mmdet3d.visualization import Det3DLocalVisualizer
from mmdet3d.structures import LiDARInstance3DBoxes from mmdet3d.structures import LiDARInstance3DBoxes
...@@ -66,8 +69,10 @@ We support drawing projected 3D boxes on image by using `draw_proj_bboxes_3d`. ...@@ -66,8 +69,10 @@ We support drawing projected 3D boxes on image by using `draw_proj_bboxes_3d`.
import mmcv import mmcv
import numpy as np import numpy as np
from mmengine import load from mmengine import load
from mmdet3d.visualization import Det3DLocalVisualizer from mmdet3d.visualization import Det3DLocalVisualizer
from mmdet3d.structures import CameraInstance3DBoxes from mmdet3d.structures import CameraInstance3DBoxes
info_file = load('demo/data/kitti/000008.pkl') info_file = load('demo/data/kitti/000008.pkl')
cam2img = np.array(info_file['data_list'][0]['images']['CAM2']['cam2img'], dtype=np.float32) cam2img = np.array(info_file['data_list'][0]['images']['CAM2']['cam2img'], dtype=np.float32)
bboxes_3d = [] bboxes_3d = []
...@@ -91,11 +96,12 @@ visualizer.show() ...@@ -91,11 +96,12 @@ visualizer.show()
### Drawing BEV Boxes ### Drawing BEV Boxes
We support drawing projected 3D boxes on image by using `draw_proj_bboxes_3d`. We support drawing BEV boxes by using `draw_bev_bboxes`.
```python ```python
import numpy as np import numpy as np
from mmengine import load from mmengine import load
from mmdet3d.visualization import Det3DLocalVisualizer from mmdet3d.visualization import Det3DLocalVisualizer
from mmdet3d.structures import CameraInstance3DBoxes from mmdet3d.structures import CameraInstance3DBoxes
...@@ -122,6 +128,7 @@ We support draw segmentation mask via per-point colorization by using `draw_seg_ ...@@ -122,6 +128,7 @@ We support draw segmentation mask via per-point colorization by using `draw_seg_
```python ```python
import torch import torch
from mmdet3d.visualization import Det3DLocalVisualizer from mmdet3d.visualization import Det3DLocalVisualizer
points = np.fromfile('tests/data/s3dis/points/Area_1_office_2.bin', dtype=np.float32) points = np.fromfile('tests/data/s3dis/points/Area_1_office_2.bin', dtype=np.float32)
...@@ -136,7 +143,7 @@ visualizer.show() ...@@ -136,7 +143,7 @@ visualizer.show()
## Results ## Results
To see the prediction results of trained models, you can run the following command To see the prediction results of trained models, you can run the following command:
```bash ```bash
python tools/test.py ${CONFIG_FILE} ${CKPT_PATH} --show --show-dir ${SHOW_DIR} python tools/test.py ${CONFIG_FILE} ${CKPT_PATH} --show --show-dir ${SHOW_DIR}
...@@ -144,9 +151,9 @@ python tools/test.py ${CONFIG_FILE} ${CKPT_PATH} --show --show-dir ${SHOW_DIR} ...@@ -144,9 +151,9 @@ python tools/test.py ${CONFIG_FILE} ${CKPT_PATH} --show --show-dir ${SHOW_DIR}
After running this command, plotted results including input data and the output of networks visualized on the input will be saved in `${SHOW_DIR}`. After running this command, plotted results including input data and the output of networks visualized on the input will be saved in `${SHOW_DIR}`.
After running this command, you will obtain the input data, the output of networks and ground-truth labels visualized on the input (e.g. `***_gt.png` and `***_pred.png` in multi-modality detection task and vision-based detection task) in `${SHOW_DIR}`. When `show` is enabled, [Open3D](http://www.open3d.org/) will be used to visualize the results online. If you are running test in remote server without GUI, the online visualization is not supported, you can download the `results.pkl` from the remote server, and visualize the prediction results offline in your local machine. After running this command, you will obtain the input data, the output of networks and ground-truth labels visualized on the input (e.g. `***_gt.png` and `***_pred.png` in multi-modality detection task and vision-based detection task) in `${SHOW_DIR}`. When `show` is enabled, [Open3D](http://www.open3d.org/) will be used to visualize the results online. If you are running test in remote server without GUI, the online visualization is not supported. You can download the `results.pkl` from the remote server, and visualize the prediction results offline in your local machine.
To visualize the results with `Open3D` backend offline, you can run the following command To visualize the results with `Open3D` backend offline, you can run the following command:
```bash ```bash
python tools/misc/visualize_results.py ${CONFIG_FILE} --result ${RESULTS_PATH} --show-dir ${SHOW_DIR} python tools/misc/visualize_results.py ${CONFIG_FILE} --result ${RESULTS_PATH} --show-dir ${SHOW_DIR}
...@@ -158,7 +165,7 @@ This allows the inference and results generation to be done in remote server and ...@@ -158,7 +165,7 @@ This allows the inference and results generation to be done in remote server and
## Dataset ## Dataset
We also provide scripts to visualize the dataset without inference. You can use `tools/misc/browse_dataset.py` to show loaded data and ground-truth online and save them on the disk. Currently we support single-modality 3D detection and 3D segmentation on all the datasets, multi-modality 3D detection on KITTI and SUN RGB-D, as well as monocular 3D detection on nuScenes. To browse the KITTI dataset, you can run the following command We also provide scripts to visualize the dataset without inference. You can use `tools/misc/browse_dataset.py` to show loaded data and ground-truth online and save them on the disk. Currently we support single-modality 3D detection and 3D segmentation on all the datasets, multi-modality 3D detection on KITTI and SUN RGB-D, as well as monocular 3D detection on nuScenes. To browse the KITTI dataset, you can run the following command:
```shell ```shell
python tools/misc/browse_dataset.py configs/_base_/datasets/kitti-3d-3class.py --task det --output-dir ${OUTPUT_DIR} python tools/misc/browse_dataset.py configs/_base_/datasets/kitti-3d-3class.py --task det --output-dir ${OUTPUT_DIR}
...@@ -169,10 +176,10 @@ python tools/misc/browse_dataset.py configs/_base_/datasets/kitti-3d-3class.py - ...@@ -169,10 +176,10 @@ python tools/misc/browse_dataset.py configs/_base_/datasets/kitti-3d-3class.py -
To verify the data consistency and the effect of data augmentation, you can also add `--aug` flag to visualize the data after data augmentation using the command as below: To verify the data consistency and the effect of data augmentation, you can also add `--aug` flag to visualize the data after data augmentation using the command as below:
```shell ```shell
python tools/misc/browse_dataset.py configs/_base_/datasets/kitti-3d-3class.py --task det --aug --output-dir ${OUTPUT_DIR} python tools/misc/browse_dataset.py configs/_base_/datasets/kitti-3d-3class.py --task lidar_det --aug --output-dir ${OUTPUT_DIR}
``` ```
If you also want to show 2D images with 3D bounding boxes projected onto them, you need to find a config that supports multi-modality data loading, and then change the `--task` args to `multi_modality-det`. An example is showed below If you also want to show 2D images with 3D bounding boxes projected onto them, you need to find a config that supports multi-modality data loading, and then change the `--task` args to `multi-modality_det`. An example is showed below:
```shell ```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} 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}
...@@ -180,15 +187,15 @@ python tools/misc/browse_dataset.py configs/mvxnet/dv_mvx-fpn_second_secfpn_adam ...@@ -180,15 +187,15 @@ python tools/misc/browse_dataset.py configs/mvxnet/dv_mvx-fpn_second_secfpn_adam
![](../../../resources/browse_dataset_multi_modality.png) ![](../../../resources/browse_dataset_multi_modality.png)
You can simply browse different datasets using different configs, e.g. visualizing the ScanNet dataset in 3D semantic segmentation task You can simply browse different datasets using different configs, e.g. visualizing the ScanNet dataset in 3D semantic segmentation task:
```shell ```shell
python tools/misc/browse_dataset.py configs/_base_/datasets/scannet_seg-3d-20class.py --task seg --output-dir ${OUTPUT_DIR} --online 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) ![](../../../resources/browse_dataset_seg.png)
And browsing the nuScenes dataset in monocular 3D detection task And browsing the nuScenes dataset in monocular 3D detection task:
```shell ```shell
python tools/misc/browse_dataset.py configs/_base_/datasets/nus-mono3d.py --task mono_det --output-dir ${OUTPUT_DIR} --online python tools/misc/browse_dataset.py configs/_base_/datasets/nus-mono3d.py --task mono_det --output-dir ${OUTPUT_DIR} --online
......
# 教程 4: 自定义模型 # 自定义模型
我们通常把模型的各个组成成分分成6种类型: 我们通常把模型的各个组成成分分成 6 种类型:
- 编码器(encoder):包括 voxel layer、voxel encoder 和 middle encoder 等进入 backbone 前所使用的基于 voxel 的方法,如 HardVFE 和 PointPillarsScatter。 - 编码器(encoder):包括 voxel layer、voxel encoder 和 middle encoder 等进入 backbone 前所使用的基于 voxel 的方法,如 HardVFE 和 PointPillarsScatter。
- 骨干网络(backbone):通常采用 FCN 网络来提取特征图,如 ResNet 和 SECOND。 - 骨干网络(backbone):通常采用 FCN 网络来提取特征图,如 ResNet 和 SECOND。
...@@ -22,10 +22,10 @@ ...@@ -22,10 +22,10 @@
```python ```python
import torch.nn as nn import torch.nn as nn
from ..builder import VOXEL_ENCODERS from mmdet3d.registry import MODELS
@VOXEL_ENCODERS.register_module() @MODELS.register_module()
class HardVFE(nn.Module): class HardVFE(nn.Module):
def __init__(self, arg1, arg2): def __init__(self, arg1, arg2):
...@@ -74,10 +74,10 @@ model = dict( ...@@ -74,10 +74,10 @@ model = dict(
```python ```python
import torch.nn as nn import torch.nn as nn
from ..builder import BACKBONES from mmdet3d.registry import MODELS
@BACKBONES.register_module() @MODELS.register_module()
class SECOND(BaseModule): class SECOND(BaseModule):
def __init__(self, arg1, arg2): def __init__(self, arg1, arg2):
...@@ -122,9 +122,9 @@ model = dict( ...@@ -122,9 +122,9 @@ model = dict(
创建一个新文件 `mmdet3d/models/necks/second_fpn.py` 创建一个新文件 `mmdet3d/models/necks/second_fpn.py`
```python ```python
from ..builder import NECKS from mmdet3d.registry import MODELS
@NECKS.register @MODELS.register_module()
class SECONDFPN(BaseModule): class SECONDFPN(BaseModule):
def __init__(self, def __init__(self,
...@@ -183,10 +183,10 @@ PartA2 RoI Head 实现一个新的 bbox head ,并用于目标检测的任务 ...@@ -183,10 +183,10 @@ PartA2 RoI Head 实现一个新的 bbox head ,并用于目标检测的任务
为了实现一个新的 bbox head,通常需要在其中实现三个功能,如下所示,有时该模块还需要实现其他相关的功能,如 `loss``get_targets` 为了实现一个新的 bbox head,通常需要在其中实现三个功能,如下所示,有时该模块还需要实现其他相关的功能,如 `loss``get_targets`
```python ```python
from mmdet.models.builder import HEADS from mmdet3d.registry import MODELS
from .bbox_head import BBoxHead from mmengine.model import BaseModule
@HEADS.register_module() @MODELS.register_module()
class PartA2BboxHead(BaseModule): class PartA2BboxHead(BaseModule):
"""PartA2 RoI head.""" """PartA2 RoI head."""
...@@ -218,71 +218,63 @@ class PartA2BboxHead(BaseModule): ...@@ -218,71 +218,63 @@ class PartA2BboxHead(BaseModule):
super(PartA2BboxHead, self).__init__(init_cfg=init_cfg) super(PartA2BboxHead, self).__init__(init_cfg=init_cfg)
def forward(self, seg_feats, part_feats): def forward(self, seg_feats, part_feats):
``` ```
其次,如果有必要的话,用户还需要实现一个新的 RoI Head,此处我们从 `Base3DRoIHead` 中继承得到一个新类 `PartAggregationROIHead`,此时我们就能发现 `Base3DRoIHead` 已经实现了下面的功能: 其次,如果有必要的话,用户还需要实现一个新的 RoI Head,此处我们从 `Base3DRoIHead` 中继承得到一个新类 `PartAggregationROIHead`,此时我们就能发现 `Base3DRoIHead` 已经实现了下面的功能:
```python ```python
from abc import ABCMeta, abstractmethod from mmdet3d.registry import MODELS, TASK_UTILS
from torch import nn as nn from mmdet.models.roi_heads import BaseRoIHead
@HEADS.register_module() class Base3DRoIHead(BaseRoIHead):
class Base3DRoIHead(BaseModule, metaclass=ABCMeta):
"""Base class for 3d RoIHeads.""" """Base class for 3d RoIHeads."""
def __init__(self, def __init__(self,
bbox_head=None, bbox_head=None,
mask_roi_extractor=None, bbox_roi_extractor=None,
mask_head=None, mask_head=None,
mask_roi_extractor=None,
train_cfg=None, train_cfg=None,
test_cfg=None, test_cfg=None,
init_cfg=None): init_cfg=None):
super(Base3DRoIHead, self).__init__(
bbox_head=bbox_head,
bbox_roi_extractor=bbox_roi_extractor,
mask_head=mask_head,
mask_roi_extractor=mask_roi_extractor,
train_cfg=train_cfg,
test_cfg=test_cfg,
init_cfg=init_cfg)
@property def init_bbox_head(self, bbox_roi_extractor: dict,
def with_bbox(self): bbox_head: dict) -> None:
"""Initialize box head and box roi extractor.
@property
def with_mask(self):
@abstractmethod
def init_weights(self, pretrained):
@abstractmethod
def init_bbox_head(self):
@abstractmethod Args:
def init_mask_head(self): bbox_roi_extractor (dict or ConfigDict): Config of box
roi extractor.
bbox_head (dict or ConfigDict): Config of box in box head.
"""
self.bbox_roi_extractor = MODELS.build(bbox_roi_extractor)
self.bbox_head = MODELS.build(bbox_head)
@abstractmethod
def init_assigner_sampler(self): def init_assigner_sampler(self):
"""Initialize assigner and sampler."""
self.bbox_assigner = None
self.bbox_sampler = None
if self.train_cfg:
if isinstance(self.train_cfg.assigner, dict):
self.bbox_assigner = TASK_UTILS.build(self.train_cfg.assigner)
elif isinstance(self.train_cfg.assigner, list):
self.bbox_assigner = [
TASK_UTILS.build(res) for res in self.train_cfg.assigner
]
self.bbox_sampler = TASK_UTILS.build(self.train_cfg.sampler)
@abstractmethod def init_mask_head(self):
def forward_train(self, """Initialize mask head, skip since ``PartAggregationROIHead`` does not
x, have one."""
img_metas,
proposal_list,
gt_bboxes,
gt_labels,
gt_bboxes_ignore=None,
**kwargs):
def simple_test(self,
x,
proposal_list,
img_metas,
proposals=None,
rescale=False,
**kwargs):
"""Test without augmentation."""
pass
def aug_test(self, x, proposal_list, img_metas, rescale=False, **kwargs):
"""Test with augmentations.
If rescale is False, then returned bboxes and masks will fit the scale
of imgs[0].
"""
pass pass
``` ```
...@@ -290,87 +282,194 @@ class Base3DRoIHead(BaseModule, metaclass=ABCMeta): ...@@ -290,87 +282,194 @@ class Base3DRoIHead(BaseModule, metaclass=ABCMeta):
接着将会对 bbox_forward 的逻辑进行修改,同时,bbox_forward 还会继承来自 `Base3DRoIHead` 的其他逻辑,在 `mmdet3d/models/roi_heads/part_aggregation_roi_head.py` 中,我们实现了新的 RoI Head,如下所示: 接着将会对 bbox_forward 的逻辑进行修改,同时,bbox_forward 还会继承来自 `Base3DRoIHead` 的其他逻辑,在 `mmdet3d/models/roi_heads/part_aggregation_roi_head.py` 中,我们实现了新的 RoI Head,如下所示:
```python ```python
from typing import Dict, List, Tuple
from mmcv import ConfigDict
from torch import Tensor
from torch.nn import functional as F from torch.nn import functional as F
from mmdet3d.core import AssignResult from mmdet3d.registry import MODELS
from mmdet3d.core.bbox import bbox3d2result, bbox3d2roi from mmdet3d.structures import bbox3d2roi
from mmdet.core import build_assigner, build_sampler from mmdet3d.utils import InstanceList
from mmdet.models import HEADS from mmdet.models.task_modules import AssignResult, SamplingResult
from ..builder import build_head, build_roi_extractor from ...structures.det3d_data_sample import SampleList
from .base_3droi_head import Base3DRoIHead from .base_3droi_head import Base3DRoIHead
@HEADS.register_module() @MODELS.register_module()
class PartAggregationROIHead(Base3DRoIHead): class PartAggregationROIHead(Base3DRoIHead):
"""Part aggregation roi head for PartA2. """Part aggregation roi head for PartA2.
Args: Args:
semantic_head (ConfigDict): Config of semantic head. semantic_head (ConfigDict): Config of semantic head.
num_classes (int): The number of classes. num_classes (int): The number of classes.
seg_roi_extractor (ConfigDict): Config of seg_roi_extractor. seg_roi_extractor (ConfigDict): Config of seg_roi_extractor.
part_roi_extractor (ConfigDict): Config of part_roi_extractor. bbox_roi_extractor (ConfigDict): Config of part_roi_extractor.
bbox_head (ConfigDict): Config of bbox_head. bbox_head (ConfigDict): Config of bbox_head.
train_cfg (ConfigDict): Training config. train_cfg (ConfigDict): Training config.
test_cfg (ConfigDict): Testing config. test_cfg (ConfigDict): Testing config.
""" """
def __init__(self, def __init__(self,
semantic_head, semantic_head: dict,
num_classes=3, num_classes: int = 3,
seg_roi_extractor=None, seg_roi_extractor: dict = None,
part_roi_extractor=None, bbox_head: dict = None,
bbox_head=None, bbox_roi_extractor: dict = None,
train_cfg=None, train_cfg: dict = None,
test_cfg=None, test_cfg: dict = None,
init_cfg=None): init_cfg: dict = None) -> None:
super(PartAggregationROIHead, self).__init__( super(PartAggregationROIHead, self).__init__(
bbox_head=bbox_head, bbox_head=bbox_head,
bbox_roi_extractor=bbox_roi_extractor,
train_cfg=train_cfg, train_cfg=train_cfg,
test_cfg=test_cfg, test_cfg=test_cfg,
init_cfg=init_cfg) init_cfg=init_cfg)
self.num_classes = num_classes self.num_classes = num_classes
assert semantic_head is not None assert semantic_head is not None
self.semantic_head = build_head(semantic_head) self.init_seg_head(seg_roi_extractor, semantic_head)
if seg_roi_extractor is not None: def init_seg_head(self, seg_roi_extractor: dict,
self.seg_roi_extractor = build_roi_extractor(seg_roi_extractor) semantic_head: dict) -> None:
if part_roi_extractor is not None: """Initialize semantic head and seg roi extractor.
self.part_roi_extractor = build_roi_extractor(part_roi_extractor)
self.init_assigner_sampler() Args:
seg_roi_extractor (dict): Config of seg
roi extractor.
semantic_head (dict): Config of semantic head.
"""
self.semantic_head = MODELS.build(semantic_head)
self.seg_roi_extractor = MODELS.build(seg_roi_extractor)
@property
def with_semantic(self):
"""bool: whether the head has semantic branch"""
return hasattr(self,
'semantic_head') and self.semantic_head is not None
def predict(self,
feats_dict: Dict,
rpn_results_list: InstanceList,
batch_data_samples: SampleList,
rescale: bool = False,
**kwargs) -> InstanceList:
"""Perform forward propagation of the roi head and predict detection
results on the features of the upstream network.
def _bbox_forward(self, seg_feats, part_feats, voxels_dict, rois):
"""Forward function of roi_extractor and bbox_head used in both
training and testing.
Args: Args:
seg_feats (torch.Tensor): Point-wise semantic features. feats_dict (dict): Contains features from the first stage.
part_feats (torch.Tensor): Point-wise part prediction features. rpn_results_list (List[:obj:`InstancesData`]): Detection results
voxels_dict (dict): Contains information of voxels. of rpn head.
rois (Tensor): Roi boxes. batch_data_samples (List[:obj:`Det3DDataSample`]): The Data
samples. It usually includes information such as
`gt_instance_3d`, `gt_panoptic_seg_3d` and `gt_sem_seg_3d`.
rescale (bool): If True, return boxes in original image space.
Defaults to False.
Returns: Returns:
dict: Contains predictions of bbox_head and list[:obj:`InstanceData`]: Detection results of each sample
features of roi_extractor. after the post process.
Each item usually contains following keys.
- scores_3d (Tensor): Classification scores, has a shape
(num_instances, )
- labels_3d (Tensor): Labels of bboxes, has a shape
(num_instances, ).
- bboxes_3d (BaseInstance3DBoxes): Prediction of bboxes,
contains a tensor with shape (num_instances, C), where
C >= 7.
""" """
pooled_seg_feats = self.seg_roi_extractor(seg_feats, assert self.with_bbox, 'Bbox head must be implemented in PartA2.'
voxels_dict['voxel_centers'], assert self.with_semantic, 'Semantic head must be implemented' \
voxels_dict['coors'][..., 0], ' in PartA2.'
rois)
pooled_part_feats = self.part_roi_extractor( batch_input_metas = [
part_feats, voxels_dict['voxel_centers'], data_samples.metainfo for data_samples in batch_data_samples
voxels_dict['coors'][..., 0], rois) ]
cls_score, bbox_pred = self.bbox_head(pooled_seg_feats, voxels_dict = feats_dict.pop('voxels_dict')
pooled_part_feats) # TODO: Split predict semantic and bbox
results_list = self.predict_bbox(feats_dict, voxels_dict,
bbox_results = dict( batch_input_metas, rpn_results_list,
cls_score=cls_score, self.test_cfg)
bbox_pred=bbox_pred, return results_list
pooled_seg_feats=pooled_seg_feats,
pooled_part_feats=pooled_part_feats) def predict_bbox(self, feats_dict: Dict, voxel_dict: Dict,
return bbox_results batch_input_metas: List[dict],
rpn_results_list: InstanceList,
test_cfg: ConfigDict) -> InstanceList:
"""Perform forward propagation of the bbox head and predict detection
results on the features of the upstream network.
Args:
feats_dict (dict): Contains features from the first stage.
voxel_dict (dict): Contains information of voxels.
batch_input_metas (list[dict], Optional): Batch image meta info.
Defaults to None.
rpn_results_list (List[:obj:`InstancesData`]): Detection results
of rpn head.
test_cfg (Config): Test config.
Returns:
list[:obj:`InstanceData`]: Detection results of each sample
after the post process.
Each item usually contains following keys.
- scores_3d (Tensor): Classification scores, has a shape
(num_instances, )
- labels_3d (Tensor): Labels of bboxes, has a shape
(num_instances, ).
- bboxes_3d (BaseInstance3DBoxes): Prediction of bboxes,
contains a tensor with shape (num_instances, C), where
C >= 7.
"""
...
def loss(self, feats_dict: Dict, rpn_results_list: InstanceList,
batch_data_samples: SampleList, **kwargs) -> dict:
"""Perform forward propagation and loss calculation of the detection
roi on the features of the upstream network.
Args:
feats_dict (dict): Contains features from the first stage.
rpn_results_list (List[:obj:`InstancesData`]): Detection results
of rpn head.
batch_data_samples (List[:obj:`Det3DDataSample`]): The Data
samples. It usually includes information such as
`gt_instance_3d`, `gt_panoptic_seg_3d` and `gt_sem_seg_3d`.
Returns:
dict[str, Tensor]: A dictionary of loss components
"""
assert len(rpn_results_list) == len(batch_data_samples)
losses = dict()
batch_gt_instances_3d = []
batch_gt_instances_ignore = []
voxels_dict = feats_dict.pop('voxels_dict')
for data_sample in batch_data_samples:
batch_gt_instances_3d.append(data_sample.gt_instances_3d)
if 'ignored_instances' in data_sample:
batch_gt_instances_ignore.append(data_sample.ignored_instances)
else:
batch_gt_instances_ignore.append(None)
if self.with_semantic:
semantic_results = self._semantic_forward_train(
feats_dict, voxels_dict, batch_gt_instances_3d)
losses.update(semantic_results.pop('loss_semantic'))
sample_results = self._assign_and_sample(rpn_results_list,
batch_gt_instances_3d)
if self.with_bbox:
feats_dict.update(semantic_results)
bbox_results = self._bbox_forward_train(feats_dict, voxels_dict,
sample_results)
losses.update(bbox_results['loss_bbox'])
return losses
``` ```
此处我们省略了与其他功能相关的细节请参考 [此处](https://github.com/open-mmlab/mmdetection3d/blob/master/mmdet3d/models/roi_heads/part_aggregation_roi_head.py) 获取更多细节。 此处我们省略相关函数的更多细节请参考[代码](https://github.com/open-mmlab/mmdetection3d/blob/dev-1.x/mmdet3d/models/roi_heads/part_aggregation_roi_head.py)了解更多细节。
最后,用户需要在 `mmdet3d/models/bbox_heads/__init__.py``mmdet3d/models/roi_heads/__init__.py` 中添加模块,使得对应的注册器能够发现并加载该模块 最后,用户需要在 `mmdet3d/models/bbox_heads/__init__.py``mmdet3d/models/roi_heads/__init__.py` 中添加模块以确保相应的注册器能够找到并加载它们
此外,用户也可以添加以下的代码到配置文件中,从而实现相同的目标。 此外,用户也可以添加以下的代码到配置文件中,从而实现相同的目标。
...@@ -379,7 +478,7 @@ custom_imports=dict( ...@@ -379,7 +478,7 @@ custom_imports=dict(
imports=['mmdet3d.models.roi_heads.part_aggregation_roi_head', 'mmdet3d.models.roi_heads.bbox_heads.parta2_bbox_head']) imports=['mmdet3d.models.roi_heads.part_aggregation_roi_head', 'mmdet3d.models.roi_heads.bbox_heads.parta2_bbox_head'])
``` ```
PartAggregationROIHead 的配置文件如下所示 PartAggregationROIHead 的配置文件如下所示
```python ```python
model = dict( model = dict(
...@@ -394,14 +493,16 @@ model = dict( ...@@ -394,14 +493,16 @@ model = dict(
seg_score_thr=0.3, seg_score_thr=0.3,
num_classes=3, num_classes=3,
loss_seg=dict( loss_seg=dict(
type='FocalLoss', type='mmdet.FocalLoss',
use_sigmoid=True, use_sigmoid=True,
reduction='sum', reduction='sum',
gamma=2.0, gamma=2.0,
alpha=0.25, alpha=0.25,
loss_weight=1.0), loss_weight=1.0),
loss_part=dict( loss_part=dict(
type='CrossEntropyLoss', use_sigmoid=True, loss_weight=1.0)), type='mmdet.CrossEntropyLoss',
use_sigmoid=True,
loss_weight=1.0)),
seg_roi_extractor=dict( seg_roi_extractor=dict(
type='Single3DRoIAwareExtractor', type='Single3DRoIAwareExtractor',
roi_layer=dict( roi_layer=dict(
...@@ -409,7 +510,7 @@ model = dict( ...@@ -409,7 +510,7 @@ model = dict(
out_size=14, out_size=14,
max_pts_per_voxel=128, max_pts_per_voxel=128,
mode='max')), mode='max')),
part_roi_extractor=dict( bbox_roi_extractor=dict(
type='Single3DRoIAwareExtractor', type='Single3DRoIAwareExtractor',
roi_layer=dict( roi_layer=dict(
type='RoIAwarePool3d', type='RoIAwarePool3d',
...@@ -433,15 +534,15 @@ model = dict( ...@@ -433,15 +534,15 @@ model = dict(
roi_feat_size=14, roi_feat_size=14,
with_corner_loss=True, with_corner_loss=True,
loss_bbox=dict( loss_bbox=dict(
type='SmoothL1Loss', type='mmdet.SmoothL1Loss',
beta=1.0 / 9.0, beta=1.0 / 9.0,
reduction='sum', reduction='sum',
loss_weight=1.0), loss_weight=1.0),
loss_cls=dict( loss_cls=dict(
type='CrossEntropyLoss', type='mmdet.CrossEntropyLoss',
use_sigmoid=True, use_sigmoid=True,
reduction='sum', reduction='sum',
loss_weight=1.0))) loss_weight=1.0))),
... ...
) )
``` ```
...@@ -459,8 +560,8 @@ PartA2 Head 的第二阶段主要使用新建的 `PartAggregationROIHead` 和 `P ...@@ -459,8 +560,8 @@ PartA2 Head 的第二阶段主要使用新建的 `PartAggregationROIHead` 和 `P
import torch import torch
import torch.nn as nn import torch.nn as nn
from ..builder import LOSSES from mmdet3d.registry import MODELS
from .utils import weighted_loss from mmdet.models.losses.utils import weighted_loss
@weighted_loss @weighted_loss
def my_loss(pred, target): def my_loss(pred, target):
...@@ -468,7 +569,7 @@ def my_loss(pred, target): ...@@ -468,7 +569,7 @@ def my_loss(pred, target):
loss = torch.abs(pred - target) loss = torch.abs(pred - target)
return loss return loss
@LOSSES.register_module() @MODELS.register_module()
class MyLoss(nn.Module): class MyLoss(nn.Module):
def __init__(self, reduction='mean', loss_weight=1.0): def __init__(self, reduction='mean', loss_weight=1.0):
......
...@@ -2,8 +2,6 @@ ...@@ -2,8 +2,6 @@
本页提供了有关在 MMDetection3D 中使用 KITTI 数据集的具体教程。 本页提供了有关在 MMDetection3D 中使用 KITTI 数据集的具体教程。
**注意**:此教程目前仅适用于基于雷达和多模态的 3D 目标检测的相关方法,与基于单目图像的 3D 目标检测相关的内容会在之后进行补充。
## 数据准备 ## 数据准备
您可以在[这里](http://www.cvlibs.net/datasets/kitti/eval_object.php?obj_benchmark=3d)下载 KITTI 3D 检测数据并解压缩所有 zip 文件。此外,您可以在[这里](https://download.openmmlab.com/mmdetection3d/data/train_planes.zip)下载道路平面信息,其在训练过程中作为一个可选项,用来提高模型的性能。道路平面信息由 [AVOD](https://github.com/kujason/avod) 生成,你可以在[这里](https://github.com/kujason/avod/issues/19)查看更多细节。 您可以在[这里](http://www.cvlibs.net/datasets/kitti/eval_object.php?obj_benchmark=3d)下载 KITTI 3D 检测数据并解压缩所有 zip 文件。此外,您可以在[这里](https://download.openmmlab.com/mmdetection3d/data/train_planes.zip)下载道路平面信息,其在训练过程中作为一个可选项,用来提高模型的性能。道路平面信息由 [AVOD](https://github.com/kujason/avod) 生成,你可以在[这里](https://github.com/kujason/avod/issues/19)查看更多细节。
...@@ -39,7 +37,7 @@ mmdetection3d ...@@ -39,7 +37,7 @@ mmdetection3d
```bash ```bash
mkdir ./data/kitti/ && mkdir ./data/kitti/ImageSets mkdir ./data/kitti/ && mkdir ./data/kitti/ImageSets
# Download data split # 下载数据划分
wget -c https://raw.githubusercontent.com/traveller59/second.pytorch/master/second/data/ImageSets/test.txt --no-check-certificate --content-disposition -O ./data/kitti/ImageSets/test.txt wget -c https://raw.githubusercontent.com/traveller59/second.pytorch/master/second/data/ImageSets/test.txt --no-check-certificate --content-disposition -O ./data/kitti/ImageSets/test.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/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
...@@ -48,7 +46,7 @@ wget -c https://raw.githubusercontent.com/traveller59/second.pytorch/master/sec ...@@ -48,7 +46,7 @@ wget -c https://raw.githubusercontent.com/traveller59/second.pytorch/master/sec
python tools/create_data.py kitti --root-path ./data/kitti --out-dir ./data/kitti --extra-tag kitti --with-plane python tools/create_data.py kitti --root-path ./data/kitti --out-dir ./data/kitti --extra-tag kitti --with-plane
``` ```
需要注意的是,如果您的本地磁盘没有充足的存储空间来存储转换后的数据,您可以通过改变 `out-dir` 来指定其他任意的存储路径。如果您没有准备 `planes` 数据,您需要移除 `--with-plane` 标志。 需要注意的是,如果您的本地磁盘没有充足的存储空间来存储转换后的数据,您可以通过改变 `--out-dir` 来指定其他任意的存储路径。如果您没有准备 `planes` 数据,您需要移除 `--with-plane` 标志。
处理后的文件夹结构应该如下: 处理后的文件夹结构应该如下:
...@@ -78,39 +76,40 @@ kitti ...@@ -78,39 +76,40 @@ kitti
├── kitti_dbinfos_train.pkl ├── kitti_dbinfos_train.pkl
├── kitti_infos_test.pkl ├── kitti_infos_test.pkl
├── kitti_infos_trainval.pkl ├── kitti_infos_trainval.pkl
├── kitti_infos_train_mono3d.coco.json
├── kitti_infos_trainval_mono3d.coco.json
├── kitti_infos_test_mono3d.coco.json
├── kitti_infos_val_mono3d.coco.json
``` ```
其中的各项文件的含义如下所示: - `kitti_gt_database/xxxxx.bin`:训练数据集中包含在 3D 标注框中的点云数据。
- `kitti_infos_train.pkl`:训练数据集,该字典包含了两个键值:`metainfo``data_list``metainfo` 包含数据集的基本信息,例如 `categories`, `dataset``info_version``data_list` 是由字典组成的列表,每个字典(以下简称 `info`)包含了单个样本的所有详细信息。
- `kitti_gt_database/xxxxx.bin`: 训练数据集中包含在 3D 标注框中的点云数据 - info\['sample_idx'\]:该样本在整个数据集的索引。
- `kitti_infos_train.pkl`:训练数据集的信息,其中每一帧的信息包含下面的内容: - info\['images'\]:多个相机捕获的图像信息。是一个字典,包含 5 个键值:`CAM0`, `CAM1`, `CAM2`, `CAM3`, `R0_rect`
- info\['point_cloud'\]: {'num_features': 4, 'velodyne_path': velodyne_path}. - info\['images'\]\['R0_rect'\]:校准旋转矩阵,是一个 4x4 数组。
- info\['annos'\]: { - info\['images'\]\['CAM2'\]:包含 `CAM2` 相机传感器的信息。
- 位置:其中 x,y,z 为相机参考坐标系下的目标的底部中心(单位为米),是一个尺寸为 Nx3 的数组 - info\['images'\]\['CAM2'\]\['img_path'\]:图像的文件名。
- 维度: 目标的高、宽、长(单位为米),是一个尺寸为 Nx3 的数组 - info\['images'\]\['CAM2'\]\['height'\]:图像的高。
- 旋转角:相机坐标系下目标绕着 Y 轴的旋转角 ry,其取值范围为 \[-pi..pi\] ,是一个尺寸为 N 的数组 - info\['images'\]\['CAM2'\]\['width'\]:图像的宽。
- 名称:标准框所包含的目标的名称,是一个尺寸为 N 的数组 - info\['images'\]\['CAM2'\]\['cam2img'\]:相机到图像的变换矩阵,是一个 4x4 数组。
- 困难度:kitti 官方所定义的困难度,包括 简单,适中,困难 - info\['images'\]\['CAM2'\]\['lidar2cam'\]:激光雷达到相机的变换矩阵,是一个 4x4 数组。
- 组别标识符:用于多部件的目标 - info\['images'\]\['CAM2'\]\['lidar2img'\]:激光雷达到图像的变换矩阵,是一个 4x4 数组。
} - info\['lidar_points'\]:是一个字典,包含了激光雷达点相关的信息。
- (optional) info\['calib'\]: { - info\['lidar_points'\]\['lidar_path'\]:激光雷达点云数据的文件名。
- P0:校对后的 camera0 投影矩阵,是一个 3x4 数组 - info\['lidar_points'\]\['num_pts_feats'\]:点的特征维度。
- P1:校对后的 camera1 投影矩阵,是一个 3x4 数组 - info\['lidar_points'\]\['Tr_velo_to_cam'\]:Velodyne 坐标到相机坐标的变换矩阵,是一个 4x4 数组。
- P2:校对后的 camera2 投影矩阵,是一个 3x4 数组 - info\['lidar_points'\]\['Tr_imu_to_velo'\]:IMU 坐标到 Velodyne 坐标的变换矩阵,是一个 4x4 数组。
- P3:校对后的 camera3 投影矩阵,是一个 3x4 数组 - info\['instances'\]:是一个字典组成的列表。每个字典包含单个实例的所有标注信息。对于其中的第 i 个实例,我们有:
- R0_rect:校准旋转矩阵,是一个 4x4 数组 - info\['instances'\]\[i\]\['bbox'\]:长度为 4 的列表,以 (x1, y1, x2, y2) 的顺序表示实例的 2D 边界框。
- Tr_velo_to_cam:从 Velodyne 坐标到相机坐标的变换矩阵,是一个 4x4 数组 - info\['instances'\]\[i\]\['bbox_3d'\]:长度为 7 的列表,以 (x, y, z, w, h, l, yaw) 的顺序表示实例的 3D 边界框。
- Tr_imu_to_velo:从 IMU 坐标到 Velodyne 坐标的变换矩阵,是一个 4x4 数组 - info\['instances'\]\[i\]\['bbox_label'\]:是一个整数,表示实例的 2D 标签,-1 代表忽略。
} - info\['instances'\]\[i\]\['bbox_label_3d'\]:是一个整数,表示实例的 3D 标签,-1 代表忽略。
- (optional) info\['image'\]:{'image_idx': idx, 'image_path': image_path, 'image_shape', image_shape}. - info\['instances'\]\[i\]\['depth'\]:3D 边界框投影到相关图像平面的中心点的深度。
- info\['instances'\]\[i\]\['num_lidar_pts'\]:3D 边界框内的激光雷达点数。
**注意**:其中的 info\['annos'\] 中的数据均位于相机参考坐标系中,更多的细节请参考[此处](http://www.cvlibs.net/publications/Geiger2013IJRR.pdf) - info\['instances'\]\[i\]\['center_2d'\]:3D 边界框投影的 2D 中心。
- info\['instances'\]\[i\]\['difficulty'\]:KITTI 官方定义的困难度,包括简单、适中、困难。
获取 kitti_infos_xxx.pkl 和 kitti_infos_xxx_mono3d.coco.json 的核心函数分别为 [get_kitti_image_info](https://github.com/open-mmlab/mmdetection3d/blob/7873c8f62b99314f35079f369d1dab8d63f8a3ce/tools/data_converter/kitti_data_utils.py#L140)[get_2d_boxes](https://github.com/open-mmlab/mmdetection3d/blob/7873c8f62b99314f35079f369d1dab8d63f8a3ce/tools/data_converter/kitti_converter.py#L378). - info\['instances'\]\[i\]\['truncated'\]:从 0(非截断)到 1(截断)的浮点数,其中截断指的是离开检测图像边界的检测目标。
- info\['instances'\]\[i\]\['occluded'\]:整数 (0,1,2,3) 表示目标的遮挡状态:0 = 完全可见,1 = 部分遮挡,2 = 大面积遮挡,3 = 未知。
- info\['instances'\]\[i\]\['group_ids'\]:用于多部分的物体。
- info\['plane'\](可选):地平面信息。
请参考 [kitti_converter.py](https://github.com/open-mmlab/mmdetection3d/blob/dev-1.x/tools/dataset_converters/kitti_converter.py)[update_infos_to_v2.py](https://github.com/open-mmlab/mmdetection3d/blob/dev-1.x/tools/dataset_converters/update_infos_to_v2.py) 了解更多细节。
## 训练流程 ## 训练流程
...@@ -122,13 +121,9 @@ train_pipeline = [ ...@@ -122,13 +121,9 @@ train_pipeline = [
type='LoadPointsFromFile', type='LoadPointsFromFile',
coord_type='LIDAR', coord_type='LIDAR',
load_dim=4, # x, y, z, intensity load_dim=4, # x, y, z, intensity
use_dim=4, # x, y, z, intensity use_dim=4),
file_client_args=file_client_args),
dict( dict(
type='LoadAnnotations3D', type='LoadAnnotations3D', with_bbox_3d=True, with_label_3d=True),
with_bbox_3d=True,
with_label_3d=True,
file_client_args=file_client_args),
dict(type='ObjectSample', db_sampler=db_sampler), dict(type='ObjectSample', db_sampler=db_sampler),
dict( dict(
type='ObjectNoise', type='ObjectNoise',
...@@ -144,8 +139,9 @@ train_pipeline = [ ...@@ -144,8 +139,9 @@ train_pipeline = [
dict(type='PointsRangeFilter', point_cloud_range=point_cloud_range), dict(type='PointsRangeFilter', point_cloud_range=point_cloud_range),
dict(type='ObjectRangeFilter', point_cloud_range=point_cloud_range), dict(type='ObjectRangeFilter', point_cloud_range=point_cloud_range),
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_bboxes_3d', 'gt_labels_3d'])
] ]
``` ```
...@@ -159,7 +155,7 @@ train_pipeline = [ ...@@ -159,7 +155,7 @@ train_pipeline = [
使用 8 个 GPU 以及 KITTI 指标评估的 PointPillars 的示例如下: 使用 8 个 GPU 以及 KITTI 指标评估的 PointPillars 的示例如下:
```shell ```shell
bash tools/dist_test.sh configs/pointpillars/pointpillars_hv_secfpn_8xb6-160e_kitti-3d-3class.py work_dirs/hv_pointpillars_secfpn_6x8_160e_kitti-3d-3class/latest.pth 8 --eval bbox bash tools/dist_test.sh configs/pointpillars/pointpillars_hv_secfpn_8xb6-160e_kitti-3d-3class.py work_dirs/pointpillars_hv_secfpn_8xb6-160e_kitti-3d-3class/latest.pth 8
``` ```
## 度量指标 ## 度量指标
...@@ -185,10 +181,32 @@ aos AP:97.70, 89.11, 87.38 ...@@ -185,10 +181,32 @@ aos AP:97.70, 89.11, 87.38
使用 8 个 GPU 在 KITTI 上测试 PointPillars 并生成对排行榜的提交的示例如下: 使用 8 个 GPU 在 KITTI 上测试 PointPillars 并生成对排行榜的提交的示例如下:
- 首先,你需要在你的配置文件中修改 `test_evaluator` 字典,并加上 `pklfile_prefix``submission_prefix`,如下所示:
```python
data_root = 'data/kitti'
test_evaluator = dict(
type='KittiMetric',
ann_file=data_root + 'kitti_infos_test.pkl',
metric='bbox',
pklfile_prefix='results/kitti-3class/kitti_results',
submission_prefix='results/kitti-3class/kitti_results')
```
- 接下来,你可以运行如下测试脚本。
```shell
mkdir -p results/kitti-3class
./tools/dist_test.sh configs/pointpillars/configs/pointpillars/pointpillars_hv_secfpn_8xb6-160e_kitti-3d-3class.py work_dirs/pointpillars_hv_secfpn_8xb6-160e_kitti-3d-3class/latest.pth 8
```
- 或者你可以在测试指令中使用 `--cfg-options "test_evaluator.pklfile_prefix=results/kitti-3class/kitti_results" "test_evaluator.submission_prefix=results/kitti-3class/kitti_results"`,然后直接运行如下测试脚本。
```shell ```shell
mkdir -p results/kitti-3class mkdir -p results/kitti-3class
./tools/dist_test.sh configs/pointpillars/pointpillars_hv_secfpn_8xb6-160e_kitti-3d-3class.py work_dirs/hv_pointpillars_secfpn_6x8_160e_kitti-3d-3class/latest.pth 8 --out results/kitti-3class/results_eval.pkl --format-only --eval-options 'pklfile_prefix=results/kitti-3class/kitti_results' 'submission_prefix=results/kitti-3class/kitti_results' ./tools/dist_test.sh configs/pointpillars/pointpillars_hv_secfpn_8xb6-160e_kitti-3d-3class.py work_dirs/pointpillars_hv_secfpn_8xb6-160e_kitti-3d-3class/latest.pth 8 --cfg-options 'test_evaluator.pklfile_prefix=results/kitti-3class/kitti_results' 'test_evaluator.submission_prefix=results/kitti-3class/kitti_results'
``` ```
在生成 `results/kitti-3class/kitti_results/xxxxx.txt` 后,您可以提交这些文件到 KITTI 官方网站进行基准测试,请参考 [KITTI 官方网站](<(http://www.cvlibs.net/datasets/kitti/index.php)>)获取更多细节。 在生成 `results/kitti-3class/kitti_results/xxxxx.txt` 后,您可以提交这些文件到 KITTI 官方网站进行基准测试,请参考 [KITTI 官方网站](<(http://www.cvlibs.net/datasets/kitti/index.php)>)获取更多细节。
...@@ -36,7 +36,7 @@ mmdetection3d ...@@ -36,7 +36,7 @@ mmdetection3d
其中 `v1.01-train``v1.01-test` 包含与 nuScenes 数据集相同的元文件,`.txt` 文件包含数据划分的信息。 其中 `v1.01-train``v1.01-test` 包含与 nuScenes 数据集相同的元文件,`.txt` 文件包含数据划分的信息。
Lyft 不提供训练集和验证集的官方划分方案,因此 MMDetection3D 对不同场景下的不同类别的目标数量进行分析,并提供了一个数据集划分方案。 Lyft 不提供训练集和验证集的官方划分方案,因此 MMDetection3D 对不同场景下的不同类别的目标数量进行分析,并提供了一个数据集划分方案。
`sample_submission.csv` 是用于提交到 Kaggle 评估服务器的基本文件。 `sample_submission.csv` 是用于提交到 Kaggle 评估服务器的基本文件。
需要注意的是,我们遵循了 Lyft 最初的文件夹命名以实现更清楚的文件组织。请将下载下来的原始文件夹重命名按照上述组织结构重新命名。 需要注意的是,我们遵循了 Lyft 最初的文件夹命名以实现更清楚的文件组织。请将下载下来的原始文件夹按照上述组织结构重新命名。
## 数据准备 ## 数据准备
...@@ -77,38 +77,48 @@ mmdetection3d ...@@ -77,38 +77,48 @@ mmdetection3d
│ │ ├── lyft_infos_train.pkl │ │ ├── lyft_infos_train.pkl
│ │ ├── lyft_infos_val.pkl │ │ ├── lyft_infos_val.pkl
│ │ ├── lyft_infos_test.pkl │ │ ├── lyft_infos_test.pkl
│ │ ├── lyft_infos_train_mono3d.coco.json
│ │ ├── lyft_infos_val_mono3d.coco.json
│ │ ├── lyft_infos_test_mono3d.coco.json
``` ```
其中,.pkl 文件通常适用于涉及到点云的相关方法,coco 类型的 .json 文件更加适用于涉及到基于图像的相关方法,如基于图像的 2D 和 3D 目标检测。 - `lyft_infos_train.pkl`:训练数据集信息,该字典包含两个关键字:`metainfo``data_list``metainfo` 包含数据集的基本信息,例如 `categories`, `dataset``info_version``data_list` 是由字典组成的列表,每个字典(以下简称 `info`)包含了单个样本的所有详细信息。
不同于 nuScenes 数据集,这里仅能使用 json 文件进行 2D 检测相关的实验,未来将会进一步支持基于图像的 3D 检测。 - info\['sample_idx'\]:样本在整个数据集的索引。
- info\['token'\]:样本数据标记。
- info\['timestamp'\]:样本数据时间戳。
- info\['lidar_points'\]:是一个字典,包含了所有与激光雷达点相关的信息。
- info\['lidar_points'\]\['lidar_path'\]:激光雷达点云数据的文件路径。
- info\['lidar_points'\]\['num_pts_feats'\]:点的特征维度。
- info\['lidar_points'\]\['lidar2ego'\]:该激光雷达传感器到自车的变换矩阵。(4x4 列表)
- info\['lidar_points'\]\['ego2global'\]:自车到全局坐标的变换矩阵。(4x4 列表)
- info\['lidar_sweeps'\]:是一个列表,包含了扫描信息(没有标注的中间帧)
- info\['lidar_sweeps'\]\[i\]\['lidar_points'\]\['data_path'\]:第 i 次扫描的激光雷达数据的文件路径。
- info\['lidar_sweeps'\]\[i\]\['lidar_points'\]\[lidar2ego''\]:当前激光雷达传感器到自车在第 i 次扫描的变换矩阵。(4x4 列表)
- info\['lidar_sweeps'\]\[i\]\['lidar_points'\]\['ego2global'\]:自车在第 i 次扫描到全局坐标的变换矩阵。(4x4 列表)
- info\['lidar_sweeps'\]\[i\]\['lidar2sensor'\]:从当前帧主激光雷达到第 i 帧扫描激光雷达的变换矩阵。(4x4 列表)
- info\['lidar_sweeps'\]\[i\]\['timestamp'\]:扫描数据的时间戳。
- info\['lidar_sweeps'\]\[i\]\['sample_data_token'\]:扫描样本数据标记。
- info\['images'\]:是一个字典,包含与每个相机对应的六个键值:`'CAM_FRONT'`, `'CAM_FRONT_RIGHT'`, `'CAM_FRONT_LEFT'`, `'CAM_BACK'`, `'CAM_BACK_LEFT'`, `'CAM_BACK_RIGHT'`。每个字典包含了对应相机的所有数据信息。
- info\['images'\]\['CAM_XXX'\]\['img_path'\]:图像的文件名。
- info\['images'\]\['CAM_XXX'\]\['cam2img'\]:当 3D 投影到图像平面时需要的内参信息相关的变换矩阵。(3x3 列表)
- info\['images'\]\['CAM_XXX'\]\['sample_data_token'\]:图像样本数据标记。
- info\['images'\]\['CAM_XXX'\]\['timestamp'\]:图像的时间戳。
- info\['images'\]\['CAM_XXX'\]\['cam2ego'\]:该相机传感器到自车的变换矩阵。(4x4 列表)
- info\['images'\]\['CAM_XXX'\]\['lidar2cam'\]:激光雷达传感器到该相机的变换矩阵。(4x4 列表)
- info\['instances'\]:是一个字典组成的列表。每个字典包含单个实例的所有标注信息。对于其中的第 i 个实例,我们有:
- info\['instances'\]\[i\]\['bbox_3d'\]:长度为 7 的列表,以 (x, y, z, l, w, h, yaw) 的顺序表示实例在激光雷达坐标系下的 3D 边界框。
- info\['instances'\]\[i\]\['bbox_label_3d'\]:整数从 0 开始表示实例的标签,其中 -1 代表忽略该类别。
- info\['instances'\]\[i\]\['bbox_3d_isvalid'\]:每个包围框是否有效。一般情况下,我们只将包含至少一个激光雷达或雷达点的 3D 框作为有效框。
接下来将详细介绍 Lyft 数据集和 nuScenes 数据集之间的数据集信息文件中的不同点: 接下来将详细介绍 Lyft 数据集和 nuScenes 数据集之间的数据集信息文件中的不同点:
- `lyft_database/xxxxx.bin` 文件不存在:由于真实标注框的采样对实验的影响可以忽略不计,在 Lyft 数据集中不会提取该目录和相关的 `.bin` 文件。 - `lyft_database/xxxxx.bin` 文件不存在:由于真实标注框的采样对实验的影响可以忽略不计,在 Lyft 数据集中不会提取该目录和相关的 `.bin` 文件。
- `lyft_infos_train.pkl`:包含训练数据集信息,每一帧包含两个关键字:`metadata``infos`
`metadata` 包含数据集自身的基础信息,如 `{'version': 'v1.01-train'}`,然而 `infos` 包含和 nuScenes 数据集相似的数据集详细信息,但是并不包含一下几点: - `lyft_infos_train.pkl`
- info\['sweeps'\]:扫描信息.
- info\['sweeps'\]\[i\]\['type'\]:扫描信息的数据类型,如 `'lidar'` - info\['instances'\]\[i\]\['velocity'\] 不存在,Lyft 数据集中不存在速度评估信息。
Lyft 数据集中的一些样例具有不同的 LiDAR 设置,然而为了数据分布的一致性,这里将一直采用顶部的 LiDAR 设备所采集的数据点信息。 - info\['instances'\]\[i\]\['num_lidar_pts'\] 及 info\['instances'\]\[i\]\['num_radar_pts'\] 不存在。
- info\['gt_names'\]:在 Lyft 数据集中有 9 个类别,相比于 nuScenes 数据集,不同类别的标注不平衡问题更加突出。
- info\['gt_velocity'\] 不存在:Lyft 数据集中不存在速度评估信息。 这里仅介绍存储在训练数据文件的数据记录信息。这同样适用于验证集和测试集(没有实例)。
- info\['num_lidar_pts'\]:默认值设置为 -1。
- info\['num_radar_pts'\]:默认值设置为 0。 请参考 [lyft_converter.py](https://github.com/open-mmlab/mmdetection3d/blob/dev-1.x/tools/dataset_converters/lyft_converter.py) 获取更多关于 `lyft_infos_xxx.pkl` 结构的细节。
- info\['valid_flag'\] 不存在:这个标志信息因无效的 `num_lidar_pts``num_radar_pts` 的存在而存在。
- `nuscenes_infos_train_mono3d.coco.json`:包含 coco 类型的训练数据集相关的信息。这个文件仅包含 2D 相关的信息,不包含 3D 目标检测所需要的信息,如相机内参。
- info\['images'\]:包含所有图像信息的列表。
- 仅包含 `'file_name'`, `'id'`, `'width'`, `'height'`
- info\['annotations'\]:包含所有标注信息的列表。
- 仅包含 `'file_name'``'image_id'``'area'``'category_name'``'category_id'``'bbox'``'is_crowd'``'segmentation'``'id'`,其中 `'is_crowd'``'segmentation'` 默认设置为 `0``[]`
Lyft 数据集中不包含属性标注信息。
这里仅介绍存储在训练数据文件的数据记录信息,在测试数据集也采用上述的数据记录方式。
获取 `lyft_infos_xxx.pkl` 的核心函数是 [\_fill_trainval_infos](https://github.com/open-mmlab/mmdetection3d/blob/master/tools/data_converter/lyft_converter.py#L93)
请参考 [lyft_converter.py](https://github.com/open-mmlab/mmdetection3d/blob/master/tools/data_converter/lyft_converter.py) 获取更多细节。
## 训练流程 ## 训练流程
...@@ -122,12 +132,10 @@ train_pipeline = [ ...@@ -122,12 +132,10 @@ train_pipeline = [
type='LoadPointsFromFile', type='LoadPointsFromFile',
coord_type='LIDAR', coord_type='LIDAR',
load_dim=5, load_dim=5,
use_dim=5, use_dim=5),
file_client_args=file_client_args),
dict( dict(
type='LoadPointsFromMultiSweeps', type='LoadPointsFromMultiSweeps',
sweeps_num=10, sweeps_num=10),
file_client_args=file_client_args),
dict(type='LoadAnnotations3D', with_bbox_3d=True, with_label_3d=True), dict(type='LoadAnnotations3D', with_bbox_3d=True, with_label_3d=True),
dict( dict(
type='GlobalRotScaleTrans', type='GlobalRotScaleTrans',
...@@ -138,8 +146,9 @@ train_pipeline = [ ...@@ -138,8 +146,9 @@ train_pipeline = [
dict(type='PointsRangeFilter', point_cloud_range=point_cloud_range), dict(type='PointsRangeFilter', point_cloud_range=point_cloud_range),
dict(type='ObjectRangeFilter', point_cloud_range=point_cloud_range), dict(type='ObjectRangeFilter', point_cloud_range=point_cloud_range),
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_bboxes_3d', 'gt_labels_3d'])
] ]
``` ```
...@@ -151,7 +160,7 @@ train_pipeline = [ ...@@ -151,7 +160,7 @@ train_pipeline = [
使用 8 个 GPU 以及 Lyft 指标评估的 PointPillars 的示例如下: 使用 8 个 GPU 以及 Lyft 指标评估的 PointPillars 的示例如下:
```shell ```shell
bash ./tools/dist_test.sh configs/pointpillars/pointpillars_hv_fpn_sbn-all_8xb2-2x_lyft-3d.py checkpoints/hv_pointpillars_fpn_sbn-all_2x8_2x_lyft-3d_20210517_202818-fc6904c3.pth 8 --eval bbox bash ./tools/dist_test.sh configs/pointpillars/pointpillars_hv_fpn_sbn-all_8xb2-2x_lyft-3d.py checkpoints/hv_pointpillars_fpn_sbn-all_2x8_2x_lyft-3d_20210517_202818-fc6904c3.pth 8
``` ```
## 度量指标 ## 度量指标
...@@ -186,7 +195,7 @@ Lyft 提出了一个更加严格的用以评估所预测的 3D 检测框的度 ...@@ -186,7 +195,7 @@ Lyft 提出了一个更加严格的用以评估所预测的 3D 检测框的度
使用 8 个 GPU 在 Lyft 上测试 PointPillars 并生成对排行榜的提交的示例如下: 使用 8 个 GPU 在 Lyft 上测试 PointPillars 并生成对排行榜的提交的示例如下:
```shell ```shell
./tools/dist_test.sh configs/pointpillars/pointpillars_hv_fpn_sbn-all_8xb2-2x_lyft-3d.py work_dirs/pp-lyft/latest.pth 8 --out work_dirs/pp-lyft/results_challenge.pkl --format-only --eval-options 'jsonfile_prefix=work_dirs/pp-lyft/results_challenge' 'csv_savepath=results/pp-lyft/results_challenge.csv' ./tools/dist_test.sh configs/pointpillars/pointpillars_hv_fpn_sbn-all_8xb2-2x_lyft-3d.py work_dirs/pp-lyft/latest.pth 8 --cfg-options test_evaluator.jsonfile_prefix=work_dirs/pp-lyft/results_challenge test_evaluator.csv_savepath=results/pp-lyft/results_challenge.csv
``` ```
在生成 `work_dirs/pp-lyft/results_challenge.csv`,您可以将生成的文件提交到 Kaggle 评估服务器,请参考[官方网址](https://www.kaggle.com/c/3d-object-detection-for-autonomous-vehicles)获取更多细节。 在生成 `work_dirs/pp-lyft/results_challenge.csv`,您可以将生成的文件提交到 Kaggle 评估服务器,请参考[官方网址](https://www.kaggle.com/c/3d-object-detection-for-autonomous-vehicles)获取更多细节。
......
...@@ -53,74 +53,57 @@ mmdetection3d ...@@ -53,74 +53,57 @@ mmdetection3d
│ │ ├── nuscenes_infos_val.pkl │ │ ├── nuscenes_infos_val.pkl
│ │ ├── nuscenes_infos_test.pkl │ │ ├── nuscenes_infos_test.pkl
│ │ ├── nuscenes_dbinfos_train.pkl │ │ ├── nuscenes_dbinfos_train.pkl
│ │ ├── nuscenes_infos_train_mono3d.coco.json
│ │ ├── nuscenes_infos_trainval_mono3d.coco.json
│ │ ├── nuscenes_infos_val_mono3d.coco.json
│ │ ├── nuscenes_infos_test_mono3d.coco.json
``` ```
这里,.pkl 文件一般用于涉及点云的方法,coco 风格的 .json 文件更适合基于图像的方法,例如基于图像的 2D 和 3D 检测。
接下来,我们将详细说明这些信息文件中记录的细节。
- `nuscenes_database/xxxxx.bin`:训练数据集的每个 3D 包围框中包含的点云数据。 - `nuscenes_database/xxxxx.bin`:训练数据集的每个 3D 包围框中包含的点云数据。
- `nuscenes_infos_train.pkl`:训练数据集信息,每帧信息有两个键值: `metadata``infos` `metadata` 包含数据集本身的基本信息,例如 `{'version': 'v1.0-trainval'}`,而 `infos` 包含详细信息如下: - `nuscenes_infos_train.pkl`:训练数据集,该字典包含了两个键值:`metainfo``data_list``metainfo` 包含数据集的基本信息,例如 `categories`, `dataset``info_version``data_list` 是由字典组成的列表,每个字典(以下简称 `info`)包含了单个样本的所有详细信息
- info\['lidar_path'\]:激光雷达点云数据的文件路径 - info\['sample_idx'\]:样本在整个数据集的索引
- info\['token'\]:样本数据标记。 - info\['token'\]:样本数据标记。
- info\['sweeps'\]:扫描信息(nuScenes 中的 `sweeps` 是指没有标注的中间帧,而 `samples` 是指那些带有标注的关键帧)。 - info\['timestamp'\]:样本数据时间戳。
- info\['sweeps'\]\[i\]\['data_path'\]:第 i 次扫描的数据路径。 - info\['lidar_points'\]:是一个字典,包含了所有与激光雷达点相关的信息。
- info\['sweeps'\]\[i\]\['type'\]:扫描数据类型,例如“激光雷达”。 - info\['lidar_points'\]\['lidar_path'\]:激光雷达点云数据的文件路径。
- info\['sweeps'\]\[i\]\['sample_data_token'\]:扫描样本数据标记。 - info\['lidar_points'\]\['num_pts_feats'\]:点的特征维度。
- info\['sweeps'\]\[i\]\['sensor2ego_translation'\]:从当前传感器(用于收集扫描数据)到自车(包含感知周围环境传感器的车辆,车辆坐标系固连在自车上)的转换(1x3 列表)。 - info\['lidar_points'\]\['lidar2ego'\]:该激光雷达传感器到自车的变换矩阵。(4x4 列表)
- info\['sweeps'\]\[i\]\['sensor2ego_rotation'\]:从当前传感器(用于收集扫描数据)到自车的旋转(四元数格式的 1x4 列表)。 - info\['lidar_points'\]\['ego2global'\]:自车到全局坐标的变换矩阵。(4x4 列表)
- info\['sweeps'\]\[i\]\['ego2global_translation'\]:从自车到全局坐标的转换(1x3 列表)。 - info\['lidar_sweeps'\]:是一个列表,包含了扫描信息(没有标注的中间帧)。
- info\['sweeps'\]\[i\]\['ego2global_rotation'\]:从自车到全局坐标的旋转(四元数格式的 1x4 列表)。 - info\['lidar_sweeps'\]\[i\]\['lidar_points'\]\['data_path'\]:第 i 次扫描的激光雷达数据的文件路径。
- info\['sweeps'\]\[i\]\['timestamp'\]:扫描数据的时间戳。 - info\['lidar_sweeps'\]\[i\]\['lidar_points'\]\[lidar2ego''\]:当前激光雷达传感器到自车的变换矩阵。(4x4 列表)
- info\['sweeps'\]\[i\]\['sensor2lidar_translation'\]:从当前传感器(用于收集扫描数据)到激光雷达的转换(1x3 列表)。 - info\['lidar_sweeps'\]\[i\]\['lidar_points'\]\['ego2global'\]:自车到全局坐标的变换矩阵。(4x4 列表)
- info\['sweeps'\]\[i\]\['sensor2lidar_rotation'\]:从当前传感器(用于收集扫描数据)到激光雷达的旋转(四元数格式的 1x4 列表)。 - info\['lidar_sweeps'\]\[i\]\['lidar2sensor'\]:从主激光雷达传感器到当前传感器(用于收集扫描数据)的变换矩阵。(4x4 列表)
- info\['cams'\]:相机校准信息。它包含与每个摄像头对应的六个键值: `'CAM_FRONT'`, `'CAM_FRONT_RIGHT'`, `'CAM_FRONT_LEFT'`, `'CAM_BACK'`, `'CAM_BACK_LEFT'`, `'CAM_BACK_RIGHT'` - info\['lidar_sweeps'\]\[i\]\['timestamp'\]:扫描数据的时间戳。
每个字典包含每个扫描数据按照上述方式的详细信息(每个信息的关键字与上述相同)。除此之外,每个相机还包含了一个键值 `'cam_intrinsic'` 用来保存 3D 点投影到图像平面上需要的内参信息。 - info\['lidar_sweeps'\]\[i\]\['sample_data_token'\]:扫描样本数据标记。
- info\['lidar2ego_translation'\]:从激光雷达到自车的转换(1x3 列表)。 - info\['images'\]:是一个字典,包含与每个相机对应的六个键值:`'CAM_FRONT'`, `'CAM_FRONT_RIGHT'`, `'CAM_FRONT_LEFT'`, `'CAM_BACK'`, `'CAM_BACK_LEFT'`, `'CAM_BACK_RIGHT'`。每个字典包含了对应相机的所有数据信息。
- info\['lidar2ego_rotation'\]:从激光雷达到自车的旋转(四元数格式的 1x4 列表)。 - info\['images'\]\['CAM_XXX'\]\['img_path'\]:图像的文件名。
- info\['ego2global_translation'\]:从自车到全局坐标的转换(1x3 列表)。 - info\['images'\]\['CAM_XXX'\]\['cam2img'\]:当 3D 投影到图像平面时需要的内参信息相关的变换矩阵。(3x3 列表)
- info\['ego2global_rotation'\]:从自我车辆到全局坐标的旋转(四元数格式的 1x4 列表)。 - info\['images'\]\['CAM_XXX'\]\['sample_data_token'\]:图像样本数据标记。
- info\['timestamp'\]:样本数据的时间戳。 - info\['images'\]\['CAM_XXX'\]\['timestamp'\]:图像的时间戳。
- info\['gt_boxes'\]:7 个自由度的 3D 包围框,一个 Nx7 数组。 - info\['images'\]\['CAM_XXX'\]\['cam2ego'\]:该相机传感器到自车的变换矩阵。(4x4 列表)
- info\['gt_names'\]:3D 包围框的类别,一个 1xN 数组。 - info\['images'\]\['CAM_XXX'\]\['lidar2cam'\]:激光雷达传感器到该相机的变换矩阵。(4x4 列表)
- info\['gt_velocity'\]:3D 包围框的速度(由于不准确,没有垂直测量),一个 Nx2 数组。 - info\['instances'\]:是一个字典组成的列表。每个字典包含单个实例的所有标注信息。对于其中的第 i 个实例,我们有:
- info\['num_lidar_pts'\]:每个 3D 包围框中包含的激光雷达点数。 - info\['instances'\]\[i\]\['bbox_3d'\]:长度为 7 的列表,以 (x, y, z, l, w, h, yaw) 的顺序表示实例的 3D 边界框。
- info\['num_radar_pts'\]:每个 3D 包围框中包含的雷达点数。 - info\['instances'\]\[i\]\['bbox_label_3d'\]:整数表示实例的标签,-1 代表忽略。
- info\['valid_flag'\]:每个包围框是否有效。一般情况下,我们只将包含至少一个激光雷达或雷达点的 3D 框作为有效框。 - info\['instances'\]\[i\]\['velocity'\]:3D 边界框的速度(由于不正确,没有垂直测量),大小为 (2, ) 的列表。
- `nuscenes_infos_train_mono3d.coco.json`:训练数据集 coco 风格的信息。该文件将基于图像的数据组织为三类(键值):`'categories'`, `'images'`, `'annotations'` - info\['instances'\]\[i\]\['num_lidar_pts'\]:每个 3D 边界框内包含的激光雷达点数。
- info\['categories'\]:包含所有类别名称的列表。每个元素都遵循字典格式并由两个键值组成:`'id'``'name'` - info\['instances'\]\[i\]\['num_radar_pts'\]:每个 3D 边界框内包含的雷达点数。
- info\['images'\]:包含所有图像信息的列表。 - info\['instances'\]\[i\]\['bbox_3d_isvalid'\]:每个包围框是否有效。一般情况下,我们只将包含至少一个激光雷达或雷达点的 3D 框作为有效框。
- info\['images'\]\[i\]\['file_name'\]:第 i 张图像的文件名。 - info\['cam_instances'\]:是一个字典,包含以下键值:`'CAM_FRONT'`, `'CAM_FRONT_RIGHT'`, `'CAM_FRONT_LEFT'`, `'CAM_BACK'`, `'CAM_BACK_LEFT'`, `'CAM_BACK_RIGHT'`。对于基于视觉的 3D 目标检测任务,我们将整个场景的 3D 标注划分至它们所属于的相应相机中。
- info\['images'\]\[i\]\['id'\]:第 i 张图像的样本数据标记。 - info\['cam_instances'\]\['CAM_XXX'\]\[i\]\['bbox_label'\]:实例标签。
- info\['images'\]\[i\]\['token'\]:与该帧对应的样本标记。 - info\['cam_instances'\]\['CAM_XXX'\]\[i\]\['bbox_label_3d'\]:实例标签。
- info\['images'\]\[i\]\['cam2ego_rotation'\]:从相机到自车的旋转(四元数格式的 1x4 列表)。 - info\['cam_instances'\]\['CAM_XXX'\]\[i\]\['bbox'\]:2D 边界框标注(3D 框投影的矩形框),顺序为 \[x1, y1, x2, y2\] 的列表。
- info\['images'\]\[i\]\['cam2ego_translation'\]:从相机到自车的转换(1x3 列表)。 - info\['cam_instances'\]\['CAM_XXX'\]\[i\]\['center_2d'\]:3D 框投影到图像上的中心点,大小为 (2, ) 的列表。
- info\['images'\]\[i\]\['ego2global_rotation''\]:从自车到全局坐标的旋转(四元数格式的 1x4 列表)。 - info\['cam_instances'\]\['CAM_XXX'\]\[i\]\['depth'\]:3D 框投影中心的深度。
- info\['images'\]\[i\]\['ego2global_translation'\]:从自车到全局坐标的转换(1x3 列表)。 - info\['cam_instances'\]\['CAM_XXX'\]\[i\]\['velocity'\]:3D 边界框的速度(由于不正确,没有垂直测量),大小为 (2, ) 的列表。
- info\['images'\]\[i\]\['cam_intrinsic'\]: 相机内参矩阵(3x3 列表)。 - info\['cam_instances'\]\['CAM_XXX'\]\[i\]\['attr_label'\]:实例的属性标签。我们为属性分类维护了一个属性集合和映射
- info\['images'\]\[i\]\['width'\]:图片宽度, nuScenes 中默认为 1600。 - info\['cam_instances'\]\['CAM_XXX'\]\[i\]\['bbox_3d'\]:长度为 7 的列表,以 (x, y, z, l, h, w, yaw) 的顺序表示实例的 3D 边界框。
- info\['images'\]\[i\]\['height'\]:图像高度, nuScenes 中默认为 900。
- info\['annotations'\]: 包含所有标注信息的列表。 注意:
- info\['annotations'\]\[i\]\['file_name'\]:对应图像的文件名。
- info\['annotations'\]\[i\]\['image_id'\]:对应图像的图像 ID (标记)。 1. `instances``cam_instances``bbox_3d` 的区别。
- info\['annotations'\]\[i\]\['area'\]:2D 包围框的面积。 `bbox_3d` 都被转换到 MMDet3D 定义的坐标系下,`instances` 中的 `bbox_3d` 是在激光雷达坐标系下,而 `cam_instances` 是在相机坐标系下。注意它们 3D 框中表示的不同('l, w, h' 和 'l, h, w')。
- info\['annotations'\]\[i\]\['category_name'\]:类别名称。
- info\['annotations'\]\[i\]\['category_id'\]:类别 id。 2. 这里我们只解释训练信息文件中记录的数据。这同样适用于验证集和测试集(测试集的 pkl 文件中不包含 `instances` 以及 `cam_instances`)。
- info\['annotations'\]\[i\]\['bbox'\]:2D 包围框标注(3D 投影框的外部矩形),1x4 列表跟随 \[x1, y1, x2-x1, y2-y1\]。x1/y1 是沿图像水平/垂直方向的最小坐标。
- info\['annotations'\]\[i\]\['iscrowd'\]:该区域是否拥挤。默认为 0。 获取 `nuscenes_infos_xxx.pkl` 的核心函数为 [\_fill_trainval_infos](https://github.com/open-mmlab/mmdetection3d/blob/dev-1.x/tools/dataset_converters/nuscenes_converter.py#L146)[get_2d_boxes](https://github.com/open-mmlab/mmdetection3d/blob/dev-1.x/tools/dataset_converters/nuscenes_converter.py#L397)。更多细节请参考 [nuscenes_converter.py](https://github.com/open-mmlab/mmdetection3d/blob/dev-1.x/tools/dataset_converters/nuscenes_converter.py)
- info\['annotations'\]\[i\]\['bbox_cam3d'\]:3D 包围框(重力)中心位置(3)、大小(3)、(全局)偏航角(1)、1x7 列表。
- info\['annotations'\]\[i\]\['velo_cam3d'\]:3D 包围框的速度(由于不准确,没有垂直测量),一个 Nx2 数组。
- info\['annotations'\]\[i\]\['center2d'\]:包含 2.5D 信息的投影 3D 中心:图像上的投影中心位置(2)和深度(1),1x3 列表。
- info\['annotations'\]\[i\]\['attribute_name'\]:属性名称。
- info\['annotations'\]\[i\]\['attribute_id'\]:属性 ID。
我们为属性分类维护了一个属性集合和映射。更多的细节请参考[这里](https://github.com/open-mmlab/mmdetection3d/blob/master/mmdet3d/datasets/nuscenes_mono_dataset.py#L53)
- info\['annotations'\]\[i\]\['id'\]:标注 ID。默认为 `i`
这里我们只解释训练信息文件中记录的数据。这同样适用于验证和测试集。
获取 `nuscenes_infos_xxx.pkl``nuscenes_infos_xxx_mono3d.coco.json` 的核心函数分别为 [\_fill_trainval_infos](https://github.com/open-mmlab/mmdetection3d/blob/master/tools/data_converter/nuscenes_converter.py#L143)[get_2d_boxes](https://github.com/open-mmlab/mmdetection3d/blob/master/tools/data_converter/nuscenes_converter.py#L397)。更多细节请参考 [nuscenes_converter.py](https://github.com/open-mmlab/mmdetection3d/blob/master/tools/data_converter/nuscenes_converter.py)
## 训练流程 ## 训练流程
...@@ -134,12 +117,10 @@ train_pipeline = [ ...@@ -134,12 +117,10 @@ train_pipeline = [
type='LoadPointsFromFile', type='LoadPointsFromFile',
coord_type='LIDAR', coord_type='LIDAR',
load_dim=5, load_dim=5,
use_dim=5, use_dim=5),
file_client_args=file_client_args),
dict( dict(
type='LoadPointsFromMultiSweeps', type='LoadPointsFromMultiSweeps',
sweeps_num=10, sweeps_num=10),
file_client_args=file_client_args),
dict(type='LoadAnnotations3D', with_bbox_3d=True, with_label_3d=True), dict(type='LoadAnnotations3D', with_bbox_3d=True, with_label_3d=True),
dict( dict(
type='GlobalRotScaleTrans', type='GlobalRotScaleTrans',
...@@ -151,8 +132,9 @@ train_pipeline = [ ...@@ -151,8 +132,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_bboxes_3d', 'gt_labels_3d'])
] ]
``` ```
...@@ -178,14 +160,11 @@ train_pipeline = [ ...@@ -178,14 +160,11 @@ train_pipeline = [
with_bbox_depth=True), with_bbox_depth=True),
dict(type='Resize', img_scale=(1600, 900), keep_ratio=True), dict(type='Resize', img_scale=(1600, 900), keep_ratio=True),
dict(type='RandomFlip3D', flip_ratio_bev_horizontal=0.5), dict(type='RandomFlip3D', flip_ratio_bev_horizontal=0.5),
dict(type='Normalize', **img_norm_cfg),
dict(type='Pad', size_divisor=32),
dict(type='DefaultFormatBundle3D', class_names=class_names),
dict( dict(
type='Collect3D', type='Pack3DDetInputs',
keys=[ keys=[
'img', 'gt_bboxes', 'gt_labels', 'attr_labels', 'gt_bboxes_3d', 'img', 'gt_bboxes', 'gt_bboxes_labels', 'attr_labels', 'gt_bboxes_3d',
'gt_labels_3d', 'centers2d', 'depths' 'gt_labels_3d', 'centers_2d', 'depths'
]), ]),
] ]
``` ```
...@@ -202,7 +181,7 @@ train_pipeline = [ ...@@ -202,7 +181,7 @@ train_pipeline = [
使用 8 个 GPU 以及 nuScenes 指标评估的 PointPillars 的示例如下 使用 8 个 GPU 以及 nuScenes 指标评估的 PointPillars 的示例如下
```shell ```shell
bash ./tools/dist_test.sh configs/pointpillars/pointpillars_hv_fpn_sbn-all_8xb4-2x_nus-3d.py checkpoints/hv_pointpillars_fpn_sbn-all_4x8_2x_nus-3d_20200620_230405-2fa62f3d.pth 8 --eval bbox bash ./tools/dist_test.sh configs/pointpillars/pointpillars_hv_fpn_sbn-all_8xb4-2x_nus-3d.py checkpoints/hv_pointpillars_fpn_sbn-all_4x8_2x_nus-3d_20200620_230405-2fa62f3d.pth 8
``` ```
## 指标 ## 指标
...@@ -241,11 +220,13 @@ barrier 0.466 0.581 0.269 0.169 nan nan ...@@ -241,11 +220,13 @@ barrier 0.466 0.581 0.269 0.169 nan nan
使用 8 个 GPU 在 nuScenes 上测试 PointPillars 并生成对排行榜的提交的示例如下 使用 8 个 GPU 在 nuScenes 上测试 PointPillars 并生成对排行榜的提交的示例如下
你需要在对应的配置文件中的 `test_evaluator` 里修改 `jsonfile_prefix`。举个例子,添加 `test_evaluator = dict(type='NuScenesMetric', jsonfile_prefix='work_dirs/pp-nus/results_eval.json')` 或在测试命令后使用 `--cfg-options "test_evaluator.jsonfile_prefix=work_dirs/pp-nus/results_eval.json)`
```shell ```shell
./tools/dist_test.sh configs/pointpillars/pointpillars_hv_fpn_sbn-all_8xb4-2x_nus-3d.py work_dirs/hv_pointpillars_secfpn_6x8_160e_kitti-3d-3class/latest.pth 8 --out work_dirs/pp-nus/results_eval.pkl --format-only --eval-options 'jsonfile_prefix=work_dirs/pp-nus/results_eval' ./tools/dist_test.sh configs/pointpillars/pointpillars_hv_fpn_sbn-all_8xb4-2x_nus-3d.py work_dirs/pp-nus/latest.pth 8 --cfg-options 'test_evaluator.jsonfile_prefix=work_dirs/pp-nus/results_eval'
``` ```
请注意,在[这里](https://github.com/open-mmlab/mmdetection3d/blob/master/configs/_base_/datasets/nus-3d.py#L132)测试信息应更改为测试集而不是验证集。 请注意,在[这里](https://github.com/open-mmlab/mmdetection3d/blob/dev-1.x/configs/_base_/datasets/nus-3d.py#L132)测试信息应更改为测试集而不是验证集。
生成 `work_dirs/pp-nus/results_eval.json` 后,您可以压缩并提交给 nuScenes 基准测试。更多信息请参考 [nuScenes 官方网站](https://www.nuscenes.org/object-detection?externalData=all&mapData=all&modalities=Any) 生成 `work_dirs/pp-nus/results_eval.json` 后,您可以压缩并提交给 nuScenes 基准测试。更多信息请参考 [nuScenes 官方网站](https://www.nuscenes.org/object-detection?externalData=all&mapData=all&modalities=Any)
......
...@@ -179,9 +179,7 @@ train_pipeline = [ ...@@ -179,9 +179,7 @@ train_pipeline = [
with_mask_3d=False, with_mask_3d=False,
with_seg_3d=True), with_seg_3d=True),
dict( dict(
type='PointSegClassMapping', type='PointSegClassMapping'),
valid_cat_ids=tuple(range(len(class_names))),
max_cat_id=13),
dict( dict(
type='IndoorPatchPointSample', type='IndoorPatchPointSample',
num_points=num_points, num_points=num_points,
...@@ -202,8 +200,7 @@ train_pipeline = [ ...@@ -202,8 +200,7 @@ train_pipeline = [
jitter_std=[0.01, 0.01, 0.01], jitter_std=[0.01, 0.01, 0.01],
clip_range=[-0.05, 0.05]), clip_range=[-0.05, 0.05]),
dict(type='RandomDropPointsColor', drop_ratio=0.2), dict(type='RandomDropPointsColor', drop_ratio=0.2),
dict(type='DefaultFormatBundle3D', class_names=class_names), dict(type='Pack3DDetInputs', keys=['points', 'pts_semantic_mask'])
dict(type='Collect3D', keys=['points', 'pts_semantic_mask'])
] ]
``` ```
...@@ -219,7 +216,7 @@ train_pipeline = [ ...@@ -219,7 +216,7 @@ train_pipeline = [
通常我们使用平均交并比 (mean Intersection over Union, mIoU) 作为 ScanNet 语义分割任务的度量指标。 通常我们使用平均交并比 (mean Intersection over Union, mIoU) 作为 ScanNet 语义分割任务的度量指标。
具体而言,我们先计算所有类别的 IoU,然后取平均值作为 mIoU。 具体而言,我们先计算所有类别的 IoU,然后取平均值作为 mIoU。
更多实现细节请参考 [seg_eval.py](https://github.com/open-mmlab/mmdetection3d/blob/master/mmdet3d/core/evaluation/seg_eval.py) 更多实现细节请参考 [seg_eval.py](https://github.com/open-mmlab/mmdetection3d/blob/dev-1.x/mmdet3d/evaluation/functional/seg_eval.py)
正如在 `提取 S3DIS 数据` 一节中所提及的,S3DIS 通常在 5 个区域上进行训练,然后在余下的 1 个区域上进行测试。但是在其他论文中,也有不同的划分方式。 正如在 `提取 S3DIS 数据` 一节中所提及的,S3DIS 通常在 5 个区域上进行训练,然后在余下的 1 个区域上进行测试。但是在其他论文中,也有不同的划分方式。
为了便于灵活划分训练和测试的子集,我们首先定义子数据集 (sub-dataset) 来表示每一个区域,然后根据区域划分对其进行合并,以得到完整的训练集。 为了便于灵活划分训练和测试的子集,我们首先定义子数据集 (sub-dataset) 来表示每一个区域,然后根据区域划分对其进行合并,以得到完整的训练集。
...@@ -232,31 +229,42 @@ class_names = ('ceiling', 'floor', 'wall', 'beam', 'column', 'window', 'door', ...@@ -232,31 +229,42 @@ class_names = ('ceiling', 'floor', 'wall', 'beam', 'column', 'window', 'door',
'table', 'chair', 'sofa', 'bookcase', 'board', 'clutter') 'table', 'chair', 'sofa', 'bookcase', 'board', 'clutter')
train_area = [1, 2, 3, 4, 6] train_area = [1, 2, 3, 4, 6]
test_area = 5 test_area = 5
data = dict( train_dataloader = dict(
train=dict( batch_size=8,
num_workers=4,
persistent_workers=True,
sampler=dict(type='DefaultSampler', shuffle=True),
dataset=dict(
type=dataset_type, type=dataset_type,
data_root=data_root, data_root=data_root,
ann_files=[ ann_files=[f's3dis_infos_Area_{i}.pkl' for i in train_area],
data_root + f's3dis_infos_Area_{i}.pkl' for i in train_area metainfo=metainfo,
], data_prefix=data_prefix,
pipeline=train_pipeline, pipeline=train_pipeline,
classes=class_names, modality=input_modality,
test_mode=False,
ignore_index=len(class_names), ignore_index=len(class_names),
scene_idxs=[ scene_idxs=[
data_root + f'seg_info/Area_{i}_resampled_scene_idxs.npy' f'seg_info/Area_{i}_resampled_scene_idxs.npy' for i in train_area
for i in train_area ],
]), test_mode=False))
val=dict( test_dataloader = dict(
batch_size=1,
num_workers=1,
persistent_workers=True,
drop_last=False,
sampler=dict(type='DefaultSampler', shuffle=False),
dataset=dict(
type=dataset_type, type=dataset_type,
data_root=data_root, data_root=data_root,
ann_files=data_root + f's3dis_infos_Area_{test_area}.pkl', ann_files=f's3dis_infos_Area_{test_area}.pkl',
metainfo=metainfo,
data_prefix=data_prefix,
pipeline=test_pipeline, pipeline=test_pipeline,
classes=class_names, modality=input_modality,
test_mode=True,
ignore_index=len(class_names), ignore_index=len(class_names),
scene_idxs=data_root + scene_idxs=f'seg_info/Area_{test_area}_resampled_scene_idxs.npy',
f'seg_info/Area_{test_area}_resampled_scene_idxs.npy')) test_mode=True))
val_dataloader = test_dataloader
``` ```
可以看到,我们通过将多个相应路径构成的列表 (list) 输入 `ann_files``scene_idxs` 以实现训练测试集的划分。 可以看到,我们通过将多个相应路径构成的列表 (list) 输入 `ann_files``scene_idxs` 以实现训练测试集的划分。
......
...@@ -227,21 +227,15 @@ scannet ...@@ -227,21 +227,15 @@ scannet
- `semantic_mask/xxxxx.bin`:每个点的语义标签,值的范围为:\[1, 40\], 也就是 `nyu40id` 的标准。请注意:在训练流程 `PointSegClassMapping` 中,`nyu40id` 的 ID 会被映射到训练 ID。 - `semantic_mask/xxxxx.bin`:每个点的语义标签,值的范围为:\[1, 40\], 也就是 `nyu40id` 的标准。请注意:在训练流程 `PointSegClassMapping` 中,`nyu40id` 的 ID 会被映射到训练 ID。
- `posed_images/scenexxxx_xx``.jpg` 图像的集合,还包含 `.txt` 格式的 4x4 相机姿态和单个 `.txt` 格式的相机内参矩阵文件。 - `posed_images/scenexxxx_xx``.jpg` 图像的集合,还包含 `.txt` 格式的 4x4 相机姿态和单个 `.txt` 格式的相机内参矩阵文件。
- `scannet_infos_train.pkl`:训练集的数据信息,每个场景的具体信息如下: - `scannet_infos_train.pkl`:训练集的数据信息,每个场景的具体信息如下:
- info\['point_cloud'\]`{'num_features': 6, 'lidar_idx': sample_idx}`,其中 `sample_idx` 为该场景的索引。 - info\['lidar_points'\]:字典包含与激光雷达点相关的信息。
- info\['pts_path'\]`points/xxxxx.bin` 的路径。 - info\['lidar_points'\]\['lidar_path'\]:点云数据 `xxx.bin` 的文件路径。
- info\['pts_instance_mask_path'\]`instance_mask/xxxxx.bin` 的路径。 - info\['lidar_points'\]\['num_pts_feats'\]:点的特征维度。
- info\['pts_semantic_mask_path'\]`semantic_mask/xxxxx.bin` 的路径。 - info\['lidar_points'\]\['axis_align_matrix'\]:用于对齐坐标轴的变换矩阵。
- info\['annos'\]:每个场景的标注。 - info\['pts_semantic_mask_path'\]:包含语义分割标注的 `xxx.bin` 文件路径。
- annotations\['gt_num'\]:真实物体 (ground truth) 的数量。 - info\['pts_instance_mask_path'\]:包含实例分割标注的 `xxx.bin` 文件路径。
- annotations\['name'\]:所有真实物体的语义类别名称,比如 `chair`(椅子)。 - info\['instances'\]:字典组成的列表,每个字典包含一个实例的所有标注信息。对于其中的第 i 个实例,我们有:
- annotations\['location'\]:depth 坐标系下与坐标轴平行的三维包围框的重力中心 (gravity center),形状为 \[K, 3\],其中 K 是真实物体的数量。 - info\['instances'\]\[i\]\['bbox_3d'\]:长度为 6 的列表,以 (x, y, z, l, w, h) 的顺序表示深度坐标系下与坐标轴平行的 3D 边界框。
- annotations\['dimensions'\]:depth 坐标系下与坐标轴平行的三维包围框的大小,形状为 \[K, 3\] - info\[instances\]\[i\]\['bbox_label_3d'\]:3D 边界框的标签。
- annotations\['gt_boxes_upright_depth'\]:depth 坐标系下与坐标轴平行的三维包围框 `(x, y, z, x_size, y_size, z_size, yaw)`,形状为 \[K, 6\]
- annotations\['unaligned_location'\]:depth 坐标系下与坐标轴不平行(对齐前)的三维包围框的重力中心。
- annotations\['unaligned_dimensions'\]:depth 坐标系下与坐标轴不平行的三维包围框的大小。
- annotations\['unaligned_gt_boxes_upright_depth'\]:depth 坐标系下与坐标轴不平行的三维包围框。
- annotations\['index'\]:所有真实物体的索引,范围为 \[0, K)。
- annotations\['class'\]:所有真实物体类别的标号,范围为 \[0, 18),形状为 \[K, \]
- `scannet_infos_val.pkl`:验证集上的数据信息,与 `scannet_infos_train.pkl` 格式完全一致。 - `scannet_infos_val.pkl`:验证集上的数据信息,与 `scannet_infos_train.pkl` 格式完全一致。
- `scannet_infos_test.pkl`:测试集上的数据信息,与 `scannet_infos_train.pkl` 格式几乎完全一致,除了缺少标注。 - `scannet_infos_test.pkl`:测试集上的数据信息,与 `scannet_infos_train.pkl` 格式几乎完全一致,除了缺少标注。
...@@ -277,9 +271,8 @@ train_pipeline = [ ...@@ -277,9 +271,8 @@ train_pipeline = [
rot_range=[-0.087266, 0.087266], rot_range=[-0.087266, 0.087266],
scale_ratio_range=[1.0, 1.0], scale_ratio_range=[1.0, 1.0],
shift_height=True), shift_height=True),
dict(type='DefaultFormatBundle3D', class_names=class_names),
dict( dict(
type='Collect3D', type='Pack3DDetInputs',
keys=[ keys=[
'points', 'gt_bboxes_3d', 'gt_labels_3d', 'pts_semantic_mask', 'points', 'gt_bboxes_3d', 'gt_labels_3d', 'pts_semantic_mask',
'pts_instance_mask' 'pts_instance_mask'
...@@ -296,6 +289,6 @@ train_pipeline = [ ...@@ -296,6 +289,6 @@ train_pipeline = [
## 评估指标 ## 评估指标
通常 mAP(全类平均精度)被用于 ScanNet 的检测任务的评估,比如 `mAP@0.25``mAP@0.5`。具体来说,评估时一个通用的计算 3D 物体检测多个类别的精度和召回率的函数被调用,可以参考 [indoor_eval](https://github.com/open-mmlab/mmdetection3d/blob/master/mmdet3D/core/evaluation/indoor_eval.py) 通常 mAP(全类平均精度)被用于 ScanNet 的检测任务的评估,比如 `mAP@0.25``mAP@0.5`。具体来说,评估时一个通用的计算 3D 物体检测多个类别的精度和召回率的函数被调用,可以参考 [indoor_eval](https://github.com/open-mmlab/mmdetection3d/blob/dev-1.x/mmdet3d/evaluation/functional/indoor_eval.py)
与在章节`提取 ScanNet 数据` 中介绍的那样,所有真实物体的三维包围框是与坐标轴平行的,也就是说旋转角为 0。因此,预测包围框的网络接受的包围框旋转角监督也是 0,且在后处理阶段我们使用适用于与坐标轴平行的包围框的非极大值抑制 (NMS) ,该过程不会考虑包围框的旋转。 与在章节`提取 ScanNet 数据` 中介绍的那样,所有真实物体的三维包围框是与坐标轴平行的,也就是说旋转角为 0。因此,预测包围框的网络接受的包围框旋转角监督也是 0,且在后处理阶段我们使用适用于与坐标轴平行的包围框的非极大值抑制 (NMS) ,该过程不会考虑包围框的旋转。
...@@ -2,7 +2,7 @@ ...@@ -2,7 +2,7 @@
## 数据集的准备 ## 数据集的准备
ScanNet 3D 语义分割数据集的准备和 3D 检测任务的准备很相似,请查看[此文档](https://github.com/open-mmlab/mmdetection3d/blob/master/docs_zh-CN/datasets/scannet_det.md#dataset-preparation)以获取更多细节。 ScanNet 3D 语义分割数据集的准备和 3D 检测任务的准备很相似,请查看[此文档](https://github.com/open-mmlab/mmdetection3d/blob/dev-1.x/docs/zh_cn/advanced_guides/datasets/scannet_det.md#%E6%95%B0%E6%8D%AE%E9%9B%86%E5%87%86%E5%A4%87)以获取更多细节。
以下我们只罗列部分 3D 语义分割特有的处理步骤和数据信息。 以下我们只罗列部分 3D 语义分割特有的处理步骤和数据信息。
### 提取 ScanNet 数据 ### 提取 ScanNet 数据
...@@ -102,8 +102,7 @@ train_pipeline = [ ...@@ -102,8 +102,7 @@ train_pipeline = [
enlarge_size=0.2, enlarge_size=0.2,
min_unique_num=None), min_unique_num=None),
dict(type='NormalizePointsColor', color_mean=None), dict(type='NormalizePointsColor', color_mean=None),
dict(type='DefaultFormatBundle3D', class_names=class_names), dict(type='Pack3DDetInputs', keys=['points', 'pts_semantic_mask'])
dict(type='Collect3D', keys=['points', 'pts_semantic_mask'])
] ]
``` ```
...@@ -115,7 +114,7 @@ train_pipeline = [ ...@@ -115,7 +114,7 @@ train_pipeline = [
通常我们使用平均交并比 (mean Intersection over Union, mIoU) 作为 ScanNet 语义分割任务的度量指标。 通常我们使用平均交并比 (mean Intersection over Union, mIoU) 作为 ScanNet 语义分割任务的度量指标。
具体而言,我们先计算所有类别的 IoU,然后取平均值作为 mIoU。 具体而言,我们先计算所有类别的 IoU,然后取平均值作为 mIoU。
更多实现细节请参考 [seg_eval.py](https://github.com/open-mmlab/mmdetection3d/blob/master/mmdet3d/core/evaluation/seg_eval.py) 更多实现细节请参考 [seg_eval.py](https://github.com/open-mmlab/mmdetection3d/blob/dev-1.x/mmdet3d/evaluation/functional/seg_eval.py)
## 在测试集上测试并提交结果 ## 在测试集上测试并提交结果
......
...@@ -133,92 +133,7 @@ python tools/create_data.py sunrgbd --root-path ./data/sunrgbd \ ...@@ -133,92 +133,7 @@ python tools/create_data.py sunrgbd --root-path ./data/sunrgbd \
bash tools/create_data.sh <job_name> sunrgbd bash tools/create_data.sh <job_name> sunrgbd
``` ```
之前提到的点云数据就会被处理并以 `.bin` 格式重新存储。与此同时,`.pkl` 文件也被生成,用于存储数据标注和元信息。这一步处理中,用于生成 `.pkl` 文件的核心函数 `process_single_scene` 如下: 之前提到的点云数据就会被处理并以 `.bin` 格式重新存储。与此同时,`.pkl` 文件也被生成,用于存储数据标注和元信息。
```python
def process_single_scene(sample_idx):
print(f'{self.split} sample_idx: {sample_idx}')
# 将深度图转换为点云并降采样点云
pc_upright_depth = self.get_depth(sample_idx)
pc_upright_depth_subsampled = random_sampling(
pc_upright_depth, self.num_points)
info = dict()
pc_info = {'num_features': 6, 'lidar_idx': sample_idx}
info['point_cloud'] = pc_info
# 将点云保存为 `.bin` 格式
mmengine.mkdir_or_exist(osp.join(self.root_dir, 'points'))
pc_upright_depth_subsampled.tofile(
osp.join(self.root_dir, 'points', f'{sample_idx:06d}.bin'))
# 存储点云存储路径
info['pts_path'] = osp.join('points', f'{sample_idx:06d}.bin')
# 存储图像存储路径以及其元信息
img_path = osp.join('image', f'{sample_idx:06d}.jpg')
image_info = {
'image_idx': sample_idx,
'image_shape': self.get_image_shape(sample_idx),
'image_path': img_path
}
info['image'] = image_info
# 保存标定信息
K, Rt = self.get_calibration(sample_idx)
calib_info = {'K': K, 'Rt': Rt}
info['calib'] = calib_info
# 保存所有数据标注
if has_label:
obj_list = self.get_label_objects(sample_idx)
annotations = {}
annotations['gt_num'] = len([
obj.classname for obj in obj_list
if obj.classname in self.cat2label.keys()
])
if annotations['gt_num'] != 0:
# 类别名称
annotations['name'] = np.array([
obj.classname for obj in obj_list
if obj.classname in self.cat2label.keys()
])
# 二维图像包围框
annotations['bbox'] = np.concatenate([
obj.box2d.reshape(1, 4) for obj in obj_list
if obj.classname in self.cat2label.keys()
], axis=0)
# depth 坐标系下的三维包围框中心坐标
annotations['location'] = np.concatenate([
obj.centroid.reshape(1, 3) for obj in obj_list
if obj.classname in self.cat2label.keys()
], axis=0)
# depth 坐标系下的三维包围框大小
annotations['dimensions'] = 2 * np.array([
[obj.l, obj.h, obj.w] for obj in obj_list
if obj.classname in self.cat2label.keys()
])
# depth 坐标系下的三维包围框旋转角
annotations['rotation_y'] = np.array([
obj.heading_angle for obj in obj_list
if obj.classname in self.cat2label.keys()
])
annotations['index'] = np.arange(
len(obj_list), dtype=np.int32)
# 类别标签(数字)
annotations['class'] = np.array([
self.cat2label[obj.classname] for obj in obj_list
if obj.classname in self.cat2label.keys()
])
# depth 坐标系下的三维包围框
annotations['gt_boxes_upright_depth'] = np.stack(
[
obj.box3d for obj in obj_list
if obj.classname in self.cat2label.keys()
], axis=0) # (K,8)
info['annos'] = annotations
return info
```
如上数据处理后,文件目录结构应如下: 如上数据处理后,文件目录结构应如下:
...@@ -236,24 +151,21 @@ sunrgbd ...@@ -236,24 +151,21 @@ sunrgbd
├── sunrgbd_infos_val.pkl ├── sunrgbd_infos_val.pkl
``` ```
- `points/0xxxxx.bin`:降采样后的点云数据。 - `points/xxxxxx.bin`:降采样后的点云数据。
- `sunrgbd_infos_train.pkl`:训练集数据信息(标注与元信息),每个场景所含数据信息具体如下: - `sunrgbd_infos_train.pkl`:训练集数据信息(标注与元信息),每个场景所含数据信息具体如下:
- info\['point_cloud'\]`{'num_features': 6, 'lidar_idx': sample_idx}`,其中 `sample_idx` 为该场景的索引。 - info\['lidar_points'\]:字典包含了与激光雷达点相关的信息。
- info\['pts_path'\]`points/0xxxxx.bin` 的路径。 - info\['lidar_points'\]\['num_pts_feats'\]:点的特征维度。
- info\['image'\]:图像路径与元信息: - info\['lidar_points'\]\['lidar_path'\]:点云数据 `xxx.bin` 的文件路径。
- image\['image_idx'\]:图像索引。 - info\['images'\]:字典包含了与图像数据相关的信息。
- image\['image_shape'\]:图像张量的形状(即其尺寸)。 - info\['images'\]\['CAM0'\]\['img_path'\]:图像的文件名。
- image\['image_path'\]:图像路径。 - info\['images'\]\['CAM0'\]\['depth2img'\]:深度到图像的变换矩阵,形状为 (4, 4)。
- info\['annos'\]:每个场景的标注: - info\['images'\]\['CAM0'\]\['height'\]:图像的高。
- annotations\['gt_num'\]:真实物体 (ground truth) 的数量。 - info\['images'\]\['CAM0'\]\['width'\]:图像的宽。
- annotations\['name'\]:所有真实物体的语义类别名称,比如 `chair`(椅子)。 - info\['instances'\]:由字典组成的列表,包含了该帧的所有标注信息。每个字典与单个实例的标注相关。对于其中的第 i 个实例,我们有:
- annotations\['location'\]:depth 坐标系下三维包围框的重力中心 (gravity center),形状为 \[K, 3\],其中 K 是真实物体的数量。 - info\['instances'\]\[i\]\['bbox_3d'\]:长度为 7 的列表,表示深度坐标系下的 3D 边界框。
- annotations\['dimensions'\]:depth 坐标系下三维包围框的大小,形状为 \[K, 3\] - info\['instances'\]\[i\]\['bbox'\]:长度为 4 的列表,以 (x1, y1, x2, y2) 的顺序表示实例的 2D 边界框。
- annotations\['rotation_y'\]:depth 坐标系下三维包围框的旋转角,形状为 \[K, \] - info\['instances'\]\[i\]\['bbox_label_3d'\]:整数表示实例的 3D 标签,-1 表示忽略该类别。
- annotations\['gt_boxes_upright_depth'\]:depth 坐标系下三维包围框 `(x, y, z, x_size, y_size, z_size, yaw)`,形状为 \[K, 7\] - info\['instances'\]\[i\]\['bbox_label'\]:整数表示实例的 2D 标签,-1 表示忽略该类别。
- annotations\['bbox'\]:二维包围框 `(x, y, x_size, y_size)`,形状为 \[K, 4\]
- annotations\['index'\]:所有真实物体的索引,范围为 \[0, K)。
- annotations\['class'\]:所有真实物体类别的标号,范围为 \[0, 10),形状为 \[K, \]
- `sunrgbd_infos_val.pkl`:验证集上的数据信息,与 `sunrgbd_infos_train.pkl` 格式完全一致。 - `sunrgbd_infos_val.pkl`:验证集上的数据信息,与 `sunrgbd_infos_train.pkl` 格式完全一致。
## 训练流程 ## 训练流程
...@@ -280,8 +192,9 @@ train_pipeline = [ ...@@ -280,8 +192,9 @@ train_pipeline = [
scale_ratio_range=[0.85, 1.15], scale_ratio_range=[0.85, 1.15],
shift_height=True), shift_height=True),
dict(type='PointSample', num_points=20000), dict(type='PointSample', num_points=20000),
dict(type='DefaultFormatBundle3D', class_names=class_names), dict(
dict(type='Collect3D', keys=['points', 'gt_bboxes_3d', 'gt_labels_3d']) type='Pack3DDetInputs',
keys=['points', 'gt_bboxes_3d', 'gt_labels_3d'])
] ]
``` ```
...@@ -304,9 +217,8 @@ train_pipeline = [ ...@@ -304,9 +217,8 @@ train_pipeline = [
dict(type='LoadImageFromFile'), dict(type='LoadImageFromFile'),
dict(type='LoadAnnotations3D'), dict(type='LoadAnnotations3D'),
dict(type='LoadAnnotations', with_bbox=True), dict(type='LoadAnnotations', with_bbox=True),
dict(type='Resize', img_scale=(1333, 600), keep_ratio=True), dict(type='Resize', scale=(1333, 600), keep_ratio=True),
dict(type='RandomFlip', flip_ratio=0.0), dict(type='RandomFlip', flip_ratio=0.0),
dict(type='Normalize', **img_norm_cfg),
dict(type='Pad', size_divisor=32), dict(type='Pad', size_divisor=32),
dict( dict(
type='RandomFlip3D', type='RandomFlip3D',
...@@ -318,28 +230,21 @@ train_pipeline = [ ...@@ -318,28 +230,21 @@ train_pipeline = [
rot_range=[-0.523599, 0.523599], rot_range=[-0.523599, 0.523599],
scale_ratio_range=[0.85, 1.15], scale_ratio_range=[0.85, 1.15],
shift_height=True), shift_height=True),
dict(type='PointSample', num_points=20000),
dict(type='DefaultFormatBundle3D', class_names=class_names),
dict( dict(
type='Collect3D', type='Pack3DDetInputs',
keys=[ keys=['points', 'gt_bboxes_3d', 'gt_labels_3d','img', 'gt_bboxes', 'gt_bboxes_labels'])
'img', 'gt_bboxes', 'gt_labels', 'points', 'gt_bboxes_3d',
'gt_labels_3d'
])
] ]
``` ```
图像上的数据增强/归一化 图像上的数据增强
- `Resize`: 改变输入图像的大小, `keep_ratio=True` 意味着图像的比例不改变。 - `Resize`: 改变输入图像的大小, `keep_ratio=True` 意味着图像的比例不改变。
- `Normalize`: 归一化图像的 RGB 通道。
- `RandomFlip`: 随机地翻折图像。 - `RandomFlip`: 随机地翻折图像。
- `Pad`: 扩大图像,默认情况下用零填充图像的边缘。
图像增强和归一化函数的实现取自 [MMDetection](https://github.com/open-mmlab/mmdetection/tree/master/mmdet/datasets/pipelines) 图像增强的实现取自 [MMDetection](https://github.com/open-mmlab/mmdetection/tree/dev-3.x/mmdet/datasets/transforms)
## 度量指标 ## 度量指标
与 ScanNet 一样,通常 mAP(全类平均精度)被用于 SUN RGB-D 的检测任务的评估,比如 `mAP@0.25``mAP@0.5`。具体来说,评估时一个通用的计算 3D 物体检测多个类别的精度和召回率的函数被调用,可以参考 [`indoor_eval.py`](https://github.com/open-mmlab/mmdetection3d/blob/master/mmdet3d/core/evaluation/indoor_eval.py) 与 ScanNet 一样,通常 mAP(全类平均精度)被用于 SUN RGB-D 的检测任务的评估,比如 `mAP@0.25``mAP@0.5`。具体来说,评估时一个通用的计算 3D 物体检测多个类别的精度和召回率的函数被调用,可以参考 [`indoor_eval.py`](https://github.com/open-mmlab/mmdetection3d/blob/dev-1.x/mmdet3d/evaluation/functional/indoor_eval.py)
因为 SUN RGB-D 包含有图像数据,所以图像上的物体检测也是可行的。举个例子,在 ImVoteNet 中,我们首先训练了一个图像检测器,并且也使用 mAP 指标,如 `mAP@0.5`,来评估其表现。我们使用 [MMDetection](https://github.com/open-mmlab/mmdetection) 库中的 `eval_map` 函数来计算 mAP。 因为 SUN RGB-D 包含有图像数据,所以图像上的物体检测也是可行的。举个例子,在 ImVoteNet 中,我们首先训练了一个图像检测器,并且也使用 mAP 指标,如 `mAP@0.5`,来评估其表现。我们使用 [MMDetection](https://github.com/open-mmlab/mmdetection) 库中的 `eval_map` 函数来计算 mAP。
...@@ -101,7 +101,7 @@ mmdetection3d ...@@ -101,7 +101,7 @@ mmdetection3d
## 评估 ## 评估
为了在 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`,然后构建二进制文件: 为了在 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`,然后构建二进制文件:
```shell ```shell
# download the code and enter the base directory # download the code and enter the base directory
...@@ -122,19 +122,16 @@ sudo apt install build-essential ...@@ -122,19 +122,16 @@ sudo apt install build-essential
bazel clean bazel clean
bazel build waymo_open_dataset/metrics/tools/compute_detection_metrics_main bazel build waymo_open_dataset/metrics/tools/compute_detection_metrics_main
cp bazel-bin/waymo_open_dataset/metrics/tools/compute_detection_metrics_main ../mmdetection3d/mmdet3d/core/evaluation/waymo_utils/ cp bazel-bin/waymo_open_dataset/metrics/tools/compute_detection_metrics_main ../mmdetection3d/mmdet3d/evaluation/functional/waymo_utils/
``` ```
接下来,您就可以在 Waymo 上评估您的模型了。如下示例是使用 8 个图形处理器 (GPU) 在 Waymo 上用 Waymo 评价指标评估 PointPillars 模型的情景: 接下来,您就可以在 Waymo 上评估您的模型了。如下示例是使用 8 个图形处理器 (GPU) 在 Waymo 上用 Waymo 评价指标评估 PointPillars 模型的情景:
```shell ```shell
./tools/slurm_test.sh ${PARTITION} ${JOB_NAME} configs/pointpillars/hv_pointpillars_secfpn_sbn-2x16_2x_waymo-3d-car.py \ ./tools/dist_test.sh 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
checkpoints/hv_pointpillars_secfpn_sbn-2x16_2x_waymo-3d-car_latest.pth --out results/waymo-car/results_eval.pkl \
--eval waymo --eval-options 'pklfile_prefix=results/waymo-car/kitti_results' \
'submission_prefix=results/waymo-car/kitti_results'
``` ```
如果需要生成 bin 文件,应在 `--eval-options`给出 `pklfile_prefix`。对于评价指标, `waymo` 是我们推荐的官方评估原型。目前,`kitti` 这一评估选项是从 KITTI 迁移而来的,且每个难度下的评估结果和 KITTI 数据集中定义得到的不尽相同——目前大多数物体被标记为难度 0(日后会修复)。`kitti` 评估选项的不稳定来源于很大的计算量,转换的数据中遮挡 (occlusion) 和截断 (truncation) 的缺失,难度的不同定义方式,以及不同的平均精度 (Average Precision) 计算方式 如果需要生成 bin 文件,需要在配置文件的 `test_evaluator`指定 `pklfile_prefix`,因此你可以在命令后添加 `--cfg-options "test_evaluator.pklfile_prefix=xxxx"`
**注意**: **注意**:
...@@ -148,25 +145,20 @@ cp bazel-bin/waymo_open_dataset/metrics/tools/compute_detection_metrics_main ../ ...@@ -148,25 +145,20 @@ cp bazel-bin/waymo_open_dataset/metrics/tools/compute_detection_metrics_main ../
如下是一个使用 8 个图形处理器在 Waymo 上测试 PointPillars,生成 bin 文件并提交结果到官方榜单的例子: 如下是一个使用 8 个图形处理器在 Waymo 上测试 PointPillars,生成 bin 文件并提交结果到官方榜单的例子:
```shell 如果你想生成 bin 文件并提交到服务器中,在运行测试指令前你需要在配置文件的 `test_evaluator` 中指定 `submission_prefix`
./tools/slurm_test.sh ${PARTITION} ${JOB_NAME} configs/pointpillars/hv_pointpillars_secfpn_sbn-2x16_2x_waymo-3d-car.py \
checkpoints/hv_pointpillars_secfpn_sbn-2x16_2x_waymo-3d-car_latest.pth --out results/waymo-car/results_eval.pkl \
--format-only --eval-options 'pklfile_prefix=results/waymo-car/kitti_results' \
'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/)创建一个提交文件。下面是一些示例:
```shell ```shell
cd ../waymo-od/ cd ../waymo-od/
bazel build waymo_open_dataset/metrics/tools/create_submission bazel build waymo_open_dataset/metrics/tools/create_submission
cp bazel-bin/waymo_open_dataset/metrics/tools/create_submission ../mmdetection3d/mmdet3d/core/evaluation/waymo_utils/ cp bazel-bin/waymo_open_dataset/metrics/tools/create_submission ../mmdetection3d/mmdet3d/core/evaluation/waymo_utils/
vim waymo_open_dataset/metrics/tools/submission.txtpb # set the metadata information vim waymo_open_dataset/metrics/tools/submission.txtpb # set the metadata information
cp waymo_open_dataset/metrics/tools/submission.txtpb ../mmdetection3d/mmdet3d/core/evaluation/waymo_utils/ cp waymo_open_dataset/metrics/tools/submission.txtpb ../mmdetection3d/mmdet3d/evaluation/functional/waymo_utils/
cd ../mmdetection3d cd ../mmdetection3d
# suppose the result bin is in `results/waymo-car/submission` # suppose the result bin is in `results/waymo-car/submission`
mmdet3d/core/evaluation/waymo_utils/create_submission --input_filenames='results/waymo-car/kitti_results_test.bin' --output_filename='results/waymo-car/submission/model' --submission_filename='mmdet3d/core/evaluation/waymo_utils/submission.txtpb' mmdet3d/core/evaluation/waymo_utils/create_submission --input_filenames='results/waymo-car/kitti_results_test.bin' --output_filename='results/waymo-car/submission/model' --submission_filename='mmdet3d/evaluation/functional/waymo_utils/submission.txtpb'
tar cvf results/waymo-car/submission/my_model.tar results/waymo-car/submission/my_model/ tar cvf results/waymo-car/submission/my_model.tar results/waymo-car/submission/my_model/
gzip results/waymo-car/submission/my_model.tar gzip results/waymo-car/submission/my_model.tar
......
# 依赖 # 依赖
在本节中,我们将展示如何使用 PyTorch 准备环境。
MMDetection3D 可以安装在 Linux, MacOS, (实验性支持 Windows) 的平台上,它具体需要下列安装包: MMDetection3D 可以安装在 Linux, MacOS, (实验性支持 Windows) 的平台上,它具体需要下列安装包:
- Python 3.6+ - Python 3.6+
- PyTorch 1.3+ - PyTorch 1.6+
- CUDA 9.2+ (如果你从源码编译 PyTorch, CUDA 9.0 也是兼容的。) - CUDA 9.2+ (如果你从源码编译 PyTorch, CUDA 9.0 也是兼容的。)
- GCC 5+ - GCC 5+
- [MMEngine](https://mmengine.readthedocs.io/en/latest/#installation)
- [MMCV](https://mmcv.readthedocs.io/en/latest/#installation) - [MMCV](https://mmcv.readthedocs.io/en/latest/#installation)
```{note} ```{note}
如果你已经装了 pytorch, 可以跳过这一部分,然后转到[下一章节](#安装). 如果没有,可以参照以下步骤安装环境。 如果你已经装了 pytorch, 可以跳过这一部分,然后转到[下一章节](#安装). 如果没有,可以参照以下步骤安装环境。
``` ```
**步骤 0.** 安装 MiniConda [官网](https://docs.conda.io/en/latest/miniconda.html). **步骤 0.** [官网](https://docs.conda.io/en/latest/miniconda.html)下载并安装 Miniconda。
**步骤 1.** 使用 conda 新建虚拟环境,并进入该虚拟环境. **步骤 1.** 使用 conda 新建虚拟环境,并进入该虚拟环境
```shell ```shell
# 鉴于 waymo-open-dataset-tf-2-6-0 要求 python>=3.7, 我们推荐安装 python3.8 # 鉴于 waymo-open-dataset-tf-2-6-0 要求 python>=3.7, 我们推荐安装 python3.8
...@@ -23,15 +25,15 @@ conda create --name openmmlab python=3.8 -y ...@@ -23,15 +25,15 @@ conda create --name openmmlab python=3.8 -y
conda activate openmmlab conda activate openmmlab
``` ```
**步骤 2.** 基于 [PyTorch 官网](https://pytorch.org/)安装 PyTorch 和 torchvision,例如: **步骤 2.** 基于 [PyTorch 官网](https://pytorch.org/)安装 PyTorch,例如:
GPU 环境下 GPU 环境下
```shell ```shell
conda install pytorch torchvision -c pytorch conda install pytorch torchvision -c pytorch
``` ```
CPU 环境下 CPU 环境下
```shell ```shell
conda install pytorch torchvision cpuonly -c pytorch conda install pytorch torchvision cpuonly -c pytorch
...@@ -43,67 +45,61 @@ conda install pytorch torchvision cpuonly -c pytorch ...@@ -43,67 +45,61 @@ conda install pytorch torchvision cpuonly -c pytorch
## 最佳实践 ## 最佳实践
如果你已经成功安装 CUDA 11.0,那么你可以使用这个快速安装命令进行 MMDetection3D 的安装。 否则,则参考下一小节的详细安装流程。 如果你已经成功安装 CUDA 11.0,那么你可以使用这个快速安装命令进行 MMDetection3D 的安装。否则,则参考下一小节的详细安装流程。
```shell ```shell
pip install openmim pip install openmim
mim install mmcv-full mim install mmengine
mim install mmdet mim install 'mmcv>=2.0.0rc0'
mim install mmsegmentation mim install 'mmdet>=3.0.0rc0'
git clone https://github.com/open-mmlab/mmdetection3d.git git clone https://github.com/open-mmlab/mmdetection3d.git -b dev-1.x
cd mmdetection3d cd mmdetection3d
pip install -e . pip install -e .
``` ```
**步骤 0. 通过[MIM](https://github.com/open-mmlab/mim) 安装 [MMCV](https://github.com/open-mmlab/mmcv).** **步骤 0.** 通过 [MIM](https://github.com/open-mmlab/mim) 安装 [MMEngine](https://github.com/open-mmlab/mmengine)[MMCV](https://github.com/open-mmlab/mmcv)
**步骤 1. 安装 [MMDetection](https://github.com/open-mmlab/mmdetection).**
```shell ```shell
pip install mmdet pip install -U openmim
mim install mmengine
mim install 'mmcv>=2.0.0rc0'
``` ```
同时,如果你想修改这部分的代码,也可以通过以下命令从源码编译 MMDetection: **步骤 1.** 安装 [MMDetection](https://github.com/open-mmlab/mmdetection)
```shell ```shell
git clone https://github.com/open-mmlab/mmdetection.git mim install 'mmdet>=3.0.0rc0'
cd mmdetection
git checkout v2.24.0 # switch to v2.24.0 branch
pip install -r requirements/build.txt
pip install -v -e . # or "python setup.py develop"
``` ```
**步骤 2. 安装 [MMSegmentation](https://github.com/open-mmlab/mmsegmentation).** 同时,如果你想修改这部分的代码,也可以通过以下命令从源码编译 MMDetection:
```shell
pip install mmsegmentation
```
同时,如果你想修改这部分的代码,也可以通过以下命令从源码编译 MMSegmentation:
```shell ```shell
git clone https://github.com/open-mmlab/mmsegmentation.git git clone https://github.com/open-mmlab/mmdetection.git -b dev-3.x
cd mmsegmentation # "-b dev-3.x" means checkout to the `dev-3.x` branch.
git checkout v0.20.0 # switch to v0.20.0 branch cd mmdetection
pip install -e . # or "python setup.py develop" pip install -v -e .
# "-v" means verbose, or more output
# "-e" means installing a project in editable mode,
# thus any local modifications made to the code will take effect without reinstallation.
``` ```
**步骤 3. 克隆 MMDetection3D 代码仓库.** **步骤 2.** 克隆 MMDetection3D 代码仓库
```shell ```shell
git clone https://github.com/open-mmlab/mmdetection3d.git git clone https://github.com/open-mmlab/mmdetection3d.git -b dev-1.x
# "-b dev-1.x" means checkout to the `dev-1.x` branch.
cd mmdetection3d cd mmdetection3d
``` ```
**步骤 4. 安装依赖包和 MMDetection3D.** **步骤 4.** 安装依赖包和 MMDetection3D
```shell ```shell
pip install -v -e . # or "python setup.py develop" pip install -v -e . # or "python setup.py develop"
``` ```
**注意:** 注意:
1. Git 的 commit id 在步骤 d 将会被写入到版本号当中,例 0.6.0+2e7045c 。版本号将保存在训练的模型里。推荐在每一次执行步骤 d 时,从 github 上获取最新的更新。如果基于 C++/CUDA 的代码被修改了,请执行以下步骤; 1. Git 的 commit id 在步骤 d 将会被写入到版本号当中,例 0.6.0+2e7045c。版本号将保存在训练的模型里。推荐在每一次执行步骤 4 时,从 github 上获取最新的更新。如果基于 C++/CUDA 的代码被修改了,请执行以下步骤;
> 重要: 如果你重装了不同版本的 CUDA 或者 PyTorch 的 mmdet,请务必移除 `./build` 文件。 > 重要: 如果你重装了不同版本的 CUDA 或者 PyTorch 的 mmdet,请务必移除 `./build` 文件。
...@@ -126,13 +122,13 @@ pip install -v -e . # or "python setup.py develop" ...@@ -126,13 +122,13 @@ pip install -v -e . # or "python setup.py develop"
pip install spconv-cuxxx pip install spconv-cuxxx
``` ```
xxx 表示 CUDA 的版本。 xxx 表示 CUDA 的版本。
例如, 使用 CUDA 10.2, 对应命令是 `pip install cumm-cu102 && pip install spconv-cu102`. 例如使用 CUDA 10.2, 对应命令是 `pip install cumm-cu102 && pip install spconv-cu102`
支持的 CUDA 版本包括 10.2, 11.1, 11.3, and 11.4. 用户可以通过源码编译来在这些版本上安装. 具体细节请参考 [spconv v2.x](https://github.com/traveller59/spconv). 支持的 CUDA 版本包括 10.211.111.3 11.4用户可以通过源码编译来在这些版本上安装具体细节请参考 [spconv v2.x](https://github.com/traveller59/spconv)
我们同时也支持 Minkowski Engine 来作为稀疏卷的后端. 如果需要,可以参照 [安装指南](https://github.com/NVIDIA/MinkowskiEngine#installation) 或使用 `pip`: 我们同时也支持 Minkowski Engine 来作为稀疏卷的后端如果需要,可以参照[安装指南](https://github.com/NVIDIA/MinkowskiEngine#installation)或使用 `pip`
```shell ```shell
conda install openblas-devel -c anaconda conda install openblas-devel -c anaconda
...@@ -141,9 +137,9 @@ pip install -v -e . # or "python setup.py develop" ...@@ -141,9 +137,9 @@ pip install -v -e . # or "python setup.py develop"
5. 我们的代码目前不能在只有 CPU 的环境(CUDA 不可用)下编译运行。 5. 我们的代码目前不能在只有 CPU 的环境(CUDA 不可用)下编译运行。
# 验证 ## 验证
## 通过点云样例程序来验证 ### 通过点云样例程序来验证
我们提供了一些样例脚本去测试单个样本,预训练的模型可以从[模型库](model_zoo.md)中下载. 运行如下命令可以去测试点云场景下一个单模态的 3D 检测算法。 我们提供了一些样例脚本去测试单个样本,预训练的模型可以从[模型库](model_zoo.md)中下载. 运行如下命令可以去测试点云场景下一个单模态的 3D 检测算法。
...@@ -151,14 +147,13 @@ pip install -v -e . # or "python setup.py develop" ...@@ -151,14 +147,13 @@ pip install -v -e . # or "python setup.py develop"
python demo/pcd_demo.py ${PCD_FILE} ${CONFIG_FILE} ${CHECKPOINT_FILE} [--device ${GPU_ID}] [--score-thr ${SCORE_THR}] [--out-dir ${OUT_DIR}] python demo/pcd_demo.py ${PCD_FILE} ${CONFIG_FILE} ${CHECKPOINT_FILE} [--device ${GPU_ID}] [--score-thr ${SCORE_THR}] [--out-dir ${OUT_DIR}]
``` ```
: 如:
```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
``` ```
如果你想输入一个 `ply` 格式的文件,你可以使用如下函数将它转换为 `bin` 的文件格式。然后就可以使用转化成 `bin` 格式的文件去运行样例程序。 如果你想输入一个 `ply` 格式的文件,你可以使用如下函数将它转换为 `bin` 的文件格式。然后就可以使用转化成 `bin` 格式的文件去运行样例程序。
请注意在使用此脚本前,你需要先安装 `pandas``plyfile`。 这个函数也可使用在数据预处理当中,为了能够直接训练 `ply data` 请注意在使用此脚本前,你需要先安装 `pandas``plyfile`。 这个函数也可使用在数据预处理当中,为了能够直接训练 `ply data`
```python ```python
...@@ -178,13 +173,13 @@ def convert_ply(input_path, output_path): ...@@ -178,13 +173,13 @@ def convert_ply(input_path, output_path):
data_np.astype(np.float32).tofile(output_path) data_np.astype(np.float32).tofile(output_path)
``` ```
: 如:
```python ```python
convert_ply('./test.ply', './test.bin') convert_ply('./test.ply', './test.bin')
``` ```
如果你有其他格式的点云文件 (例:`off`, `obj`), 你可以使用 `trimesh` 将它们转化成 `ply`. 如果你有其他格式的点云文件 (例:`off``obj`)你可以使用 `trimesh` 将它们转化成 `ply`
```python ```python
import trimesh import trimesh
...@@ -194,19 +189,22 @@ def to_ply(input_path, output_path, original_type): ...@@ -194,19 +189,22 @@ def to_ply(input_path, output_path, original_type):
mesh.export(output_path, file_type='ply') # convert to ply mesh.export(output_path, file_type='ply') # convert to ply
``` ```
: 如:
```python ```python
to_ply('./test.obj', './test.ply', 'obj') to_ply('./test.obj', './test.ply', 'obj')
``` ```
更多的关于单/多模态和室内/室外的 3D 检测的样例可以在[](demo.md)找到. 更多的关于单/多模态和室内/室外的 3D 检测的样例可以在[](user_guides/inference.md)找到
## 测试点云的高级接口 ## 自定义安装
### 同步接口 ### CUDA 版本
当安装 PyTorch,你需要指定 CUDA 的版本。如果你不清楚选择哪个版本,可以参考我们的建议:
这里有一个例子去说明如何构建模型以及测试给出的点云: - 对于 Ampere 架构的英伟达显卡,例如 GeForce 30 系列以及 NVIDIA A100,CUDA 11 是必须的。
- 对于较旧的英伟达显卡,CUDA 11 是向后兼容的,但 CUDA 10.2 提供更好的兼容性,并且更轻量。
```python ```python
from mmdet3d.apis import init_model, inference_detector from mmdet3d.apis import init_model, inference_detector
...@@ -239,25 +237,35 @@ model.show_results(data, result, out_dir='results') ...@@ -239,25 +237,35 @@ model.show_results(data, result, out_dir='results')
如果你参照最佳实践,你只需要安装 CUDA runtime libraries。 这是因为没有代码需要在本地通过 CUDA 编译。然而如果你需要编译MMCV源码,或者编译其他 CUDA 代码,你需要基于 NVIDIA [website](https://developer.nvidia.com/cuda-downloads) 安装完整的 CUDA toolkit,并且要保证它的版本跟 PyTorch 匹配。比如在 'conda install` 里对应的 cudatoolkit 版本。 如果你参照最佳实践,你只需要安装 CUDA runtime libraries。 这是因为没有代码需要在本地通过 CUDA 编译。然而如果你需要编译MMCV源码,或者编译其他 CUDA 代码,你需要基于 NVIDIA [website](https://developer.nvidia.com/cuda-downloads) 安装完整的 CUDA toolkit,并且要保证它的版本跟 PyTorch 匹配。比如在 'conda install` 里对应的 cudatoolkit 版本。
``` ```
### 不通过MIM 安装MMCV ### 不通过 MIM 安装 MMEngine
如果想要使用 pip 而不是 MIM 安装 MMEngine, 请参考 [MMEngine 安装指南](https://mmengine.readthedocs.io/zh_CN/latest/get_started/installation.html)
例如,你可以通过以下指令安装 MMEngine。
```shell
pip install mmengine
```
### 不通过 MIM 安装 MMCV
MMCV 包含一些 C++ 和 CUDA 扩展,因此以复杂的方式依赖于 PyTorch。 MIM 会自动解决此类依赖关系并使安装更容易。但是,这不是必须的。 MMCV 包含一些 C++ 和 CUDA 扩展因此以复杂的方式依赖于 PyTorch。MIM 会自动解决此类依赖关系并使安装更容易。但是,这不是必须的。
如果想要使用 pip 而不是 MIM 安装 MMCV, 请参考 [MMCV 安装指南](https://mmcv.readthedocs.io/en/latest/get_started/installation.html). 这需要根据 PyTorch 版本及其 CUDA 版本手动指定 find-url。 如果想要使用 pip 而不是 MIM 安装 MMCV请参考 [MMCV 安装指南](https://mmcv.readthedocs.io/zh_CN/latest/get_started/installation.html)这需要根据 PyTorch 版本及其 CUDA 版本手动指定 find-url。
例如, 下面的脚本安装 的 mmcv-full 是对应的 PyTorch 1.10.x 和 CUDA 11.3. 例如下面的脚本安装 的 mmcv 是对应的 PyTorch 1.10.x 和 CUDA 11.3
```shell ```shell
pip install mmcv-full -f https://download.openmmlab.com/mmcv/dist/cu113/torch1.10/index.html pip install mmcv -f https://download.openmmlab.com/mmcv/dist/cu113/torch1.10/index.html
``` ```
### 通过Docker 安装 ### 通过 Docker 安装 MMDetection3D
我们提供了 [Dockerfile](https://github.com/open-mmlab/mmdetection3d/blob/master/docker/Dockerfile) 来建立一个镜像。 我们提供了 [Dockerfile](https://github.com/open-mmlab/mmdetection3d/blob/master/docker/Dockerfile) 来建立一个镜像。
```shell ```shell
# 基于 PyTorch 1.6, CUDA 10.1 生成 docker 的镜像 # 基于 PyTorch 1.6, CUDA 10.1 生成 docker 的镜像
docker build -t mmdetection3d docker/ docker build -t mmdetection3d -f docker/Dockerfile .
``` ```
运行命令: 运行命令:
...@@ -279,22 +287,21 @@ conda activate open-mmlab ...@@ -279,22 +287,21 @@ conda activate open-mmlab
# install latest PyTorch prebuilt with the default prebuilt CUDA version (usually the latest) # install latest PyTorch prebuilt with the default prebuilt CUDA version (usually the latest)
conda install -c pytorch pytorch torchvision -y conda install -c pytorch pytorch torchvision -y
# install mmcv # install mmengine and mmcv
pip install mmcv-full pip install openmim
mim install mmengine
mim install 'mmcv>=2.0.0rc0'
# install mmdetection # install mmdetection
pip install git+https://github.com/open-mmlab/mmdetection.git mim install 'mmdet>=3.0.0rc0'
# install mmsegmentation
pip install git+https://github.com/open-mmlab/mmsegmentation.git
# install mmdetection3d # install mmdetection3d
git clone https://github.com/open-mmlab/mmdetection3d.git git clone https://github.com/open-mmlab/mmdetection3d.git -b dev-1.x
cd mmdetection3d cd mmdetection3d
pip install -v -e . pip install -e .
``` ```
## 故障排除 ## 故障排除
如果在安装过程中遇到什么问题,可以先参考 [FAQ](faq.md) 页面. 如果在安装过程中遇到什么问题,可以先参考 [FAQ](notes/faq.md) 页面.
如果没有找到对应的解决方案,你也可以在 Github [提一个 issue](https://github.com/open-mmlab/mmdetection3d/issues/new/choose) 如果没有找到对应的解决方案,你也可以在 Github [提一个 issue](https://github.com/open-mmlab/mmdetection3d/issues/new/choose)
# 变更日志 # v1.1 变更日志
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