mmdetection中使用PASCAL VOC数据集统计recall结果的bug修复

问题描述

在使用mmdetection做实验时,使用Pascal VOC数据集,模型为Faster RCNN,测试时一般使用的代码为:

bash tools/dist_test.sh \
configs/pascal_voc/faster_rcnn_r50_fpn_1x_voc0712.py \
checkpoints/faster_rcnn_r50_fpn_1x_voc0712_20220320_192712-54bef0f3.pth \
4 \
--eval mAP

在实验过程中想要观察recall(召回率),确认一些实验现象,按照代码里的提示,直接将eval改为recall,即:

...
--eval recall

此时会出现报错信息:

 File "mmdetection/mmdet/core/evaluation/recall.py", line 90, in eval_recalls
    if proposals[i].ndim == 2 and proposals[i].shape[1] == 5:
AttributeError: 'list' object has no attribute 'ndim'

mmdetection代码库里也有对应的issue:Find an error when evaluating recall in VOC Dataset #5792

可以看到,这就是mmdetection自己的bug:

 def eval_recalls(gts, 
                  proposals, 
                  proposal_nums=None, 
                  iou_thrs=0.5, 
                  logger=None): 
     """Calculate recalls. 
  
     Args: 
         gts (list[ndarray]): a list of arrays of shape (n, 4) 
         proposals (list[ndarray]): a list of arrays of shape (k, 4) or (k, 5) 
         proposal_nums (int | Sequence[int]): Top N proposals to be evaluated. 
         iou_thrs (float | Sequence[float]): IoU thresholds. Default: 0.5. 

eval_recalls函数里的proposals期望接受的是list[nparray] 类型的数据,而实际传入的是list[list] 类型的数据。下面改正一些代码,修复这个bug。

问题解决

需要修改的地方为:mmdetection/mmdet/core/evaluation/recall.py中的eval_recalls函数,一共需要修改两个地方。

原本的proposals为4952*20的一个list,即4952张照片,每张照片有20个类别各自的proposal。list里面存储的是大小为k * 5的一个nparray,k为该图片该类别下具有的proposal的数量,如果为0的话,存储的就是空矩阵。

这里原始的每个proposals[i] 为长度为20,内容不固定的list;而函数希望的proposals[i] 的形式为:n * 5的一个nparray,n为无论什么类别、所有proposal的数量。因此这里对每个proposals[i] 做一个转换。代码修改如下:

def eval_recalls(gts,
                 proposals,
                 proposal_nums=None,
                 iou_thrs=0.5,
                 logger=None,
                 use_legacy_coordinate=False):

    img_num = len(gts)
    assert img_num == len(proposals)
    proposal_nums, iou_thrs = set_recall_param(proposal_nums, iou_thrs)
    all_ious = []
    for i in range(img_num):
    
    	# modify 1
        new_proposals = []
        for j in range(len(proposals[i])):
            idx = 0
            while idx < proposals[i][j].shape[0]:
                new_proposals.append(proposals[i][j][idx])
                idx = idx + 1
        proposals[i] = np.array(new_proposals)
        # modify1

        if proposals[i].ndim == 2 and proposals[i].shape[1] == 5:
            scores = proposals[i][:, 4]
            sort_idx = np.argsort(scores)[::-1]
            img_proposal = proposals[i][sort_idx, :]
        else:
            img_proposal = proposals[i]
        print(img_proposal.shape)

需要修改的第二个地方为:转化之后有的proposals[i] 可能是空的,即该张照片下没有一张预测出来的proposal,而函数里没有考虑这种情况,会导致bug,因此在if语句里加上判断:img_proposal.shape[0] == 0 来保证代码正常运行。代码如下:

		prop_num = min(img_proposal.shape[0], proposal_nums[-1])
		
		# modify2
        if gts[i] is None or gts[i].shape[0] == 0 or img_proposal.shape[0] == 0:
        # modify2
        
            ious = np.zeros((0, img_proposal.shape[0]), dtype=np.float32)
        else:
            ious = bbox_overlaps(
                gts[i],
                img_proposal[:prop_num, :4],
                use_legacy_coordinate=use_legacy_coordinate)
        all_ious.append(ious)
    all_ious = np.array(all_ious)
    recalls = _recalls(all_ious, proposal_nums, iou_thrs)

    print_recall_summary(recalls, proposal_nums, iou_thrs, logger=logger)
    return recalls
  • 3
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值