IoU(Intersection over Union),又称重叠度/交并比。
即上图中的 intersection/Union,代码实现:
# one pre, one gt
def IoU(pred_box, gt_box):
ixmin = max(pred_box[0], gt_box[0])
iymin = max(pred_box[1], gt_box[1])
ixmax = min(pred_box[2], gt_box[2])
iymax = min(pred_box[3], gt_box[3])
inter_w = np.maximum(ixmax - ixmin + 1., 0)
inter_h = np.maximum(iymax - iymin + 1., 0)
inters = inter_w * inter_h
uni = ((pred_box[2] - pred_box[0] + 1.) * (pred_box[3] - pred_box[1] + 1.) +
(gt_box[2] - gt_box[0] + 1.) * (gt_box[3] - gt_box[1] + 1.) - inters)
ious = inters / uni
return ious
# multi pre, one gt
def maxIoU(pred_box, gt_box):
ixmin = np.maximum(pred_box[:, 0], gt_box[0])
iymin = np.maximum(pred_box[:, 1], gt_box[1])
ixmax = np.minimum(pred_box[:, 2], gt_box[2])
iymax = np.minimum(pred_box[:, 3], gt_box[3])
inters_w = np.maximum(ixmax - ixmin + 1., 0) # 逐元素求最大值和最小值 broadcasting
inters_h = np.maximum(iymax - iymin + 1., 0) # 逐元素求最大值和最小值 broadcasting
inters = inters_w * inters_h
uni = ((pred_box[:, 2] - pred_box[:, 0] + 1.) * (pred_box[:, 3] - pred_box[:, 1] + 1.) +
(gt_box[2] - gt_box[0] + 1.) * (gt_box[3] - gt_box[1] + 1.) - inters)
ious = inters / uni
iou = np.max(ious)
iou_id = np.argmax(ious)
return iou, iou_id
# multi pre, multi gt
def box_IoU(pred_box, gt_boxes):
result = []
for gt_box in gt_boxes:
temp = []
ixmin = np.maximum(pred_box[:, 0], gt_box[0])
iymin = np.maximum(pred_box[:, 1], gt_box[1])
ixmax = np.minimum(pred_box[:, 2], gt_box[2])
iymax = np.minimum(pred_box[:, 3], gt_box[3])
inters_w = np.maximum(ixmax - ixmin + 1., 0) # 逐元素求最大值和最小值 broadcasting
inters_h = np.maximum(iymax - iymin + 1., 0) # 逐元素求最大值和最小值 broadcasting
inters = inters_w * inters_h
uni = ((pred_box[:, 2] - pred_box[:, 0] + 1.) * (pred_box[:, 3] - pred_box[:, 1] + 1.) +
(gt_box[2] - gt_box[0] + 1.) * (gt_box[3] - gt_box[1] + 1.) - inters)
ious = inters / uni
iou = np.max(ious)
iou_id = np.argmax(ious)
temp.append(iou)
temp.append(iou_id)
result.append(temp)
return result
以及与 IoU 相关的一些损失函数:
目标检测回归损失函数简介:SmoothL1/IoU/GIoU/DIoU/CIoU Loss - 知乎
收藏 | 目标检测回归损失函数总结
目标检测回归损失函数简介:SmoothL1/IoU/GIoU/DIoU/CIoU Loss
由数据标注引出的问题:边界框的概率分布,模型预测出的边界框的不确定度。
一文了解目标检测边界框概率分布 - 知乎
对 Bounding Box 的高斯分布建模。
武汉大学提出NWD:小目标检测新范式,抛弃IoU-Based暴力涨点(登顶SOTA)
NMS(non-maximum suppression),非极大值抑制。检测模型输出的检测结果中,有很多都是多余的,是对同一个物体的重复预测,因此需要用 NMS 来抑制掉一些。具体做法是:
每一个类别过滤的代码实现:
def py_cpu_nms(dets, thresh):
"""Pure Python NMS baseline."""
x1 = dets[:, 0] # pred bbox top_x
y1 = dets[:, 1] # pred bbox top_y
x2 = dets[:, 2] # pred bbox bottom_x
y2 = dets[:, 3] # pred bbox bottom_y
scores = dets[:, 4] # pred bbox cls score
areas = (x2 - x1 + 1) * (y2 - y1 + 1) # pred bbox areas
order = scores.argsort()[::-1] # 对pred bbox按score做降序排序,对应step-2
keep = [] # NMS后,保留的pred bbox
while order.size > 0:
i = order[0] # top-1 score bbox
keep.append(i) # top-1 score的话,自然就保留了
xx1 = np.maximum(x1[i], x1[order[1:]]) # top-1 bbox(score最大)与order中剩余bbox计算NMS
yy1 = np.maximum(y1[i], y1[order[1:]])
xx2 = np.minimum(x2[i], x2[order[1:]])
yy2 = np.minimum(y2[i], y2[order[1:]])
w = np.maximum(0.0, xx2 - xx1 + 1)
h = np.maximum(0.0, yy2 - yy1 + 1)
inter = w * h
ovr = inter / (areas[i] + areas[order[1:]] - inter) # 无处不在的IoU计算~~~
inds = np.where(ovr <= thresh)[0] # 这个操作可以对代码断点调试理解下,结合step-3,我们希望剔除所有与当前top-1 bbox IoU > thresh的冗余bbox,那么保留下来的bbox,自然就是ovr <= thresh的非冗余bbox,其inds保留下来,作进一步筛选
order = order[inds + 1] # 保留有效bbox,就是这轮NMS未被抑制掉的幸运儿,为什么 + 1?因为ind = 0就是这轮NMS的top-1,剩余有效bbox在IoU计算中与top-1做的计算,inds对应回原数组,自然要做 +1 的映射,接下来就是step-4的循环
return keep # 最终NMS结果返回
以及 NMS 的变体:
一文打尽目标检测NMS——精度提升篇 - 知乎
NMS也能玩出花样来…… - 知乎
OHEM、Focal Loss、ATSS
Adaptive Training Sample Selection (ATSS) - 知乎