当前位置:   article > 正文

目标检测 IoU、GIoU、DIoU、CIoU、EIoU

giou

1. 简介

        在目标检测任务中,常用到一个指标IoU,即交并比,IoU可以很好的描述一个目标检测模型的好坏。在训练阶段IoU可以作为anchor-based方法中,划分正负样本的依据;同时也可用作损失函数;在推理阶段,NMS中会用到IoU。同时IoU有着比较严重的缺陷,于是出现了GIoU、DIoU、CIoU、EIoU,下面我们一起看一下这几种IoU。

2. IoU(Intersection over Union)

                        

        IoU的计算是用预测框(A)和真实框(B)的交集除以二者的并集,其公式为:

                                                \LARGE IoU = \frac{A\bigcap B}{A\bigcup B}

        IoU的值越高也说明A框与B框重合程度越高,代表模型预测越准确。反之,IoU越低模型性能越差。

IoU优点:

        (1)IoU具有尺度不变性

        (2)结果非负,且范围是(0, 1)

IoU缺点:

        (1)如果两个目标没有重叠,IoU将会为0,并且不会反应两个目标之间的距离,在这种无重叠目标的情况下,如果IoU用作于损失函数,梯度为0,无法优化。

        (2)IoU无法精确的反映两者的重合度大小。如下图所示,三种情况IoU都相等,但看得出来他们的重合度是不一样的,左边的图回归的效果最好,右边的最差。

        

3. GIoU(Generalized IoU)

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

        为了解决IoU作为损失函数时的两个缺点,有大神提出了GIoU,在IoU后面增加了一项,计算两个框的最小外接矩形,用于表征两个框的距离,从而解决了两个目标没有交集时梯度为零的问题,公式为:

                                        

        其中C是两个框的最小外接矩形的面积。

        当IOU=0时:

                ​​​​​​​        ​​​​​​​        ​​​​​​​        

        当IOU为0时,意味着A与B没有交集,这个时候两个框离得越远,GIOU越接近-1;两框重合,GIOU=1,所以GIOU的取值为(-1, 1]。

        ​​​​​​​        

         GIOU作为loss函数时:

        ​​​​​​​        ​​​​​​​        ​​​​​​​        ​​​​​​​        ​​​​​​​          

        当A、B两框不相交时不变,最大化GIoU就是最小化C,这样就会促使两个框不断靠近。

优点:

        (1)当IoU=0时,仍然可以很好的表示两个框的距离。

        (2)GIoU不仅关注重叠区域,还关注其他的非重合区域,能更好的反映两者的重合度。

缺点:

        (1)当两个框属于包含关系时,GIoU会退化成IoU,无法区分其相对位置关系,如下图:

        

         (2)由于GIoU仍然严重依赖IoU,因此在两个垂直方向,误差很大,很难收敛。两个框在相同距离的情况下,水平垂直方向时,此部分面积最小,对loss的贡献也就越小,从而导致在垂直水平方向上回归效果较差。

        如下图,三种情况下GIoU的值一样,GIoU将很难区分这种情况。

4. DIoU(Distance-IoU)

        论文地址:https://arxiv.org/pdf/1911.08287.pdf

        针对上述GIoU的两个问题,有大神将GIoU中最小外接框来最大化重叠面积的惩罚项修改成最小化两个BBox中心点的标准化距离从而加速损失的收敛过程,这就诞生了DIoU。

        DIoU要比GIou更加符合目标框回归的机制,将目标与预测之间的距离,重叠率以及尺度都考虑进去,使得目标框回归变得更加稳定,不会像IoU和GIoU一样出现训练过程中发散等问题。

        公式如下:

        ​​​​​​​        ​​​​​​​        ​​​​​​​        ​​​​​​​        ​​​​​​​        

        其中分别代表了预测框和真实框的中心点,且 ρ 代表的是计算预测框与真实框中心点间的欧式距离。 c 代表的是能够同时包含预测框和真实框的最小外接矩形的对角线长度。

        ​​​​​​​        ​​​​​​​        

