在NMS之前,我们先手撕IOU
IOU(Intersection over Union,交并比)**是目标检测任务中常用的一种评价指标,用于衡量两个边界框(bounding boxes)之间的重叠程度。IOU 的取值范围为 0 到 1,值越大表示两个框的重叠部分越多。
IOU 的计算步骤:
-
交集(Intersection):计算两个边界框相交部分的面积,即两个框重叠的区域。
-
并集(Union):计算两个边界框的并集面积,等于两个框的总面积减去它们的交集面积。
-
IOU 公式:
IOU=交集面积/并集面积其中:
- 交集面积 是两个边界框重叠部分的面积。
- 并集面积 是两个边界框总的覆盖面积(交集加上剩余的非重叠部分)。
直观解释:
- IOU = 1:两个框完全相等,重叠程度最大。
- IOU = 0:两个框没有任何重叠,完全不相交。
- 0 < IOU < 1:两个框部分重叠,IOU 越接近 1,说明它们的重叠部分越大。
IOU 计算的 Python 实现:
下面的代码展示了如何计算两个边界框之间的 IOU 值:
import numpy as npdef calculate_iou(box1, box2):"""计算两个边界框之间的 IOU 值。参数:- box1: 第一个边界框,格式为 [x1, y1, x2, y2]- box2: 第二个边界框,格式为 [x1, y1, x2, y2]返回:- IOU 值"""# 获取两个框的坐标x1_1, y1_1, x2_1, y2_1 = box1x1_2, y1_2, x2_2, y2_2 = box2# 计算相交区域的坐标x1_inter = max(x1_1, x1_2)y1_inter = max(y1_1, y1_2)x2_inter = min(x2_1, x2_2)y2_inter = min(y2_1, y2_2)# 计算交集的宽度和高度inter_width = max(0, x2_inter - x1_inter)inter_height = max(0, y2_inter - y1_inter)# 计算交集面积intersection_area = inter_width * inter_height# 计算每个框的面积box1_area = (x2_1 - x1_1) * (y2_1 - y1_1)box2_area = (x2_2 - x1_2) * (y2_2 - y1_2)# 计算并集面积union_area = box1_area + box2_area - intersection_area# 计算 IOUiou = intersection_area / union_areareturn iou# 示例边界框
box1 = [100, 100, 200, 200] # [x1, y1, x2, y2]
box2 = [150, 150, 250, 250] # [x1, y1, x2, y2]# 计算 IOU
iou_value = calculate_iou(box1, box2)
print("IOU 值:", iou_value)
NMS 的主要步骤:
-
置信度排序:
- 目标检测网络在检测过程中会生成多个候选框,每个框都有一个对应的置信度分数(表示框中包含目标的概率)。
- 首先,根据每个框的置信度分数(通常是分类分数或目标置信度)进行降序排序,优先保留置信度最高的框。
-
选择最高置信度的框:
- 从所有候选框中选择置信度最高的框,称为“主框”(reference box)。该框会被保留,因为它最有可能是正确的目标位置。
-
抑制与主框重叠较大的框:
- 计算其他框与主框的重叠度(通常使用 IoU,即交并比,Intersection over Union)。
- IoU 是两个边界框的交集面积与并集面积的比值,用于衡量它们的重叠程度。IoU 的取值范围是 0 到 1,IoU 越大,说明两个框的重叠部分越大。
-
删除重叠较大的框:
- 设置一个重叠阈值(IoU 阈值),如果两个框的 IoU 大于这个阈值(通常取值为 0.5 或 0.6),则将重叠框视为对同一目标的重复检测,删除置信度较低的框。
- 重叠较小的框不会被删除,保留它们作为其他潜在目标的候选框。
-
重复步骤:
- 对剩下的候选框重复上述过程,继续选择置信度最高的框,然后抑制与它重叠的框,直到所有候选框处理完毕。
NMS 的直观解释:
假设一个目标检测算法检测到一辆车的多个候选框。通过 NMS,只会保留最高置信度的那个框,而删除其余重叠框。这样,模型输出的检测结果就更加简洁和准确。
NMS 的示例代码(Python实现):
使用 NumPy 实现一个简单的 NMS 算法:
import numpy as npdef IOU(box1,box2):x1=max(box1[0],box2[0])y1=max(box1[1],box2[1])x2=min(box1[2],box2[2])y2=min(box1[3],box2[3])IOU_hei=max(0,y2-y1+1)IOU_wid=max(0,x2-x1+1)iou_area=IOU_wid*IOU_heiunion_area=((box1[2]-box1[0])*(box1[3]-box1[1]))+((box2[2]-box2[0])*(box2[3]-box2[1]))iou = iou_area/union_areareturn ioudef NMS(boxs,threshold,score):if len(boxs)==0:return []boxs=boxs.astype(float)xx1=boxs[:,0]yy1=boxs[:,1]xx2=boxs[:,2]yy2=boxs[:,3]remind=[]area_boxes=(xx2-xx1)*(yy2-yy1)order=score.argsort()[::-1]while len(order)>0:index=order[0]remind.append(index)#IOUx1 = np.maximum(xx1[index], xx1[order[1:]])y1 = np.maximum(yy1[index], yy1[order[1:]])x2 = np.minimum(xx2[index], xx2[order[1:]])y2 = np.minimum(yy2[index], yy2[order[1:]])wid=np.maximum(0,(x2-x1+1))hei=np.maximum(0,(y2-y1+1))area=wid*heiiou_arr=area/(area_boxes[index]+area_boxes[order[1:]])index_remind=np.where(iou_arr<=threshold)[0]order=order[index_remind+1]return remind# 示例边界框
box1 = [100, 100, 200, 200] # [x1, y1, x2, y2]
box2 = [150, 150, 250, 250] # [x1, y1, x2, y2]# 计算 IOU
iou_value = IOU(box1, box2)
print("IOU 值:", iou_value)boxes = np.array([[100, 100, 210, 210],[105, 105, 215, 215],[150, 150, 300, 300]
])# 每个框的置信度分数
scores = np.array([0.9, 0.75, 0.6])# NMS 阈值(IoU阈值)
iou_threshold = 0.1# 执行 NMS
selected_indices = NMS(boxes, iou_threshold, scores)
selected_boxes = boxes[selected_indices]print("保留的框:")
print(selected_boxes)