PV-RCNN代码解读——TP,FP,TN,FN的计算

PV-RCNN:paper,code
配套实践篇:
PV-RCNN代码测试——TP,FP,TN,FN的计算(一)
PV-RCNN代码测试——TP,FP,TN,FN的计算(二)

(一)目标检测模型评估的简介

后面的代码中涉及到目标检测中几个重要的定义:

True positives : 正样本被正确识别为正样本,飞机的图片被正确的识别成飞机。

True negatives: 负样本被正确识别为负样本,飞机的图片被正确地识别为不是大雁。

False positives: 负样本被错误识别为正样本,飞机的图片被错误地识别成了大雁。

False negatives: 正样本被错误识别为负样本,飞机的图片被错误地识别为不是飞机。

Precision :检索出来的条目中有多大比例是我们需要的。
在这里插入图片描述
Recall:我们需要的条目中有多大比例被检索出来了。
在这里插入图片描述

(二)compute_statistics_jit函数

compute_statistics_jit函数的调用

# pcdet/datasets/kitti/kitti_object_eval_python/eval.py
# 计算统计量tp, fp, fn, similarity, thresholds
rets = compute_statistics_jit(
	overlaps[i], # 单个图像的iou值b/n gt和dt
	gt_datas_list[i], # N x 5阵列
	dt_datas_list[i], # N x 6阵列
	ignored_gts[i], # 长度N数组,-1、0、1
	ignored_dets[i], # 长度N数组,-1、0、1
	dontcares[i], # 无关框数量x 4
	metric, # 0, 1, 或 2 (bbox, bev, 3d)
	min_overlap=min_overlap, # 浮动最小IOU阈值为正
	thresh=0.0, # 忽略得分低于此值的dt。
	compute_fp=False)
tp, fp, fn, similarity, thresholds, _ = rets 

compute_statistics_jit函数的定义

