目标检测任务的损失函数由Classificition Loss和Bounding Box Regeression Loss两部分构成。
Bounding Box Regression Loss Function的演进路线是:
Smooth L1 Loss --> IoU Loss --> GIoU Loss --> DIoU Loss --> CIoU Loss
之前写到了 Smooth L1 Loss 和 IoU Loss。
本文介绍GIoU Loss。
IoU Loss 存在的问题:
GIoU Loss计算过程如下:
以上图为例,绿色框表示预测框 B p = ( x 1 p , y 1 p , x 2 p , y 2 p ) B^p=(x_1^p,y_1^p,x_2^p,y_2^p) Bp=(x1p,y1p,x2p,y2p),黑色框表示边界框 B g = ( x 1 g , y 1 g , x 2 g , y 2 g ) B^g=(x_1^g,y_1^g,x_2^g,y_2^g) Bg=(x1g,y1g,x2g,y2g),首先计算IoU:
I o U = I U IoU = \frac{I}{U} IoU=UI
其中, I I I 表示:上图中的灰色阴影部分, U U U 表示:两个矩形面积之和 ( A p + A g ) (A^p +A^g) (Ap+Ag)减去两个矩形相交的面积 I I I,因此IoU也可以表示为:
GIoU在IoU基础上,考虑了两个矩形最小闭包(the smallest enclosing convex object,两个矩形的最小外接矩形)的大小,GIoU的计算表达式为:
其中, − 1 ≤ G I o U < 1 -1≤GIoU<1 −1≤GIoU<1, A c A^c Ac是两个矩形的最小外接矩形的面积,也就是上图中虚线框的面积。
G I o U L o s s = 1 − G I o U GIoU Loss = 1-GIoU GIoULoss=1−GIoU
此时 0 < G I o U L o s s ≤ 2 0
算法计算过程对比:
可见计算GIoU损失的方式其实就是计算GIoU,只不过最终结果返回的是1-GIoU。
这是因为1-GIoU的取值范围在[0,2]上,且有一定的“距离”性质,即两个框重叠区域越大,损失越小,反之越大。
图看GIoU到底解决了IoU的什么问题
如上图所示,三种不同相对位置的框拥有相同的IoU=0.33值,但是拥有不同的GIoU=0.33,0.24,-0.1。当框的对齐方向更好一些时GIoU的值会更高一些。
很简单,直接看代码注释即可。
import numpy as np
import cv2
def CountIOU(RecA, RecB):
xA = max(RecA[0], RecB[0])
yA = max(RecA[1], RecB[1])
xB = min(RecA[2], RecB[2])
yB = min(RecA[3], RecB[3])
# 计算交集部分面积
interArea = max(0, xB - xA + 1) * max(0, yB - yA + 1)
# 计算预测值和真实值的面积
RecA_Area = (RecA[2] - RecA[0] + 1) * (RecA[3] - RecA[1] + 1)
RecB_Area = (RecB[2] - RecB[0] + 1) * (RecB[3] - RecB[1] + 1)
# 计算IOU
iou = interArea / float(RecA_Area + RecB_Area - interArea)
return iou
def Giou(rec1,rec2):
# 分别是矩形左上、右下的坐标
x1,y1,x2,y2 = rec1
x3,y3,x4,y4 = rec2
iou = CountIOU(rec1,rec2)
area_C = (max(x1,x2,x3,x4)-min(x1,x2,x3,x4))*(max(y1,y2,y3,y4)-min(y1,y2,y3,y4))
area_1 = (x2-x1)*(y1-y2)
area_2 = (x4-x3)*(y3-y4)
sum_area = area_1 + area_2
w1 = x2 - x1 #第一个矩形的宽
w2 = x4 - x3 #第二个矩形的宽
h1 = y1 - y2
h2 = y3 - y4
W = min(x1,x2,x3,x4)+w1+w2-max(x1,x2,x3,x4) # 交叉部分的宽
H = min(y1,y2,y3,y4)+h1+h2-max(y1,y2,y3,y4) # 交叉部分的高
# 交叉的面积
Area = W * H
# 两矩形并集的面积
add_area = sum_area - Area
# 闭包区域中不属于两个框的区域占闭包区域的比重
end_area = (area_C - add_area)/area_C
giou = iou - end_area
return giou
img = np.zeros((512,512,3), np.uint8)
img.fill(255)
# 分别是矩形左上、右下的坐标
RecA = [30,30,300,300]
RecB = [60,60,350,340]
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)
GIoU = Giou(RecA,RecB)
font = cv2.FONT_HERSHEY_SIMPLEX
cv2.putText(img,"IOU = %.2f"%IOU,(130, 190),font,0.8,(0,0,0),2)
cv2.putText(img,"GIOU = %.2f"%GIoU,(130, 220),font,0.8,(0,0,0),2)
cv2.imshow("image",img)
cv2.waitKey()
cv2.destroyAllWindows()
https://zhuanlan.zhihu.com/p/104236411
https://blog.csdn.net/A_A666/article/details/111504851
https://zhuanlan.zhihu.com/p/94799295