日萌社
人工智能AI:Keras PyTorch MXNet TensorFlow PaddlePaddle 深度学习实战(不定时更新)
NMS每个类别处理过程顺序
1)每个候选框通过SVM得到的若干score得到最大分数对应的所属类别为候选框的类别
2) 再次进行选择最大的score的框,放入结合检测结果集合中,进行重复的IoU判断和删除候选框
3) 剩余候选框与M计算将IoU值大于threshold的框从原有集合中删除
4) 每个类别候选框中进行置信度排序,选择一个最大score的候选框M放入检测结果集合
5)NMS过程开始对所有候选框中属于相同类别所属的候选框进行筛选
"""
非极大值抑制(NMS)
1.输入数据:
通过SVM分类器对每个锚框分类好之后,每个锚框都带上了预测类别标签值和该预测类别的置信度score,最终每个锚框都放到对应的类别列表中。
2.迭代过程:
对每个分类列表中的锚框进行处理,比如对某个类别的列表中所有锚框根据其预测类别的置信度score按从大到小进行排序,
首先类别的列表中取出第一个score值最大的锚框放到输出列表中,然后类别的列表中剩余的所有锚框逐一和输出列表中第一个锚框进行计算IoU值(交并比),
把IoU值>0.5的锚框都丢弃掉,只留下IoU值<0.5的锚框继续进行下一轮比较。
下一轮比较中,仍然先把分类列表中剩余的(score值最大)第一个锚框放到输出列表中,
然后分类列表中剩余的所有锚框再和输出列表中最后添加进去的锚框进行计算IoU值(交并比),
同样的把IoU值>0.5的锚框都丢弃掉,只留下IoU值<0.5的锚框,以此类推继续进行下一轮比较。
"""
import numpy as np
def nms(bounding_boxes, confidence_score, threshold):
"""
:param bounding_boxes: 检测的 boxes 及对应的 scores
:param confidence_score: 置信度score
:param threshold: 设定的阈值
:return:
"""
# boxes 位置
x1 = bounding_boxes[:, 0] # 左上角的x坐标
y1 = bounding_boxes[:, 1] # 左上角的y坐标
x2 = bounding_boxes[:, 2] # 右下角的x坐标
y2 = bounding_boxes[:, 3] # 右下角的y坐标
#一般的都是左上角的x/y坐标小,右下角的x/y坐标大,因为x/y坐标轴的零点位于左上角处。
# 右下角的x坐标x2 - 左上角的x坐标x1 = 长
# 右下角的y坐标y2 - 左上角的y坐标y1 = 高
areas = (x2 - x1 + 1) * (y2 - y1 + 1) # 各 box 的面积 = 长*高
# argsort从小到大排序,返回的是元素的索引值,[::-1]表示倒排变成从大到小 排序,排序后返回的结果为元素索引值[0, 2, 1]
order = confidence_score.argsort()[::-1] # boxes 的按照 置信度score 从大到小 排序
keep_box = [] # 记录保留下的 boxes 作为输出列表
keep_confidence_score = [] # 记录保留下的 置信度score 作为输出列表
while order.size > 0:
i = order[0] # score 最大的 box 对应的 index
keep_box.append(i) # 将本轮 score 最大的 box 的 index 保留
keep_confidence_score.append(i) # 将本轮 score 最大的 box 的 置信度score
# 计算剩余 boxes 与当前 box 的重叠程度 IoU
""" 类别的列表中剩余的所有锚框逐一和输出列表中第一个锚框进行 计算交集,作为分子 """
xx1 = np.maximum(x1[i], x1[order[1:]])
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
"""
areas[i]:最后添加进输出列表的的锚框的面积
areas[order[1:]]:类别列表中的剩余锚框的面积
areas[i] + areas[order[1:]] - inter:两者相加再减去两者的交集求出两者的并集面积
两者的交集面积作为分子,两者的并集面积作为分母,求出的分别都是剩余锚框的 IoU值(交并比)
"""
ovr = inter / (areas[i] + areas[order[1:]] - inter)
"""
np.where()[0] 表示行的索引
np.where()[1] 表示列的索引
把剩余锚框的<=阈值的锚框都留下继续进行下一轮比较
此处的np.where()[0]取出的是类别列表中剩余锚框的索引值
"""
# 保留 IoU 小于设定阈值的 boxes
inds = np.where(ovr <= threshold)[0]
#只保留 <=阈值的锚框,根据所取出类别列表中剩余锚框的索引值取出该部分锚框进行下一轮比较
order = order[inds + 1]
return keep_box, keep_confidence_score
def union(au, bu, area_intersection):
"""
计算并集
:param au:
:param bu:
:param area_intersection:
:return:
"""
# 计算a的面积
area_a = (au[2] - au[0]) * (au[3] - au[1])
# 计算b的面积
area_b = (bu[2] - bu[0]) * (bu[3] - bu[1])
# a和b的面积-交集面积=总共面积
area_union = area_a + area_b - area_intersection
return area_union
def intersection(ai, bi):
"""
计算交集
:param ai:a框坐标
:param bi:b框坐标
:return:
"""
# 1、取出交集的左上角点
x = max(ai[0], bi[0])
y = max(ai[1], bi[1])
# 2、取出交集的右下角点,并减去左上角点值,计算出交集长宽
w = min(ai[2], bi[2]) - x
h = min(ai[3], bi[3]) - y
# 3、如果一个为0,返回交集面积为0
if w < 0 or h < 0:
return 0
return w*h
def iou(a, b):
"""
计算交并比
:param a: a框坐标
:param b: b框坐标
:return:
"""
# 1、如果a,b 传入有问题
if a[0] >= a[2] or a[1] >= a[3] or b[0] >= b[2] or b[1] >= b[3]:
return 0.0
# 2、计算IOU
# 交集区域
area_i = intersection(a, b)
# 并集区域
area_u = union(a, b, area_i)
return float(area_i) / float(area_u + 1e-6) # 防止分母为0,加一个稳定系数
if __name__ == '__main__':
# 1.候选框的坐标表示为(左上角的x坐标,左上角的y坐标,右下角的x坐标,右下角的y坐标)
# 2.一般的都是左上角的x/y坐标小,右下角的x/y坐标大,因为x/y坐标轴的零点位于左上角处。
bounding_boxes = np.array([(187, 82, 337, 317), (150, 67, 305, 282), (246, 121, 368, 304)])
confidence_score = np.array([0.9, 0.75, 0.8]) # 置信度score
threshold = 0.4 # 阈(yu)值
keep_box, keep_confidence_score = nms(bounding_boxes,confidence_score,threshold)
print(bounding_boxes[keep_box]) #[[187 82 337 317]]
print(confidence_score[keep_confidence_score]) # [0.9]
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]
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]])
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)
keep_boxes_indics = np.where(ratio < threshold)
order = order[keep_boxes_indics]
return picked_boxes, picked_score
if __name__ == '__main__':
bounding = [(187, 82, 337, 317), (150, 67, 305, 282), (246, 121, 368, 304)]
confidence_score = [0.9, 0.75, 0.8]
threshold = 0.4
picked_boxes, picked_score = nms(bounding, confidence_score, threshold)
print('阈值threshold为:', threshold)
print('最终bbox列表:', picked_boxes)
print('最终confidence分数列表:', picked_score)