├── pcdet ├── datasets │ ├── once │ │ ├── __init__.py │ │ └── once_eval │ │ │ └── eval_utils.py │ ├── kitti │ │ ├── kitti_object_eval_python │ │ │ ├── evaluate.py │ │ │ ├── LICENSE │ │ │ └── README.md │ │ └── kitti_utils.py │ ├── kitti360 │ │ └── kitti360_dataset.py │ ├── processor │ │ └── point_feature_encoder.py │ └── __init__.py ├── models │ ├── img_backbones │ │ └── __init__.py │ ├── fusion_layers │ │ ├── __init__.py │ │ └── point_sample.py │ ├── backbones_3d │ │ ├── pfe │ │ │ └── __init__.py │ │ ├── vfe │ │ │ ├── __init__.py │ │ │ ├── vfe_template.py │ │ │ └── mean_vfe.py │ │ └── __init__.py │ ├── roi_heads │ │ └── __init__.py │ ├── backbones_2d │ │ ├── __init__.py │ │ ├── map_to_bev │ │ │ ├── __init__.py │ │ │ ├── height_compression.py │ │ │ └── pointpillar_scatter.py │ │ ├── sst_bev_backbone.py │ │ └── base_bev_backbone.py │ ├── dense_heads │ │ ├── __init__.py │ │ ├── anchor_head_single.py │ │ ├── point_head_simple.py │ │ └── target_assigner │ │ │ └── anchor_generator.py │ ├── detectors │ │ ├── __init__.py │ │ ├── ssd3d.py │ │ ├── second_net.py │ │ ├── pointpillar.py │ │ ├── gd_mae.py │ │ ├── graph_rcnn.py │ │ └── centerpoint.py │ ├── __init__.py │ └── model_utils │ │ ├── fusion_utils.py │ │ └── model_nms_utils.py ├── ops │ ├── pointnet2 │ │ ├── pointnet2_stack │ │ │ ├── src │ │ │ │ ├── cuda_utils.h │ │ │ │ ├── sampling_gpu.h │ │ │ │ ├── voxel_query_gpu.h │ │ │ │ ├── ball_query_gpu.h │ │ │ │ ├── pointnet2_api.cpp │ │ │ │ ├── sampling.cpp │ │ │ │ ├── group_points_gpu.h │ │ │ │ ├── interpolate_gpu.h │ │ │ │ ├── voxel_query.cpp │ │ │ │ ├── ball_query.cpp │ │ │ │ ├── group_points.cpp │ │ │ │ ├── ball_query_gpu.cu │ │ │ │ ├── interpolate.cpp │ │ │ │ └── voxel_query_gpu.cu │ │ │ └── voxel_query_utils.py │ │ └── pointnet2_batch │ │ │ └── src │ │ │ ├── cuda_utils.h │ │ │ ├── group_points_gpu.h │ │ │ ├── ball_query_gpu.h │ │ │ ├── interpolate_gpu.h │ │ │ ├── group_points.cpp │ │ │ ├── sampling_gpu.h │ │ │ ├── pointnet2_api.cpp │ │ │ ├── ball_query.cpp │ │ │ ├── sampling.cpp │ │ │ ├── interpolate.cpp │ │ │ └── group_points_gpu.cu │ ├── patch_ops │ │ └── src │ │ │ ├── cuda_utils.h │ │ │ ├── patch_ops_api.cpp │ │ │ ├── patch_query_gpu.h │ │ │ ├── roipatch_dfvs_pool3d_gpu.h │ │ │ ├── patch_query.cpp │ │ │ └── roipatch_dfvs_pool3d.cpp │ ├── iou3d_nms │ │ ├── src │ │ │ ├── iou3d_cpu.h │ │ │ ├── iou3d_nms.h │ │ │ └── iou3d_nms_api.cpp │ │ └── iou3d_nms_utils.py │ ├── dcn │ │ ├── __init__.py │ │ └── setup.py │ ├── sst_ops │ │ ├── src │ │ │ ├── sst_ops_api.cpp │ │ │ ├── sst_ops_gpu.h │ │ │ ├── sst_ops.cpp │ │ │ └── sst_ops_gpu.cu │ │ └── sst_ops_utils.py │ ├── roipoint_pool3d │ │ ├── src │ │ │ └── roipoint_pool3d.cpp │ │ └── roipoint_pool3d_utils.py │ └── roiaware_pool3d │ │ └── roiaware_pool3d_utils.py ├── __init__.py ├── config.py └── utils │ ├── spconv_utils.py │ ├── object3d_kitti.py │ ├── transform_utils.py │ └── calibration_kitti.py ├── tools ├── _init_path.py ├── scripts │ ├── dist_train.sh │ ├── dist_test.sh │ ├── dist_ts_train.sh │ └── dist_ssl_train.sh ├── cfgs │ ├── dataset_configs │ │ ├── kitti360_dataset.yaml │ │ ├── once_dataset.yaml │ │ ├── kitti_dataset.yaml │ │ ├── nuscenes_dataset.yaml │ │ └── waymo_dataset.yaml │ ├── kitti_models │ │ ├── second_mini.yaml │ │ ├── pointpillar.yaml │ │ └── 3dssd.yaml │ ├── waymo_models │ │ └── centerpoint.yaml │ └── once_models │ │ └── centerpoint_pillar.yaml ├── train_utils │ └── optimization │ │ ├── __init__.py │ │ └── learning_schedules_fastai.py └── visual_utils │ └── open3d_vis_utils.py ├── data └── once │ └── ImageSets │ ├── val.txt │ ├── train.txt │ ├── test.txt │ ├── raw_small.txt │ └── raw_medium.txt ├── setup.cfg ├── .gitignore └── setup.py /pcdet/datasets/once/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /tools/_init_path.py: -------------------------------------------------------------------------------- 1 | import sys 2 | sys.path.insert(0, '../') -------------------------------------------------------------------------------- /data/once/ImageSets/val.txt: -------------------------------------------------------------------------------- 1 | 000027 2 | 000028 3 | 000112 4 | 000201 5 | -------------------------------------------------------------------------------- /setup.cfg: -------------------------------------------------------------------------------- 1 | [easy_install] 2 | 3 | index_url = https://mirrors.bfsu.edu.cn/pypi/web/simple 4 | -------------------------------------------------------------------------------- /data/once/ImageSets/train.txt: -------------------------------------------------------------------------------- 1 | 000076 2 | 000080 3 | 000092 4 | 000104 5 | 000113 6 | 000121 7 | -------------------------------------------------------------------------------- /pcdet/models/img_backbones/__init__.py: -------------------------------------------------------------------------------- 1 | from .dla import DLASeg 2 | 3 | __all__ = { 4 | 'DLASeg': DLASeg 5 | } -------------------------------------------------------------------------------- /pcdet/models/fusion_layers/__init__.py: -------------------------------------------------------------------------------- 1 | from .point_sample import PointSample 2 | 3 | 4 | __all__ = { 5 | 'PointSample': PointSample 6 | } 7 | -------------------------------------------------------------------------------- /data/once/ImageSets/test.txt: -------------------------------------------------------------------------------- 1 | 000034 2 | 000077 3 | 000168 4 | 000200 5 | 000273 6 | 000275 7 | 000303 8 | 000318 9 | 000322 10 | 000334 11 | -------------------------------------------------------------------------------- /pcdet/models/backbones_3d/pfe/__init__.py: -------------------------------------------------------------------------------- 1 | from .voxel_set_abstraction import VoxelSetAbstraction 2 | 3 | __all__ = { 4 | 'VoxelSetAbstraction': VoxelSetAbstraction 5 | } 6 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | **__pycache__** 2 | **build** 3 | **egg-info** 4 | *.pyc 5 | venv/ 6 | *.idea/ 7 | *.so 8 | *.pth 9 | *.pkl 10 | *.zip 11 | *.bin 12 | output 13 | version.py 14 | .vscode -------------------------------------------------------------------------------- /pcdet/models/roi_heads/__init__.py: -------------------------------------------------------------------------------- 1 | from .roi_head_template import RoIHeadTemplate 2 | from .graphrcnn_head import GraphRCNNHead 3 | 4 | 5 | __all__ = { 6 | 'RoIHeadTemplate': RoIHeadTemplate, 7 | 'GraphRCNNHead': GraphRCNNHead 8 | } 9 | -------------------------------------------------------------------------------- /pcdet/models/backbones_2d/__init__.py: -------------------------------------------------------------------------------- 1 | from .base_bev_backbone import BaseBEVBackbone 2 | from .sst_bev_backbone import SSTBEVBackbone 3 | 4 | 5 | __all__ = { 6 | 'BaseBEVBackbone': BaseBEVBackbone, 7 | 'SSTBEVBackbone': SSTBEVBackbone 8 | } 9 | -------------------------------------------------------------------------------- /pcdet/ops/pointnet2/pointnet2_stack/src/cuda_utils.h: -------------------------------------------------------------------------------- 1 | #ifndef _STACK_CUDA_UTILS_H 2 | #define _STACK_CUDA_UTILS_H 3 | 4 | #include 5 | 6 | #define THREADS_PER_BLOCK 256 7 | #define DIVUP(m,n) ((m) / (n) + ((m) % (n) > 0)) 8 | 9 | #endif 10 | -------------------------------------------------------------------------------- /pcdet/models/backbones_2d/map_to_bev/__init__.py: -------------------------------------------------------------------------------- 1 | from .height_compression import HeightCompression 2 | from .pointpillar_scatter import PointPillarScatter 3 | 4 | __all__ = { 5 | 'HeightCompression': HeightCompression, 6 | 'PointPillarScatter': PointPillarScatter 7 | } 8 | -------------------------------------------------------------------------------- /pcdet/ops/patch_ops/src/cuda_utils.h: -------------------------------------------------------------------------------- 1 | #ifndef _STACK_CUDA_UTILS_H 2 | #define _STACK_CUDA_UTILS_H 3 | 4 | #include 5 | 6 | #define THREADS_PER_BLOCK 256 7 | #define EMPTY_KEY -1 8 | #define DIVUP(m,n) ((m) / (n) + ((m) % (n) > 0)) 9 | 10 | #endif 11 | -------------------------------------------------------------------------------- /pcdet/ops/iou3d_nms/src/iou3d_cpu.h: -------------------------------------------------------------------------------- 1 | #ifndef IOU3D_CPU_H 2 | #define IOU3D_CPU_H 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | int boxes_iou_bev_cpu(at::Tensor boxes_a_tensor, at::Tensor boxes_b_tensor, at::Tensor ans_iou_tensor); 10 | 11 | #endif 12 | -------------------------------------------------------------------------------- /pcdet/models/backbones_3d/vfe/__init__.py: -------------------------------------------------------------------------------- 1 | from .mean_vfe import MeanVFE 2 | from .pillar_vfe import PillarVFE 3 | from .vfe_template import VFETemplate 4 | from .dyn_vfe import DynVFE 5 | 6 | 7 | __all__ = { 8 | 'VFETemplate': VFETemplate, 9 | 'MeanVFE': MeanVFE, 10 | 'PillarVFE': PillarVFE, 11 | 'DynVFE': DynVFE 12 | } 13 | -------------------------------------------------------------------------------- /pcdet/ops/dcn/__init__.py: -------------------------------------------------------------------------------- 1 | from .deform_conv import (DeformConv, DeformConvPack, ModulatedDeformConv, 2 | ModulatedDeformConvPack, deform_conv, 3 | modulated_deform_conv) 4 | 5 | __all__ = [ 6 | 'DeformConv', 'DeformConvPack', 'ModulatedDeformConv', 7 | 'ModulatedDeformConvPack', 'deform_conv', 'modulated_deform_conv' 8 | ] 9 | -------------------------------------------------------------------------------- /pcdet/ops/sst_ops/src/sst_ops_api.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include "sst_ops_gpu.h" 5 | 6 | PYBIND11_MODULE(TORCH_EXTENSION_NAME, m) { 7 | m.def("ingroup_inds_wrapper", &ingroup_inds_wrapper, "ingroup_inds_wrapper"); 8 | m.def("group_inner_inds_wrapper", &group_inner_inds_wrapper, "group_inner_inds_wrapper"); 9 | } 10 | -------------------------------------------------------------------------------- /pcdet/ops/pointnet2/pointnet2_batch/src/cuda_utils.h: -------------------------------------------------------------------------------- 1 | #ifndef _CUDA_UTILS_H 2 | #define _CUDA_UTILS_H 3 | 4 | #include 5 | 6 | #define TOTAL_THREADS 1024 7 | #define THREADS_PER_BLOCK 256 8 | #define DIVUP(m,n) ((m) / (n) + ((m) % (n) > 0)) 9 | 10 | inline int opt_n_threads(int work_size) { 11 | const int pow_2 = std::log(static_cast(work_size)) / std::log(2.0); 12 | 13 | return max(min(1 << pow_2, TOTAL_THREADS), 1); 14 | } 15 | #endif 16 | -------------------------------------------------------------------------------- /pcdet/ops/patch_ops/src/patch_ops_api.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include "patch_query_gpu.h" 5 | #include "roipatch_dfvs_pool3d_gpu.h" 6 | 7 | 8 | PYBIND11_MODULE(TORCH_EXTENSION_NAME, m) { 9 | m.def("patch_query_wrapper", &patch_query_wrapper_stack, "patch_query_wrapper_stack"); 10 | m.def("roilocal_dfvs_pool3d_wrapper_v2", &roilocal_dfvs_pool3d_wrapper_stack_v2, "roilocal_dfvs_pool3d_wrapper_stack_v2"); 11 | } 12 | -------------------------------------------------------------------------------- /pcdet/ops/pointnet2/pointnet2_stack/src/sampling_gpu.h: -------------------------------------------------------------------------------- 1 | #ifndef _SAMPLING_GPU_H 2 | #define _SAMPLING_GPU_H 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | 9 | int furthest_point_sampling_wrapper(int b, int n, int m, 10 | at::Tensor points_tensor, at::Tensor temp_tensor, at::Tensor idx_tensor); 11 | 12 | void furthest_point_sampling_kernel_launcher(int b, int n, int m, 13 | const float *dataset, float *temp, int *idxs); 14 | 15 | #endif 16 | -------------------------------------------------------------------------------- /pcdet/ops/iou3d_nms/src/iou3d_nms.h: -------------------------------------------------------------------------------- 1 | #ifndef IOU3D_NMS_H 2 | #define IOU3D_NMS_H 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | int boxes_overlap_bev_gpu(at::Tensor boxes_a, at::Tensor boxes_b, at::Tensor ans_overlap); 10 | int boxes_iou_bev_gpu(at::Tensor boxes_a, at::Tensor boxes_b, at::Tensor ans_iou); 11 | int nms_gpu(at::Tensor boxes, at::Tensor keep, float nms_overlap_thresh); 12 | int nms_normal_gpu(at::Tensor boxes, at::Tensor keep, float nms_overlap_thresh); 13 | 14 | #endif 15 | -------------------------------------------------------------------------------- /pcdet/models/backbones_3d/__init__.py: -------------------------------------------------------------------------------- 1 | from .pointnet2_backbone import PointNet2MSG, PointNet2SAMSG 2 | from .spconv_backbone import VoxelBackBone8x, VoxelResBackBone8x 3 | from .spconv_unet import UNetV2 4 | from .spt_backbone import SPTBackbone 5 | from .spt_backbone_mae import SPTBackboneMAE 6 | 7 | 8 | __all__ = { 9 | 'VoxelBackBone8x': VoxelBackBone8x, 10 | 'UNetV2': UNetV2, 11 | 'PointNet2MSG': PointNet2MSG, 12 | 'PointNet2SAMSG': PointNet2SAMSG, 13 | 'VoxelResBackBone8x': VoxelResBackBone8x, 14 | 'SPTBackboneMAE': SPTBackboneMAE, 15 | 'SPTBackbone': SPTBackbone 16 | } 17 | -------------------------------------------------------------------------------- /pcdet/models/backbones_3d/vfe/vfe_template.py: -------------------------------------------------------------------------------- 1 | import torch.nn as nn 2 | 3 | 4 | class VFETemplate(nn.Module): 5 | def __init__(self, model_cfg, **kwargs): 6 | super().__init__() 7 | self.model_cfg = model_cfg 8 | 9 | def get_output_feature_dim(self): 10 | raise NotImplementedError 11 | 12 | def forward(self, **kwargs): 13 | """ 14 | Args: 15 | **kwargs: 16 | 17 | Returns: 18 | batch_dict: 19 | ... 20 | vfe_features: (num_voxels, C) 21 | """ 22 | raise NotImplementedError 23 | -------------------------------------------------------------------------------- /pcdet/ops/sst_ops/src/sst_ops_gpu.h: -------------------------------------------------------------------------------- 1 | #ifndef _SST_OPS_GPU_H 2 | #define _SST_OPS_GPU_H 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | int ingroup_inds_wrapper(at::Tensor group_inds_tensor, at::Tensor out_inds_tensor); 10 | 11 | void ingroup_inds_launcher(int N, int max_group_id, const long *group_inds, long *out_inds); 12 | 13 | int group_inner_inds_wrapper(at::Tensor inverse_inds_tensor, at::Tensor group_inds_tensor); 14 | 15 | void group_inner_inds_launcher(int N, int M, int K, const long *inverse_inds, long *group_inds); 16 | 17 | #endif 18 | -------------------------------------------------------------------------------- /pcdet/__init__.py: -------------------------------------------------------------------------------- 1 | import subprocess 2 | from pathlib import Path 3 | 4 | from .version import __version__ 5 | 6 | __all__ = [ 7 | '__version__' 8 | ] 9 | 10 | 11 | def get_git_commit_number(): 12 | if not (Path(__file__).parent / '../.git').exists(): 13 | return '0000000' 14 | 15 | cmd_out = subprocess.run(['git', 'rev-parse', 'HEAD'], stdout=subprocess.PIPE) 16 | git_commit_number = cmd_out.stdout.decode('utf-8')[:7] 17 | return git_commit_number 18 | 19 | 20 | script_version = get_git_commit_number() 21 | 22 | 23 | if script_version not in __version__: 24 | __version__ = __version__ + '+py%s' % script_version 25 | -------------------------------------------------------------------------------- /pcdet/ops/iou3d_nms/src/iou3d_nms_api.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | #include "iou3d_cpu.h" 8 | #include "iou3d_nms.h" 9 | 10 | 11 | PYBIND11_MODULE(TORCH_EXTENSION_NAME, m) { 12 | m.def("boxes_overlap_bev_gpu", &boxes_overlap_bev_gpu, "oriented boxes overlap"); 13 | m.def("boxes_iou_bev_gpu", &boxes_iou_bev_gpu, "oriented boxes iou"); 14 | m.def("nms_gpu", &nms_gpu, "oriented nms gpu"); 15 | m.def("nms_normal_gpu", &nms_normal_gpu, "nms gpu"); 16 | m.def("boxes_iou_bev_cpu", &boxes_iou_bev_cpu, "oriented boxes iou"); 17 | } 18 | -------------------------------------------------------------------------------- /tools/scripts/dist_train.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | while true 4 | do 5 | PORT=$(( ((RANDOM<<15)|RANDOM) % 49152 + 10000 )) 6 | status="$(nc -z 127.0.0.1 $PORT < /dev/null &>/dev/null; echo $?)" 7 | if [ "${status}" != "0" ]; then 8 | break; 9 | fi 10 | done 11 | echo $PORT 12 | 13 | export CUDA_VISIBLE_DEVICES=0,1,2,3 14 | 15 | NGPUS=4 16 | 17 | CFG_NAME=kitti_models/graph_rcnn_po 18 | TAG_NAME=default 19 | 20 | python -m torch.distributed.launch --nproc_per_node=${NGPUS} --master_port $PORT train.py --launcher pytorch --cfg_file cfgs/$CFG_NAME.yaml --workers 2 --extra_tag $TAG_NAME --max_ckpt_save_num 10 --num_epochs_to_eval 10 21 | -------------------------------------------------------------------------------- /tools/scripts/dist_test.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | while true 4 | do 5 | PORT=$(( ((RANDOM<<15)|RANDOM) % 49152 + 10000 )) 6 | status="$(nc -z 127.0.0.1 $PORT < /dev/null &>/dev/null; echo $?)" 7 | if [ "${status}" != "0" ]; then 8 | break; 9 | fi 10 | done 11 | echo $PORT 12 | 13 | export CUDA_VISIBLE_DEVICES=0,1,2,3 14 | 15 | NGPUS=4 16 | 17 | CFG_NAME=kitti_models/graph_rcnn_po 18 | TAG_NAME=default 19 | 20 | CKPT=../data/ckpts/graph_rcnn_po.pth 21 | 22 | python -m torch.distributed.launch --nproc_per_node=${NGPUS} --master_port $PORT test.py --launcher pytorch --cfg_file cfgs/$CFG_NAME.yaml --workers 4 --extra_tag $TAG_NAME --ckpt $CKPT 23 | -------------------------------------------------------------------------------- /pcdet/ops/dcn/setup.py: -------------------------------------------------------------------------------- 1 | from setuptools import setup 2 | from torch.utils.cpp_extension import BuildExtension, CUDAExtension 3 | 4 | setup( 5 | name='masked_conv', 6 | ext_modules=[ 7 | CUDAExtension('deform_conv_cuda', [ 8 | 'src/deform_conv_cuda.cpp', 9 | 'src/deform_conv_cuda_kernel.cu', 10 | ], 11 | define_macros=[('WITH_CUDA', None)], 12 | extra_compile_args={ 13 | 'cxx': [], 14 | 'nvcc': [ 15 | '-D__CUDA_NO_HALF_OPERATORS__', 16 | '-D__CUDA_NO_HALF_CONVERSIONS__', 17 | '-D__CUDA_NO_HALF2_OPERATORS__', 18 | ]})], 19 | cmdclass={'build_ext': BuildExtension}) 20 | 21 | -------------------------------------------------------------------------------- /pcdet/models/dense_heads/__init__.py: -------------------------------------------------------------------------------- 1 | from .anchor_head_multi import AnchorHeadMulti 2 | from .anchor_head_single import AnchorHeadSingle 3 | from .anchor_head_template import AnchorHeadTemplate 4 | from .point_head_box import PointHeadBox 5 | from .point_head_simple import PointHeadSimple 6 | from .point_intra_part_head import PointIntraPartOffsetHead 7 | from .center_head import CenterHead 8 | from .point_head_vote import PointHeadVote 9 | 10 | __all__ = { 11 | 'AnchorHeadTemplate': AnchorHeadTemplate, 12 | 'AnchorHeadSingle': AnchorHeadSingle, 13 | 'PointIntraPartOffsetHead': PointIntraPartOffsetHead, 14 | 'PointHeadSimple': PointHeadSimple, 15 | 'PointHeadBox': PointHeadBox, 16 | 'AnchorHeadMulti': AnchorHeadMulti, 17 | 'CenterHead': CenterHead, 18 | 'PointHeadVote': PointHeadVote 19 | } 20 | -------------------------------------------------------------------------------- /pcdet/ops/pointnet2/pointnet2_stack/src/voxel_query_gpu.h: -------------------------------------------------------------------------------- 1 | #ifndef _STACK_VOXEL_QUERY_GPU_H 2 | #define _STACK_VOXEL_QUERY_GPU_H 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | int voxel_query_wrapper_stack(int M, int R1, int R2, int R3, int nsample, float radius, 10 | int z_range, int y_range, int x_range, at::Tensor new_xyz_tensor, at::Tensor xyz_tensor, 11 | at::Tensor new_coords_tensor, at::Tensor point_indices_tensor, at::Tensor idx_tensor); 12 | 13 | 14 | void voxel_query_kernel_launcher_stack(int M, int R1, int R2, int R3, int nsample, 15 | float radius, int z_range, int y_range, int x_range, const float *new_xyz, 16 | const float *xyz, const int *new_coords, const int *point_indices, int *idx); 17 | 18 | 19 | #endif 20 | -------------------------------------------------------------------------------- /pcdet/models/detectors/__init__.py: -------------------------------------------------------------------------------- 1 | from .detector3d_template import Detector3DTemplate 2 | from .pointpillar import PointPillar 3 | from .second_net import SECONDNet 4 | from .centerpoint import CenterPoint 5 | from .ssd3d import SSD3D 6 | from .graph_rcnn import GraphRCNN 7 | from .gd_mae import GDMAE 8 | 9 | 10 | __all__ = { 11 | 'Detector3DTemplate': Detector3DTemplate, 12 | 'SECONDNet': SECONDNet, 13 | 'PointPillar': PointPillar, 14 | 'CenterPoint': CenterPoint, 15 | 'GraphRCNN': GraphRCNN, 16 | 'SSD3D': SSD3D, 17 | 'GraphRCNN': GraphRCNN, 18 | 'GDMAE': GDMAE 19 | } 20 | 21 | 22 | def build_detector(model_cfg, num_class, dataset, logger): 23 | model = __all__[model_cfg.NAME]( 24 | model_cfg=model_cfg, num_class=num_class, dataset=dataset, logger=logger 25 | ) 26 | 27 | return model 28 | -------------------------------------------------------------------------------- /data/once/ImageSets/raw_small.txt: -------------------------------------------------------------------------------- 1 | 000000 2 | 000001 3 | 000002 4 | 000003 5 | 000004 6 | 000005 7 | 000006 8 | 000007 9 | 000008 10 | 000009 11 | 000010 12 | 000011 13 | 000012 14 | 000013 15 | 000014 16 | 000015 17 | 000016 18 | 000017 19 | 000018 20 | 000019 21 | 000020 22 | 000021 23 | 000022 24 | 000023 25 | 000024 26 | 000025 27 | 000026 28 | 000029 29 | 000030 30 | 000031 31 | 000032 32 | 000033 33 | 000035 34 | 000036 35 | 000037 36 | 000038 37 | 000039 38 | 000040 39 | 000041 40 | 000042 41 | 000043 42 | 000044 43 | 000045 44 | 000046 45 | 000047 46 | 000048 47 | 000049 48 | 000050 49 | 000051 50 | 000052 51 | 000053 52 | 000054 53 | 000055 54 | 000056 55 | 000057 56 | 000058 57 | 000059 58 | 000060 59 | 000061 60 | 000062 61 | 000063 62 | 000064 63 | 000065 64 | 000066 65 | 000067 66 | 000068 67 | 000069 68 | 000070 69 | 000071 70 | 000072 71 | 000073 -------------------------------------------------------------------------------- /pcdet/ops/patch_ops/src/patch_query_gpu.h: -------------------------------------------------------------------------------- 1 | #ifndef _STACK_VOXEL_QUERY_GPU_H 2 | #define _STACK_VOXEL_QUERY_GPU_H 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | int patch_query_wrapper_stack(int batch_size, int boxes_num, int boxes_per_patch_num, 10 | int range_x, int range_y, float offset_x, float offset_y, float patch_size_x, float patch_size_y, 11 | at::Tensor boxes3d_tensor, at::Tensor patch_indices_tensor, at::Tensor patch2box_indices_tensor); 12 | 13 | void patch_query_kernel_launcher_stack(int batch_size, int boxes_num, int boxes_per_patch_num, 14 | int range_x, int range_y, float offset_x, float offset_y, float patch_size_x, float patch_size_y, 15 | const float *boxes3d, const int *patch_indices, int *patch2box_indices); 16 | 17 | 18 | #endif 19 | -------------------------------------------------------------------------------- /pcdet/ops/sst_ops/sst_ops_utils.py: -------------------------------------------------------------------------------- 1 | import torch 2 | from . import sst_ops_cuda 3 | 4 | 5 | def get_inner_win_inds(group_inds): 6 | """ 7 | Args: 8 | group_inds: (N,) 9 | """ 10 | out_inds = torch.zeros_like(group_inds) - 1 11 | sst_ops_cuda.ingroup_inds_wrapper(group_inds.contiguous(), out_inds) 12 | return out_inds 13 | 14 | 15 | def group_inner_inds(points, inverse_inds, K): 16 | """ 17 | Args: 18 | points: (N, C) 19 | inverse_inds: (N, ) 20 | Return: 21 | group_points: (valid_voxel_num + 1, K, C) 22 | """ 23 | valid_voxel_num = inverse_inds.max().item() 24 | group_inds = torch.full((valid_voxel_num + 1, K), -1, dtype=torch.long, device=points.device) 25 | sst_ops_cuda.group_inner_inds_wrapper(inverse_inds.contiguous(), group_inds) 26 | group_points = points[group_inds] 27 | return group_points 28 | -------------------------------------------------------------------------------- /pcdet/ops/pointnet2/pointnet2_stack/src/ball_query_gpu.h: -------------------------------------------------------------------------------- 1 | /* 2 | Stacked-batch-data version of ball query, modified from the original implementation of official PointNet++ codes. 3 | Written by Shaoshuai Shi 4 | All Rights Reserved 2019-2020. 5 | */ 6 | 7 | 8 | #ifndef _STACK_BALL_QUERY_GPU_H 9 | #define _STACK_BALL_QUERY_GPU_H 10 | 11 | #include 12 | #include 13 | #include 14 | #include 15 | 16 | int ball_query_wrapper_stack(int B, int M, float radius, int nsample, 17 | at::Tensor new_xyz_tensor, at::Tensor new_xyz_batch_cnt_tensor, 18 | at::Tensor xyz_tensor, at::Tensor xyz_batch_cnt_tensor, at::Tensor idx_tensor); 19 | 20 | 21 | void ball_query_kernel_launcher_stack(int B, int M, float radius, int nsample, 22 | const float *new_xyz, const int *new_xyz_batch_cnt, const float *xyz, const int *xyz_batch_cnt, int *idx); 23 | 24 | 25 | #endif 26 | -------------------------------------------------------------------------------- /pcdet/ops/pointnet2/pointnet2_batch/src/group_points_gpu.h: -------------------------------------------------------------------------------- 1 | #ifndef _GROUP_POINTS_GPU_H 2 | #define _GROUP_POINTS_GPU_H 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | 10 | int group_points_wrapper_fast(int b, int c, int n, int npoints, int nsample, 11 | at::Tensor points_tensor, at::Tensor idx_tensor, at::Tensor out_tensor); 12 | 13 | void group_points_kernel_launcher_fast(int b, int c, int n, int npoints, int nsample, 14 | const float *points, const int *idx, float *out); 15 | 16 | int group_points_grad_wrapper_fast(int b, int c, int n, int npoints, int nsample, 17 | at::Tensor grad_out_tensor, at::Tensor idx_tensor, at::Tensor grad_points_tensor); 18 | 19 | void group_points_grad_kernel_launcher_fast(int b, int c, int n, int npoints, int nsample, 20 | const float *grad_out, const int *idx, float *grad_points); 21 | 22 | #endif 23 | -------------------------------------------------------------------------------- /pcdet/ops/pointnet2/pointnet2_batch/src/ball_query_gpu.h: -------------------------------------------------------------------------------- 1 | #ifndef _BALL_QUERY_GPU_H 2 | #define _BALL_QUERY_GPU_H 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | int ball_query_wrapper_fast(int b, int n, int m, float radius, int nsample, 10 | at::Tensor new_xyz_tensor, at::Tensor xyz_tensor, at::Tensor idx_cnt_tensor, at::Tensor idx_tensor); 11 | 12 | void ball_query_kernel_launcher_fast(int b, int n, int m, float radius, int nsample, 13 | const float *xyz, const float *new_xyz, int *idx_cnt, int *idx); 14 | 15 | int ball_query_dilated_wrapper_fast(int b, int n, int m, float radius_in, float radius_out, int nsample, 16 | at::Tensor new_xyz_tensor, at::Tensor xyz_tensor, at::Tensor idx_cnt_tensor, at::Tensor idx_tensor); 17 | 18 | void ball_query_dilated_kernel_launcher_fast(int b, int n, int m, float radius_in, float radius_out, int nsample, 19 | const float *xyz, const float *new_xyz, int *idx_cnt, int *idx); 20 | 21 | #endif 22 | -------------------------------------------------------------------------------- /pcdet/models/backbones_2d/map_to_bev/height_compression.py: -------------------------------------------------------------------------------- 1 | import torch.nn as nn 2 | 3 | 4 | class HeightCompression(nn.Module): 5 | def __init__(self, model_cfg, **kwargs): 6 | super().__init__() 7 | self.model_cfg = model_cfg 8 | self.num_bev_features = self.model_cfg.NUM_BEV_FEATURES 9 | 10 | def forward(self, batch_dict): 11 | """ 12 | Args: 13 | batch_dict: 14 | encoded_spconv_tensor: sparse tensor 15 | Returns: 16 | batch_dict: 17 | spatial_features: 18 | 19 | """ 20 | encoded_spconv_tensor = batch_dict['encoded_spconv_tensor'] 21 | spatial_features = encoded_spconv_tensor.dense() 22 | N, C, D, H, W = spatial_features.shape 23 | spatial_features = spatial_features.view(N, C * D, H, W) 24 | batch_dict['spatial_features'] = spatial_features 25 | batch_dict['spatial_features_stride'] = batch_dict['encoded_spconv_tensor_stride'] 26 | return batch_dict 27 | -------------------------------------------------------------------------------- /pcdet/ops/patch_ops/src/roipatch_dfvs_pool3d_gpu.h: -------------------------------------------------------------------------------- 1 | #ifndef _STACK_ROIPATCH_DFVS_POOL3D_GPU_H 2 | #define _STACK_ROIPATCH_DFVS_POOL3D_GPU_H 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | int roilocal_dfvs_pool3d_wrapper_stack_v2(int batch_size, int pts_num, int boxes_num, int dvs_pts_num, int fps_pts_num, 10 | int num_boxes_per_patch, int hash_size, float lambda, float delta, at::Tensor xyz_tensor, at::Tensor boxes3d_tensor, 11 | at::Tensor point2patch_indices_tensor, at::Tensor patch2box_indices_tensor, 12 | at::Tensor pooled_pts_num_tensor, at::Tensor pooled_pts_idx_tensor); 13 | 14 | void roilocal_dfvs_pool3d_kernel_launcher_stack_v2(int batch_size, int pts_num, int boxes_num, int dvs_pts_num, int fps_pts_num, 15 | int num_boxes_per_patch, int hash_size, float lambda, float delta, const float *xyz, const float *boxes3d, 16 | const int *point2patch_indices, const int *patch2box_indices, int *pooled_pts_num, int *pooled_pts_idx); 17 | 18 | #endif 19 | -------------------------------------------------------------------------------- /pcdet/models/detectors/ssd3d.py: -------------------------------------------------------------------------------- 1 | from .detector3d_template import Detector3DTemplate 2 | 3 | 4 | class SSD3D(Detector3DTemplate): 5 | def __init__(self, model_cfg, num_class, dataset, logger): 6 | super().__init__(model_cfg=model_cfg, num_class=num_class, dataset=dataset, logger=logger) 7 | self.module_list = self.build_networks() 8 | 9 | def forward(self, batch_dict): 10 | for cur_module in self.module_list: 11 | batch_dict = cur_module(batch_dict) 12 | 13 | if self.training: 14 | loss, tb_dict, disp_dict = self.get_training_loss() 15 | 16 | ret_dict = { 17 | 'loss': loss 18 | } 19 | return ret_dict, tb_dict, disp_dict 20 | else: 21 | pred_dicts, recall_dicts = self.post_processing(batch_dict) 22 | return pred_dicts, recall_dicts 23 | 24 | def get_training_loss(self): 25 | disp_dict = {} 26 | loss_rpn, tb_dict = self.point_head.get_loss() 27 | loss = loss_rpn 28 | return loss, tb_dict, disp_dict 29 | -------------------------------------------------------------------------------- /tools/scripts/dist_ts_train.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | while true 4 | do 5 | PORT=$(( ((RANDOM<<15)|RANDOM) % 49152 + 10000 )) 6 | status="$(nc -z 127.0.0.1 $PORT < /dev/null &>/dev/null; echo $?)" 7 | if [ "${status}" != "0" ]; then 8 | break; 9 | fi 10 | done 11 | echo $PORT 12 | 13 | export CUDA_VISIBLE_DEVICES=0,1,2,3 14 | 15 | NGPUS=4 16 | 17 | EPOCH=epoch_77 18 | 19 | CFG_NAME=kitti_models/second_mini 20 | TAG_NAME=default 21 | 22 | python -m torch.distributed.launch --nproc_per_node=${NGPUS} --master_port $PORT train.py --launcher pytorch --cfg_file cfgs/$CFG_NAME.yaml --workers 2 --extra_tag $TAG_NAME --max_ckpt_save_num 10 --num_epochs_to_eval 10 23 | 24 | TS_CFG_NAME=kitti_models/graph_rcnn_vo 25 | TS_TAG_NAME=ts_$TAG_NAME 26 | 27 | python -m torch.distributed.launch --nproc_per_node=${NGPUS} --master_port $PORT train.py --launcher pytorch --cfg_file cfgs/$TS_CFG_NAME.yaml --workers 2 --extra_tag $TS_TAG_NAME --max_ckpt_save_num 10 --num_epochs_to_eval 10 \ 28 | --pretrained_model ../output/$CFG_NAME/$TAG_NAME/ckpt/checkpoint_$EPOCH.pth 29 | -------------------------------------------------------------------------------- /pcdet/datasets/kitti/kitti_object_eval_python/evaluate.py: -------------------------------------------------------------------------------- 1 | import time 2 | 3 | import fire 4 | 5 | import .kitti_common as kitti 6 | from .eval import get_coco_eval_result, get_official_eval_result 7 | 8 | 9 | def _read_imageset_file(path): 10 | with open(path, 'r') as f: 11 | lines = f.readlines() 12 | return [int(line) for line in lines] 13 | 14 | 15 | def evaluate(label_path, 16 | result_path, 17 | label_split_file, 18 | current_class=0, 19 | coco=False, 20 | score_thresh=-1): 21 | dt_annos = kitti.get_label_annos(result_path) 22 | if score_thresh > 0: 23 | dt_annos = kitti.filter_annos_low_score(dt_annos, score_thresh) 24 | val_image_ids = _read_imageset_file(label_split_file) 25 | gt_annos = kitti.get_label_annos(label_path, val_image_ids) 26 | if coco: 27 | return get_coco_eval_result(gt_annos, dt_annos, current_class) 28 | else: 29 | return get_official_eval_result(gt_annos, dt_annos, current_class) 30 | 31 | 32 | if __name__ == '__main__': 33 | fire.Fire() 34 | -------------------------------------------------------------------------------- /pcdet/datasets/kitti/kitti_object_eval_python/LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /pcdet/models/backbones_3d/vfe/mean_vfe.py: -------------------------------------------------------------------------------- 1 | import torch 2 | 3 | from .vfe_template import VFETemplate 4 | 5 | 6 | class MeanVFE(VFETemplate): 7 | def __init__(self, model_cfg, num_point_features, **kwargs): 8 | super().__init__(model_cfg=model_cfg) 9 | self.num_point_features = num_point_features 10 | 11 | def get_output_feature_dim(self): 12 | return self.num_point_features 13 | 14 | def forward(self, batch_dict, **kwargs): 15 | """ 16 | Args: 17 | batch_dict: 18 | voxels: (num_voxels, max_points_per_voxel, C) 19 | voxel_num_points: optional (num_voxels) 20 | **kwargs: 21 | 22 | Returns: 23 | vfe_features: (num_voxels, C) 24 | """ 25 | voxel_features, voxel_num_points = batch_dict['voxels'], batch_dict['voxel_num_points'] 26 | points_mean = voxel_features[:, :, :].sum(dim=1, keepdim=False) 27 | normalizer = torch.clamp_min(voxel_num_points.view(-1, 1), min=1.0).type_as(voxel_features) 28 | points_mean = points_mean / normalizer 29 | batch_dict['voxel_features'] = points_mean.contiguous() 30 | 31 | return batch_dict 32 | -------------------------------------------------------------------------------- /pcdet/ops/pointnet2/pointnet2_stack/src/pointnet2_api.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include "ball_query_gpu.h" 5 | #include "group_points_gpu.h" 6 | #include "sampling_gpu.h" 7 | #include "interpolate_gpu.h" 8 | #include "voxel_query_gpu.h" 9 | 10 | 11 | PYBIND11_MODULE(TORCH_EXTENSION_NAME, m) { 12 | m.def("ball_query_wrapper", &ball_query_wrapper_stack, "ball_query_wrapper_stack"); 13 | m.def("voxel_query_wrapper", &voxel_query_wrapper_stack, "voxel_query_wrapper_stack"); 14 | 15 | m.def("furthest_point_sampling_wrapper", &furthest_point_sampling_wrapper, "furthest_point_sampling_wrapper"); 16 | 17 | m.def("group_points_wrapper", &group_points_wrapper_stack, "group_points_wrapper_stack"); 18 | m.def("group_points_grad_wrapper", &group_points_grad_wrapper_stack, "group_points_grad_wrapper_stack"); 19 | 20 | m.def("three_nn_wrapper", &three_nn_wrapper_stack, "three_nn_wrapper_stack"); 21 | m.def("three_interpolate_wrapper", &three_interpolate_wrapper_stack, "three_interpolate_wrapper_stack"); 22 | m.def("three_interpolate_grad_wrapper", &three_interpolate_grad_wrapper_stack, "three_interpolate_grad_wrapper_stack"); 23 | } 24 | -------------------------------------------------------------------------------- /pcdet/models/detectors/second_net.py: -------------------------------------------------------------------------------- 1 | from .detector3d_template import Detector3DTemplate 2 | 3 | 4 | class SECONDNet(Detector3DTemplate): 5 | def __init__(self, model_cfg, num_class, dataset, logger): 6 | super().__init__(model_cfg=model_cfg, num_class=num_class, dataset=dataset, logger=logger) 7 | self.module_list = self.build_networks() 8 | 9 | def forward(self, batch_dict): 10 | for cur_module in self.module_list: 11 | batch_dict = cur_module(batch_dict) 12 | 13 | if self.training: 14 | loss, tb_dict, disp_dict = self.get_training_loss() 15 | 16 | ret_dict = { 17 | 'loss': loss 18 | } 19 | return ret_dict, tb_dict, disp_dict 20 | else: 21 | pred_dicts, recall_dicts = self.post_processing(batch_dict) 22 | return pred_dicts, recall_dicts 23 | 24 | def get_training_loss(self): 25 | disp_dict = {} 26 | 27 | loss_rpn, tb_dict = self.dense_head.get_loss() 28 | tb_dict = { 29 | 'loss_rpn': loss_rpn.item(), 30 | **tb_dict 31 | } 32 | 33 | loss = loss_rpn 34 | return loss, tb_dict, disp_dict 35 | -------------------------------------------------------------------------------- /pcdet/models/detectors/pointpillar.py: -------------------------------------------------------------------------------- 1 | from .detector3d_template import Detector3DTemplate 2 | 3 | 4 | class PointPillar(Detector3DTemplate): 5 | def __init__(self, model_cfg, num_class, dataset, logger): 6 | super().__init__(model_cfg=model_cfg, num_class=num_class, dataset=dataset, logger=logger) 7 | self.module_list = self.build_networks() 8 | 9 | def forward(self, batch_dict): 10 | for cur_module in self.module_list: 11 | batch_dict = cur_module(batch_dict) 12 | 13 | if self.training: 14 | loss, tb_dict, disp_dict = self.get_training_loss() 15 | 16 | ret_dict = { 17 | 'loss': loss 18 | } 19 | return ret_dict, tb_dict, disp_dict 20 | else: 21 | pred_dicts, recall_dicts = self.post_processing(batch_dict) 22 | return pred_dicts, recall_dicts 23 | 24 | def get_training_loss(self): 25 | disp_dict = {} 26 | 27 | loss_rpn, tb_dict = self.dense_head.get_loss() 28 | tb_dict = { 29 | 'loss_rpn': loss_rpn.item(), 30 | **tb_dict 31 | } 32 | 33 | loss = loss_rpn 34 | return loss, tb_dict, disp_dict 35 | -------------------------------------------------------------------------------- /pcdet/ops/pointnet2/pointnet2_stack/src/sampling.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | // #include 5 | 6 | #include "sampling_gpu.h" 7 | 8 | // extern THCState *state; 9 | #define CHECK_CUDA(x) do { \ 10 | if (!x.type().is_cuda()) { \ 11 | fprintf(stderr, "%s must be CUDA tensor at %s:%d\n", #x, __FILE__, __LINE__); \ 12 | exit(-1); \ 13 | } \ 14 | } while (0) 15 | #define CHECK_CONTIGUOUS(x) do { \ 16 | if (!x.is_contiguous()) { \ 17 | fprintf(stderr, "%s must be contiguous tensor at %s:%d\n", #x, __FILE__, __LINE__); \ 18 | exit(-1); \ 19 | } \ 20 | } while (0) 21 | #define CHECK_INPUT(x) CHECK_CUDA(x);CHECK_CONTIGUOUS(x) 22 | 23 | 24 | int furthest_point_sampling_wrapper(int b, int n, int m, 25 | at::Tensor points_tensor, at::Tensor temp_tensor, at::Tensor idx_tensor) { 26 | 27 | CHECK_INPUT(points_tensor); 28 | CHECK_INPUT(temp_tensor); 29 | CHECK_INPUT(idx_tensor); 30 | 31 | const float *points = points_tensor.data(); 32 | float *temp = temp_tensor.data(); 33 | int *idx = idx_tensor.data(); 34 | 35 | furthest_point_sampling_kernel_launcher(b, n, m, points, temp, idx); 36 | return 1; 37 | } 38 | -------------------------------------------------------------------------------- /pcdet/models/detectors/gd_mae.py: -------------------------------------------------------------------------------- 1 | from .detector3d_template import Detector3DTemplate 2 | 3 | 4 | class GDMAE(Detector3DTemplate): 5 | def __init__(self, model_cfg, num_class, dataset, logger): 6 | super().__init__(model_cfg=model_cfg, num_class=num_class, dataset=dataset, logger=logger) 7 | self.module_list = self.build_networks() 8 | 9 | def forward(self, batch_dict): 10 | for cur_module in self.module_list: 11 | batch_dict = cur_module(batch_dict) 12 | 13 | if self.training: 14 | loss, tb_dict, disp_dict = self.get_training_loss() 15 | 16 | ret_dict = { 17 | 'loss': loss 18 | } 19 | return ret_dict, tb_dict, disp_dict 20 | else: 21 | pred_dicts, recall_dicts = self.post_processing(batch_dict) 22 | return pred_dicts, recall_dicts 23 | 24 | def post_processing(self, batch_dict): 25 | return {}, {} 26 | 27 | def get_training_loss(self): 28 | disp_dict = {} 29 | 30 | loss_rpn, tb_dict = self.backbone_3d.get_loss() 31 | tb_dict = { 32 | 'loss_rpn': loss_rpn.item(), 33 | **tb_dict 34 | } 35 | 36 | loss = loss_rpn 37 | return loss, tb_dict, disp_dict 38 | -------------------------------------------------------------------------------- /pcdet/ops/pointnet2/pointnet2_batch/src/interpolate_gpu.h: -------------------------------------------------------------------------------- 1 | #ifndef _INTERPOLATE_GPU_H 2 | #define _INTERPOLATE_GPU_H 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | 10 | void three_nn_wrapper_fast(int b, int n, int m, at::Tensor unknown_tensor, 11 | at::Tensor known_tensor, at::Tensor dist2_tensor, at::Tensor idx_tensor); 12 | 13 | void three_nn_kernel_launcher_fast(int b, int n, int m, const float *unknown, 14 | const float *known, float *dist2, int *idx); 15 | 16 | 17 | void three_interpolate_wrapper_fast(int b, int c, int m, int n, at::Tensor points_tensor, 18 | at::Tensor idx_tensor, at::Tensor weight_tensor, at::Tensor out_tensor); 19 | 20 | void three_interpolate_kernel_launcher_fast(int b, int c, int m, int n, 21 | const float *points, const int *idx, const float *weight, float *out); 22 | 23 | 24 | void three_interpolate_grad_wrapper_fast(int b, int c, int n, int m, at::Tensor grad_out_tensor, 25 | at::Tensor idx_tensor, at::Tensor weight_tensor, at::Tensor grad_points_tensor); 26 | 27 | void three_interpolate_grad_kernel_launcher_fast(int b, int c, int n, int m, const float *grad_out, 28 | const int *idx, const float *weight, float *grad_points); 29 | 30 | #endif 31 | -------------------------------------------------------------------------------- /tools/scripts/dist_ssl_train.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | while true 4 | do 5 | PORT=$(( ((RANDOM<<15)|RANDOM) % 49152 + 10000 )) 6 | status="$(nc -z 127.0.0.1 $PORT < /dev/null &>/dev/null; echo $?)" 7 | if [ "${status}" != "0" ]; then 8 | break; 9 | fi 10 | done 11 | echo $PORT 12 | 13 | export CUDA_VISIBLE_DEVICES=0,1,2,3 14 | NGPUS=4 15 | 16 | EPOCH=epoch_30 17 | 18 | SSL_CFG_NAME=waymo_models/gd_mae_ssl 19 | SSL_TAG_NAME=default 20 | 21 | python -m torch.distributed.launch --nproc_per_node=${NGPUS} --master_port $PORT train.py --launcher pytorch --cfg_file cfgs/$SSL_CFG_NAME.yaml --workers 2 --extra_tag $SSL_TAG_NAME --max_ckpt_save_num 1 --num_epochs_to_eval 0 22 | 23 | CFG_NAME=waymo_models/gd_mae 24 | TAG_NAME=ssl_pretrain_$SSL_TAG_NAME 25 | 26 | python -m torch.distributed.launch --nproc_per_node=${NGPUS} --master_port $PORT train.py --launcher pytorch --cfg_file cfgs/$CFG_NAME.yaml --workers 2 --extra_tag $TAG_NAME --max_ckpt_save_num 1 --num_epochs_to_eval 1 \ 27 | --pretrained_model ../output/$SSL_CFG_NAME/$SSL_TAG_NAME/ckpt/checkpoint_$EPOCH.pth 28 | 29 | GT=../data/waymo/gt.bin 30 | EVAL=../data/waymo/compute_detection_metrics_main 31 | DT_DIR=../output/$CFG_NAME/$TAG_NAME/eval/eval_with_train/$EPOCH/val/final_result/data 32 | 33 | $EVAL $DT_DIR/detection_pred.bin $GT 34 | -------------------------------------------------------------------------------- /pcdet/models/__init__.py: -------------------------------------------------------------------------------- 1 | from collections import namedtuple 2 | 3 | import numpy as np 4 | import torch 5 | 6 | from .detectors import build_detector 7 | 8 | 9 | def build_network(model_cfg, num_class, dataset, logger): 10 | model = build_detector( 11 | model_cfg=model_cfg, num_class=num_class, dataset=dataset, logger=logger 12 | ) 13 | return model 14 | 15 | 16 | def load_data_to_gpu(batch_dict): 17 | for key, val in batch_dict.items(): 18 | if not isinstance(val, np.ndarray): 19 | continue 20 | elif key in ['frame_id', 'metadata', 'calib', 'image_shape', 'image_pad_shape', 'image_rescale_shape']: 21 | continue 22 | else: 23 | batch_dict[key] = torch.from_numpy(val).float().cuda() 24 | 25 | 26 | def model_fn_decorator(): 27 | ModelReturn = namedtuple('ModelReturn', ['loss', 'tb_dict', 'disp_dict']) 28 | 29 | def model_func(model, batch_dict, **kwargs): 30 | load_data_to_gpu(batch_dict) 31 | ret_dict, tb_dict, disp_dict = model(batch_dict) 32 | 33 | loss = ret_dict['loss'].mean() 34 | if hasattr(model, 'update_global_step'): 35 | model.update_global_step() 36 | else: 37 | model.module.update_global_step() 38 | 39 | return ModelReturn(loss, tb_dict, disp_dict) 40 | 41 | return model_func 42 | -------------------------------------------------------------------------------- /pcdet/ops/pointnet2/pointnet2_stack/src/group_points_gpu.h: -------------------------------------------------------------------------------- 1 | /* 2 | Stacked-batch-data version of point grouping, modified from the original implementation of official PointNet++ codes. 3 | Written by Shaoshuai Shi 4 | All Rights Reserved 2019-2020. 5 | */ 6 | 7 | 8 | #ifndef _STACK_GROUP_POINTS_GPU_H 9 | #define _STACK_GROUP_POINTS_GPU_H 10 | 11 | #include 12 | #include 13 | #include 14 | #include 15 | 16 | 17 | int group_points_wrapper_stack(int B, int M, int C, int nsample, 18 | at::Tensor features_tensor, at::Tensor features_batch_cnt_tensor, 19 | at::Tensor idx_tensor, at::Tensor idx_batch_cnt_tensor, at::Tensor out_tensor); 20 | 21 | void group_points_kernel_launcher_stack(int B, int M, int C, int nsample, 22 | const float *features, const int *features_batch_cnt, const int *idx, const int *idx_batch_cnt, float *out); 23 | 24 | int group_points_grad_wrapper_stack(int B, int M, int C, int N, int nsample, 25 | at::Tensor grad_out_tensor, at::Tensor idx_tensor, at::Tensor idx_batch_cnt_tensor, 26 | at::Tensor features_batch_cnt_tensor, at::Tensor grad_features_tensor); 27 | 28 | void group_points_grad_kernel_launcher_stack(int B, int M, int C, int N, int nsample, 29 | const float *grad_out, const int *idx, const int *idx_batch_cnt, const int *features_batch_cnt, float *grad_features); 30 | 31 | #endif 32 | -------------------------------------------------------------------------------- /pcdet/ops/pointnet2/pointnet2_stack/src/interpolate_gpu.h: -------------------------------------------------------------------------------- 1 | #ifndef _INTERPOLATE_GPU_H 2 | #define _INTERPOLATE_GPU_H 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | 10 | void three_nn_wrapper_stack(at::Tensor unknown_tensor, 11 | at::Tensor unknown_batch_cnt_tensor, at::Tensor known_tensor, 12 | at::Tensor known_batch_cnt_tensor, at::Tensor dist2_tensor, at::Tensor idx_tensor); 13 | 14 | 15 | void three_interpolate_wrapper_stack(at::Tensor features_tensor, 16 | at::Tensor idx_tensor, at::Tensor weight_tensor, at::Tensor out_tensor); 17 | 18 | 19 | 20 | void three_interpolate_grad_wrapper_stack(at::Tensor grad_out_tensor, at::Tensor idx_tensor, 21 | at::Tensor weight_tensor, at::Tensor grad_features_tensor); 22 | 23 | 24 | void three_nn_kernel_launcher_stack(int batch_size, int N, int M, const float *unknown, 25 | const int *unknown_batch_cnt, const float *known, const int *known_batch_cnt, 26 | float *dist2, int *idx); 27 | 28 | 29 | void three_interpolate_kernel_launcher_stack(int N, int channels, 30 | const float *features, const int *idx, const float *weight, float *out); 31 | 32 | 33 | 34 | void three_interpolate_grad_kernel_launcher_stack(int N, int channels, const float *grad_out, 35 | const int *idx, const float *weight, float *grad_features); 36 | 37 | 38 | 39 | #endif -------------------------------------------------------------------------------- /pcdet/ops/pointnet2/pointnet2_batch/src/group_points.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | batch version of point grouping, modified from the original implementation of official PointNet++ codes. 3 | Written by Shaoshuai Shi 4 | All Rights Reserved 2018. 5 | */ 6 | 7 | 8 | #include 9 | #include 10 | #include 11 | #include 12 | // #include 13 | #include "group_points_gpu.h" 14 | 15 | // extern THCState *state; 16 | 17 | 18 | int group_points_grad_wrapper_fast(int b, int c, int n, int npoints, int nsample, 19 | at::Tensor grad_out_tensor, at::Tensor idx_tensor, at::Tensor grad_points_tensor) { 20 | 21 | float *grad_points = grad_points_tensor.data_ptr(); 22 | const int *idx = idx_tensor.data_ptr(); 23 | const float *grad_out = grad_out_tensor.data_ptr(); 24 | 25 | group_points_grad_kernel_launcher_fast(b, c, n, npoints, nsample, grad_out, idx, grad_points); 26 | return 1; 27 | } 28 | 29 | 30 | int group_points_wrapper_fast(int b, int c, int n, int npoints, int nsample, 31 | at::Tensor points_tensor, at::Tensor idx_tensor, at::Tensor out_tensor) { 32 | 33 | const float *points = points_tensor.data_ptr(); 34 | const int *idx = idx_tensor.data_ptr(); 35 | float *out = out_tensor.data_ptr(); 36 | 37 | group_points_kernel_launcher_fast(b, c, n, npoints, nsample, points, idx, out); 38 | return 1; 39 | } 40 | -------------------------------------------------------------------------------- /pcdet/ops/pointnet2/pointnet2_batch/src/sampling_gpu.h: -------------------------------------------------------------------------------- 1 | #ifndef _SAMPLING_GPU_H 2 | #define _SAMPLING_GPU_H 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | 9 | int gather_points_wrapper_fast(int b, int c, int n, int npoints, 10 | at::Tensor points_tensor, at::Tensor idx_tensor, at::Tensor out_tensor); 11 | 12 | void gather_points_kernel_launcher_fast(int b, int c, int n, int npoints, 13 | const float *points, const int *idx, float *out); 14 | 15 | 16 | int gather_points_grad_wrapper_fast(int b, int c, int n, int npoints, 17 | at::Tensor grad_out_tensor, at::Tensor idx_tensor, at::Tensor grad_points_tensor); 18 | 19 | void gather_points_grad_kernel_launcher_fast(int b, int c, int n, int npoints, 20 | const float *grad_out, const int *idx, float *grad_points); 21 | 22 | 23 | int furthest_point_sampling_wrapper(int b, int n, int m, 24 | at::Tensor points_tensor, at::Tensor temp_tensor, at::Tensor idx_tensor); 25 | 26 | void furthest_point_sampling_kernel_launcher(int b, int n, int m, 27 | const float *dataset, float *temp, int *idxs); 28 | 29 | int furthest_point_sampling_matrix_wrapper(int b, int n, int m, 30 | at::Tensor matrix_tensor, at::Tensor temp_tensor, at::Tensor idx_tensor); 31 | 32 | void furthest_point_sampling_matrix_kernel_launcher(int b, int n, int m, 33 | const float *matrix, float *temp, int *idxs); 34 | 35 | #endif 36 | -------------------------------------------------------------------------------- /tools/cfgs/dataset_configs/kitti360_dataset.yaml: -------------------------------------------------------------------------------- 1 | DATASET: 'Kitti360Dataset' 2 | DATA_PATH: '../data/kitti-360' 3 | 4 | BACKEND: &BACKEND 5 | NAME: HardDiskBackend 6 | 7 | POINT_CLOUD_RANGE: [0, -40, -3, 70.4, 40, 1] 8 | 9 | DATA_SPLIT: { 10 | 'train': train, 11 | 'test': val 12 | } 13 | 14 | GET_ITEM_LIST: ["points"] 15 | 16 | DATA_AUGMENTOR: 17 | DISABLE_AUG_LIST: ['placeholder'] 18 | AUG_CONFIG_LIST: 19 | - NAME: random_world_flip 20 | PROBABILITY: 0.5 21 | ALONG_AXIS_LIST: ['x'] 22 | 23 | - NAME: random_world_rotation 24 | PROBABILITY: 1.0 25 | WORLD_ROT_ANGLE: [-0.78539816, 0.78539816] 26 | 27 | - NAME: random_world_scaling 28 | PROBABILITY: 1.0 29 | WORLD_SCALE_RANGE: [0.95, 1.05] 30 | 31 | 32 | POINT_FEATURE_ENCODING: { 33 | encoding_type: absolute_coordinates_encoding, 34 | used_feature_list: ['x', 'y', 'z', 'intensity'], 35 | src_feature_list: ['x', 'y', 'z', 'intensity'], 36 | } 37 | 38 | 39 | DATA_PROCESSOR: 40 | - NAME: mask_points_and_boxes_outside_range 41 | REMOVE_OUTSIDE_BOXES: True 42 | 43 | - NAME: shuffle_points 44 | SHUFFLE_ENABLED: { 45 | 'train': True, 46 | 'test': False 47 | } 48 | 49 | - NAME: transform_points_to_voxels 50 | VOXEL_SIZE: [0.05, 0.05, 0.1] 51 | MAX_POINTS_PER_VOXEL: 5 52 | MAX_NUMBER_OF_VOXELS: { 53 | 'train': 16000, 54 | 'test': 40000 55 | } 56 | -------------------------------------------------------------------------------- /pcdet/ops/pointnet2/pointnet2_batch/src/pointnet2_api.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include "ball_query_gpu.h" 5 | #include "group_points_gpu.h" 6 | #include "sampling_gpu.h" 7 | #include "interpolate_gpu.h" 8 | 9 | 10 | PYBIND11_MODULE(TORCH_EXTENSION_NAME, m) { 11 | m.def("ball_query_wrapper", &ball_query_wrapper_fast, "ball_query_wrapper_fast"); 12 | m.def("ball_query_dilated_wrapper", &ball_query_dilated_wrapper_fast, "ball_query_dilated_wrapper_fast"); 13 | 14 | m.def("group_points_wrapper", &group_points_wrapper_fast, "group_points_wrapper_fast"); 15 | m.def("group_points_grad_wrapper", &group_points_grad_wrapper_fast, "group_points_grad_wrapper_fast"); 16 | 17 | m.def("gather_points_wrapper", &gather_points_wrapper_fast, "gather_points_wrapper_fast"); 18 | m.def("gather_points_grad_wrapper", &gather_points_grad_wrapper_fast, "gather_points_grad_wrapper_fast"); 19 | 20 | m.def("furthest_point_sampling_wrapper", &furthest_point_sampling_wrapper, "furthest_point_sampling_wrapper"); 21 | m.def("furthest_point_sampling_matrix_wrapper", &furthest_point_sampling_matrix_wrapper, "furthest_point_sampling_matrix_wrapper"); 22 | 23 | m.def("three_nn_wrapper", &three_nn_wrapper_fast, "three_nn_wrapper_fast"); 24 | m.def("three_interpolate_wrapper", &three_interpolate_wrapper_fast, "three_interpolate_wrapper_fast"); 25 | m.def("three_interpolate_grad_wrapper", &three_interpolate_grad_wrapper_fast, "three_interpolate_grad_wrapper_fast"); 26 | } 27 | -------------------------------------------------------------------------------- /pcdet/models/detectors/graph_rcnn.py: -------------------------------------------------------------------------------- 1 | from .detector3d_template import Detector3DTemplate 2 | 3 | 4 | class GraphRCNN(Detector3DTemplate): 5 | def __init__(self, model_cfg, num_class, dataset, logger): 6 | super().__init__(model_cfg=model_cfg, num_class=num_class, dataset=dataset, logger=logger) 7 | self.module_list = self.build_networks() 8 | if self.model_cfg.get('FREEZE_LAYERS', None) is not None: 9 | self.freeze(self.model_cfg.FREEZE_LAYERS) 10 | 11 | def forward(self, batch_dict): 12 | for cur_module in self.module_list: 13 | batch_dict = cur_module(batch_dict) 14 | 15 | if self.training: 16 | loss, tb_dict, disp_dict = self.get_training_loss() 17 | 18 | ret_dict = { 19 | 'loss': loss 20 | } 21 | return ret_dict, tb_dict, disp_dict 22 | else: 23 | pred_dicts, recall_dicts = self.post_processing(batch_dict) 24 | return pred_dicts, recall_dicts 25 | 26 | def get_training_loss(self): 27 | disp_dict, tb_dict = {}, {} 28 | loss = 0 29 | if self.model_cfg.get('FREEZE_LAYERS', None) is None: 30 | if self.dense_head is not None: 31 | loss_rpn, tb_dict = self.dense_head.get_loss(tb_dict) 32 | else: 33 | loss_rpn, tb_dict = self.point_head.get_loss(tb_dict) 34 | loss += loss_rpn 35 | 36 | loss_rcnn, tb_dict = self.roi_head.get_loss(tb_dict) 37 | loss += loss_rcnn 38 | 39 | return loss, tb_dict, disp_dict 40 | -------------------------------------------------------------------------------- /pcdet/ops/patch_ops/src/patch_query.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | // #include 4 | #include 5 | #include 6 | #include "patch_query_gpu.h" 7 | 8 | #define CHECK_CUDA(x) do { \ 9 | if (!x.type().is_cuda()) { \ 10 | fprintf(stderr, "%s must be CUDA tensor at %s:%d\n", #x, __FILE__, __LINE__); \ 11 | exit(-1); \ 12 | } \ 13 | } while (0) 14 | #define CHECK_CONTIGUOUS(x) do { \ 15 | if (!x.is_contiguous()) { \ 16 | fprintf(stderr, "%s must be contiguous tensor at %s:%d\n", #x, __FILE__, __LINE__); \ 17 | exit(-1); \ 18 | } \ 19 | } while (0) 20 | #define CHECK_INPUT(x) CHECK_CUDA(x);CHECK_CONTIGUOUS(x) 21 | 22 | int patch_query_wrapper_stack(int batch_size, int boxes_num, int boxes_per_patch_num, 23 | int range_x, int range_y, float offset_x, float offset_y, float patch_size_x, float patch_size_y, 24 | at::Tensor boxes3d_tensor, at::Tensor patch_indices_tensor, at::Tensor patch2box_indices_tensor) { 25 | CHECK_INPUT(boxes3d_tensor); 26 | CHECK_INPUT(patch_indices_tensor); 27 | CHECK_INPUT(patch2box_indices_tensor); 28 | 29 | const float *boxes3d = boxes3d_tensor.data_ptr(); 30 | const int *patch_indices = patch_indices_tensor.data_ptr(); 31 | int *patch2box_indices = patch2box_indices_tensor.data_ptr(); 32 | 33 | patch_query_kernel_launcher_stack(batch_size, boxes_num, boxes_per_patch_num, range_x, range_y, 34 | offset_x, offset_y, patch_size_x, patch_size_y, boxes3d, patch_indices, patch2box_indices); 35 | return 1; 36 | } 37 | -------------------------------------------------------------------------------- /pcdet/models/backbones_2d/sst_bev_backbone.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | import torch 3 | import torch.nn as nn 4 | 5 | 6 | class SSTBEVBackbone(nn.Module): 7 | def __init__(self, model_cfg, **kwargs): 8 | super().__init__() 9 | self.model_cfg = model_cfg 10 | input_channels = model_cfg.NUM_FILTER 11 | self.conv_shortcut = model_cfg.CONV_SHORTCUT 12 | conv_kwargs = model_cfg.CONV_KWARGS 13 | conv_list = [] 14 | for i in range(len(conv_kwargs)): 15 | conv_kwargs_i = conv_kwargs[i] 16 | conv_list.append(nn.Sequential( 17 | nn.Conv2d(input_channels, **conv_kwargs_i, bias=False), 18 | nn.BatchNorm2d(conv_kwargs_i['out_channels'], eps=1e-3, momentum=0.01), 19 | nn.ReLU(inplace=True) 20 | )) 21 | input_channels = conv_kwargs_i['out_channels'] 22 | self.conv_layer = nn.ModuleList(conv_list) 23 | 24 | self.num_bev_features = input_channels 25 | 26 | def forward(self, data_dict): 27 | """ 28 | Args: 29 | data_dict: 30 | spatial_features 31 | Returns: 32 | """ 33 | output = data_dict['spatial_features'] 34 | if len(self.conv_layer) > 0: 35 | for i, conv in enumerate(self.conv_layer): 36 | temp = conv(output) 37 | if temp.shape == output.shape and i in self.conv_shortcut: 38 | output = temp + output 39 | else: 40 | output = temp 41 | 42 | data_dict['spatial_features_2d'] = output 43 | return data_dict -------------------------------------------------------------------------------- /pcdet/models/backbones_2d/map_to_bev/pointpillar_scatter.py: -------------------------------------------------------------------------------- 1 | import torch 2 | import torch.nn as nn 3 | 4 | 5 | class PointPillarScatter(nn.Module): 6 | def __init__(self, model_cfg, grid_size, **kwargs): 7 | super().__init__() 8 | 9 | self.model_cfg = model_cfg 10 | self.num_bev_features = self.model_cfg.NUM_BEV_FEATURES 11 | self.nx, self.ny, self.nz = grid_size 12 | assert self.nz == 1 13 | 14 | def forward(self, batch_dict, **kwargs): 15 | pillar_features, coords = batch_dict['pillar_features'], batch_dict['voxel_coords'] 16 | batch_spatial_features = [] 17 | batch_size = coords[:, 0].max().int().item() + 1 18 | for batch_idx in range(batch_size): 19 | spatial_feature = torch.zeros( 20 | self.num_bev_features, 21 | self.nz * self.nx * self.ny, 22 | dtype=pillar_features.dtype, 23 | device=pillar_features.device) 24 | 25 | batch_mask = coords[:, 0] == batch_idx 26 | this_coords = coords[batch_mask, :] 27 | indices = this_coords[:, 1] + this_coords[:, 2] * self.nx + this_coords[:, 3] 28 | indices = indices.type(torch.long) 29 | pillars = pillar_features[batch_mask, :] 30 | pillars = pillars.t() 31 | spatial_feature[:, indices] = pillars 32 | batch_spatial_features.append(spatial_feature) 33 | 34 | batch_spatial_features = torch.stack(batch_spatial_features, 0) 35 | batch_spatial_features = batch_spatial_features.view(batch_size, self.num_bev_features * self.nz, self.ny, self.nx) 36 | batch_dict['spatial_features'] = batch_spatial_features 37 | return batch_dict 38 | -------------------------------------------------------------------------------- /pcdet/ops/pointnet2/pointnet2_stack/src/voxel_query.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | // #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include "voxel_query_gpu.h" 10 | 11 | // extern THCState *state; 12 | 13 | #define CHECK_CUDA(x) do { \ 14 | if (!x.type().is_cuda()) { \ 15 | fprintf(stderr, "%s must be CUDA tensor at %s:%d\n", #x, __FILE__, __LINE__); \ 16 | exit(-1); \ 17 | } \ 18 | } while (0) 19 | #define CHECK_CONTIGUOUS(x) do { \ 20 | if (!x.is_contiguous()) { \ 21 | fprintf(stderr, "%s must be contiguous tensor at %s:%d\n", #x, __FILE__, __LINE__); \ 22 | exit(-1); \ 23 | } \ 24 | } while (0) 25 | #define CHECK_INPUT(x) CHECK_CUDA(x);CHECK_CONTIGUOUS(x) 26 | 27 | 28 | int voxel_query_wrapper_stack(int M, int R1, int R2, int R3, int nsample, float radius, 29 | int z_range, int y_range, int x_range, at::Tensor new_xyz_tensor, at::Tensor xyz_tensor, 30 | at::Tensor new_coords_tensor, at::Tensor point_indices_tensor, at::Tensor idx_tensor) { 31 | CHECK_INPUT(new_coords_tensor); 32 | CHECK_INPUT(point_indices_tensor); 33 | CHECK_INPUT(new_xyz_tensor); 34 | CHECK_INPUT(xyz_tensor); 35 | 36 | const float *new_xyz = new_xyz_tensor.data(); 37 | const float *xyz = xyz_tensor.data(); 38 | const int *new_coords = new_coords_tensor.data(); 39 | const int *point_indices = point_indices_tensor.data(); 40 | int *idx = idx_tensor.data(); 41 | 42 | voxel_query_kernel_launcher_stack(M, R1, R2, R3, nsample, radius, z_range, y_range, x_range, new_xyz, xyz, new_coords, point_indices, idx); 43 | return 1; 44 | } 45 | -------------------------------------------------------------------------------- /pcdet/ops/sst_ops/src/sst_ops.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include "sst_ops_gpu.h" 6 | 7 | #define CHECK_CUDA(x) do { \ 8 | if (!x.type().is_cuda()) { \ 9 | fprintf(stderr, "%s must be CUDA tensor at %s:%d\n", #x, __FILE__, __LINE__); \ 10 | exit(-1); \ 11 | } \ 12 | } while (0) 13 | #define CHECK_CONTIGUOUS(x) do { \ 14 | if (!x.is_contiguous()) { \ 15 | fprintf(stderr, "%s must be contiguous tensor at %s:%d\n", #x, __FILE__, __LINE__); \ 16 | exit(-1); \ 17 | } \ 18 | } while (0) 19 | #define CHECK_INPUT(x) CHECK_CUDA(x);CHECK_CONTIGUOUS(x) 20 | 21 | int ingroup_inds_wrapper(at::Tensor group_inds_tensor, at::Tensor out_inds_tensor) { 22 | CHECK_INPUT(group_inds_tensor); 23 | CHECK_INPUT(out_inds_tensor); 24 | 25 | int N = group_inds_tensor.size(0); 26 | int max_group_id = group_inds_tensor.max().item().toLong(); 27 | 28 | const long *group_inds = group_inds_tensor.data_ptr(); 29 | long *out_inds = out_inds_tensor.data_ptr(); 30 | 31 | ingroup_inds_launcher(N, max_group_id, group_inds, out_inds); 32 | return 1; 33 | } 34 | 35 | int group_inner_inds_wrapper(at::Tensor inverse_inds_tensor, at::Tensor group_inds_tensor) { 36 | CHECK_INPUT(inverse_inds_tensor); 37 | CHECK_INPUT(group_inds_tensor); 38 | 39 | int N = inverse_inds_tensor.size(0); 40 | int M = group_inds_tensor.size(0); 41 | int K = group_inds_tensor.size(1); 42 | 43 | const long *inverse_inds = inverse_inds_tensor.data_ptr(); 44 | long *group_inds = group_inds_tensor.data_ptr(); 45 | 46 | group_inner_inds_launcher(N, M, K, inverse_inds, group_inds); 47 | return 1; 48 | } 49 | -------------------------------------------------------------------------------- /pcdet/datasets/once/once_eval/eval_utils.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | 3 | def compute_split_parts(num_samples, num_parts): 4 | part_samples = num_samples // num_parts 5 | remain_samples = num_samples % num_parts 6 | if part_samples == 0: 7 | return [num_samples] 8 | if remain_samples == 0: 9 | return [part_samples] * num_parts 10 | else: 11 | return [part_samples] * num_parts + [remain_samples] 12 | 13 | def overall_filter(boxes): 14 | ignore = np.zeros(boxes.shape[0], dtype=np.bool) # all false 15 | return ignore 16 | 17 | def distance_filter(boxes, level): 18 | ignore = np.ones(boxes.shape[0], dtype=np.bool) # all true 19 | dist = np.sqrt(np.sum(boxes[:, 0:3] * boxes[:, 0:3], axis=1)) 20 | 21 | if level == 0: # 0-30m 22 | flag = dist < 30 23 | elif level == 1: # 30-50m 24 | flag = (dist >= 30) & (dist < 50) 25 | elif level == 2: # 50m-inf 26 | flag = dist >= 50 27 | else: 28 | assert False, 'level < 3 for distance metric, found level %s' % (str(level)) 29 | 30 | ignore[flag] = False 31 | return ignore 32 | 33 | def overall_distance_filter(boxes, level): 34 | ignore = np.ones(boxes.shape[0], dtype=np.bool) # all true 35 | dist = np.sqrt(np.sum(boxes[:, 0:3] * boxes[:, 0:3], axis=1)) 36 | 37 | if level == 0: 38 | flag = np.ones(boxes.shape[0], dtype=np.bool) 39 | elif level == 1: # 0-30m 40 | flag = dist < 30 41 | elif level == 2: # 30-50m 42 | flag = (dist >= 30) & (dist < 50) 43 | elif level == 3: # 50m-inf 44 | flag = dist >= 50 45 | else: 46 | assert False, 'level < 4 for overall & distance metric, found level %s' % (str(level)) 47 | 48 | ignore[flag] = False 49 | return ignore -------------------------------------------------------------------------------- /pcdet/ops/pointnet2/pointnet2_stack/src/ball_query.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | Stacked-batch-data version of ball query, modified from the original implementation of official PointNet++ codes. 3 | Written by Shaoshuai Shi 4 | All Rights Reserved 2019-2020. 5 | */ 6 | 7 | 8 | #include 9 | #include 10 | // #include 11 | #include 12 | #include 13 | #include "ball_query_gpu.h" 14 | 15 | // extern THCState *state; 16 | 17 | #define CHECK_CUDA(x) do { \ 18 | if (!x.type().is_cuda()) { \ 19 | fprintf(stderr, "%s must be CUDA tensor at %s:%d\n", #x, __FILE__, __LINE__); \ 20 | exit(-1); \ 21 | } \ 22 | } while (0) 23 | #define CHECK_CONTIGUOUS(x) do { \ 24 | if (!x.is_contiguous()) { \ 25 | fprintf(stderr, "%s must be contiguous tensor at %s:%d\n", #x, __FILE__, __LINE__); \ 26 | exit(-1); \ 27 | } \ 28 | } while (0) 29 | #define CHECK_INPUT(x) CHECK_CUDA(x);CHECK_CONTIGUOUS(x) 30 | 31 | int ball_query_wrapper_stack(int B, int M, float radius, int nsample, 32 | at::Tensor new_xyz_tensor, at::Tensor new_xyz_batch_cnt_tensor, 33 | at::Tensor xyz_tensor, at::Tensor xyz_batch_cnt_tensor, at::Tensor idx_tensor) { 34 | CHECK_INPUT(new_xyz_tensor); 35 | CHECK_INPUT(xyz_tensor); 36 | CHECK_INPUT(new_xyz_batch_cnt_tensor); 37 | CHECK_INPUT(xyz_batch_cnt_tensor); 38 | 39 | const float *new_xyz = new_xyz_tensor.data(); 40 | const float *xyz = xyz_tensor.data(); 41 | const int *new_xyz_batch_cnt = new_xyz_batch_cnt_tensor.data(); 42 | const int *xyz_batch_cnt = xyz_batch_cnt_tensor.data(); 43 | int *idx = idx_tensor.data(); 44 | 45 | ball_query_kernel_launcher_stack(B, M, radius, nsample, new_xyz, new_xyz_batch_cnt, xyz, xyz_batch_cnt, idx); 46 | return 1; 47 | } 48 | -------------------------------------------------------------------------------- /pcdet/datasets/kitti/kitti_object_eval_python/README.md: -------------------------------------------------------------------------------- 1 | # kitti-object-eval-python 2 | **Note**: This is borrowed from [traveller59/kitti-object-eval-python](https://github.com/traveller59/kitti-object-eval-python) 3 | 4 | Fast kitti object detection eval in python(finish eval in less than 10 second), support 2d/bev/3d/aos. , support coco-style AP. If you use command line interface, numba need some time to compile jit functions. 5 | ## Dependencies 6 | Only support python 3.6+, need `numpy`, `skimage`, `numba`, `fire`. If you have Anaconda, just install `cudatoolkit` in anaconda. Otherwise, please reference to this [page](https://github.com/numba/numba#custom-python-environments) to set up llvm and cuda for numba. 7 | * Install by conda: 8 | ``` 9 | conda install -c numba cudatoolkit=x.x (8.0, 9.0, 9.1, depend on your environment) 10 | ``` 11 | ## Usage 12 | * commandline interface: 13 | ``` 14 | python evaluate.py evaluate --label_path=/path/to/your_gt_label_folder --result_path=/path/to/your_result_folder --label_split_file=/path/to/val.txt --current_class=0 --coco=False 15 | ``` 16 | * python interface: 17 | ```Python 18 | import kitti_common as kitti 19 | from eval import get_official_eval_result, get_coco_eval_result 20 | def _read_imageset_file(path): 21 | with open(path, 'r') as f: 22 | lines = f.readlines() 23 | return [int(line) for line in lines] 24 | det_path = "/path/to/your_result_folder" 25 | dt_annos = kitti.get_label_annos(det_path) 26 | gt_path = "/path/to/your_gt_label_folder" 27 | gt_split_file = "/path/to/val.txt" # from https://xiaozhichen.github.io/files/mv3d/imagesets.tar.gz 28 | val_image_ids = _read_imageset_file(gt_split_file) 29 | gt_annos = kitti.get_label_annos(gt_path, val_image_ids) 30 | print(get_official_eval_result(gt_annos, dt_annos, 0)) # 6s in my computer 31 | print(get_coco_eval_result(gt_annos, dt_annos, 0)) # 18s in my computer 32 | ``` 33 | -------------------------------------------------------------------------------- /pcdet/models/detectors/centerpoint.py: -------------------------------------------------------------------------------- 1 | from .detector3d_template import Detector3DTemplate 2 | 3 | 4 | class CenterPoint(Detector3DTemplate): 5 | def __init__(self, model_cfg, num_class, dataset, logger): 6 | super().__init__(model_cfg=model_cfg, num_class=num_class, dataset=dataset, logger=logger) 7 | self.module_list = self.build_networks() 8 | 9 | def forward(self, batch_dict): 10 | for cur_module in self.module_list: 11 | batch_dict = cur_module(batch_dict) 12 | 13 | if self.training: 14 | loss, tb_dict, disp_dict = self.get_training_loss() 15 | 16 | ret_dict = { 17 | 'loss': loss 18 | } 19 | return ret_dict, tb_dict, disp_dict 20 | else: 21 | pred_dicts, recall_dicts = self.post_processing(batch_dict) 22 | return pred_dicts, recall_dicts 23 | 24 | def get_training_loss(self): 25 | disp_dict = {} 26 | 27 | loss_rpn, tb_dict = self.dense_head.get_loss() 28 | tb_dict = { 29 | 'loss_rpn': loss_rpn.item(), 30 | **tb_dict 31 | } 32 | 33 | loss = loss_rpn 34 | return loss, tb_dict, disp_dict 35 | 36 | def post_processing(self, batch_dict): 37 | post_process_cfg = self.model_cfg.POST_PROCESSING 38 | batch_size = batch_dict['batch_size'] 39 | final_pred_dict = batch_dict['final_box_dicts'] 40 | recall_dict = {} 41 | for index in range(batch_size): 42 | pred_boxes = final_pred_dict[index]['pred_boxes'] 43 | 44 | recall_dict = self.generate_recall_record( 45 | box_preds=pred_boxes, 46 | recall_dict=recall_dict, batch_index=index, data_dict=batch_dict, 47 | thresh_list=post_process_cfg.RECALL_THRESH_LIST 48 | ) 49 | 50 | return final_pred_dict, recall_dict 51 | -------------------------------------------------------------------------------- /pcdet/ops/patch_ops/src/roipatch_dfvs_pool3d.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | // #include 4 | #include 5 | #include 6 | #include "roipatch_dfvs_pool3d_gpu.h" 7 | 8 | #define CHECK_CUDA(x) do { \ 9 | if (!x.type().is_cuda()) { \ 10 | fprintf(stderr, "%s must be CUDA tensor at %s:%d\n", #x, __FILE__, __LINE__); \ 11 | exit(-1); \ 12 | } \ 13 | } while (0) 14 | #define CHECK_CONTIGUOUS(x) do { \ 15 | if (!x.is_contiguous()) { \ 16 | fprintf(stderr, "%s must be contiguous tensor at %s:%d\n", #x, __FILE__, __LINE__); \ 17 | exit(-1); \ 18 | } \ 19 | } while (0) 20 | #define CHECK_INPUT(x) CHECK_CUDA(x);CHECK_CONTIGUOUS(x) 21 | 22 | 23 | int roilocal_dfvs_pool3d_wrapper_stack_v2(int batch_size, int pts_num, int boxes_num, int dvs_pts_num, int fps_pts_num, 24 | int num_boxes_per_patch, int hash_size, float lambda, float delta, at::Tensor xyz_tensor, at::Tensor boxes3d_tensor, 25 | at::Tensor point2patch_indices_tensor, at::Tensor patch2box_indices_tensor, 26 | at::Tensor pooled_pts_num_tensor, at::Tensor pooled_pts_idx_tensor) { 27 | CHECK_INPUT(xyz_tensor); 28 | CHECK_INPUT(boxes3d_tensor); 29 | CHECK_INPUT(point2patch_indices_tensor); 30 | CHECK_INPUT(patch2box_indices_tensor); 31 | CHECK_INPUT(pooled_pts_num_tensor); 32 | CHECK_INPUT(pooled_pts_idx_tensor); 33 | 34 | const float *xyz = xyz_tensor.data_ptr(); 35 | const float *boxes3d = boxes3d_tensor.data_ptr(); 36 | const int *point2patch_indices = point2patch_indices_tensor.data_ptr(); 37 | const int *patch2box_indices = patch2box_indices_tensor.data_ptr(); 38 | int *pooled_pts_num = pooled_pts_num_tensor.data_ptr(); 39 | int *pooled_pts_idx = pooled_pts_idx_tensor.data_ptr(); 40 | 41 | roilocal_dfvs_pool3d_kernel_launcher_stack_v2(batch_size, pts_num, boxes_num, dvs_pts_num, fps_pts_num, 42 | num_boxes_per_patch, hash_size, lambda, delta, xyz, boxes3d, point2patch_indices, patch2box_indices, 43 | pooled_pts_num, pooled_pts_idx); 44 | return 1; 45 | } 46 | -------------------------------------------------------------------------------- /tools/cfgs/dataset_configs/once_dataset.yaml: -------------------------------------------------------------------------------- 1 | DATASET: 'ONCEDataset' 2 | DATA_PATH: '../data/once' 3 | 4 | BACKEND: &BACKEND 5 | NAME: HardDiskBackend 6 | 7 | POINT_CLOUD_RANGE: [-75.2, -75.2, -5.0, 75.2, 75.2, 3.0] 8 | 9 | INFO_PATH: { 10 | 'raw_large': [once_infos_raw_large.pkl], 11 | 'train': [once_infos_train.pkl], 12 | 'val': [once_infos_val.pkl], 13 | 'test': [once_infos_test.pkl], 14 | } 15 | 16 | DATA_SPLIT: { 17 | 'train': train, 18 | 'test': val 19 | } 20 | 21 | DATA_AUGMENTOR: 22 | DISABLE_AUG_LIST: ['placeholder'] 23 | AUG_CONFIG_LIST: 24 | - NAME: gt_sampling 25 | BACKEND: 26 | <<: *BACKEND 27 | USE_ROAD_PLANE: False 28 | DB_INFO_PATH: 29 | - once_dbinfos_train.pkl 30 | PREPARE: { 31 | filter_by_min_points: ['Car:5', 'Bus:5', 'Truck:5', 'Pedestrian:5', 'Cyclist:5'], 32 | } 33 | 34 | SAMPLE_GROUPS: ['Car:1', 'Bus:4', 'Truck:3', 'Pedestrian:2', 'Cyclist:2'] 35 | NUM_POINT_FEATURES: 4 36 | REMOVE_EXTRA_WIDTH: [0.0, 0.0, 0.0] 37 | LIMIT_WHOLE_SCENE: True 38 | 39 | - NAME: random_world_flip 40 | PROBABILITY: 0.5 41 | ALONG_AXIS_LIST: ['x', 'y'] 42 | 43 | - NAME: random_world_rotation 44 | PROBABILITY: 1.0 45 | WORLD_ROT_ANGLE: [-0.78539816, 0.78539816] 46 | 47 | - NAME: random_world_scaling 48 | PROBABILITY: 1.0 49 | WORLD_SCALE_RANGE: [0.95, 1.05] 50 | 51 | 52 | POINT_FEATURE_ENCODING: { 53 | encoding_type: absolute_coordinates_encoding, 54 | used_feature_list: ['x', 'y', 'z', 'intensity'], 55 | src_feature_list: ['x', 'y', 'z', 'intensity'], 56 | } 57 | 58 | 59 | DATA_PROCESSOR: 60 | - NAME: mask_points_and_boxes_outside_range 61 | REMOVE_OUTSIDE_BOXES: True 62 | 63 | - NAME: shuffle_points 64 | SHUFFLE_ENABLED: { 65 | 'train': True, 66 | 'test': False 67 | } 68 | 69 | - NAME: transform_points_to_voxels 70 | VOXEL_SIZE: [0.1, 0.1, 0.2] 71 | MAX_POINTS_PER_VOXEL: 5 72 | MAX_NUMBER_OF_VOXELS: { 73 | 'train': 60000, 74 | 'test': 60000 75 | } -------------------------------------------------------------------------------- /pcdet/ops/pointnet2/pointnet2_batch/src/ball_query.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | batch version of ball query, modified from the original implementation of official PointNet++ codes. 3 | Written by Shaoshuai Shi 4 | All Rights Reserved 2018. 5 | */ 6 | 7 | 8 | #include 9 | #include 10 | // #include 11 | #include 12 | #include 13 | #include "ball_query_gpu.h" 14 | 15 | // extern THCState *state; 16 | 17 | #define CHECK_CUDA(x) do { \ 18 | if (!x.type().is_cuda()) { \ 19 | fprintf(stderr, "%s must be CUDA tensor at %s:%d\n", #x, __FILE__, __LINE__); \ 20 | exit(-1); \ 21 | } \ 22 | } while (0) 23 | #define CHECK_CONTIGUOUS(x) do { \ 24 | if (!x.is_contiguous()) { \ 25 | fprintf(stderr, "%s must be contiguous tensor at %s:%d\n", #x, __FILE__, __LINE__); \ 26 | exit(-1); \ 27 | } \ 28 | } while (0) 29 | #define CHECK_INPUT(x) CHECK_CUDA(x);CHECK_CONTIGUOUS(x) 30 | 31 | 32 | int ball_query_wrapper_fast(int b, int n, int m, float radius, int nsample, 33 | at::Tensor new_xyz_tensor, at::Tensor xyz_tensor, at::Tensor idx_cnt_tensor, at::Tensor idx_tensor) { 34 | CHECK_INPUT(new_xyz_tensor); 35 | CHECK_INPUT(xyz_tensor); 36 | const float *new_xyz = new_xyz_tensor.data_ptr(); 37 | const float *xyz = xyz_tensor.data_ptr(); 38 | int *idx_cnt = idx_cnt_tensor.data_ptr(); 39 | int *idx = idx_tensor.data_ptr(); 40 | 41 | ball_query_kernel_launcher_fast(b, n, m, radius, nsample, new_xyz, xyz, idx_cnt, idx); 42 | return 1; 43 | } 44 | 45 | int ball_query_dilated_wrapper_fast(int b, int n, int m, float radius_in, float radius_out, int nsample, 46 | at::Tensor new_xyz_tensor, at::Tensor xyz_tensor, at::Tensor idx_cnt_tensor, at::Tensor idx_tensor) { 47 | CHECK_INPUT(new_xyz_tensor); 48 | CHECK_INPUT(xyz_tensor); 49 | const float *new_xyz = new_xyz_tensor.data_ptr(); 50 | const float *xyz = xyz_tensor.data_ptr(); 51 | int *idx_cnt = idx_cnt_tensor.data_ptr(); 52 | int *idx = idx_tensor.data_ptr(); 53 | 54 | ball_query_dilated_kernel_launcher_fast(b, n, m, radius_in, radius_out, nsample, new_xyz, xyz, idx_cnt, idx); 55 | return 1; 56 | } -------------------------------------------------------------------------------- /tools/cfgs/dataset_configs/kitti_dataset.yaml: -------------------------------------------------------------------------------- 1 | DATASET: 'KittiDataset' 2 | DATA_PATH: '../data/kitti' 3 | 4 | BACKEND: &BACKEND 5 | NAME: HardDiskBackend 6 | 7 | POINT_CLOUD_RANGE: [0, -40, -3, 70.4, 40, 1] 8 | 9 | DATA_SPLIT: { 10 | 'train': train, 11 | 'test': val 12 | } 13 | 14 | INFO_PATH: { 15 | 'train': [kitti_infos_train.pkl], 16 | 'test': [kitti_infos_val.pkl], 17 | } 18 | 19 | GET_ITEM_LIST: ["points"] 20 | FOV_POINTS_ONLY: True 21 | ENABLE_SIMILAR_TYPE: True 22 | 23 | DATA_AUGMENTOR: 24 | DISABLE_AUG_LIST: ['placeholder'] 25 | AUG_CONFIG_LIST: 26 | - NAME: gt_sampling 27 | BACKEND: 28 | <<: *BACKEND 29 | USE_ROAD_PLANE: True 30 | DB_INFO_PATH: 31 | - kitti_dbinfos_train.pkl 32 | PREPARE: { 33 | filter_by_min_points: ['Car:5', 'Pedestrian:5', 'Cyclist:5'], 34 | filter_by_difficulty: [-1], 35 | } 36 | 37 | SAMPLE_GROUPS: ['Car:20','Pedestrian:15', 'Cyclist:15'] 38 | NUM_POINT_FEATURES: 4 39 | DATABASE_WITH_FAKELIDAR: False 40 | REMOVE_EXTRA_WIDTH: [0.0, 0.0, 0.0] 41 | LIMIT_WHOLE_SCENE: True 42 | 43 | - NAME: random_world_flip 44 | PROBABILITY: 0.5 45 | ALONG_AXIS_LIST: ['x'] 46 | 47 | - NAME: random_world_rotation 48 | PROBABILITY: 1.0 49 | WORLD_ROT_ANGLE: [-0.78539816, 0.78539816] 50 | 51 | - NAME: random_world_scaling 52 | PROBABILITY: 1.0 53 | WORLD_SCALE_RANGE: [0.95, 1.05] 54 | 55 | 56 | POINT_FEATURE_ENCODING: { 57 | encoding_type: absolute_coordinates_encoding, 58 | used_feature_list: ['x', 'y', 'z', 'intensity'], 59 | src_feature_list: ['x', 'y', 'z', 'intensity'], 60 | } 61 | 62 | 63 | DATA_PROCESSOR: 64 | - NAME: mask_points_and_boxes_outside_range 65 | REMOVE_OUTSIDE_BOXES: True 66 | 67 | - NAME: shuffle_points 68 | SHUFFLE_ENABLED: { 69 | 'train': True, 70 | 'test': False 71 | } 72 | 73 | - NAME: transform_points_to_voxels 74 | VOXEL_SIZE: [0.05, 0.05, 0.1] 75 | MAX_POINTS_PER_VOXEL: 5 76 | MAX_NUMBER_OF_VOXELS: { 77 | 'train': 16000, 78 | 'test': 40000 79 | } 80 | -------------------------------------------------------------------------------- /pcdet/ops/pointnet2/pointnet2_batch/src/sampling.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | batch version of point sampling and gathering, modified from the original implementation of official PointNet++ codes. 3 | Written by Shaoshuai Shi 4 | All Rights Reserved 2018. 5 | */ 6 | 7 | 8 | #include 9 | #include 10 | #include 11 | // #include 12 | 13 | #include "sampling_gpu.h" 14 | 15 | // extern THCState *state; 16 | 17 | 18 | int gather_points_wrapper_fast(int b, int c, int n, int npoints, 19 | at::Tensor points_tensor, at::Tensor idx_tensor, at::Tensor out_tensor){ 20 | const float *points = points_tensor.data_ptr(); 21 | const int *idx = idx_tensor.data_ptr(); 22 | float *out = out_tensor.data_ptr(); 23 | 24 | gather_points_kernel_launcher_fast(b, c, n, npoints, points, idx, out); 25 | return 1; 26 | } 27 | 28 | 29 | int gather_points_grad_wrapper_fast(int b, int c, int n, int npoints, 30 | at::Tensor grad_out_tensor, at::Tensor idx_tensor, at::Tensor grad_points_tensor) { 31 | 32 | const float *grad_out = grad_out_tensor.data_ptr(); 33 | const int *idx = idx_tensor.data_ptr(); 34 | float *grad_points = grad_points_tensor.data_ptr(); 35 | 36 | gather_points_grad_kernel_launcher_fast(b, c, n, npoints, grad_out, idx, grad_points); 37 | return 1; 38 | } 39 | 40 | 41 | int furthest_point_sampling_wrapper(int b, int n, int m, 42 | at::Tensor points_tensor, at::Tensor temp_tensor, at::Tensor idx_tensor) { 43 | 44 | const float *points = points_tensor.data_ptr(); 45 | float *temp = temp_tensor.data_ptr(); 46 | int *idx = idx_tensor.data_ptr(); 47 | 48 | furthest_point_sampling_kernel_launcher(b, n, m, points, temp, idx); 49 | return 1; 50 | } 51 | 52 | 53 | int furthest_point_sampling_matrix_wrapper(int b, int n, int m, 54 | at::Tensor matrix_tensor, at::Tensor temp_tensor, at::Tensor idx_tensor) { 55 | 56 | const float *matrix = matrix_tensor.data_ptr(); 57 | float *temp = temp_tensor.data_ptr(); 58 | int *idx = idx_tensor.data_ptr(); 59 | 60 | furthest_point_sampling_matrix_kernel_launcher(b, n, m, matrix, temp, idx); 61 | return 1; 62 | } 63 | -------------------------------------------------------------------------------- /pcdet/ops/pointnet2/pointnet2_batch/src/interpolate.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | batch version of point interpolation, modified from the original implementation of official PointNet++ codes. 3 | Written by Shaoshuai Shi 4 | All Rights Reserved 2018. 5 | */ 6 | 7 | 8 | #include 9 | #include 10 | // #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include "interpolate_gpu.h" 17 | 18 | // extern THCState *state; 19 | 20 | 21 | void three_nn_wrapper_fast(int b, int n, int m, at::Tensor unknown_tensor, 22 | at::Tensor known_tensor, at::Tensor dist2_tensor, at::Tensor idx_tensor) { 23 | const float *unknown = unknown_tensor.data_ptr(); 24 | const float *known = known_tensor.data_ptr(); 25 | float *dist2 = dist2_tensor.data_ptr(); 26 | int *idx = idx_tensor.data_ptr(); 27 | 28 | three_nn_kernel_launcher_fast(b, n, m, unknown, known, dist2, idx); 29 | } 30 | 31 | 32 | void three_interpolate_wrapper_fast(int b, int c, int m, int n, 33 | at::Tensor points_tensor, 34 | at::Tensor idx_tensor, 35 | at::Tensor weight_tensor, 36 | at::Tensor out_tensor) { 37 | 38 | const float *points = points_tensor.data_ptr(); 39 | const float *weight = weight_tensor.data_ptr(); 40 | float *out = out_tensor.data_ptr(); 41 | const int *idx = idx_tensor.data_ptr(); 42 | 43 | three_interpolate_kernel_launcher_fast(b, c, m, n, points, idx, weight, out); 44 | } 45 | 46 | void three_interpolate_grad_wrapper_fast(int b, int c, int n, int m, 47 | at::Tensor grad_out_tensor, 48 | at::Tensor idx_tensor, 49 | at::Tensor weight_tensor, 50 | at::Tensor grad_points_tensor) { 51 | 52 | const float *grad_out = grad_out_tensor.data_ptr(); 53 | const float *weight = weight_tensor.data_ptr(); 54 | float *grad_points = grad_points_tensor.data_ptr(); 55 | const int *idx = idx_tensor.data_ptr(); 56 | 57 | three_interpolate_grad_kernel_launcher_fast(b, c, n, m, grad_out, idx, weight, grad_points); 58 | } 59 | -------------------------------------------------------------------------------- /pcdet/datasets/kitti360/kitti360_dataset.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | from ..dataset import DatasetTemplate 3 | import glob 4 | import os 5 | 6 | 7 | class Kitti360Dataset(DatasetTemplate): 8 | def __init__(self, dataset_cfg, class_names, training=True, root_path=None, logger=None): 9 | """ 10 | Args: 11 | root_path: 12 | dataset_cfg: 13 | class_names: 14 | training: 15 | logger: 16 | """ 17 | super().__init__( 18 | dataset_cfg=dataset_cfg, class_names=class_names, training=training, root_path=root_path, logger=logger 19 | ) 20 | 21 | self.kitti_infos = [] 22 | self.include_kitti_data() 23 | 24 | def include_kitti_data(self): 25 | if self.logger is not None: 26 | self.logger.info('Loading KITTI dataset') 27 | 28 | self.kitti_infos = list(self.client.list_dir_or_file( 29 | os.path.join(self.root_path, 'data_3d_raw'), 30 | list_dir=False, recursive=True, suffix='.bin' 31 | )) 32 | 33 | if self.logger is not None: 34 | self.logger.info('Total samples for KITTI dataset: %d' % (len(self.kitti_infos))) 35 | 36 | def get_lidar(self, lidar_file): 37 | return self.client.load_to_numpy(str(self.root_path / 'data_3d_raw' / lidar_file), dtype=np.float32).reshape(-1, 4) 38 | 39 | def __len__(self): 40 | if self._merge_all_iters_to_one_epoch: 41 | return len(self.kitti_infos) * self.total_epochs 42 | 43 | return len(self.kitti_infos) 44 | 45 | def __getitem__(self, index): 46 | # index = 4 47 | if self._merge_all_iters_to_one_epoch: 48 | index = index % len(self.kitti_infos) 49 | 50 | lidar_path = self.kitti_infos[index] 51 | 52 | get_item_list = self.dataset_cfg.get('GET_ITEM_LIST', ['points']) 53 | 54 | path_split = str(lidar_path).split('/') 55 | input_dict = { 56 | 'frame_id': path_split[-4] + '_' + path_split[-1][:-4], 57 | } 58 | 59 | if "points" in get_item_list: 60 | points = self.get_lidar(lidar_path) 61 | input_dict['points'] = points 62 | 63 | data_dict = self.prepare_data(data_dict=input_dict) 64 | 65 | return data_dict 66 | -------------------------------------------------------------------------------- /pcdet/ops/roipoint_pool3d/src/roipoint_pool3d.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #define CHECK_CUDA(x) do { \ 5 | if (!x.type().is_cuda()) { \ 6 | fprintf(stderr, "%s must be CUDA tensor at %s:%d\n", #x, __FILE__, __LINE__); \ 7 | exit(-1); \ 8 | } \ 9 | } while (0) 10 | #define CHECK_CONTIGUOUS(x) do { \ 11 | if (!x.is_contiguous()) { \ 12 | fprintf(stderr, "%s must be contiguous tensor at %s:%d\n", #x, __FILE__, __LINE__); \ 13 | exit(-1); \ 14 | } \ 15 | } while (0) 16 | #define CHECK_INPUT(x) CHECK_CUDA(x);CHECK_CONTIGUOUS(x) 17 | 18 | 19 | void roipool3dLauncher(int batch_size, int pts_num, int boxes_num, int feature_in_len, int sampled_pts_num, 20 | const float *xyz, const float *boxes3d, const float *pts_feature, float *pooled_features, int *pooled_empty_flag); 21 | 22 | 23 | int roipool3d_gpu(at::Tensor xyz, at::Tensor boxes3d, at::Tensor pts_feature, at::Tensor pooled_features, at::Tensor pooled_empty_flag){ 24 | // params xyz: (B, N, 3) 25 | // params boxes3d: (B, M, 7) 26 | // params pts_feature: (B, N, C) 27 | // params pooled_features: (B, M, 512, 3+C) 28 | // params pooled_empty_flag: (B, M) 29 | CHECK_INPUT(xyz); 30 | CHECK_INPUT(boxes3d); 31 | CHECK_INPUT(pts_feature); 32 | CHECK_INPUT(pooled_features); 33 | CHECK_INPUT(pooled_empty_flag); 34 | 35 | int batch_size = xyz.size(0); 36 | int pts_num = xyz.size(1); 37 | int boxes_num = boxes3d.size(1); 38 | int feature_in_len = pts_feature.size(2); 39 | int sampled_pts_num = pooled_features.size(2); 40 | 41 | 42 | const float * xyz_data = xyz.data(); 43 | const float * boxes3d_data = boxes3d.data(); 44 | const float * pts_feature_data = pts_feature.data(); 45 | float * pooled_features_data = pooled_features.data(); 46 | int * pooled_empty_flag_data = pooled_empty_flag.data(); 47 | 48 | roipool3dLauncher(batch_size, pts_num, boxes_num, feature_in_len, sampled_pts_num, 49 | xyz_data, boxes3d_data, pts_feature_data, pooled_features_data, pooled_empty_flag_data); 50 | 51 | 52 | 53 | return 1; 54 | } 55 | 56 | 57 | PYBIND11_MODULE(TORCH_EXTENSION_NAME, m) { 58 | m.def("forward", &roipool3d_gpu, "roipool3d forward (CUDA)"); 59 | } 60 | 61 | -------------------------------------------------------------------------------- /pcdet/datasets/processor/point_feature_encoder.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | 3 | 4 | class PointFeatureEncoder(object): 5 | def __init__(self, config, point_cloud_range=None): 6 | super().__init__() 7 | self.point_encoding_config = config 8 | assert list(self.point_encoding_config.src_feature_list[0:3]) == ['x', 'y', 'z'] 9 | self.used_feature_list = self.point_encoding_config.used_feature_list 10 | self.src_feature_list = self.point_encoding_config.src_feature_list 11 | self.point_cloud_range = point_cloud_range 12 | 13 | @property 14 | def num_point_features(self): 15 | return getattr(self, self.point_encoding_config.encoding_type)(points=None) 16 | 17 | def forward(self, data_dict): 18 | """ 19 | Args: 20 | data_dict: 21 | points: (N, 3 + C_in) 22 | ... 23 | Returns: 24 | data_dict: 25 | points: (N, 3 + C_out), 26 | use_lead_xyz: whether to use xyz as point-wise features 27 | ... 28 | """ 29 | data_dict['points'], use_lead_xyz = getattr(self, self.point_encoding_config.encoding_type)( 30 | data_dict['points'] 31 | ) 32 | data_dict['use_lead_xyz'] = use_lead_xyz 33 | 34 | if self.point_encoding_config.get('filter_sweeps', False) and 'timestamp' in self.src_feature_list: 35 | max_sweeps = self.point_encoding_config.max_sweeps 36 | idx = self.src_feature_list.index('timestamp') 37 | dt = np.round(data_dict['points'][:, idx], 2) 38 | max_dt = sorted(np.unique(dt))[min(len(np.unique(dt))-1, max_sweeps-1)] 39 | data_dict['points'] = data_dict['points'][dt <= max_dt] 40 | 41 | return data_dict 42 | 43 | def absolute_coordinates_encoding(self, points=None): 44 | if points is None: 45 | num_output_features = len(self.used_feature_list) 46 | return num_output_features 47 | 48 | point_feature_list = [points[:, 0:3]] 49 | for x in self.used_feature_list: 50 | if x in ['x', 'y', 'z']: 51 | continue 52 | idx = self.src_feature_list.index(x) 53 | point_feature_list.append(points[:, idx:idx+1]) 54 | point_features = np.concatenate(point_feature_list, axis=1) 55 | return point_features, True 56 | -------------------------------------------------------------------------------- /pcdet/ops/roipoint_pool3d/roipoint_pool3d_utils.py: -------------------------------------------------------------------------------- 1 | import torch 2 | import torch.nn as nn 3 | from torch.autograd import Function 4 | 5 | from ...utils import box_utils 6 | from . import roipoint_pool3d_cuda 7 | 8 | 9 | class RoIPointPool3d(nn.Module): 10 | def __init__(self, num_sampled_points=512, pool_extra_width=1.0): 11 | super().__init__() 12 | self.num_sampled_points = num_sampled_points 13 | self.pool_extra_width = pool_extra_width 14 | 15 | def forward(self, points, point_features, boxes3d): 16 | """ 17 | Args: 18 | points: (B, N, 3) 19 | point_features: (B, N, C) 20 | boxes3d: (B, M, 7), [x, y, z, dx, dy, dz, heading] 21 | 22 | Returns: 23 | pooled_features: (B, M, 512, 3 + C) 24 | pooled_empty_flag: (B, M) 25 | """ 26 | return RoIPointPool3dFunction.apply( 27 | points, point_features, boxes3d, self.pool_extra_width, self.num_sampled_points 28 | ) 29 | 30 | 31 | class RoIPointPool3dFunction(Function): 32 | @staticmethod 33 | def forward(ctx, points, point_features, boxes3d, pool_extra_width, num_sampled_points=512): 34 | """ 35 | Args: 36 | ctx: 37 | points: (B, N, 3) 38 | point_features: (B, N, C) 39 | boxes3d: (B, num_boxes, 7), [x, y, z, dx, dy, dz, heading] 40 | pool_extra_width: 41 | num_sampled_points: 42 | 43 | Returns: 44 | pooled_features: (B, num_boxes, 512, 3 + C) 45 | pooled_empty_flag: (B, num_boxes) 46 | """ 47 | assert points.shape.__len__() == 3 and points.shape[2] == 3 48 | batch_size, boxes_num, feature_len = points.shape[0], boxes3d.shape[1], point_features.shape[2] 49 | pooled_boxes3d = box_utils.enlarge_box3d(boxes3d.view(-1, 7), pool_extra_width).view(batch_size, -1, 7) 50 | 51 | pooled_features = point_features.new_zeros((batch_size, boxes_num, num_sampled_points, 3 + feature_len)) 52 | pooled_empty_flag = point_features.new_zeros((batch_size, boxes_num)).int() 53 | 54 | roipoint_pool3d_cuda.forward( 55 | points.contiguous(), pooled_boxes3d.contiguous(), 56 | point_features.contiguous(), pooled_features, pooled_empty_flag 57 | ) 58 | 59 | return pooled_features, pooled_empty_flag 60 | 61 | @staticmethod 62 | def backward(ctx, grad_out): 63 | raise NotImplementedError 64 | 65 | 66 | if __name__ == '__main__': 67 | pass 68 | -------------------------------------------------------------------------------- /tools/cfgs/dataset_configs/nuscenes_dataset.yaml: -------------------------------------------------------------------------------- 1 | DATASET: 'NuScenesDataset' 2 | DATA_PATH: '../data/nuscenes' 3 | 4 | VERSION: 'v1.0-trainval' 5 | MAX_SWEEPS: 10 6 | PRED_VELOCITY: True 7 | SET_NAN_VELOCITY_TO_ZEROS: True 8 | FILTER_MIN_POINTS_IN_GT: 1 9 | 10 | BACKEND: &BACKEND 11 | NAME: HardDiskBackend 12 | 13 | DATA_SPLIT: { 14 | 'train': train, 15 | 'test': val 16 | } 17 | 18 | INFO_PATH: { 19 | 'train': [nuscenes_infos_10sweeps_train.pkl], 20 | 'test': [nuscenes_infos_10sweeps_val.pkl], 21 | } 22 | 23 | POINT_CLOUD_RANGE: [-51.2, -51.2, -5.0, 51.2, 51.2, 3.0] 24 | 25 | BALANCED_RESAMPLING: True 26 | 27 | DATA_AUGMENTOR: 28 | DISABLE_AUG_LIST: ['placeholder'] 29 | AUG_CONFIG_LIST: 30 | - NAME: gt_sampling 31 | BACKEND: 32 | <<: *BACKEND 33 | DB_INFO_PATH: 34 | - nuscenes_dbinfos_10sweeps_withvelo.pkl 35 | PREPARE: { 36 | filter_by_min_points: [ 37 | 'car:5','truck:5', 'construction_vehicle:5', 'bus:5', 'trailer:5', 38 | 'barrier:5', 'motorcycle:5', 'bicycle:5', 'pedestrian:5', 'traffic_cone:5' 39 | ], 40 | } 41 | 42 | SAMPLE_GROUPS: [ 43 | 'car:2','truck:3', 'construction_vehicle:7', 'bus:4', 'trailer:6', 44 | 'barrier:2', 'motorcycle:6', 'bicycle:6', 'pedestrian:2', 'traffic_cone:2' 45 | ] 46 | 47 | NUM_POINT_FEATURES: 5 48 | DATABASE_WITH_FAKELIDAR: False 49 | REMOVE_EXTRA_WIDTH: [0.0, 0.0, 0.0] 50 | LIMIT_WHOLE_SCENE: True 51 | 52 | - NAME: random_world_flip 53 | ALONG_AXIS_LIST: ['x', 'y'] 54 | 55 | - NAME: random_world_rotation 56 | WORLD_ROT_ANGLE: [-0.3925, 0.3925] 57 | 58 | - NAME: random_world_scaling 59 | WORLD_SCALE_RANGE: [0.95, 1.05] 60 | 61 | 62 | POINT_FEATURE_ENCODING: { 63 | encoding_type: absolute_coordinates_encoding, 64 | used_feature_list: ['x', 'y', 'z', 'intensity', 'timestamp'], 65 | src_feature_list: ['x', 'y', 'z', 'intensity', 'timestamp'], 66 | } 67 | 68 | 69 | DATA_PROCESSOR: 70 | - NAME: mask_points_and_boxes_outside_range 71 | REMOVE_OUTSIDE_BOXES: True 72 | 73 | - NAME: shuffle_points 74 | SHUFFLE_ENABLED: { 75 | 'train': True, 76 | 'test': True 77 | } 78 | 79 | - NAME: transform_points_to_voxels 80 | VOXEL_SIZE: [0.1, 0.1, 0.2] 81 | MAX_POINTS_PER_VOXEL: 10 82 | MAX_NUMBER_OF_VOXELS: { 83 | 'train': 60000, 84 | 'test': 60000 85 | } 86 | -------------------------------------------------------------------------------- /pcdet/datasets/kitti/kitti_utils.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | from ...utils import box_utils 3 | 4 | 5 | def transform_annotations_to_kitti_format(annos, map_name_to_kitti=None, info_with_fakelidar=False): 6 | """ 7 | Args: 8 | annos: 9 | map_name_to_kitti: dict, map name to KITTI names (Car, Pedestrian, Cyclist) 10 | info_with_fakelidar: 11 | Returns: 12 | 13 | """ 14 | for anno in annos: 15 | for k in range(anno['name'].shape[0]): 16 | anno['name'][k] = map_name_to_kitti[anno['name'][k]] 17 | 18 | anno['bbox'] = np.zeros((len(anno['name']), 4)) 19 | anno['bbox'][:, 2:4] = 50 # [0, 0, 50, 50] 20 | anno['truncated'] = np.zeros(len(anno['name'])) 21 | anno['occluded'] = np.zeros(len(anno['name'])) 22 | if 'boxes_lidar' in anno: 23 | gt_boxes_lidar = anno['boxes_lidar'].copy() 24 | else: 25 | gt_boxes_lidar = anno['gt_boxes_lidar'].copy() 26 | 27 | if len(gt_boxes_lidar) > 0: 28 | if info_with_fakelidar: 29 | gt_boxes_lidar = box_utils.boxes3d_kitti_fakelidar_to_lidar(gt_boxes_lidar) 30 | 31 | gt_boxes_lidar[:, 2] -= gt_boxes_lidar[:, 5] / 2 32 | anno['location'] = np.zeros((gt_boxes_lidar.shape[0], 3)) 33 | anno['location'][:, 0] = -gt_boxes_lidar[:, 1] # x = -y_lidar 34 | anno['location'][:, 1] = -gt_boxes_lidar[:, 2] # y = -z_lidar 35 | anno['location'][:, 2] = gt_boxes_lidar[:, 0] # z = x_lidar 36 | dxdydz = gt_boxes_lidar[:, 3:6] 37 | anno['dimensions'] = dxdydz[:, [0, 2, 1]] # lwh ==> lhw 38 | anno['rotation_y'] = -gt_boxes_lidar[:, 6] - np.pi / 2.0 39 | anno['alpha'] = -np.arctan2(-gt_boxes_lidar[:, 1], gt_boxes_lidar[:, 0]) + anno['rotation_y'] 40 | else: 41 | anno['location'] = anno['dimensions'] = np.zeros((0, 3)) 42 | anno['rotation_y'] = anno['alpha'] = np.zeros(0) 43 | 44 | return annos 45 | 46 | 47 | def calib_to_matricies(calib): 48 | """ 49 | Converts calibration object to transformation matricies 50 | Args: 51 | calib: calibration.Calibration, Calibration object 52 | Returns 53 | V2R: (4, 4), Lidar to rectified camera transformation matrix 54 | P2: (3, 4), Camera projection matrix 55 | """ 56 | V2C = np.vstack((calib.V2C, np.array([0, 0, 0, 1], dtype=np.float32))) # (4, 4) 57 | R0 = np.hstack((calib.R0, np.zeros((3, 1), dtype=np.float32))) # (3, 4) 58 | R0 = np.vstack((R0, np.array([0, 0, 0, 1], dtype=np.float32))) # (4, 4) 59 | V2R = R0 @ V2C 60 | P2 = calib.P2 61 | return V2R, P2 -------------------------------------------------------------------------------- /tools/train_utils/optimization/__init__.py: -------------------------------------------------------------------------------- 1 | from functools import partial 2 | 3 | import torch.nn as nn 4 | import torch.optim as optim 5 | import torch.optim.lr_scheduler as lr_sched 6 | 7 | from .fastai_optim import OptimWrapper 8 | from .learning_schedules_fastai import CosineWarmupLR, OneCycle 9 | 10 | 11 | def build_optimizer(model, optim_cfg): 12 | if optim_cfg.OPTIMIZER == 'adam': 13 | optimizer = optim.Adam(model.parameters(), lr=optim_cfg.LR, weight_decay=optim_cfg.WEIGHT_DECAY) 14 | elif optim_cfg.OPTIMIZER == 'sgd': 15 | optimizer = optim.SGD( 16 | model.parameters(), lr=optim_cfg.LR, weight_decay=optim_cfg.WEIGHT_DECAY, 17 | momentum=optim_cfg.MOMENTUM 18 | ) 19 | elif optim_cfg.OPTIMIZER == 'adam_onecycle': 20 | def children(m: nn.Module): 21 | return list(m.children()) 22 | 23 | def num_children(m: nn.Module) -> int: 24 | return len(children(m)) 25 | 26 | flatten_model = lambda m: sum(map(flatten_model, m.children()), []) if num_children(m) else [m] 27 | get_layer_groups = lambda m: [nn.Sequential(*flatten_model(m))] 28 | 29 | optimizer_func = partial(optim.Adam, betas=(0.9, 0.99)) 30 | optimizer = OptimWrapper.create( 31 | optimizer_func, 3e-3, get_layer_groups(model), wd=optim_cfg.WEIGHT_DECAY, true_wd=True, bn_wd=True 32 | ) 33 | else: 34 | raise NotImplementedError 35 | 36 | return optimizer 37 | 38 | 39 | def build_scheduler(optimizer, total_iters_each_epoch, total_epochs, last_epoch, optim_cfg): 40 | decay_steps = [x * total_iters_each_epoch for x in optim_cfg.DECAY_STEP_LIST] 41 | def lr_lbmd(cur_epoch): 42 | cur_decay = 1 43 | for decay_step in decay_steps: 44 | if cur_epoch >= decay_step: 45 | cur_decay = cur_decay * optim_cfg.LR_DECAY 46 | return max(cur_decay, optim_cfg.LR_CLIP / optim_cfg.LR) 47 | 48 | lr_warmup_scheduler = None 49 | total_steps = total_iters_each_epoch * total_epochs 50 | if optim_cfg.OPTIMIZER == 'adam_onecycle': 51 | lr_scheduler = OneCycle( 52 | optimizer, total_steps, optim_cfg.LR, list(optim_cfg.MOMS), optim_cfg.DIV_FACTOR, optim_cfg.PCT_START 53 | ) 54 | else: 55 | lr_scheduler = lr_sched.LambdaLR(optimizer, lr_lbmd, last_epoch=last_epoch) 56 | 57 | if optim_cfg.LR_WARMUP: 58 | lr_warmup_scheduler = CosineWarmupLR( 59 | optimizer, T_max=optim_cfg.WARMUP_EPOCH * len(total_iters_each_epoch), 60 | eta_min=optim_cfg.LR / optim_cfg.DIV_FACTOR 61 | ) 62 | 63 | return lr_scheduler, lr_warmup_scheduler 64 | -------------------------------------------------------------------------------- /pcdet/ops/sst_ops/src/sst_ops_gpu.cu: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | #define THREADS_PER_BLOCK 256 7 | #define DIVUP(m,n) ((m) / (n) + ((m) % (n) > 0)) 8 | #define EMPTY_KEY -1 9 | 10 | __device__ inline int simple_hash(int k, int hash_size) { 11 | return k % hash_size; 12 | } 13 | 14 | __global__ void ingroup_inds_kernel(int N, const long *group_inds, long *out_inds, int *ingroup_counter) { 15 | int pt_idx = blockIdx.x * blockDim.x + threadIdx.x; 16 | if (pt_idx >= N) return; 17 | long this_group_id = group_inds[pt_idx]; 18 | int cnt = atomicAdd(ingroup_counter + this_group_id, 1); 19 | out_inds[pt_idx] = cnt; 20 | } 21 | 22 | __global__ void group_inner_inds_kernel(int N, int K, const long *inverse_inds, long *group_inds, int *ingroup_counter) { 23 | int pt_idx = blockIdx.x * blockDim.x + threadIdx.x; 24 | if (pt_idx >= N) return; 25 | long this_group_id = inverse_inds[pt_idx]; 26 | int cnt = atomicAdd(ingroup_counter + this_group_id, 1); 27 | if (cnt < K) group_inds[this_group_id * K + cnt] = pt_idx; 28 | } 29 | 30 | __global__ void repeat_group_idx_kernel(int M, int K, const int *ingroup_counter, long *group_inds){ 31 | // params ingroup_counter: (M,) 32 | // params group_inds: (M, K) 33 | int pt_idx = blockIdx.x * blockDim.x + threadIdx.x; 34 | if (pt_idx >= M) return; 35 | int cnt = ingroup_counter[pt_idx]; 36 | if (cnt == 0) return; 37 | for (int i = cnt; i < K; i++) 38 | group_inds[pt_idx * K + i] = group_inds[pt_idx * K + i % cnt]; 39 | } 40 | 41 | void ingroup_inds_launcher(int N, int max_group_id, const long *group_inds, long *out_inds) { 42 | int *ingroup_counter = NULL; 43 | cudaMalloc(&ingroup_counter, (max_group_id + 1) * sizeof(int)); 44 | cudaMemset(ingroup_counter, 0, (max_group_id + 1) * sizeof(int)); 45 | 46 | dim3 blocks(DIVUP(N, THREADS_PER_BLOCK)); 47 | dim3 threads(THREADS_PER_BLOCK); 48 | 49 | ingroup_inds_kernel<<>>(N, group_inds, out_inds, ingroup_counter); 50 | cudaFree(ingroup_counter); 51 | } 52 | 53 | void group_inner_inds_launcher(int N, int M, int K, const long *inverse_inds, long *group_inds) { 54 | int *ingroup_counter = NULL; 55 | cudaMalloc(&ingroup_counter, M * sizeof(int)); 56 | cudaMemset(ingroup_counter, 0, M * sizeof(int)); 57 | 58 | dim3 blocks(DIVUP(N, THREADS_PER_BLOCK)); 59 | dim3 threads(THREADS_PER_BLOCK); 60 | group_inner_inds_kernel<<>>(N, K, inverse_inds, group_inds, ingroup_counter); 61 | 62 | dim3 blocks1(DIVUP(M, THREADS_PER_BLOCK)); 63 | repeat_group_idx_kernel<<>>(M, K, ingroup_counter, group_inds); 64 | cudaFree(ingroup_counter); 65 | } 66 | -------------------------------------------------------------------------------- /pcdet/ops/pointnet2/pointnet2_stack/src/group_points.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | Stacked-batch-data version of point grouping, modified from the original implementation of official PointNet++ codes. 3 | Written by Shaoshuai Shi 4 | All Rights Reserved 2019-2020. 5 | */ 6 | 7 | 8 | #include 9 | #include 10 | #include 11 | #include 12 | // #include 13 | #include "group_points_gpu.h" 14 | 15 | // extern THCState *state; 16 | #define CHECK_CUDA(x) do { \ 17 | if (!x.type().is_cuda()) { \ 18 | fprintf(stderr, "%s must be CUDA tensor at %s:%d\n", #x, __FILE__, __LINE__); \ 19 | exit(-1); \ 20 | } \ 21 | } while (0) 22 | #define CHECK_CONTIGUOUS(x) do { \ 23 | if (!x.is_contiguous()) { \ 24 | fprintf(stderr, "%s must be contiguous tensor at %s:%d\n", #x, __FILE__, __LINE__); \ 25 | exit(-1); \ 26 | } \ 27 | } while (0) 28 | #define CHECK_INPUT(x) CHECK_CUDA(x);CHECK_CONTIGUOUS(x) 29 | 30 | 31 | int group_points_grad_wrapper_stack(int B, int M, int C, int N, int nsample, 32 | at::Tensor grad_out_tensor, at::Tensor idx_tensor, at::Tensor idx_batch_cnt_tensor, 33 | at::Tensor features_batch_cnt_tensor, at::Tensor grad_features_tensor) { 34 | 35 | CHECK_INPUT(grad_out_tensor); 36 | CHECK_INPUT(idx_tensor); 37 | CHECK_INPUT(idx_batch_cnt_tensor); 38 | CHECK_INPUT(features_batch_cnt_tensor); 39 | CHECK_INPUT(grad_features_tensor); 40 | 41 | const float *grad_out = grad_out_tensor.data(); 42 | const int *idx = idx_tensor.data(); 43 | const int *idx_batch_cnt = idx_batch_cnt_tensor.data(); 44 | const int *features_batch_cnt = features_batch_cnt_tensor.data(); 45 | float *grad_features = grad_features_tensor.data(); 46 | 47 | group_points_grad_kernel_launcher_stack(B, M, C, N, nsample, grad_out, idx, idx_batch_cnt, features_batch_cnt, grad_features); 48 | return 1; 49 | } 50 | 51 | 52 | int group_points_wrapper_stack(int B, int M, int C, int nsample, 53 | at::Tensor features_tensor, at::Tensor features_batch_cnt_tensor, 54 | at::Tensor idx_tensor, at::Tensor idx_batch_cnt_tensor, at::Tensor out_tensor) { 55 | 56 | CHECK_INPUT(features_tensor); 57 | CHECK_INPUT(features_batch_cnt_tensor); 58 | CHECK_INPUT(idx_tensor); 59 | CHECK_INPUT(idx_batch_cnt_tensor); 60 | CHECK_INPUT(out_tensor); 61 | 62 | const float *features = features_tensor.data(); 63 | const int *idx = idx_tensor.data(); 64 | const int *features_batch_cnt = features_batch_cnt_tensor.data(); 65 | const int *idx_batch_cnt = idx_batch_cnt_tensor.data(); 66 | float *out = out_tensor.data(); 67 | 68 | group_points_kernel_launcher_stack(B, M, C, nsample, features, features_batch_cnt, idx, idx_batch_cnt, out); 69 | return 1; 70 | } -------------------------------------------------------------------------------- /tools/cfgs/dataset_configs/waymo_dataset.yaml: -------------------------------------------------------------------------------- 1 | DATASET: 'WaymoDataset' 2 | DATA_PATH: '../data/waymo' 3 | PROCESSED_DATA_TAG: 'waymo_processed_data' 4 | 5 | NUM_SWEEPS: 1 6 | USE_TIME_LAG: False 7 | BACKEND: &BACKEND 8 | NAME: HardDiskBackend 9 | 10 | POINT_CLOUD_RANGE: [-75.2, -75.2, -2, 75.2, 75.2, 4] 11 | 12 | DATA_SPLIT: { 13 | 'train': train, 14 | 'test': val 15 | } 16 | 17 | SAMPLED_INTERVAL: { 18 | 'train': 1, 19 | 'test': 1 20 | } 21 | 22 | FILTER_EMPTY_BOXES_FOR_TRAIN: True 23 | DISABLE_NLZ_FLAG_ON_POINTS: True 24 | 25 | USE_SHARED_MEMORY: False # it will load the data to shared memory to speed up (DO NOT USE IT IF YOU DO NOT FULLY UNDERSTAND WHAT WILL HAPPEN) 26 | SHARED_MEMORY_FILE_LIMIT: 35000 # set it based on the size of your shared memory 27 | 28 | DATA_AUGMENTOR: 29 | DISABLE_AUG_LIST: ['placeholder'] 30 | AUG_CONFIG_LIST: 31 | - NAME: gt_sampling 32 | 33 | BACKEND: 34 | <<: *BACKEND 35 | USE_ROAD_PLANE: False 36 | DB_INFO_PATH: 37 | - waymo_processed_data_waymo_dbinfos_train_sampled_1.pkl 38 | 39 | USE_SHARED_MEMORY: False # set it to True to speed up (it costs about 15GB shared memory) 40 | DB_DATA_PATH: 41 | - waymo_processed_data_gt_database_train_sampled_1_global.npy 42 | 43 | PREPARE: { 44 | filter_by_min_points: ['Vehicle:5', 'Pedestrian:5', 'Cyclist:5'], 45 | filter_by_difficulty: [-1], 46 | } 47 | 48 | SAMPLE_GROUPS: ['Vehicle:15', 'Pedestrian:10', 'Cyclist:10'] 49 | NUM_POINT_FEATURES: 5 50 | REMOVE_POINTS: True 51 | REMOVE_EXTRA_WIDTH: [0.0, 0.0, 0.0] 52 | LIMIT_WHOLE_SCENE: True 53 | 54 | - NAME: random_world_flip 55 | PROBABILITY: 0.5 56 | ALONG_AXIS_LIST: ['x', 'y'] 57 | 58 | - NAME: random_world_rotation 59 | PROBABILITY: 1.0 60 | WORLD_ROT_ANGLE: [-0.78539816, 0.78539816] 61 | 62 | - NAME: random_world_scaling 63 | PROBABILITY: 1.0 64 | WORLD_SCALE_RANGE: [0.95, 1.05] 65 | 66 | - NAME: random_world_translation 67 | PROBABILITY: 1.0 68 | NOISE_TRANSLATE_STD: 0.5 69 | 70 | 71 | POINT_FEATURE_ENCODING: { 72 | encoding_type: absolute_coordinates_encoding, 73 | used_feature_list: ['x', 'y', 'z', 'intensity', 'elongation'], 74 | src_feature_list: ['x', 'y', 'z', 'intensity', 'elongation'], 75 | } 76 | 77 | 78 | DATA_PROCESSOR: 79 | - NAME: mask_points_and_boxes_outside_range 80 | REMOVE_OUTSIDE_BOXES: True 81 | 82 | - NAME: shuffle_points 83 | SHUFFLE_ENABLED: { 84 | 'train': True, 85 | 'test': True 86 | } 87 | 88 | - NAME: transform_points_to_voxels 89 | VOXEL_SIZE: [0.1, 0.1, 0.15] 90 | MAX_POINTS_PER_VOXEL: 5 91 | MAX_NUMBER_OF_VOXELS: { 92 | 'train': 150000, 93 | 'test': 150000 94 | } 95 | -------------------------------------------------------------------------------- /pcdet/datasets/__init__.py: -------------------------------------------------------------------------------- 1 | import torch 2 | from torch.utils.data import DataLoader 3 | from torch.utils.data import DistributedSampler as _DistributedSampler 4 | 5 | from pcdet.utils import common_utils 6 | 7 | from .dataset import DatasetTemplate 8 | from .kitti.kitti_dataset import KittiDataset 9 | from .nuscenes.nuscenes_dataset import NuScenesDataset 10 | from .waymo.waymo_dataset import WaymoDataset 11 | from .kitti360.kitti360_dataset import Kitti360Dataset 12 | from .once.once_dataset import ONCEDataset 13 | 14 | __all__ = { 15 | 'DatasetTemplate': DatasetTemplate, 16 | 'KittiDataset': KittiDataset, 17 | 'NuScenesDataset': NuScenesDataset, 18 | 'WaymoDataset': WaymoDataset, 19 | 'Kitti360Dataset': Kitti360Dataset, 20 | 'ONCEDataset': ONCEDataset 21 | } 22 | 23 | 24 | class DistributedSampler(_DistributedSampler): 25 | 26 | def __init__(self, dataset, num_replicas=None, rank=None, shuffle=True): 27 | super().__init__(dataset, num_replicas=num_replicas, rank=rank) 28 | self.shuffle = shuffle 29 | 30 | def __iter__(self): 31 | if self.shuffle: 32 | g = torch.Generator() 33 | g.manual_seed(self.epoch) 34 | indices = torch.randperm(len(self.dataset), generator=g).tolist() 35 | else: 36 | indices = torch.arange(len(self.dataset)).tolist() 37 | 38 | indices += indices[:(self.total_size - len(indices))] 39 | assert len(indices) == self.total_size 40 | 41 | indices = indices[self.rank:self.total_size:self.num_replicas] 42 | assert len(indices) == self.num_samples 43 | 44 | return iter(indices) 45 | 46 | 47 | def build_dataloader(dataset_cfg, class_names, batch_size, dist, root_path=None, workers=4, 48 | logger=None, training=True, merge_all_iters_to_one_epoch=False, total_epochs=0, drop_last=False): 49 | 50 | dataset = __all__[dataset_cfg.DATASET]( 51 | dataset_cfg=dataset_cfg, 52 | class_names=class_names, 53 | root_path=root_path, 54 | training=training, 55 | logger=logger, 56 | ) 57 | 58 | if merge_all_iters_to_one_epoch: 59 | assert hasattr(dataset, 'merge_all_iters_to_one_epoch') 60 | dataset.merge_all_iters_to_one_epoch(merge=True, epochs=total_epochs) 61 | 62 | if dist: 63 | if training: 64 | sampler = torch.utils.data.distributed.DistributedSampler(dataset) 65 | else: 66 | rank, world_size = common_utils.get_dist_info() 67 | sampler = DistributedSampler(dataset, world_size, rank, shuffle=False) 68 | else: 69 | sampler = None 70 | dataloader = DataLoader( 71 | dataset, batch_size=batch_size, pin_memory=True, num_workers=workers, 72 | shuffle=(sampler is None) and training, collate_fn=dataset.collate_batch, 73 | drop_last=drop_last, sampler=sampler, timeout=0 74 | ) 75 | 76 | return dataset, dataloader, sampler 77 | -------------------------------------------------------------------------------- /pcdet/models/fusion_layers/point_sample.py: -------------------------------------------------------------------------------- 1 | import torch 2 | import torch.nn as nn 3 | from ...utils import transform_utils 4 | 5 | 6 | def img_grid_sample(pts_2d, img_shape, img_feats): 7 | """ 8 | Args: 9 | pts_2d: (N, 2) 10 | img_shape: [H, W] 11 | img_feats: (1, C, H', W') 12 | """ 13 | coor_x, coor_y = torch.split(pts_2d, 1, dim=1) 14 | h, w = img_shape 15 | coor_y = coor_y / (h - 1) * 2 - 1 16 | coor_x = coor_x / (w - 1) * 2 - 1 17 | grid = torch.cat([coor_x, coor_y], dim=1).unsqueeze(0).unsqueeze(0) 18 | 19 | point_features = torch.nn.functional.grid_sample( 20 | img_feats, 21 | grid, 22 | align_corners=True) 23 | point_features = point_features.squeeze().t() 24 | return point_features 25 | 26 | 27 | class PointSample(nn.Module): 28 | def __init__(self): 29 | super().__init__() 30 | 31 | def forward(self, batch_dict): 32 | """ 33 | Args: 34 | img_feats: (B, C, H/4, W/4) 35 | sampled_points: (N1+N2+..., 4+C), (B, N, 3+C) 36 | Return: 37 | point_feats: (N1+N2+..., C), (B, N, C) 38 | """ 39 | batch_size = batch_dict['batch_size'] 40 | img_feats = batch_dict['image_features'] 41 | raw_points = batch_dict['sampled_points'].clone() 42 | point_feats = [] 43 | for index in range(batch_size): 44 | if len(raw_points.shape) == 2: 45 | cur_points = raw_points[raw_points[:, 0] == index][:, 1:4] 46 | else: 47 | cur_points = raw_points[index][:, :3] 48 | proj_mat = batch_dict['trans_cam_to_img'][index] @ batch_dict['trans_lidar_to_cam'][index] 49 | 50 | if batch_dict.get('transformation_3d_list', None) is not None: 51 | cur_3d_trans_list = batch_dict['transformation_3d_list'][index] 52 | cur_3d_trans_params = batch_dict['transformation_3d_params'][index] 53 | for key in cur_3d_trans_list[::-1]: 54 | cur_points, _ = getattr(transform_utils, key)(cur_3d_trans_params[key], reverse=True, points_3d=cur_points) 55 | 56 | cur_points_2d = transform_utils.points_lidar2img(cur_points, proj_mat) 57 | 58 | cur_2d_trans_list = batch_dict['transformation_2d_list'][index] 59 | cur_2d_trans_params = batch_dict['transformation_2d_params'][index] 60 | for key in cur_2d_trans_list: 61 | cur_points_2d, _ = getattr(transform_utils, key)(cur_2d_trans_params[key], reverse=False, points_2d=cur_points_2d) 62 | 63 | img_shape = batch_dict['image'].shape[2:] 64 | cur_img_feats = img_feats[index: index + 1] 65 | cur_point_feats = img_grid_sample(cur_points_2d, img_shape, cur_img_feats) 66 | 67 | point_feats.append(cur_point_feats) 68 | if len(raw_points.shape) == 2: 69 | point_feats = torch.cat(point_feats, dim=0) # (N1+N2+..., C) 70 | else: 71 | point_feats = torch.stack(point_feats, dim=0) # (B, N, C) 72 | return point_feats 73 | -------------------------------------------------------------------------------- /pcdet/config.py: -------------------------------------------------------------------------------- 1 | from pathlib import Path 2 | 3 | import yaml 4 | from easydict import EasyDict 5 | 6 | 7 | def log_config_to_file(cfg, pre='cfg', logger=None): 8 | for key, val in cfg.items(): 9 | if isinstance(cfg[key], EasyDict): 10 | logger.info('\n%s.%s = edict()' % (pre, key)) 11 | log_config_to_file(cfg[key], pre=pre + '.' + key, logger=logger) 12 | continue 13 | logger.info('%s.%s: %s' % (pre, key, val)) 14 | 15 | 16 | def cfg_from_list(cfg_list, config): 17 | """Set config keys via list (e.g., from command line).""" 18 | from ast import literal_eval 19 | assert len(cfg_list) % 2 == 0 20 | for k, v in zip(cfg_list[0::2], cfg_list[1::2]): 21 | key_list = k.split('.') 22 | d = config 23 | for subkey in key_list[:-1]: 24 | assert subkey in d, 'NotFoundKey: %s' % subkey 25 | d = d[subkey] 26 | subkey = key_list[-1] 27 | assert subkey in d, 'NotFoundKey: %s' % subkey 28 | try: 29 | value = literal_eval(v) 30 | except: 31 | value = v 32 | 33 | if type(value) != type(d[subkey]) and isinstance(d[subkey], EasyDict): 34 | key_val_list = value.split(',') 35 | for src in key_val_list: 36 | cur_key, cur_val = src.split(':') 37 | val_type = type(d[subkey][cur_key]) 38 | cur_val = val_type(cur_val) 39 | d[subkey][cur_key] = cur_val 40 | elif type(value) != type(d[subkey]) and isinstance(d[subkey], list): 41 | val_list = value.split(',') 42 | for k, x in enumerate(val_list): 43 | val_list[k] = type(d[subkey][0])(x) 44 | d[subkey] = val_list 45 | else: 46 | assert type(value) == type(d[subkey]), \ 47 | 'type {} does not match original type {}'.format(type(value), type(d[subkey])) 48 | d[subkey] = value 49 | 50 | 51 | def merge_new_config(config, new_config): 52 | if '_BASE_CONFIG_' in new_config: 53 | with open(new_config['_BASE_CONFIG_'], 'r') as f: 54 | try: 55 | yaml_config = yaml.safe_load(f, Loader=yaml.FullLoader) 56 | except: 57 | yaml_config = yaml.safe_load(f) 58 | config.update(EasyDict(yaml_config)) 59 | 60 | for key, val in new_config.items(): 61 | if not isinstance(val, dict): 62 | config[key] = val 63 | continue 64 | if key not in config: 65 | config[key] = EasyDict() 66 | merge_new_config(config[key], val) 67 | 68 | return config 69 | 70 | 71 | def cfg_from_yaml_file(cfg_file, config): 72 | with open(cfg_file, 'r') as f: 73 | try: 74 | new_config = yaml.safe_load(f, Loader=yaml.FullLoader) 75 | except: 76 | new_config = yaml.safe_load(f) 77 | 78 | merge_new_config(config=config, new_config=new_config) 79 | 80 | return config 81 | 82 | 83 | cfg = EasyDict() 84 | cfg.ROOT_DIR = (Path(__file__).resolve().parent / '../').resolve() 85 | cfg.LOCAL_RANK = 0 86 | -------------------------------------------------------------------------------- /tools/cfgs/kitti_models/second_mini.yaml: -------------------------------------------------------------------------------- 1 | CLASS_NAMES: ['Car'] 2 | 3 | DATA_CONFIG: 4 | _BASE_CONFIG_: cfgs/dataset_configs/kitti_dataset.yaml 5 | DATA_PROCESSOR: 6 | - NAME: mask_points_and_boxes_outside_range 7 | REMOVE_OUTSIDE_BOXES: True 8 | 9 | - NAME: shuffle_points 10 | SHUFFLE_ENABLED: { 11 | 'train': True, 12 | 'test': False 13 | } 14 | 15 | - NAME: calculate_grid_size 16 | VOXEL_SIZE: [0.05, 0.05, 0.1] 17 | 18 | MODEL: 19 | NAME: SECONDNet 20 | 21 | VFE: 22 | NAME: DynVFE 23 | TYPE: mean 24 | 25 | BACKBONE_3D: 26 | NAME: VoxelBackBone8x 27 | 28 | MAP_TO_BEV: 29 | NAME: HeightCompression 30 | NUM_BEV_FEATURES: 256 31 | 32 | BACKBONE_2D: 33 | NAME: BaseBEVBackbone 34 | 35 | LAYER_NUMS: [4, 4] 36 | LAYER_STRIDES: [1, 2] 37 | NUM_FILTERS: [64, 128] 38 | UPSAMPLE_STRIDES: [1, 2] 39 | NUM_UPSAMPLE_FILTERS: [128, 128] 40 | 41 | DENSE_HEAD: 42 | NAME: AnchorHeadSingle 43 | CLASS_AGNOSTIC: False 44 | 45 | USE_DIRECTION_CLASSIFIER: True 46 | DIR_OFFSET: 0.78539 47 | DIR_LIMIT_OFFSET: 0.0 48 | NUM_DIR_BINS: 2 49 | 50 | ANCHOR_GENERATOR_CONFIG: [ 51 | { 52 | 'class_name': 'Car', 53 | 'anchor_sizes': [[3.9, 1.6, 1.56]], 54 | 'anchor_rotations': [0, 1.57], 55 | 'anchor_bottom_heights': [-1.78], 56 | 'align_center': False, 57 | 'feature_map_stride': 8, 58 | 'matched_threshold': 0.6, 59 | 'unmatched_threshold': 0.45 60 | } 61 | ] 62 | 63 | TARGET_ASSIGNER_CONFIG: 64 | NAME: AxisAlignedTargetAssigner 65 | POS_FRACTION: -1.0 66 | SAMPLE_SIZE: 512 67 | NORM_BY_NUM_EXAMPLES: False 68 | MATCH_HEIGHT: False 69 | BOX_CODER: ResidualCoder 70 | 71 | LOSS_CONFIG: 72 | LOSS_WEIGHTS: { 73 | 'cls_weight': 1.0, 74 | 'loc_weight': 2.0, 75 | 'dir_weight': 0.2, 76 | 'code_weights': [1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0] 77 | } 78 | 79 | POST_PROCESSING: 80 | RECALL_THRESH_LIST: [0.3, 0.5, 0.7] 81 | SCORE_THRESH: 0.3 82 | OUTPUT_RAW_SCORE: False 83 | 84 | EVAL_METRIC: kitti 85 | 86 | NMS_CONFIG: 87 | MULTI_CLASSES_NMS: False 88 | NMS_TYPE: nms_gpu 89 | NMS_THRESH: 0.01 90 | NMS_PRE_MAXSIZE: 4096 91 | NMS_POST_MAXSIZE: 500 92 | 93 | 94 | OPTIMIZATION: 95 | BATCH_SIZE_PER_GPU: 4 96 | NUM_EPOCHS: 80 97 | 98 | OPTIMIZER: adam_onecycle 99 | LR: 0.003 100 | WEIGHT_DECAY: 0.01 101 | MOMENTUM: 0.9 102 | 103 | MOMS: [0.95, 0.85] 104 | PCT_START: 0.4 105 | DIV_FACTOR: 10 106 | DECAY_STEP_LIST: [35, 45] 107 | LR_DECAY: 0.1 108 | LR_CLIP: 0.0000001 109 | 110 | LR_WARMUP: False 111 | WARMUP_EPOCH: 1 112 | 113 | GRAD_NORM_CLIP: 10 114 | -------------------------------------------------------------------------------- /pcdet/models/dense_heads/anchor_head_single.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | import torch.nn as nn 3 | 4 | from .anchor_head_template import AnchorHeadTemplate 5 | 6 | 7 | class AnchorHeadSingle(AnchorHeadTemplate): 8 | def __init__(self, model_cfg, input_channels, num_class, class_names, grid_size, point_cloud_range, 9 | predict_boxes_when_training=True, **kwargs): 10 | super().__init__( 11 | model_cfg=model_cfg, num_class=num_class, class_names=class_names, grid_size=grid_size, point_cloud_range=point_cloud_range, 12 | predict_boxes_when_training=predict_boxes_when_training 13 | ) 14 | 15 | self.num_anchors_per_location = sum(self.num_anchors_per_location) 16 | 17 | self.conv_cls = nn.Conv2d( 18 | input_channels, self.num_anchors_per_location * self.num_class, 19 | kernel_size=1 20 | ) 21 | self.conv_box = nn.Conv2d( 22 | input_channels, self.num_anchors_per_location * self.box_coder.code_size, 23 | kernel_size=1 24 | ) 25 | 26 | if self.model_cfg.get('USE_DIRECTION_CLASSIFIER', None) is not None: 27 | self.conv_dir_cls = nn.Conv2d( 28 | input_channels, 29 | self.num_anchors_per_location * self.model_cfg.NUM_DIR_BINS, 30 | kernel_size=1 31 | ) 32 | else: 33 | self.conv_dir_cls = None 34 | self.init_weights() 35 | 36 | def init_weights(self): 37 | pi = 0.01 38 | nn.init.constant_(self.conv_cls.bias, -np.log((1 - pi) / pi)) 39 | nn.init.normal_(self.conv_box.weight, mean=0, std=0.001) 40 | 41 | def forward(self, data_dict): 42 | spatial_features_2d = data_dict['spatial_features_2d'] 43 | 44 | cls_preds = self.conv_cls(spatial_features_2d) 45 | box_preds = self.conv_box(spatial_features_2d) 46 | 47 | cls_preds = cls_preds.permute(0, 2, 3, 1).contiguous() # [N, H, W, C] 48 | box_preds = box_preds.permute(0, 2, 3, 1).contiguous() # [N, H, W, C] 49 | 50 | self.forward_ret_dict['cls_preds'] = cls_preds 51 | self.forward_ret_dict['box_preds'] = box_preds 52 | 53 | if self.conv_dir_cls is not None: 54 | dir_cls_preds = self.conv_dir_cls(spatial_features_2d) 55 | dir_cls_preds = dir_cls_preds.permute(0, 2, 3, 1).contiguous() 56 | self.forward_ret_dict['dir_cls_preds'] = dir_cls_preds 57 | else: 58 | dir_cls_preds = None 59 | 60 | if self.training: 61 | targets_dict = self.assign_targets( 62 | gt_boxes=data_dict['gt_boxes'] 63 | ) 64 | self.forward_ret_dict.update(targets_dict) 65 | 66 | if not self.training or self.predict_boxes_when_training: 67 | batch_cls_preds, batch_box_preds = self.generate_predicted_boxes( 68 | batch_size=data_dict['batch_size'], 69 | cls_preds=cls_preds, box_preds=box_preds, dir_cls_preds=dir_cls_preds 70 | ) 71 | data_dict['batch_cls_preds'] = batch_cls_preds 72 | data_dict['batch_box_preds'] = batch_box_preds 73 | data_dict['cls_preds_normalized'] = False 74 | 75 | return data_dict 76 | -------------------------------------------------------------------------------- /tools/cfgs/waymo_models/centerpoint.yaml: -------------------------------------------------------------------------------- 1 | CLASS_NAMES: ['Vehicle', 'Pedestrian', 'Cyclist'] 2 | 3 | DATA_CONFIG: 4 | _BASE_CONFIG_: cfgs/dataset_configs/waymo_dataset.yaml 5 | 6 | POINT_CLOUD_RANGE: [-75.2, -75.2, -2, 75.2, 75.2, 4] 7 | 8 | SAMPLED_INTERVAL: { 9 | 'train': 1, 10 | 'test': 1 11 | } 12 | 13 | DATA_PROCESSOR: 14 | - NAME: shuffle_points 15 | SHUFFLE_ENABLED: { 16 | 'train': True, 17 | 'test': False 18 | } 19 | 20 | - NAME: calculate_grid_size 21 | VOXEL_SIZE: [0.1, 0.1, 0.15] 22 | 23 | MODEL: 24 | NAME: CenterPoint 25 | 26 | VFE: 27 | NAME: DynVFE 28 | TYPE: mean 29 | 30 | BACKBONE_3D: 31 | NAME: VoxelResBackBone8x 32 | 33 | MAP_TO_BEV: 34 | NAME: HeightCompression 35 | NUM_BEV_FEATURES: 256 36 | 37 | BACKBONE_2D: 38 | NAME: BaseBEVBackbone 39 | 40 | LAYER_NUMS: [5, 5] 41 | LAYER_STRIDES: [1, 2] 42 | NUM_FILTERS: [128, 256] 43 | UPSAMPLE_STRIDES: [1, 2] 44 | NUM_UPSAMPLE_FILTERS: [256, 256] 45 | 46 | DENSE_HEAD: 47 | NAME: CenterHead 48 | CLASS_AGNOSTIC: False 49 | 50 | CLASS_NAMES_EACH_HEAD: [ 51 | ['Vehicle', 'Pedestrian', 'Cyclist'] 52 | ] 53 | 54 | SHARED_CONV_CHANNEL: 64 55 | USE_BIAS_BEFORE_NORM: True 56 | NUM_HM_CONV: 2 57 | SEPARATE_HEAD_CFG: 58 | HEAD_ORDER: ['center', 'center_z', 'dim', 'rot'] 59 | HEAD_DICT: { 60 | 'center': {'out_channels': 2, 'num_conv': 2}, 61 | 'center_z': {'out_channels': 1, 'num_conv': 2}, 62 | 'dim': {'out_channels': 3, 'num_conv': 2}, 63 | 'rot': {'out_channels': 2, 'num_conv': 2}, 64 | } 65 | 66 | TARGET_ASSIGNER_CONFIG: 67 | FEATURE_MAP_STRIDE: 8 68 | NUM_MAX_OBJS: 500 69 | GAUSSIAN_OVERLAP: 0.1 70 | MIN_RADIUS: 2 71 | 72 | LOSS_CONFIG: 73 | LOSS_WEIGHTS: { 74 | 'cls_weight': 1.0, 75 | 'loc_weight': 2.0, 76 | 'code_weights': [1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0] 77 | } 78 | 79 | POST_PROCESSING: 80 | SCORE_THRESH: 0.1 81 | POST_CENTER_LIMIT_RANGE: [-75.2, -75.2, -2, 75.2, 75.2, 4] 82 | MAX_OBJ_PER_SAMPLE: 500 83 | NMS_CONFIG: 84 | NMS_TYPE: nms_gpu 85 | NMS_THRESH: 0.7 86 | NMS_PRE_MAXSIZE: 4096 87 | NMS_POST_MAXSIZE: 500 88 | 89 | POST_PROCESSING: 90 | RECALL_THRESH_LIST: [0.3, 0.5, 0.7] 91 | 92 | EVAL_METRIC: waymo_custom 93 | 94 | 95 | OPTIMIZATION: 96 | BATCH_SIZE_PER_GPU: 4 97 | NUM_EPOCHS: 30 98 | 99 | OPTIMIZER: adam_onecycle 100 | LR: 0.003 101 | WEIGHT_DECAY: 0.01 102 | MOMENTUM: 0.9 103 | SYNC_BN: True 104 | 105 | MOMS: [0.95, 0.85] 106 | PCT_START: 0.4 107 | DIV_FACTOR: 10 108 | DECAY_STEP_LIST: [35, 45] 109 | LR_DECAY: 0.1 110 | LR_CLIP: 0.0000001 111 | 112 | LR_WARMUP: False 113 | WARMUP_EPOCH: 1 114 | 115 | GRAD_NORM_CLIP: 35 116 | -------------------------------------------------------------------------------- /pcdet/models/model_utils/fusion_utils.py: -------------------------------------------------------------------------------- 1 | import copy 2 | import torch 3 | from torch import nn 4 | from ..img_backbones.dla import DeformConv 5 | from ...utils.spconv_utils import spconv 6 | 7 | 8 | def fuse_conv_bn_eval(conv, bn, transpose=False): 9 | # assert(not (conv.training or bn.training)), "Fusion only for eval!" 10 | fused_conv = copy.deepcopy(conv) 11 | 12 | fused_conv.weight, fused_conv.bias = \ 13 | fuse_conv_bn_weights(fused_conv.weight, fused_conv.bias, 14 | bn.running_mean, bn.running_var, bn.eps, bn.weight, bn.bias, transpose) 15 | 16 | return fused_conv 17 | 18 | 19 | def fuse_conv_bn_weights(conv_w, conv_b, bn_rm, bn_rv, bn_eps, bn_w, bn_b, transpose=False): 20 | if conv_b is None: 21 | conv_b = torch.zeros_like(bn_rm) 22 | if bn_w is None: 23 | bn_w = torch.ones_like(bn_rm) 24 | if bn_b is None: 25 | bn_b = torch.zeros_like(bn_rm) 26 | bn_var_rsqrt = torch.rsqrt(bn_rv + bn_eps) 27 | 28 | if transpose: 29 | shape = [1, -1] + [1] * (len(conv_w.shape) - 2) 30 | else: 31 | shape = [-1, 1] + [1] * (len(conv_w.shape) - 2) 32 | 33 | conv_w = conv_w * (bn_w * bn_var_rsqrt).reshape(shape) 34 | conv_b = (conv_b - bn_rm) * bn_var_rsqrt * bn_w + bn_b 35 | 36 | return torch.nn.Parameter(conv_w), torch.nn.Parameter(conv_b) 37 | 38 | 39 | def fuse_linear_bn_eval(linear, bn): 40 | # assert(not (linear.training or bn.training)), "Fusion only for eval!" 41 | fused_linear = copy.deepcopy(linear) 42 | 43 | fused_linear.weight, fused_linear.bias = fuse_linear_bn_weights( 44 | fused_linear.weight, fused_linear.bias, 45 | bn.running_mean, bn.running_var, bn.eps, bn.weight, bn.bias) 46 | 47 | return fused_linear 48 | 49 | 50 | def fuse_linear_bn_weights(linear_w, linear_b, bn_rm, bn_rv, bn_eps, bn_w, bn_b): 51 | if linear_b is None: 52 | linear_b = torch.zeros_like(bn_rm) 53 | bn_scale = bn_w * torch.rsqrt(bn_rv + bn_eps) 54 | 55 | fused_w = linear_w * bn_scale.unsqueeze(-1) 56 | fused_b = (linear_b - bn_rm) * bn_scale + bn_b 57 | 58 | return torch.nn.Parameter(fused_w), torch.nn.Parameter(fused_b) 59 | 60 | 61 | def fuse_module(m): 62 | last_conv = None 63 | last_conv_name = None 64 | 65 | for name, child in m.named_children(): 66 | if isinstance(child, (nn.BatchNorm2d, nn.SyncBatchNorm, nn.BatchNorm1d)): 67 | if last_conv is None: # only fuse BN that is after Conv 68 | continue 69 | if isinstance(last_conv, (nn.Conv2d, nn.Conv1d)): 70 | fused_conv = fuse_conv_bn_eval(last_conv, child) 71 | elif isinstance(last_conv, nn.ConvTranspose2d): 72 | fused_conv = fuse_conv_bn_eval(last_conv, child, True) 73 | else: 74 | fused_conv = fuse_linear_bn_eval(last_conv, child) 75 | m._modules[last_conv_name] = fused_conv 76 | # To reduce changes, set BN as Identity instead of deleting it. 77 | m._modules[name] = nn.Identity() 78 | last_conv = None 79 | elif isinstance(child, (nn.Conv2d, nn.Conv1d, nn.ConvTranspose2d, nn.Linear)): 80 | last_conv = child 81 | last_conv_name = name 82 | elif isinstance(child, (DeformConv, spconv.SparseSequential)): 83 | continue 84 | else: 85 | fuse_module(child) 86 | return m 87 | -------------------------------------------------------------------------------- /pcdet/ops/pointnet2/pointnet2_stack/src/ball_query_gpu.cu: -------------------------------------------------------------------------------- 1 | /* 2 | Stacked-batch-data version of ball query, modified from the original implementation of official PointNet++ codes. 3 | Written by Shaoshuai Shi 4 | All Rights Reserved 2019-2020. 5 | */ 6 | 7 | 8 | #include 9 | #include 10 | #include 11 | 12 | #include "ball_query_gpu.h" 13 | #include "cuda_utils.h" 14 | 15 | 16 | __global__ void ball_query_kernel_stack(int B, int M, float radius, int nsample, \ 17 | const float *new_xyz, const int *new_xyz_batch_cnt, const float *xyz, const int *xyz_batch_cnt, int *idx) { 18 | // :param xyz: (N1 + N2 ..., 3) xyz coordinates of the features 19 | // :param xyz_batch_cnt: (batch_size), [N1, N2, ...] 20 | // :param new_xyz: (M1 + M2 ..., 3) centers of the ball query 21 | // :param new_xyz_batch_cnt: (batch_size), [M1, M2, ...] 22 | // output: 23 | // idx: (M, nsample) 24 | int pt_idx = blockIdx.x * blockDim.x + threadIdx.x; 25 | if (pt_idx >= M) return; 26 | 27 | int bs_idx = 0, pt_cnt = new_xyz_batch_cnt[0]; 28 | for (int k = 1; k < B; k++){ 29 | if (pt_idx < pt_cnt) break; 30 | pt_cnt += new_xyz_batch_cnt[k]; 31 | bs_idx = k; 32 | } 33 | 34 | int xyz_batch_start_idx = 0; 35 | for (int k = 0; k < bs_idx; k++) xyz_batch_start_idx += xyz_batch_cnt[k]; 36 | // for (int k = 0; k < bs_idx; k++) new_xyz_batch_start_idx += new_xyz_batch_cnt[k]; 37 | 38 | new_xyz += pt_idx * 3; 39 | xyz += xyz_batch_start_idx * 3; 40 | idx += pt_idx * nsample; 41 | 42 | float radius2 = radius * radius; 43 | float new_x = new_xyz[0]; 44 | float new_y = new_xyz[1]; 45 | float new_z = new_xyz[2]; 46 | int n = xyz_batch_cnt[bs_idx]; 47 | 48 | int cnt = 0; 49 | for (int k = 0; k < n; ++k) { 50 | float x = xyz[k * 3 + 0]; 51 | float y = xyz[k * 3 + 1]; 52 | float z = xyz[k * 3 + 2]; 53 | float d2 = (new_x - x) * (new_x - x) + (new_y - y) * (new_y - y) + (new_z - z) * (new_z - z); 54 | if (d2 < radius2){ 55 | if (cnt == 0){ 56 | for (int l = 0; l < nsample; ++l) { 57 | idx[l] = k; 58 | } 59 | } 60 | idx[cnt] = k; 61 | ++cnt; 62 | if (cnt >= nsample) break; 63 | } 64 | } 65 | if (cnt == 0) idx[0] = -1; 66 | } 67 | 68 | 69 | void ball_query_kernel_launcher_stack(int B, int M, float radius, int nsample, 70 | const float *new_xyz, const int *new_xyz_batch_cnt, const float *xyz, const int *xyz_batch_cnt, int *idx){ 71 | // :param xyz: (N1 + N2 ..., 3) xyz coordinates of the features 72 | // :param xyz_batch_cnt: (batch_size), [N1, N2, ...] 73 | // :param new_xyz: (M1 + M2 ..., 3) centers of the ball query 74 | // :param new_xyz_batch_cnt: (batch_size), [M1, M2, ...] 75 | // output: 76 | // idx: (M, nsample) 77 | 78 | cudaError_t err; 79 | 80 | dim3 blocks(DIVUP(M, THREADS_PER_BLOCK)); // blockIdx.x(col), blockIdx.y(row) 81 | dim3 threads(THREADS_PER_BLOCK); 82 | 83 | ball_query_kernel_stack<<>>(B, M, radius, nsample, new_xyz, new_xyz_batch_cnt, xyz, xyz_batch_cnt, idx); 84 | // cudaDeviceSynchronize(); // for using printf in kernel function 85 | err = cudaGetLastError(); 86 | if (cudaSuccess != err) { 87 | fprintf(stderr, "CUDA kernel failed : %s\n", cudaGetErrorString(err)); 88 | exit(-1); 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /pcdet/utils/spconv_utils.py: -------------------------------------------------------------------------------- 1 | from typing import Set 2 | 3 | try: 4 | import spconv.pytorch as spconv 5 | except: 6 | import spconv as spconv 7 | 8 | import torch.nn as nn 9 | 10 | 11 | def find_all_spconv_keys(model: nn.Module, prefix="") -> Set[str]: 12 | """ 13 | Finds all spconv keys that need to have weight's transposed 14 | """ 15 | found_keys: Set[str] = set() 16 | for name, child in model.named_children(): 17 | new_prefix = f"{prefix}.{name}" if prefix != "" else name 18 | 19 | if isinstance(child, spconv.conv.SparseConvolution): 20 | new_prefix = f"{new_prefix}.weight" 21 | found_keys.add(new_prefix) 22 | 23 | found_keys.update(find_all_spconv_keys(child, prefix=new_prefix)) 24 | 25 | return found_keys 26 | 27 | 28 | def replace_feature(out, new_features): 29 | if "replace_feature" in out.__dir__(): 30 | # spconv 2.x behaviour 31 | return out.replace_feature(new_features) 32 | else: 33 | out.features = new_features 34 | return out 35 | 36 | 37 | def post_act_block(in_channels, out_channels, kernel_size, indice_key=None, stride=1, padding=0, 38 | conv_type='subm', norm_fn=None, dim=3): 39 | 40 | if conv_type == 'subm': 41 | conv = getattr(spconv, f'SubMConv{dim}d')(in_channels, out_channels, kernel_size, bias=False, indice_key=indice_key) 42 | elif conv_type == 'spconv': 43 | conv = getattr(spconv, f'SparseConv{dim}d')(in_channels, out_channels, kernel_size, stride=stride, padding=padding, 44 | bias=False, indice_key=indice_key) 45 | elif conv_type == 'inverseconv': 46 | conv = getattr(spconv, f'SparseInverseConv{dim}d')(in_channels, out_channels, kernel_size, indice_key=indice_key, bias=False) 47 | else: 48 | raise NotImplementedError 49 | 50 | m = spconv.SparseSequential( 51 | conv, 52 | norm_fn(out_channels), 53 | nn.ReLU(), 54 | ) 55 | 56 | return m 57 | 58 | 59 | class SparseBasicBlock(spconv.SparseModule): 60 | expansion = 1 61 | 62 | def __init__(self, inplanes, planes, stride=1, norm_fn=None, downsample=None, indice_key=None, dim=3): 63 | super(SparseBasicBlock, self).__init__() 64 | 65 | assert norm_fn is not None 66 | bias = norm_fn is not None 67 | self.conv1 = getattr(spconv, f'SubMConv{dim}d')( 68 | inplanes, planes, kernel_size=3, stride=stride, padding=1, bias=bias, indice_key=indice_key 69 | ) 70 | self.bn1 = norm_fn(planes) 71 | self.relu = nn.ReLU() 72 | self.conv2 = getattr(spconv, f'SubMConv{dim}d')( 73 | planes, planes, kernel_size=3, stride=stride, padding=1, bias=bias, indice_key=indice_key 74 | ) 75 | self.bn2 = norm_fn(planes) 76 | self.downsample = downsample 77 | self.stride = stride 78 | 79 | def forward(self, x): 80 | identity = x 81 | 82 | out = self.conv1(x) 83 | out = replace_feature(out, self.bn1(out.features)) 84 | out = replace_feature(out, self.relu(out.features)) 85 | 86 | out = self.conv2(out) 87 | out = replace_feature(out, self.bn2(out.features)) 88 | 89 | if self.downsample is not None: 90 | identity = self.downsample(x) 91 | 92 | out = replace_feature(out, out.features + identity.features) 93 | out = replace_feature(out, self.relu(out.features)) 94 | 95 | return out -------------------------------------------------------------------------------- /tools/cfgs/once_models/centerpoint_pillar.yaml: -------------------------------------------------------------------------------- 1 | CLASS_NAMES: ['Car', 'Bus', 'Truck', 'Pedestrian', 'Cyclist'] 2 | 3 | DATA_CONFIG: 4 | _BASE_CONFIG_: cfgs/dataset_configs/once_dataset.yaml 5 | 6 | POINT_CLOUD_RANGE: [-74.88, -74.88, -5.0, 74.88, 74.88, 3.0] 7 | 8 | DATA_PROCESSOR: 9 | - NAME: mask_points_and_boxes_outside_range 10 | REMOVE_OUTSIDE_BOXES: True 11 | 12 | - NAME: shuffle_points 13 | SHUFFLE_ENABLED: { 14 | 'train': True, 15 | 'test': False 16 | } 17 | 18 | - NAME: calculate_grid_size 19 | VOXEL_SIZE: [0.32, 0.32, 8.0] 20 | 21 | 22 | MODEL: 23 | NAME: CenterPoint 24 | 25 | VFE: 26 | NAME: DynVFE 27 | TYPE: mean 28 | WITH_DISTANCE: False 29 | USE_ABSLOTE_XYZ: True 30 | USE_CLUSTER_XYZ: True 31 | MLPS: [[64, 64]] 32 | 33 | MAP_TO_BEV: 34 | NAME: PointPillarScatter 35 | NUM_BEV_FEATURES: 64 36 | 37 | BACKBONE_2D: 38 | NAME: BaseBEVBackbone 39 | LAYER_NUMS: [3, 5, 5] 40 | LAYER_STRIDES: [1, 2, 2] 41 | NUM_FILTERS: [64, 128, 256] 42 | UPSAMPLE_STRIDES: [1, 2, 4] 43 | NUM_UPSAMPLE_FILTERS: [128, 128, 128] 44 | 45 | DENSE_HEAD: 46 | NAME: CenterHead 47 | CLASS_AGNOSTIC: False 48 | 49 | CLASS_NAMES_EACH_HEAD: [ 50 | ['Car', 'Bus', 'Truck', 'Pedestrian', 'Cyclist'] 51 | ] 52 | 53 | SHARED_CONV_CHANNEL: 64 54 | USE_BIAS_BEFORE_NORM: True 55 | NUM_HM_CONV: 2 56 | SEPARATE_HEAD_CFG: 57 | HEAD_ORDER: ['center', 'center_z', 'dim', 'rot'] 58 | HEAD_DICT: { 59 | 'center': {'out_channels': 2, 'num_conv': 2}, 60 | 'center_z': {'out_channels': 1, 'num_conv': 2}, 61 | 'dim': {'out_channels': 3, 'num_conv': 2}, 62 | 'rot': {'out_channels': 2, 'num_conv': 2}, 63 | } 64 | 65 | TARGET_ASSIGNER_CONFIG: 66 | FEATURE_MAP_STRIDE: 1 67 | NUM_MAX_OBJS: 500 68 | GAUSSIAN_OVERLAP: 0.1 69 | MIN_RADIUS: 2 70 | 71 | LOSS_CONFIG: 72 | LOSS_WEIGHTS: { 73 | 'cls_weight': 1.0, 74 | 'loc_weight': 2.0, 75 | 'code_weights': [1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0] 76 | } 77 | 78 | POST_PROCESSING: 79 | SCORE_THRESH: 0.1 80 | POST_CENTER_LIMIT_RANGE: [-74.88, -74.88, -5.0, 74.88, 74.88, 3.0] 81 | MAX_OBJ_PER_SAMPLE: 500 82 | NMS_CONFIG: 83 | NMS_TYPE: nms_gpu 84 | NMS_THRESH: 0.7 85 | NMS_PRE_MAXSIZE: 4096 86 | NMS_POST_MAXSIZE: 500 87 | 88 | POST_PROCESSING: 89 | RECALL_THRESH_LIST: [0.3, 0.5, 0.7] 90 | 91 | OUTPUT_RAW_SCORE: False 92 | 93 | EVAL_METRIC: once 94 | 95 | 96 | OPTIMIZATION: 97 | BATCH_SIZE_PER_GPU: 8 98 | NUM_EPOCHS: 80 99 | 100 | OPTIMIZER: adam_onecycle 101 | LR: 0.003 102 | WEIGHT_DECAY: 0.01 103 | MOMENTUM: 0.9 104 | 105 | MOMS: [0.95, 0.85] 106 | PCT_START: 0.4 107 | DIV_FACTOR: 10 108 | DECAY_STEP_LIST: [35, 45] 109 | LR_DECAY: 0.1 110 | LR_CLIP: 0.0000001 111 | 112 | LR_WARMUP: False 113 | WARMUP_EPOCH: 1 114 | 115 | GRAD_NORM_CLIP: 35 116 | -------------------------------------------------------------------------------- /pcdet/ops/pointnet2/pointnet2_batch/src/group_points_gpu.cu: -------------------------------------------------------------------------------- 1 | /* 2 | batch version of point grouping, modified from the original implementation of official PointNet++ codes. 3 | Written by Shaoshuai Shi 4 | All Rights Reserved 2018. 5 | */ 6 | 7 | #include 8 | #include 9 | 10 | #include "cuda_utils.h" 11 | #include "group_points_gpu.h" 12 | 13 | 14 | __global__ void group_points_grad_kernel_fast(int b, int c, int n, int npoints, int nsample, 15 | const float *__restrict__ grad_out, const int *__restrict__ idx, float *__restrict__ grad_points) { 16 | // grad_out: (B, C, npoints, nsample) 17 | // idx: (B, npoints, nsample) 18 | // output: 19 | // grad_points: (B, C, N) 20 | int bs_idx = blockIdx.z; 21 | int c_idx = blockIdx.y; 22 | int index = blockIdx.x * blockDim.x + threadIdx.x; 23 | int pt_idx = index / nsample; 24 | if (bs_idx >= b || c_idx >= c || pt_idx >= npoints) return; 25 | 26 | int sample_idx = index % nsample; 27 | grad_out += bs_idx * c * npoints * nsample + c_idx * npoints * nsample + pt_idx * nsample + sample_idx; 28 | idx += bs_idx * npoints * nsample + pt_idx * nsample + sample_idx; 29 | 30 | atomicAdd(grad_points + bs_idx * c * n + c_idx * n + idx[0] , grad_out[0]); 31 | } 32 | 33 | void group_points_grad_kernel_launcher_fast(int b, int c, int n, int npoints, int nsample, 34 | const float *grad_out, const int *idx, float *grad_points) { 35 | // grad_out: (B, C, npoints, nsample) 36 | // idx: (B, npoints, nsample) 37 | // output: 38 | // grad_points: (B, C, N) 39 | cudaError_t err; 40 | dim3 blocks(DIVUP(npoints * nsample, THREADS_PER_BLOCK), c, b); // blockIdx.x(col), blockIdx.y(row) 41 | dim3 threads(THREADS_PER_BLOCK); 42 | 43 | group_points_grad_kernel_fast<<>>(b, c, n, npoints, nsample, grad_out, idx, grad_points); 44 | 45 | err = cudaGetLastError(); 46 | if (cudaSuccess != err) { 47 | fprintf(stderr, "CUDA kernel failed : %s\n", cudaGetErrorString(err)); 48 | exit(-1); 49 | } 50 | } 51 | 52 | 53 | __global__ void group_points_kernel_fast(int b, int c, int n, int npoints, int nsample, 54 | const float *__restrict__ points, const int *__restrict__ idx, float *__restrict__ out) { 55 | // points: (B, C, N) 56 | // idx: (B, npoints, nsample) 57 | // output: 58 | // out: (B, C, npoints, nsample) 59 | int bs_idx = blockIdx.z; 60 | int c_idx = blockIdx.y; 61 | int index = blockIdx.x * blockDim.x + threadIdx.x; 62 | int pt_idx = index / nsample; 63 | if (bs_idx >= b || c_idx >= c || pt_idx >= npoints) return; 64 | 65 | int sample_idx = index % nsample; 66 | 67 | idx += bs_idx * npoints * nsample + pt_idx * nsample + sample_idx; 68 | int in_idx = bs_idx * c * n + c_idx * n + idx[0]; 69 | int out_idx = bs_idx * c * npoints * nsample + c_idx * npoints * nsample + pt_idx * nsample + sample_idx; 70 | 71 | out[out_idx] = points[in_idx]; 72 | } 73 | 74 | 75 | void group_points_kernel_launcher_fast(int b, int c, int n, int npoints, int nsample, 76 | const float *points, const int *idx, float *out) { 77 | // points: (B, C, N) 78 | // idx: (B, npoints, nsample) 79 | // output: 80 | // out: (B, C, npoints, nsample) 81 | cudaError_t err; 82 | dim3 blocks(DIVUP(npoints * nsample, THREADS_PER_BLOCK), c, b); // blockIdx.x(col), blockIdx.y(row) 83 | dim3 threads(THREADS_PER_BLOCK); 84 | 85 | group_points_kernel_fast<<>>(b, c, n, npoints, nsample, points, idx, out); 86 | // cudaDeviceSynchronize(); // for using printf in kernel function 87 | err = cudaGetLastError(); 88 | if (cudaSuccess != err) { 89 | fprintf(stderr, "CUDA kernel failed : %s\n", cudaGetErrorString(err)); 90 | exit(-1); 91 | } 92 | } 93 | -------------------------------------------------------------------------------- /pcdet/utils/object3d_kitti.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | 3 | 4 | def get_objects_from_label(label_file, client): 5 | with client.get_local_path(label_file) as path: 6 | lines = open(path).readlines() 7 | objects = [Object3d(line) for line in lines] 8 | return objects 9 | 10 | 11 | def cls_type_to_id(cls_type): 12 | type_to_id = {'Car': 1, 'Pedestrian': 2, 'Cyclist': 3, 'Van': 4} 13 | if cls_type not in type_to_id.keys(): 14 | return -1 15 | return type_to_id[cls_type] 16 | 17 | 18 | class Object3d(object): 19 | def __init__(self, line): 20 | label = line.strip().split(' ') 21 | self.src = line 22 | self.cls_type = label[0] 23 | self.cls_id = cls_type_to_id(self.cls_type) 24 | self.truncation = float(label[1]) 25 | self.occlusion = float(label[2]) # 0:fully visible 1:partly occluded 2:largely occluded 3:unknown 26 | self.alpha = float(label[3]) 27 | self.box2d = np.array((float(label[4]), float(label[5]), float(label[6]), float(label[7])), dtype=np.float32) 28 | self.h = float(label[8]) 29 | self.w = float(label[9]) 30 | self.l = float(label[10]) 31 | self.loc = np.array((float(label[11]), float(label[12]), float(label[13])), dtype=np.float32) 32 | self.dis_to_cam = np.linalg.norm(self.loc) 33 | self.ry = float(label[14]) 34 | self.score = float(label[15]) if label.__len__() == 16 else -1.0 35 | self.level_str = None 36 | self.level = self.get_kitti_obj_level() 37 | 38 | def get_kitti_obj_level(self): 39 | height = float(self.box2d[3]) - float(self.box2d[1]) + 1 40 | 41 | if height >= 40 and self.truncation <= 0.15 and self.occlusion <= 0: 42 | self.level_str = 'Easy' 43 | return 0 # Easy 44 | elif height >= 25 and self.truncation <= 0.3 and self.occlusion <= 1: 45 | self.level_str = 'Moderate' 46 | return 1 # Moderate 47 | elif height >= 25 and self.truncation <= 0.5 and self.occlusion <= 2: 48 | self.level_str = 'Hard' 49 | return 2 # Hard 50 | else: 51 | self.level_str = 'UnKnown' 52 | return -1 53 | 54 | def generate_corners3d(self): 55 | """ 56 | generate corners3d representation for this object 57 | :return corners_3d: (8, 3) corners of box3d in camera coord 58 | """ 59 | l, h, w = self.l, self.h, self.w 60 | x_corners = [l / 2, l / 2, -l / 2, -l / 2, l / 2, l / 2, -l / 2, -l / 2] 61 | y_corners = [0, 0, 0, 0, -h, -h, -h, -h] 62 | z_corners = [w / 2, -w / 2, -w / 2, w / 2, w / 2, -w / 2, -w / 2, w / 2] 63 | 64 | R = np.array([[np.cos(self.ry), 0, np.sin(self.ry)], 65 | [0, 1, 0], 66 | [-np.sin(self.ry), 0, np.cos(self.ry)]]) 67 | corners3d = np.vstack([x_corners, y_corners, z_corners]) # (3, 8) 68 | corners3d = np.dot(R, corners3d).T 69 | corners3d = corners3d + self.loc 70 | return corners3d 71 | 72 | def to_str(self): 73 | print_str = '%s %.3f %.3f %.3f box2d: %s hwl: [%.3f %.3f %.3f] pos: %s ry: %.3f' \ 74 | % (self.cls_type, self.truncation, self.occlusion, self.alpha, self.box2d, self.h, self.w, self.l, 75 | self.loc, self.ry) 76 | return print_str 77 | 78 | def to_kitti_format(self): 79 | kitti_str = '%s %.2f %d %.2f %.2f %.2f %.2f %.2f %.2f %.2f %.2f %.2f %.2f %.2f %.2f' \ 80 | % (self.cls_type, self.truncation, int(self.occlusion), self.alpha, self.box2d[0], self.box2d[1], 81 | self.box2d[2], self.box2d[3], self.h, self.w, self.l, self.loc[0], self.loc[1], self.loc[2], 82 | self.ry) 83 | return kitti_str 84 | -------------------------------------------------------------------------------- /pcdet/models/model_utils/model_nms_utils.py: -------------------------------------------------------------------------------- 1 | import torch 2 | 3 | from ...ops.iou3d_nms import iou3d_nms_utils 4 | 5 | 6 | def class_agnostic_nms(box_scores, box_preds, nms_config, score_thresh=None): 7 | src_box_scores = box_scores 8 | if score_thresh is not None: 9 | scores_mask = (box_scores >= score_thresh) 10 | box_scores = box_scores[scores_mask] 11 | box_preds = box_preds[scores_mask] 12 | 13 | selected = [] 14 | if box_scores.shape[0] > 0: 15 | box_scores_nms, indices = torch.topk(box_scores, k=min(nms_config.NMS_PRE_MAXSIZE, box_scores.shape[0])) 16 | boxes_for_nms = box_preds[indices] 17 | keep_idx, selected_scores = getattr(iou3d_nms_utils, nms_config.NMS_TYPE)( 18 | boxes_for_nms[:, 0:7], box_scores_nms, nms_config.NMS_THRESH, **nms_config 19 | ) 20 | selected = indices[keep_idx[:nms_config.NMS_POST_MAXSIZE]] 21 | 22 | if score_thresh is not None: 23 | original_idxs = scores_mask.nonzero().view(-1) 24 | selected = original_idxs[selected] 25 | return selected, src_box_scores[selected] 26 | 27 | 28 | def multi_class_agnostic_nms(box_scores, box_ious, box_labels, box_preds, nms_config): 29 | iou_rectifier = box_scores.new_tensor(nms_config.IOU_RECTIFIER) 30 | iou_rectifier = iou_rectifier[box_labels] 31 | rect_scores = torch.pow(box_scores, 1 - iou_rectifier) * torch.pow(box_ious, iou_rectifier) 32 | selected = [] 33 | for cls in range(len(nms_config.NMS_THRESH)): 34 | class_mask = box_labels == cls 35 | if class_mask.sum() > 0: 36 | src_idx = class_mask.nonzero(as_tuple=True)[0] 37 | box_scores_nms, indices = torch.topk(rect_scores[class_mask], k=min(nms_config.NMS_PRE_MAXSIZE[cls], rect_scores[class_mask].shape[0])) 38 | boxes_for_nms = box_preds[class_mask][indices] 39 | keep_idx, _ = iou3d_nms_utils.nms_gpu( 40 | boxes_for_nms[:, 0:7], box_scores_nms, nms_config.NMS_THRESH[cls] 41 | ) 42 | per_selected = src_idx[indices[keep_idx[:nms_config.NMS_POST_MAXSIZE[cls]]]] 43 | selected.append(per_selected) 44 | if len(selected) > 0: 45 | selected = torch.cat(selected, dim=0) 46 | return selected, rect_scores[selected] 47 | 48 | 49 | def multi_classes_nms(cls_scores, box_preds, nms_config, score_thresh=None): 50 | """ 51 | Args: 52 | cls_scores: (N, num_class) 53 | box_preds: (N, 7 + C) 54 | nms_config: 55 | score_thresh: 56 | 57 | Returns: 58 | 59 | """ 60 | pred_scores, pred_labels, pred_boxes = [], [], [] 61 | for k in range(cls_scores.shape[1]): 62 | if score_thresh is not None: 63 | scores_mask = (cls_scores[:, k] >= score_thresh) 64 | box_scores = cls_scores[scores_mask, k] 65 | cur_box_preds = box_preds[scores_mask] 66 | else: 67 | box_scores = cls_scores[:, k] 68 | cur_box_preds = box_preds 69 | 70 | selected = [] 71 | if box_scores.shape[0] > 0: 72 | box_scores_nms, indices = torch.topk(box_scores, k=min(nms_config.NMS_PRE_MAXSIZE, box_scores.shape[0])) 73 | boxes_for_nms = cur_box_preds[indices] 74 | keep_idx, selected_scores = getattr(iou3d_nms_utils, nms_config.NMS_TYPE)( 75 | boxes_for_nms[:, 0:7], box_scores_nms, nms_config.NMS_THRESH, **nms_config 76 | ) 77 | selected = indices[keep_idx[:nms_config.NMS_POST_MAXSIZE]] 78 | 79 | pred_scores.append(box_scores[selected]) 80 | pred_labels.append(box_scores.new_ones(len(selected)).long() * k) 81 | pred_boxes.append(cur_box_preds[selected]) 82 | 83 | pred_scores = torch.cat(pred_scores, dim=0) 84 | pred_labels = torch.cat(pred_labels, dim=0) 85 | pred_boxes = torch.cat(pred_boxes, dim=0) 86 | 87 | return pred_scores, pred_labels, pred_boxes 88 | -------------------------------------------------------------------------------- /pcdet/models/dense_heads/point_head_simple.py: -------------------------------------------------------------------------------- 1 | import torch 2 | 3 | from ...utils import box_utils 4 | from .point_head_template import PointHeadTemplate 5 | 6 | 7 | class PointHeadSimple(PointHeadTemplate): 8 | """ 9 | A simple point-based segmentation head, which are used for PV-RCNN keypoint segmentaion. 10 | Reference Paper: https://arxiv.org/abs/1912.13192 11 | PV-RCNN: Point-Voxel Feature Set Abstraction for 3D Object Detection 12 | """ 13 | def __init__(self, num_class, input_channels, model_cfg, **kwargs): 14 | super().__init__(model_cfg=model_cfg, num_class=num_class) 15 | self.cls_layers = self.make_fc_layers( 16 | fc_cfg=self.model_cfg.CLS_FC, 17 | input_channels=input_channels, 18 | output_channels=num_class 19 | ) 20 | 21 | def assign_targets(self, input_dict): 22 | """ 23 | Args: 24 | input_dict: 25 | point_features: (N1 + N2 + N3 + ..., C) 26 | batch_size: 27 | point_coords: (N1 + N2 + N3 + ..., 4) [bs_idx, x, y, z] 28 | gt_boxes (optional): (B, M, 8) 29 | Returns: 30 | point_cls_labels: (N1 + N2 + N3 + ...), long type, 0:background, -1:ignored 31 | point_part_labels: (N1 + N2 + N3 + ..., 3) 32 | """ 33 | point_coords = input_dict['point_coords'] 34 | gt_boxes = input_dict['gt_boxes'] 35 | assert gt_boxes.shape.__len__() == 3, 'gt_boxes.shape=%s' % str(gt_boxes.shape) 36 | assert point_coords.shape.__len__() in [2], 'points.shape=%s' % str(point_coords.shape) 37 | 38 | batch_size = gt_boxes.shape[0] 39 | extend_gt_boxes = box_utils.enlarge_box3d( 40 | gt_boxes.view(-1, gt_boxes.shape[-1]), extra_width=self.model_cfg.TARGET_CONFIG.GT_EXTRA_WIDTH 41 | ).view(batch_size, -1, gt_boxes.shape[-1]) 42 | targets_dict = self.assign_stack_targets( 43 | points=point_coords, gt_boxes=gt_boxes, extend_gt_boxes=extend_gt_boxes, 44 | set_ignore_flag=True, use_ball_constraint=False, 45 | ret_part_labels=False 46 | ) 47 | 48 | return targets_dict 49 | 50 | def get_loss(self, tb_dict=None): 51 | tb_dict = {} if tb_dict is None else tb_dict 52 | point_loss_cls, tb_dict_1 = self.get_cls_layer_loss() 53 | 54 | point_loss = point_loss_cls 55 | tb_dict.update(tb_dict_1) 56 | return point_loss, tb_dict 57 | 58 | def forward(self, batch_dict): 59 | """ 60 | Args: 61 | batch_dict: 62 | batch_size: 63 | point_features: (N1 + N2 + N3 + ..., C) or (B, N, C) 64 | point_features_before_fusion: (N1 + N2 + N3 + ..., C) 65 | point_coords: (N1 + N2 + N3 + ..., 4) [bs_idx, x, y, z] 66 | point_labels (optional): (N1 + N2 + N3 + ...) 67 | gt_boxes (optional): (B, M, 8) 68 | Returns: 69 | batch_dict: 70 | point_cls_scores: (N1 + N2 + N3 + ..., 1) 71 | point_part_offset: (N1 + N2 + N3 + ..., 3) 72 | """ 73 | if self.model_cfg.get('USE_POINT_FEATURES_BEFORE_FUSION', False): 74 | point_features = batch_dict['point_features_before_fusion'] 75 | else: 76 | point_features = batch_dict['point_features'] 77 | point_cls_preds = self.cls_layers(point_features) # (total_points, num_class) 78 | 79 | ret_dict = { 80 | 'point_cls_preds': point_cls_preds, 81 | } 82 | 83 | point_cls_scores = torch.sigmoid(point_cls_preds) 84 | batch_dict['point_cls_scores'], _ = point_cls_scores.max(dim=-1) 85 | 86 | if self.training: 87 | targets_dict = self.assign_targets(batch_dict) 88 | ret_dict['point_cls_labels'] = targets_dict['point_cls_labels'] 89 | self.forward_ret_dict = ret_dict 90 | 91 | return batch_dict 92 | -------------------------------------------------------------------------------- /tools/visual_utils/open3d_vis_utils.py: -------------------------------------------------------------------------------- 1 | """ 2 | Open3d visualization tool box 3 | Written by Jihan YANG 4 | All rights preserved from 2021 - present. 5 | """ 6 | import open3d 7 | import torch 8 | import matplotlib 9 | import numpy as np 10 | 11 | box_colormap = [ 12 | [1, 1, 1], 13 | [0, 1, 0], 14 | [0, 1, 1], 15 | [1, 1, 0], 16 | ] 17 | 18 | 19 | def get_coor_colors(obj_labels): 20 | """ 21 | Args: 22 | obj_labels: 1 is ground, labels > 1 indicates different instance cluster 23 | 24 | Returns: 25 | rgb: [N, 3]. color for each point. 26 | """ 27 | colors = matplotlib.colors.XKCD_COLORS.values() 28 | max_color_num = obj_labels.max() 29 | 30 | color_list = list(colors)[:max_color_num+1] 31 | colors_rgba = [matplotlib.colors.to_rgba_array(color) for color in color_list] 32 | label_rgba = np.array(colors_rgba)[obj_labels] 33 | label_rgba = label_rgba.squeeze()[:, :3] 34 | 35 | return label_rgba 36 | 37 | 38 | def draw_scenes(points, gt_boxes=None, ref_boxes=None, ref_labels=None, ref_scores=None, point_colors=None, draw_origin=True): 39 | if isinstance(points, torch.Tensor): 40 | points = points.cpu().numpy() 41 | if isinstance(gt_boxes, torch.Tensor): 42 | gt_boxes = gt_boxes.cpu().numpy() 43 | if isinstance(ref_boxes, torch.Tensor): 44 | ref_boxes = ref_boxes.cpu().numpy() 45 | 46 | vis = open3d.visualization.Visualizer() 47 | vis.create_window() 48 | 49 | vis.get_render_option().point_size = 1.0 50 | vis.get_render_option().background_color = np.zeros(3) 51 | 52 | # draw origin 53 | if draw_origin: 54 | axis_pcd = open3d.geometry.TriangleMesh.create_coordinate_frame(size=1.0, origin=[0, 0, 0]) 55 | vis.add_geometry(axis_pcd) 56 | 57 | pts = open3d.geometry.PointCloud() 58 | pts.points = open3d.utility.Vector3dVector(points[:, :3]) 59 | 60 | vis.add_geometry(pts) 61 | if point_colors is None: 62 | pts.colors = open3d.utility.Vector3dVector(np.ones((points.shape[0], 3))) 63 | else: 64 | pts.colors = open3d.utility.Vector3dVector(point_colors) 65 | 66 | if gt_boxes is not None: 67 | vis = draw_box(vis, gt_boxes, (0, 0, 1)) 68 | 69 | if ref_boxes is not None: 70 | vis = draw_box(vis, ref_boxes, (0, 1, 0), ref_labels, ref_scores) 71 | 72 | vis.run() 73 | vis.destroy_window() 74 | 75 | 76 | def translate_boxes_to_open3d_instance(gt_boxes): 77 | """ 78 | 4-------- 6 79 | /| /| 80 | 5 -------- 3 . 81 | | | | | 82 | . 7 -------- 1 83 | |/ |/ 84 | 2 -------- 0 85 | """ 86 | center = gt_boxes[0:3] 87 | lwh = gt_boxes[3:6] 88 | axis_angles = np.array([0, 0, gt_boxes[6] + 1e-10]) 89 | rot = open3d.geometry.get_rotation_matrix_from_axis_angle(axis_angles) 90 | box3d = open3d.geometry.OrientedBoundingBox(center, rot, lwh) 91 | 92 | line_set = open3d.geometry.LineSet.create_from_oriented_bounding_box(box3d) 93 | 94 | # import ipdb; ipdb.set_trace(context=20) 95 | lines = np.asarray(line_set.lines) 96 | lines = np.concatenate([lines, np.array([[1, 4], [7, 6]])], axis=0) 97 | 98 | line_set.lines = open3d.utility.Vector2iVector(lines) 99 | 100 | return line_set, box3d 101 | 102 | 103 | def draw_box(vis, gt_boxes, color=(0, 1, 0), ref_labels=None, score=None): 104 | for i in range(gt_boxes.shape[0]): 105 | line_set, box3d = translate_boxes_to_open3d_instance(gt_boxes[i]) 106 | if ref_labels is None: 107 | line_set.paint_uniform_color(color) 108 | else: 109 | line_set.paint_uniform_color(box_colormap[ref_labels[i]]) 110 | 111 | vis.add_geometry(line_set) 112 | 113 | # if score is not None: 114 | # corners = box3d.get_box_points() 115 | # vis.add_3d_label(corners[5], '%.2f' % score[i]) 116 | return vis 117 | -------------------------------------------------------------------------------- /data/once/ImageSets/raw_medium.txt: -------------------------------------------------------------------------------- 1 | 000000 2 | 000001 3 | 000002 4 | 000003 5 | 000004 6 | 000005 7 | 000006 8 | 000007 9 | 000008 10 | 000009 11 | 000010 12 | 000011 13 | 000012 14 | 000013 15 | 000014 16 | 000015 17 | 000016 18 | 000017 19 | 000018 20 | 000019 21 | 000020 22 | 000021 23 | 000022 24 | 000023 25 | 000024 26 | 000025 27 | 000026 28 | 000029 29 | 000030 30 | 000031 31 | 000032 32 | 000033 33 | 000035 34 | 000036 35 | 000037 36 | 000038 37 | 000039 38 | 000040 39 | 000041 40 | 000042 41 | 000043 42 | 000044 43 | 000045 44 | 000046 45 | 000047 46 | 000048 47 | 000049 48 | 000050 49 | 000051 50 | 000052 51 | 000053 52 | 000054 53 | 000055 54 | 000056 55 | 000057 56 | 000058 57 | 000059 58 | 000060 59 | 000061 60 | 000062 61 | 000063 62 | 000064 63 | 000065 64 | 000066 65 | 000067 66 | 000068 67 | 000069 68 | 000070 69 | 000071 70 | 000072 71 | 000073 72 | 000074 73 | 000075 74 | 000078 75 | 000079 76 | 000081 77 | 000082 78 | 000083 79 | 000084 80 | 000085 81 | 000086 82 | 000087 83 | 000088 84 | 000089 85 | 000090 86 | 000091 87 | 000093 88 | 000094 89 | 000095 90 | 000096 91 | 000097 92 | 000098 93 | 000099 94 | 000100 95 | 000101 96 | 000102 97 | 000103 98 | 000105 99 | 000106 100 | 000107 101 | 000108 102 | 000109 103 | 000110 104 | 000111 105 | 000114 106 | 000115 107 | 000116 108 | 000117 109 | 000118 110 | 000119 111 | 000120 112 | 000122 113 | 000123 114 | 000124 115 | 000125 116 | 000126 117 | 000127 118 | 000128 119 | 000129 120 | 000130 121 | 000131 122 | 000132 123 | 000133 124 | 000134 125 | 000135 126 | 000136 127 | 000137 128 | 000138 129 | 000139 130 | 000140 131 | 000141 132 | 000142 133 | 000143 134 | 000144 135 | 000145 136 | 000146 137 | 000147 138 | 000148 139 | 000149 140 | 000150 141 | 000151 142 | 000152 143 | 000153 144 | 000154 145 | 000155 146 | 000156 147 | 000157 148 | 000158 149 | 000159 150 | 000160 151 | 000161 152 | 000162 153 | 000163 154 | 000164 155 | 000165 156 | 000166 157 | 000167 158 | 000169 159 | 000170 160 | 000171 161 | 000172 162 | 000173 163 | 000174 164 | 000175 165 | 000176 166 | 000177 167 | 000178 168 | 000179 169 | 000180 170 | 000181 171 | 000182 172 | 000183 173 | 000184 174 | 000185 175 | 000186 176 | 000187 177 | 000188 178 | 000189 179 | 000190 180 | 000191 181 | 000192 182 | 000193 183 | 000194 184 | 000195 185 | 000196 186 | 000197 187 | 000198 188 | 000199 189 | 000202 190 | 000203 191 | 000204 192 | 000205 193 | 000206 194 | 000207 195 | 000208 196 | 000209 197 | 000210 198 | 000211 199 | 000212 200 | 000213 201 | 000214 202 | 000215 203 | 000216 204 | 000217 205 | 000218 206 | 000219 207 | 000220 208 | 000221 209 | 000222 210 | 000223 211 | 000224 212 | 000225 213 | 000226 214 | 000227 215 | 000228 216 | 000229 217 | 000230 218 | 000231 219 | 000232 220 | 000233 221 | 000234 222 | 000235 223 | 000236 224 | 000237 225 | 000238 226 | 000239 227 | 000240 228 | 000241 229 | 000242 230 | 000243 231 | 000244 232 | 000245 233 | 000246 234 | 000247 235 | 000248 236 | 000249 237 | 000250 238 | 000251 239 | 000252 240 | 000253 241 | 000254 242 | 000255 243 | 000256 244 | 000257 245 | 000258 246 | 000259 247 | 000260 248 | 000261 249 | 000262 250 | 000263 251 | 000264 252 | 000265 253 | 000266 254 | 000267 255 | 000268 256 | 000269 257 | 000270 258 | 000271 259 | 000272 260 | 000274 261 | 000276 262 | 000277 263 | 000278 264 | 000279 265 | 000280 266 | 000281 267 | 000282 268 | 000283 269 | 000284 270 | 000285 271 | 000286 272 | 000287 273 | 000288 274 | 000289 275 | 000290 276 | 000291 277 | 000292 278 | 000293 279 | 000294 280 | 000295 281 | 000296 282 | 000297 283 | 000298 284 | 000299 285 | 000300 286 | 000301 287 | 000302 288 | 000304 289 | 000305 290 | 000306 291 | 000307 292 | 000308 293 | 000309 294 | 000310 295 | 000311 296 | 000312 297 | 000313 298 | 000314 299 | 000315 300 | 000316 301 | 000317 302 | 000319 303 | 000320 304 | 000321 305 | 000323 306 | 000324 307 | 000325 308 | 000326 309 | 000327 310 | 000328 311 | 000329 312 | 000330 313 | 000331 314 | 000332 315 | 000333 316 | 000335 317 | 000336 318 | 000337 319 | 000338 320 | 000339 321 | 000340 322 | 000341 -------------------------------------------------------------------------------- /pcdet/utils/transform_utils.py: -------------------------------------------------------------------------------- 1 | import torch 2 | import numpy as np 3 | from . import common_utils 4 | 5 | 6 | def random_world_flip(params, reverse=False, points_3d=None, boxes_3d=None): 7 | if reverse: 8 | params = params[::-1] 9 | for cur_axis in params: 10 | if cur_axis == 'x': 11 | if points_3d is not None: 12 | points_3d[:, 1] = -points_3d[:, 1] 13 | if boxes_3d is not None: 14 | boxes_3d[:, 1] = -boxes_3d[:, 1] 15 | boxes_3d[:, 6] = -boxes_3d[:, 6] 16 | elif cur_axis == 'y': 17 | if points_3d is not None: 18 | points_3d[:, 0] = -points_3d[:, 0] 19 | if boxes_3d is not None: 20 | boxes_3d[:, 0] = -boxes_3d[:, 0] 21 | boxes_3d[:, 6] = -(boxes_3d[:, 6] + np.pi) 22 | else: 23 | raise NotImplementedError 24 | return points_3d, boxes_3d 25 | 26 | 27 | def random_world_rotation(params, reverse=False, points_3d=None, boxes_3d=None): 28 | if reverse: 29 | params = -params 30 | if points_3d is not None: 31 | points_3d = common_utils.rotate_points_along_z(points_3d.unsqueeze(0), points_3d.new_tensor([params]))[0] 32 | if boxes_3d is not None: 33 | boxes_3d[:, 0:3] = common_utils.rotate_points_along_z(boxes_3d[:, 0:3].unsqueeze(0), boxes_3d.new_tensor([params]))[0] 34 | boxes_3d[:, 6] += params 35 | return points_3d, boxes_3d 36 | 37 | 38 | def random_world_scaling(params, reverse=False, points_3d=None, boxes_3d=None): 39 | if reverse: 40 | params = 1.0 / params 41 | if points_3d is not None: 42 | points_3d[:, :3] *= params 43 | if boxes_3d is not None: 44 | boxes_3d[:, :6] *= params 45 | return points_3d, boxes_3d 46 | 47 | 48 | def random_world_translation(params, reverse=False, points_3d=None, boxes_3d=None): 49 | if reverse: 50 | params = -params 51 | if points_3d is not None: 52 | points_3d[:, :3] += points_3d.new_tensor(params) 53 | if boxes_3d is not None: 54 | boxes_3d[:, :3] += boxes_3d.new_tensor(params) 55 | return points_3d, boxes_3d 56 | 57 | 58 | def imrescale(params, reverse=False, points_2d=None, boxes_2d=None): 59 | w_scale, h_scale = params 60 | if reverse: 61 | w_scale, h_scale = 1.0 / w_scale, 1.0 / h_scale 62 | if points_2d is not None: 63 | points_2d[:, 0:2] *= points_2d.new_tensor([w_scale, h_scale]) 64 | if boxes_2d is not None: 65 | boxes_2d[:, :4] *= boxes_2d.new_tensor([w_scale, h_scale, w_scale, h_scale]) 66 | return points_2d, boxes_2d 67 | 68 | 69 | def imflip(params, reverse=False, points_2d=None, boxes_2d=None): 70 | enable_x, rescale_w = params 71 | if enable_x: 72 | if points_2d is not None: 73 | points_2d[:, 0] = rescale_w - 1 - points_2d[..., 0] 74 | if boxes_2d is not None: 75 | flipped = boxes_2d.clone() 76 | flipped[:, 0] = rescale_w - 1 - boxes_2d[..., 2] 77 | flipped[:, 2] = rescale_w - 1 - boxes_2d[..., 0] 78 | boxes_2d = flipped 79 | return points_2d, boxes_2d 80 | 81 | 82 | def points_lidar2img(points_3d, proj_mat, with_depth=False): 83 | """Project points from lidar coordicates to image coordinates. 84 | 85 | Args: 86 | points_3d (torch.Tensor): Points in shape (N, 3). 87 | proj_mat (torch.Tensor): (3, 4), transformation matrix between coordinates(left R). 88 | with_depth (bool, optional): Whether to keep depth in the output. 89 | Defaults to False. 90 | 91 | Returns: 92 | torch.Tensor: Points in image coordinates with shape [N, 2]. 93 | """ 94 | # (N, 4) 95 | points_4 = torch.cat([points_3d, points_3d.new_ones((points_3d.shape[0], 1))], dim=-1) 96 | point_2d = torch.matmul(points_4, proj_mat.t()) 97 | point_2d_res = point_2d[..., :2] / torch.clamp(point_2d[..., 2:3], min=1e-5, max=1e5) 98 | 99 | if with_depth: 100 | return torch.cat([point_2d_res, point_2d[..., 2:3]], dim=-1) 101 | return point_2d_res 102 | -------------------------------------------------------------------------------- /pcdet/ops/iou3d_nms/iou3d_nms_utils.py: -------------------------------------------------------------------------------- 1 | """ 2 | 3D IoU Calculation and Rotated NMS 3 | Written by Shaoshuai Shi 4 | All Rights Reserved 2019-2020. 5 | """ 6 | import torch 7 | 8 | from ...utils import common_utils 9 | from . import iou3d_nms_cuda 10 | 11 | 12 | def boxes_bev_iou_cpu(boxes_a, boxes_b): 13 | """ 14 | Args: 15 | boxes_a: (N, 7) [x, y, z, dx, dy, dz, heading] 16 | boxes_b: (M, 7) [x, y, z, dx, dy, dz, heading] 17 | 18 | Returns: 19 | ans_iou: (N, M) 20 | """ 21 | boxes_a, is_numpy = common_utils.check_numpy_to_torch(boxes_a) 22 | boxes_b, is_numpy = common_utils.check_numpy_to_torch(boxes_b) 23 | assert not (boxes_a.is_cuda or boxes_b.is_cuda), 'Only support CPU tensors' 24 | assert boxes_a.shape[1] == 7 and boxes_b.shape[1] == 7 25 | ans_iou = boxes_a.new_zeros(torch.Size((boxes_a.shape[0], boxes_b.shape[0]))) 26 | iou3d_nms_cuda.boxes_iou_bev_cpu(boxes_a.contiguous(), boxes_b.contiguous(), ans_iou) 27 | 28 | return ans_iou.numpy() if is_numpy else ans_iou 29 | 30 | 31 | def boxes_iou_bev(boxes_a, boxes_b): 32 | """ 33 | Args: 34 | boxes_a: (N, 7) [x, y, z, dx, dy, dz, heading] 35 | boxes_b: (M, 7) [x, y, z, dx, dy, dz, heading] 36 | 37 | Returns: 38 | ans_iou: (N, M) 39 | """ 40 | assert boxes_a.shape[1] == boxes_b.shape[1] == 7 41 | ans_iou = torch.cuda.FloatTensor(torch.Size((boxes_a.shape[0], boxes_b.shape[0]))).zero_() 42 | 43 | iou3d_nms_cuda.boxes_iou_bev_gpu(boxes_a.contiguous(), boxes_b.contiguous(), ans_iou) 44 | 45 | return ans_iou 46 | 47 | 48 | def boxes_iou3d_gpu(boxes_a, boxes_b): 49 | """ 50 | Args: 51 | boxes_a: (N, 7) [x, y, z, dx, dy, dz, heading] 52 | boxes_b: (M, 7) [x, y, z, dx, dy, dz, heading] 53 | 54 | Returns: 55 | ans_iou: (N, M) 56 | """ 57 | assert boxes_a.shape[1] == boxes_b.shape[1] == 7 58 | 59 | # height overlap 60 | boxes_a_height_max = (boxes_a[:, 2] + boxes_a[:, 5] / 2).view(-1, 1) 61 | boxes_a_height_min = (boxes_a[:, 2] - boxes_a[:, 5] / 2).view(-1, 1) 62 | boxes_b_height_max = (boxes_b[:, 2] + boxes_b[:, 5] / 2).view(1, -1) 63 | boxes_b_height_min = (boxes_b[:, 2] - boxes_b[:, 5] / 2).view(1, -1) 64 | 65 | # bev overlap 66 | overlaps_bev = torch.cuda.FloatTensor(torch.Size((boxes_a.shape[0], boxes_b.shape[0]))).zero_() # (N, M) 67 | iou3d_nms_cuda.boxes_overlap_bev_gpu(boxes_a.contiguous(), boxes_b.contiguous(), overlaps_bev) 68 | 69 | max_of_min = torch.max(boxes_a_height_min, boxes_b_height_min) 70 | min_of_max = torch.min(boxes_a_height_max, boxes_b_height_max) 71 | overlaps_h = torch.clamp(min_of_max - max_of_min, min=0) 72 | 73 | # 3d iou 74 | overlaps_3d = overlaps_bev * overlaps_h 75 | 76 | vol_a = (boxes_a[:, 3] * boxes_a[:, 4] * boxes_a[:, 5]).view(-1, 1) 77 | vol_b = (boxes_b[:, 3] * boxes_b[:, 4] * boxes_b[:, 5]).view(1, -1) 78 | 79 | iou3d = overlaps_3d / torch.clamp(vol_a + vol_b - overlaps_3d, min=1e-6) 80 | 81 | return iou3d 82 | 83 | 84 | def nms_gpu(boxes, scores, thresh, pre_maxsize=None, **kwargs): 85 | """ 86 | :param boxes: (N, 7) [x, y, z, dx, dy, dz, heading] 87 | :param scores: (N) 88 | :param thresh: 89 | :return: 90 | """ 91 | assert boxes.shape[1] == 7 92 | order = scores.sort(0, descending=True)[1] 93 | if pre_maxsize is not None: 94 | order = order[:pre_maxsize] 95 | 96 | boxes = boxes[order].contiguous() 97 | keep = torch.LongTensor(boxes.size(0)) 98 | num_out = iou3d_nms_cuda.nms_gpu(boxes, keep, thresh) 99 | return order[keep[:num_out].cuda()].contiguous(), None 100 | 101 | 102 | def nms_normal_gpu(boxes, scores, thresh, **kwargs): 103 | """ 104 | :param boxes: (N, 7) [x, y, z, dx, dy, dz, heading] 105 | :param scores: (N) 106 | :param thresh: 107 | :return: 108 | """ 109 | assert boxes.shape[1] == 7 110 | order = scores.sort(0, descending=True)[1] 111 | 112 | boxes = boxes[order].contiguous() 113 | 114 | keep = torch.LongTensor(boxes.size(0)) 115 | num_out = iou3d_nms_cuda.nms_normal_gpu(boxes, keep, thresh) 116 | return order[keep[:num_out].cuda()].contiguous(), None 117 | -------------------------------------------------------------------------------- /pcdet/models/dense_heads/target_assigner/anchor_generator.py: -------------------------------------------------------------------------------- 1 | import torch 2 | 3 | 4 | class AnchorGenerator(object): 5 | def __init__(self, anchor_range, anchor_generator_config): 6 | super().__init__() 7 | self.anchor_generator_cfg = anchor_generator_config 8 | self.anchor_range = anchor_range 9 | self.anchor_sizes = [config['anchor_sizes'] for config in anchor_generator_config] 10 | self.anchor_rotations = [config['anchor_rotations'] for config in anchor_generator_config] 11 | self.anchor_heights = [config['anchor_bottom_heights'] for config in anchor_generator_config] 12 | self.align_center = [config.get('align_center', False) for config in anchor_generator_config] 13 | 14 | assert len(self.anchor_sizes) == len(self.anchor_rotations) == len(self.anchor_heights) 15 | self.num_of_anchor_sets = len(self.anchor_sizes) 16 | 17 | def generate_anchors(self, grid_sizes): 18 | assert len(grid_sizes) == self.num_of_anchor_sets 19 | all_anchors = [] 20 | num_anchors_per_location = [] 21 | for grid_size, anchor_size, anchor_rotation, anchor_height, align_center in zip( 22 | grid_sizes, self.anchor_sizes, self.anchor_rotations, self.anchor_heights, self.align_center): 23 | 24 | num_anchors_per_location.append(len(anchor_rotation) * len(anchor_size) * len(anchor_height)) 25 | if align_center: 26 | x_stride = (self.anchor_range[3] - self.anchor_range[0]) / grid_size[0] 27 | y_stride = (self.anchor_range[4] - self.anchor_range[1]) / grid_size[1] 28 | x_offset, y_offset = x_stride / 2, y_stride / 2 29 | else: 30 | x_stride = (self.anchor_range[3] - self.anchor_range[0]) / (grid_size[0] - 1) 31 | y_stride = (self.anchor_range[4] - self.anchor_range[1]) / (grid_size[1] - 1) 32 | x_offset, y_offset = 0, 0 33 | 34 | x_shifts = torch.arange( 35 | self.anchor_range[0] + x_offset, self.anchor_range[3] + 1e-5, step=x_stride, dtype=torch.float32, 36 | ).cuda() 37 | y_shifts = torch.arange( 38 | self.anchor_range[1] + y_offset, self.anchor_range[4] + 1e-5, step=y_stride, dtype=torch.float32, 39 | ).cuda() 40 | z_shifts = x_shifts.new_tensor(anchor_height) 41 | 42 | num_anchor_size, num_anchor_rotation = anchor_size.__len__(), anchor_rotation.__len__() 43 | anchor_rotation = x_shifts.new_tensor(anchor_rotation) 44 | anchor_size = x_shifts.new_tensor(anchor_size) 45 | x_shifts, y_shifts, z_shifts = torch.meshgrid([ 46 | x_shifts, y_shifts, z_shifts 47 | ]) # [x_grid, y_grid, z_grid] 48 | anchors = torch.stack((x_shifts, y_shifts, z_shifts), dim=-1) # [x, y, z, 3] 49 | anchors = anchors[:, :, :, None, :].repeat(1, 1, 1, anchor_size.shape[0], 1) 50 | anchor_size = anchor_size.view(1, 1, 1, -1, 3).repeat([*anchors.shape[0:3], 1, 1]) 51 | anchors = torch.cat((anchors, anchor_size), dim=-1) 52 | anchors = anchors[:, :, :, :, None, :].repeat(1, 1, 1, 1, num_anchor_rotation, 1) 53 | anchor_rotation = anchor_rotation.view(1, 1, 1, 1, -1, 1).repeat([*anchors.shape[0:3], num_anchor_size, 1, 1]) 54 | anchors = torch.cat((anchors, anchor_rotation), dim=-1) # [x, y, z, num_size, num_rot, 7] 55 | 56 | anchors = anchors.permute(2, 1, 0, 3, 4, 5).contiguous() 57 | #anchors = anchors.view(-1, anchors.shape[-1]) 58 | anchors[..., 2] += anchors[..., 5] / 2 # shift to box centers 59 | all_anchors.append(anchors) 60 | return all_anchors, num_anchors_per_location 61 | 62 | 63 | if __name__ == '__main__': 64 | from easydict import EasyDict 65 | config = [ 66 | EasyDict({ 67 | 'anchor_sizes': [[2.1, 4.7, 1.7], [0.86, 0.91, 1.73], [0.84, 1.78, 1.78]], 68 | 'anchor_rotations': [0, 1.57], 69 | 'anchor_heights': [0, 0.5] 70 | }) 71 | ] 72 | 73 | A = AnchorGenerator( 74 | anchor_range=[-75.2, -75.2, -2, 75.2, 75.2, 4], 75 | anchor_generator_config=config 76 | ) 77 | import pdb 78 | pdb.set_trace() 79 | A.generate_anchors([[188, 188]]) 80 | -------------------------------------------------------------------------------- /pcdet/ops/pointnet2/pointnet2_stack/src/interpolate.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | Stacked-batch-data version of point interpolation, modified from the original implementation of official PointNet++ codes. 3 | Written by Shaoshuai Shi 4 | All Rights Reserved 2019-2020. 5 | */ 6 | 7 | 8 | #include 9 | #include 10 | // #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include "interpolate_gpu.h" 17 | 18 | // extern THCState *state; 19 | 20 | #define CHECK_CUDA(x) do { \ 21 | if (!x.type().is_cuda()) { \ 22 | fprintf(stderr, "%s must be CUDA tensor at %s:%d\n", #x, __FILE__, __LINE__); \ 23 | exit(-1); \ 24 | } \ 25 | } while (0) 26 | #define CHECK_CONTIGUOUS(x) do { \ 27 | if (!x.is_contiguous()) { \ 28 | fprintf(stderr, "%s must be contiguous tensor at %s:%d\n", #x, __FILE__, __LINE__); \ 29 | exit(-1); \ 30 | } \ 31 | } while (0) 32 | #define CHECK_INPUT(x) CHECK_CUDA(x);CHECK_CONTIGUOUS(x) 33 | 34 | 35 | void three_nn_wrapper_stack(at::Tensor unknown_tensor, 36 | at::Tensor unknown_batch_cnt_tensor, at::Tensor known_tensor, 37 | at::Tensor known_batch_cnt_tensor, at::Tensor dist2_tensor, at::Tensor idx_tensor){ 38 | // unknown: (N1 + N2 ..., 3) 39 | // unknown_batch_cnt: (batch_size), [N1, N2, ...] 40 | // known: (M1 + M2 ..., 3) 41 | // known_batch_cnt: (batch_size), [M1, M2, ...] 42 | // Return: 43 | // dist: (N1 + N2 ..., 3) l2 distance to the three nearest neighbors 44 | // idx: (N1 + N2 ..., 3) index of the three nearest neighbors 45 | CHECK_INPUT(unknown_tensor); 46 | CHECK_INPUT(unknown_batch_cnt_tensor); 47 | CHECK_INPUT(known_tensor); 48 | CHECK_INPUT(known_batch_cnt_tensor); 49 | CHECK_INPUT(dist2_tensor); 50 | CHECK_INPUT(idx_tensor); 51 | 52 | int batch_size = unknown_batch_cnt_tensor.size(0); 53 | int N = unknown_tensor.size(0); 54 | int M = known_tensor.size(0); 55 | const float *unknown = unknown_tensor.data(); 56 | const int *unknown_batch_cnt = unknown_batch_cnt_tensor.data(); 57 | const float *known = known_tensor.data(); 58 | const int *known_batch_cnt = known_batch_cnt_tensor.data(); 59 | float *dist2 = dist2_tensor.data(); 60 | int *idx = idx_tensor.data(); 61 | 62 | three_nn_kernel_launcher_stack(batch_size, N, M, unknown, unknown_batch_cnt, known, known_batch_cnt, dist2, idx); 63 | } 64 | 65 | 66 | void three_interpolate_wrapper_stack(at::Tensor features_tensor, 67 | at::Tensor idx_tensor, at::Tensor weight_tensor, at::Tensor out_tensor) { 68 | // features_tensor: (M1 + M2 ..., C) 69 | // idx_tensor: [N1 + N2 ..., 3] 70 | // weight_tensor: [N1 + N2 ..., 3] 71 | // Return: 72 | // out_tensor: (N1 + N2 ..., C) 73 | CHECK_INPUT(features_tensor); 74 | CHECK_INPUT(idx_tensor); 75 | CHECK_INPUT(weight_tensor); 76 | CHECK_INPUT(out_tensor); 77 | 78 | int N = out_tensor.size(0); 79 | int channels = features_tensor.size(1); 80 | const float *features = features_tensor.data(); 81 | const float *weight = weight_tensor.data(); 82 | const int *idx = idx_tensor.data(); 83 | float *out = out_tensor.data(); 84 | 85 | three_interpolate_kernel_launcher_stack(N, channels, features, idx, weight, out); 86 | } 87 | 88 | 89 | void three_interpolate_grad_wrapper_stack(at::Tensor grad_out_tensor, at::Tensor idx_tensor, 90 | at::Tensor weight_tensor, at::Tensor grad_features_tensor) { 91 | // grad_out_tensor: (N1 + N2 ..., C) 92 | // idx_tensor: [N1 + N2 ..., 3] 93 | // weight_tensor: [N1 + N2 ..., 3] 94 | // Return: 95 | // grad_features_tensor: (M1 + M2 ..., C) 96 | CHECK_INPUT(grad_out_tensor); 97 | CHECK_INPUT(idx_tensor); 98 | CHECK_INPUT(weight_tensor); 99 | CHECK_INPUT(grad_features_tensor); 100 | 101 | int N = grad_out_tensor.size(0); 102 | int channels = grad_out_tensor.size(1); 103 | const float *grad_out = grad_out_tensor.data(); 104 | const float *weight = weight_tensor.data(); 105 | const int *idx = idx_tensor.data(); 106 | float *grad_features = grad_features_tensor.data(); 107 | 108 | // printf("N=%d, channels=%d\n", N, channels); 109 | three_interpolate_grad_kernel_launcher_stack(N, channels, grad_out, idx, weight, grad_features); 110 | } -------------------------------------------------------------------------------- /tools/cfgs/kitti_models/pointpillar.yaml: -------------------------------------------------------------------------------- 1 | CLASS_NAMES: ['Car'] 2 | 3 | DATA_CONFIG: 4 | _BASE_CONFIG_: cfgs/dataset_configs/kitti_dataset.yaml 5 | POINT_CLOUD_RANGE: [0, -39.68, -3, 69.12, 39.68, 1] 6 | DATA_PROCESSOR: 7 | - NAME: mask_points_and_boxes_outside_range 8 | REMOVE_OUTSIDE_BOXES: True 9 | 10 | - NAME: shuffle_points 11 | SHUFFLE_ENABLED: { 12 | 'train': True, 13 | 'test': False 14 | } 15 | 16 | - NAME: transform_points_to_voxels 17 | VOXEL_SIZE: [0.16, 0.16, 4] 18 | MAX_POINTS_PER_VOXEL: 32 19 | MAX_NUMBER_OF_VOXELS: { 20 | 'train': 16000, 21 | 'test': 40000 22 | } 23 | 24 | DATA_AUGMENTOR: 25 | DISABLE_AUG_LIST: ['placeholder'] 26 | AUG_CONFIG_LIST: 27 | - NAME: gt_sampling 28 | USE_ROAD_PLANE: True 29 | DB_INFO_PATH: 30 | - kitti_dbinfos_train.pkl 31 | PREPARE: { 32 | filter_by_min_points: ['Car:5', 'Pedestrian:5', 'Cyclist:5'], 33 | filter_by_difficulty: [-1], 34 | } 35 | 36 | SAMPLE_GROUPS: ['Car:15','Pedestrian:15', 'Cyclist:15'] 37 | NUM_POINT_FEATURES: 4 38 | DATABASE_WITH_FAKELIDAR: False 39 | REMOVE_EXTRA_WIDTH: [0.0, 0.0, 0.0] 40 | LIMIT_WHOLE_SCENE: False 41 | 42 | - NAME: random_world_flip 43 | ALONG_AXIS_LIST: ['x'] 44 | 45 | - NAME: random_world_rotation 46 | WORLD_ROT_ANGLE: [-0.78539816, 0.78539816] 47 | 48 | - NAME: random_world_scaling 49 | WORLD_SCALE_RANGE: [0.95, 1.05] 50 | 51 | MODEL: 52 | NAME: PointPillar 53 | 54 | VFE: 55 | NAME: PillarVFE 56 | WITH_DISTANCE: False 57 | USE_ABSLOTE_XYZ: True 58 | USE_NORM: True 59 | NUM_FILTERS: [64] 60 | 61 | MAP_TO_BEV: 62 | NAME: PointPillarScatter 63 | NUM_BEV_FEATURES: 64 64 | 65 | BACKBONE_2D: 66 | NAME: BaseBEVBackbone 67 | LAYER_NUMS: [3, 5, 5] 68 | LAYER_STRIDES: [2, 2, 2] 69 | NUM_FILTERS: [64, 128, 256] 70 | UPSAMPLE_STRIDES: [1, 2, 4] 71 | NUM_UPSAMPLE_FILTERS: [128, 128, 128] 72 | 73 | DENSE_HEAD: 74 | NAME: AnchorHeadSingle 75 | CLASS_AGNOSTIC: False 76 | 77 | USE_DIRECTION_CLASSIFIER: True 78 | DIR_OFFSET: 0.78539 79 | DIR_LIMIT_OFFSET: 0.0 80 | NUM_DIR_BINS: 2 81 | 82 | ANCHOR_GENERATOR_CONFIG: [ 83 | { 84 | 'class_name': 'Car', 85 | 'anchor_sizes': [[3.9, 1.6, 1.56]], 86 | 'anchor_rotations': [0, 1.57], 87 | 'anchor_bottom_heights': [-1.78], 88 | 'align_center': False, 89 | 'feature_map_stride': 2, 90 | 'matched_threshold': 0.6, 91 | 'unmatched_threshold': 0.45 92 | } 93 | ] 94 | 95 | TARGET_ASSIGNER_CONFIG: 96 | NAME: AxisAlignedTargetAssigner 97 | POS_FRACTION: -1.0 98 | SAMPLE_SIZE: 512 99 | NORM_BY_NUM_EXAMPLES: False 100 | MATCH_HEIGHT: False 101 | BOX_CODER: ResidualCoder 102 | 103 | LOSS_CONFIG: 104 | LOSS_WEIGHTS: { 105 | 'cls_weight': 1.0, 106 | 'loc_weight': 2.0, 107 | 'dir_weight': 0.2, 108 | 'code_weights': [1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0] 109 | } 110 | 111 | POST_PROCESSING: 112 | RECALL_THRESH_LIST: [0.3, 0.5, 0.7] 113 | SCORE_THRESH: 0.3 114 | OUTPUT_RAW_SCORE: False 115 | 116 | EVAL_METRIC: kitti 117 | 118 | NMS_CONFIG: 119 | MULTI_CLASSES_NMS: False 120 | NMS_TYPE: nms_gpu 121 | NMS_THRESH: 0.01 122 | NMS_PRE_MAXSIZE: 4096 123 | NMS_POST_MAXSIZE: 500 124 | 125 | 126 | OPTIMIZATION: 127 | BATCH_SIZE_PER_GPU: 4 128 | NUM_EPOCHS: 80 129 | 130 | OPTIMIZER: adam_onecycle 131 | LR: 0.003 132 | WEIGHT_DECAY: 0.01 133 | MOMENTUM: 0.9 134 | 135 | MOMS: [0.95, 0.85] 136 | PCT_START: 0.4 137 | DIV_FACTOR: 10 138 | DECAY_STEP_LIST: [35, 45] 139 | LR_DECAY: 0.1 140 | LR_CLIP: 0.0000001 141 | 142 | LR_WARMUP: False 143 | WARMUP_EPOCH: 1 144 | 145 | GRAD_NORM_CLIP: 10 146 | -------------------------------------------------------------------------------- /pcdet/ops/pointnet2/pointnet2_stack/src/voxel_query_gpu.cu: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | #include "voxel_query_gpu.h" 7 | #include "cuda_utils.h" 8 | 9 | 10 | __global__ void voxel_query_kernel_stack(int M, int R1, int R2, int R3, int nsample, 11 | float radius, int z_range, int y_range, int x_range, const float *new_xyz, 12 | const float *xyz, const int *new_coords, const int *point_indices, int *idx) { 13 | // :param new_coords: (M1 + M2 ..., 4) centers of the ball query 14 | // :param point_indices: (B, Z, Y, X) 15 | // output: 16 | // idx: (M1 + M2, nsample) 17 | int pt_idx = blockIdx.x * blockDim.x + threadIdx.x; 18 | if (pt_idx >= M) return; 19 | 20 | new_xyz += pt_idx * 3; 21 | new_coords += pt_idx * 4; 22 | idx += pt_idx * nsample; 23 | 24 | curandState state; 25 | curand_init(pt_idx, 0, 0, &state); 26 | 27 | float radius2 = radius * radius; 28 | float new_x = new_xyz[0]; 29 | float new_y = new_xyz[1]; 30 | float new_z = new_xyz[2]; 31 | 32 | int batch_idx = new_coords[0]; 33 | int new_coords_z = new_coords[1]; 34 | int new_coords_y = new_coords[2]; 35 | int new_coords_x = new_coords[3]; 36 | 37 | int cnt = 0; 38 | int cnt2 = 0; 39 | // for (int dz = -1*z_range; dz <= z_range; ++dz) { 40 | for (int dz = -1*z_range; dz <= z_range; ++dz) { 41 | int z_coord = new_coords_z + dz; 42 | if (z_coord < 0 || z_coord >= R1) continue; 43 | 44 | for (int dy = -1*y_range; dy <= y_range; ++dy) { 45 | int y_coord = new_coords_y + dy; 46 | if (y_coord < 0 || y_coord >= R2) continue; 47 | 48 | for (int dx = -1*x_range; dx <= x_range; ++dx) { 49 | int x_coord = new_coords_x + dx; 50 | if (x_coord < 0 || x_coord >= R3) continue; 51 | 52 | int index = batch_idx * R1 * R2 * R3 + \ 53 | z_coord * R2 * R3 + \ 54 | y_coord * R3 + \ 55 | x_coord; 56 | int neighbor_idx = point_indices[index]; 57 | if (neighbor_idx < 0) continue; 58 | 59 | float x_per = xyz[neighbor_idx*3 + 0]; 60 | float y_per = xyz[neighbor_idx*3 + 1]; 61 | float z_per = xyz[neighbor_idx*3 + 2]; 62 | 63 | float dist2 = (x_per - new_x) * (x_per - new_x) + (y_per - new_y) * (y_per - new_y) + (z_per - new_z) * (z_per - new_z); 64 | 65 | if (dist2 > radius2) continue; 66 | 67 | ++cnt2; 68 | 69 | if (cnt < nsample) { 70 | if (cnt == 0) { 71 | for (int l = 0; l < nsample; ++l) { 72 | idx[l] = neighbor_idx; 73 | } 74 | } 75 | idx[cnt] = neighbor_idx; 76 | ++cnt; 77 | } 78 | // else { 79 | // float rnd = curand_uniform(&state); 80 | // if (rnd < (float(nsample) / cnt2)) { 81 | // int insertidx = ceilf(curand_uniform(&state) * nsample) - 1; 82 | // idx[insertidx] = neighbor_idx; 83 | // } 84 | // } 85 | } 86 | } 87 | } 88 | if (cnt == 0) idx[0] = -1; 89 | } 90 | 91 | 92 | void voxel_query_kernel_launcher_stack(int M, int R1, int R2, int R3, int nsample, 93 | float radius, int z_range, int y_range, int x_range, const float *new_xyz, 94 | const float *xyz, const int *new_coords, const int *point_indices, int *idx) { 95 | // :param new_coords: (M1 + M2 ..., 4) centers of the voxel query 96 | // :param point_indices: (B, Z, Y, X) 97 | // output: 98 | // idx: (M1 + M2, nsample) 99 | 100 | cudaError_t err; 101 | 102 | dim3 blocks(DIVUP(M, THREADS_PER_BLOCK)); // blockIdx.x(col), blockIdx.y(row) 103 | dim3 threads(THREADS_PER_BLOCK); 104 | 105 | voxel_query_kernel_stack<<>>(M, R1, R2, R3, nsample, radius, z_range, y_range, x_range, new_xyz, xyz, new_coords, point_indices, idx); 106 | // cudaDeviceSynchronize(); // for using printf in kernel function 107 | 108 | err = cudaGetLastError(); 109 | if (cudaSuccess != err) { 110 | fprintf(stderr, "CUDA kernel failed : %s\n", cudaGetErrorString(err)); 111 | exit(-1); 112 | } 113 | } 114 | -------------------------------------------------------------------------------- /pcdet/ops/roiaware_pool3d/roiaware_pool3d_utils.py: -------------------------------------------------------------------------------- 1 | import torch 2 | import torch.nn as nn 3 | from torch.autograd import Function 4 | 5 | from ...utils import common_utils 6 | from . import roiaware_pool3d_cuda 7 | 8 | 9 | def points_in_boxes_cpu(points, boxes): 10 | """ 11 | Args: 12 | points: (num_points, 3) 13 | boxes: [x, y, z, dx, dy, dz, heading], (x, y, z) is the box center, each box DO NOT overlaps 14 | Returns: 15 | point_indices: (N, num_points) 16 | """ 17 | assert boxes.shape[1] == 7 18 | assert points.shape[1] == 3 19 | points, is_numpy = common_utils.check_numpy_to_torch(points) 20 | boxes, is_numpy = common_utils.check_numpy_to_torch(boxes) 21 | 22 | point_indices = points.new_zeros((boxes.shape[0], points.shape[0]), dtype=torch.int) 23 | roiaware_pool3d_cuda.points_in_boxes_cpu(boxes.float().contiguous(), points.float().contiguous(), point_indices) 24 | 25 | return point_indices.numpy() if is_numpy else point_indices 26 | 27 | 28 | def points_in_boxes_gpu(points, boxes): 29 | """ 30 | :param points: (B, M, 3) 31 | :param boxes: (B, T, 7), num_valid_boxes <= T 32 | :return box_idxs_of_pts: (B, M), default background = -1 33 | """ 34 | assert boxes.shape[0] == points.shape[0] 35 | assert boxes.shape[2] == 7 and points.shape[2] == 3 36 | batch_size, num_points, _ = points.shape 37 | 38 | box_idxs_of_pts = points.new_zeros((batch_size, num_points), dtype=torch.int).fill_(-1) 39 | roiaware_pool3d_cuda.points_in_boxes_gpu(boxes.contiguous(), points.contiguous(), box_idxs_of_pts) 40 | 41 | return box_idxs_of_pts 42 | 43 | 44 | class RoIAwarePool3d(nn.Module): 45 | def __init__(self, out_size, max_pts_each_voxel=128): 46 | super().__init__() 47 | self.out_size = out_size 48 | self.max_pts_each_voxel = max_pts_each_voxel 49 | 50 | def forward(self, rois, pts, pts_feature, pool_method='max'): 51 | assert pool_method in ['max', 'avg'] 52 | return RoIAwarePool3dFunction.apply(rois, pts, pts_feature, self.out_size, self.max_pts_each_voxel, pool_method) 53 | 54 | 55 | class RoIAwarePool3dFunction(Function): 56 | @staticmethod 57 | def forward(ctx, rois, pts, pts_feature, out_size, max_pts_each_voxel, pool_method): 58 | """ 59 | Args: 60 | ctx: 61 | rois: (N, 7) [x, y, z, dx, dy, dz, heading] (x, y, z) is the box center 62 | pts: (npoints, 3) 63 | pts_feature: (npoints, C) 64 | out_size: int or tuple, like 7 or (7, 7, 7) 65 | max_pts_each_voxel: 66 | pool_method: 'max' or 'avg' 67 | 68 | Returns: 69 | pooled_features: (N, out_x, out_y, out_z, C) 70 | """ 71 | assert rois.shape[1] == 7 and pts.shape[1] == 3 72 | if isinstance(out_size, int): 73 | out_x = out_y = out_z = out_size 74 | else: 75 | assert len(out_size) == 3 76 | for k in range(3): 77 | assert isinstance(out_size[k], int) 78 | out_x, out_y, out_z = out_size 79 | 80 | num_rois = rois.shape[0] 81 | num_channels = pts_feature.shape[-1] 82 | num_pts = pts.shape[0] 83 | 84 | pooled_features = pts_feature.new_zeros((num_rois, out_x, out_y, out_z, num_channels)) 85 | argmax = pts_feature.new_zeros((num_rois, out_x, out_y, out_z, num_channels), dtype=torch.int) 86 | pts_idx_of_voxels = pts_feature.new_zeros((num_rois, out_x, out_y, out_z, max_pts_each_voxel), dtype=torch.int) 87 | 88 | pool_method_map = {'max': 0, 'avg': 1} 89 | pool_method = pool_method_map[pool_method] 90 | roiaware_pool3d_cuda.forward(rois, pts, pts_feature, argmax, pts_idx_of_voxels, pooled_features, pool_method) 91 | 92 | ctx.roiaware_pool3d_for_backward = (pts_idx_of_voxels, argmax, pool_method, num_pts, num_channels) 93 | return pooled_features 94 | 95 | @staticmethod 96 | def backward(ctx, grad_out): 97 | """ 98 | :param grad_out: (N, out_x, out_y, out_z, C) 99 | :return: 100 | grad_in: (npoints, C) 101 | """ 102 | pts_idx_of_voxels, argmax, pool_method, num_pts, num_channels = ctx.roiaware_pool3d_for_backward 103 | 104 | grad_in = grad_out.new_zeros((num_pts, num_channels)) 105 | roiaware_pool3d_cuda.backward(pts_idx_of_voxels, argmax, grad_out.contiguous(), grad_in, pool_method) 106 | 107 | return None, None, grad_in, None, None, None 108 | 109 | 110 | if __name__ == '__main__': 111 | pass 112 | -------------------------------------------------------------------------------- /pcdet/ops/pointnet2/pointnet2_stack/voxel_query_utils.py: -------------------------------------------------------------------------------- 1 | import torch 2 | from torch.autograd import Variable 3 | from torch.autograd import Function 4 | import torch.nn as nn 5 | from typing import List 6 | 7 | from . import pointnet2_stack_cuda as pointnet2 8 | from . import pointnet2_utils 9 | 10 | class VoxelQuery(Function): 11 | 12 | @staticmethod 13 | def forward(ctx, max_range: int, radius: float, nsample: int, xyz: torch.Tensor, \ 14 | new_xyz: torch.Tensor, new_coords: torch.Tensor, point_indices: torch.Tensor): 15 | """ 16 | Args: 17 | ctx: 18 | max_range: int, max range of voxels to be grouped 19 | nsample: int, maximum number of features in the balls 20 | new_coords: (M1 + M2, 4), [batch_id, z, y, x] cooridnates of keypoints 21 | new_xyz_batch_cnt: (batch_size), [M1, M2, ...] 22 | point_indices: (batch_size, Z, Y, X) 4-D tensor recording the point indices of voxels 23 | Returns: 24 | idx: (M1 + M2, nsample) tensor with the indicies of the features that form the query balls 25 | """ 26 | assert new_xyz.is_contiguous() 27 | assert xyz.is_contiguous() 28 | assert new_coords.is_contiguous() 29 | assert point_indices.is_contiguous() 30 | 31 | M = new_coords.shape[0] 32 | B, Z, Y, X = point_indices.shape 33 | idx = torch.cuda.IntTensor(M, nsample).zero_() 34 | 35 | z_range, y_range, x_range = max_range 36 | pointnet2.voxel_query_wrapper(M, Z, Y, X, nsample, radius, z_range, y_range, x_range, \ 37 | new_xyz, xyz, new_coords, point_indices, idx) 38 | 39 | empty_ball_mask = (idx[:, 0] == -1) 40 | idx[empty_ball_mask] = 0 41 | 42 | return idx, empty_ball_mask 43 | 44 | @staticmethod 45 | def backward(ctx, a=None): 46 | return None, None, None, None 47 | 48 | voxel_query = VoxelQuery.apply 49 | 50 | 51 | class VoxelQueryAndGrouping(nn.Module): 52 | def __init__(self, max_range: int, radius: float, nsample: int): 53 | """ 54 | Args: 55 | radius: float, radius of ball 56 | nsample: int, maximum number of features to gather in the ball 57 | """ 58 | super().__init__() 59 | self.max_range, self.radius, self.nsample = max_range, radius, nsample 60 | 61 | def forward(self, new_coords: torch.Tensor, xyz: torch.Tensor, xyz_batch_cnt: torch.Tensor, 62 | new_xyz: torch.Tensor, new_xyz_batch_cnt: torch.Tensor, 63 | features: torch.Tensor, voxel2point_indices: torch.Tensor): 64 | """ 65 | Args: 66 | new_coords: (M1 + M2 ..., 3) centers voxel indices of the ball query 67 | xyz: (N1 + N2 ..., 3) xyz coordinates of the features 68 | xyz_batch_cnt: (batch_size), [N1, N2, ...] 69 | new_xyz: (M1 + M2 ..., 3) centers of the ball query 70 | new_xyz_batch_cnt: (batch_size), [M1, M2, ...] 71 | features: (N1 + N2 ..., C) tensor of features to group 72 | voxel2point_indices: (B, Z, Y, X) tensor of points indices of voxels 73 | 74 | Returns: 75 | new_features: (M1 + M2, C, nsample) tensor 76 | """ 77 | assert xyz.shape[0] == xyz_batch_cnt.sum(), 'xyz: %s, xyz_batch_cnt: %s' % (str(xyz.shape), str(new_xyz_batch_cnt)) 78 | assert new_coords.shape[0] == new_xyz_batch_cnt.sum(), \ 79 | 'new_coords: %s, new_xyz_batch_cnt: %s' % (str(new_coords.shape), str(new_xyz_batch_cnt)) 80 | batch_size = xyz_batch_cnt.shape[0] 81 | 82 | # idx: (M1 + M2 ..., nsample), empty_ball_mask: (M1 + M2 ...) 83 | idx1, empty_ball_mask1 = voxel_query(self.max_range, self.radius, self.nsample, xyz, new_xyz, new_coords, voxel2point_indices) 84 | 85 | idx1 = idx1.view(batch_size, -1, self.nsample) 86 | count = 0 87 | for bs_idx in range(batch_size): 88 | idx1[bs_idx] -= count 89 | count += xyz_batch_cnt[bs_idx] 90 | idx1 = idx1.view(-1, self.nsample) 91 | idx1[empty_ball_mask1] = 0 92 | 93 | idx = idx1 94 | empty_ball_mask = empty_ball_mask1 95 | 96 | grouped_xyz = pointnet2_utils.grouping_operation(xyz, xyz_batch_cnt, idx, new_xyz_batch_cnt) 97 | # grouped_features: (M1 + M2, C, nsample) 98 | grouped_features = pointnet2_utils.grouping_operation(features, xyz_batch_cnt, idx, new_xyz_batch_cnt) 99 | 100 | return grouped_features, grouped_xyz, empty_ball_mask 101 | -------------------------------------------------------------------------------- /tools/train_utils/optimization/learning_schedules_fastai.py: -------------------------------------------------------------------------------- 1 | # This file is modified from https://github.com/traveller59/second.pytorch 2 | 3 | import math 4 | from functools import partial 5 | 6 | import numpy as np 7 | import torch.optim.lr_scheduler as lr_sched 8 | 9 | from .fastai_optim import OptimWrapper 10 | 11 | 12 | class LRSchedulerStep(object): 13 | def __init__(self, fai_optimizer: OptimWrapper, total_step, lr_phases, 14 | mom_phases): 15 | # if not isinstance(fai_optimizer, OptimWrapper): 16 | # raise TypeError('{} is not a fastai OptimWrapper'.format( 17 | # type(fai_optimizer).__name__)) 18 | self.optimizer = fai_optimizer 19 | self.total_step = total_step 20 | self.lr_phases = [] 21 | 22 | for i, (start, lambda_func) in enumerate(lr_phases): 23 | if len(self.lr_phases) != 0: 24 | assert self.lr_phases[-1][0] < start 25 | if isinstance(lambda_func, str): 26 | lambda_func = eval(lambda_func) 27 | if i < len(lr_phases) - 1: 28 | self.lr_phases.append((int(start * total_step), int(lr_phases[i + 1][0] * total_step), lambda_func)) 29 | else: 30 | self.lr_phases.append((int(start * total_step), total_step, lambda_func)) 31 | assert self.lr_phases[0][0] == 0 32 | self.mom_phases = [] 33 | for i, (start, lambda_func) in enumerate(mom_phases): 34 | if len(self.mom_phases) != 0: 35 | assert self.mom_phases[-1][0] < start 36 | if isinstance(lambda_func, str): 37 | lambda_func = eval(lambda_func) 38 | if i < len(mom_phases) - 1: 39 | self.mom_phases.append((int(start * total_step), int(mom_phases[i + 1][0] * total_step), lambda_func)) 40 | else: 41 | self.mom_phases.append((int(start * total_step), total_step, lambda_func)) 42 | assert self.mom_phases[0][0] == 0 43 | 44 | def step(self, step): 45 | for start, end, func in self.lr_phases: 46 | if step >= start: 47 | self.optimizer.lr = func((step - start) / (end - start)) 48 | for start, end, func in self.mom_phases: 49 | if step >= start: 50 | self.optimizer.mom = func((step - start) / (end - start)) 51 | 52 | 53 | def annealing_cos(start, end, pct): 54 | # print(pct, start, end) 55 | "Cosine anneal from `start` to `end` as pct goes from 0.0 to 1.0." 56 | cos_out = np.cos(np.pi * pct) + 1 57 | return end + (start - end) / 2 * cos_out 58 | 59 | 60 | class OneCycle(LRSchedulerStep): 61 | def __init__(self, fai_optimizer, total_step, lr_max, moms, div_factor, 62 | pct_start): 63 | self.lr_max = lr_max 64 | self.moms = moms 65 | self.div_factor = div_factor 66 | self.pct_start = pct_start 67 | a1 = int(total_step * self.pct_start) 68 | a2 = total_step - a1 69 | low_lr = self.lr_max / self.div_factor 70 | lr_phases = ((0, partial(annealing_cos, low_lr, self.lr_max)), 71 | (self.pct_start, 72 | partial(annealing_cos, self.lr_max, low_lr / 1e4))) 73 | mom_phases = ((0, partial(annealing_cos, *self.moms)), 74 | (self.pct_start, partial(annealing_cos, 75 | *self.moms[::-1]))) 76 | fai_optimizer.lr, fai_optimizer.mom = low_lr, self.moms[0] 77 | super().__init__(fai_optimizer, total_step, lr_phases, mom_phases) 78 | 79 | 80 | class CosineWarmupLR(lr_sched._LRScheduler): 81 | def __init__(self, optimizer, T_max, eta_min=0, last_epoch=-1): 82 | self.T_max = T_max 83 | self.eta_min = eta_min 84 | super(CosineWarmupLR, self).__init__(optimizer, last_epoch) 85 | 86 | def get_lr(self): 87 | return [self.eta_min + (base_lr - self.eta_min) * 88 | (1 - math.cos(math.pi * self.last_epoch / self.T_max)) / 2 89 | for base_lr in self.base_lrs] 90 | 91 | 92 | class FakeOptim: 93 | def __init__(self): 94 | self.lr = 0 95 | self.mom = 0 96 | 97 | 98 | if __name__ == "__main__": 99 | import matplotlib.pyplot as plt 100 | 101 | opt = FakeOptim() # 3e-3, wd=0.4, div_factor=10 102 | schd = OneCycle(opt, 100, 3e-3, (0.95, 0.85), 10.0, 0.1) 103 | 104 | lrs = [] 105 | moms = [] 106 | for i in range(100): 107 | schd.step(i) 108 | lrs.append(opt.lr) 109 | moms.append(opt.mom) 110 | plt.plot(lrs) 111 | # plt.plot(moms) 112 | plt.show() 113 | plt.plot(moms) 114 | plt.show() 115 | -------------------------------------------------------------------------------- /tools/cfgs/kitti_models/3dssd.yaml: -------------------------------------------------------------------------------- 1 | CLASS_NAMES: ['Car'] 2 | 3 | DATA_CONFIG: 4 | _BASE_CONFIG_: cfgs/dataset_configs/kitti_dataset.yaml 5 | DATA_AUGMENTOR: 6 | DISABLE_AUG_LIST: ['placeholder'] 7 | AUG_CONFIG_LIST: 8 | - NAME: gt_sampling 9 | BACKEND: 10 | NAME: HardDiskBackend 11 | KWARGS: { 12 | 13 | } 14 | USE_ROAD_PLANE: True 15 | DB_INFO_PATH: 16 | - kitti_dbinfos_train.pkl 17 | PREPARE: { 18 | filter_by_min_points: ['Car:5'], 19 | filter_by_difficulty: [-1], 20 | } 21 | 22 | SAMPLE_GROUPS: ['Car:15'] 23 | NUM_POINT_FEATURES: 4 24 | DATABASE_WITH_FAKELIDAR: False 25 | REMOVE_EXTRA_WIDTH: [0.0, 0.0, 0.0] 26 | LIMIT_WHOLE_SCENE: True 27 | 28 | - NAME: random_world_flip 29 | PROBABILITY: 0.5 30 | ALONG_AXIS_LIST: ['x'] 31 | 32 | - NAME: random_world_rotation 33 | PROBABILITY: 1.0 34 | WORLD_ROT_ANGLE: [-0.78539816, 0.78539816] 35 | 36 | - NAME: random_world_scaling 37 | PROBABILITY: 1.0 38 | WORLD_SCALE_RANGE: [0.9, 1.1] 39 | 40 | DATA_PROCESSOR: 41 | - NAME: mask_points_and_boxes_outside_range 42 | REMOVE_OUTSIDE_BOXES: True 43 | 44 | - NAME: sample_points 45 | NUM_POINTS: { 46 | 'train': 16384, 47 | 'test': 16384 48 | } 49 | 50 | - NAME: shuffle_points 51 | SHUFFLE_ENABLED: { 52 | 'train': True, 53 | 'test': False 54 | } 55 | 56 | MODEL: 57 | NAME: SSD3D 58 | 59 | BACKBONE_3D: 60 | NAME: PointNet2SAMSG 61 | SA_CONFIG: 62 | NPOINTS: [[4096], [512, 512], [256, 256]] 63 | SAMPLE_RANGE: [[[0, 16384]], [[0, 4096], [0, 4096]], [[0, 512], [512, 1024]]] 64 | SAMPLE_METHOD: [['d-fps'], ['f-fps', 'd-fps'], ['f-fps', 'd-fps']] 65 | RADIUS: [[0.2, 0.4, 0.8], [0.4, 0.8, 1.6], [1.6, 3.2, 4.8]] 66 | NSAMPLE: [[32, 32, 64], [32, 32, 64], [32, 32, 64]] 67 | MLPS: [[[16, 16, 32], [16, 16, 32], [32, 32, 64]], 68 | [[64, 64, 128], [64, 64, 128], [64, 96, 128]], 69 | [[128, 128, 256], [128, 192, 256], [128, 256, 256]]] 70 | AGGREGATION_MLPS: [[64], [128], [256]] 71 | WEIGHT_GAMMA: 1.0 72 | DILATED_RADIUS_GROUP: True 73 | 74 | POINT_HEAD: 75 | NAME: PointHeadVote 76 | CLASS_AGNOSTIC: False 77 | 78 | VOTE_CONFIG: 79 | SAMPLE_RANGE: [0, 256] 80 | VOTE_XYZ_RANGE: [3.0, 3.0, 2.0] 81 | MLPS: [128] 82 | 83 | VOTE_SA_CONFIG: 84 | RADIUS: [4.8, 6.4] 85 | NSAMPLE: [48, 64] 86 | MLPS: [[256, 256, 512], [256, 512, 1024]] 87 | 88 | SHARED_FC: [512, 256] 89 | CLS_FC: [128] 90 | REG_FC: [128] 91 | 92 | TARGET_CONFIG: 93 | GT_EXTRA_WIDTH: [0.1, 0.1, 0.1] 94 | GT_CENTRAL_RADIUS: 10.0 95 | AUX_GT_EXTRA_WIDTH: [1.0, 1.0, 1.0] 96 | BOX_CODER: PointBinResidualCoder 97 | BOX_CODER_CONFIG: { 98 | 'angle_bin_num': 12, 99 | 'use_mean_size': False, 100 | 'mean_size': [ 101 | [3.9, 1.6, 1.56] 102 | ] 103 | } 104 | 105 | LOSS_CONFIG: 106 | PREDICT_BOXES: True 107 | CLS_POS_NORM: False 108 | AUX_CLS_POS_NORM: False 109 | CLS_LOSS: WeightedBinaryCrossEntropyLossWithCenterness 110 | AUX_CLS_LOSS: WeightedBinaryCrossEntropyLoss 111 | REG_LOSS: WeightedSmoothL1Loss 112 | CORNER_LOSS_TYPE: 1 113 | LOSS_WEIGHTS: { 114 | 'aux_cls_weight_list': [], 115 | 'vote_cls_weight': 1.0, 116 | 'vote_reg_weight': 1.0, 117 | 'seed_reg_weight_list': [1.0], 118 | 'vote_corner_weight': 1.0 119 | } 120 | 121 | POST_PROCESSING: 122 | RECALL_THRESH_LIST: [0.3, 0.5, 0.7] 123 | SCORE_THRESH: 0.1 124 | OUTPUT_RAW_SCORE: False 125 | 126 | EVAL_METRIC: kitti 127 | 128 | NMS_CONFIG: 129 | MULTI_CLASSES_NMS: False 130 | NMS_TYPE: nms_gpu 131 | NMS_THRESH: 0.01 132 | NMS_PRE_MAXSIZE: 4096 133 | NMS_POST_MAXSIZE: 500 134 | 135 | 136 | OPTIMIZATION: 137 | BATCH_SIZE_PER_GPU: 4 138 | NUM_EPOCHS: 80 139 | 140 | OPTIMIZER: adam_onecycle 141 | LR: 0.01 142 | WEIGHT_DECAY: 0.01 143 | MOMENTUM: 0.9 144 | 145 | MOMS: [0.95, 0.85] 146 | PCT_START: 0.4 147 | DIV_FACTOR: 10 148 | DECAY_STEP_LIST: [35, 45] 149 | LR_DECAY: 0.1 150 | LR_CLIP: 0.0000001 151 | 152 | LR_WARMUP: False 153 | WARMUP_EPOCH: 1 154 | 155 | GRAD_NORM_CLIP: 10 156 | -------------------------------------------------------------------------------- /pcdet/models/backbones_2d/base_bev_backbone.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | import torch 3 | import torch.nn as nn 4 | 5 | 6 | class BaseBEVBackbone(nn.Module): 7 | def __init__(self, model_cfg, input_channels): 8 | super().__init__() 9 | self.model_cfg = model_cfg 10 | 11 | if self.model_cfg.get('LAYER_NUMS', None) is not None: 12 | assert len(self.model_cfg.LAYER_NUMS) == len(self.model_cfg.LAYER_STRIDES) == len(self.model_cfg.NUM_FILTERS) 13 | layer_nums = self.model_cfg.LAYER_NUMS 14 | layer_strides = self.model_cfg.LAYER_STRIDES 15 | num_filters = self.model_cfg.NUM_FILTERS 16 | else: 17 | layer_nums = layer_strides = num_filters = [] 18 | 19 | if self.model_cfg.get('UPSAMPLE_STRIDES', None) is not None: 20 | assert len(self.model_cfg.UPSAMPLE_STRIDES) == len(self.model_cfg.NUM_UPSAMPLE_FILTERS) 21 | num_upsample_filters = self.model_cfg.NUM_UPSAMPLE_FILTERS 22 | upsample_strides = self.model_cfg.UPSAMPLE_STRIDES 23 | else: 24 | upsample_strides = num_upsample_filters = [] 25 | 26 | num_levels = len(layer_nums) 27 | c_in_list = [input_channels, *num_filters[:-1]] 28 | self.blocks = nn.ModuleList() 29 | self.deblocks = nn.ModuleList() 30 | for idx in range(num_levels): 31 | cur_layers = [ 32 | nn.ZeroPad2d(1), 33 | nn.Conv2d( 34 | c_in_list[idx], num_filters[idx], kernel_size=3, 35 | stride=layer_strides[idx], padding=0, bias=False 36 | ), 37 | nn.BatchNorm2d(num_filters[idx], eps=1e-3, momentum=0.01), 38 | nn.ReLU() 39 | ] 40 | for k in range(layer_nums[idx]): 41 | cur_layers.extend([ 42 | nn.Conv2d(num_filters[idx], num_filters[idx], kernel_size=3, padding=1, bias=False), 43 | nn.BatchNorm2d(num_filters[idx], eps=1e-3, momentum=0.01), 44 | nn.ReLU() 45 | ]) 46 | self.blocks.append(nn.Sequential(*cur_layers)) 47 | if len(upsample_strides) > 0: 48 | stride = upsample_strides[idx] 49 | if stride >= 1: 50 | self.deblocks.append(nn.Sequential( 51 | nn.ConvTranspose2d( 52 | num_filters[idx], num_upsample_filters[idx], 53 | upsample_strides[idx], 54 | stride=upsample_strides[idx], bias=False 55 | ), 56 | nn.BatchNorm2d(num_upsample_filters[idx], eps=1e-3, momentum=0.01), 57 | nn.ReLU() 58 | )) 59 | else: 60 | stride = np.round(1 / stride).astype(np.int) 61 | self.deblocks.append(nn.Sequential( 62 | nn.Conv2d( 63 | num_filters[idx], num_upsample_filters[idx], 64 | stride, 65 | stride=stride, bias=False 66 | ), 67 | nn.BatchNorm2d(num_upsample_filters[idx], eps=1e-3, momentum=0.01), 68 | nn.ReLU() 69 | )) 70 | 71 | c_in = sum(num_upsample_filters) 72 | if len(upsample_strides) > num_levels: 73 | self.deblocks.append(nn.Sequential( 74 | nn.ConvTranspose2d(c_in, c_in, upsample_strides[-1], stride=upsample_strides[-1], bias=False), 75 | nn.BatchNorm2d(c_in, eps=1e-3, momentum=0.01), 76 | nn.ReLU(), 77 | )) 78 | 79 | self.num_bev_features = c_in 80 | 81 | def forward(self, data_dict): 82 | """ 83 | Args: 84 | data_dict: 85 | spatial_features 86 | Returns: 87 | """ 88 | spatial_features = data_dict['spatial_features'] 89 | ups = [] 90 | ret_dict = {} 91 | x = spatial_features 92 | for i in range(len(self.blocks)): 93 | x = self.blocks[i](x) 94 | 95 | stride = int(spatial_features.shape[2] / x.shape[2]) 96 | ret_dict['spatial_features_%dx' % stride] = x 97 | if len(self.deblocks) > 0: 98 | ups.append(self.deblocks[i](x)) 99 | else: 100 | ups.append(x) 101 | 102 | if len(ups) > 1: 103 | x = torch.cat(ups, dim=1) 104 | elif len(ups) == 1: 105 | x = ups[0] 106 | 107 | if len(self.deblocks) > len(self.blocks): 108 | x = self.deblocks[-1](x) 109 | 110 | data_dict['spatial_features_2d'] = x 111 | 112 | return data_dict 113 | -------------------------------------------------------------------------------- /pcdet/utils/calibration_kitti.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | 3 | 4 | def get_calib_from_file(calib_file, client): 5 | with client.get_local_path(calib_file) as path: 6 | lines = open(path).readlines() 7 | 8 | obj = lines[2].strip().split(' ')[1:] 9 | P2 = np.array(obj, dtype=np.float32) 10 | obj = lines[3].strip().split(' ')[1:] 11 | P3 = np.array(obj, dtype=np.float32) 12 | obj = lines[4].strip().split(' ')[1:] 13 | R0 = np.array(obj, dtype=np.float32) 14 | obj = lines[5].strip().split(' ')[1:] 15 | Tr_velo_to_cam = np.array(obj, dtype=np.float32) 16 | 17 | return {'P2': P2.reshape(3, 4), 18 | 'P3': P3.reshape(3, 4), 19 | 'R0': R0.reshape(3, 3), 20 | 'Tr_velo2cam': Tr_velo_to_cam.reshape(3, 4)} 21 | 22 | 23 | class Calibration(object): 24 | def __init__(self, calib_file, client): 25 | if not isinstance(calib_file, dict): 26 | calib = get_calib_from_file(calib_file, client) 27 | else: 28 | calib = calib_file 29 | 30 | self.P2 = calib['P2'] # 3 x 4 31 | self.R0 = calib['R0'] # 3 x 3 32 | self.V2C = calib['Tr_velo2cam'] # 3 x 4 33 | 34 | # Camera intrinsics and extrinsics 35 | self.cu = self.P2[0, 2] 36 | self.cv = self.P2[1, 2] 37 | self.fu = self.P2[0, 0] 38 | self.fv = self.P2[1, 1] 39 | self.tx = self.P2[0, 3] / (-self.fu) 40 | self.ty = self.P2[1, 3] / (-self.fv) 41 | 42 | def cart_to_hom(self, pts): 43 | """ 44 | :param pts: (N, 3 or 2) 45 | :return pts_hom: (N, 4 or 3) 46 | """ 47 | pts_hom = np.hstack((pts, np.ones((pts.shape[0], 1), dtype=np.float32))) 48 | return pts_hom 49 | 50 | def rect_to_lidar(self, pts_rect): 51 | """ 52 | :param pts_lidar: (N, 3) 53 | :return pts_rect: (N, 3) 54 | """ 55 | pts_rect_hom = self.cart_to_hom(pts_rect) # (N, 4) 56 | R0_ext = np.hstack((self.R0, np.zeros((3, 1), dtype=np.float32))) # (3, 4) 57 | R0_ext = np.vstack((R0_ext, np.zeros((1, 4), dtype=np.float32))) # (4, 4) 58 | R0_ext[3, 3] = 1 59 | V2C_ext = np.vstack((self.V2C, np.zeros((1, 4), dtype=np.float32))) # (4, 4) 60 | V2C_ext[3, 3] = 1 61 | 62 | pts_lidar = np.dot(pts_rect_hom, np.linalg.inv(np.dot(R0_ext, V2C_ext).T)) 63 | return pts_lidar[:, 0:3] 64 | 65 | def lidar_to_rect(self, pts_lidar): 66 | """ 67 | :param pts_lidar: (N, 3) 68 | :return pts_rect: (N, 3) 69 | """ 70 | pts_lidar_hom = self.cart_to_hom(pts_lidar) 71 | pts_rect = np.dot(pts_lidar_hom, np.dot(self.V2C.T, self.R0.T)) 72 | # pts_rect = reduce(np.dot, (pts_lidar_hom, self.V2C.T, self.R0.T)) 73 | return pts_rect 74 | 75 | def rect_to_img(self, pts_rect): 76 | """ 77 | :param pts_rect: (N, 3) 78 | :return pts_img: (N, 2) 79 | """ 80 | pts_rect_hom = self.cart_to_hom(pts_rect) 81 | pts_2d_hom = np.dot(pts_rect_hom, self.P2.T) 82 | pts_img = (pts_2d_hom[:, 0:2].T / pts_rect_hom[:, 2]).T # (N, 2) 83 | pts_rect_depth = pts_2d_hom[:, 2] - self.P2.T[3, 2] # depth in rect camera coord 84 | return pts_img, pts_rect_depth 85 | 86 | def lidar_to_img(self, pts_lidar): 87 | """ 88 | :param pts_lidar: (N, 3) 89 | :return pts_img: (N, 2) 90 | """ 91 | pts_rect = self.lidar_to_rect(pts_lidar) 92 | pts_img, pts_depth = self.rect_to_img(pts_rect) 93 | return pts_img, pts_depth 94 | 95 | def img_to_rect(self, u, v, depth_rect): 96 | """ 97 | :param u: (N) 98 | :param v: (N) 99 | :param depth_rect: (N) 100 | :return: 101 | """ 102 | x = ((u - self.cu) * depth_rect) / self.fu + self.tx 103 | y = ((v - self.cv) * depth_rect) / self.fv + self.ty 104 | pts_rect = np.concatenate((x.reshape(-1, 1), y.reshape(-1, 1), depth_rect.reshape(-1, 1)), axis=1) 105 | return pts_rect 106 | 107 | def corners3d_to_img_boxes(self, corners3d): 108 | """ 109 | :param corners3d: (N, 8, 3) corners in rect coordinate 110 | :return: boxes: (None, 4) [x1, y1, x2, y2] in rgb coordinate 111 | :return: boxes_corner: (None, 8) [xi, yi] in rgb coordinate 112 | """ 113 | sample_num = corners3d.shape[0] 114 | corners3d_hom = np.concatenate((corners3d, np.ones((sample_num, 8, 1))), axis=2) # (N, 8, 4) 115 | 116 | img_pts = np.matmul(corners3d_hom, self.P2.T) # (N, 8, 3) 117 | 118 | x, y = img_pts[:, :, 0] / img_pts[:, :, 2], img_pts[:, :, 1] / img_pts[:, :, 2] 119 | x1, y1 = np.min(x, axis=1), np.min(y, axis=1) 120 | x2, y2 = np.max(x, axis=1), np.max(y, axis=1) 121 | 122 | boxes = np.concatenate((x1.reshape(-1, 1), y1.reshape(-1, 1), x2.reshape(-1, 1), y2.reshape(-1, 1)), axis=1) 123 | boxes_corner = np.concatenate((x.reshape(-1, 8, 1), y.reshape(-1, 8, 1)), axis=2) 124 | 125 | return boxes, boxes_corner 126 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | import os 2 | import subprocess 3 | 4 | from setuptools import find_packages, setup 5 | from torch.utils.cpp_extension import BuildExtension, CUDAExtension 6 | 7 | 8 | def get_git_commit_number(): 9 | if not os.path.exists('.git'): 10 | return '0000000' 11 | 12 | cmd_out = subprocess.run(['git', 'rev-parse', 'HEAD'], stdout=subprocess.PIPE) 13 | git_commit_number = cmd_out.stdout.decode('utf-8')[:7] 14 | return git_commit_number 15 | 16 | 17 | def make_cuda_ext(name, module, sources): 18 | cuda_ext = CUDAExtension( 19 | name='%s.%s' % (module, name), 20 | sources=[os.path.join(*module.split('.'), src) for src in sources] 21 | ) 22 | return cuda_ext 23 | 24 | 25 | def write_version_to_file(version, target_file): 26 | with open(target_file, 'w') as f: 27 | print('__version__ = "%s"' % version, file=f) 28 | 29 | 30 | if __name__ == '__main__': 31 | version = '0.5.1+%s' % get_git_commit_number() 32 | write_version_to_file(version, 'pcdet/version.py') 33 | 34 | setup( 35 | name='gd-mae', 36 | version=version, 37 | install_requires=[ 38 | 'numpy', 39 | 'llvmlite', 40 | 'numba', 41 | 'tensorboardX', 42 | 'easydict', 43 | 'pyyaml', 44 | 'tqdm', 45 | 'SharedArray', 46 | 'pycocotools', 47 | 'terminaltables', 48 | 'einops', 49 | 'timm' 50 | ], 51 | 52 | author='Honghui Yang', 53 | author_email='yanghonghui@zju.edu.cn', 54 | license='Apache License 2.0', 55 | packages=find_packages(exclude=['tools', 'data', 'output']), 56 | cmdclass={ 57 | 'build_ext': BuildExtension, 58 | }, 59 | ext_modules=[ 60 | make_cuda_ext( 61 | name='iou3d_nms_cuda', 62 | module='pcdet.ops.iou3d_nms', 63 | sources=[ 64 | 'src/iou3d_cpu.cpp', 65 | 'src/iou3d_nms_api.cpp', 66 | 'src/iou3d_nms.cpp', 67 | 'src/iou3d_nms_kernel.cu', 68 | ] 69 | ), 70 | make_cuda_ext( 71 | name='roiaware_pool3d_cuda', 72 | module='pcdet.ops.roiaware_pool3d', 73 | sources=[ 74 | 'src/roiaware_pool3d.cpp', 75 | 'src/roiaware_pool3d_kernel.cu', 76 | ] 77 | ), 78 | make_cuda_ext( 79 | name='roipoint_pool3d_cuda', 80 | module='pcdet.ops.roipoint_pool3d', 81 | sources=[ 82 | 'src/roipoint_pool3d.cpp', 83 | 'src/roipoint_pool3d_kernel.cu', 84 | ] 85 | ), 86 | make_cuda_ext( 87 | name='pointnet2_stack_cuda', 88 | module='pcdet.ops.pointnet2.pointnet2_stack', 89 | sources=[ 90 | 'src/pointnet2_api.cpp', 91 | 'src/ball_query.cpp', 92 | 'src/ball_query_gpu.cu', 93 | 'src/group_points.cpp', 94 | 'src/group_points_gpu.cu', 95 | 'src/sampling.cpp', 96 | 'src/sampling_gpu.cu', 97 | 'src/interpolate.cpp', 98 | 'src/interpolate_gpu.cu', 99 | 'src/voxel_query.cpp', 100 | 'src/voxel_query_gpu.cu', 101 | ], 102 | ), 103 | make_cuda_ext( 104 | name='pointnet2_batch_cuda', 105 | module='pcdet.ops.pointnet2.pointnet2_batch', 106 | sources=[ 107 | 'src/pointnet2_api.cpp', 108 | 'src/ball_query.cpp', 109 | 'src/ball_query_gpu.cu', 110 | 'src/group_points.cpp', 111 | 'src/group_points_gpu.cu', 112 | 'src/interpolate.cpp', 113 | 'src/interpolate_gpu.cu', 114 | 'src/sampling.cpp', 115 | 'src/sampling_gpu.cu', 116 | 117 | ], 118 | ), 119 | make_cuda_ext( 120 | name='patch_ops_cuda', 121 | module='pcdet.ops.patch_ops', 122 | sources=[ 123 | 'src/patch_ops_api.cpp', 124 | 'src/patch_query.cpp', 125 | 'src/patch_query_gpu.cu', 126 | 'src/roipatch_dfvs_pool3d.cpp', 127 | 'src/roipatch_dfvs_pool3d_gpu.cu', 128 | ], 129 | ), 130 | make_cuda_ext( 131 | name='sst_ops_cuda', 132 | module='pcdet.ops.sst_ops', 133 | sources=[ 134 | 'src/sst_ops_api.cpp', 135 | 'src/sst_ops.cpp', 136 | 'src/sst_ops_gpu.cu' 137 | ] 138 | ), 139 | ], 140 | ) 141 | --------------------------------------------------------------------------------