3D-Dection系列论文1:Pointpillars ---example是如何生成的?

注 : 此 文 是 解 析 的 是 p o i n t p i l l a r 不 是 s e c o n d \color{red}{注:此文是解析的是pointpillar不是second} pointpillarsecond

我们来看一下网络中的example是如何来的~

首先我们看一下example在哪里用到的,然后反推一下:
exaple 使用位置
可以看到exaple是从eval_dataloader迭代器里面进行引用的,这是常规操作,然后看一下eval_dataloader的生成过程:

eval_dataloader = torch.utils.data.DataLoader(
        eval_dataset,
        batch_size=input_cfg.batch_size,
        shuffle=False,
        num_workers=input_cfg.num_workers,
        pin_memory=False,
        collate_fn=merge_second_batch)

然后我们看下eval_dataset是如何来的:

    eval_dataset = input_reader_builder.build(
        input_cfg,
        model_cfg,
        training=False,
        voxel_generator=voxel_generator,
        target_assigner=target_assigner)

这里来进行dataset的建立,我们传入了voxel_generator和target_assigner.
那就先看一下它俩:
1.voxel_generator:通过voxel_builder.build(model_cfg.voxel_generator)来建立,然后调用VoxelGenerator()这个类的构造函数定义一些函数等但是这里面的generate还没被调用,这里面定义好后后面会在input_reader_builder.build()里面用到,最终接收到的是A tensor dict based on the input_reader_config.

class VoxelGenerator:
    def __init__(self,
                 voxel_size,
                 point_cloud_range,
                 max_num_points,
                 max_voxels=20000):
        point_cloud_range = np.array(point_cloud_range, dtype=np.float32)
        # [0, -40, -3, 70.4, 40, 1]
        voxel_size = np.array(voxel_size, dtype=np.float32)
        grid_size = (
            point_cloud_range[3:] - point_cloud_range[:3]) / voxel_size
        grid_size = np.round(grid_size).astype(np.int64)

        self._voxel_size = voxel_size
        self._point_cloud_range = point_cloud_range
        self._max_num_points = max_num_points
        self._max_voxels = max_voxels
        self._grid_size = grid_size

    def generate(self, points, max_voxels):
        return points_to_voxel(
            points, self._voxel_size, self._point_cloud_range,
            self._max_num_points, True, max_voxels)

接着我们进入到:input_reader_builder.build这个函数
2. target_assigner:target_assigner_builder.build(..)建立,
其中box_coder是由 box_coder = box_coder_builder.build(..)建立

    box_coder: {
      ground_box3d_coder: {
        linear_dim: false
        encode_angle_vector: false
      }
    }

定义了一大串函数....
最后进入target_assigner = target_assigner_builder.build(..)调用build函数:

def build(input_reader_config,
          model_config,
          training,
          voxel_generator,
          target_assigner=None) -> DatasetWrapper:
    """Builds a tensor dictionary based on the InputReader config.

    Args:
        input_reader_config: A input_reader_pb2.InputReader object.

    Returns:
        A tensor dict based on the input_reader_config.

    Raises:
        ValueError: On invalid input reader proto.
        ValueError: If no input paths are specified.
    """
    if not isinstance(input_reader_config, input_reader_pb2.InputReader):
        raise ValueError('input_reader_config not of type '
                         'input_reader_pb2.InputReader.')
    dataset = dataset_builder.build(input_reader_config, model_config,
                                    training, voxel_generator, target_assigner)
    dataset = DatasetWrapper(dataset)
    return dataset

同样的主要还是调用:dataset_builder.build()这个函数:先从配置文件读取一些参数:
这里有个重要的函数:prep_func = partial(…)
关于python的偏函数可以看这里:偏函数
prep_pointcloud的这个函数,大多数我们想要的到的example参数使用过这个函数得到的,首先我们传入大多数的参数值,这里面我们会先进入到KittiDataset()这个函数读取我们的配置文件,比如info_path.
1.anchor的生成:
 target_assigner.generate_anchors(feature_map_size)进入的我们的anchor生成,中间步骤挺繁琐…,最终生成1x248x216x2=107136个anchor,每个anchor有7个值,长宽高,中心点,航向,返回的anchors=(1, 248, 216, 2, 7),最后reshape成: (107136, 7)的形式,这里面有bev_anchor的生成,就是在俯视图上划分的anchor,只需要最左上和最右下两个坐标就可以了.
 令大家需要知道的是dataset = KittiDataset(…)这个函数返回的是含有pkl信息的kitti_infos和pre_func函数,这里面还有一个内置函数:

    def __getitem__(self, idx):
        return _read_and_prep_v9(
            info=self._kitti_infos[idx],
            root_path=self._root_path,
            num_point_features=self._num_point_features,
            prep_func=self._prep_func)