优点:

        (1)DIoU loss可以直接最小化两个目标框的距离,因此比GIoU loss收敛快得多。

        (2)对于包含两个框在水平方向和垂直方向上这种情况,DIoU损失可以使回归非常快。

        (3)DIoU还可以替换普通的IoU评价策略,应用于NMS中,使得NMS得到的结果更加合理和有效。

缺点:

        虽然DIOU能够直接最小化预测框和真实框的中心点距离加速收敛,但是Bounding box的回归还有一个重要的因素纵横比暂未考虑。如下图,三个红框的面积相同,但是长宽比不一样,红框与绿框中心点重合,这时三种情况的DIoU相同,证明DIoU不能很好的区分这种情况。

5. CIoU(Complete-IoU)

        CIoU与DIoU出自同一篇论文,CIoU大多数用于训练。DIoU的作者考虑到,在两个框中心点重合时,c与d的值都不变。所以此时需要引入框的宽高比:

                                                

        其中α是权重函数,v用来度量宽高比的一致性:

                                                        ​​​​​​​        

        ​​​​​​​        ​​​​​​​        ​​​​​​​        ​​​​​​​        ​​​​​​​        

最终CIoU Loss定义为:

                                                

优点:

        考虑了框的纵横比,可以解决DIoU的问题。

缺点:

        通过CIoU公式中的v反映的纵横比的差异,而不是宽高分别与其置信度的真实差异,所以有时会阻碍模型有效的优化相似性。

6. EIoU(Efficient-IoU)

        论文地址:https://arxiv.org/pdf/2101.08158.pdf

        为了解决CIoU的问题,有学者在CIOU的基础上将纵横比拆开,提出了EIOU Loss,并且加入Focal聚焦优质的预测框,与CIoU相似的,EIoU是损失函数的解决方案,只用于训练。

        EIOU的惩罚项是在CIOU的惩罚项基础上将纵横比的影响因子拆开分别计算目标框和预测框的长和宽,该损失函数包含三个部分:重叠损失,中心距离损失,宽高损失,前两部分延续CIoU中的方法,但是宽高损失直接使目标框与预测框的宽度和高度之差最小,使得收敛速度更快。惩罚项公式如下:

                        ​     

其中\LARGE c_w  和 \LARGE c_h是覆盖两个Box的最小外接框的宽度和高度。

        通过整合EIoU Loss和FocalL1 loss,最终得到了最终的Focal-EIoU loss,其中 γ是一个用于控制曲线弧度的超参。

        ​​​​​​​        ​​​​​​​        ​​​​​​​        ​​​​​​​                

优点:

1)将纵横比的损失项拆分成预测的宽高分别与最小外接框宽高的差值,加速了收敛提高了回归精度。

2)引入了Focal Loss优化了边界框回归任务中的样本不平衡问题,即减少与目标框重叠较少的大量锚框对BBox 回归的优化贡献,使回归过程专注于高质量锚框。

