目录
一、简介
二、IoU(Intersection over Union)
三、GIoU(Generalized IoU)
四、DIoU(Distance-IoU)
五、CIoU(Complete-IoU)
六、EIoU(Efficient-IoU)
七、pytorch代码实现
八、总结
一、简介
在目标检测任务中,常用到一个指标IoU,即交并比,IoU可以很好的描述一个目标检测模型的好坏。在训练阶段IoU可以作为anchor-based方法中,划分正负样本的依据;同时也可用作损失函数;在推理阶段,NMS中会用到IoU。同时IoU有着比较严重的缺陷,于是出现了GIoU、DIoU、CIoU、EIoU,下面我们一起看一下这几种IoU。
二、IoU(Intersection over Union)
IoU的计算是用预测框(A)和真实框(B)的交集除以二者的并集,其公式为:
IoU的值越高也说明A框与B框重合程度越高,代表模型预测越准确。反之,IoU越低模型性能越差。
IoU优点:
(1)IoU具有尺度不变性
(2)结果非负,且范围是(0, 1)
IoU缺点:
(1)如果两个目标没有重叠,IoU将会为0,并且不会反应两个目标之间的距离,在这种无重叠目标的情况下,如果IoU用作于损失函数,梯度为0,无法优化。
(2)IoU无法精确的反映两者的重合度大小。如下图所示,三种情况IoU都相等,但看得出来他们的重合度是不一样的,左边的图回归的效果最好,右边的最差。
三、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将很难区分这种情况。
四、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不能很好的区分这种情况。
五、CIoU(Complete-IoU)
CIoU与DIoU出自同一篇论文,CIoU大多数用于训练。DIoU的作者考虑到,在两个框中心点重合时,c与d的值都不变。所以此时需要引入框的宽高比:
其中α是权重函数,v用来度量宽高比的一致性:
最终CIoU Loss定义为:
优点:
考虑了框的纵横比,可以解决DIoU的问题。
缺点:
通过CIoU公式中的v反映的纵横比的差异,而不是宽高分别与其置信度的真实差异,所以有时会阻碍模型有效的优化相似性。
六、EIoU(Efficient-IoU)
论文地址:https://arxiv.org/pdf/2101.08158.pdf
为了解决CIoU的问题,有学者在CIOU的基础上将纵横比拆开,提出了EIOU Loss,并且加入Focal聚焦优质的预测框,与CIoU相似的,EIoU是损失函数的解决方案,只用于训练。
EIOU的惩罚项是在CIOU的惩罚项基础上将纵横比的影响因子拆开分别计算目标框和预测框的长和宽,该损失函数包含三个部分:重叠损失,中心距离损失,宽高损失,前两部分延续CIoU中的方法,但是宽高损失直接使目标框与预测框的宽度和高度之差最小,使得收敛速度更快。惩罚项公式如下:
其中和是覆盖两个Box的最小外接框的宽度和高度。
通过整合EIoU Loss和FocalL1 loss,最终得到了最终的Focal-EIoU loss,其中 γ是一个用于控制曲线弧度的超参。
优点:
1)将纵横比的损失项拆分成预测的宽高分别与最小外接框宽高的差值,加速了收敛提高了回归精度。
2)引入了Focal Loss优化了边界框回归任务中的样本不平衡问题,即减少与目标框重叠较少的大量锚框对BBox 回归的优化贡献,使回归过程专注于高质量锚框。
七、pytorch代码实现
代码中使用pytorch是方便在作为损失函数的时候,方便反向传播,下面的代码计算各种IoU,用作损失函数的时候需要用1减去返回结果。
import torch
import math
import numpy as np
def bbox_iou(box1, box2, xywh=False, giou=False, diou=False, ciou=False, eiou=False, eps=1e-7):
"""
实现各种IoU
Parameters
----------
box1 shape(b, c, h, w,4)
box2 shape(b, c, h, w,4)
xywh 是否使用中心点和wh,如果是False,输入就是左上右下四个坐标
GIoU 是否GIoU
DIoU 是否DIoU
CIoU 是否CIoU
EIoU 是否EIoU
eps 防止除零的小量
Returns
-------
"""
# 获取边界框的坐标
# 判断输入的box数据格式,如果为xywh,则将其转换为xyxy格式
if xywh:
# xywh转为xyxy,对于box1和box2分别进行操作
# box1和box2是包含四个元素的张量,这四个元素分别是x, y, w, h
# 这里的xy1和xy2是将原始的box1和box2的坐标中心点计算出来,然后根据新的宽度和高度(原宽度和高度的算术平均值)计算出新的坐标范围
b1_x1, b1_x2 = box1[..., 0] - box1[..., 2] / 2, box1[..., 0] + box1[..., 2] / 2
b1_y1, b1_y2 = box1[..., 1] - box1[..., 3] / 2, box1[..., 1] + box1[..., 3] / 2
b2_x1, b2_x2 = box2[..., 0] - box2[..., 2] / 2, box2[..., 0] + box2[..., 2] / 2
b2_y1, b2_y2 = box2[..., 1] - box2[..., 3] / 2, box2[..., 1] + box2[..., 3] / 2
else:
# 如果输入的box数据格式不是xywh,则直接使用输入的box数据(x1, y1, x2, y2)
# box1和box2是包含四个元素的张量,这四个元素分别是x1, y1, x2, y2
b1_x1, b1_y1, b1_x2, b1_y2 = box1[..., 0], box1[..., 1], box1[..., 2], box1[..., 3]
b2_x1, b2_y1, b2_x2, b2_y2 = box2[..., 0], box2[..., 1], box2[..., 2], box2[..., 3]
# 计算两个box的交集面积
# inter = (torch.min(b1_x2, b2_x2) - torch.max(b1_x1, b2_x1)).clamp(0) * \
# (torch.min(b1_y2, b2_y2) - torch.max(b1_y1, b2_y1)).clamp(0)
inter = torch.min(b1_x2, b2_x2) - torch.max(b1_x1, b2_x1) # 行交集
inter = torch.min(inter, torch.zeros_like(inter)) # 对可能出现的负值使用零进行替换
inter = inter.clamp(0) # 对可能出现的负值使用零进行替换
inter = inter * torch.min(b1_y2, b2_y2) - torch.max(b1_y1, b2_y1) # 列交集
inter = torch.min(inter, torch.zeros_like(inter)) # 对可能出现的负值使用零进行替换
inter = inter.clamp(0) # 对可能出现的负值使用零进行替换
# 计算两个box的并集面积(不考虑重叠部分)
w1, h1 = b1_x2 - b1_x1, b1_y2 - b1_y1 + eps # box1的宽和高
w2, h2 = b2_x2 - b2_x1, b2_y2 - b2_y1 + eps # box2的宽和高
union = w1 * h1 + w2 * h2 - inter + eps # 并集面积,需要减去交集部分以避免重复计算
# 计算IOU(Intersection over Union)值,即两个box的交集面积除以两个box的并集面积
iou = inter / union # IOU值
# 判断是否需要计算GIoU、DIoU、CIoU、EIoU中的至少一种
if giou or diou or ciou or eiou:
# 计算两个box的最小外接矩形的宽度和高度
# 矩形的左上和右下坐标分别为(b1_x1, b1_y1)和(b1_x2, b1_y2),(b2_x1, b2_y1)和(b2_x2, b2_y2)
cw = torch.max(b1_x2, b2_x2) - torch.min(b1_x1, b2_x1)
ch = torch.max(b1_y2, b2_y2) - torch.min(b1_y1, b2_y1)
# 如果需要计算CIoU、DIoU、EIoU中的至少一种
if ciou or diou or eiou:
# 计算最小外接矩形角线的平方
c2 = cw ** 2 + ch ** 2 + eps # eps是防止除数为0的微小值
# 计算最小外接矩形中点距离的平方
rho2 = ((b2_x1 + b2_x2 - b1_x1 - b1_x2) ** 2 +
(b2_y1 + b2_y2 - b1_y1 - b1_y2) ** 2) / 4
# 如果需要计算DIoU
if diou:
# 输出DIoU
return iou - rho2 / c2
# 如果需要计算CIoU
elif ciou:
v = (4 / math.pi ** 2) * torch.pow(torch.atan(w2 / h2) - torch.atan(w1 / h1), 2)
with torch.no_grad():
alpha = v / (v - iou + (1 + eps)) # eps是防止除数为0的微小值
# 输出CIoU
return iou - (rho2 / c2 + v * alpha)
# 如果需要计算EIoU
elif eiou:
rho_w2 = ((b2_x2 - b2_x1) - (b1_x2 - b1_x1)) ** 2
rho_h2 = ((b2_y2 - b2_y1) - (b1_y2 - b1_y1)) ** 2
cw2 = cw ** 2 + eps # eps是防止除数为0的微小值
ch2 = ch ** 2 + eps # eps是防止除数为0的微小值
# 输出EIoU
return iou - (rho2 / c2 + rho_w2 / cw2 + rho_h2 / ch2)
else:
# 计算凸包面积(最小外接矩形的面积)
c_area = cw * ch + eps # eps是防止除数为0的微小值
# 输出GIoU,GIoU是GIoU、CIoU、DIoU、EIoU之外的IoU变种,它的计算方式和IoU相同,只是最后用凸包面积代替了交集面积
return iou - (c_area - union) / c_area
else:
# 如果不需要计算任何IoU的变种,则直接输出原始的IoU值
# 这里的iou应该是在函数外部定义的变量,表示两个box的交集面积除以两个box的并集面积
return iou
if __name__ == '__main__':
box1 = torch.from_numpy(np.asarray([170, 110, 310, 370]))
box1 = box1.expand(1, 1, 1, 1, 4)
# 有交集
box2 = torch.from_numpy(np.asarray([250, 60, 375, 300]))
box2 = box2.expand(1, 1, 1, 1, 4)
# 无交集
box3 = torch.from_numpy(np.asarray([730, 420, 1000, 700]))
box3 = box3.expand(1, 1, 1, 1, 4)
print('iou有交集:', bbox_iou(box1, box2))
print('giou有交集:', bbox_iou(box1, box2, giou=True))
print('diou有交集:', bbox_iou(box1, box2, diou=True))
print('ciou有交集:', bbox_iou(box1, box2, ciou=True))
print('eiou有交集:', bbox_iou(box1, box2, eiou=True))
print("=" * 20)
print('iou无交集:', bbox_iou(box1, box3))
print('giou无交集:', bbox_iou(box1, box3, giou=True))
print('diou无交集:', bbox_iou(box1, box3, diou=True))
print('ciou无交集:', bbox_iou(box1, box3, ciou=True))
print('eiou无交集:', bbox_iou(box1, box3, eiou=True))
八、总结
边界框回归的三大几何因素:重叠面积、中心点距离、纵横比。
重叠 | 中心点 | 纵横比 | 优点 | 缺点 | |
---|---|---|---|---|---|
IoU | √ | × | × | 尺度不变性,非负性;同一性;对称性;三角不等性。 | 1.如果两个框不相交,不能反映两个框距离远近 2.无法精确的反映两个框的重合度大小 |
GIoU | √ | × | × | 解决检测框和真实框没有重叠时loss等于0问题 | 1.当检测框和真实框出现包含现象的时候GIOU退化成IOU2.两个框相交时,在水平和垂直方向上收敛慢 |
DIoU | √ | √ | × | 直接回归两个框中心点的欧式距离,加速收敛。 | 回归过程中未考虑Bounding box的纵横比,精确度上尚有进一步提升的空间 |
CIoU | √ | √ | √ | 增加了检测框尺度的loss,增加了长和宽的loss,这样预测框就会更加的符合真实框。 | 1. 纵横比描述的是相对值,存在一定的模糊2. 未考虑难易样本的平衡问题 |
EIoU | √ | √ | √ | 分别计算宽高的差异值取代了纵横比,同时引入Focal Loss解决难易样本不平衡的问题。 |