【基础实操】损失函数:IoU-GIoU-DIoU-CIoU计算复现
前言
丢失函数常见于方针检测任务中,其解决问题的效率十分依赖界说的丢失函数。关于方针检测丢失函数的界说根本可以认定为依赖于边界框回归方针的聚合,例如猜测框和实在框(即 GIoU、CIoU、DIoU 等)的间隔、重叠区域和纵横比。
在本文中咱们侧重点将放在代码完成以及结果展示,关于原理部分咱们可自行查阅论文以及相关的论文解说,我在这里就不过多的炒冷饭了,望海涵!
核算完成
IoU家族代码完成
关于IoU、GIoU、DIoU和CIoU的代码完成摘抄于互联网。结合核算部分的代码选用如图(w=h=300,c=3)进行实验:咱们在标示软件上标定企鹅的鼻子部分,标签文件存储为Voc格式,查阅xml文件得到企鹅鼻子(方针方位)的[xmin,ymin,xmax,yxmax]=[105, 100, 195, 133],易得标示框的 w= 90,h=33。
为了核算的快捷性以及iou贴合度,咱们将设定猜测框的大小等同于实在方针框,也即 W真 = W 预 & H真 = H预。选用滑动框的方式在图像上进行滑动核算(STEP=1),动图如下:绿色框为方针方位,紫色框为滑动框也便是猜测框。
核算结果
核算每一步得到的“iou”值得到如下图,其中在核算iou时出现了负数,我分别将负数未置零以及负数置零绘制了一遍供咱们参考。 图像排列次序为:左上=IOU 右上=GIOU 左下=DIOU 右下=CIOU
未置零图
置零图
IOU代码
import math
from mpmath import eps
from numpy import where, arcsin
def euclidean_distance(p1, p2):
"""核算两个点的欧式间隔"""
x1, y1 = p1
x2, y2 = p2
return math.sqrt((x2 - x1) ** 2 + (y2 - y1) ** 2)
class BBox(object):
def __init__(self, x, y, r, b):
self.x, self.y, self.r, self.b = x, y, r, b
def __xor__(self, other):
"""核算box和other的IoU"""
cross = self & other
union = self | other
return cross / (union + 1e-6)
def __or__(self, other):
""" 核算box和other的并集"""
cross = self & other
return self.area + other.area - cross
def __and__(self, other):
"""核算box和other的交集"""
xmax = min(self.r, other.r)
ymax = min(self.b, other.b)
xmin = max(self.x, other.x)
ymin = max(self.y, other.y)
return BBox(xmin, ymin, xmax, ymax).area
def boundof(self, other):
"""核算box和other的边际外包框,使得2个box都在框内的最小矩形"""
xmin = min(self.x, other.x)
ymin = min(self.y, other.y)
xmax = max(self.r, other.r)
ymax = max(self.b, other.b)
return BBox(xmin, ymin, xmax, ymax)
def center_distance(self, other):
"""核算两个box的中心点间隔"""
return euclidean_distance(self.center, other.center)
def bound_diagonal_distance(self, other):
"""核算两个box的bound的对角线间隔"""
bound = self.boundof(other)
return euclidean_distance((bound.x, bound.y), (bound.r, bound.b))
@property
def center(self):
return (self.x + self.r) / 2, (self.y + self.b) / 2
@property
def area(self):
return self.width * self.height
@property
def width(self):
# todo 假如不考虑右侧的一个像素 回来 self.r - self.x
return self.r - self.x + 1
@property
def height(self):
# todo 假如不考虑下侧的一个像素 回来 self.b - self.y
return self.b - self.y + 1
def __repr__(self):
return f"{self.x}, {self.y}, {self.r}, {self.b}"
def IoU(box1: BBox, box2: BBox):
return box1 ^ box2
def GIoU(box1: BBox, box2: BBox):
bound_area = box1.boundof(box2).area
union_area = box1 | box2
return IoU(box1, box2) - (bound_area - union_area) / bound_area
def DIoU(box1: BBox, box2: BBox):
d = box1.center_distance(box2)
c = box1.bound_diagonal_distance(box2)
return IoU(box1, box2) - d ** 2 / c ** 2
def CIoU(box1: BBox, box2: BBox):
diou = DIoU(box1, box2)
v = 4 / (math.pi ** 2) * (math.atan(box1.width / box1.height) - math.atan(box2.width / box2.height)) ** 2
iou = IoU(box1, box2)
alpha = v / (1 - iou + v)
return diou - alpha * v
拓展
咱们可以经过本文的核算方法结合自己的项目中IOU核算方式进行替换更改或结合项目做适合自己项目的IOU。自己能力有限,路过的各位大神若发现疏忽的地方还望指教一二!感谢!希望本文可以协助到咱们。