# pcdet/datasets/kitti/kitti_object_eval_python/eval.py
# 计算统计量tp, fp, fn, similarity, thresholds
def compute_statistics_jit(overlaps,
                           gt_datas,
                           dt_datas,
                           ignored_gt,
                           ignored_det,
                           dc_bboxes,
                           metric,
                           min_overlap,
                           thresh=0,
                           compute_fp=False, # 如果我们在计算召回阈值(recall thresholds),则将compute_fp设置为False。
                           compute_aos=False):
                           
    det_size = dt_datas.shape[0]
    gt_size = gt_datas.shape[0]
    dt_scores = dt_datas[:, -1]
    dt_alphas = dt_datas[:, 4]
    gt_alphas = gt_datas[:, 4]
    dt_bboxes = dt_datas[:, :4]
    gt_bboxes = gt_datas[:, :4]

    assigned_detection = [False] * det_size # 存储是否每个检测都分配给了一个gt。
    ignored_threshold = [False] * det_size # 如果检测分数低于阈值,则存储数组
    if compute_fp:
        for i in range(det_size):
            if (dt_scores[i] < thresh):
                ignored_threshold[i] = True

    NO_DETECTION = -10000000
    tp, fp, fn, similarity = 0, 0, 0, 0
    # thresholds = [0.0]
    # delta = [0.0]
    thresholds = np.zeros((gt_size, ))
    thresh_idx = 0 # 用于计算阈值
    delta = np.zeros((gt_size, ))
    delta_idx = 0

    #! detection-toolbox的作者补充的代码 ----
    extra = {
     
        #! 对于每个gt框,存储它是否为-1 (忽略), 0 (false negative (不匹配)), 1 (true positive (匹配))
        "gt_box_type": np.full((gt_size, ), -1),
        
        #! 对于每个dt框,存储它是否为-1 (无关), 0 (false positive (不匹配)), 1 (true positive (匹配))
        #! -1表示它在don't care范围内,属于其他类,等等
        "dt_box_type": np.full((det_size, ), -1),
        
        #! 存储匹配对象的idx
        "gt_box_matched_idx": np.full((gt_size, ), -1),
        "dt_box_matched_idx": np.full((det_size, ), -1)
    }
   
    #! 遍历gt框
    for i in range(gt_size): #遍历gt
        if ignored_gt[i] == -1: #! 不要匹配完全不相关的gt框
            continue
        
        det_idx = -1 #! 储存对此gt存储的最佳检测的idx
        valid_detection = NO_DETECTION #! 存储到目前为止检测到的最大分数。
        max_overlap = 0 #! 最佳检测的overlap。best是最高重叠
        assigned_ignored_det = False

        for j in range(det_size): #遍历dt
            if (ignored_det[j] == -1): #! 与完全不相关的dt框不匹配
                continue
            if (assigned_detection[j]): #! 如果已经分配了dt,请跳过(分配给更好的gt)
                continue
            if (ignored_threshold[j]): #! 如果dt分数低于阈值,则跳过
                continue        
            
            overlap = overlaps[j, i] #! 当前此dt和此gt之间的overlap 
            dt_score = dt_scores[j] #! 当前dt的分数

            if (not compute_fp # compute_fp为false,则这是唯一重要的部分
            		and (overlap > min_overlap) # 找到足够的重叠
                    and dt_score > valid_detection): # 找最高分的检测
                det_idx = j
                valid_detection = dt_score
            
            elif (compute_fp #! 当compute_fp为true时,我们基于overlap进行选择
            	  and (overlap > min_overlap) #! compute_fp为true。 这意味着我们正在度量,而不是设置阈值。
                  and (overlap > max_overlap or assigned_ignored_det) #! 如果重叠足够(比以前的重叠好,或者我们不关心以前的重复)
                  and ignored_det[j] == 0): #而当前的需求是我们所关心的,
                max_overlap = overlap #  分配
                det_idx = j #更新重叠的det_idx
                valid_detection = 1 #由于我们没有按分数排名,因此我们对有效检测进行了留一法检验。
                assigned_ignored_det = False #用留一法来表明已经分配了关心的单位

            elif (compute_fp #! compute_fp为true。
            	  and (overlap > min_overlap)  #! 如果重叠足够
                  and (valid_detection == NO_DETECTION) #尚未分配任何东西
                  and ignored_det[j] == 1): #是我们不关心的检测
                det_idx = j #我们分配它,因为我们将max_overlap保留为默认设置,因此任何内容都可以覆盖它。
                valid_detection = 1
                assigned_ignored_det = True
                #? 一个奇怪的事情是我们不关心的事物,如果我们分配第一个,则下一个无法覆盖它,因为valid_detection!= NO_DETECTION

        if (valid_detection == NO_DETECTION) #! 如果我们无法将此gt与任何东西匹配
        	and ignored_gt[i] == 0: #而这是我们关心的事情
            fn += 1 #那将是一个false negative
            extra['gt_box_type'][i] = 0

        elif ((valid_detection != NO_DETECTION) #! 如果我们确实将此gt与某些内容匹配
              and (ignored_gt[i] == 1 or ignored_det[det_idx] == 1 )): #gt是我们不关心的东西,或者det是我们不关心的东西
            assigned_detection[det_idx] = True #! 我们分配它
            # 为什么? 可能是因为如果我们不分配它,那么以后它将是一个误报(未分配的det)
            extra['gt_box_type'][i] = -1
            extra['dt_box_type'][det_idx] = -1

        elif valid_detection != NO_DETECTION: #! 如果我们确实将此gt与某些内容匹配,
        #! 剩下的条件是:gt是我们关心的东西,det是我们关心的东西
        #! true positive. 
            extra['gt_box_type'][i] = 1
            extra['dt_box_type'][det_idx] = 1
            extra['gt_box_matched_idx'][i] = det_idx
            extra['dt_box_matched_idx'][det_idx] = i
            tp += 1 # 仅在tp上添加阈值。
            # thresholds.append(dt_scores[det_idx])
            thresholds[thresh_idx] = dt_scores[det_idx] #! 在这里,我们还将det分数附加到阈值的末尾
            thresh_idx += 1
            if compute_aos:
                # delta.append(gt_alphas[i] - dt_alphas[det_idx])
                delta[delta_idx] = gt_alphas[i] - dt_alphas[det_idx]
                delta_idx += 1
            assigned_detection[det_idx] = True #! 然后,将检测分配为True
            
        #! 在没有检测到结果并且gt是我们不在乎的时候
        else:
            extra['gt_box_type'][i] = -1

 
    #? 到目前为止,我们还没有使用 dc boxes。 这是因为它们仅用于误报计算(false positive )因为我们还没有研究过不匹配的检测
    #? 如果里面有一个不匹配的东西不在乎,我们就不算它为FP
    if compute_fp:
        for i in range(det_size):  #! 遍历检测
            if (not (assigned_detection[i]  #未分配给gt
            		 or ignored_det[i] == -1 #!  不同的类别
                     or ignored_det[i] == 1 #!  大小不同
                     or ignored_threshold[i])): #!  不低于分数阈值
                fp += 1
                extra['dt_box_type'][i] = 0 #! false positive!

        #! 这是我们从无关区域中收集到的检测数量。 我们将从fp中减去它。
        nstuff = 0
        if metric == 0: #! Metric == 0 是 2d bbox
            overlaps_dt_dc = image_box_overlap(dt_bboxes, dc_bboxes, 0) #! dt盒和dc盒之间的ious。
            for i in range(dc_bboxes.shape[0]):
                for j in range(det_size):
                    # 跳过上面没有添加到fp的内容
                    if (assigned_detection[j]):
                        continue
                    if (ignored_det[j] == -1 or ignored_det[j] == 1):
                        continue
                    if (ignored_threshold[j]):
                        continue
                    
                    if overlaps_dt_dc[j, i] > min_overlap: #! 如果两者之间的重叠大于min_overlap
                        assigned_detection[j] = True # 将检测结果分配给dc
                        nstuff += 1 #并将其添加到我们从fp中获得的东西中。
                        extra['dt_box_type'][j] = -1 

        fp -= nstuff # 从FP减去nstuff

        if compute_aos:
            tmp = np.zeros((fp + delta_idx, ))
            # tmp = [0] * fp
            for i in range(delta_idx):
                tmp[i + fp] = (1.0 + np.cos(delta[i])) / 2.0
                # tmp.append((1.0 + np.cos(delta[i])) / 2.0)
            # assert len(tmp) == fp + tp
            # assert len(delta) == tp
            if tp > 0 or fp > 0:
                similarity = np.sum(tmp)
            else:
                similarity = -1
                
    '''
     ! 我们在这里说明条件。
     ! if compute_fp:
     ! 	tp和fn在这里,fp和similarity就没有用
     ! 	thresholds[:thresh_idx] = thresholds
     ! 	因此,基本上,我们必须使用工具来计算召回率和匹配的dts的得分。
     '''
    return tp, fp, fn, similarity, thresholds[:thresh_idx]

致谢:detection-toolbox的作者将代码中的注释写的太清晰了,对我理解代码起到了很大帮助。有需要的读者可以看英文原版。

你可能感兴趣的:(PV-RCNN,python,深度学习,人工智能,大数据,pytorch)