7. pytorch代码实现

        代码中使用pytorch是方便在作为损失函数的时候,方便反向传播,下面的代码计算各种IoU,用作损失函数的时候需要用1减去返回结果。

  1. import torch
  2. import math
  3. import numpy as np
  4.  
  5.  
  6. def bbox_iou(box1, box2, xywh=False, giou=False, diou=False, ciou=False, eiou=False, eps=1e-7):
  7.     """
  8.     实现各种IoU
  9.     Parameters
  10.     ----------
  11.     box1        shape(b, c, h, w,4)
  12.     box2        shape(b, c, h, w,4)
  13.     xywh        是否使用中心点和wh,如果是False,输入就是左上右下四个坐标
  14.     GIoU        是否GIoU
  15.     DIoU        是否DIoU
  16.     CIoU        是否CIoU
  17.     EIoU        是否EIoU
  18.     eps         防止除零的小量
  19.     Returns
  20.     -------
  21.     """
  22.     # 获取边界框的坐标
  23.     if xywh:
  24.         # 将 xywh 转换成 xyxy
  25.         b1_x1, b1_x2 = box1[..., 0] - box1[..., 2] / 2, box1[..., 0] + box1[..., 2] / 2
  26.         b1_y1, b1_y2 = box1[..., 1] - box1[..., 3] / 2, box1[..., 1] + box1[..., 3] / 2
  27.         b2_x1, b2_x2 = box2[..., 0] - box2[..., 2] / 2, box2[..., 0] + box2[..., 2] / 2
  28.         b2_y1, b2_y2 = box2[..., 1] - box2[..., 3] / 2, box2[..., 1] + box2[..., 3] / 2
  29.  
  30.     else:
  31.         # x1, y1, x2, y2 = box1
  32.         b1_x1, b1_y1, b1_x2, b1_y2 = box1[..., 0], box1[..., 1], box1[..., 2], box1[..., 3]
  33.         b2_x1, b2_y1, b2_x2, b2_y2 = box2[..., 0], box2[..., 1], box2[..., 2], box2[..., 3]
  34.  
  35.     # 区域交集
  36.     inter = (torch.min(b1_x2, b2_x2) - torch.max(b1_x1, b2_x1)).clamp(0) * \
  37.             (torch.min(b1_y2, b2_y2) - torch.max(b1_y1, b2_y1)).clamp(0)
  38.  
  39.     # 区域并集
  40.     w1, h1 = b1_x2 - b1_x1, b1_y2 - b1_y1 + eps
  41.     w2, h2 = b2_x2 - b2_x1, b2_y2 - b2_y1 + eps
  42.     union = w1 * h1 + w2 * h2 - inter + eps
  43.     # 计算iou
  44.     iou = inter / union
  45.  
  46.     if giou or diou or ciou or eiou:
  47.         # 计算最小外接矩形的wh
  48.         cw = torch.max(b1_x2, b2_x2) - torch.min(b1_x1, b2_x1)
  49.         ch = torch.max(b1_y2, b2_y2) - torch.min(b1_y1, b2_y1)
  50.  
  51.         if ciou or diou or eiou:
  52.             # 计算最小外接矩形角线的平方
  53.             c2 = cw ** 2 + ch ** 2 + eps
  54.             # 计算预测框与真实框的两个中心之间距离的平方
  55.             rho2 = ((b2_x1 + b2_x2 - b1_x1 - b1_x2) ** 2 +
  56.                     (b2_y1 + b2_y2 - b1_y1 - b1_y2) ** 2) / 4
  57.             if diou:
  58.                 # 输出DIoU
  59.                 return iou - rho2 / c2
  60.             elif ciou:
  61.                 v = (4 / math.pi ** 2) * torch.pow(torch.atan(w2 / h2) - torch.atan(w1 / h1), 2)
  62.                 with torch.no_grad():
  63.                     alpha = v / (v - iou + (1 + eps))
  64.                 # 输出CIoU
  65.                 return iou - (rho2 / c2 + v * alpha)
  66.             elif eiou:
  67.                 rho_w2 = ((b2_x2 - b2_x1) - (b1_x2 - b1_x1)) ** 2
  68.                 rho_h2 = ((b2_y2 - b2_y1) - (b1_y2 - b1_y1)) ** 2
  69.                 cw2 = cw ** 2 + eps
  70.                 ch2 = ch ** 2 + eps
  71.                 # 输出EIoU
  72.                 return iou - (rho2 / c2 + rho_w2 / cw2 + rho_h2 / ch2)
  73.         else:
  74.             c_area = cw * ch + eps  # convex area
  75.             # 输出GIoU
  76.             return iou - (c_area - union) / c_area
  77.     else:
  78.         # 输出IoU
  79.         return iou
  80.  
  81.  
  82. if __name__ == '__main__':
  83.     box1 = torch.from_numpy(np.asarray([170, 110, 310, 370]))
  84.     box1 = box1.expand(1, 1, 1, 1, 4)
  85.     # 有交集
  86.     box2 = torch.from_numpy(np.asarray([250, 60, 375, 300]))
  87.     box2 = box2.expand(1, 1, 1, 1, 4)
  88.     # 无交集
  89.     box3 = torch.from_numpy(np.asarray([730, 420, 1000, 700]))
  90.     box3 = box3.expand(1, 1, 1, 1, 4)
  91.     print('iou有交集:', bbox_iou(box1, box2))
  92.     print('giou有交集:', bbox_iou(box1, box2, giou=True))
  93.     print('diou有交集:', bbox_iou(box1, box2, diou=True))
  94. print('ciou有交集:', bbox_iou(box1, box2, ciou=True))
  95.     print('eiou有交集:', bbox_iou(box1, box2, eiou=True))
  96.     print("=" * 20)
  97.     print('iou无交集:', bbox_iou(box1, box3))
  98.     print('giou无交集:', bbox_iou(box1, box3, giou=True))
  99.     print('diou无交集:', bbox_iou(box1, box3, diou=True))
  100.     print('ciou无交集:', bbox_iou(box1, box3, ciou=True))
  101.     print('eiou无交集:', bbox_iou(box1, box3, eiou=True))
  102.  

