OpenPCDet自定义数据集训练(非KITTI格式)

前言:

本文的想法来自于https://github.com/open-mmlab/OpenPCDet/issues/771该讨论区下155cannon大佬的想法,但是在训练的最后一步一直出错,希望有大佬可以指点一下,或者遇到同样的问题可以帮助一下,在下不胜感激。

一、获取数据集

根据自己采集的PCD点云,使用cloudcompare里面的cross section功能,把目标物体的检测框标注出来,在属性栏获取他的中心点坐标和长宽高。

点云文件0000.pcd

标签文件0000.txt  每行x, y, z, dx, dy, dz就是上面说的那6个值,按顺序放,heading统一设为1.57,也就是90°。

二、训练准备阶段

先自己创建一个my_dataset.py,此文件和kitti_dataset.py放在同一目录下。

import glob
import numpy as np
from pathlib import Path
from ..dataset import DatasetTemplate

class MyDataset(DatasetTemplate):
    def __init__(self,dataset_cfg,class_names,training=True,root_path=None,logger=None):
        """
        Args:
            root_path:
            dataset_cfg:
            class_names:
            training:
            logger:
        """
        super().__init__(dataset_cfg=dataset_cfg,class_names=class_names,training=training,root_path=root_path,logger=logger)
        point_file_list=glob.glob(str(self.root_path/'training/points/*.bin'))
        labels_file_list=glob.glob(str(self.root_path/'training/labels/*.txt'))
        point_file_list.sort()
        labels_file_list.sort()
        self.sample_file_list=point_file_list
        self.samplelabel_file_list=labels_file_list

    def __len__(self):
        return len(self.sample_file_list)

    def __getitem__(self, index):
        #stem返回的不是整个路径,而是文件名
        sample_idx=Path(self.sample_file_list[index]).stem
        points=np.fromfile(self.sample_file_list[index],dtype=np.float32).reshape(-1,4)[:,:3]
        #在加载点云和标签的同时,将坐标转换成统一坐标系下
        points=points[:,[2,0,1]]
        points[:,0]=-points[:,0]
        points[:,1]=-points[:,1]
        points_label=np.loadtxt(self.samplelabel_file_list[index],dtype=np.float32).reshape(-1,7)
        points_label=points_label[:,[2,0,1,5,3,4,6]]
        gt_names=np.array(['cylinder']*points_label.shape[0])

        input_dict={
            'points':points,
            'frame_id':sample_idx,
            'gt_names':gt_names,
            'gt_boxes':points_label
        }

        data_dict = self.prepare_data(data_dict=input_dict)
        return data_dict

   创建一个my_dataset.yaml,此文件和kitti_dataset.yaml放在同一目录下。

DATASET: 'MyDataset'
DATA_PATH: '../data/cylinder'

POINT_CLOUD_RANGE: [0, -40, -3, 70.4, 40, 1]

DATA_SPLIT: {
    'train': train,
    'test': val
}

INFO_PATH: {
    'train': [kitti_infos_train.pkl],
    'test': [kitti_infos_val.pkl],
}

GET_ITEM_LIST: ["points"]
FOV_POINTS_ONLY: True

DATA_AUGMENTOR:
    DISABLE_AUG_LIST: ['placeholder']
    AUG_CONFIG_LIST:

        - NAME: random_world_flip
          ALONG_AXIS_LIST: ['x']

        - NAME: random_world_rotation
          WORLD_ROT_ANGLE: [-0.78539816, 0.78539816]

        - NAME: random_world_scaling
          WORLD_SCALE_RANGE: [0.95, 1.05]


POINT_FEATURE_ENCODING: {
    encoding_type: absolute_coordinates_encoding,
    used_feature_list: ['x', 'y', 'z'],
    src_feature_list: ['x', 'y', 'z'],
}


DATA_PROCESSOR:
    - NAME: mask_points_and_boxes_outside_range
      REMOVE_OUTSIDE_BOXES: True

    - NAME: shuffle_points
      SHUFFLE_ENABLED: {
        'train': True,
        'test': False
      }

    - NAME: transform_points_to_voxels
      VOXEL_SIZE: [0.05, 0.05, 0.1]
      MAX_POINTS_PER_VOXEL: 5
      MAX_NUMBER_OF_VOXELS: {
        'train': 16000,
        'test': 40000
      }

创建pointpillar.yaml,此文件放置在tools\cfgs\cylinder_models下面,去掉了gt_sampling数据增强,需要修改POINT_CLOUD_RANGE和VOXEL_SIZE,POINT_CLOUD_RANGE就是你的点云范围,point cloud range along z-axis / voxel_size is 40,point cloud range along x,y -axis / voxel_size is the multiple of 16. 这是必须满足的设置,不然会报错。

CLASS_NAMES: ['cylinder']

