(一)_prepare_data函数
_prepare_data
函数的调用
# pcdet/datasets/kitti/kitti_object_eval_python/eval.py
# 准备数据
rets = _prepare_data(gt_annos, dt_annos, current_class, difficulty)
(gt_datas_list, dt_datas_list, ignored_gts, ignored_dets, dontcares, total_dc_num, total_num_valid_gt) = rets
_prepare_data
函数的定义
# pcdet/datasets/kitti/kitti_object_eval_python/eval.py
# 准备数据
def _prepare_data(gt_annos, dt_annos, current_class, difficulty, extra_info=None):
# 数据初始化
gt_datas_list = []
dt_datas_list = []
total_dc_num = []
ignored_gts, ignored_dets, dontcares = [], [], []
total_num_valid_gt = 0
gt_extra_info, dt_extra_info, general_extra_info = extra_info
# 遍历每个图像
for i in range(len(gt_annos)):
if 'clean_data_function' not in general_extra_info.keys() or general_extra_info['clean_data_function'] == None:
rets = clean_data(gt_annos[i], dt_annos[i], current_class, difficulty, \
extra_info_single=(gt_extra_info[i], dt_extra_info[i], general_extra_info))
else: #产生log日志
rets = general_extra_info['clean_data_function'](
gt_annos[i], dt_annos[i], current_class, difficulty, \
extra_info_single=(gt_extra_info[i], dt_extra_info[i], general_extra_info)
)
num_valid_gt, ignored_gt, ignored_det, dc_bboxes = rets
ignored_gts.append(np.array(ignored_gt, dtype=np.int64))
ignored_dets.append(np.array(ignored_det, dtype=np.int64))
#! 最终形成ignored_gts的List
if len(dc_bboxes) == 0:
dc_bboxes = np.zeros((0, 4)).astype(np.float64)
#! dc_boxes 是一个np array,形状(该图像中的don't care boxes 数量, 4)
else:
dc_bboxes = np.stack(dc_bboxes, 0).astype(np.float64)
#! 每一列是一个Don't Care bbox
total_dc_num.append(dc_bboxes.shape[0])
#! don't care boxes的数量. total_dc_num是该图像dc_boxes数量的list,每个图像对应一个total_dc_num
dontcares.append(dc_bboxes)
#! 该图像的dc_boxes list的list
total_num_valid_gt += num_valid_gt
#! 有效 gt boxes 总数的计数器
gt_datas = np.concatenate(
[gt_annos[i]["bbox"], #! bbox index 形状是 N x 4
gt_annos[i]["alpha"]#! alpha index 形状是 N -> 当np.newaxis, 是 N x 1
[..., np.newaxis]], 1)
#! 所以合并后成为 N x 5 ,5表示 [x1, y1, x2, y2, alpha]
dt_datas = np.concatenate([
dt_annos[i]["bbox"], dt_annos[i]["alpha"][..., np.newaxis],
dt_annos[i]["score"][..., np.newaxis]
], 1)
#! 类似的, 形状为N x 6, 6是 [x1, y1, x2, y2, alpha, score]
gt_datas_list.append(gt_datas)
dt_datas_list.append(dt_datas)
# boxes list 的 list
# gt_datas只和gt_annos[i]有关,dt_datas只和dt_annos[i]有关
# 因此每个图像对应一个gt_datas_list和dt_datas_list
total_dc_num = np.stack(total_dc_num, axis=0)
'''
此处的所有数组的长度 = 数据集中的图像数量
gt_datas_list:list(N x 5个数组)
dt_datas_list:list(N x 6个数组)
ignore_gts:list(长度为N的数组(值-1、0或1))
ignore_dets :list(长度为N的数组(值-1、0或1))
dontcares:list((图像x 4个数组中的无关框数量)
total_dc_num:list(图像值中的无关框数量)
total_num_valid_gt:有效gt的总数(int)
'''
return (gt_datas_list, dt_datas_list, ignored_gts, ignored_dets, dontcares,
total_dc_num, total_num_valid_gt)
(二)clean_data函数
clean_data
函数的调用
# pcdet/datasets/kitti/kitti_object_eval_python/eval.py
# 遍历每个图像
for i in range(len(gt_annos)):
if 'clean_data_function' not in general_extra_info.keys() or general_extra_info['clean_data_function'] == None:
rets = clean_data(gt_annos[i], dt_annos[i], current_class, difficulty, \
extra_info_single=(gt_extra_info[i], dt_extra_info[i], general_extra_info))
else: #产生log日志
rets = general_extra_info['clean_data_function'](
gt_annos[i], dt_annos[i], current_class, difficulty, \
extra_info_single=(gt_extra_info[i], dt_extra_info[i], general_extra_info)
)
clean_data
函数的定义
def clean_data(gt_anno, dt_anno, current_class, difficulty):
CLASS_NAMES = ['car', 'pedestrian', 'cyclist', 'van', 'person_sitting', 'truck'] #类别
# difficultys = [0, 1, 2]
#检测难度从易到难,为了检测到同样数目的gt,使最小值减小,最大值增大
MIN_HEIGHT = [40, 25, 25] #高度
MAX_OCCLUSION = [0, 1, 2] #遮挡
MAX_TRUNCATION = [0.15, 0.3, 0.5] #截断
dc_bboxes, ignored_gt, ignored_dt = [], [], []
current_cls_name = CLASS_NAMES[current_class].lower()
num_gt = len(gt_anno["name"]) #gt数量
num_dt = len(dt_anno["name"]) #dt数量
num_valid_gt = 0
for i in range(num_gt): #遍历所有的gt框
bbox = gt_anno["bbox"][i]
gt_name = gt_anno["name"][i].lower() #lower()函数是返回str所有小写字母
height = bbox[3] - bbox[1] #高度
valid_class = -1
if (gt_name == current_cls_name): #gt_name为当前检验的类别名称时为有效类别
valid_class = 1
elif (current_cls_name == "Pedestrian".lower()
and "Pedestrian".lower() == gt_name):# Pedestrian和Pedestrian的有效值为0
valid_class = 0
elif (current_cls_name == "Car".lower() and "Van".lower() == gt_name): # Car和Van的有效值为0
valid_class = 0
else:
valid_class = -1
ignore = False
if ((gt_anno["occluded"][i] > MAX_OCCLUSION[difficulty]) #如果遮挡得太厉害
or (gt_anno["truncated"][i] > MAX_TRUNCATION[difficulty]) #如果截断得太厉害
or (height <= MIN_HEIGHT[difficulty])): #如果太小了
ignore = True #就忽略
if valid_class == 1 and not ignore:
ignored_gt.append(0) #0表示有效
num_valid_gt += 1 #有效框数量+1
elif (valid_class == 0 or (ignore and (valid_class == 1))):
ignored_gt.append(1) # 1表示忽略
else:
ignored_gt.append(-1) # -1表示其他
if gt_anno["name"][i] == "DontCare":
dc_bboxes.append(gt_anno["bbox"][i])
for i in range(num_dt):
if (dt_anno["name"][i].lower() == current_cls_name): #如果dt的类别名称等于当前检验的类别名称
valid_class = 1 #有效值为1
else:
valid_class = -1
height = abs(dt_anno["bbox"][i, 3] - dt_anno["bbox"][i, 1]) #计算高度
if height < MIN_HEIGHT[difficulty]: #如果高度太低
ignored_dt.append(1) #忽略
elif valid_class == 1:
ignored_dt.append(0) #有效
else:
ignored_dt.append(-1) #其他
return num_valid_gt, ignored_gt, ignored_dt, dc_bboxes
致谢:detection-toolbox的作者将代码中的注释写的太清晰了,对我理解代码起到了很大帮助。有需要的读者可以看英文原版。
我的其他PV-RCNN
代码解读系列文章,如果对你有帮助的话,请给我点赞哦~
PV-RCNN代码解读——输入参数介绍
PV-RCNN代码解读——TP,FP,TN,FN的计算
PV-RCNN代码解读——eval.py
PV-RCNN代码解读——计算iou
PV-RCNN代码解读——数据初始化
PV-RCNN代码解读——从点云到输入神经网络的数据处理