8.C++ 实现

8.1 IOU计算

  1. struct bbox
  2. {
  3. int m_left;
  4. int m_top;
  5. int m_width;
  6. int m_height;
  7. bbox() {}
  8. bbox(int left, int top, int width, int height)
  9. {
  10. m_left = left;
  11. m_top = top;
  12. m_width = width;
  13. m_height = height;
  14. }
  15. };
  16. float IOU_compute(const bbox b1, const bbox b2)
  17. {
  18. w = max(min((b1.m_left + b1.m_width), (b2.m_left + b2.m_width)) - max(b1.m_left, b2.m_left), 0);
  19. h = max(min((b1.m_top + b1.m_height), (b2.m_top + b2.m_height)) - max(b1.m_top, b2.m_top), 0);
  20. return w*h / (b1.m_width*b1.m_height + b2.m_width*b2.m_height - w*h);
  21. }

8.2 NMS计算

NMS(non maximum suppression),中文名非极大值抑制,在很多计算机视觉任务中都有广泛应用,如:边缘检测、目标检测等。

在物体检测中NMS(Non-maximum suppression)非极大抑制应用十分广泛,其目的是为了消除多余的框,找到最佳的物体检测的位置。

在RCNN系列算法中,会从一张图片中找出很多个候选框(可能包含物体的矩形边框),然后为每个矩形框为做类别分类概率。

就像上面的图片一样,定位一个车辆,最后算法就找出了一堆的方框,我们需要判别哪些矩形框是没用的。

