前面目标检测1: 目标检测20年综述之(一)和目标检测2: 目标检测20年综述之(二)让大家对目标检测有个大概的认识,接下来我们通过系列博客总结一下目标检测基础。本文总结目标检测中的重要概念IoU。
IoU,全称Intersection over Union,可翻译为交并比,是两个框交集与并集的比值。计算IoU的公式如下图,可以看到IoU是一个比值,即交并比。
在分子中,我们计算预测框和ground-truth之间的重叠区域;分母是并集区域,是预测框和ground-truth所包含的总区域。重叠区域和并集区域的比值,就是IoU。
为了用IoU指标来评估目标检测器,我们需要: Ground-truth bounding boxes和我们训练好的模型预测的bounding boxes。下图是一个示例。图中绿色框为Ground-truth bounding box,红色框为预测框,我们的目标是计算它们的IoU。
与分类任务不同,我们预测的bounding box的坐标需要去匹配ground-truth的坐标,而坐标完全匹配基本是不现实的。因此,我们需要定义一个评估指标,奖励那些与ground-truth匹配较好(重叠较大)的预测框。
上图展示了IoU的优劣对比,与ground-truth bounding boxes 重叠比例更大的预测边界框比重叠较少的边界框具有更高的分数,这使得IoU成为评估目标检测器的极佳指标。
我们并不关心(x,y)坐标的精确匹配,但我们希望确保我们预测的边界框尽可能匹配,因此IoU是目标检测一个很好的评价指标。
现在我们已经了解了IoU是什么以及为什么用它来评估目标检测模型,接下来就用Python实现它,如果你简历中有目标检测的经历,这会是面试经常需要手写的,虽然很简单,但是快速bugfree的写出也是基础较好的一种体现。
我们的目标是使用IoU来评估目标探测器的性能。具体来说,给定预测的边界框(红色)与ground-truth(绿色),也就是我们训练好模型的输出与真实label,我们用IoU指标来评估模型的好坏。
def bb_intersection_over_union(boxA, boxB):
# determine the (x, y)-coordinates of the intersection rectangle
xA = max(boxA[0], boxB[0])
yA = max(boxA[1], boxB[1])
xB = min(boxA[2], boxB[2])
yB = min(boxA[3], boxB[3])
# compute the area of intersection rectangle
interArea = max(0, xB - xA + 1) * max(0, yB - yA + 1)
# compute the area of both the prediction and ground-truth
# rectangles
boxAArea = (boxA[2] - boxA[0] + 1) * (boxA[3] - boxA[1] + 1)
boxBArea = (boxB[2] - boxB[0] + 1) * (boxB[3] - boxB[1] + 1)
# compute the intersection over union by taking the intersection
# area and dividing it by the sum of prediction + ground-truth
# areas - the interesection area
iou = interArea / float(boxAArea + boxBArea - interArea)
# return the intersection over union value
return iou
函数需要两个参数:boxA和boxB,假定是我们的ground-truth和预测框。第3-6行确定两个矩形框的(x,y)坐标,然后我们用它们来计算交集的面积(第9行)。interArea变量表示IoU公式中的分子。为了计算分母,我们首先需要计算出预测框和ground-truth的区域(第13和14行)。然后可以在19行上通过将交集区域除以两个边界框的并集区域与交叉区域的差来计算IoU,(注意从分母中减去交叉区域,否则交叉区域将被双倍计数)。最后,IoU分数将返回到第22行的调用函数。现在我们的IoU方法已经完成。
商汤mmdet是目标检测方向的一个非常优秀的开源库,下面实现是商汤mmdet中IoU的实现,基本是一样的,只是是batch的矩阵运算。
import numpy as np
def bbox_overlaps(bboxes1, bboxes2, mode='iou'):
"""Calculate the ious between each bbox of bboxes1 and bboxes2.
Args:
bboxes1(ndarray): shape (n, 4)
bboxes2(ndarray): shape (k, 4)
mode(str): iou (intersection over union) or iof (intersection
over foreground)
Returns:
ious(ndarray): shape (n, k)
"""
assert mode in ['iou', 'iof']
bboxes1 = bboxes1.astype(np.float32)
bboxes2 = bboxes2.astype(np.float32)
rows = bboxes1.shape[0]
cols = bboxes2.shape[0]
ious = np.zeros((rows, cols), dtype=np.float32)
if rows * cols == 0:
return ious
exchange = False
if bboxes1.shape[0] > bboxes2.shape[0]:
bboxes1, bboxes2 = bboxes2, bboxes1
ious = np.zeros((cols, rows), dtype=np.float32)
exchange = True
area1 = (bboxes1[:, 2] - bboxes1[:, 0] + 1) * (
bboxes1[:, 3] - bboxes1[:, 1] + 1)
area2 = (bboxes2[:, 2] - bboxes2[:, 0] + 1) * (
bboxes2[:, 3] - bboxes2[:, 1] + 1)
for i in range(bboxes1.shape[0]):
x_start = np.maximum(bboxes1[i, 0], bboxes2[:, 0])
y_start = np.maximum(bboxes1[i, 1], bboxes2[:, 1])
x_end = np.minimum(bboxes1[i, 2], bboxes2[:, 2])
y_end = np.minimum(bboxes1[i, 3], bboxes2[:, 3])
overlap = np.maximum(x_end - x_start + 1, 0) * np.maximum(
y_end - y_start + 1, 0)
if mode == 'iou':
union = area1[i] + area2 - overlap
else:
union = area1[i] if not exchange else area2
ious[i, :] = overlap / union
if exchange:
ious = ious.T
return ious
Intersection over Union (IoU) for object detection
bbox_overlaps.py