[论文]CVPR2019论文GIoU解读

  本文是对 CVPR2019 论文《Generalized Intersection over Union: A Metric and A Loss for Bounding Box Regression》的解读:

   论文地址: https://arxiv.org/abs/1902.09630

1.Motivation

     包围框回归是2D/3D 视觉任务中一个最基础的模块,不管是目标检测,目标跟踪,还是实例分割,都依赖于对bounding box进行回归,以获得准确的定位效果。目前基于深度学习的方法想获得更好的检测性能,要么是用更好的backbone,要么是设计更好的策略提取更好的feature,然而却忽视了bounding box regression中L1、L2 loss这个可以提升的点。

      IoU是目标检测中一个重要的概念,在anchor-based的方法中,他的作用不仅用来确定正样本和负样本,还可以用来评价输出框(predict box)和ground-truth的距离,或者说predict box的准确性。IoU有一个好的特性就是对尺度不敏感(scale invariant)。 在regression任务中,判断predict box和gt的距离最直接的指标就是IoU,但所采用的loss却不适合,如图所示,在loss相同的情况下,regression的效果却大不相同,也就是说loss没有体现出regression的效果,而IoU却可以根据不同的情况得到不同的数值,能最直接反应回归效果。

                                         

2.Method

除了能反映预测检测框与真实检测框的检测效果外,IoU还具有尺度不变性。可是既然IOU这么好,为什么之前不直接用IoU呢,这是由于IoU有两个缺点,直接用IoU作为损失函数会出现两个问题:

  1. 如果两个框没有相交,根据定义,IoU=0,不能反映两者的距离大小(重合度)。同时因为loss=0,没有梯度回传,无法进行学习训练。
  2. IoU无法精确的反映两者的重合度大小。如下图所示,三种情况IoU都相等,但看得出来他们的重合度是不一样的,左边的图回归的效果最好,右边的最差。

             

                   

针对IoU上述两个缺点,本文提出一个新的指标generalized IoU(GIoU):

                     

GIoU的定义很简单,就是先计算两个框的最小闭包区域面积,再计算IoU,再计算闭包区域中不属于两个框的区域占闭包区域的比重,最后用IoU减去这个比重得到GIoU。GIoU有如下4个特点:

  • 与IoU相似,GIoU也是一种距离度量,作为损失函数的话,满足损失函数的基本要求
  • GIoU对scale不敏感
  • GIoU是IoU的下界,在两个框无线重合的情况下,IoU=GIoU
  • IoU取值[0,1],但GIoU有对称区间,取值范围[-1,1]。在两者重合的时候取最大值1,在两者无交集且无限远的时候取最小值-1,因此GIoU是一个非常好的距离度量指标。
  • 与IoU只关注重叠区域不同,GIoU不仅关注重叠区域,还关注其他的非重合区域,能更好的反映两者的重合度。

论文中作者认为 GIoU 具有以下几个性质:

  • 与 IoU 类似,都可以作为一个距离度量,计算损失函数时可以用 LossGIoU=1−GIoU 来计算;
  • 同样,GIoU 也对物体的大小不敏感;
  • 根据上面的公式可以看出,GIoU 总是会小于或等于 IoU. 另外对于 IoU 而言,其值域为 [0,1], 而 GIoU 的值域为 [−1,1]. 在两个形状完全重合时,有 GIoU = IoU = 1, 当两个形状没有重叠部分时,IoU 为 0, 减数为 1, 所以此时的 GIoU 为 - 1;
  • 由于 GIoU 引入了包含 A,B 两个形状的 C, 所以当 A,B 不重合时,依然可以进行优化.

GIoU和IoU作为loss的算法如下所示:

                         

step1: 分别计算gt和predict box的面积

step2: 计算intersection的面积

step3: 计算最小闭包区域面积

step4: 计算IoU和GIoU

step5: 根据公式得到loss

3.Experiments

GIoU loss可以替换掉大多数目标检测算法中bounding box regression,本文选取了Faster R-CNN、Mask R-CNN和YOLO v3 三个方法验证GIoU loss的效果。实验在Pascal VOC和MS COCO数据集上进行。

