-
GIOU 概述
GIOU(Generalized IoU)是目标检测领域中用于评估边界框相似度的度量指标,也是对传统IoU的改进。它解决了IoU在零重叠情况下的问题,并为边界框回归提供了更好的损失函数。
-
IoU 的局限性
传统 IoU 公式
python
IoU = Area of Intersection / Area of Union
IoU 的缺点:
1.零重叠时梯度为零:当两个框不重叠时,IoU=0,梯度也为0,无法优化
2.无法区分不同分离情况:当IoU=0时,无法知道两个框有多远
3.对尺度敏感:相同的偏移量,对小目标影响更大
- GIOU 的定义
GIOU 在 IoU 的基础上引入了最小闭合区域的概念:
python
GIOU = IoU - (C - U) / C
其中:
C:包含预测框A和真实框B的最小闭合凸区域(通常是矩形)的面积
U:A和B的并集面积
(C - U):最小闭合区域中不属于A∪B的部分
- GIOU 的数学公式
具体计算步骤:
1.计算两个框的交集面积:I = Area(A ∩ B)
2.计算两个框的并集面积:U = Area(A) + Area(B) - I
3.计算IoU:IoU = I / U
4.找到包含A和B的最小闭合矩形C
5.计算GIOU:GIOU = IoU - (Area© - U) / Area©
公式表达:
python
GIOU = IoU - |C \ (A ∪ B)| / |C|
- GIOU 的特性
值域范围:
最佳情况:A和B完全重合时,GIOU = IoU = 1
最差情况:当A和B相距无限远时,GIOU趋近于 -1
取值范围:GIOU ∈ [-1, 1]
优点:
保持尺度不变性
非重叠时有梯度:即使两个框不重叠,GIOU也能提供有效的梯度
对称性:GIOU(A,B) = GIOU(B,A)
三角不等式不敏感:更符合实际需求
- GIOU Loss
GIOU Loss 通常定义为:
python
L_GIOU = 1 - GIOU
这样:
当两个框完全重合时,Loss = 0
当两个框完全不重合且距离很远时,Loss ≈ 2
- 代码实现
Python 实现示例:
python
import numpy as np
import torch
def calculate_giou(box1, box2):
"""
计算GIOU
box1, box2: [x1, y1, x2, y2] 格式
"""
# 确保x2 > x1, y2 > y1
box1 = [min(box1[0], box1[2]), min(box1[1], box1[3]),
max(box1[0], box1[2]), max(box1[1], box1[3])]
box2 = [min(box2[0], box2[2]), min(box2[1], box2[3]),
max(box2[0], box2[2]), max(box2[1], box2[3])]
# 计算交集
x1_inter = max(box1[0], box2[0])
y1_inter = max(box1[1], box2[1])
x2_inter = min(box1[2], box2[2])
y2_inter = min(box1[3], box2[3])
width_inter = max(0, x2_inter - x1_inter)
height_inter = max(0, y2_inter - y1_inter)
area_inter = width_inter * height_inter
# 计算各自面积
area_box1 = (box1[2] - box1[0]) * (box1[3] - box1[1])
area_box2 = (box2[2] - box2[0]) * (box2[3] - box2[1])
# 计算并集
area_union = area_box1 + area_box2 - area_inter
# 计算IoU
iou = area_inter / area_union if area_union > 0 else 0
# 计算最小闭合区域C
x1_c = min(box1[0], box2[0])
y1_c = min(box1[1], box2[1])
x2_c = max(box1[2], box2[2])
y2_c = max(box1[3], box2[3])
area_c = (x2_c - x1_c) * (y2_c - y1_c)
# 计算GIOU
giou = iou - (area_c - area_union) / area_c if area_c > 0 else -1
return giou
def giou_loss(pred_boxes, target_boxes):
"""计算GIOU损失"""
giou = calculate_giou(pred_boxes, target_boxes)
return 1 - giou
PyTorch 实现:
python
import torch
import torch.nn as nn
class GIoULoss(nn.Module):
def __init__(self, reduction='mean'):
super(GIoULoss, self).__init__()
self.reduction = reduction
def forward(self, pred, target):
"""
pred, target: [N, 4] 格式,坐标形式为 [x1, y1, x2, y2]
"""
# 确保坐标顺序正确
pred_x1 = torch.min(pred[:, 0], pred[:, 2])
pred_y1 = torch.min(pred[:, 1], pred[:, 3])
pred_x2 = torch.max(pred[:, 0], pred[:, 2])
pred_y2 = torch.max(pred[:, 1], pred[:, 3])
target_x1 = torch.min(target[:, 0], target[:, 2])
target_y1 = torch.min(target[:, 1], target[:, 3])
target_x2 = torch.max(target[:, 0], target[:, 2])
target_y2 = torch.max(target[:, 1], target[:, 3])
# 计算交集
inter_x1 = torch.max(pred_x1, target_x1)
inter_y1 = torch.max(pred_y1, target_y1)
inter_x2 = torch.min(pred_x2, target_x2)
inter_y2 = torch.min(pred_y2, target_y2)
inter_area = torch.clamp(inter_x2 - inter_x1, min=0) * \
torch.clamp(inter_y2 - inter_y1, min=0)
# 计算各自面积
pred_area = (pred_x2 - pred_x1) * (pred_y2 - pred_y1)
target_area = (target_x2 - target_x1) * (target_y2 - target_y1)
# 计算并集
union_area = pred_area + target_area - inter_area
# 计算IoU
iou = inter_area / (union_area + 1e-6)
# 计算最小闭合区域
c_x1 = torch.min(pred_x1, target_x1)
c_y1 = torch.min(pred_y1, target_y1)
c_x2 = torch.max(pred_x2, target_x2)
c_y2 = torch.max(pred_y2, target_y2)
c_area = (c_x2 - c_x1) * (c_y2 - c_y1) + 1e-6
# 计算GIOU
giou = iou - (c_area - union_area) / c_area
# 计算损失
loss = 1 - giou
if self.reduction == 'mean':
return loss.mean()
elif self.reduction == 'sum':
return loss.sum()
else:
return loss