代码实现部分有两套代码,使用场景分别是:
(1)使用iou对anchor进行正负样本的筛选;
(2)计算各类的iou损失;
iou
giou
在iou的基础上引入了能够包围两个box的最小框C,
不仅关注两个box的重叠区域,还关注两个框的非重叠区域
diou
ciou
代码实现:
(1)交叉计算两个box矩阵的iou
使用过了pytorch作为实现的工具包,将各类iou整理成一个类,可以直接调用这个类,其中有问题大家可以用留言的形式提出来,我会尽快修改
# ======================IoU类========================#
# 计算各类IoU,Tensor格式
# ======================IoU类========================#
class IoU:
types = ("iou", "giou", "diou", "ciou", "eiou")
def __init__(self, iou_type="iou"):
assert iou_type in self.types, "iou_type ERROR, iou_type must in %s" % str(self.types)
self.iou_type = iou_type
def __call__(self, bbox1: torch.Tensor, bbox2: torch.Tensor, is_center=True, eps=1e-7):
"""
:param bbox1: (n, 4)或者(4, )
:param bbox2: (m, 4)或者(4, )
:param is_center: 输入坐标的格式。True:Center;False:Corner
:return: ious:(n, m)
"""
CHANGE = False
dim_ = -1
# ==============================================#
# 统一bbox的格式
# ==============================================#
corner1 = torch.zeros_like(bbox1)
corner2 = torch.zeros_like(bbox2)
if is_center:
# center -> corner
corner1[..., 0] = bbox1[..., 0] - bbox1[..., 2] / 2
corner1[..., 1] = bbox1[..., 1] - bbox1[..., 3] / 2
corner1[..., 2] = bbox1[..., 0] + bbox1[..., 2] / 2
corner1[..., 3] = bbox1[..., 1] + bbox1[..., 3] / 2
corner2[..., 0] = bbox2[..., 0] - bbox2[..., 2] / 2
corner2[..., 1] = bbox2[..., 1] - bbox2[..., 3] / 2
corner2[..., 2] = bbox2[..., 0] + bbox2[..., 2] / 2
corner2[..., 3] = bbox2[..., 1] + bbox2[..., 3] / 2
else:
corner1 = bbox1
corner2 = bbox2
# ==============================================#
# 维度统一,
# 将输入的维度统一为2维
# 只有当dim为1时CHANGE = True;
# 有如下几种情况:
# corner1:(4, )->(1, 4);corner2:(m, 4);->(1, 4)->(4, )
# corner1:(n, 4);corner2:(4, )->(1, 4);->(n, 1)->(n, )
# corner1:(4, )->(1, 4);corner2:(4, )->(1, 4);->(1, 1)->(1, )
# ==============================================#
if corner1.ndim < 2:
CHANGE = True
dim_ = 0
corner1 = corner1.unsqueeze(dim=0)
elif corner1.dim() > 2:
raise ValueError("corner1.dim() should be equal 2.")
if corner2.dim() < 2:
CHANGE = True
dim_ = 1
corner2 = corner2.unsqueeze(dim=0)
elif corner2.ndim > 2:
raise ValueError("corner1 dim should be equal 2.")
if self.iou_type is self.types[0]: # iou
iou = self._iou(corner1, corner2, eps=eps)
if CHANGE:
iou = torch.squeeze(iou, dim=dim_)
return iou
elif self.iou_type is self.types[1]: # giou
giou = self._giou(corner1, corner2, eps=eps)
if CHANGE:
giou = torch.squeeze(giou, dim=dim_)
return giou
elif self.iou_type is self.types[2]: # diou
diou = self._diou(corner1, corner2, eps=eps)
if CHANGE:
diou = torch.squeeze(diou, dim=dim_)
return diou
elif self.iou_type is self.types[3]: # ciou
ciou = self._ciou(corner1, corner2, eps=eps)
if CHANGE:
ciou = torch.squeeze(ciou, dim=dim_)
return ciou
elif self.iou_type is self.types[4]: # eiou
pass
@staticmethod
def _iou(corner1: torch.Tensor, corner2: torch.Tensor, eps=1e-7) -> torch.Tensor:
"""
:param corner1:(n, 4)
:param corner2:(m ,4)
:return:(n, m)
"""
# 记录边界框的个数
num_box1 = corner1.shape[0]
num_box2 = corner2.shape[0]
# ----------------------计算交集的对角坐标----------------------开始-#
# 两个box的右下角中 数值小 的就是交集的右下角坐标,输出的维度为:(n, m, 2)
# 维度变化:(n, 2)->(n, 1, 2)->(n, m, 2);(m, 2)->(1, m, 2)->(n, m, 2)
inter_bottom_right = torch.min(torch.unsqueeze(corner1[..., 2:], dim=1).expand(num_box1, num_box2, 2),
torch.unsqueeze(corner2[..., 2:], dim=0).expand(num_box1, num_box2, 2))
# 两个box的左上角中 数值大 的就是交集的左上角坐标,输出的维度为:(n, m, 2)
# 维度变化:(n, 2)->(n, 1, 2)->(n, m, 2);(m, 2)->(1, m, 2)->(n, m, 2)
inter_top_left = torch.max(torch.unsqueeze(corner1[..., 0:2], dim=1).expand(num_box1, num_box2, 2),
torch.unsqueeze(corner2[..., 0:2], dim=0).expand(num_box1, num_box2, 2))
# (n, m, 2)
inter_wh = torch.clamp((inter_bottom_right - inter_top_left), min=0)
# 交集:(n, m)
inter = inter_wh[..., 0] * inter_wh[..., 1]
# ----------------------计算交集的对角坐标----------------------结束-#
# ----------------------计算bbox的面积----------------------开始-#
# (n, )
area_corner1 = (corner1[..., 2] - corner1[..., 0]) * (corner1[..., 3] - corner1[..., 1])
# (n, )->(n, 1)->(n, m)
area_corner1 = torch.unsqueeze(area_corner1, dim=1).expand_as(inter)
# (m, )
area_corner2 = (corner2[..., 2] - corner2[..., 0]) * (corner2[..., 3] - corner2[..., 1])
# (m, )->(1, m)->(n, m)
area_corner2 = torch.unsqueeze(area_corner2, dim=0).expand_as(inter)
# ----------------------计算bbox的面积----------------------结束-#
# ----------------------计算交并比----------------------开始-#
# 交集:(n, m)
union = area_corner1 + area_corner2 - inter + eps
# iou:(n, m)
iou = inter / union
# ----------------------计算交并比----------------------结束-#
return iou
@staticmethod
def _giou(corner1: torch.Tensor, corner2: torch.Tensor, eps=1e-7) -> torch.Tensor:
"""
在预测框和真实框没有很好的对齐时,会导致最小外界框C的面积增大,
从而使GIOU的值变小,而两个矩形框不重合时,也可以计算GIoU。
GIoU Loss虽然解决了IOU的问题,但是产生的新bug就是当两个框属于包含关系是,GIoU无法区分其相对位置.
由于GIoU仍然严重依赖于IoU,因此在两个垂直方向,误差很大,基本很难收敛,这就是GIoU不稳定的原因。
:param corner1: (n, 4)
:param corner2: (m, 4)
:return: d iou:(n, m)
"""
# 记录box个数
num_box1 = corner1.shape[0]
num_box2 = corner2.shape[0]
# ----------------------包围两个box的最小边界框----------------------开始-#
# (n, 2)->(n, 1, 2)->(n, m, 2);(m, 2)->(1, m, 2)->(n, m, 2);
c_union_bottom_right = torch.max(torch.unsqueeze(corner1[..., 2:], dim=1).expand(num_box1, num_box2, 2),
torch.unsqueeze(corner2[..., 2:], dim=0).expand(num_box1, num_box2, 2))
# (n, 2)->(n, 1, 2)->(n, m, 2);(m, 2)->(1, m, 2)->(n, m, 2);
c_union_top_left = torch.min(torch.unsqueeze(corner1[..., 0:2], dim=1).expand(num_box1, num_box2, 2),
torch.unsqueeze(corner2[..., 0:2], dim=0).expand(num_box1, num_box2, 2))
# (n, m, 2)
c_union_wh = torch.clamp((c_union_bottom_right - c_union_top_left), min=0)
# (n, m)
area_c = c_union_wh[..., 0] * c_union_wh[..., 1] + eps
# ----------------------包围两个box的最小边界框----------------------结束-#
# ----------------------计算两个box的交集----------------------开始-#
# (n, 2)->(n, 1, 2)->(n, m, 2);(m, 2)->(1, m, 2)->(n, m, 2);
inter_bottom_right = torch.min(torch.unsqueeze(corner1[..., 2:], dim=1).expand(num_box1, num_box2, 2),
torch.unsqueeze(corner2[..., 2:], dim=0).expand(num_box1, num_box2, 2))
# (n, 2)->(n, 1, 2)->(n, m, 2);(m, 2)->(1, m, 2)->(n, m, 2);
inter_top_left = torch.max(torch.unsqueeze(corner1[..., 0:2], dim=1).expand(num_box1, num_box2, 2),
torch.unsqueeze(corner2[..., 0:2], dim=0).expand(num_box1, num_box2, 2))
# (n, m, 2)
inter_wh = torch.clamp((inter_bottom_right - inter_top_left), min=0)
# (n, m)
inter = inter_wh[..., 0] * inter_wh[..., 1]
# ----------------------计算两个box的交集----------------------结束-#
# ----------------------两个box的面积----------------------开始-#
# (n, )
area_corner1 = (corner1[..., 2] - corner1[..., 0]) * (corner1[..., 3] - corner1[..., 1])
# (n, )->(n, 1)->(n, m)
area_corner1 = torch.unsqueeze(area_corner1, dim=1).expand_as(inter)
# (m, )
area_corner2 = (corner2[..., 2] - corner2[..., 0]) * (corner2[..., 3] - corner2[..., 1])
# (m, )->(1, m)->(n, m)
area_corner2 = torch.unsqueeze(area_corner2, dim=0).expand_as(inter)
# ----------------------两个box的面积----------------------结束-#
# ======================计算IOU======================#
union = area_corner1 + area_corner2 - inter + eps
iou = inter / union
giou = iou - (area_c - union) / area_c
return giou
@staticmethod
def _diou(corner1: torch.Tensor, corner2: torch.Tensor, eps=1e-7) -> torch.Tensor:
"""
DIoU Loss的惩罚项能够直接最小化中心点间的距离,而且GIoU Loss旨在减少外界包围框的面积
DIoU与IoU,GIoU一样具有尺度不变性
DIoU与GIoU一样在与目标框不重叠时,仍然可以为边界框提供移动方向
DIoU可以直接最小化两个目标框的距离,因此比GIoU Loss 收敛快得多
DIoU在包含两个水平或垂直方向上的情况回归很快,而GIoU几乎退化为IoU
:param corner1:(n, 4)
:param corner2:(m, 4)
:return:(n, m)
"""
# 记录边box个数
num_box1 = corner1.shape[0]
num_box2 = corner2.shape[0]
# ----------------------计算包围两个box最小的框C----------------------开始-#
# (n, 2)->(n, 1, 2)->(n, m, 2);(m, 2)->(1, m, 2)->(n, m, 2)
c_union_bottom_right = torch.max(torch.unsqueeze(corner1[..., 2:], dim=1).expand(num_box1, num_box2, 2),
torch.unsqueeze(corner2[..., 2:], dim=0).expand(num_box1, num_box2, 2))
# (n, 2)->(n, 1, 2)->(n, m, 2);(m, 2)->(1, m, 2)->(n, m, 2)
c_union_top_left = torch.min(torch.unsqueeze(corner1[..., 0:2], dim=1).expand(num_box1, num_box2, 2),
torch.unsqueeze(corner2[..., 0:2], dim=0).expand(num_box1, num_box2, 2))
# (n, m, 2)
c_union_wh = torch.clamp((c_union_bottom_right - c_union_top_left), min=0)
# (n, m)
c_union_diagonal2 = c_union_wh[..., 0] ** 2 + c_union_wh[..., 1] ** 2
# ----------------------计算包围两个box最小的框C----------------------结束-#
# ----------------------计算交集----------------------开始-#
# (n, 2)->(n, 1, 2)->(n, m, 2);(m, 2)->(1, m, 2)->(n, m, 2)
inter_bottom_right = torch.min(torch.unsqueeze(corner1[..., 2:], dim=1).expand(num_box1, num_box2, 2),
torch.unsqueeze(corner2[..., 2:], dim=0).expand(num_box1, num_box2, 2))
# (n, 2)->(n, 1, 2)->(n, m, 2);(m, 2)->(1, m, 2)->(n, m, 2)
inter_top_left = torch.max(torch.unsqueeze(corner1[..., 0:2], dim=1).expand(num_box1, num_box2, 2),
torch.unsqueeze(corner2[..., 0:2], dim=0).expand(num_box1, num_box2, 2))
# (n, m, 2)
inter_wh = torch.clamp((inter_bottom_right) - inter_top_left, min=0)
# (n, m)
inter = inter_wh[..., 0] * inter_wh[..., 1]
# ----------------------计算交集----------------------结束-#
# ----------------------计算两个box的中心点之间的欧氏距离的平方----------------------开始-#
# corner1的中心点坐标:(n, 2)->(n, 1, 2)->(n, m, 2)
center1_xy = torch.unsqueeze((corner1[..., 2:] + corner1[..., 0:2]) / 2, dim=1).expand(num_box1, num_box2, 2)
# corner2的中心点坐标:(m, 2)->(1, m, 2)->(n, m, 2)
center2_xy = torch.unsqueeze((corner2[..., 2:] + corner2[..., 0:2]) / 2, dim=0).expand(num_box1, num_box2, 2)
# (n, m)
center_distance2 = (center2_xy[..., 0] - center1_xy[..., 0]) ** 2 + \
(center2_xy[..., 1] - center1_xy[..., 1]) ** 2
# ----------------------计算两个box的中心点之间的欧氏距离的平方----------------------结束-#
# ----------------------计算两个box的并集----------------------开始-#
# (n, )
area_corner1 = (corner1[..., 2] - corner1[..., 0]) * (corner1[..., 3] - corner1[..., 1])
# (n, )->(n, 1)->(n, m)
area_corner1 = torch.unsqueeze(area_corner1, dim=1).expand_as(inter)
# (m, )
area_corner2 = (corner2[..., 2] - corner2[..., 0]) * (corner2[..., 3] - corner2[..., 1])
# (m, )->(1, m)->(n, m)
area_corner2 = torch.unsqueeze(area_corner2, dim=0).expand_as(inter)
# ----------------------计算两个box的并集----------------------结束-#
# ======================计算diou======================#
# (n, m)
union = area_corner1 + area_corner2 - inter
iou = inter / (union + eps)
diou = iou - center_distance2 / (c_union_diagonal2 + eps)
return diou
@staticmethod
def _ciou(corner1: torch.Tensor, corner2: torch.Tensor, eps=1e-7) -> torch.Tensor:
"""
CIoU Loss和DIoU Loss的区别在于CIoU考虑了Bounding box的纵横比,进一步提升了回归进度。
CIoU的惩罚项是DIoU的惩罚基础上加了一个影响因子 , 这个因子把预测框纵横比拟合真实框的纵横比考虑进去。
:param corner1:
:param corner2:
:return:
"""
# 记录box的个数
num_box1 = corner1.shape[0]
num_box2 = corner2.shape[0]
# ----------------------计算包围两个box最小的框C----------------------开始-#
# (n, 2)->(n, 1, 2)->(n, m, 2);(m, 2)->(1, m, 2)->(n, m, 2)
c_union_bottom_right = torch.max(torch.unsqueeze(corner1[..., 2:], dim=1).expand(num_box1, num_box2, 2),
torch.unsqueeze(corner2[..., 2:], dim=0).expand(num_box1, num_box2, 2))
# (n, 2)->(n, 1, 2)->(n, m, 2);(m, 2)->(1, m, 2)->(n, m, 2)
c_union_top_left = torch.min(torch.unsqueeze(corner1[..., 0:2], dim=1).expand(num_box1, num_box2, 2),
torch.unsqueeze(corner2[..., 0:2], dim=0).expand(num_box1, num_box2, 2))
# (n, m, 2)
c_union_wh = torch.clamp(c_union_bottom_right - c_union_top_left, min=0)
# (n, m)
c_union_diagonal2 = c_union_wh[..., 0] ** 2 + c_union_wh[..., 1] ** 2
# ----------------------计算包围两个box最小的框C----------------------结束-#
# ----------------------计算交集----------------------开始-#
# (n, 2)->(n, 1, 2)->(n, m, 2);(m, 2)->(1, m, 2)->(n, m, 2)
inter_bottom_right = torch.min(torch.unsqueeze(corner1[..., 2:], dim=1).expand(num_box1, num_box2, 2),
torch.unsqueeze(corner2[..., 2:], dim=0).expand(num_box1, num_box2, 2))
# (n, 2)->(n, 1, 2)->(n, m, 2);(m, 2)->(1, m, 2)->(n, m, 2)
inter_top_left = torch.max(torch.unsqueeze(corner1[..., 0:2], dim=1).expand(num_box1, num_box2, 2),
torch.unsqueeze(corner2[..., 0:2], dim=0).expand(num_box1, num_box2, 2))
# (n, m, 2)
inter_wh = torch.clamp(inter_bottom_right - inter_top_left, min=0)
# (n, m)
inter = inter_wh[..., 0] * inter_wh[..., 1]
# ----------------------计算交集----------------------结束-#
# ----------------------计算box的面积----------------------开始-#
# (n, )
area_corner1 = (corner1[..., 2] - corner1[..., 0]) * (corner1[..., 3] - corner1[..., 1])
# (n, )->(n, 1)->(n, m)
area_corner1 = torch.unsqueeze(area_corner1, dim=1).expand_as(inter)
# (m, )
area_corner2 = (corner2[..., 2] - corner2[..., 0]) * (corner2[..., 3] - corner2[..., 1])
# (m, )->(1, m)->(n, m)
area_corner2 = torch.unsqueeze(area_corner2, dim=0).expand_as(inter)
# ----------------------计算box的面积----------------------结束-#
# ----------------------计算iou----------------------开始-#
union = area_corner1 + area_corner2 - inter
iou = inter / (union + eps)
# ----------------------计算iou----------------------结束-#
# ----------------------计算纵横比惩罚项----------------------开始-#
# box1的wh:(n, 2)->(n, 1, 2)->(n, m, 2)
box1_wh = torch.unsqueeze(corner1[..., 2:] - corner1[..., 0:2], dim=1).expand(num_box1, num_box2, 2)
# box1的wh:(n, 2)->(n, 1, 2)->(n, m, 2)
box2_wh = torch.unsqueeze(corner2[..., 2:] - corner2[..., 0:2], dim=0).expand(num_box1, num_box2, 2)
# 衡量长宽比一致性的参数v:(n, m)
vv = (torch.arctan(box1_wh[..., 0] / box1_wh[..., 1]) - torch.arctan(box2_wh[..., 0] / box2_wh[..., 1])) ** 2
v = 4 * vv / (math.pi ** 2)
# 用于做trade-off的参数alpha
alpha = v / (1 - iou + v + eps)
# ----------------------计算纵横比惩罚项----------------------结束-#
# ----------------------计算两个box的中心点距离平方----------------------开始-#
# (n, 2)->(n, 1, 2)->(n, m, 2)
corner1_xy = torch.unsqueeze((corner1[..., 2:] + corner1[..., 0:2]) / 2, dim=1).expand(num_box1, num_box2, 2)
# (m, 2)->(1, m, 2)->(n, m, 2)
corner2_xy = torch.unsqueeze((corner2[..., 2:] + corner2[..., 0:2]) / 2, dim=0).expand(num_box1, num_box2, 2)
# (n, m)
center_distance2 = (corner2_xy[..., 0] - corner1_xy[..., 0]) ** 2 + \
(corner2_xy[..., 1] - corner1_xy[..., 1]) ** 2
# ----------------------计算连个box的中心点距离平方----------------------结束-#
# ======================计算ciou======================#
ciou = iou - center_distance2 / (c_union_diagonal2 + eps) - alpha * v
return ciou
(2)计算预测box和GT box的iou损失
要求:输入的两个box矩阵shape必须都是(n, 4)
有时间更新代码,暂时不写,敬请谅解
以上一部分代码不同之处在于,计算iou损失时,回归狂只需要和自己对应的GT计算iou就行,不需要交叉和其余的GT计算iou,即输入是两个(n, 4)输出是一个(n, );这是与上面代码最大的不同 [代码(1)中输入是(m, 4), (n, 4)输出是(m, n)]
# ======================IoU Loss类========================#
# 计算个类IoU Loss
# ======================IoU Loss类========================#
class IouLoss:
types = ("iou_loss", "giou_loss", "diou_loss", "ciou_loss", "eiou_loss")
def __init__(self, loss_type="iou_loss"):
assert loss_type in self.types, "iou_type ERROR, iou_type must in %s" % str(self.types)
self.loss_type = loss_type
def __call__(self, bbox1: torch.Tensor, bbox2: torch.Tensor, is_center=True, eps=1e-7):
"""
:param bbox1: (n, 4)
:param bbox2: (n, 4)
:param is_center:
:return:
"""
assert bbox1.shape == bbox2.shape, "The shape of bbox1 and bbox2 must be consistent."
# ==============================================#
# 统一bbox的格式
# ==============================================#
corner1 = torch.zeros_like(bbox1)
corner2 = torch.zeros_like(bbox2)
if is_center:
# center -> corner
corner1[..., 0] = bbox1[..., 0] - bbox1[..., 2] / 2
corner1[..., 1] = bbox1[..., 1] - bbox1[..., 3] / 2
corner1[..., 2] = bbox1[..., 0] + bbox1[..., 2] / 2
corner1[..., 3] = bbox1[..., 1] + bbox1[..., 3] / 2
corner2[..., 0] = bbox2[..., 0] - bbox2[..., 2] / 2
corner2[..., 1] = bbox2[..., 1] - bbox2[..., 3] / 2
corner2[..., 2] = bbox2[..., 0] + bbox2[..., 2] / 2
corner2[..., 3] = bbox2[..., 1] + bbox2[..., 3] / 2
else:
corner1 = bbox1
corner2 = bbox2
if self.loss_type == self.types[0]: # iou loss
loss = self._iou_loss(corner1, corner2, eps=eps)
return loss
elif self.loss_type == self.types[1]: # giou loss
loss = self._giou_loss(corner1, corner2, eps=eps)
return loss
elif self.loss_type == self.types[2]: # diou loss
loss = self._diou_loss(corner1, corner2, eps=eps)
return loss
elif self.loss_type == self.types[3]: # ciou loss
loss = self._ciou_loss(corner1, corner2, eps=eps)
return loss
elif self.loss_type == self.types[4]: # eiou loss
print("敬请期待...")
@staticmethod
def _iou_loss(corner1, corner2, eps=1e-7) -> torch.Tensor:
"""
:param corner1: (n ,4)
:param corner2: (n ,4)
:return:
"""
# ----------------------计算交集----------------------开始-#
# (n, 2)
inter_bottom_right = torch.min(corner1[:, 2:], corner2[:, 2:])
# (n, 2)
inter_top_left = torch.max(corner1[:, :2], corner2[:, :2])
# (n ,2)
inter_wh = torch.clamp((inter_bottom_right - inter_top_left), min=0)
# 交集:(n, )
inter = inter_wh[:, 0] * inter_wh[:, 1]
# ----------------------计算交集----------------------结束-#
# ----------------------计算box的面积----------------------开始-#
# (n, )
area_corner1 = (corner1[:, 2] - corner1[:, 0]) * (corner1[:, 3] - corner1[:, 1])
# (n, )
area_corner2 = (corner2[:, 2] - corner2[:, 0]) * (corner2[:, 3] - corner2[:, 1])
# ----------------------计算box的面积----------------------结束-#
# ----------------------计算交并比----------------------开始-#
# 交集:(n, )
union = area_corner1 + area_corner2 - inter + eps
# (n, )
ious = inter / union
# ----------------------计算交并比----------------------结束-#
# (n, )
loss = 1 - ious
return loss
@staticmethod
def _giou_loss(corner1: torch.Tensor, corner2: torch.Tensor, eps=1e-7) -> torch.Tensor:
"""
:param corner1:
:param corner2:
:return:
"""
# ----------------------计算交集----------------------开始-#
# (n, 2)
inter_bottom_right = torch.min(corner1[:, 2:], corner2[:, 2:])
# (n, 2)
inter_top_left = torch.max(corner1[:, :2], corner2[:, :2])
# (n ,2)
inter_wh = torch.clamp((inter_bottom_right - inter_top_left), min=0)
# 交集:(n, )
inter = inter_wh[:, 0] * inter_wh[:, 1]
# ----------------------计算交集----------------------结束-#
# ----------------------计算box的面积----------------------开始-#
# (n, )
area_corner1 = (corner1[:, 2] - corner1[:, 0]) * (corner1[:, 3] - corner1[:, 1])
# (n, )
area_corner2 = (corner2[:, 2] - corner2[:, 0]) * (corner2[:, 3] - corner2[:, 1])
# ----------------------计算box的面积----------------------结束-#
# ----------------------计算交并比----------------------开始-#
# 交集:(n, )
union = area_corner1 + area_corner2 - inter + eps
# (n, )
ious = inter / union
# ----------------------计算交并比----------------------结束-#
# ----------------------计算最小包围框C----------------------开始-#
# (n ,2)
c_union_bottom_right = torch.max(corner1[:, 2:], corner2[:, 2:])
# (n ,2)
c_union_top_left = torch.min(corner1[:, :2], corner2[:, :2])
# (n, 2)
c_union_wh = torch.clamp((c_union_bottom_right - c_union_top_left), min=0)
# (n, )
area_c = c_union_wh[:, 0] * c_union_wh[:, 1] + eps
# ----------------------计算最小包围框C----------------------结束-#
giou = ious - (area_c - union) / area_c
# (n, )
loss = 1 - giou
return loss
@staticmethod
def _diou_loss(corner1: torch.Tensor, corner2: torch.Tensor, eps=1e-7) -> torch.Tensor:
"""
:param corner1:
:param corner2:
:return:
"""
# ----------------------计算交集----------------------开始-#
# (n, 2)
inter_bottom_right = torch.min(corner1[:, 2:], corner2[:, 2:])
# (n, 2)
inter_top_left = torch.max(corner1[:, :2], corner2[:, :2])
# (n ,2)
inter_wh = torch.clamp((inter_bottom_right - inter_top_left), min=0)
# 交集:(n, )
inter = inter_wh[:, 0] * inter_wh[:, 1]
# ----------------------计算交集----------------------结束-#
# ----------------------计算box的面积----------------------开始-#
# (n, )
area_corner1 = (corner1[:, 2] - corner1[:, 0]) * (corner1[:, 3] - corner1[:, 1])
# (n, )
area_corner2 = (corner2[:, 2] - corner2[:, 0]) * (corner2[:, 3] - corner2[:, 1])
# ----------------------计算box的面积----------------------结束-#
# ----------------------计算交并比----------------------开始-#
# 交集:(n, )
union = area_corner1 + area_corner2 - inter
# (n, )
ious = inter / (union + eps)
# ----------------------计算交并比----------------------结束-#
# ----------------------计算最小包围框C----------------------开始-#
# (n ,2)
c_union_bottom_right = torch.max(corner1[:, 2:], corner2[:, 2:])
# (n ,2)
c_union_top_left = torch.min(corner1[:, :2], corner2[:, :2])
# (n, 2)
c_union_wh = torch.clamp((c_union_bottom_right - c_union_top_left), min=0)
# (n, ):使用勾股定理计算对角线长度
c_union_diagonal2 = c_union_wh[:, 0] ** 2 + c_union_wh[:, 1] ** 2
# ----------------------计算最小包围框C----------------------结束-#
# ----------------------计算两个box中心点之间的距离----------------------开始-#
# (n, 2)
center1_xy = (corner1[:, 2:] + corner1[:, :2]) / 2
# (n ,2)
center2_xy = (corner2[:, 2:] + corner2[:, :2]) / 2
# (n ,2)
distance_xy = center2_xy - center1_xy
# (n, )
center_distance2 = distance_xy[:, 0] ** 2 + distance_xy[:, 1] ** 2 + eps
# ----------------------计算两个box中心点之间的距离----------------------结束-#
diou = ious - center_distance2 / c_union_diagonal2
# (n, )
loss = 1 - diou
return loss
@staticmethod
def _ciou_loss(corner1: torch.Tensor, corner2: torch.Tensor, eps=1e-7) -> torch.Tensor:
"""
:param corner1:
:param corner2:
:return:
"""
# ----------------------计算交集----------------------开始-#
# (n, 2)
inter_bottom_right = torch.min(corner1[:, 2:], corner2[:, 2:])
# (n, 2)
inter_top_left = torch.max(corner1[:, :2], corner2[:, :2])
# (n ,2)
inter_wh = torch.clamp((inter_bottom_right - inter_top_left), min=0)
# 交集:(n, )
inter = inter_wh[:, 0] * inter_wh[:, 1]
# ----------------------计算交集----------------------结束-#
# ----------------------计算box的面积----------------------开始-#
# (n, )
area_corner1 = (corner1[:, 2] - corner1[:, 0]) * (corner1[:, 3] - corner1[:, 1])
# (n, )
area_corner2 = (corner2[:, 2] - corner2[:, 0]) * (corner2[:, 3] - corner2[:, 1])
# ----------------------计算box的面积----------------------结束-#
# ----------------------计算交并比----------------------开始-#
# 交集:(n, )
union = area_corner1 + area_corner2 - inter
# (n, )
ious = inter / (union + eps)
# ----------------------计算交并比----------------------结束-#
# ----------------------计算最小包围框C----------------------开始-#
# (n ,2)
c_union_bottom_right = torch.max(corner1[:, 2:], corner2[:, 2:])
# (n ,2)
c_union_top_left = torch.min(corner1[:, :2], corner2[:, :2])
# (n, 2)
c_union_wh = torch.clamp((c_union_bottom_right - c_union_top_left), min=0)
# (n, ):使用勾股定理计算对角线长度
c_union_diagonal2 = c_union_wh[:, 0] ** 2 + c_union_wh[:, 1] ** 2
# ----------------------计算最小包围框C----------------------结束-#
# ----------------------计算两个box中心点之间的距离----------------------开始-#
# (n, 2)
center1_xy = (corner1[:, 2:] + corner1[:, :2]) / 2
# (n ,2)
center2_xy = (corner2[:, 2:] + corner2[:, :2]) / 2
# (n ,2)
distance_xy = center2_xy - center1_xy
# (n, )
center_distance2 = distance_xy[:, 0] ** 2 + distance_xy[:, 1] ** 2
# ----------------------计算两个box中心点之间的距离----------------------结束-#
# ----------------------计算宽高比惩罚项----------------------开始-#
# (n, 2)
wh1 = corner1[:, 2:] - corner1[:, :2]
# (n ,2)
wh2 = corner2[:, 2:] - corner2[:, :2]
# 衡量长宽比一致醒的参数:(n, )
v = ((torch.arctan(wh1[:, 0] / wh1[:, 1]) - torch.arctan(wh2[:, 0] / wh2[:, 1])) * 2 / math.pi) ** 2
alpha = v / (1 - ious + v + eps)
# ----------------------计算宽高比惩罚项----------------------结束-#
ciou = ious - center_distance2 / (c_union_diagonal2 + eps) - alpha * v
# (n, )
loss = 1 - ciou
return loss