实际上我们通过dataloader进行索引的时候最终调用的是这个函数,其实之前的都是在做准备工作,在进行dataloader迭代的时候我们会调用_getitem_这个函数,在里面还会调用到pre_func函数,最终返回一个example.
接下来我们仔细看一下read_and_prep_v9()在干嘛:
1.读取infos里面的velodyne_path的路径;
2.读取点云信息points = np.fromfile()
这时候下面的example这些信息就已经有了,剩余的一些需要生成.

    input_dict = {
        'points': points,
        'rect': rect,
        'Trv2c': Trv2c,
        'P2': P2,
        'image_shape': np.array(info["img_shape"], dtype=np.int32),
        'image_idx': image_idx,
        'image_path': info['img_path'],
         'gt_boxes': gt_boxes,
         'gt_names': gt_names,
         'difficulty': difficulty,
         'group_ids' = group_ids,
        # 'pointcloud_num_features': num_point_features,
    }

3.进入prep_func(input_dict=input_dict)函数,函数说明:convert point cloud to voxels, create targets if ground truths exists.
4.执行voxels, coordinates, num_points = voxel_generator.generate(..)函数.里面执行的是points_to_voxel()这个函数,说明如下:

    Returns:
        voxels: [M, max_points, ndim] float tensor. only contain points.
        coordinates: [M, 3] int32 tensor.
        num_points_per_voxel: [M] int32 tensor
name shape info
voxels [M, max_points, ndim] [voxel_nums,100,4] 含有点云的voxel以及信息
coordinates [M, 3] -> [voxel_nums,3] 每个点的XYZ在map上的索引
num_points_per_voxel [voxel_nums] 每个voxel内点的个数

随后这些信息会增添到example里面.


至此,我们网络需要的example已经完全Done了;
接下来我们进行网络部分的解析:
Pointpillars —数据流动篇

PS: 
我们通过create_data.py得到了各种pkl文件.我们生成的pkl文件有25个属性:{‘image_idx’,‘pointcloud_num_features’,‘velodyne_path’’,img_path’:,‘img_shape’,‘calib/P0/p1/p2/p3/R0_rect_Tr/velo_to_cam/Tr_imu_to_velo’,‘annos’’,‘truncated’,‘occluded’,‘alpha’,‘bbox’,‘dimensions’,‘location’,‘rotation_y’,‘score’,‘index’,‘group_ids’,‘difficulty’,‘num_points_in_gt’}

这里我们可以注意一下下面的操作,为什么要gridsize要除以2得到features呢?:

#grid_size [432,496,1]
#feature_size [1,248,216]
    grid_size = voxel_generator.grid_size
    feature_map_size = grid_size[:2] // out_size_factor
    feature_map_size = [*feature_map_size, 1][::-1]

随后会把之前生成的anchor添加到example里面,anchor的生成还有些没看懂,后面再详细补充, 这里是通过partial函数传入的.
待补充:anchor及anchor_mask的生成过程

这是anchor的一些东西:
读取配置:

    target_assigner: {
      anchor_generators: {
         anchor_generator_stride: {
           sizes: [1.6, 3.9, 1.56] # wlh
           strides: [0.32, 0.32, 0.0] # if generate only 1 z_center, z_stride will be ignored
           offsets: [0.16, -39.52, -1.78] # origin_offset + strides / 2
           rotations: [0, 1.57] # 0, pi/2
           matched_threshold : 0.6
           unmatched_threshold : 0.45
         }
       }

      sample_positive_fraction : -1
      sample_size : 512
      region_similarity_calculator: {
        nearest_iou_similarity: {
        }
      }
    }

文章内容部分参考:
1.pointpillars代码阅读----prep_pointcloud篇

你可能感兴趣的:(3D障碍物检测论文,Pytorch深度学习,激光雷达目标检测)