一文读懂IoU,GIoU, DIoU, CIoU, Alpha-IoU (代码非常优雅)
作者:互联网
IoU就是就是我们说的交并比 Intersection over Union ,具体就是两个box的交集除以并集。
当我们计算我们的anchors 或者 proposals 与 ground truth bounding boxes 的损失的时候,就需要用到IoU。不同的IoU有不同的特性。
IoU:
IoU计算了最简单的情况:
GIoU:
当两个anchor与gt box都不相交的时候,IoU的loss是一样大的,我们理论认为anchor距离gt box越近,loss应该越小,不应该一样大。这样GIoU就提出来了。GIoU通过计算两个box的最小闭包区域ac来计算loss。底色为红色的范围是Anchor2与Gt box的最小闭包区域,底色为黄色的范围是Anchor1与Gt box的最小闭包区域。明显Anchor2的最小闭包区域小,u代表并集,ac代表最小闭包区域,ac越大, \(L_{GIoU}\) 值越大。Anchor1的ac大,所以Anchor1的损失更高
\[L_{GIoU}=1- IoU+\frac{ac-u}{ac} \]DIoU:
黄色为proposal,蓝色为gt box。当propsals与gt box重叠时,我们认为下面的两种情况左边的效果好,因为它位于gt box中心。GIoU并不能解决这个问题,所以DIoU被提出来了,DIoU用,两个box中心点距离平方 除以 最小闭包区域对角线距离平方,来衡量预测的proposal是否位于gt box中心。
\[L_{DIoU}=IoU+\frac{\rho^2 (b,b^{gt})}{c^2} \]CIoU:
当两个proposals都位于gt box中心时,我们还是认为坐标的效果比较好,因为左边的宽高比跟我们的gt box一致,所以CIoU在DIoU的基础上改进了宽高比。
\[L_{CIoU}=1-IoU+\frac{\rho^2 (b,b^{gt})}{c^2}+\alpha v \]v用来度量长宽比的相似性。
\[v=\frac{4}{\pi^2}(arctan\frac{w^{gt}}{h^{gt}}-arctan\frac{w}{h}) ^2 \]alpha是权重值,衡量ciou公式中第三项和第四项的权重,当IoU越大,alpha就越大,alpha大就优先考虑v; IoU越小时,alpha越小,alpha小优先考虑第三项,距离比。
\[\alpha=\frac{v}{(1-IoU)+v} \]Alpha-IoU:
Alpha-IoU主要是考虑IoU大于0.5的时候的梯度,因为在普通 \(L_{IoU}=1-IoU\) 中,IoU的梯度一直是-1。但是在Alpha-IoU中,当iou大于0.5的时候,loss的梯度是大于-1的,收敛的更快,在map0.7/map0.9有提升效果。
\[L_{\alpha-IoU}=1-IoU^{\alpha} \]\(\alpha>0\),当 \(\alpha=2,iou=0.6\) 时,\(L_{\alpha-IoU}\) 的梯度已经是-1.2了。训练时经验所得alpha取3比较好。
下面是每个IoU的代码实现。
import math
def IoU(boxa, boxb):
"""
boxa/boxb:[x1,y1,x2,y2], x2,y2保证大于x1,y1
loss = 1 - iou
交集的左上角坐标正好是两个box的左上角角坐标的较大值,交集的右下角坐标正好是两个box的右下角坐标的较小值。
画出所有的情况判断一下就可以了,求并集面积的时候别忘了减去交集面积
"""
inter_x1, inter_y1 = max(boxa[0], boxb[0]), max(boxa[1], boxb[1])
inter_x2, inter_y2 = min(boxa[2], boxb[2]), min(boxa[3], boxb[3])
inter_h = max(0, inter_y2 - inter_y1)
inter_w = max(0, inter_x2 - inter_x1)
inter_area = inter_w * inter_h
union_area = ((boxa[3] - boxa[1]) * (boxa[2] - boxa[0])) + \
((boxb[3] - boxb[1]) * (boxb[2] - boxb[0])) - inter_area + 1e-8 # + 1e-8 防止除零
iou = inter_area / union_area
return iou
# 为了解决当两个bbox不相交时,距离远的和距离近的损失值一样大。我们认为距离近的损失应该小一点。
# 注意:划分anchor是否是正样本的时候,anchor与label不一定相交,这样giou能够起到积极的作用
# 当用正样本计算与label的iou损失时,这时候正样本与label都是相交的情况,这时候GIoU不一定起到积极的作用。
def GIoU(boxa, boxb):
"""
giou = iou-(|ac-u|)/|ac| ac最小闭包区域,u并集
loss = 1 - giou
"""
# 求交集
inter_x1, inter_y1 = max(boxa[0], boxb[0]), max(boxa[1], boxb[1])
inter_x2, inter_y2 = min(boxa[2], boxb[2]), min(boxa[3], boxb[3])
inter_w, inter_h = max(0, inter_x2 - inter_x1), max(0, inter_y2 - inter_y1)
inter_area = inter_w * inter_h
# 求并集
union_area = ((boxa[2] - boxa[0]) * boxa[3] - boxa[1]) + \
((boxb[2] - boxb[0]) * (boxb[3] - boxb[1])) - inter_area + 1e-8 # + 1e-8 防止除零
# 求最小闭包区域的x1,y1,x2,y2,h,w,area
ac_x1, ac_y1 = min(boxa[0], boxb[0]), min(boxa[1], boxb[1])
ac_x2, ac_y2 = max(boxa[2], boxb[2]), max(boxa[3], boxb[3])
ac_w = ac_x2 - ac_x1
ac_h = ac_y2 - ac_y1
ac_area = ac_w * ac_h
giou = (inter_area / union_area) - (abs(ac_area - union_area) / ac_area)
return giou
# 当boxes与真实box重合时,一个在中间重合,一个在边缘重合,我们认为在中间重合的是比较好的,
# 所以提出计算两个box中心点的距离,因为预测小目标的中心点box与真实值box本来距离就很小,
# 所以再除以一个最小闭包区域对角线长度,来平衡小目标和大目标的diou。都用平方不开根号减少计算量和精度损失。
def DIoU(boxa, boxb):
"""
diou=iou-两个box中心点距离平方/最小闭包区域对角线距离平方
loss=1-diou
"""
# 求交集
inter_x1, inter_y1 = max(boxa[0], boxb[0]), max(boxa[1], boxb[1])
inter_x2, inter_y2 = min(boxa[2], boxb[2]), min(boxa[3], boxb[3])
inter_w, inter_h = max(0, inter_x2 - inter_x1), max(0, inter_y2 - inter_y1)
inter_area = inter_w * inter_h
# 求并集
union_area = ((boxa[2] - boxa[0]) * boxa[3] - boxa[1]) + \
((boxb[2] - boxb[0]) * (boxb[3] - boxb[1])) - inter_area + 1e-8 # + 1e-8 防止除零
# 求最小闭包区域的x1,y1,x2,y2
ac_x1, ac_y1 = min(boxa[0], boxb[0]), min(boxa[1], boxb[1])
ac_x2, ac_y2 = max(boxa[2], boxb[2]), max(boxa[3], boxb[3])
# 把两个bbox的x1,y1,x2,y2转换成ctr_x,ctr_y
boxa_ctrx, boxa_ctry = boxa[0] + (boxa[2] - boxa[0]) / 2, boxa[1] + (boxa[3] - boxa[1]) / 2
boxb_ctrx, boxb_ctry = boxb[0] + (boxb[2] - boxb[0]) / 2, boxb[1] + (boxb[3] - boxb[1]) / 2
# 求两个box中心点距离平方length_box_ctr,最小闭包区域对角线距离平方length_ac,以及diou
length_box_ctr = (boxb_ctrx - boxa_ctrx) * (boxb_ctrx - boxa_ctrx) + \
(boxb_ctry - boxa_ctry) * (boxb_ctry - boxa_ctry)
length_ac = (ac_x2 - ac_x1) * (ac_x2 - ac_x1) + (ac_y2 - ac_y1) * (ac_y2 - ac_y1)
# 求平方,相乘是最快的
diou = inter_area / union_area - length_box_ctr / length_ac
return diou
# 当boxes与真实box重合时,且都在在中心点重合时,一个长宽比接近真实box,一个差异很大
# 我们认为长宽比接近的是比较好的,损失应该是比较小的。所以ciou增加了对box长宽比的考虑
def CIoU(boxa, boxb):
"""
ciou=iou+两个box中心点距离平方/最小闭包区域对角线距离平方+alpha*v
loss=1-iou+两个box中心点距离平方/最小闭包区域对角线距离平方+alpha*v
注意loss跟上边不一样,这里不是1-ciou
v用来度量长宽比的相似性,4/(pi *pi)*(arctan(boxa_w/boxa_h)-arctan(boxb_w/boxb_h))^2
alpha是权重值,衡量ciou公式中第二项和第三项的权重,
alpha大优先考虑v,alpha小优先考虑第二项距离比,alpha = v / ((1 - iou) + v)。
"""
# 求交集
inter_x1, inter_y1 = max(boxa[0], boxb[0]), max(boxa[1], boxb[1])
inter_x2, inter_y2 = min(boxa[2], boxb[2]), min(boxa[3], boxb[3])
inter_w, inter_h = max(0, inter_x2 - inter_x1), max(0, inter_y2 - inter_y1)
inter_area = inter_w * inter_h
# 求并集
union_area = ((boxa[2] - boxa[0]) * boxa[3] - boxa[1]) + \
((boxb[2] - boxb[0]) * (boxb[3] - boxb[1])) - inter_area + 1e-8 # + 1e-8 防止除零
# 求最小闭包区域的x1,y1,x2,y2
ac_x1, ac_y1 = min(boxa[0], boxb[0]), min(boxa[1], boxb[1])
ac_x2, ac_y2 = max(boxa[2], boxb[2]), max(boxa[3], boxb[3])
# 把两个bbox的x1,y1,x2,y2转换成ctr_x,ctr_y,w,h
boxa_ctrx, boxa_ctry = boxa[0] + (boxa[2] - boxa[0]) / 2, boxa[1] + (boxa[3] - boxa[1]) / 2
boxb_ctrx, boxb_ctry = boxb[0] + (boxb[2] - boxb[0]) / 2, boxb[1] + (boxb[3] - boxb[1]) / 2
boxa_w, boxa_h = boxa[2] - boxa[0], boxa[3] - boxa[1]
boxb_w, boxb_h = boxb[2] - boxb[0], boxb[3] - boxb[1]
# 求两个box中心点距离平方length_box_ctr,最小闭包区域对角线距离平方length_ac
length_box_ctr = (boxb_ctrx - boxa_ctrx) * (boxb_ctrx - boxa_ctrx) + \
(boxb_ctry - boxa_ctry) * (boxb_ctry - boxa_ctry)
length_ac = (ac_x2 - ac_x1) * (ac_x2 - ac_x1) + (ac_y2 - ac_y1) * (ac_y2 - ac_y1)
v = (4 / (math.pi * math.pi)) * (math.atan(boxa_w / boxa_h) - math.atan(boxb_w / boxb_h)) \
* (math.atan(boxa_w / boxa_h) - math.atan(boxb_w / boxb_h))
iou = inter_area / (union_area + 1e-8)
alpha = v / ((1 - iou) + v)
ciou = iou + length_box_ctr / length_ac + alpha * v
return ciou
# 除了alpha-iou,还有alpha-giou, alpha-diou, alpha-ciou,这里就不写了。
# alpha-iou的优点是,例如alpha取2,当iou大于0.5的时候,loss的梯度是大于1的,
# 相比iou的loss一直等于-1,收敛的更快,map0.7/map0.9有提升效果。
def AlphaIoU(boxa, boxb, alpha):
"""
loss = (1 - iou^alpha) alpha>0,取3效果比较好
"""
inter_x1, inter_y1 = max(boxa[0], boxb[0]), max(boxa[1], boxb[1])
inter_x2, inter_y2 = min(boxa[2], boxb[2]), min(boxa[3], boxb[3])
inter_h = max(0, inter_y2 - inter_y1)
inter_w = max(0, inter_x2 - inter_x1)
inter_area = inter_w * inter_h
union_area = ((boxa[3] - boxa[1]) * (boxa[2] - boxa[0])) + \
((boxb[3] - boxb[1]) * (boxb[2] - boxb[0])) - inter_area + 1e-8 # + 1e-8 防止除零
iou = inter_area / union_area
alpha_iou = (1 - math.pow(iou, alpha))
return alpha_iou
if __name__ == '__main__':
# box1 area=4, box2 area=4,inter area=1, union area=7, ac area=9, iou=1/7
box1, box2 = [0, 0, 2, 2], [1, 1, 3, 3]
# box1, box2 = [1, 0, 3, 2], [0, 1, 2, 3]
# 下面我把并集中1e-8省略了,所以会有略微差距。
print(IoU(box1, box2))
print(1 / 7)
# 0.14285714265306124
# 0.14285714285714285
print(GIoU(box1, box2))
print(1 / (7) - (9 - 7) / 9)
# -0.07936507845804988
# -0.07936507936507936
print(DIoU(box1, box2))
print(1 / 7 - (1 * 1) / (3 * 3))
# 0.03174603154195013
# 0.031746031746031744
print(CIoU(box1, box2))
v = 4 / (math.pi * math.pi) * ((math.atan(2 / 2) - math.atan(2 / 2)) * (math.atan(2 / 2) - math.atan(2 / 2)))
print(1 / 7 + (1 * 1) / (3 * 3) + v / ((1 - 1 / 7) + v) * v)
# 0.2539682535600907
# 0.25396825396825395
print(AlphaIoU(box1, box2, 1)) # 求的是loss,所以跟第一个iou()值不一样
print(1 - 1 / 7)
# 0.8571428573469387
# 0.8571428571428572
print(AlphaIoU(box1, box2, 3)) # 求的是loss,所以跟第一个iou()值不一样
print(1 - math.pow((1 / 7), 3))
# 0.9970845481174511
# 0.9970845481049563
标签:GIoU,box,ac,area,IoU,boxa,boxb,DIoU,inter 来源: https://www.cnblogs.com/gy77/p/16199305.html