DATA_CONFIG:
    _BASE_CONFIG_: cfgs/dataset_configs/my_dataset.yaml
    POINT_CLOUD_RANGE: [0.9, -2, -2, 3.46, 3.12, 2]
    DATA_PROCESSOR:
        - NAME: mask_points_and_boxes_outside_range
          REMOVE_OUTSIDE_BOXES: True

        - NAME: shuffle_points
          SHUFFLE_ENABLED: {
            'train': True,
            'test': False
          }

        - NAME: transform_points_to_voxels
          VOXEL_SIZE: [0.16, 0.16, 4]
          MAX_POINTS_PER_VOXEL: 32
          MAX_NUMBER_OF_VOXELS: {
            'train': 16000,
            'test': 40000
          }
    DATA_AUGMENTOR:
        DISABLE_AUG_LIST: ['placeholder']
        AUG_CONFIG_LIST:
            - NAME: random_world_flip
              ALONG_AXIS_LIST: ['x']

            - NAME: random_world_rotation
              WORLD_ROT_ANGLE: [-0.78539816, 0.78539816]

            - NAME: random_world_scaling
              WORLD_SCALE_RANGE: [0.95, 1.05]

MODEL:
    NAME: PointPillar

    VFE:
        NAME: PillarVFE
        WITH_DISTANCE: False
        USE_ABSLOTE_XYZ: True
        USE_NORM: True
        NUM_FILTERS: [64]

    MAP_TO_BEV:
        NAME: PointPillarScatter
        NUM_BEV_FEATURES: 64

    BACKBONE_2D:
        NAME: BaseBEVBackbone
        LAYER_NUMS: [3, 5, 5]
        LAYER_STRIDES: [2, 2, 2]
        NUM_FILTERS: [64, 128, 256]
        UPSAMPLE_STRIDES: [1, 2, 4]
        NUM_UPSAMPLE_FILTERS: [128, 128, 128]

    DENSE_HEAD:
        NAME: AnchorHeadSingle
        CLASS_AGNOSTIC: False

        USE_DIRECTION_CLASSIFIER: True
        DIR_OFFSET: 0.78539
        DIR_LIMIT_OFFSET: 0.0
        NUM_DIR_BINS: 2

        ANCHOR_GENERATOR_CONFIG: [
            {
                'class_name': 'cylinder',
                'anchor_sizes': [[0.05, 0.05, 0.14]],
                'anchor_rotations': [0, 1.57],
                'anchor_bottom_heights': [-1.78],
                'align_center': False,
                'feature_map_stride': 2,
                'matched_threshold': 0.6,
                'unmatched_threshold': 0.45
            }
        ]

        TARGET_ASSIGNER_CONFIG:
            NAME: AxisAlignedTargetAssigner
            POS_FRACTION: -1.0
            SAMPLE_SIZE: 512
            NORM_BY_NUM_EXAMPLES: False
            MATCH_HEIGHT: False
            BOX_CODER: ResidualCoder

        LOSS_CONFIG:
            LOSS_WEIGHTS: {
                'cls_weight': 1.0,
                'loc_weight': 2.0,
                'dir_weight': 0.2,
                'code_weights': [1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0]
            }

    POST_PROCESSING:
        RECALL_THRESH_LIST: [0.3, 0.5, 0.7]
        SCORE_THRESH: 0.1
        OUTPUT_RAW_SCORE: False

        EVAL_METRIC: kitti

        NMS_CONFIG:
            MULTI_CLASSES_NMS: False
            NMS_TYPE: nms_gpu
            NMS_THRESH: 0.01
            NMS_PRE_MAXSIZE: 4096
            NMS_POST_MAXSIZE: 500


OPTIMIZATION:
    BATCH_SIZE_PER_GPU: 4
    NUM_EPOCHS: 80

    OPTIMIZER: adam_onecycle
    LR: 0.003
    WEIGHT_DECAY: 0.01
    MOMENTUM: 0.9

    MOMS: [0.95, 0.85]
    PCT_START: 0.4
    DIV_FACTOR: 10
    DECAY_STEP_LIST: [35, 45]
    LR_DECAY: 0.1
    LR_CLIP: 0.0000001

    LR_WARMUP: False
    WARMUP_EPOCH: 1

    GRAD_NORM_CLIP: 10

在pcdet\datasets\_init_.py里面加上写的MyDataset信息:
 

from .kitti.my_dataset import MyDataset
all = {
'DatasetTemplate': DatasetTemplate,
'KittiDataset': KittiDataset,
'MyDataset': MyDataset,
'NuScenesDataset': NuScenesDataset,
'WaymoDataset': WaymoDataset,
'PandasetDataset': PandasetDataset,
'LyftDataset': LyftDataset
}

三、开始训练

python train.py --cfg_file cfgs/cylinder_models/pointpillar.yaml --batch_size 1 --workers 1 --epochs 40

然后就一直报这个错误。麻了,搞了半个多月了都没解决

