IoU
IoU的值表示预测框A和真实框B之间交并比的差值,反映预测检测框的检测效果。
公式:
I
o
U
=
(
A
∩
B
A
∪
B
)
IoU= (\frac {A ∩ B}{A ∪ B} ) \quad
IoU=(A∪BA∩B)
IoU_loss其实就是
I
o
U
l
o
s
s
=
1
−
I
o
U
IoU_{loss} = 1 - IoU
IoUloss=1−IoU
两个矩形直接的交并比越大,那么对应的IoU_loss就会越小。
GIoU
IoU有两个个弊端,第一个就跟下面这张图一样,预测框和真实框之间的IoU都为0,但是上面的预测的预测框明显要比下面的好一点,那这样IoU_loss就没有了对比性。
第二个弊端就是下面的图片,它们的IoU其实都是一样的,但是两个矩形框的重合的明显有差别,那IoU_loss此时也失去了对比性。
所以GIoU就来了。
GIoU就是在原来的IOU损失的基础上加上一个惩罚项。
蓝色是真实目标边界框A,红色是预测目标边界框B,最外面的黄色边框C是将红绿矩形用最小矩形框起来的边界。加入非重合区域的影响,当IOU值相同时,非重合区域占比越小,代表预测框与目标框的对比效果越好。当A和B完全重合时,GIOU = 1。当A和B交集为空时,且A和B离的无限远时,即GIOU = -1。
G I o U = ( I o U − ∣ C − A ∪ B ∣ C ) GIoU= (IoU - \frac {|C - A ∪ B|}{C} ) \quad GIoU=(IoU−C∣C−A∪B∣)
DIoU
好像是为了加快GIoU的收敛速度,DIOU在IOU损失的基础上加一个惩罚项,用来最小化两个检测框中心点之间的标准化距离。用对角线距离把检测框和预测框的中心点距离进行归一化,在IOU值相同的情况下,两个框的中心点归一化距离越小,代表预测框与目标框的对比效果越好。中心点的归一化距离代替了GIOU中的非重合区域占比指标。
D
I
o
U
=
(
I
o
U
−
p
2
(
A
,
B
)
c
2
)
DIoU= (IoU - \frac {p^2(A,B)}{c^2} ) \quad
DIoU=(IoU−c2p2(A,B))
p
(
A
,
B
)
p(A,B)
p(A,B)是A框与B框中心点坐标的欧式距离,而
c
c
c则是包住它们的最小方框的对角线距离。
CIoU
CIoU的作者考虑到bbox回归三要素中的长宽比还没被考虑到计算中,因此,进一步在DIoU的基础上提出了CIoU。其惩罚项如下面公式:
D
I
o
U
=
(
I
o
U
−
p
2
(
A
,
B
)
c
2
−
α
v
)
DIoU= (IoU - \frac {p^2(A,B)}{c^2} -αv)
DIoU=(IoU−c2p2(A,B)−αv)
其中:
v
=
4
π
2
(
a
r
c
t
a
n
w
g
t
h
g
t
−
a
r
c
t
a
n
w
h
)
2
α
=
v
1
−
I
o
U
+
v
v = \frac {4}{\pi ^2} (arctan \frac {w^{gt}}{h ^{gt}} - arctan \frac {w}{h})^2 \quad\quad α= \frac {v}{1-IoU+v}
v=π24(arctanhgtwgt−arctanhw)2α=1−IoU+vv
IoU、GIoU、CIoU、DIoU的对比
IoU | GIoU | DIoU | CIoU | |
---|---|---|---|---|
优点 | IOU算法是目标检测中最常用的指标,具有尺度不变性,满足非负性;同一性;对称性;三角不等性等特点。 | GIOU在基于IOU特性的基础上引入最小外接框解决检测框和真实框没有重叠时loss等于0问题。 | DIOU在基于IOU特性的基础上考虑到GIOU的缺点,直接回归两个框中心点的欧式距离,加速收敛。 | CIOU就是在DIOU的基础上增加了检测框尺度的loss,增加了长和宽的loss,这样预测框就会更加的符合真实框。 |
缺点 | 1.如果两个框不相交,不能反映两个框距离远近 2.无法精确的反映两个框的重合度大小 | 1.当检测框和真实框出现包含现象的时候GIOU退化成IOU 2.两个框相交时,在水平和垂直方向上收敛慢 | 回归过程中未考虑Bounding box的纵横比,精确度上尚有进一步提升的空间 | 1. 纵横比描述的是相对值,存在一定的模糊 2.未考虑难易样本的平衡问题 |
代码
import cv2
import numpy as np
import math
def CountIOU(RecA, RecB, IoU_loss="IoU"):
"""
:param RecA: 预测框
:param RecB: 真实框
:param IoU_loss: 不同IoU计算方法: IoU GIoU DIoU CIoU
:return: IoU值,要计算IoU loss还要用1减去IoU值
"""
iou_result = 0
x1 = max(RecA[0], RecB[0])
y1 = max(RecA[1], RecB[1])
x2 = min(RecA[2], RecB[2])
y2 = min(RecA[3], RecB[3])
# 计算交集部分面积
interArea = max(0, x2 - x1) * max(0, y2 - y1 )
# 计算预测值和真实值的面积
RecA_Area = max(0, (RecA[2] - RecA[0]) * (RecA[3] - RecA[1]))
RecB_Area = max(0, (RecB[2] - RecB[0]) * (RecB[3] - RecB[1]))
ComAB_Area = RecA_Area + RecB_Area - interArea
# 计算IOU
iou = interArea / float(ComAB_Area)
if IoU_loss == "IoU":
iou_result = iou
elif IoU_loss == "GIoU":
# 计算真实框与预测框的最小外接矩形C的面积
c_x1 = min(RecA[0], RecB[0])
c_y1 = min(RecA[1], RecB[1])
c_x2 = max(RecA[2], RecB[2])
c_y2 = max(RecA[3], RecB[3])
RecC_Area = max(0, (c_x2 - c_x1) * (c_y2 - c_y1))
# 计算GIoU
iou_result = iou - (RecC_Area - ComAB_Area) / float(RecC_Area)
elif IoU_loss == "DIoU":
# 计算真实框与预测框中心点的欧式距离
xA = RecA[0] - RecA[2]
yA = RecA[1] - RecA[3]
xB = RecB[0] - RecB[2]
yB = RecB[1] - RecB[3]
dist = pow(xA - xB, 2) + pow(yA - yB, 2)
# 计算真实框与预测框的最小外接矩形对角线的距离
c_x1 = min(RecA[0], RecB[0])
c_y1 = min(RecA[1], RecB[1])
c_x2 = max(RecA[2], RecB[2])
c_y2 = max(RecA[3], RecB[3])
diag = pow(c_x1 - c_x2, 2) + pow(c_y1 - c_y2, 2)
# 计算GIoU
iou_result = iou - dist / float(diag)
elif IoU_loss == "CIoU":
# 计算真实框与预测框中心点的欧式距离
xA = RecA[0] - RecA[2]
yA = RecA[1] - RecA[3]
xB = RecB[0] - RecB[2]
yB = RecB[1] - RecB[3]
dist = pow(xA - xB, 2) + pow(yA - yB, 2)
# 计算真实框与预测框的最小外接矩形对角线的距离
c_x1 = min(RecA[0], RecB[0])
c_y1 = min(RecA[1], RecB[1])
c_x2 = max(RecA[2], RecB[2])
c_y2 = max(RecA[3], RecB[3])
diag = pow(c_x1 - c_x2, 2) + pow(c_y1 - c_y2, 2)
# 计算真实框和预测框的高宽
w_p = max(0, RecA[2] - RecA[0])
h_p = max(0, RecA[3] - RecA[1])
w_gt = max(0, RecB[2] - RecB[0])
h_gt = max(0, RecB[3] - RecB[1])
# 计算参数 v 和 α
v = ( 4 * pow( (math.atan(w_gt/float(h_gt)) - math.atan(w_p/float(h_p))), 2) ) / pow(math.pi, 2)
a = v / (( 1 - iou) + v)
#计算CIoU
iou_result = iou - (pow(dist,2) / float(pow(diag,2))) - (a*v)
else:
raise NotImplementedError('IoU_loss method [%s] is not implemented' % IoU_loss)
return iou_result
img = np.zeros((512, 512, 3), np.uint8)
img.fill(255)
RecA = [50, 50, 300, 300]
RecB = [90, 90, 320, 320]
cv2.rectangle(img, (RecA[0], RecA[1]), (RecA[2], RecA[3]), (0, 255, 0), 5)
cv2.rectangle(img, (RecB[0], RecB[1]), (RecB[2], RecB[3]), (255, 0, 0), 5)
IOU = CountIOU(RecA, RecB, IoU_loss="DIoU")
font = cv2.FONT_HERSHEY_SIMPLEX
cv2.putText(img, "IOU = %.2f" % IOU, (130, 190), font, 0.8, (0, 0, 0), 2)
cv2.imshow("image", img)
cv2.waitKey()
cv2.destroyAllWindows()