非极大值抑制:先假设有6个候选框,根据分类器类别分类概率做排序,从小到大分别属于车辆的概率分别为A、B、C、D、E、F。

  1. 从最大概率矩形框(即面积最大的框)F开始,分别判断A~E与F的重叠度IOU是否大于某个设定的阈值;
  2. 假设B、D与F的重叠度超过阈值,那么就扔掉B、D(因为超过阈值,说明D与F或者B与F,已经有很大部分是重叠的,那我们保留面积最大的F即可,其余小面积的B,D就是多余的,用F完全可以表示一个物体了,所以保留F丢掉B,D);并标记第一个矩形框F,是我们保留下来的。
  3. 从剩下的矩形框A、C、E中,选择概率最大的E,然后判断E与A、C的重叠度,重叠度大于一定的阈值,那么就扔掉;并标记E是我们保留下来的第二个矩形框。
  4. 一直重复这个过程,找到所有曾经被保留下来的矩形框。
  1. //升序排列
  2. bool cmpScore(Bbox lsh, Bbox rsh) {
  3. if (lsh.score < rsh.score)
  4. return true;
  5. else
  6. return false;
  7. }
  8. void nms(vector<Bbox> &boundingBox_, const float overlap_threshold, string modelname = "Union"){
  9. if(boundingBox_.empty()){
  10. return;
  11. }
  12. //对各个候选框根据score的大小进行升序排列
  13. sort(boundingBox_.begin(), boundingBox_.end(), cmpScore);
  14. float IOU = 0;
  15. float maxX = 0;
  16. float maxY = 0;
  17. float minX = 0;
  18. float minY = 0;
  19. vector<int> vPick;
  20. int nPick = 0;
  21. multimap<float, int> vScores; //存放升序排列后的score和对应的序号
  22. const int num_boxes = boundingBox_.size();
  23. vPick.resize(num_boxes);
  24. for (int i = 0; i < num_boxes; ++i){
  25. vScores.insert(pair<float, int>(boundingBox_[i].score, i));
  26. }
  27. while(vScores.size() > 0){
  28. int last = vScores.rbegin()->second; //反向迭代器,获得vScores序列的最后那个***
  29. vPick[nPick] = last;
  30. nPick += 1;
  31. for (multimap<float, int>::iterator it = vScores.begin(); it != vScores.end();){
  32. int it_idx = it->second;
  33. maxX = max(boundingBox_.at(it_idx).x1, boundingBox_.at(last).x1);
  34. maxY = max(boundingBox_.at(it_idx).y1, boundingBox_.at(last).y1);
  35. minX = min(boundingBox_.at(it_idx).x2, boundingBox_.at(last).x2);
  36. minY = min(boundingBox_.at(it_idx).y2, boundingBox_.at(last).y2);
  37. //转换成了两个边界框相交区域的边长
  38. maxX = ((minX-maxX+1)>0)? (minX-maxX+1) : 0;
  39. maxY = ((minY-maxY+1)>0)? (minY-maxY+1) : 0;
  40. //求交并比IOU
  41. IOU = (maxX * maxY)/(boundingBox_.at(it_idx).area + boundingBox_.at(last).area - IOU);
  42. if(IOU > overlap_threshold){
  43. it = vScores.erase(it); //删除交并比大于阈值的候选框,erase返回删除元素的下一个元素
  44. }else{
  45. it++;
  46. }
  47. }
  48. }
  49. vPick.resize(nPick);
  50. vector<Bbox> tmp_;
  51. tmp_.resize(nPick);
  52. for(int i = 0; i < nPick; i++){
  53. tmp_[i] = boundingBox_[vPick[i]];
  54. }
  55. boundingBox_ = tmp_;

9.总结

边界框回归的三大几何因素:重叠面积、中心点距离、纵横比。

重叠中心点纵横比优点缺点
IoU

××尺度不变性,非负性;同一性;对称性;三角不等性。1.如果两个框不相交,不能反映两个框距离远近 2.无法精确的反映两个框的重合度大小。
GIoU××解决检测框和真实框没有重叠时loss等于0问题

1.当检测框和真实框出现包含现象的时候GIOU退化成IOU。

2.两个框相交时,在水平和垂直方向上收敛慢。

DIoU×直接回归两个框中心点的欧式距离,加速收敛。回归过程中未考虑Bounding box的纵横比,精确度上尚有进一步提升的空间。
CIoU增加了检测框尺度的loss,增加了长和宽的loss,这样预测框就会更加的符合真实框。

1. 纵横比描述的是相对值,存在一定的模糊。

2. 未考虑难易样本的平衡问题

EIoU分别计算宽高的差异值取代了纵横比,同时引入Focal Loss解决难易样本不平衡的问题。

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/凡人多烦事01/article/detail/719429
推荐阅读
相关标签
  

闽ICP备14008679号