MMDet逐行解读之MaxIOUAssigner

前言

  本篇是MMdet逐行解读第二篇,代码地址:mmdet/bbox/assigners/max_iou_assigner.py。由于源码assigner的输入参数多而且不易理解,因此本文是从由简到难的顺序逐步分析各个参数的作用。历史文章如下:
AnchorGenerator解读

1、关闭match_low_quality参数

 我们首先构造了一个match_low_quality的assigner,该参数含义后面会讲。这一节主要分析pos_iou_thr和neg_iou_thr两个参数。另外,我又人为的分别构建了四个预测框和四个GT。

import torch
from mmdet.core.bbox import build_assigner
    # maxIOU模块调试
    config = dict(
        # 最大 IoU 原则分配器
        type='MaxIoUAssigner',
        # 正样本阈值
        pos_iou_thr=0.5,
        # 负样本阈值
        neg_iou_thr=0.4,
        # 正样本阈值下限
        min_pos_iou=0.,     # 仅当开启匹配低质量时该阈值才发挥作用
        match_low_quality = False,  #先为FALSE
        # 忽略 bboes 的阈值,-1表示不忽略
        ignore_iof_thr=-1)
        
    assigner = build_assigner(config) # 构建一个分配器
    bboxes = torch.Tensor([[0, 0, 10, 10], [10, 10, 20, 20],
							    [3, 3, 6, 6],[2, 2, 3, 3]]) 
    gt_bboxes = torch.Tensor([[0, 0, 10, 9], [10,10,19,19], 
    							[10,10,15,15],[3,3,4,4]])
    res = assigner.assign(bboxes, gt_bboxes)
    print(res.gt_inds)  # tensor([1, 2, 0, 0])

 首先从运行结果来看,bboxes和gt_bboxes的匹配结果为[1,2,0,0]。其含义是指:第一个bbox匹配第一个gt,第二个bbox匹配第二个gt,第三四个bbox匹配背景(0表示背景)。接下来,我们分析源码:

	overlaps = self.iou_calculator(gt_bboxes, bboxes)
    # 创建一个-1的tensor,用来保存匹配的结果。-1表示当前bbox为忽略样本
    assigned_gt_inds = overlaps.new_full((num_bboxes, ),
                                         -1,
                                         dtype=torch.long)  
    # 从列方向看分别得到每个bbox和哪个gtbox的iou最大
    max_overlaps, argmax_overlaps = overlaps.max(dim=0) 
    # 将iou低于阈值[0,0.4]的置为0,即处于该范围阈值的bbox为背景。
	if isinstance(self.neg_iou_thr, float):
		assigned_gt_inds[(max_overlaps >= 0)
	                  & (max_overlaps < self.neg_iou_thr)] = 0 
    #若iou处于[0.5,1]范围的,将bbox置为ind+1,表示bbox和该gt匹配
   pos_inds = max_overlaps >= self.pos_iou_thr                      
   assigned_gt_inds[pos_inds] = argmax_overlaps[pos_inds] + 1         
                                         

  其实实际执行的代码只有上述几行,注释我差不多已经注明了,我这里画个图阐述下,读者可以结合注释和图自行理解。首先计算每个gt和所有bbox的iou,得到一个4*4的iou矩阵overlaps。
在这里插入图片描述

 之后再dim=0上执行max操作,得到的max_overlaps=[0.9,0.81,0.11,0.01],argmax_overlaps=[0,1,3,0]。也就是图中带颜色的字体。之后,绿色字体由于<0.4故被划分为背景;红色的两个bbox>0.5故分为正样本,但是最终的结果向量assigned_gt_inds需要存储每个bbox和第几个gt匹配,以bbox1为例,是和index=0的gt匹配,但0已被背景占用了,故需要+1。即bbox1和第一个gt1匹配。故最终结果向量就是代码执行出的[1,2,0,0]。

2、开启match_low_quality参数

  上节我们是关闭了该参数,从结果上来说,总共有四个gt,但是最终只有两个gt匹配成功。有两个gt未进行匹配。从保证召回率角度讲,这样肯定不合理。因此,assigner又设置了match_low_quality参数,即尽量要使所有gt都有一个anchor。这次我们开启该参数来学习该参数的作用,需要额外注意,该参数需要和min_pos_iou参数搭配使用。同理,先看执行效果:

