Commit e585f0d5 authored by Xiangxu-0103's avatar Xiangxu-0103 Committed by ZwwWayne
Browse files

[Docs] Update Chinese documentation (#1891)

parent 0be27ffb
.. toctree::
:maxdepth: 3
1_exist_data_model.md
2_new_data_model.md
backends_support.md
config.md
coord_sys_tutorial.md
data_pipeline.md
data_prepare.md
demo.md
dataset_prepare.md
inference.md
index.rst
model_deployment.md
train_test.md
useful_tools.md
visualization.md
......@@ -2,7 +2,7 @@
## 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
......
......@@ -44,7 +44,7 @@ The arguments:
- `type`: The name of the corresponding metric, usually associated with the dataset.
- `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.
- `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:
......@@ -70,7 +70,7 @@ Assume that you have already downloaded the checkpoints to the directory `checkp
```shell
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.
......@@ -243,17 +243,17 @@ If you use launch training jobs with Slurm, there are two ways to specify the po
In `config1.py`,
```python
env_cfg = dict(
dist_cfg=dict(backend='nccl', port=29500)
)
env_cfg = dict(
dist_cfg=dict(backend='nccl', port=29500)
)
```
In `config2.py`,
```python
env_cfg = dict(
dist_cfg=dict(backend='nccl', port=29500)
)
env_cfg = dict(
dist_cfg=dict(backend='nccl', port=29501)
)
```
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.
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
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`.
```python
import mmcv
import numpy as np
from mmengine import load
from mmdet3d.visualization import Det3DLocalVisualizer
import numpy as np
info_file = load('demo/data/kitti/000008.pkl')
points = np.fromfile('demo/data/kitti/000008.bin', dtype=np.float32)
points = points.reshape(-1, 4)[:, :3]
......@@ -40,6 +42,7 @@ We support drawing 3D boxes on point cloud by using `draw_bboxes_3d`.
```python
import torch
from mmdet3d.visualization import Det3DLocalVisualizer
from mmdet3d.structures import LiDARInstance3DBoxes
......@@ -66,8 +69,10 @@ We support drawing projected 3D boxes on image by using `draw_proj_bboxes_3d`.
import mmcv
import numpy as np
from mmengine import load
from mmdet3d.visualization import Det3DLocalVisualizer
from mmdet3d.structures import CameraInstance3DBoxes
info_file = load('demo/data/kitti/000008.pkl')
cam2img = np.array(info_file['data_list'][0]['images']['CAM2']['cam2img'], dtype=np.float32)
bboxes_3d = []
......@@ -91,11 +96,12 @@ visualizer.show()
### 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
import numpy as np
from mmengine import load
from mmdet3d.visualization import Det3DLocalVisualizer
from mmdet3d.structures import CameraInstance3DBoxes
......@@ -122,6 +128,7 @@ We support draw segmentation mask via per-point colorization by using `draw_seg_
```python
import torch
from mmdet3d.visualization import Det3DLocalVisualizer
points = np.fromfile('tests/data/s3dis/points/Area_1_office_2.bin', dtype=np.float32)
......@@ -136,7 +143,7 @@ visualizer.show()
## 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
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, 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
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
## 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
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 -
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
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
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
![](../../../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
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)
And browsing the nuScenes dataset in monocular 3D detection task
And browsing the nuScenes dataset in monocular 3D detection task:
```shell
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。
- 骨干网络(backbone):通常采用 FCN 网络来提取特征图,如 ResNet 和 SECOND。
......@@ -22,10 +22,10 @@
```python
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):
def __init__(self, arg1, arg2):
......@@ -74,10 +74,10 @@ model = dict(
```python
import torch.nn as nn
from ..builder import BACKBONES
from mmdet3d.registry import MODELS
@BACKBONES.register_module()
@MODELS.register_module()
class SECOND(BaseModule):
def __init__(self, arg1, arg2):
......@@ -122,9 +122,9 @@ model = dict(
创建一个新文件 `mmdet3d/models/necks/second_fpn.py`
```python
from ..builder import NECKS
from mmdet3d.registry import MODELS
@NECKS.register
@MODELS.register_module()
class SECONDFPN(BaseModule):
def __init__(self,
......@@ -183,10 +183,10 @@ PartA2 RoI Head 实现一个新的 bbox head ,并用于目标检测的任务
为了实现一个新的 bbox head,通常需要在其中实现三个功能,如下所示,有时该模块还需要实现其他相关的功能,如 `loss``get_targets`
```python
from mmdet.models.builder import HEADS
from .bbox_head import BBoxHead
from mmdet3d.registry import MODELS
from mmengine.model import BaseModule
@HEADS.register_module()
@MODELS.register_module()
class PartA2BboxHead(BaseModule):
"""PartA2 RoI head."""
......@@ -218,71 +218,63 @@ class PartA2BboxHead(BaseModule):
super(PartA2BboxHead, self).__init__(init_cfg=init_cfg)
def forward(self, seg_feats, part_feats):
```
其次,如果有必要的话,用户还需要实现一个新的 RoI Head,此处我们从 `Base3DRoIHead` 中继承得到一个新类 `PartAggregationROIHead`,此时我们就能发现 `Base3DRoIHead` 已经实现了下面的功能:
```python
from abc import ABCMeta, abstractmethod
from torch import nn as nn
from mmdet3d.registry import MODELS, TASK_UTILS
from mmdet.models.roi_heads import BaseRoIHead
@HEADS.register_module()
class Base3DRoIHead(BaseModule, metaclass=ABCMeta):
class Base3DRoIHead(BaseRoIHead):
"""Base class for 3d RoIHeads."""
def __init__(self,
bbox_head=None,
mask_roi_extractor=None,
bbox_roi_extractor=None,
mask_head=None,
mask_roi_extractor=None,
train_cfg=None,
test_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 with_bbox(self):
@property
def with_mask(self):
@abstractmethod
def init_weights(self, pretrained):
@abstractmethod
def init_bbox_head(self):
def init_bbox_head(self, bbox_roi_extractor: dict,
bbox_head: dict) -> None:
"""Initialize box head and box roi extractor.
@abstractmethod
def init_mask_head(self):
Args:
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):
"""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 forward_train(self,
x,
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].
"""
def init_mask_head(self):
"""Initialize mask head, skip since ``PartAggregationROIHead`` does not
have one."""
pass
```
......@@ -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,如下所示:
```python
from typing import Dict, List, Tuple
from mmcv import ConfigDict
from torch import Tensor
from torch.nn import functional as F
from mmdet3d.core import AssignResult
from mmdet3d.core.bbox import bbox3d2result, bbox3d2roi
from mmdet.core import build_assigner, build_sampler
from mmdet.models import HEADS
from ..builder import build_head, build_roi_extractor
from mmdet3d.registry import MODELS
from mmdet3d.structures import bbox3d2roi
from mmdet3d.utils import InstanceList
from mmdet.models.task_modules import AssignResult, SamplingResult
from ...structures.det3d_data_sample import SampleList
from .base_3droi_head import Base3DRoIHead
@HEADS.register_module()
@MODELS.register_module()
class PartAggregationROIHead(Base3DRoIHead):
"""Part aggregation roi head for PartA2.
Args:
semantic_head (ConfigDict): Config of semantic head.
num_classes (int): The number of classes.
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.
train_cfg (ConfigDict): Training config.
test_cfg (ConfigDict): Testing config.
"""
def __init__(self,
semantic_head,
num_classes=3,
seg_roi_extractor=None,
part_roi_extractor=None,
bbox_head=None,
train_cfg=None,
test_cfg=None,
init_cfg=None):
semantic_head: dict,
num_classes: int = 3,
seg_roi_extractor: dict = None,
bbox_head: dict = None,
bbox_roi_extractor: dict = None,
train_cfg: dict = None,
test_cfg: dict = None,
init_cfg: dict = None) -> None:
super(PartAggregationROIHead, self).__init__(
bbox_head=bbox_head,
bbox_roi_extractor=bbox_roi_extractor,
train_cfg=train_cfg,
test_cfg=test_cfg,
init_cfg=init_cfg)
self.num_classes = num_classes
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:
self.seg_roi_extractor = build_roi_extractor(seg_roi_extractor)
if part_roi_extractor is not None:
self.part_roi_extractor = build_roi_extractor(part_roi_extractor)
def init_seg_head(self, seg_roi_extractor: dict,
semantic_head: dict) -> None:
"""Initialize semantic head and seg 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:
seg_feats (torch.Tensor): Point-wise semantic features.
part_feats (torch.Tensor): Point-wise part prediction features.
voxels_dict (dict): Contains information of voxels.
rois (Tensor): Roi boxes.
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`.
rescale (bool): If True, return boxes in original image space.
Defaults to False.
Returns:
dict: Contains predictions of bbox_head and
features of roi_extractor.
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.
"""
pooled_seg_feats = self.seg_roi_extractor(seg_feats,
voxels_dict['voxel_centers'],
voxels_dict['coors'][..., 0],
rois)
pooled_part_feats = self.part_roi_extractor(
part_feats, voxels_dict['voxel_centers'],
voxels_dict['coors'][..., 0], rois)
cls_score, bbox_pred = self.bbox_head(pooled_seg_feats,
pooled_part_feats)
bbox_results = dict(
cls_score=cls_score,
bbox_pred=bbox_pred,
pooled_seg_feats=pooled_seg_feats,
pooled_part_feats=pooled_part_feats)
return bbox_results
assert self.with_bbox, 'Bbox head must be implemented in PartA2.'
assert self.with_semantic, 'Semantic head must be implemented' \
' in PartA2.'
batch_input_metas = [
data_samples.metainfo for data_samples in batch_data_samples
]
voxels_dict = feats_dict.pop('voxels_dict')
# TODO: Split predict semantic and bbox
results_list = self.predict_bbox(feats_dict, voxels_dict,
batch_input_metas, rpn_results_list,
self.test_cfg)
return results_list
def predict_bbox(self, feats_dict: Dict, voxel_dict: Dict,
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(
imports=['mmdet3d.models.roi_heads.part_aggregation_roi_head', 'mmdet3d.models.roi_heads.bbox_heads.parta2_bbox_head'])
```
PartAggregationROIHead 的配置文件如下所示
PartAggregationROIHead 的配置文件如下所示
```python
model = dict(
......@@ -394,14 +493,16 @@ model = dict(
seg_score_thr=0.3,
num_classes=3,
loss_seg=dict(
type='FocalLoss',
type='mmdet.FocalLoss',
use_sigmoid=True,
reduction='sum',
gamma=2.0,
alpha=0.25,
loss_weight=1.0),
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(
type='Single3DRoIAwareExtractor',
roi_layer=dict(
......@@ -409,7 +510,7 @@ model = dict(
out_size=14,
max_pts_per_voxel=128,
mode='max')),
part_roi_extractor=dict(
bbox_roi_extractor=dict(
type='Single3DRoIAwareExtractor',
roi_layer=dict(
type='RoIAwarePool3d',
......@@ -433,15 +534,15 @@ model = dict(
roi_feat_size=14,
with_corner_loss=True,
loss_bbox=dict(
type='SmoothL1Loss',
type='mmdet.SmoothL1Loss',
beta=1.0 / 9.0,
reduction='sum',
loss_weight=1.0),
loss_cls=dict(
type='CrossEntropyLoss',
type='mmdet.CrossEntropyLoss',
use_sigmoid=True,
reduction='sum',
loss_weight=1.0)))
loss_weight=1.0))),
...
)
```
......@@ -459,8 +560,8 @@ PartA2 Head 的第二阶段主要使用新建的 `PartAggregationROIHead` 和 `P
import torch
import torch.nn as nn
from ..builder import LOSSES
from .utils import weighted_loss
from mmdet3d.registry import MODELS
from mmdet.models.losses.utils import weighted_loss
@weighted_loss
def my_loss(pred, target):
......@@ -468,7 +569,7 @@ def my_loss(pred, target):
loss = torch.abs(pred - target)
return loss
@LOSSES.register_module()
@MODELS.register_module()
class MyLoss(nn.Module):
def __init__(self, reduction='mean', loss_weight=1.0):
......
......@@ -2,8 +2,6 @@
本页提供了有关在 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)查看更多细节。
......@@ -39,7 +37,7 @@ mmdetection3d
```bash
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/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
......@@ -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
```
需要注意的是,如果您的本地磁盘没有充足的存储空间来存储转换后的数据,您可以通过改变 `out-dir` 来指定其他任意的存储路径。如果您没有准备 `planes` 数据,您需要移除 `--with-plane` 标志。
需要注意的是,如果您的本地磁盘没有充足的存储空间来存储转换后的数据,您可以通过改变 `--out-dir` 来指定其他任意的存储路径。如果您没有准备 `planes` 数据,您需要移除 `--with-plane` 标志。
处理后的文件夹结构应该如下:
......@@ -78,39 +76,40 @@ kitti
├── kitti_dbinfos_train.pkl
├── kitti_infos_test.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`:训练数据集的信息,其中每一帧的信息包含下面的内容:
- info\['point_cloud'\]: {'num_features': 4, 'velodyne_path': velodyne_path}.
- info\['annos'\]: {
- 位置:其中 x,y,z 为相机参考坐标系下的目标的底部中心(单位为米),是一个尺寸为 Nx3 的数组
- 维度: 目标的高、宽、长(单位为米),是一个尺寸为 Nx3 的数组
- 旋转角:相机坐标系下目标绕着 Y 轴的旋转角 ry,其取值范围为 \[-pi..pi\] ,是一个尺寸为 N 的数组
- 名称:标准框所包含的目标的名称,是一个尺寸为 N 的数组
- 困难度:kitti 官方所定义的困难度,包括 简单,适中,困难
- 组别标识符:用于多部件的目标
}
- (optional) info\['calib'\]: {
- P0:校对后的 camera0 投影矩阵,是一个 3x4 数组
- P1:校对后的 camera1 投影矩阵,是一个 3x4 数组
- P2:校对后的 camera2 投影矩阵,是一个 3x4 数组
- P3:校对后的 camera3 投影矩阵,是一个 3x4 数组
- R0_rect:校准旋转矩阵,是一个 4x4 数组
- Tr_velo_to_cam:从 Velodyne 坐标到相机坐标的变换矩阵,是一个 4x4 数组
- Tr_imu_to_velo:从 IMU 坐标到 Velodyne 坐标的变换矩阵,是一个 4x4 数组
}
- (optional) info\['image'\]:{'image_idx': idx, 'image_path': image_path, 'image_shape', image_shape}.
**注意**:其中的 info\['annos'\] 中的数据均位于相机参考坐标系中,更多的细节请参考[此处](http://www.cvlibs.net/publications/Geiger2013IJRR.pdf)
获取 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).
- `kitti_gt_database/xxxxx.bin`:训练数据集中包含在 3D 标注框中的点云数据。
- `kitti_infos_train.pkl`:训练数据集,该字典包含了两个键值:`metainfo``data_list``metainfo` 包含数据集的基本信息,例如 `categories`, `dataset``info_version``data_list` 是由字典组成的列表,每个字典(以下简称 `info`)包含了单个样本的所有详细信息。
- info\['sample_idx'\]:该样本在整个数据集的索引。
- info\['images'\]:多个相机捕获的图像信息。是一个字典,包含 5 个键值:`CAM0`, `CAM1`, `CAM2`, `CAM3`, `R0_rect`
- info\['images'\]\['R0_rect'\]:校准旋转矩阵,是一个 4x4 数组。
- info\['images'\]\['CAM2'\]:包含 `CAM2` 相机传感器的信息。
- info\['images'\]\['CAM2'\]\['img_path'\]:图像的文件名。
- info\['images'\]\['CAM2'\]\['height'\]:图像的高。
- info\['images'\]\['CAM2'\]\['width'\]:图像的宽。
- info\['images'\]\['CAM2'\]\['cam2img'\]:相机到图像的变换矩阵,是一个 4x4 数组。
- info\['images'\]\['CAM2'\]\['lidar2cam'\]:激光雷达到相机的变换矩阵,是一个 4x4 数组。
- info\['images'\]\['CAM2'\]\['lidar2img'\]:激光雷达到图像的变换矩阵,是一个 4x4 数组。
- info\['lidar_points'\]:是一个字典,包含了激光雷达点相关的信息。
- info\['lidar_points'\]\['lidar_path'\]:激光雷达点云数据的文件名。
- info\['lidar_points'\]\['num_pts_feats'\]:点的特征维度。
- info\['lidar_points'\]\['Tr_velo_to_cam'\]:Velodyne 坐标到相机坐标的变换矩阵,是一个 4x4 数组。
- info\['lidar_points'\]\['Tr_imu_to_velo'\]:IMU 坐标到 Velodyne 坐标的变换矩阵,是一个 4x4 数组。
- info\['instances'\]:是一个字典组成的列表。每个字典包含单个实例的所有标注信息。对于其中的第 i 个实例,我们有:
- info\['instances'\]\[i\]\['bbox'\]:长度为 4 的列表,以 (x1, y1, x2, y2) 的顺序表示实例的 2D 边界框。
- info\['instances'\]\[i\]\['bbox_3d'\]:长度为 7 的列表,以 (x, y, z, w, h, l, yaw) 的顺序表示实例的 3D 边界框。
- info\['instances'\]\[i\]\['bbox_label'\]:是一个整数,表示实例的 2D 标签,-1 代表忽略。
- info\['instances'\]\[i\]\['bbox_label_3d'\]:是一个整数,表示实例的 3D 标签,-1 代表忽略。
- info\['instances'\]\[i\]\['depth'\]:3D 边界框投影到相关图像平面的中心点的深度。
- info\['instances'\]\[i\]\['num_lidar_pts'\]:3D 边界框内的激光雷达点数。
- info\['instances'\]\[i\]\['center_2d'\]:3D 边界框投影的 2D 中心。
- info\['instances'\]\[i\]\['difficulty'\]:KITTI 官方定义的困难度,包括简单、适中、困难。
- 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 = [
type='LoadPointsFromFile',
coord_type='LIDAR',
load_dim=4, # x, y, z, intensity
use_dim=4, # x, y, z, intensity
file_client_args=file_client_args),
use_dim=4),
dict(
type='LoadAnnotations3D',
with_bbox_3d=True,
with_label_3d=True,
file_client_args=file_client_args),
type='LoadAnnotations3D', with_bbox_3d=True, with_label_3d=True),
dict(type='ObjectSample', db_sampler=db_sampler),
dict(
type='ObjectNoise',
......@@ -144,8 +139,9 @@ train_pipeline = [
dict(type='PointsRangeFilter', point_cloud_range=point_cloud_range),
dict(type='ObjectRangeFilter', point_cloud_range=point_cloud_range),
dict(type='PointShuffle'),
dict(type='DefaultFormatBundle3D', class_names=class_names),
dict(type='Collect3D', keys=['points', 'gt_bboxes_3d', 'gt_labels_3d'])
dict(
type='Pack3DDetInputs',
keys=['points', 'gt_bboxes_3d', 'gt_labels_3d'])
]
```
......@@ -159,7 +155,7 @@ train_pipeline = [
使用 8 个 GPU 以及 KITTI 指标评估的 PointPillars 的示例如下:
```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
使用 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
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)>)获取更多细节。
......@@ -36,7 +36,7 @@ mmdetection3d
其中 `v1.01-train``v1.01-test` 包含与 nuScenes 数据集相同的元文件,`.txt` 文件包含数据划分的信息。
Lyft 不提供训练集和验证集的官方划分方案,因此 MMDetection3D 对不同场景下的不同类别的目标数量进行分析,并提供了一个数据集划分方案。
`sample_submission.csv` 是用于提交到 Kaggle 评估服务器的基本文件。
需要注意的是,我们遵循了 Lyft 最初的文件夹命名以实现更清楚的文件组织。请将下载下来的原始文件夹重命名按照上述组织结构重新命名。
需要注意的是,我们遵循了 Lyft 最初的文件夹命名以实现更清楚的文件组织。请将下载下来的原始文件夹按照上述组织结构重新命名。
## 数据准备
......@@ -77,38 +77,48 @@ mmdetection3d
│ │ ├── lyft_infos_train.pkl
│ │ ├── lyft_infos_val.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 目标检测。
不同于 nuScenes 数据集,这里仅能使用 json 文件进行 2D 检测相关的实验,未来将会进一步支持基于图像的 3D 检测。
- `lyft_infos_train.pkl`:训练数据集信息,该字典包含两个关键字:`metainfo``data_list``metainfo` 包含数据集的基本信息,例如 `categories`, `dataset``info_version``data_list` 是由字典组成的列表,每个字典(以下简称 `info`)包含了单个样本的所有详细信息。
- 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_database/xxxxx.bin` 文件不存在:由于真实标注框的采样对实验的影响可以忽略不计,在 Lyft 数据集中不会提取该目录和相关的 `.bin` 文件。
- `lyft_infos_train.pkl`:包含训练数据集信息,每一帧包含两个关键字:`metadata``infos`
`metadata` 包含数据集自身的基础信息,如 `{'version': 'v1.01-train'}`,然而 `infos` 包含和 nuScenes 数据集相似的数据集详细信息,但是并不包含一下几点:
- info\['sweeps'\]:扫描信息.
- info\['sweeps'\]\[i\]\['type'\]:扫描信息的数据类型,如 `'lidar'`
Lyft 数据集中的一些样例具有不同的 LiDAR 设置,然而为了数据分布的一致性,这里将一直采用顶部的 LiDAR 设备所采集的数据点信息。
- info\['gt_names'\]:在 Lyft 数据集中有 9 个类别,相比于 nuScenes 数据集,不同类别的标注不平衡问题更加突出。
- info\['gt_velocity'\] 不存在:Lyft 数据集中不存在速度评估信息。
- info\['num_lidar_pts'\]:默认值设置为 -1。
- info\['num_radar_pts'\]:默认值设置为 0。
- 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) 获取更多细节。
- `lyft_infos_train.pkl`
- info\['instances'\]\[i\]\['velocity'\] 不存在,Lyft 数据集中不存在速度评估信息。
- info\['instances'\]\[i\]\['num_lidar_pts'\] 及 info\['instances'\]\[i\]\['num_radar_pts'\] 不存在。
这里仅介绍存储在训练数据文件的数据记录信息。这同样适用于验证集和测试集(没有实例)。
请参考 [lyft_converter.py](https://github.com/open-mmlab/mmdetection3d/blob/dev-1.x/tools/dataset_converters/lyft_converter.py) 获取更多关于 `lyft_infos_xxx.pkl` 结构的细节。
## 训练流程
......@@ -122,12 +132,10 @@ train_pipeline = [
type='LoadPointsFromFile',
coord_type='LIDAR',
load_dim=5,
use_dim=5,
file_client_args=file_client_args),
use_dim=5),
dict(
type='LoadPointsFromMultiSweeps',
sweeps_num=10,
file_client_args=file_client_args),
sweeps_num=10),
dict(type='LoadAnnotations3D', with_bbox_3d=True, with_label_3d=True),
dict(
type='GlobalRotScaleTrans',
......@@ -138,8 +146,9 @@ train_pipeline = [
dict(type='PointsRangeFilter', point_cloud_range=point_cloud_range),
dict(type='ObjectRangeFilter', point_cloud_range=point_cloud_range),
dict(type='PointShuffle'),
dict(type='DefaultFormatBundle3D', class_names=class_names),
dict(type='Collect3D', keys=['points', 'gt_bboxes_3d', 'gt_labels_3d'])
dict(
type='Pack3DDetInputs',
keys=['points', 'gt_bboxes_3d', 'gt_labels_3d'])
]
```
......@@ -151,7 +160,7 @@ train_pipeline = [
使用 8 个 GPU 以及 Lyft 指标评估的 PointPillars 的示例如下:
```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 检测框的度
使用 8 个 GPU 在 Lyft 上测试 PointPillars 并生成对排行榜的提交的示例如下:
```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)获取更多细节。
......
......@@ -53,74 +53,57 @@ mmdetection3d
│ │ ├── nuscenes_infos_val.pkl
│ │ ├── nuscenes_infos_test.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_infos_train.pkl`:训练数据集信息,每帧信息有两个键值: `metadata``infos` `metadata` 包含数据集本身的基本信息,例如 `{'version': 'v1.0-trainval'}`,而 `infos` 包含详细信息如下:
- info\['lidar_path'\]:激光雷达点云数据的文件路径
- `nuscenes_infos_train.pkl`:训练数据集,该字典包含了两个键值:`metainfo``data_list``metainfo` 包含数据集的基本信息,例如 `categories`, `dataset``info_version``data_list` 是由字典组成的列表,每个字典(以下简称 `info`)包含了单个样本的所有详细信息
- info\['sample_idx'\]:样本在整个数据集的索引
- info\['token'\]:样本数据标记。
- info\['sweeps'\]:扫描信息(nuScenes 中的 `sweeps` 是指没有标注的中间帧,而 `samples` 是指那些带有标注的关键帧)。
- info\['sweeps'\]\[i\]\['data_path'\]:第 i 次扫描的数据路径。
- info\['sweeps'\]\[i\]\['type'\]:扫描数据类型,例如“激光雷达”。
- info\['sweeps'\]\[i\]\['sample_data_token'\]:扫描样本数据标记。
- info\['sweeps'\]\[i\]\['sensor2ego_translation'\]:从当前传感器(用于收集扫描数据)到自车(包含感知周围环境传感器的车辆,车辆坐标系固连在自车上)的转换(1x3 列表)。
- info\['sweeps'\]\[i\]\['sensor2ego_rotation'\]:从当前传感器(用于收集扫描数据)到自车的旋转(四元数格式的 1x4 列表)。
- info\['sweeps'\]\[i\]\['ego2global_translation'\]:从自车到全局坐标的转换(1x3 列表)。
- info\['sweeps'\]\[i\]\['ego2global_rotation'\]:从自车到全局坐标的旋转(四元数格式的 1x4 列表)。
- info\['sweeps'\]\[i\]\['timestamp'\]:扫描数据的时间戳。
- info\['sweeps'\]\[i\]\['sensor2lidar_translation'\]:从当前传感器(用于收集扫描数据)到激光雷达的转换(1x3 列表)。
- info\['sweeps'\]\[i\]\['sensor2lidar_rotation'\]:从当前传感器(用于收集扫描数据)到激光雷达的旋转(四元数格式的 1x4 列表)。
- info\['cams'\]:相机校准信息。它包含与每个摄像头对应的六个键值: `'CAM_FRONT'`, `'CAM_FRONT_RIGHT'`, `'CAM_FRONT_LEFT'`, `'CAM_BACK'`, `'CAM_BACK_LEFT'`, `'CAM_BACK_RIGHT'`
每个字典包含每个扫描数据按照上述方式的详细信息(每个信息的关键字与上述相同)。除此之外,每个相机还包含了一个键值 `'cam_intrinsic'` 用来保存 3D 点投影到图像平面上需要的内参信息。
- info\['lidar2ego_translation'\]:从激光雷达到自车的转换(1x3 列表)。
- info\['lidar2ego_rotation'\]:从激光雷达到自车的旋转(四元数格式的 1x4 列表)。
- info\['ego2global_translation'\]:从自车到全局坐标的转换(1x3 列表)。
- info\['ego2global_rotation'\]:从自我车辆到全局坐标的旋转(四元数格式的 1x4 列表)。
- info\['timestamp'\]:样本数据的时间戳。
- info\['gt_boxes'\]:7 个自由度的 3D 包围框,一个 Nx7 数组。
- info\['gt_names'\]:3D 包围框的类别,一个 1xN 数组。
- info\['gt_velocity'\]:3D 包围框的速度(由于不准确,没有垂直测量),一个 Nx2 数组。
- info\['num_lidar_pts'\]:每个 3D 包围框中包含的激光雷达点数。
- info\['num_radar_pts'\]:每个 3D 包围框中包含的雷达点数。
- info\['valid_flag'\]:每个包围框是否有效。一般情况下,我们只将包含至少一个激光雷达或雷达点的 3D 框作为有效框。
- `nuscenes_infos_train_mono3d.coco.json`:训练数据集 coco 风格的信息。该文件将基于图像的数据组织为三类(键值):`'categories'`, `'images'`, `'annotations'`
- info\['categories'\]:包含所有类别名称的列表。每个元素都遵循字典格式并由两个键值组成:`'id'``'name'`
- info\['images'\]:包含所有图像信息的列表。
- info\['images'\]\[i\]\['file_name'\]:第 i 张图像的文件名。
- info\['images'\]\[i\]\['id'\]:第 i 张图像的样本数据标记。
- info\['images'\]\[i\]\['token'\]:与该帧对应的样本标记。
- info\['images'\]\[i\]\['cam2ego_rotation'\]:从相机到自车的旋转(四元数格式的 1x4 列表)。
- info\['images'\]\[i\]\['cam2ego_translation'\]:从相机到自车的转换(1x3 列表)。
- info\['images'\]\[i\]\['ego2global_rotation''\]:从自车到全局坐标的旋转(四元数格式的 1x4 列表)。
- info\['images'\]\[i\]\['ego2global_translation'\]:从自车到全局坐标的转换(1x3 列表)。
- info\['images'\]\[i\]\['cam_intrinsic'\]: 相机内参矩阵(3x3 列表)。
- info\['images'\]\[i\]\['width'\]:图片宽度, nuScenes 中默认为 1600。
- info\['images'\]\[i\]\['height'\]:图像高度, nuScenes 中默认为 900。
- info\['annotations'\]: 包含所有标注信息的列表。
- info\['annotations'\]\[i\]\['file_name'\]:对应图像的文件名。
- info\['annotations'\]\[i\]\['image_id'\]:对应图像的图像 ID (标记)。
- info\['annotations'\]\[i\]\['area'\]:2D 包围框的面积。
- info\['annotations'\]\[i\]\['category_name'\]:类别名称。
- info\['annotations'\]\[i\]\['category_id'\]:类别 id。
- info\['annotations'\]\[i\]\['bbox'\]:2D 包围框标注(3D 投影框的外部矩形),1x4 列表跟随 \[x1, y1, x2-x1, y2-y1\]。x1/y1 是沿图像水平/垂直方向的最小坐标。
- info\['annotations'\]\[i\]\['iscrowd'\]:该区域是否拥挤。默认为 0。
- 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)
- 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''\]:当前激光雷达传感器到自车的变换矩阵。(4x4 列表)
- info\['lidar_sweeps'\]\[i\]\['lidar_points'\]\['ego2global'\]:自车到全局坐标的变换矩阵。(4x4 列表)
- info\['lidar_sweeps'\]\[i\]\['lidar2sensor'\]:从主激光雷达传感器到当前传感器(用于收集扫描数据)的变换矩阵。(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'\]:整数表示实例的标签,-1 代表忽略。
- info\['instances'\]\[i\]\['velocity'\]:3D 边界框的速度(由于不正确,没有垂直测量),大小为 (2, ) 的列表。
- info\['instances'\]\[i\]\['num_lidar_pts'\]:每个 3D 边界框内包含的激光雷达点数。
- info\['instances'\]\[i\]\['num_radar_pts'\]:每个 3D 边界框内包含的雷达点数。
- info\['instances'\]\[i\]\['bbox_3d_isvalid'\]:每个包围框是否有效。一般情况下,我们只将包含至少一个激光雷达或雷达点的 3D 框作为有效框。
- info\['cam_instances'\]:是一个字典,包含以下键值:`'CAM_FRONT'`, `'CAM_FRONT_RIGHT'`, `'CAM_FRONT_LEFT'`, `'CAM_BACK'`, `'CAM_BACK_LEFT'`, `'CAM_BACK_RIGHT'`。对于基于视觉的 3D 目标检测任务,我们将整个场景的 3D 标注划分至它们所属于的相应相机中。
- info\['cam_instances'\]\['CAM_XXX'\]\[i\]\['bbox_label'\]:实例标签。
- info\['cam_instances'\]\['CAM_XXX'\]\[i\]\['bbox_label_3d'\]:实例标签。
- info\['cam_instances'\]\['CAM_XXX'\]\[i\]\['bbox'\]:2D 边界框标注(3D 框投影的矩形框),顺序为 \[x1, y1, x2, y2\] 的列表。
- info\['cam_instances'\]\['CAM_XXX'\]\[i\]\['center_2d'\]:3D 框投影到图像上的中心点,大小为 (2, ) 的列表。
- info\['cam_instances'\]\['CAM_XXX'\]\[i\]\['depth'\]:3D 框投影中心的深度。
- info\['cam_instances'\]\['CAM_XXX'\]\[i\]\['velocity'\]:3D 边界框的速度(由于不正确,没有垂直测量),大小为 (2, ) 的列表。
- info\['cam_instances'\]\['CAM_XXX'\]\[i\]\['attr_label'\]:实例的属性标签。我们为属性分类维护了一个属性集合和映射
- info\['cam_instances'\]\['CAM_XXX'\]\[i\]\['bbox_3d'\]:长度为 7 的列表,以 (x, y, z, l, h, w, yaw) 的顺序表示实例的 3D 边界框。
注意:
1. `instances``cam_instances``bbox_3d` 的区别。
`bbox_3d` 都被转换到 MMDet3D 定义的坐标系下,`instances` 中的 `bbox_3d` 是在激光雷达坐标系下,而 `cam_instances` 是在相机坐标系下。注意它们 3D 框中表示的不同('l, w, h' 和 'l, h, w')。
2. 这里我们只解释训练信息文件中记录的数据。这同样适用于验证集和测试集(测试集的 pkl 文件中不包含 `instances` 以及 `cam_instances`)。
获取 `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)
## 训练流程
......@@ -134,12 +117,10 @@ train_pipeline = [
type='LoadPointsFromFile',
coord_type='LIDAR',
load_dim=5,
use_dim=5,
file_client_args=file_client_args),
use_dim=5),
dict(
type='LoadPointsFromMultiSweeps',
sweeps_num=10,
file_client_args=file_client_args),
sweeps_num=10),
dict(type='LoadAnnotations3D', with_bbox_3d=True, with_label_3d=True),
dict(
type='GlobalRotScaleTrans',
......@@ -151,8 +132,9 @@ train_pipeline = [
dict(type='ObjectRangeFilter', point_cloud_range=point_cloud_range),
dict(type='ObjectNameFilter', classes=class_names),
dict(type='PointShuffle'),
dict(type='DefaultFormatBundle3D', class_names=class_names),
dict(type='Collect3D', keys=['points', 'gt_bboxes_3d', 'gt_labels_3d'])
dict(
type='Pack3DDetInputs',
keys=['points', 'gt_bboxes_3d', 'gt_labels_3d'])
]
```
......@@ -178,14 +160,11 @@ train_pipeline = [
with_bbox_depth=True),
dict(type='Resize', img_scale=(1600, 900), keep_ratio=True),
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(
type='Collect3D',
type='Pack3DDetInputs',
keys=[
'img', 'gt_bboxes', 'gt_labels', 'attr_labels', 'gt_bboxes_3d',
'gt_labels_3d', 'centers2d', 'depths'
'img', 'gt_bboxes', 'gt_bboxes_labels', 'attr_labels', 'gt_bboxes_3d',
'gt_labels_3d', 'centers_2d', 'depths'
]),
]
```
......@@ -202,7 +181,7 @@ train_pipeline = [
使用 8 个 GPU 以及 nuScenes 指标评估的 PointPillars 的示例如下
```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
使用 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
./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)
......
......@@ -179,9 +179,7 @@ train_pipeline = [
with_mask_3d=False,
with_seg_3d=True),
dict(
type='PointSegClassMapping',
valid_cat_ids=tuple(range(len(class_names))),
max_cat_id=13),
type='PointSegClassMapping'),
dict(
type='IndoorPatchPointSample',
num_points=num_points,
......@@ -202,8 +200,7 @@ train_pipeline = [
jitter_std=[0.01, 0.01, 0.01],
clip_range=[-0.05, 0.05]),
dict(type='RandomDropPointsColor', drop_ratio=0.2),
dict(type='DefaultFormatBundle3D', class_names=class_names),
dict(type='Collect3D', keys=['points', 'pts_semantic_mask'])
dict(type='Pack3DDetInputs', keys=['points', 'pts_semantic_mask'])
]
```
......@@ -219,7 +216,7 @@ train_pipeline = [
通常我们使用平均交并比 (mean Intersection over Union, mIoU) 作为 ScanNet 语义分割任务的度量指标。
具体而言,我们先计算所有类别的 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 个区域上进行测试。但是在其他论文中,也有不同的划分方式。
为了便于灵活划分训练和测试的子集,我们首先定义子数据集 (sub-dataset) 来表示每一个区域,然后根据区域划分对其进行合并,以得到完整的训练集。
......@@ -232,31 +229,42 @@ class_names = ('ceiling', 'floor', 'wall', 'beam', 'column', 'window', 'door',
'table', 'chair', 'sofa', 'bookcase', 'board', 'clutter')
train_area = [1, 2, 3, 4, 6]
test_area = 5
data = dict(
train=dict(
train_dataloader = dict(
batch_size=8,
num_workers=4,
persistent_workers=True,
sampler=dict(type='DefaultSampler', shuffle=True),
dataset=dict(
type=dataset_type,
data_root=data_root,
ann_files=[
data_root + f's3dis_infos_Area_{i}.pkl' for i in train_area
],
ann_files=[f's3dis_infos_Area_{i}.pkl' for i in train_area],
metainfo=metainfo,
data_prefix=data_prefix,
pipeline=train_pipeline,
classes=class_names,
test_mode=False,
modality=input_modality,
ignore_index=len(class_names),
scene_idxs=[
data_root + f'seg_info/Area_{i}_resampled_scene_idxs.npy'
for i in train_area
]),
val=dict(
f'seg_info/Area_{i}_resampled_scene_idxs.npy' for i in train_area
],
test_mode=False))
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,
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,
classes=class_names,
test_mode=True,
modality=input_modality,
ignore_index=len(class_names),
scene_idxs=data_root +
f'seg_info/Area_{test_area}_resampled_scene_idxs.npy'))
scene_idxs=f'seg_info/Area_{test_area}_resampled_scene_idxs.npy',
test_mode=True))
val_dataloader = test_dataloader
```
可以看到,我们通过将多个相应路径构成的列表 (list) 输入 `ann_files``scene_idxs` 以实现训练测试集的划分。
......
......@@ -227,21 +227,15 @@ scannet
- `semantic_mask/xxxxx.bin`:每个点的语义标签,值的范围为:\[1, 40\], 也就是 `nyu40id` 的标准。请注意:在训练流程 `PointSegClassMapping` 中,`nyu40id` 的 ID 会被映射到训练 ID。
- `posed_images/scenexxxx_xx``.jpg` 图像的集合,还包含 `.txt` 格式的 4x4 相机姿态和单个 `.txt` 格式的相机内参矩阵文件。
- `scannet_infos_train.pkl`:训练集的数据信息,每个场景的具体信息如下:
- info\['point_cloud'\]`{'num_features': 6, 'lidar_idx': sample_idx}`,其中 `sample_idx` 为该场景的索引。
- info\['pts_path'\]`points/xxxxx.bin` 的路径。
- info\['pts_instance_mask_path'\]`instance_mask/xxxxx.bin` 的路径。
- info\['pts_semantic_mask_path'\]`semantic_mask/xxxxx.bin` 的路径。
- info\['annos'\]:每个场景的标注。
- annotations\['gt_num'\]:真实物体 (ground truth) 的数量。
- annotations\['name'\]:所有真实物体的语义类别名称,比如 `chair`(椅子)。
- annotations\['location'\]:depth 坐标系下与坐标轴平行的三维包围框的重力中心 (gravity center),形状为 \[K, 3\],其中 K 是真实物体的数量。
- annotations\['dimensions'\]:depth 坐标系下与坐标轴平行的三维包围框的大小,形状为 \[K, 3\]
- 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, \]
- info\['lidar_points'\]:字典包含与激光雷达点相关的信息。
- info\['lidar_points'\]\['lidar_path'\]:点云数据 `xxx.bin` 的文件路径。
- info\['lidar_points'\]\['num_pts_feats'\]:点的特征维度。
- info\['lidar_points'\]\['axis_align_matrix'\]:用于对齐坐标轴的变换矩阵。
- info\['pts_semantic_mask_path'\]:包含语义分割标注的 `xxx.bin` 文件路径。
- info\['pts_instance_mask_path'\]:包含实例分割标注的 `xxx.bin` 文件路径。
- info\['instances'\]:字典组成的列表,每个字典包含一个实例的所有标注信息。对于其中的第 i 个实例,我们有:
- info\['instances'\]\[i\]\['bbox_3d'\]:长度为 6 的列表,以 (x, y, z, l, w, h) 的顺序表示深度坐标系下与坐标轴平行的 3D 边界框。
- info\[instances\]\[i\]\['bbox_label_3d'\]:3D 边界框的标签。
- `scannet_infos_val.pkl`:验证集上的数据信息,与 `scannet_infos_train.pkl` 格式完全一致。
- `scannet_infos_test.pkl`:测试集上的数据信息,与 `scannet_infos_train.pkl` 格式几乎完全一致,除了缺少标注。
......@@ -277,9 +271,8 @@ train_pipeline = [
rot_range=[-0.087266, 0.087266],
scale_ratio_range=[1.0, 1.0],
shift_height=True),
dict(type='DefaultFormatBundle3D', class_names=class_names),
dict(
type='Collect3D',
type='Pack3DDetInputs',
keys=[
'points', 'gt_bboxes_3d', 'gt_labels_3d', 'pts_semantic_mask',
'pts_instance_mask'
......@@ -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) ,该过程不会考虑包围框的旋转。
......@@ -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 语义分割特有的处理步骤和数据信息。
### 提取 ScanNet 数据
......@@ -102,8 +102,7 @@ train_pipeline = [
enlarge_size=0.2,
min_unique_num=None),
dict(type='NormalizePointsColor', color_mean=None),
dict(type='DefaultFormatBundle3D', class_names=class_names),
dict(type='Collect3D', keys=['points', 'pts_semantic_mask'])
dict(type='Pack3DDetInputs', keys=['points', 'pts_semantic_mask'])
]
```
......@@ -115,7 +114,7 @@ train_pipeline = [
通常我们使用平均交并比 (mean Intersection over Union, mIoU) 作为 ScanNet 语义分割任务的度量指标。
具体而言,我们先计算所有类别的 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 \
bash tools/create_data.sh <job_name> sunrgbd
```
之前提到的点云数据就会被处理并以 `.bin` 格式重新存储。与此同时,`.pkl` 文件也被生成,用于存储数据标注和元信息。这一步处理中,用于生成 `.pkl` 文件的核心函数 `process_single_scene` 如下:
```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
```
之前提到的点云数据就会被处理并以 `.bin` 格式重新存储。与此同时,`.pkl` 文件也被生成,用于存储数据标注和元信息。
如上数据处理后,文件目录结构应如下:
......@@ -236,24 +151,21 @@ sunrgbd
├── sunrgbd_infos_val.pkl
```
- `points/0xxxxx.bin`:降采样后的点云数据。
- `points/xxxxxx.bin`:降采样后的点云数据。
- `sunrgbd_infos_train.pkl`:训练集数据信息(标注与元信息),每个场景所含数据信息具体如下:
- info\['point_cloud'\]`{'num_features': 6, 'lidar_idx': sample_idx}`,其中 `sample_idx` 为该场景的索引。
- info\['pts_path'\]`points/0xxxxx.bin` 的路径。
- info\['image'\]:图像路径与元信息:
- image\['image_idx'\]:图像索引。
- image\['image_shape'\]:图像张量的形状(即其尺寸)。
- image\['image_path'\]:图像路径。
- info\['annos'\]:每个场景的标注:
- annotations\['gt_num'\]:真实物体 (ground truth) 的数量。
- annotations\['name'\]:所有真实物体的语义类别名称,比如 `chair`(椅子)。
- annotations\['location'\]:depth 坐标系下三维包围框的重力中心 (gravity center),形状为 \[K, 3\],其中 K 是真实物体的数量。
- annotations\['dimensions'\]:depth 坐标系下三维包围框的大小,形状为 \[K, 3\]
- annotations\['rotation_y'\]:depth 坐标系下三维包围框的旋转角,形状为 \[K, \]
- annotations\['gt_boxes_upright_depth'\]:depth 坐标系下三维包围框 `(x, y, z, x_size, y_size, z_size, yaw)`,形状为 \[K, 7\]
- annotations\['bbox'\]:二维包围框 `(x, y, x_size, y_size)`,形状为 \[K, 4\]
- annotations\['index'\]:所有真实物体的索引,范围为 \[0, K)。
- annotations\['class'\]:所有真实物体类别的标号,范围为 \[0, 10),形状为 \[K, \]
- info\['lidar_points'\]:字典包含了与激光雷达点相关的信息。
- info\['lidar_points'\]\['num_pts_feats'\]:点的特征维度。
- info\['lidar_points'\]\['lidar_path'\]:点云数据 `xxx.bin` 的文件路径。
- info\['images'\]:字典包含了与图像数据相关的信息。
- info\['images'\]\['CAM0'\]\['img_path'\]:图像的文件名。
- info\['images'\]\['CAM0'\]\['depth2img'\]:深度到图像的变换矩阵,形状为 (4, 4)。
- info\['images'\]\['CAM0'\]\['height'\]:图像的高。
- info\['images'\]\['CAM0'\]\['width'\]:图像的宽。
- info\['instances'\]:由字典组成的列表,包含了该帧的所有标注信息。每个字典与单个实例的标注相关。对于其中的第 i 个实例,我们有:
- info\['instances'\]\[i\]\['bbox_3d'\]:长度为 7 的列表,表示深度坐标系下的 3D 边界框。
- info\['instances'\]\[i\]\['bbox'\]:长度为 4 的列表,以 (x1, y1, x2, y2) 的顺序表示实例的 2D 边界框。
- info\['instances'\]\[i\]\['bbox_label_3d'\]:整数表示实例的 3D 标签,-1 表示忽略该类别。
- info\['instances'\]\[i\]\['bbox_label'\]:整数表示实例的 2D 标签,-1 表示忽略该类别。
- `sunrgbd_infos_val.pkl`:验证集上的数据信息,与 `sunrgbd_infos_train.pkl` 格式完全一致。
## 训练流程
......@@ -280,8 +192,9 @@ train_pipeline = [
scale_ratio_range=[0.85, 1.15],
shift_height=True),
dict(type='PointSample', num_points=20000),
dict(type='DefaultFormatBundle3D', class_names=class_names),
dict(type='Collect3D', keys=['points', 'gt_bboxes_3d', 'gt_labels_3d'])
dict(
type='Pack3DDetInputs',
keys=['points', 'gt_bboxes_3d', 'gt_labels_3d'])
]
```
......@@ -304,9 +217,8 @@ train_pipeline = [
dict(type='LoadImageFromFile'),
dict(type='LoadAnnotations3D'),
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='Normalize', **img_norm_cfg),
dict(type='Pad', size_divisor=32),
dict(
type='RandomFlip3D',
......@@ -318,28 +230,21 @@ train_pipeline = [
rot_range=[-0.523599, 0.523599],
scale_ratio_range=[0.85, 1.15],
shift_height=True),
dict(type='PointSample', num_points=20000),
dict(type='DefaultFormatBundle3D', class_names=class_names),
dict(
type='Collect3D',
keys=[
'img', 'gt_bboxes', 'gt_labels', 'points', 'gt_bboxes_3d',
'gt_labels_3d'
])
type='Pack3DDetInputs',
keys=['points', 'gt_bboxes_3d', 'gt_labels_3d','img', 'gt_bboxes', 'gt_bboxes_labels'])
]
```
图像上的数据增强/归一化
图像上的数据增强
- `Resize`: 改变输入图像的大小, `keep_ratio=True` 意味着图像的比例不改变。
- `Normalize`: 归一化图像的 RGB 通道。
- `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。
......@@ -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
# download the code and enter the base directory
......@@ -122,19 +122,16 @@ sudo apt install build-essential
bazel clean
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 模型的情景:
```shell
./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 \
--eval waymo --eval-options 'pklfile_prefix=results/waymo-car/kitti_results' \
'submission_prefix=results/waymo-car/kitti_results'
./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
```
如果需要生成 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 ../
如下是一个使用 8 个图形处理器在 Waymo 上测试 PointPillars,生成 bin 文件并提交结果到官方榜单的例子:
```shell
./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 文件并提交到服务器中,在运行测试指令前你需要在配置文件的 `test_evaluator` 中指定 `submission_prefix`
在生成 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
cd ../waymo-od/
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/
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
# 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/
gzip results/waymo-car/submission/my_model.tar
......
# 依赖
在本节中,我们将展示如何使用 PyTorch 准备环境。
MMDetection3D 可以安装在 Linux, MacOS, (实验性支持 Windows) 的平台上,它具体需要下列安装包:
- Python 3.6+
- PyTorch 1.3+
- PyTorch 1.6+
- CUDA 9.2+ (如果你从源码编译 PyTorch, CUDA 9.0 也是兼容的。)
- GCC 5+
- [MMEngine](https://mmengine.readthedocs.io/en/latest/#installation)
- [MMCV](https://mmcv.readthedocs.io/en/latest/#installation)
```{note}
如果你已经装了 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
# 鉴于 waymo-open-dataset-tf-2-6-0 要求 python>=3.7, 我们推荐安装 python3.8
......@@ -23,15 +25,15 @@ conda create --name openmmlab python=3.8 -y
conda activate openmmlab
```
**步骤 2.** 基于 [PyTorch 官网](https://pytorch.org/)安装 PyTorch 和 torchvision,例如:
**步骤 2.** 基于 [PyTorch 官网](https://pytorch.org/)安装 PyTorch,例如:
GPU 环境下
GPU 环境下
```shell
conda install pytorch torchvision -c pytorch
```
CPU 环境下
CPU 环境下
```shell
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
pip install openmim
mim install mmcv-full
mim install mmdet
mim install mmsegmentation
git clone https://github.com/open-mmlab/mmdetection3d.git
mim install mmengine
mim install 'mmcv>=2.0.0rc0'
mim install 'mmdet>=3.0.0rc0'
git clone https://github.com/open-mmlab/mmdetection3d.git -b dev-1.x
cd mmdetection3d
pip install -e .
```
**步骤 0. 通过[MIM](https://github.com/open-mmlab/mim) 安装 [MMCV](https://github.com/open-mmlab/mmcv).**
**步骤 1. 安装 [MMDetection](https://github.com/open-mmlab/mmdetection).**
**步骤 0.** 通过 [MIM](https://github.com/open-mmlab/mim) 安装 [MMEngine](https://github.com/open-mmlab/mmengine)[MMCV](https://github.com/open-mmlab/mmcv)
```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
git clone https://github.com/open-mmlab/mmdetection.git
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"
mim install 'mmdet>=3.0.0rc0'
```
**步骤 2. 安装 [MMSegmentation](https://github.com/open-mmlab/mmsegmentation).**
```shell
pip install mmsegmentation
```
同时,如果你想修改这部分的代码,也可以通过以下命令从源码编译 MMSegmentation:
同时,如果你想修改这部分的代码,也可以通过以下命令从源码编译 MMDetection:
```shell
git clone https://github.com/open-mmlab/mmsegmentation.git
cd mmsegmentation
git checkout v0.20.0 # switch to v0.20.0 branch
pip install -e . # or "python setup.py develop"
git clone https://github.com/open-mmlab/mmdetection.git -b dev-3.x
# "-b dev-3.x" means checkout to the `dev-3.x` branch.
cd mmdetection
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
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
```
**步骤 4. 安装依赖包和 MMDetection3D.**
**步骤 4.** 安装依赖包和 MMDetection3D
```shell
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` 文件。
......@@ -126,13 +122,13 @@ pip install -v -e . # or "python setup.py develop"
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
conda install openblas-devel -c anaconda
......@@ -141,9 +137,9 @@ pip install -v -e . # or "python setup.py develop"
5. 我们的代码目前不能在只有 CPU 的环境(CUDA 不可用)下编译运行。
# 验证
## 验证
## 通过点云样例程序来验证
### 通过点云样例程序来验证
我们提供了一些样例脚本去测试单个样本,预训练的模型可以从[模型库](model_zoo.md)中下载. 运行如下命令可以去测试点云场景下一个单模态的 3D 检测算法。
......@@ -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}]
```
:
如:
```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` 格式的文件去运行样例程序。
请注意在使用此脚本前,你需要先安装 `pandas``plyfile`。 这个函数也可使用在数据预处理当中,为了能够直接训练 `ply data`
```python
......@@ -178,13 +173,13 @@ def convert_ply(input_path, output_path):
data_np.astype(np.float32).tofile(output_path)
```
:
如:
```python
convert_ply('./test.ply', './test.bin')
```
如果你有其他格式的点云文件 (例:`off`, `obj`), 你可以使用 `trimesh` 将它们转化成 `ply`.
如果你有其他格式的点云文件 (例:`off``obj`)你可以使用 `trimesh` 将它们转化成 `ply`
```python
import trimesh
......@@ -194,19 +189,22 @@ def to_ply(input_path, output_path, original_type):
mesh.export(output_path, file_type='ply') # convert to ply
```
:
如:
```python
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
from mmdet3d.apis import init_model, inference_detector
......@@ -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 版本。
```
### 不通过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
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) 来建立一个镜像。
```shell
# 基于 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
# install latest PyTorch prebuilt with the default prebuilt CUDA version (usually the latest)
conda install -c pytorch pytorch torchvision -y
# install mmcv
pip install mmcv-full
# install mmengine and mmcv
pip install openmim
mim install mmengine
mim install 'mmcv>=2.0.0rc0'
# install mmdetection
pip install git+https://github.com/open-mmlab/mmdetection.git
# install mmsegmentation
pip install git+https://github.com/open-mmlab/mmsegmentation.git
mim install 'mmdet>=3.0.0rc0'
# 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
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)
# 变更日志
# 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