Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Enhance] Migrate to PointSample and enhance PointSample function #840

Merged
merged 7 commits into from
Aug 5, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions configs/_base_/datasets/scannet-3d-18class.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@
valid_cat_ids=(3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 14, 16, 24, 28, 33, 34,
36, 39),
max_cat_id=40),
dict(type='IndoorPointSample', num_points=40000),
dict(type='PointSample', num_points=40000),
dict(
type='RandomFlip3D',
sync_2d=False,
Expand Down Expand Up @@ -67,7 +67,7 @@
sync_2d=False,
flip_ratio_bev_horizontal=0.5,
flip_ratio_bev_vertical=0.5),
dict(type='IndoorPointSample', num_points=40000),
dict(type='PointSample', num_points=40000),
dict(
type='DefaultFormatBundle3D',
class_names=class_names,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,7 @@
type='PointSegClassMapping',
valid_cat_ids=(3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 14, 16, 24, 28, 33, 34,
36, 39)),
dict(type='IndoorPointSample', num_points=50000),
dict(type='PointSample', num_points=50000),
dict(
type='RandomFlip3D',
sync_2d=False,
Expand Down Expand Up @@ -133,7 +133,7 @@
sync_2d=False,
flip_ratio_bev_horizontal=0.5,
flip_ratio_bev_vertical=0.5),
dict(type='IndoorPointSample', num_points=50000),
dict(type='PointSample', num_points=50000),
dict(
type='DefaultFormatBundle3D',
class_names=class_names,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,7 @@
type='PointSegClassMapping',
valid_cat_ids=(3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 14, 16, 24, 28, 33, 34,
36, 39)),
dict(type='IndoorPointSample', num_points=50000),
dict(type='PointSample', num_points=50000),
dict(
type='RandomFlip3D',
sync_2d=False,
Expand Down Expand Up @@ -132,7 +132,7 @@
sync_2d=False,
flip_ratio_bev_horizontal=0.5,
flip_ratio_bev_vertical=0.5),
dict(type='IndoorPointSample', num_points=50000),
dict(type='PointSample', num_points=50000),
dict(
type='DefaultFormatBundle3D',
class_names=class_names,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,7 @@
type='PointSegClassMapping',
valid_cat_ids=(3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 14, 16, 24, 28, 33, 34,
36, 39)),
dict(type='IndoorPointSample', num_points=50000),
dict(type='PointSample', num_points=50000),
dict(
type='RandomFlip3D',
sync_2d=False,
Expand Down Expand Up @@ -148,7 +148,7 @@
sync_2d=False,
flip_ratio_bev_horizontal=0.5,
flip_ratio_bev_vertical=0.5),
dict(type='IndoorPointSample', num_points=50000),
dict(type='PointSample', num_points=50000),
dict(
type='DefaultFormatBundle3D',
class_names=class_names,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -108,7 +108,7 @@
type='PointSegClassMapping',
valid_cat_ids=(3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 14, 16, 24, 28, 33, 34,
36, 39)),
dict(type='IndoorPointSample', num_points=50000),
dict(type='PointSample', num_points=50000),
dict(
type='RandomFlip3D',
sync_2d=False,
Expand Down Expand Up @@ -149,7 +149,7 @@
sync_2d=False,
flip_ratio_bev_horizontal=0.5,
flip_ratio_bev_vertical=0.5),
dict(type='IndoorPointSample', num_points=50000),
dict(type='PointSample', num_points=50000),
dict(
type='DefaultFormatBundle3D',
class_names=class_names,
Expand Down
2 changes: 1 addition & 1 deletion docs/compatibility.md
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ We adopt new pre-processing and conversion steps of ScanNet dataset. In previous

- Since the aligned boxes share the same key as in old data infos, we do not need to modify the code related to it. But do remember that they are not in the same coordinate system as the saved points.

- There is an `IndoorPointSample` pipeline in the data pipelines for ScanNet detection task which down-samples points. So removing down-sampling in data generation will not affect the code.
- There is an `PointSample` pipeline in the data pipelines for ScanNet detection task which down-samples points. So removing down-sampling in data generation will not affect the code.

We have trained a [VoteNet](https://github.com/open-mmlab/mmdetection3d/blob/master/configs/votenet/votenet_8x8_scannet-3d-18class.py) model on the newly processed ScanNet dataset and get similar benchmark results. In order to prepare ScanNet data for both detection and segmentation tasks, please re-run the new pre-processing scripts following the ScanNet [README.md](https://github.com/open-mmlab/mmdetection3d/blob/master/data/scannet/README.md/).

Expand Down
4 changes: 2 additions & 2 deletions docs/datasets/scannet_det.md
Original file line number Diff line number Diff line change
Expand Up @@ -267,7 +267,7 @@ train_pipeline = [
valid_cat_ids=(3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 14, 16, 24, 28, 33, 34,
36, 39),
max_cat_id=40),
dict(type='IndoorPointSample', num_points=40000),
dict(type='PointSample', num_points=40000),
dict(
type='RandomFlip3D',
sync_2d=False,
Expand All @@ -291,7 +291,7 @@ train_pipeline = [
- `GlobalAlignment`: The previous point cloud would be axis-aligned using the axis-aligned matrix.
- `PointSegClassMapping`: Only the valid category ids will be mapped to class label ids like [0, 18) during training.
- Data augmentation:
- `IndoorPointSample`: downsample the input point cloud.
- `PointSample`: downsample the input point cloud.
- `RandomFlip3D`: randomly flip the input point cloud horizontally or vertically.
- `GlobalRotScaleTrans`: rotate the input point cloud, usually in the range of [-5, 5] (degrees) for ScanNet; then scale the input point cloud, usually by 1.0 for ScanNet; finally translate the input point cloud, usually by 0 for ScanNet.

Expand Down
6 changes: 3 additions & 3 deletions docs/datasets/sunrgbd_det.md
Original file line number Diff line number Diff line change
Expand Up @@ -282,7 +282,7 @@ train_pipeline = [
rot_range=[-0.523599, 0.523599],
scale_ratio_range=[0.85, 1.15],
shift_height=True),
dict(type='IndoorPointSample', num_points=20000),
dict(type='PointSample', num_points=20000),
dict(type='DefaultFormatBundle3D', class_names=class_names),
dict(type='Collect3D', keys=['points', 'gt_bboxes_3d', 'gt_labels_3d'])
]
Expand All @@ -291,7 +291,7 @@ train_pipeline = [
Data augmentation for point clouds:
- `RandomFlip3D`: randomly flip the input point cloud horizontally or vertically.
- `GlobalRotScaleTrans`: rotate the input point cloud, usually in the range of [-30, 30] (degrees) for SUN RGB-D; then scale the input point cloud, usually in the range of [0.85, 1.15] for SUN RGB-D; finally translate the input point cloud, usually by 0 for SUN RGB-D.
- `IndoorPointSample`: downsample the input point cloud.
- `PointSample`: downsample the input point cloud.

A typical train pipeline of SUN RGB-D for multi-modality (point cloud and image) 3D detection is as follows.

Expand Down Expand Up @@ -320,7 +320,7 @@ train_pipeline = [
rot_range=[-0.523599, 0.523599],
scale_ratio_range=[0.85, 1.15],
shift_height=True),
dict(type='IndoorPointSample', num_points=20000),
dict(type='PointSample', num_points=20000),
dict(type='DefaultFormatBundle3D', class_names=class_names),
dict(
type='Collect3D',
Expand Down
6 changes: 3 additions & 3 deletions docs_zh-CN/datasets/sunrgbd_det.md
Original file line number Diff line number Diff line change
Expand Up @@ -281,7 +281,7 @@ train_pipeline = [
rot_range=[-0.523599, 0.523599],
scale_ratio_range=[0.85, 1.15],
shift_height=True),
dict(type='IndoorPointSample', num_points=20000),
dict(type='PointSample', num_points=20000),
dict(type='DefaultFormatBundle3D', class_names=class_names),
dict(type='Collect3D', keys=['points', 'gt_bboxes_3d', 'gt_labels_3d'])
]
Expand All @@ -290,7 +290,7 @@ train_pipeline = [
点云上的数据增强
- `RandomFlip3D`:随机左右或前后翻转输入点云。
- `GlobalRotScaleTrans`:旋转输入点云,对于 SUN RGB-D 角度通常落入 [-30, 30] (度)的范围;并放缩输入点云,对于 SUN RGB-D 比例通常落入 [0.85, 1.15] 的范围;最后平移输入点云,对于 SUN RGB-D 通常位移量为 0。
- `IndoorPointSample`:降采样输入点云。
- `PointSample`:降采样输入点云。

SUN RGB-D 上多模态(点云和图像)3D 物体检测的经典流程如下:

Expand Down Expand Up @@ -319,7 +319,7 @@ train_pipeline = [
rot_range=[-0.523599, 0.523599],
scale_ratio_range=[0.85, 1.15],
shift_height=True),
dict(type='IndoorPointSample', num_points=20000),
dict(type='PointSample', num_points=20000),
dict(type='DefaultFormatBundle3D', class_names=class_names),
dict(
type='Collect3D',
Expand Down
10 changes: 5 additions & 5 deletions docs_zh-CN/tutorials/config.md
Original file line number Diff line number Diff line change
Expand Up @@ -204,7 +204,7 @@ train_pipeline = [ # 训练流水线,更多细节请参考 mmdet3d.datasets.p
valid_cat_ids=(3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 14, 16, 24, 28, 33, 34,
36, 39), # 所有有效类别的编号
max_cat_id=40), # 输入语义分割掩码中可能存在的最大类别编号
dict(type='IndoorPointSample', # 室内点采样,更多细节请参考 mmdet3d.datasets.pipelines.indoor_sample
dict(type='PointSample', # 室内点采样,更多细节请参考 mmdet3d.datasets.pipelines.indoor_sample
num_points=40000), # 采样的点的数量
dict(type='IndoorFlipData', # 数据增广流程,随机翻转点和 3D 框
flip_ratio_yz=0.5, # 沿着 yz 平面被翻转的概率
Expand Down Expand Up @@ -233,7 +233,7 @@ test_pipeline = [ # 测试流水线,更多细节请参考 mmdet3d.datasets.pi
shift_height=True, # 是否使用变换高度
load_dim=6, # 读取的点的维度
use_dim=[0, 1, 2]), # 使用所读取点的哪些维度
dict(type='IndoorPointSample', # 室内点采样,更多细节请参考 mmdet3d.datasets.pipelines.indoor_sample
dict(type='PointSample', # 室内点采样,更多细节请参考 mmdet3d.datasets.pipelines.indoor_sample
num_points=40000), # 采样的点的数量
dict(
type='DefaultFormatBundle3D', # 默认格式打包以收集读取的所有数据,更多细节请参考 mmdet3d.datasets.pipelines.formating
Expand Down Expand Up @@ -287,7 +287,7 @@ data = dict(
valid_cat_ids=(3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 14, 16, 24,
28, 33, 34, 36, 39),
max_cat_id=40),
dict(type='IndoorPointSample', num_points=40000),
dict(type='PointSample', num_points=40000),
dict(
type='IndoorFlipData',
flip_ratio_yz=0.5,
Expand Down Expand Up @@ -326,7 +326,7 @@ data = dict(
shift_height=True,
load_dim=6,
use_dim=[0, 1, 2]),
dict(type='IndoorPointSample', num_points=40000),
dict(type='PointSample', num_points=40000),
dict(
type='DefaultFormatBundle3D',
class_names=('cabinet', 'bed', 'chair', 'sofa', 'table',
Expand All @@ -351,7 +351,7 @@ data = dict(
shift_height=True,
load_dim=6,
use_dim=[0, 1, 2]),
dict(type='IndoorPointSample', num_points=40000),
dict(type='PointSample', num_points=40000),
dict(
type='DefaultFormatBundle3D',
class_names=('cabinet', 'bed', 'chair', 'sofa', 'table',
Expand Down
16 changes: 12 additions & 4 deletions mmdet3d/datasets/pipelines/transforms_3d.py
Original file line number Diff line number Diff line change
Expand Up @@ -846,6 +846,10 @@ class PointSample(object):
Args:
num_points (int): Number of points to be sampled.
sample_range (float, optional): The range where to sample points.
If not None, the points with depth larger than `sample_range` are
prior to be sampled. Defaults to None.
replace (bool, optional): Whether the sampling is with or without
replacement. Defaults to False.
"""

def __init__(self, num_points, sample_range=None, replace=False):
Expand All @@ -867,8 +871,7 @@ def _points_random_sampling(self,
points (np.ndarray | :obj:`BasePoints`): 3D Points.
num_samples (int): Number of samples to be sampled.
sample_range (float, optional): Indicating the range where the
points will be sampled.
Defaults to None.
points will be sampled. Defaults to None.
replace (bool, optional): Sampling with or without replacement.
Defaults to None.
return_choices (bool, optional): Whether return choice.
Expand All @@ -886,6 +889,10 @@ def _points_random_sampling(self,
depth = np.linalg.norm(points.tensor, axis=1)
far_inds = np.where(depth > sample_range)[0]
near_inds = np.where(depth <= sample_range)[0]
# in case there are too many far points
if len(far_inds) > num_samples:
far_inds = np.random.choice(
far_inds, num_samples, replace=False)
point_range = near_inds
num_samples -= len(far_inds)
choices = np.random.choice(point_range, num_samples, replace=replace)
Expand All @@ -907,11 +914,11 @@ def __call__(self, results):
dict: Results after sampling, 'points', 'pts_instance_mask' \
and 'pts_semantic_mask' keys are updated in the result dict.
"""
from mmdet3d.core.points import CameraPoints
points = results['points']
# Points in Camera coord can provide the depth information.
# TODO: Need to suport distance-based sampling for other coord system.
if self.sample_range is not None:
from mmdet3d.core.points import CameraPoints
assert isinstance(points, CameraPoints), \
'Sampling based on distance is only appliable for CAMERA coord'
points, choices = self._points_random_sampling(
Expand Down Expand Up @@ -939,7 +946,8 @@ def __repr__(self):
"""str: Return a string that describes the module."""
repr_str = self.__class__.__name__
repr_str += f'(num_points={self.num_points},'
repr_str += f' sample_range={self.sample_range})'
repr_str += f' sample_range={self.sample_range},'
repr_str += f' replace={self.replace})'

return repr_str

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -722,7 +722,7 @@ def test_points_sample():
points.copy(), points_dim=4).convert_to(Coord3DMode.CAM, rect @ Trv2c)
num_points = 20
sample_range = 40
input_dict = dict(points=points)
input_dict = dict(points=points.clone())

point_sample = PointSample(
num_points=num_points, sample_range=sample_range)
Expand All @@ -736,6 +736,17 @@ def test_points_sample():
assert np.allclose(sampled_pts.tensor.numpy(), expected_pts)

repr_str = repr(point_sample)
expected_repr_str = f'PointSample(num_points={num_points},'\
+ f' sample_range={sample_range})'
expected_repr_str = f'PointSample(num_points={num_points}, ' \
f'sample_range={sample_range}, ' \
'replace=False)'
assert repr_str == expected_repr_str

# test when number of far points are larger than number of sampled points
np.random.seed(0)
point_sample = PointSample(num_points=2, sample_range=sample_range)
input_dict = dict(points=points.clone())
sampled_pts = point_sample(input_dict)['points']

select_idx = np.array([449, 444])
expected_pts = points.tensor.numpy()[select_idx]
assert np.allclose(sampled_pts.tensor.numpy(), expected_pts)
4 changes: 3 additions & 1 deletion tests/test_data/test_pipelines/test_indoor_sample.py
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,9 @@ def test_indoor_sample():
sunrgbd_choices = np.array([2, 8, 4, 9, 1])
sunrgbd_points_result = sunrgbd_results['points'].tensor.numpy()
repr_str = repr(sunrgbd_sample_points)
expected_repr_str = 'PointSample(num_points=5, sample_range=None)'
expected_repr_str = 'PointSample(num_points=5, ' \
'sample_range=None, ' \
'replace=False)'
assert repr_str == expected_repr_str
assert np.allclose(sunrgbd_point_cloud[sunrgbd_choices],
sunrgbd_points_result)
Expand Down