From 2691dd6c4016338fd88cdad86245bfe94c6aecdc Mon Sep 17 00:00:00 2001 From: Xiangxu-0103 Date: Thu, 4 Jan 2024 09:02:27 +0000 Subject: [PATCH 1/2] support nus_seg --- configs/_base_/datasets/nus-seg.py | 209 ++++++++++++++++++ mmdet3d/datasets/__init__.py | 3 +- mmdet3d/datasets/nuscenes_dataset.py | 89 +++++++- mmdet3d/datasets/seg3d_dataset.py | 8 +- ...c1e0a0c3b444b97e78d2aa3fa34d2_lidarseg.bin | 1 + tests/data/nuscenes/nus_info.pkl | Bin 26647 -> 26744 bytes ...+0800__LIDAR_TOP__1533201470948018.pcd.bin | Bin 2000 -> 800 bytes tests/test_datasets/test_nuscenes_dataset.py | 184 +++++++++++---- 8 files changed, 451 insertions(+), 43 deletions(-) create mode 100644 configs/_base_/datasets/nus-seg.py create mode 100644 tests/data/nuscenes/lidarseg/v1.0-trainval/5f6c1e0a0c3b444b97e78d2aa3fa34d2_lidarseg.bin diff --git a/configs/_base_/datasets/nus-seg.py b/configs/_base_/datasets/nus-seg.py new file mode 100644 index 0000000000..16e4ac3992 --- /dev/null +++ b/configs/_base_/datasets/nus-seg.py @@ -0,0 +1,209 @@ +# For nuScenes we usually do 16-class segmentation. +# For labels_map we follow the uniform format of MMDetection & MMSegmentation +# i.e. we consider the unlabeled class as the last one, which is different +# from the original implementation of some methods e.g. Cylinder3D. + +dataset_type = 'NuScenesSegDataset' +data_root = 'data/nuscenes/' +class_names = [ + 'barrier', 'bicycle', 'bus', 'car', 'construction_vehicle', 'motorcycle', + 'pedestrian', 'traffic_cone', 'trailer', 'truck', 'driveable_surface', + 'other_flat', 'sidewalk', 'terrain', 'manmade', 'vegetation' +] +labels_map = { + 0: 16, + 1: 16, + 2: 6, + 3: 6, + 4: 6, + 5: 16, + 6: 6, + 7: 16, + 8: 16, + 9: 0, + 10: 16, + 11: 16, + 12: 7, + 13: 16, + 14: 1, + 15: 2, + 16: 2, + 17: 3, + 18: 4, + 19: 16, + 20: 16, + 21: 5, + 22: 8, + 23: 9, + 24: 10, + 25: 11, + 26: 12, + 27: 13, + 28: 14, + 29: 16, + 30: 15, + 31: 16 +} + +metainfo = dict( + classes=class_names, seg_label_mapping=labels_map, max_label=31) + +input_modality = dict(use_lidar=True, use_camera=False) +data_prefix = dict( + pts='samples/LIDAR_TOP', + img='', + pts_semantic_mask='lidarseg/v1.0-trainval') + +# Example to use different file client +# Method 1: simply set the data root and let the file I/O module +# automatically infer from prefix (not support LMDB and Memcache yet) + +# data_root = 's3://openmmlab/datasets/detection3d/nuscenes/' + +# Method 2: Use backend_args, file_client_args in versions before 1.1.0 +# backend_args = dict( +# backend='petrel', +# path_mapping=dict({ +# './data/': 's3://openmmlab/datasets/detection3d/', +# 'data/': 's3://openmmlab/datasets/detection3d/' +# })) +backend_args = None + +train_pipeline = [ + dict( + type='LoadPointsFromFile', + coord_type='LIDAR', + load_dim=5, + use_dim=4, + backend_args=backend_args), + dict( + type='LoadAnnotations3D', + with_bbox_3d=False, + with_label_3d=False, + with_seg_3d=True, + seg_3d_dtype='np.uint8', + backend_args=backend_args), + dict(type='PointSegClassMapping'), + dict( + type='RandomFlip3D', + sync_2d=False, + flip_ratio_bev_horizontal=0.5, + flip_ratio_bev_vertical=0.5), + dict( + type='GlobalRotScaleTrans', + rot_range=[-0.78539816, 0.78539816], + scale_ratio_range=[0.95, 1.05], + translation_std=[0.1, 0.1, 0.1]), + dict(type='Pack3DDetInputs', keys=['points', 'pts_semantic_mask']) +] +test_pipeline = [ + dict( + type='LoadPointsFromFile', + coord_type='LIDAR', + load_dim=5, + use_dim=4, + backend_args=backend_args), + dict( + type='LoadAnnotations3D', + with_bbox_3d=False, + with_label_3d=False, + with_seg_3d=True, + seg_3d_dtype='np.uint8', + backend_args=backend_args), + dict(type='PointSegClassMapping'), + dict(type='Pack3DDetInputs', keys=['points']) +] +tta_pipeline = [ + dict( + type='LoadPointsFromFile', + coord_type='LIDAR', + load_dim=5, + use_dim=4, + backend_args=backend_args), + dict( + type='LoadAnnotations3D', + with_bbox_3d=False, + with_label_3d=False, + with_seg_3d=True, + seg_3d_dtype='np.uint8', + backend_args=backend_args), + dict(type='PointSegClassMapping'), + dict( + type='TestTimeAug', + transforms=[[ + dict( + type='RandomFlip3D', + sync_2d=False, + flip_ratio_bev_horizontal=0., + flip_ratio_bev_vertical=0.), + dict( + type='RandomFlip3D', + sync_2d=False, + flip_ratio_bev_horizontal=0., + flip_ratio_bev_vertical=1.), + dict( + type='RandomFlip3D', + sync_2d=False, + flip_ratio_bev_horizontal=1., + flip_ratio_bev_vertical=0.), + dict( + type='RandomFlip3D', + sync_2d=False, + flip_ratio_bev_horizontal=1., + flip_ratio_bev_vertical=1.) + ], + [ + dict( + type='GlobalRotScaleTrans', + rot_range=[pcd_rotate_range, pcd_rotate_range], + scale_ratio_range=[ + pcd_scale_factor, pcd_scale_factor + ], + translation_std=[0, 0, 0]) + for pcd_rotate_range in [-0.78539816, 0.0, 0.78539816] + for pcd_scale_factor in [0.95, 1.0, 1.05] + ], [dict(type='Pack3DDetInputs', keys=['points'])]]) +] + +train_dataloader = dict( + batch_size=2, + num_workers=4, + persistent_workers=True, + sampler=dict(type='DefaultSampler', shuffle=True), + dataset=dict( + type=dataset_type, + data_root=data_root, + ann_file='nuscenes_infos_train.pkl', + data_prefix=data_prefix, + pipeline=train_pipeline, + metainfo=metainfo, + modality=input_modality, + ignore_index=16, + backend_args=backend_args)) +val_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_file='nuscenes_infos_val.pkl', + data_prefix=data_prefix, + pipeline=test_pipeline, + metainfo=metainfo, + modality=input_modality, + ignore_index=16, + test_mode=True, + backend_args=backend_args)) +test_dataloader = val_dataloader + +val_evaluator = dict(type='SegMetric') +test_evaluator = val_evaluator + +vis_backends = [dict(type='LocalVisBackend')] +visualizer = dict( + type='Det3DLocalVisualizer', vis_backends=vis_backends, name='visualizer') + +tta_model = dict(type='Seg3DTTAModel') diff --git a/mmdet3d/datasets/__init__.py b/mmdet3d/datasets/__init__.py index d573ca4ed9..e071989fab 100644 --- a/mmdet3d/datasets/__init__.py +++ b/mmdet3d/datasets/__init__.py @@ -3,7 +3,7 @@ from .det3d_dataset import Det3DDataset from .kitti_dataset import KittiDataset from .lyft_dataset import LyftDataset -from .nuscenes_dataset import NuScenesDataset +from .nuscenes_dataset import NuScenesDataset, NuScenesSegDataset # yapf: enable from .s3dis_dataset import S3DISDataset, S3DISSegDataset from .scannet_dataset import (ScanNetDataset, ScanNetInstanceSegDataset, @@ -38,4 +38,5 @@ 'VoxelBasedPointSampler', 'get_loading_pipeline', 'RandomDropPointsColor', 'RandomJitterPoints', 'ObjectNameFilter', 'AffineResize', 'RandomShiftScale', 'LoadPointsFromDict', 'Resize3D', 'RandomResize3D', + 'NuScenesSegDataset' ] diff --git a/mmdet3d/datasets/nuscenes_dataset.py b/mmdet3d/datasets/nuscenes_dataset.py index 553480a588..419aeda2f9 100644 --- a/mmdet3d/datasets/nuscenes_dataset.py +++ b/mmdet3d/datasets/nuscenes_dataset.py @@ -1,6 +1,6 @@ # Copyright (c) OpenMMLab. All rights reserved. from os import path as osp -from typing import Callable, List, Union +from typing import Callable, List, Optional, Union import numpy as np @@ -8,6 +8,7 @@ from mmdet3d.structures import LiDARInstance3DBoxes from mmdet3d.structures.bbox_3d.cam_box3d import CameraInstance3DBoxes from .det3d_dataset import Det3DDataset +from .seg3d_dataset import Seg3DDataset @DATASETS.register_module() @@ -246,3 +247,89 @@ def parse_data_info(self, info: dict) -> Union[List[dict], dict]: else: data_info = super().parse_data_info(info) return data_info + + +@DATASETS.register_module() +class NuScenesSegDataset(Seg3DDataset): + """NuScenes Dataset. + + This class serves as the API for experiments on the NuScenes Seg Dataset. + + Please refer to `NuScenes Dataset `_ + for data downloading. + + Args: + data_root (str, optional): Path of dataset root. Defaults to None. + ann_file (str): Path of annotation file. Defaults to ''. + metainfo (dict, optional): Meta information for dataset, such as class + information. Defaults to None. + data_prefix (dict): Prefix for training data. Defaults to + dict(pts='', img='', pts_instance_mask='', pts_semantic_mask=''). + pipeline (List[dict or Callable]): Pipeline used for data + preprocessing. Defaults to []. + modality (dict): Modality to specify the sensor data used as input, + it usually has following keys: + + - use_camera: bool + - use_lidar: bool + + Defaults to dict(use_lidar=True, use_camera=False). + ignore_index (int, optional): The label index to be ignored, e.g. + unannotated points. If None is given, set to len(self.classes) to + be consistent with PointSegClassMapping function in pipeline. + Defaults to None. + scene_idxs (str or np.ndarray, optional): Precomputed index to load + data. For scenes with many points, we may sample it several times. + Defaults to None. + test_mode (bool): Whether the dataset is in test mode. + Defaults to False. + """ + METAINFO = { + 'classes': ('barrier', 'bicycle', 'bus', 'car', 'construction_vehicle', + 'motorcycle', 'pedestrian', 'traffic_cone', 'trailer', + 'truck', 'driveable_surface', 'other_flat', 'sidewalk', + 'terrain', 'manmade', 'vegetation'), + 'palette': [[255, 120, 50], [255, 192, 203], [255, 255, 0], + [0, 150, 245], [0, 255, 255], [255, 127, 0], [255, 0, 0], + [255, 240, 150], [135, 60, 0], [160, 32, + 240], [255, 0, 255], + [139, 137, 137], [75, 0, 75], [150, 240, 80], + [230, 230, 250], [0, 175, 0]], + 'seg_valid_class_ids': + tuple(range(16)), + 'seg_all_class_ids': + tuple(range(16)), + } + + def __init__(self, + data_root: Optional[str] = None, + ann_file: str = '', + metainfo: Optional[dict] = None, + data_prefix: dict = dict( + pts='', + img='', + pts_instance_mask='', + pts_semantic_mask=''), + pipeline: List[Union[dict, Callable]] = [], + modality: dict = dict(use_lidar=True, use_camera=False), + ignore_index: Optional[int] = None, + scene_idxs: Optional[Union[str, np.ndarray]] = None, + test_mode: bool = False, + **kwargs) -> None: + super(NuScenesSegDataset, self).__init__( + data_root=data_root, + ann_file=ann_file, + metainfo=metainfo, + data_prefix=data_prefix, + pipeline=pipeline, + modality=modality, + ignore_index=ignore_index, + scene_idxs=scene_idxs, + test_mode=test_mode, + **kwargs) + + def get_seg_label_mapping(self, metainfo: dict) -> np.ndarray: + seg_label_mapping = np.zeros(metainfo['max_label'] + 1, dtype=np.int64) + for idx in metainfo['seg_label_mapping']: + seg_label_mapping[idx] = metainfo['seg_label_mapping'][idx] + return seg_label_mapping diff --git a/mmdet3d/datasets/seg3d_dataset.py b/mmdet3d/datasets/seg3d_dataset.py index 5c4de10d77..239c4946a8 100644 --- a/mmdet3d/datasets/seg3d_dataset.py +++ b/mmdet3d/datasets/seg3d_dataset.py @@ -263,8 +263,12 @@ def parse_data_info(self, info: dict) -> dict: if self.modality['use_camera']: for cam_id, img_info in info['images'].items(): if 'img_path' in img_info: - img_info['img_path'] = osp.join( - self.data_prefix.get('img', ''), img_info['img_path']) + if cam_id in self.data_prefix: + cam_prefix = self.data_prefix[cam_id] + else: + cam_prefix = self.data_prefix.get('img', '') + img_info['img_path'] = osp.join(cam_prefix, + img_info['img_path']) if 'pts_instance_mask_path' in info: info['pts_instance_mask_path'] = \ diff --git a/tests/data/nuscenes/lidarseg/v1.0-trainval/5f6c1e0a0c3b444b97e78d2aa3fa34d2_lidarseg.bin b/tests/data/nuscenes/lidarseg/v1.0-trainval/5f6c1e0a0c3b444b97e78d2aa3fa34d2_lidarseg.bin new file mode 100644 index 0000000000..f47cb92a88 --- /dev/null +++ b/tests/data/nuscenes/lidarseg/v1.0-trainval/5f6c1e0a0c3b444b97e78d2aa3fa34d2_lidarseg.bin @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/tests/data/nuscenes/nus_info.pkl b/tests/data/nuscenes/nus_info.pkl index e1976f20024a90665321450b9da242c138bd7884..031ed5cf5e0a71d48adcbed05f564d8961564a69 100644 GIT binary patch delta 6386 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 Ti~sLMR-tD_`~zHiUaS8CRX=t+ delta 5175 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 diff --git a/tests/data/nuscenes/samples/LIDAR_TOP/n015-2018-08-02-17-16-37+0800__LIDAR_TOP__1533201470948018.pcd.bin b/tests/data/nuscenes/samples/LIDAR_TOP/n015-2018-08-02-17-16-37+0800__LIDAR_TOP__1533201470948018.pcd.bin index 5c278a6c8e68cd52b07d952b8c98684a64f2ec3e..91b419a6b55ce6027f88d8a4738054abbe553228 100644 GIT binary patch literal 800 zcmZvZYe-XJ7{^~Kuo}%vX`>bmRFaGw7`mx=-luW|D&C0tplOyBXv~!sq2aoLC`xFG zx5iK?l`ut-RrBmUBO@>AiV%&EIV(d#E6B`1hzc%ghv?Pg{ai79h0KjC8CGI$!^uXCz4nVQBgBZT z75=4PrZr*9tkAp}na`@O`~8*3gMm*qdj_H{QWhcOkQ1*2hvJXTRGk+~NZnEFhHEMJ zne|v4bJIEO=tP|lc$Q7P*MxyfQOxt$!Qn?EdJKZ0*8ob9$A1NS9iVG@$69Tf`I2SCNHa@}LUW#h zd3FtQsfJeelNw~k z)!vBcmB_T8C0bL#GTv0Wvm4^oqww%6XP2J{jmDXgS>TY&#e-Vp_5QusVL@i(;)1Ja zS8M?PAZ=0F4H=`J?Ni*{LWfZHVek5GL*Dx|0rkb&EhFN3hkBalWxAQzgt%_ZlkP%D zT92Mym}=r&Z@y!mG>efAyA-rmn<$(O-iCQZVjy!@LDAnt`>Ca0bnT7A4d2)M@MBuO zl+QBi33^-6tAf7S-?Y{LIvopjU`Gh{=0j8Vq|mT9XWN3rJ{&c@X=bNGF5Bq7P-YG7|3DQ2gVU+BCT(cs8CVC^3aMxOLrmkR(9t8k#BzI?mcI}Y*20h zcE42x_N1kn;}rfJT^E*0@yO;%Xow$R3jZB1BJes%C0v@%n3<6Zl;oQ7;i~klg-KC#(OLbvc^AMnEU}8# z(tV=_F`hCx(k%5R>kplPMY1=RS_SG{AtfEkLg@XO^{by_gwkW-^TrmWluAIm!Z`g0iRXKp8T*HU25D0hF3 zlhT|KIksuZM2!sAmtx9{l$lV^;vK|!qzaIao-z&X_clr_Ur(HP6;$Rtpp2tA^PssH z=W<*#x@uTOHu)ukWBmkL$G)#^P)<&BU5Eq)VClV5;3EfI|B92Y<5LM4ux41NB_}~+k3GfdH`8F*53enZN6Bl;^c7<- zz$Ldn*XSP7!Hj1vyqoLy@2gqfMdv{!P{jWb$wuh~LEglha%=`qV`I zdp!iseaQS{wK{Zch?cxL+G#MI68(GBB#?ye=Z3ho%)cm_!^m;^cJe612VV5~$2>Kn z1rN0(HTa65qmcP$ru~|b(pg80ITZ%|cSGoJ4SjASSF@bBT>KK|-mRxOHUl%V$%`hN z8vLLrx|=e#M>G0%hKxjJc|fMpfo9nEwV~y{8%fXmGoW*yJ7o&`bfN3p0`ck3PJ;D+ zzHr=K*HC+~ts~ zN{1^}rNT5%(Z8N+)!5U#2aF$x{<%8VVeQS`F!LPqkGU&BF*#51=k1%p>VN))hnDF_ z{Ba(%6GZ<8F`mu!UB+fFM|SOA#HKO`%9^??eJD`uA}MjWB!L;bSZ^9SD;28GUq_BE z9|fkB%s(AvYBc-h5i&u&#_+^L^iNqJkegqGR6#QHkDZ$aDN6-n9gUqpRN9zFG<&X!8iQ@ft@J=XBD$D`99??Hj hQ3Rg7FdJImV>x!$ar_lU3=WPy3{5R8$Non=|1Wq-Fuwo* diff --git a/tests/test_datasets/test_nuscenes_dataset.py b/tests/test_datasets/test_nuscenes_dataset.py index 4b85f34f4e..8c558316e5 100644 --- a/tests/test_datasets/test_nuscenes_dataset.py +++ b/tests/test_datasets/test_nuscenes_dataset.py @@ -1,11 +1,14 @@ # Copyright (c) OpenMMLab. All rights reserved. +import unittest + import numpy as np from mmcv.transforms.base import BaseTransform from mmengine.registry import TRANSFORMS from mmengine.structures import InstanceData -from mmdet3d.datasets import NuScenesDataset +from mmdet3d.datasets import NuScenesDataset, NuScenesSegDataset from mmdet3d.structures import Det3DDataSample, LiDARInstance3DBoxes +from mmdet3d.utils import register_all_modules def _generate_nus_dataset_config(): @@ -41,41 +44,144 @@ def transform(self, info): return data_root, ann_file, classes, data_prefix, pipeline, modality -def test_getitem(): - np.random.seed(0) - data_root, ann_file, classes, data_prefix, pipeline, modality = \ - _generate_nus_dataset_config() - - nus_dataset = NuScenesDataset( - data_root=data_root, - ann_file=ann_file, - data_prefix=data_prefix, - pipeline=pipeline, - metainfo=dict(classes=classes), - modality=modality) - - nus_dataset.prepare_data(0) - input_dict = nus_dataset.get_data_info(0) - # assert the the path should contains data_prefix and data_root - assert data_prefix['pts'] in input_dict['lidar_points']['lidar_path'] - assert data_root in input_dict['lidar_points']['lidar_path'] - - for cam_id, img_info in input_dict['images'].items(): - if 'img_path' in img_info: - assert data_prefix['img'] in img_info['img_path'] - assert data_root in img_info['img_path'] - - ann_info = nus_dataset.parse_ann_info(input_dict) - - # assert the keys in ann_info and the type - assert 'gt_labels_3d' in ann_info - assert ann_info['gt_labels_3d'].dtype == np.int64 - assert len(ann_info['gt_labels_3d']) == 37 - - assert 'gt_bboxes_3d' in ann_info - assert isinstance(ann_info['gt_bboxes_3d'], LiDARInstance3DBoxes) - - assert len(nus_dataset.metainfo['classes']) == 10 - - assert input_dict['token'] == 'fd8420396768425eabec9bdddf7e64b6' - assert input_dict['timestamp'] == 1533201470.448696 +def _generate_nus_seg_dataset_config(): + data_root = './tests/data/nuscenes' + ann_file = 'nus_info.pkl' + classes = ('barrier', 'bicycle', 'bus', 'car', 'construction_vehicle', + 'motorcycle', 'pedestrian', 'traffic_cone', 'trailer', 'truck', + 'driveable_surface', 'other_flat', 'sidewalk', 'terrain', + 'manmade', 'vegetation') + seg_label_mapping = { + 0: 16, + 1: 16, + 2: 6, + 3: 6, + 4: 6, + 5: 16, + 6: 6, + 7: 16, + 8: 16, + 9: 0, + 10: 16, + 11: 16, + 12: 7, + 13: 16, + 14: 1, + 15: 2, + 16: 2, + 17: 3, + 18: 4, + 19: 16, + 20: 16, + 21: 5, + 22: 8, + 23: 9, + 24: 10, + 25: 11, + 26: 12, + 27: 13, + 28: 14, + 29: 16, + 30: 15, + 31: 16 + } + max_label = 31 + modality = dict(use_lidar=True, use_camera=False) + pipeline = [ + dict( + type='LoadPointsFromFile', + coord_type='LIDAR', + shift_height=True, + load_dim=5, + use_dim=4), + dict( + type='LoadAnnotations3D', + with_bbox_3d=False, + with_label_3d=False, + with_mask_3d=False, + with_seg_3d=True, + seg_3d_dtype='np.uint8'), + dict(type='PointSegClassMapping'), + dict(type='Pack3DDetInputs', keys=['points', 'pts_semantic_mask']) + ] + data_prefix = dict( + pts='samples/LIDAR_TOP', pts_semantic_mask='lidarseg/v1.0-trainval') + + return (data_root, ann_file, classes, data_prefix, pipeline, modality, + seg_label_mapping, max_label) + + +class TestNuScenesDataset(unittest.TestCase): + + def test_nuscenes(self): + np.random.seed(0) + data_root, ann_file, classes, data_prefix, pipeline, modality = \ + _generate_nus_dataset_config() + + nus_dataset = NuScenesDataset( + data_root=data_root, + ann_file=ann_file, + data_prefix=data_prefix, + pipeline=pipeline, + metainfo=dict(classes=classes), + modality=modality) + + nus_dataset.prepare_data(0) + input_dict = nus_dataset.get_data_info(0) + # assert the path should contains data_prefix and data_root + self.assertIn(data_prefix['pts'], + input_dict['lidar_points']['lidar_path']) + self.assertIn(data_root, input_dict['lidar_points']['lidar_path']) + + for cam_id, img_info in input_dict['images'].items(): + if 'img_path' in img_info: + self.assertIn(data_prefix['img'], img_info['img_path']) + self.assertIn(data_root, img_info['img_path']) + + ann_info = nus_dataset.parse_ann_info(input_dict) + + # assert the keys in ann_info and the type + self.assertIn('gt_labels_3d', ann_info) + self.assertEqual(ann_info['gt_labels_3d'].dtype, np.int64) + assert len(ann_info['gt_labels_3d']) == 37 + + self.assertIn('gt_bboxes_3d', ann_info) + self.assertIsInstance(ann_info['gt_bboxes_3d'], LiDARInstance3DBoxes) + + assert len(nus_dataset.metainfo['classes']) == 10 + + self.assertEqual(input_dict['token'], + 'fd8420396768425eabec9bdddf7e64b6') + self.assertEqual(input_dict['timestamp'], 1533201470.448696) + + def test_nuscenes_seg(self): + data_root, ann_file, classes, data_prefix, pipeline, modality, \ + seg_label_mapping, max_label = _generate_nus_seg_dataset_config() + + register_all_modules() + np.random.seed(0) + + nus_seg_dataset = NuScenesSegDataset( + data_root=data_root, + ann_file=ann_file, + data_prefix=data_prefix, + pipeline=pipeline, + metainfo=dict( + classes=classes, + seg_label_mapping=seg_label_mapping, + max_label=max_label), + modality=modality) + + expected_pts_semantic_mask = np.array([ + 10, 10, 14, 14, 10, 16, 14, 10, 16, 14, 10, 10, 10, 10, 13, 10, 14, + 14, 10, 16, 14, 3, 16, 14, 16, 10, 10, 16, 16, 10, 10, 14, 16, 10, + 15, 14, 14, 14, 16, 3 + ]) + + input_dict = nus_seg_dataset.prepare_data(0) + points = input_dict['inputs']['points'] + data_sample = input_dict['data_samples'] + pts_semantic_mask = data_sample.gt_pts_seg.pts_semantic_mask + self.assertEqual(points.shape[0], pts_semantic_mask.shape[0]) + self.assertTrue( + (pts_semantic_mask.numpy() == expected_pts_semantic_mask).all()) From 2a1556548c0af3bc35559cb70a05fbfb2d08bd20 Mon Sep 17 00:00:00 2001 From: Xiang Xu Date: Fri, 19 Jan 2024 14:28:49 +0800 Subject: [PATCH 2/2] add docs --- mmdet3d/datasets/nuscenes_dataset.py | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/mmdet3d/datasets/nuscenes_dataset.py b/mmdet3d/datasets/nuscenes_dataset.py index 419aeda2f9..490344ff6b 100644 --- a/mmdet3d/datasets/nuscenes_dataset.py +++ b/mmdet3d/datasets/nuscenes_dataset.py @@ -329,6 +329,18 @@ def __init__(self, **kwargs) def get_seg_label_mapping(self, metainfo: dict) -> np.ndarray: + """Get segmentation label mapping. + + The ``seg_label_mapping`` is an array, its indices are the old label + ids and its values are the new label ids, and is specifically used for + changing point labels in PointSegClassMapping. + + Args: + metainfo (dict): Meta information to set seg_label_mapping. + + Returns: + np.ndarray: The mapping from old classes to new classes. + """ seg_label_mapping = np.zeros(metainfo['max_label'] + 1, dtype=np.int64) for idx in metainfo['seg_label_mapping']: seg_label_mapping[idx] = metainfo['seg_label_mapping'][idx]