上期讲解了目标检测中的三种数据增强的方法,这期我们讲讲目标检测中用来评估对象检测算法的IOU和CIOU的原理应用以及代码实现。
交并比IOU
交并比IOU(Intersection over union)
在目标检测任务中,我们用框框来定位对象,如下图定位图片中这个汽车,假设实际框是图中红色的框框,你的算法预测给出的是紫色的框框,怎么判断你的算法预测的这个框框的效果好坏呢?
这就用到我们的交并比函数IOU了,计算公式如下:
将我们图片汽车的实际红色框记为A,算法的预测框记为B,交并比就是数学中A和B的交集A∩B跟A和B的并集的A∪B的面积之比,非常容易理解。IOU实际上衡量了两个边界框重叠地相对大小,预测框和真实框重叠越大,说明你的算法预测效果比较好,IOU是一个评价指标。
那么回到刚刚的问题:如何利用IOU进行判断算法的预测效果好坏呢?也就是这个预测框是否可以当做正确的定位到我们的目标汽车呢?
在计算机检测任务中,如果我们算法的预测框和实际框之间的交并比IOU≧0.5,那么你的算法预测结果是可以接受的,就说你预测的这个框框是正确的,这个阈值0.5,你可以设置得更高,边界框越精确,在YOLOv3中正是用到这个IOU来对我们的先验框进行了一个筛选,以及计算测试集的模型效果mAP时也用到了IOU进行阈值判断。
效果图
首先借助Opencv和numpy框画两个框,然后计算IOU进行展示,如下图。
IOU实现代码
以框的左上角(x1,y1)和右下角(x2,y2)坐标形式来计算它们之间的IOU。
importcv2
importnumpyasnp
defCountIOU(RecA,RecB):
xA =max(RecA[],RecB[])
yA =max(RecA[1],RecB[1])
xB =min(RecA[2],RecB[2])
yB =min(RecA[3],RecB[3])
#计算交集部分面积
interArea =max(,xB - xA +1) *max(,yB - yA +1)
#计算预测值和真实值的面积
RecA_Area = (RecA[2] - RecA[] +1) * (RecA[3] - RecA[1] +1)
RecB_Area = (RecB[2] - RecB[] +1) * (RecB[3] - RecB[1] +1)
#计算IOU
iou = interArea /float(RecA_Area + RecB_Area - interArea)
returniou
if__name__ =="__main__":
img = np.zeros((512,512,3),np.uint8)
img.fill(255)
RecA = [50,50,300,300]# x1,y1,x2,y2
RecB = [60,60,320,320]
cv2.rectangle(img,(RecA[],RecA[1]),(RecA[2],RecA[3]),(,255,),5)
cv2.rectangle(img,(RecB[],RecB[1]),(RecB[2],RecB[3]),(255,,),5)
IOU = CountIOU(RecA,RecB)
font = cv2.FONT_HERSHEY_SIMPLEX
cv2.putText(img,"IOU = %.2f"% IOU,(130,190),font,0.8,(,,),2)
cv2.imshow("image",img)
cv2.waitKey()
cv2.destroyAllWindows()
CIOU(Complete-IOU)
CIOU(Complete-IOU)
IoU是比值的概念,对目标物体的尺寸scale是不敏感的。我们在计算框框BBox的回归损失函数进行优化有多种优化方式,如在CIOU之前有GIOU、DIOU,而CIOU解决了一般IoU无法直接优化两个框框没有重叠的部分。
IoU经过GIOU再到DIOU最终发展到CIOU,CIOU将目标与框框anchor之间的距离,重叠率、尺度以及惩罚项都考虑进去,使得目标框回归变得更加稳定,不会像IoU和GIoU一样出现训练过程中发散等问题,而惩罚因子把预测框长宽比拟合目标框的长宽比考虑进去,在最新发布的yolov4中anchor的回归就是用的CIOU方式。
CIOU计算公式
其中分别代表了预测框和真实框的中心点的欧式距离,也就是图中的d。c代表的是能够同时包含预测框和真实框的最小闭包区域的对角线距离。
CIOU回归时的LOSS计算:
效果图
首先借助Opencv和numpy框画两个框,然后按照公式计算CIOU进行展示,如下图。
代码实现:
importtorch
importnumpyasnp
importcv2
importmath
defbox_ciou(b1,b2):
"""
输入为:----------
b1: tensor, shape=(batch, feat_w, feat_h, anchor_num, 4), xywh
b2: tensor, shape=(batch, feat_w, feat_h, anchor_num, 4), xywh
返回为:-------
ciou: tensor, shape=(batch, feat_w, feat_h, anchor_num, 1)
"""
#求出预测框左上角右下角
b1_xy = b1[...,:2]
b1_wh = b1[...,2:4]
b1_wh_half = b1_wh/2.
b1_mins = b1_xy - b1_wh_half
b1_maxes = b1_xy + b1_wh_half
#求出真实框左上角右下角
b2_xy = b2[...,:2]
b2_wh = b2[...,2:4]
b2_wh_half = b2_wh/2.
b2_mins = b2_xy - b2_wh_half
b2_maxes = b2_xy + b2_wh_half
#求真实框和预测框所有的iou
intersect_mins = torch.max(b1_mins,b2_mins)
intersect_maxes = torch.min(b1_maxes,b2_maxes)
intersect_wh = torch.max(intersect_maxes - intersect_mins,torch.zeros_like(intersect_maxes))
intersect_area = intersect_wh[...,] * intersect_wh[...,1]
b1_area = b1_wh[...,] * b1_wh[...,1]
b2_area = b2_wh[...,] * b2_wh[...,1]
union_area = b1_area + b2_area - intersect_area
iou = intersect_area / (union_area +1e-6)
#计算中心的差距
center_distance = torch.sum(torch.pow((b1_xy - b2_xy),2),axis=-1)
#找到包裹两个框的最小框的左上角和右下角
enclose_mins = torch.min(b1_mins,b2_mins)
enclose_maxes = torch.max(b1_maxes,b2_maxes)
enclose_wh = torch.max(enclose_maxes - enclose_mins,torch.zeros_like(intersect_maxes))
#计算对角线距离
enclose_diagonal = torch.sum(torch.pow(enclose_wh,2),axis=-1)
ciou = iou -1.0* (center_distance) / (enclose_diagonal +1e-7)
v = (4/ (math.pi **2)) * torch.pow((torch.atan(b1_wh[...,]/b1_wh[...,1]) - torch.atan(b2_wh[...,]/b2_wh[...,1])),2)
alpha = v / (1.0- iou + v)
ciou = ciou - alpha * v
returnciou
if__name__ =="__main__":
img = np.zeros((512,512,3),np.uint8)
img.fill(255)
RecA = [1,90,90,150,150]
RecB = [1,150,150,200,200]
a = torch.tensor(RecA,dtype=torch.float)# tensor, shape=(batch, feat_w, feat_h, anchor_num, 4), xywh
b = torch.tensor(RecB,dtype=torch.float)
cv2.rectangle(img,(int(RecA[1]-RecA[3]/2),int(RecA[2]-RecA[4]/2)),(int(RecA[1]+RecA[3]/2),int(RecA[2]+RecA[4]/2)),(,255,),5)
cv2.rectangle(img,(int(RecB[1]-RecB[3]/2),int(RecB[2]-RecB[4]/2)),(int(RecB[1]+RecB[3]/2),int(RecB[2]+RecB[4]/2)),(255,,),5)
CIOU = box_ciou(a,b)
font = cv2.FONT_HERSHEY_SIMPLEX
cv2.putText(img,"CIOU = %.2f"% CIOU,(130,190),font,0.8,(,,),2)
cv2.imshow("image",img)
cv2.waitKey()
cv2.destroyAllWindows()