目录
相关知识
非极大值抑制介绍
目前,常用的目标检测算法,无论是One-stage
的SSD
系列算法、YOLO系列算法还是Two-stage
的基于R-CNN
系列的算法,非极大值抑制都是其中必不可少的一个组件。在现有的基于anchor的目标检测算法中,都会产生数量巨大的候选矩形框,这些矩形框有很多是指向同一目标,因此就存在大量冗余的候选矩形框。非极大值抑制算法的目的正在于此,它可以消除多余的框,找到最佳的物体检测位置。
非极大值抑制(Non-Maximum Suppression
,以下简称NMS
算法)的思想是搜索局部极大值,抑制非极大值元素。
由于是滑动窗口,一个对象可能有多个Bounding box
,每个边界框都有一个分类器得分,而我们的最终目标是留下那个最优的Bounding box
。 而其中,我们应用了非极大值抑制来去掉冗余的框:它就是一个迭代-遍历-消除的过程。
-
将所有框的得分进行排序,选中最高分及其对应的框:
-
遍历其余的框,如果和当前最高分框的重叠面积(IOU)大于一定阈值,我们就将框删除:
-
从未处理的框中继续选一个得分最高的,重复上述过程:
举例,下面图片定位一个车辆,最后算法找出一堆的方框,我们需要判别哪些矩形框是没用的。非极大值抑制的方法是:先假设有6个矩形框,根据分类器的类别分类概率做排序,假设从小到大属于车辆的概率 分别为A、B、C、D、E、F。
- 1、从最大概率矩形框F开始,分别判断A~E与F的重叠度IOU是否大于某个设定的阈值;
- 2、假设B、D与F的重叠度超过阈值,那么就扔掉B、D;并标记第一个矩形框F,是我们保留下来的。
- 3、从剩下的矩形框A、C、E中,选择概率最大的E,然后判断E与A、C的重叠度,重叠度大于一定的阈值,那么就扔掉;并标记E是我们保留下来的第二个矩形框。
- 4、一直重复这个过程,找到所有曾经被保留下来的矩形框。
代码部分
获取坐标位置以及置信度
# 生成数据,shape=(5, 5)
a = np.eye(5)
# 获取第一列的数据
res = a[:, 1]
print(res)
# 显示结果
array([0., 1., 0., 0., 0.])
置信度得分排序,这里使用的是argsort函数进行排序,但是argsort函数默认是从小到大进行排序,所以在后面用[::-1]实现倒序。
argsort函数介绍:
x = np.array([1, 4, 3, -1, 6, 9])
print(x.argsort())
# 返回结果,它是索引排序和数据类型
array([3, 0, 2, 1, 4, 5], dtype=int64)
置信度排序过程:
代码参考
import numpy as np
from matplotlib import pyplot as plt
# 2.假设生成如下Bbox
boxes = np.array([[100, 100, 210, 220, 0.71],
[250, 250, 420, 420, 0.8],
[220, 200, 320, 330, 0.92],
[100, 100, 210, 210, 0.72],
[230, 240, 325, 330, 0.81],
[220, 230, 315, 340, 0.9]])
def nms(bboxs, thresh):
# bboxs:形似上面设置的boxes,是一组包含了诸多框坐标的数组
# thresh: IOU阈值
# 请在此添加代码,获取坐标位置以及置信度
########## Begin ##########
xmin = boxes[:, 0]
ymin = boxes[:, 1]
xmax = boxes[:, 2]
ymax = boxes[:, 3]
scores = boxes[:, 4]
########## Begin ##########
# print(xmin, ymin, xmax, ymax, scores)
# 计算每个框的面积
# 那为什么要加1操作呢?因为图片像素最小是1,而数组是0。
areas = (xmax - xmin + 1) * (ymax - ymin + 1)
# print(areas)
# 获取得分以排序
# print(scores)
# 请在此添加代码,argot默认从小到大排序,[::-1]实现翻转,获取索引index
########## Begin ##########
index = scores.argsort()[::-1]
########## End ##########
# print(index)
# 4.保留结果集,返回输出保留下来的Bbox最终结果
result = []
while index.size > 0:
i = index[0] # index中存储Bbox按分排序后的索引,所以第一个就是得分最高的Bbox索引,直接保留
result.append(i)
# 两个框的左上角坐标x取大值,右下角坐标x取小值,小值-大值+1==相交区域的长度
# 两个框的左上角坐标y取大值,右下角坐标y取小值,小值-大值+1==相交区域的高度
x11 = np.maximum(xmin[i], xmin[index[1:]])
y11 = np.maximum(ymin[i], ymin[index[1:]])
x22 = np.minimum(xmax[i], xmax[index[1:]])
y22 = np.minimum(ymax[i], ymax[index[1:]])
# 计算相交面积,当两个框不相交时,w和h必有一个为0,面积也为0
w = np.maximum(0, x22 - x11 + 1)
h = np.maximum(0, y22 - y11 + 1)
overlaps = w * h
ious = overlaps / (areas[i] + areas[index[1:]] - overlaps) # index[1:]从下标1开始取到列表结束 最高分的面积加其余的面积
idx = np.where(ious <= thresh)[0]
# 因为index是从1开始的,加1
index = index[idx + 1]
print(result)
return result
def plot_bbox(bboxs, c='k'):
x1 = bboxs[:, 0]
y1 = bboxs[:, 1]
x2 = bboxs[:, 2]
y2 = bboxs[:, 3]
plt.plot([x1, x2], [y1, y1], c)
plt.plot([x1, x1], [y1, y2], c)
plt.plot([x1, x2], [y2, y2], c)
plt.plot([x2, x2], [y1, y2], c)
plt.title(" nms")
if __name__ == '__main__':
plt.figure(1)
ax1 = plt.subplot(1, 2, 1) # 定义子图,把NMS前和NMS后的绘制在一起
ax2 = plt.subplot(1, 2, 2)
plt.sca(ax1)
plot_bbox(boxes, 'k') # before nms
# 接收平台的IoU阈值
k = float(input())
res = nms(boxes, thresh=k)
plt.sca(ax2)
plot_bbox(boxes[res], 'r') # after nms
plt.savefig('src/step1/img1/figure.jpg')
plt.show()