文章目录

  • DataProcessor模块解析
  • 1. mask_points_and_boxes_outside_range
  • 2. shuffle_points
  • 3. transform_points_to_voxels

DataProcessor模块解析

在对batch_data的处理中,经过了point_feature_encoder模块处理后,就轮到了进行data_processor处理。在data_processor的forward流程中比较简单,就是依次经过队列各模块处理即可,如下所示:

def forward(self, data_dict):"""Args:data_dict:points: (N, 3 + C_in)gt_boxes: optional, (N, 7 + C) [x, y, z, dx, dy, dz, heading, ...]gt_names: optional, (N), string...Returns:"""# 依次进行各类数据处理,不断更新data_dictfor cur_processor in self.data_processor_queue:data_dict = cur_processor(data_dict=data_dict)

其中,这里的data_dict保留了是否启动某些数据增强的相关信息,以及具体确定的随机参数:

在pointpillars中主要进行3种数据处理,分别是:mask_points_and_boxes_outside_range、shuffle_points、transform_points_to_voxels;下面对别对着几个函数进行记录。


1. mask_points_and_boxes_outside_range

给定点云场景的一个限制范围limit_range: [minx, miny, minz, maxx, maxy, maxz],现在对超过这个范围的点进行过滤,同时对gt中心点在这个范围外也进行过滤。那么得到的结果是gt的部分点可能会因为这个范围限制而被截取。

核心函数与核心代码如下所示:

# 功能: 限制点云在限制范围集中,返回的是每个点云是否符合范围的掩码(布尔变量)def mask_points_by_range(points, limit_range):"""Args:points: (N, 4) 点云特征limit_range: [minx, miny, minz, maxx, maxy, maxz]"""mask = (points[:, 0] >= limit_range[0]) & (points[:, 0] <= limit_range[3]) \ & (points[:, 1] >= limit_range[1]) & (points[:, 1] <= limit_range[4])return mask# 功能:移除范围外的gtdef mask_boxes_outside_range_numpy(boxes, limit_range, min_num_corners=1, use_center_to_filter=True):"""Args:boxes: (N, 7) [x, y, z, dx, dy, dz, heading, ...], (x, y, z) is the box centerlimit_range: [minx, miny, minz, maxx, maxy, maxz]min_num_corners: 1use_center_to_filter: True 是否利用gt中心店来进行采样Returns:"""if boxes.shape[1] > 7:boxes = boxes[:, 0:7] # 这里去除最后一位的类别idif use_center_to_filter:box_centers = boxes[:, 0:3] # 提取xyz中心位置信息mask = ((box_centers >= limit_range[0:3]) & (box_centers <= limit_range[3:6])).all(axis=-1)# 中心点是否在限制范围内return mask

2. shuffle_points

随后根据点数量构建一个随机顺序的索引序列,然后根据这个随机的索引序列对点云进行重新编排点顺序,核心代码如下所示:

# 训练过程打乱,测试过程不打乱if config.SHUFFLE_ENABLED[self.mode]:points = data_dict['points']shuffle_idx = np.random.permutation(points.shape[0])# 生成随机序列索引points = points[shuffle_idx]# 根据索引重新编排点顺序data_dict['points'] = points

3. transform_points_to_voxels

这部分的具体执行代码调用了sponv进行稀疏卷积,将点场景转换为voxel场景,核心代码如下所示:

# 功能:将点云转换为voxel,调用spconv的VoxelGeneratorV2def transform_points_to_voxels(self, data_dict=None, config=None):# 初始化确认网格大小与体素大小if data_dict is None:grid_size = (self.point_cloud_range[3:6] - self.point_cloud_range[0:3]) / np.array(config.VOXEL_SIZE) # 网格数量self.grid_size = np.round(grid_size).astype(np.int64) # 四舍五入取整self.voxel_size = config.VOXEL_SIZE# 从配置文件中获取指定的体素大小# just bind the config, we will create the VoxelGeneratorWrapper later,# to avoid pickling issues in multiprocess spawnreturn partial(self.transform_points_to_voxels, config=config)if self.voxel_generator is None:self.voxel_generator = VoxelGeneratorWrapper(vsize_xyz=config.VOXEL_SIZE, # 体素大小 [0.16, 0.16, 4]coors_range_xyz=self.point_cloud_range, # 场景范围 [0, -39.68, -3, 69.12, 39.68, 1]num_point_features=self.num_point_features, # 每个点特征数量 4max_num_points_per_voxel=config.MAX_POINTS_PER_VOXEL, # 每个voxel最大点云数 32max_num_voxels=config.MAX_NUMBER_OF_VOXELS[self.mode],# 场景的最大voxel数 训练模式是 16000)# 调用spconv的voxel_generator的generate方法生成体素points = data_dict['points']voxel_output = self.voxel_generator.generate(points)voxels, coordinates, num_points = voxel_output"""voxels: (num_voxels, max_points_per_voxel, 3 + C)表示每个体素中有32个点云,每个点有3+C(4)和特征维度coordinates: (num_voxels, 3) 在点云场景中voxel的位置信息,pointpillars算法这里的voxel就是pillars,所以只有平面上的2d坐标,没有z维度切分num_points: (num_voxels) 表示每个voxel内的有效点数量"""......

在data_process模块处理完后,单帧点云场景的数据处理流程就此结束了,剩下的就是就是收集batch_size各如此的单帧点云数据构建成一个batch数据,然后对batch数据进行处理。