PV-RCNN:paper,code
在上图中,本文所讲述的是左侧Data部分,也即传入foward函数之前的data处理部分,以方便如何使用该论文的方法应用于自己的数据。
思路:
(一)建立一个DemoDataset类,其中储存关于输入数据的所有信息,包含六个参数
(二)bulid_network的数据处理,包含三个参数
(三)collect_batch中的数据处理
在main函数中
demo_dataset = DemoDataset(
dataset_cfg=cfg.DATA_CONFIG, class_names=cfg.CLASS_NAMES, training=False,
root_path=Path(args.data_path), ext=args.ext, logger=logger
)
因此,我们需要创建以上文件
(1)DATA_CONFIG(重点)
第一个参数cfg.DATA_CONFIG在pv_rcnn.yaml文件中
_BASE_CONFIG_: cfgs/dataset_configs/kitti_dataset.yaml #基本配置
根据其基本配置的路径找到kitti_dataset.yaml(最外层文件夹是tools)
DATASET: 'KittiDataset' #数据集
DATA_PATH: '../data/kitti' #数据路径
*POINT_CLOUD_RANGE: [0, -40, -3, 70.4, 40, 1] #点云范围
DATA_SPLIT: {
#数据分割
'train': train,
'test': val
}
INFO_PATH: {
#信息路径
'train': [kitti_infos_train.pkl], #从kitti数据库中调用
'test': [kitti_infos_val.pkl],
}
FOV_POINTS_ONLY: True #仅视点
POINT_FEATURE_ENCODING: {
#点特征编码
encoding_type: absolute_coordinates_encoding, #编码类型
used_feature_list: ['x', 'y', 'z', 'intensity'], #使用的特征
src_feature_list: ['x', 'y', 'z', 'intensity'], #src特征
}
DATA_PROCESSOR: #数据处理器
- NAME: mask_points_and_boxes_outside_range #遮罩点和框超出范围
REMOVE_OUTSIDE_BOXES: True #移除外框
- NAME: shuffle_points #Shuffle点
SHUFFLE_ENABLED: {
#启用的Shuffle点
'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
}
回到pv_rcnn.yaml,其中还补充了一些信息
DATA_CONFIG: #数据配置
_BASE_CONFIG_: cfgs/dataset_configs/kitti_dataset.yaml #基本配置
DATA_AUGMENTOR: #数据增强器
DISABLE_AUG_LIST: ['placeholder'] #停用增强器
AUG_CONFIG_LIST: #增强器
- NAME: gt_sampling #gt采样
USE_ROAD_PLANE: True # 使用道路平面
DB_INFO_PATH: # 数据库信息路径
* - kitti_dbinfos_train.pkl #从数据库中调用
PREPARE: {
filter_by_min_points: ['Car:5', 'Pedestrian:5', 'Cyclist:5'],#按最低分数过滤
filter_by_difficulty: [-1],#按难度过滤
}
SAMPLE_GROUPS: ['Car:15','Pedestrian:10', 'Cyclist:10'] #样本组
NUM_POINT_FEATURES: 4 #特征点数量
DATABASE_WITH_FAKELIDAR: False #不使用FAKELIDAR的数据库
REMOVE_EXTRA_WIDTH: [0.0, 0.0, 0.0] #不删除多余的宽度
LIMIT_WHOLE_SCENE: False #整个场景限制
- 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] #范围
(2) CLASS_NAMES
第二个参数是cfg.CLASS_NAMES,即类别名称
CLASS_NAMES: ['Car', 'Pedestrian', 'Cyclist']
(3)training
一个Bool变量,表示是否训练
training = False
(4) root_path
根路径
args, cfg = parse_config()
root_path = Path(args.data_path)#指定点云数据文件或目录
其中parse_config的定义为
def parse_config(): #创建一个ArgumentParser对象,格式: 参数名,类型,默认值, 帮助信息
parser = argparse.ArgumentParser(description='arg parser')
parser.add_argument('--cfg_file', type=str, default='cfgs/kitti_models/second.yaml',
help='specify the config for demo') #指定演示的配置
parser.add_argument('--data_path', type=str, default='demo_data',
help='specify the point cloud data file or directory')#指定点云数据文件或目录
parser.add_argument('--ckpt', type=str, default=None, help='specify the pretrained model')#指定预训练模型
# Check point 校验点 在系统运行中当出现查找数据请求时,系统从数据库中找出这些数据并存入内存区,这样用户就可以对这些内存区数据进行修改等
parser.add_argument('--ext', type=str, default='.bin', help='specify the extension of your point cloud data file')
# extension 指定点云数据文件的扩展名
args = parser.parse_args()
cfg_from_yaml_file(args.cfg_file, cfg)
return args, cfg #arguments参数,configuration配制
即,args的参数包含:
执行时需要手动输入以上参数,或会采用默认数值。
github上给出的执行范例:
python demo.py --cfg_file cfgs/kitti_models/pv_rcnn.yaml \
--ckpt pv_rcnn_8369.pth \
--data_path ${
POINT_CLOUD_DATA}
# Here ${POINT_CLOUD_DATA} could be the following format:
# The original KITTI .bin data within data/kitti, like data/kitti/training/velodyne/000008.bin
(5)ext
args, cfg = parse_config()
ext = args.ext #指定点云数据文件的扩展名
(6)logger
logger = common_utils.create_logger() #logger记录日志
model = build_network(model_cfg=cfg.MODEL, num_class=len(cfg.CLASS_NAMES), dataset=demo_dataset)
(1)MODEL(重点)
model_cfg=cfg.MODEL
依然是在pvrcnn.yaml中
MODEL:
NAME: PVRCNN #名称
VFE:#体素特征编码
NAME: MeanVFE
BACKBONE_3D:
NAME: VoxelBackBone8x
MAP_TO_BEV: #映射到BEV
NAME: HeightCompression
NUM_BEV_FEATURES: 256 #BEV功能个数
BACKBONE_2D:
NAME: BaseBEVBackbone
LAYER_NUMS: [5, 5] #层数
LAYER_STRIDES: [1, 2] #图层等级
NUM_FILTERS: [128, 256] #过滤器数量
UPSAMPLE_STRIDES: [1, 2] #上采样等级
NUM_UPSAMPLE_FILTERS: [256, 256] #示例过滤器数量
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': 'Car',
'anchor_sizes': [[3.9, 1.6, 1.56]], #锚的尺寸
'anchor_rotations': [0, 1.57], #锚的旋转
'anchor_bottom_heights': [-1.78], #锚底高度
'align_center': False, #对齐中心
'feature_map_stride': 8, #特征图跨度
'matched_threshold': 0.6, #匹配阈值
'unmatched_threshold': 0.45 #唯一的阈值
},
{
'class_name': 'Pedestrian',
#和car类相似
},
{
'class_name': 'Cyclist',
#和car类相似
}
]
TARGET_ASSIGNER_CONFIG:#目标分配器配置
NAME: AxisAlignedTargetAssigner
POS_FRACTION: -1.0 #位置分数
SAMPLE_SIZE: 512 #样本大小
NORM_BY_NUM_EXAMPLES: False #使用num个例子归一
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] #编码权重
}
(2)类别数量
num_class=len(cfg.CLASS_NAMES)
(3)输入处理好的DemoDataset类的数据
dataset=demo_dataset
demo_dataset类 中 collate_batch的定义:
def collate_batch(batch_list, _unused=False):
data_dict = defaultdict(list) #defaultdict 表示在当字典里的key不存在但被查找时,返回的不是keyError而是一个默认值
将data_dict传入后命名为batch_list,创建一个字典data_dict
for cur_sample in batch_list:
for key, val in cur_sample.items():
data_dict[key].append(val)
batch_size = len(batch_list)
ret = {
}
cur_sample 表示现在的样本值(current sample),也是DemoDataset类
用data_dict记录cur_sample的内容,主要创建记录数据的list,如下
for key, val in data_dict.items():
if key in ['voxels', 'voxel_num_points']: #对应体素
ret[key] = np.concatenate(val, axis=0) #多个数组的拼接
elif key in ['points', 'voxel_coords']: #对应关键点
coors = []
for i, coor in enumerate(val):
coor_pad = np.pad(coor, ((0, 0), (1, 0)), mode='constant', constant_values=i)
"""
((0,0),(1,0))
在二维数组array第一维(此处便是行)前面填充0行,最后面填充0行;
在二维数组array第二维(此处便是列)前面填充1列,最后面填充0列
mode='constant'表示指定填充的参数
constant_values=i 表示第一维填充i
"""
coors.append(coor_pad) #将coor_pad补充在coors后面
ret[key] = np.concatenate(coors, axis=0)
elif key in ['gt_boxes']: #对应真值
max_gt = max([len(x) for x in val]) #找寻最大价值的点
batch_gt_boxes3d = np.zeros((batch_size, max_gt, val[0].shape[-1]), dtype=np.float32) #画可视图用的
for k in range(batch_size):
batch_gt_boxes3d[k, :val[k].__len__(), :] = val[k]
ret[key] = batch_gt_boxes3d
else:
ret[key] = np.stack(val, axis=0) #类似concatenate,给指定axis增加维度
ret['batch_size'] = batch_size
return ret