import torch
from mmdet.core.bbox import build_assigner
    # maxIOU模块调试
    config = dict(
        # 最大 IoU 原则分配器
        type='MaxIoUAssigner',
        # 正样本阈值
        pos_iou_thr=0.5,
        # 负样本阈值
        neg_iou_thr=0.4,
        # 正样本阈值下限
        min_pos_iou=0.,     # 仅当开启匹配低质量时该阈值才发挥作用
        match_low_quality = True,  # 开启该参数
        # 忽略 bboes 的阈值,-1表示不忽略
        ignore_iof_thr=-1)
        
    assigner = build_assigner(config) # 构建一个分配器
    bboxes = torch.Tensor([[0, 0, 10, 10], [10, 10, 20, 20],
							    [3, 3, 6, 6],[2, 2, 3, 3]]) 
    gt_bboxes = torch.Tensor([[0, 0, 10, 9], [10,10,19,19], 
    							[10,10,15,15],[3,3,4,4]])
    res = assigner.assign(bboxes, gt_bboxes)
    print(res.gt_inds)  # tensor([1, 3,4,0])

 开启后执行结果成了[1,3,4,0],即第二个bbox和第三个gt匹配,第三个bbox和第四个匹配,让人一头雾水。下面分析下代码:

	'''
	overlaps = self.iou_calculator(gt_bboxes, bboxes)
    # 创建一个-1的tensor,用来保存匹配的结果。-1表示当前bbox为忽略样本
    assigned_gt_inds = overlaps.new_full((num_bboxes, ),
                                         -1,
                                         dtype=torch.long)  
    # 从列方向看分别得到每个bbox和哪个gtbox的iou最大
    max_overlaps, argmax_overlaps = overlaps.max(dim=0) 
    # 将iou低于阈值[0,0.4]的置为0,即处于该范围阈值的bbox为背景。
	if isinstance(self.neg_iou_thr, float):
		assigned_gt_inds[(max_overlaps >= 0)
	                  & (max_overlaps < self.neg_iou_thr)] = 0 
    #若iou处于[0.5,1]范围的,将bbox置为ind+1,表示bbox和该gt匹配
   pos_inds = max_overlaps >= self.pos_iou_thr                      
   assigned_gt_inds[pos_inds] = argmax_overlaps[pos_inds] + 1    
   '''     
   #获取每个gt和哪个bbox的iou最大。
   gt_max_overlaps, gt_argmax_overlaps = overlaps.max(dim=1)   
   if self.match_low_quality:
   	  # 遍历每个gt
      for i in range(num_gts):
          # 若阈值超过0.
          if gt_max_overlaps[i] >= self.min_pos_iou:
              # 将所有gt均进行匹配
              if self.gt_max_assign_all:
                  # 找到和gt最大iou相等的overlaps的位置
                  max_iou_inds = overlaps[i, :]==gt_max_overlaps[i]
                  # 同理+1
                  assigned_gt_inds[max_iou_inds] = i + 1                                

  上述标绿代码就是未开启参数的效果,即执行完注释代码后匹配结果为[1,2,0,0]。下面分析未注释部分:
在这里插入图片描述
 首先标红的字体就是gt_max_overlaps。即每个gt和哪个bbox的iou最大。然后遍历每个gt,若最大iou>min_pos_iou,则将该bbox和gt进行匹配。故bbox1显然还和gt1匹配,bbox2还和gt2匹配,但是遍历到第三个gt3时候,由于其还是和bbox2的iou最高,故将该bbox2匹配改了gt3,把gt2给覆盖掉了。即此时的assigned_gt_inds由[1,2,0,0]变成了[1,3,0,0]。即Bbox2匹配给了一个低质量gt。 之后,遍历gt4的时候,将其匹配给bbox3,也就得到了最终匹配结果assigned_gt_inds = [1,3,4,0]。从最终效果上看,确实能够保证将所有gt匹配上,但是有可能后匹配的把前面iou高的匹配给覆盖掉,导致低质量匹配产生。

总结

  在一阶段算法或者RPN阶段,可以开启低质量匹配,保证最大的召回率。但是在第二阶段RoI Head中要确保准确率,就不开启低质量匹配了。若有问题欢迎+vx:wulele2541612007,拉你进群探讨交流。

  • 15
    点赞
  • 26
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值