2022-09-30 08:57:41,568   INFO  **********************Start training cylinder_models/pointpillar(default)**********************
epochs:   0%|                                            | 0/40 [00:05
    main()
  File "train.py", line 171, in main
    merge_all_iters_to_one_epoch=args.merge_all_iters_to_one_epoch
  File "/home/wangchen/OpenPCDet/tools/train_utils/train_utils.py", line 118, in train_model
    dataloader_iter=dataloader_iter
  File "/home/wangchen/OpenPCDet/tools/train_utils/train_utils.py", line 25, in train_one_epoch
    batch = next(dataloader_iter)
  File "/home/wangchen/anaconda3/envs/pcdet/lib/python3.7/site-packages/torch/utils/data/dataloader.py", line 517, in __next__
    data = self._next_data()
  File "/home/wangchen/anaconda3/envs/pcdet/lib/python3.7/site-packages/torch/utils/data/dataloader.py", line 557, in _next_data
    data = self._dataset_fetcher.fetch(index)  # may raise StopIteration
  File "/home/wangchen/anaconda3/envs/pcdet/lib/python3.7/site-packages/torch/utils/data/_utils/fetch.py", line 44, in fetch
    data = [self.dataset[idx] for idx in possibly_batched_index]
  File "/home/wangchen/anaconda3/envs/pcdet/lib/python3.7/site-packages/torch/utils/data/_utils/fetch.py", line 44, in 
    data = [self.dataset[idx] for idx in possibly_batched_index]
  File "../pcdet/datasets/kitti/my_dataset.py", line 49, in __getitem__
    data_dict = self.prepare_data(data_dict=input_dict)
  File "../pcdet/datasets/dataset.py", line 155, in prepare_data
    return self.__getitem__(new_index)
  File "../pcdet/datasets/kitti/my_dataset.py", line 49, in __getitem__
    data_dict = self.prepare_data(data_dict=input_dict)
。。。。。。。。。。。。。。。。。。。。。。。。中间都是重复的
  File "../pcdet/datasets/kitti/my_dataset.py", line 49, in __getitem__
    data_dict = self.prepare_data(data_dict=input_dict)
  File "../pcdet/datasets/dataset.py", line 155, in prepare_data
    return self.__getitem__(new_index)
  File "../pcdet/datasets/kitti/my_dataset.py", line 49, in __getitem__
    data_dict = self.prepare_data(data_dict=input_dict)
  File "../pcdet/datasets/dataset.py", line 155, in prepare_data
    return self.__getitem__(new_index)
  File "../pcdet/datasets/dataset.py", line 155, in prepare_data
    return self.__getitem__(new_index)
  File "../pcdet/datasets/kitti/my_dataset.py", line 49, in __getitem__
    data_dict = self.prepare_data(data_dict=input_dict)
  File "../pcdet/datasets/dataset.py", line 155, in prepare_data
    return self.__getitem__(new_index)
  File "../pcdet/datasets/kitti/my_dataset.py", line 49, in __getitem__
    data_dict = self.prepare_data(data_dict=input_dict)
  File "../pcdet/datasets/dataset.py", line 155, in prepare_data
    return self.__getitem__(new_index)
  File "../pcdet/datasets/kitti/my_dataset.py", line 49, in __getitem__
    data_dict = self.prepare_data(data_dict=input_dict)
  File "../pcdet/datasets/dataset.py", line 131, in prepare_data
    'gt_boxes_mask': gt_boxes_mask
  File "../pcdet/datasets/augmentor/data_augmentor.py", line 240, in forward
    data_dict = cur_augmentor(data_dict=data_dict)
  File "../pcdet/datasets/augmentor/data_augmentor.py", line 50, in random_world_flip
    gt_boxes, points,
  File "../pcdet/datasets/augmentor/augmentor_utils.py", line 15, in random_flip_along_x
    enable = np.random.choice([False, True], replace=False, p=[0.5, 0.5])
  File "mtrand.pyx", line 978, in numpy.random.mtrand.RandomState.choice
  File "mtrand.pyx", line 1179, in numpy.random.mtrand.RandomState.rand
  File "mtrand.pyx", line 425, in numpy.random.mtrand.RandomState.random_sample
  File "_common.pyx", line 295, in numpy.random._common.double_fill
RecursionError: maximum recursion depth exceeded while calling a Python object

四、终于解决了这个报错

在my_dataset.py里面我读取标签的时候坐标转换错了,导致一直不行,现在加了两行,终于ok了,哭了。

points=points[:,[2,0,1]]
points[:,0]=-points[:,0]
points[:,1]=-points[:,1]
points_label=np.loadtxt(self.samplelabel_file_list[index],dtype=np.float32).reshape(-1,7)
points_label=points_label[:,[2,0,1,5,3,4,6]]
points_label[:,0]=-points_label[:,0]
points_label[:,1]=-points_label[:,1]

gt_names=np.array(['cylinder']*points_label.shape[0])

你可能感兴趣的:(深度学习,人工智能)