今天有学生找我过来聊天,问了我一个问题,非极大值抑制的具体实现思路是什么?今天又是周五,加个班把这个问题简单说一下:
接下来,我们就以这张图片为例来进行说明:
假设:
上图中三个判别汽车位置的矩形框(红、黄、蓝)他们的置信度评分分别为:96/82/78;
正文:
因为红框是置信度得分最高的,与红框的IoU(交并比)>0.5(常规参数,经验值)删除。黄、蓝框现在与红框计算IoU,红蓝框交并比均结果>0.5,剔除黄蓝框,红框作为一个预测结果。至此我们痛过极大值抑制的思想成功的留下我们要找的最佳匹配结果红框,放入存储结果的集合(序列)
上文是针对,一张图上的一个对象来进行说明,还是担心大家不理解,那接下来我们找个一张图上游多个对象的情况来进行说明:
上图是找在网上随便找的一张图片,没收广告费哈!!!
-
多了两个框:假设绿框置信度92、棕色框置信度为85;
同理:这张图我们也可以按照上面的思想来分析一下: -
第一轮:因为红框是置信度得分最高的,与红框的IoU(交并比)>0.5(常规参数,经验值)删除。黄、蓝框现在与红框计算IoU,红蓝框交并比均结果>0.5,剔除黄蓝框,保留交并比为零的绿色框和棕色框。红框作为一个预测结果。至此我们痛过极大值抑制的思想成功的留下我们要找的最佳匹配结果红框,放入存储结果的集合(序列)
-
第二轮:绿框的得分最高,与棕色计算IoU,棕色框交并比的结果>0.5,剔除棕色框,绿色框作为一个我们要找的结果也放入目标序列。
-
最终结果为在这个5个中检测出了两个目标为红框内目标和绿框内目标。
刚刚我们举了几个例子来说明了一下极大值抑制的思想,希望能对大家有所帮助。接下来我们就使用代码来实现一个但应用场景下的非极大值抑制的实现思路:
import numpy as np
def nms(bboxes, confidence_score, threshold):
"""非极大抑制过程
:param bboxes: 同类别候选框坐标
:param confidence: 同类别候选框分数
:param threshold: iou阈值
:return:
"""
# 1、传入无候选框返回空
if len(bboxes) == 0:
return [], []
# 强转数组
bboxes = np.array(bboxes)
score = np.array(confidence_score)
# 取出n个的极坐标点
x1 = bboxes[:, 0]
y1 = bboxes[:, 1]
x2 = bboxes[:, 2]
y2 = bboxes[:, 3]
# 2、对候选框进行NMS筛选
# 返回的框坐标和分数
picked_boxes = []
picked_score = []
# 对置信度进行排序, 获取排序后的下标序号, argsort默认从小到大排序
order = np.argsort(score)
areas = (x2 - x1) * (y2 - y1)
while order.size > 0:
# 将当前置信度最大的框加入返回值列表中
index = order[-1]
#保留该类剩余box中得分最高的一个
picked_boxes.append(bboxes[index])
picked_score.append(confidence_score[index])
# 获取当前置信度最大的候选框与其他任意候选框的相交面积
x11 = np.maximum(x1[index], x1[order[:-1]])
y11 = np.maximum(y1[index], y1[order[:-1]])
x22 = np.minimum(x2[index], x2[order[:-1]])
y22 = np.minimum(y2[index], y2[order[:-1]])
# 计算相交的面积,不重叠时面积为0
w = np.maximum(0.0, x22 - x11)
h = np.maximum(0.0, y22 - y11)
intersection = w * h
# 利用相交的面积和两个框自身的面积计算框的交并比
ratio = intersection / (areas[index] + areas[order[:-1]] - intersection)
# 保留IoU小于阈值的box
keep_boxes_indics = np.where(ratio < threshold)
# 保留剩余的框
order = order[keep_boxes_indics]
# 返回NMS后的框及分类结果
return picked_boxes, picked_score
bounding = [(187, 82, 337, 317), (150, 67, 305, 282), (246, 121, 368, 304)]
confidence_score = [0.9, 0.65, 0.8]
threshold = 0.3
picked_boxes, picked_score = nms(bounding, confidence_score, threshold)
print('阈值threshold为:', threshold)
print('NMS后得到的bbox是:', picked_boxes)
print('NMS后得到的bbox的confidences是:', picked_score)
输出结果:
阈值threshold为: 0.3
NMS后得到的bbox是: [array([187, 82, 337, 317])]
NMS后得到的bbox的confidences是: [0.9]
说明:此案例,我这边是针对上图中单物体检测场景,设计的,如果想实现多物体检测场景,只要把return位置返回值换成函数的递归调用就可以了。思想是一样的。希望能对大家对非极大值抑制带来更深入的理解,家里孩子哭闹呢,先写到这吧。