实验效果如下:

                           

                          

可以看出在YOLOv3在COCO上有明显涨点,但在其他模型下涨点并不明显,作者也指出了faster rcnn和mask rcnn效果不明显的原因是anchor很密,GIoU发挥作用的情况并不多。

  总体来说,文章的motivation比较好,指出用L1、L2作为regression损失函数的缺点,以及用直接指标IoU作为损失函数的缺陷性,提出新的metric来代替L1、L2损失函数,从而提升regression效果,想法简单粗暴,但work的场景有很大局限性。

 随着GIoU丢失,mAP和AP75增加但AP50减, 少正确的分类可能性较小,但边界框更紧密

参考链接:https://zhuanlan.zhihu.com/p/57863810

GIOU实现代码:

import torch
import numpy as np
import matplotlib.pyplot as plt
def generalized_iou(Box_p,Box_gt):
    """
    Input:
        Box_p : 模型预测得到的物体的坐标信息,格式为(n,4)(x1,y,x2,y2),且
        Box_gt: 标注的物体坐标信息,格式为(n,4)(x1,y1,x2,y2)
    Output:
        loss_giou: 平均iou loss
    """
    assert Box_p.shape == Box_gt.shape
    # 转换数据格式
    Box_p = Box_p.float()
    Box_gt = Box_gt.float()
    # 确保格式为 x2>x1,y2>y1
    xp_1 = torch.min(Box_p[:,0],Box_p[:,2]).reshape(-1,1)
    xp_2 = torch.max(Box_p[:,0],Box_p[:,2]).reshape(-1,1)
    yp_1 = torch.min(Box_p[:,1],Box_p[:,3]).reshape(-1,1)
    yp_2 = torch.max(Box_p[:,1],Box_p[:,3]).reshape(-1,1)
    Box_p = torch.cat([xp_1,yp_1,xp_2,yp_2],1)
    # 计算预测框的面积
    box_p_area =  (Box_p[:,2]  - Box_p[:,0])  * (Box_p[:,3]  - Box_p[:,1])
    # 计算标签的面积
    box_gt_area = (Box_gt[:,2] - Box_gt[:,0]) * (Box_gt[:,3] - Box_gt[:,1])
    # 计算预测框与标签框之间的交集
    xI_1 = torch.max(Box_p[:,0],Box_gt[:,0])
    xI_2 = torch.min(Box_p[:,2],Box_gt[:,2])
    yI_1 = torch.max(Box_p[:,1],Box_gt[:,1])
    yI_2 = torch.min(Box_p[:,3],Box_gt[:,3])
    # 交集
    intersection =(yI_2 - yI_1) * (xI_2 - xI_1)
    #intersection = torch.max((yI_2 - yI_1),0) * torch.max((xI_2 - xI_1),0)
    # 计算得到最小封闭图形 C
    xC_1 = torch.min(Box_p[:,0],Box_gt[:,0])
    xC_2 = torch.max(Box_p[:,2],Box_gt[:,2])
    yC_1 = torch.min(Box_p[:,1],Box_gt[:,1])
    yC_2 = torch.max(Box_p[:,3],Box_gt[:,3])
    # 计算最小封闭图形C的面积
    c_area = (xC_2 - xC_1) * (yC_2 - yC_1)
    union = box_p_area + box_gt_area - intersection
    iou = intersection / union
    # GIoU
    giou = iou - (c_area - union) / c_area
    # GIoU loss
    loss_giou = 1 - giou

    return loss_giou.mean()

if __name__ == "__main__":
    box_p = torch.tensor([[125,456,321,647],
                          [25,321,216,645],
                          [111,195,341,679],
                          [30,134,105,371]])
    box_gt = torch.tensor([[132,407,301,667],
                           [29,322,234,664],
                           [109,201,315,680],
                           [41,140,115,384]])
    giou_loss =  generalized_iou(box_p,box_gt)
    print(giou_loss)
发布了33 篇原创文章 · 获赞 46 · 访问量 4万+

猜你喜欢

转载自blog.csdn.net/qq_34795071/article/details/91492605