公司手机网站开发招标书,php二次网站开发步骤,沭阳网站建设方案,商品列表页面html模板什么是非极大抑制NMS 非极大值抑制#xff08;Non-Maximum Suppression#xff0c;简称NMS#xff09;是一种在计算机视觉和图像处理领域中广泛使用的后处理技术#xff0c;特别是在目标检测任务中。它的主要目的是解决目标检测过程中出现的重复检测问题#xff0c;即对于… 什么是非极大抑制NMS 非极大值抑制Non-Maximum Suppression简称NMS是一种在计算机视觉和图像处理领域中广泛使用的后处理技术特别是在目标检测任务中。它的主要目的是解决目标检测过程中出现的重复检测问题即对于同一个物体算法可能会预测出多个重叠或相似的边界框bounding boxes。
在目标检测算法得出一系列候选边界框及其对应的类别得分confidence score之后NMS过程如下 排序首先根据每个边界框的得分进行降序排序选取得分最高的边界框作为保留的对象。 抑制对于排序后的边界框列表对每一个框i检查其与得分低于它的所有其他框j之间的重叠程度。通常使用交并比Intersection over UnionIoU来量化两个框的重叠面积占它们并集面积的比例。 剔除如果框i与某个框j的IoU超过预设的阈值比如0.5则认为框j是冗余的将其剔除抑制。 迭代重复上述步骤直到处理完所有候选边界框最终剩下的边界框集合即是经过非极大值抑制后的结果这些框代表了各自区域内最有可能对应真实物体的检测结果。 总之非极大值抑制确保了对同一物体只有一个最精确的边界框被保留下来从而减少误报和重复检测提高了目标检测的精度。 在目标检测中NMS的目的就是要去除冗余的检测框,保留最好的一个 非极大抑制的概念只需要看这两幅图就知道了
下图是经过非极大抑制的。 下图是未经过非极大抑制的。 NMS的原理是对于预测框的集合S及其对应的置信度score这里的置信度就是softmax得出的概率值它的含义是多大的把握预测正确也就是有多大的把握确定检测框中存在真正的目标,选择具有最大score的检测框,记为M,将其从集合S中移除并加入到最终的检测结果集合中.并且将集合S中剩余检测框中与检测框M的IoU大于阈值的框从集合S中移除.重复这个过程,直到集合S为空。
使用流程如下图所示
首先是检测出一系列的检测框
将检测框按照类别进行分类
对同一类别的检测框应用NMS获取最终的检测结果 代码
NMS 算法一般是为了去掉模型预测后的多余框其一般设有一个nms_threshold0.5具体的实现思路如下
选取这类box中scores最大的哪一个它的index记为 i 并保留它计算 boxes[i] 与其余的 boxes 的 IOU 值如果其 IOU0.5 了那么就舍弃这个box由于可能这两个box表示同一目标所以保留分数高的哪一个从最后剩余的boxes中再找出最大scores的哪一个如此循环往复。
def nms(boxes, scores, threshold0.5, top_k200):Args:boxes: 预测出的box, shape[M,4]scores: 预测出的置信度shape[M]threshold: 阈值top_k: 要考虑的box的最大个数Return:keep: nms筛选后的box的新的index数组count: 保留下来box的个数keep scores.new(scores.size(0)).zero_().long()x1 boxes[:, 0]y1 boxes[:, 1]x2 boxes[:, 2]y2 boxes[:, 3]area (x2-x1)*(y2-y1) # 面积,shape[M]_, idx scores.sort(0, descendingTrue) # 降序排列scores的值大小# 取前top_k个进行nmsidx idx[:top_k]count 0while idx.numel():# 记录最大score值的indexi idx[0]# 保存到keep中keep[count] i# keep 的序号count 1if idx.size(0) 1: # 保留框只剩一个breakidx idx[1:] # 移除已经保存的index# 计算boxes[i]和其他boxes之间的iouxx1 x1[idx].clamp(minx1[i])yy1 y1[idx].clamp(miny1[i])xx2 x2[idx].clamp(maxx2[i])yy2 y2[idx].clamp(maxy2[i])w (xx2 - xx1).clamp(min0)h (yy2 - yy1).clamp(min0)# 交集的面积inter w * h # shape[M-1]iou inter / (area[i] area[idx] - inter)# iou满足条件的idxidx idx[iou.le(threshold)] # Shape[M-1]return keep, count 其中
torch.numel() 表示一个张量总元素的个数torch.clamp(min, max) 设置上下限tensor.le(x) 返回tensorx的判断 //// 通过一个例子看些NMS的使用方法假设定位车辆算法就找出了一系列的矩形框我们需要判别哪些矩形框是没用的需要使用NMS的方法来实现。 假设集合S中有A、B、C、D、E 5个候选框每个框旁边的数字是它的置信度我们设定NMS的iou阈值是0.5接下来进行迭代计算
第一轮因为B是得分最高的即B的置信度最高在集合S的其余候选框中如果与B的IoU0.5会被删除。ACDE中现在分别与B计算IoUDE结果0.5剔除DE说明BDE检测的是同一个目标保留置信度最大的候选框而AC可能检测的是另一个目标B作为一个预测结果从集合S中移除并放入最终的检测结果集合中。此时新的集合S中只剩下候选框A,C
第二轮在新的集合S中A的置信度得分最高将集合S中剩下的候选框分别与A计算IoU因为A与C的iou0.5所以剔除CA作为另外一个预测结果从集合S中移除并放入最终的检测结果集合中此时集合S为空所以循环结束。
最终结果为在这个5个中检测出了两个目标为A和B。
单类别的NMS的实现方法如下所示
import numpy as np
def nms(bboxes, confidence_score, threshold):非极大抑制过程:param bboxes: 同类别候选框坐标:param confidence: 同类别候选框分数即置信度:param threshold: iou阈值:return:# 1、没有传入候选框则返回空列表if len(bboxes) 0:return [], []#强制转换为numpy类型的数组这样才可以进行切片等numpy所支持的操作bboxes np.array(bboxes)score np.array(confidence_score)# 取出所有候选框的左上角坐标和右下角坐标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:# 将当前置信度最大的候选框的索引加入返回值列表中因为是从小到大排序所有最后一个值最大即 order[-1]表示最后一个元素index order[-1]#将置信度最大的候选框及其置信度值加入返回值列表中picked_boxes.append(bboxes[index])picked_score.append(score[index])# 获取当前置信度最大的候选框与其他任意候选框的相交面积这里的order[:-1]表示除了最后一个元素之外的所有元素np.max和np.maximum的实现功能是不同的#np.maximum的用法np.maximum([2,4,7],[3,1,5])输出的结果是array([3, 4, 7])np.maximum([2],[3,1,5])的输出结果是array([3, 2, 5])#np.max的用法np.max([2,4,7])输出结果是7x11 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]])# 计算相交的面积,不重叠时面积设为0w np.maximum(0.0, x22 - x11)h np.maximum(0.0, y22 - y11)inter_area w * h# 计算交并比iou inter_area / (areas[index] areas[order[:-1]] - inter_area)# 获取IoU小于阈值的候选框的索引keep_boxes np.where(iou threshold)#更新order以便保留IoU小于阈值的框order order[keep_boxes]# 返回NMS后的框及分类结果 return picked_boxes, picked_score 假设有检测结果如下当阈值threshold设置的越大则保留越多的候选框 当threshold取0.3时
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] 当threshold取0.5时 bounding [(187, 82, 337, 317), (150, 67, 305, 282), (246, 121, 368, 304)]
confidence_score [0.9, 0.65, 0.8]
threshold 0.5
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.5
NMS后得到的bbox是 [array([187, 82, 337, 317]), array([246, 121, 368, 304])]
NMS后得到的bbox的confidences是 [0.9, 0.8] 上述所讲的NMS方法都是先将检测框按照类别进行分类然后对对同一类别的检测框应用NMS。但是在实际的任务中如果所预测的类别很多时那么这种效率非常低。所以有些时候我们会使用新的方法进行NMS:它的大致思想是先将不同类别的预测框在坐标位置上尽可能的区分开然后就可以一次性对所有预测框进行NMS此时不用先进行分类然后分别对每一个类别依次做NMS
比如下图所示蓝色方框的类别索引是1黄色方框的类别索引是2这些不同类别的预测框在位置上靠的很近此时如果直接对所有类别同时做NMS效果就很差。所以我们会设法将蓝色方框和黄色方框分离开本例的方法是首先找到所有方框中坐标值最大的数值max_value比如这里是81 然后使用类别索引 indxs与val_value相乘得到不同类别框的偏移量offsets它的公式是offsetsindxs*max_value
比如对于类别索引为1的方框它的偏移量是offsetsindxs*max_value1*8181对于类别索引为1的方框它的偏移量是offsetsindxs*max_value2*81162
计算完每个类别的偏移量后我们就得到新的预测框的坐标以及其对于的新位置如下所示。然后就可以一次性对所有预测框进行NMS此时不用先进行分类然后分别对每一个类别依次做NMS ////
概括非极大抑制的功能就是
筛选出一定区域内属于同一种类得分最大的框。
1、非极大抑制NMS的实现过程 本博文实现的是多分类的非极大抑制 输入shape为[ batch_size, all_anchors, 5num_classes ]
第一个维度是图片的数量。 第二个维度是所有的预测框。 第三个维度是所有的预测框的预测结果。
非极大抑制的执行过程如下所示 1、对所有图片进行循环。 2、找出该图片中得分大于门限函数的框。在进行重合框筛选前就进行得分的筛选可以大幅度减少框的数量。 3、判断第2步中获得的框的种类与得分。取出预测结果中框的位置与之进行堆叠。此时最后一维度里面的内容由5num_classes变成了412四个参数代表框的位置一个参数代表预测框是否包含物体两个参数分别代表种类的置信度与种类。 4、对种类进行循环非极大抑制的作用是筛选出一定区域内属于同一种类得分最大的框对种类进行循环可以帮助我们对每一个类分别进行非极大抑制。 5、根据得分对该种类进行从大到小排序。 6、每次取出得分最大的框计算其与其它所有预测框的重合程度重合程度过大的则剔除。
视频中实现的代码是numpy形式而且库比较久远。这里改成pytorch的形式且适应当前的库。
实现代码如下
def bbox_iou(self, box1, box2, x1y1x2y2True):计算IOUif not x1y1x2y2:b1_x1, b1_x2 box1[:, 0] - box1[:, 2] / 2, box1[:, 0] box1[:, 2] / 2b1_y1, b1_y2 box1[:, 1] - box1[:, 3] / 2, box1[:, 1] box1[:, 3] / 2b2_x1, b2_x2 box2[:, 0] - box2[:, 2] / 2, box2[:, 0] box2[:, 2] / 2b2_y1, b2_y2 box2[:, 1] - box2[:, 3] / 2, box2[:, 1] box2[:, 3] / 2else:b1_x1, b1_y1, b1_x2, b1_y2 box1[:, 0], box1[:, 1], box1[:, 2], box1[:, 3]b2_x1, b2_y1, b2_x2, b2_y2 box2[:, 0], box2[:, 1], box2[:, 2], box2[:, 3]inter_rect_x1 torch.max(b1_x1, b2_x1)inter_rect_y1 torch.max(b1_y1, b2_y1)inter_rect_x2 torch.min(b1_x2, b2_x2)inter_rect_y2 torch.min(b1_y2, b2_y2)inter_area torch.clamp(inter_rect_x2 - inter_rect_x1, min0) * \torch.clamp(inter_rect_y2 - inter_rect_y1, min0)b1_area (b1_x2 - b1_x1) * (b1_y2 - b1_y1)b2_area (b2_x2 - b2_x1) * (b2_y2 - b2_y1)iou inter_area / torch.clamp(b1_area b2_area - inter_area, min 1e-6)return ioudef non_max_suppression(self, prediction, num_classes, input_shape, image_shape, letterbox_image, conf_thres0.5, nms_thres0.4):#----------------------------------------------------------## 将预测结果的格式转换成左上角右下角的格式。# prediction [batch_size, num_anchors, 85]#----------------------------------------------------------#box_corner prediction.new(prediction.shape)box_corner[:, :, 0] prediction[:, :, 0] - prediction[:, :, 2] / 2box_corner[:, :, 1] prediction[:, :, 1] - prediction[:, :, 3] / 2box_corner[:, :, 2] prediction[:, :, 0] prediction[:, :, 2] / 2box_corner[:, :, 3] prediction[:, :, 1] prediction[:, :, 3] / 2prediction[:, :, :4] box_corner[:, :, :4]output [None for _ in range(len(prediction))]for i, image_pred in enumerate(prediction):#----------------------------------------------------------## 对种类预测部分取max。# class_conf [num_anchors, 1] 种类置信度# class_pred [num_anchors, 1] 种类#----------------------------------------------------------#class_conf, class_pred torch.max(image_pred[:, 5:5 num_classes], 1, keepdimTrue)#----------------------------------------------------------## 利用置信度进行第一轮筛选#----------------------------------------------------------#conf_mask (image_pred[:, 4] * class_conf[:, 0] conf_thres).squeeze()#----------------------------------------------------------## 根据置信度进行预测结果的筛选#----------------------------------------------------------#image_pred image_pred[conf_mask]class_conf class_conf[conf_mask]class_pred class_pred[conf_mask]if not image_pred.size(0):continue#-------------------------------------------------------------------------## detections [num_anchors, 7]# 7的内容为x1, y1, x2, y2, obj_conf, class_conf, class_pred#-------------------------------------------------------------------------#detections torch.cat((image_pred[:, :5], class_conf.float(), class_pred.float()), 1)#------------------------------------------## 获得预测结果中包含的所有种类#------------------------------------------#unique_labels detections[:, -1].cpu().unique()if prediction.is_cuda:unique_labels unique_labels.cuda()detections detections.cuda()for c in unique_labels:#------------------------------------------## 获得某一类得分筛选后全部的预测结果#------------------------------------------#detections_class detections[detections[:, -1] c]# #------------------------------------------## # 使用官方自带的非极大抑制会速度更快一些# #------------------------------------------## keep nms(# detections_class[:, :4],# detections_class[:, 4] * detections_class[:, 5],# nms_thres# )# max_detections detections_class[keep]# 按照存在物体的置信度排序_, conf_sort_index torch.sort(detections_class[:, 4]*detections_class[:, 5], descendingTrue)detections_class detections_class[conf_sort_index]# 进行非极大抑制max_detections []while detections_class.size(0):# 取出这一类置信度最高的一步一步往下判断判断重合程度是否大于nms_thres如果是则去除掉max_detections.append(detections_class[0].unsqueeze(0))if len(detections_class) 1:breakious self.bbox_iou(max_detections[-1], detections_class[1:])detections_class detections_class[1:][ious nms_thres]# 堆叠max_detections torch.cat(max_detections).data# Add max detections to outputsoutput[i] max_detections if output[i] is None else torch.cat((output[i], max_detections))if output[i] is not None:output[i] output[i].cpu().numpy()box_xy, box_wh (output[i][:, 0:2] output[i][:, 2:4])/2, output[i][:, 2:4] - output[i][:, 0:2]output[i][:, :4] self.yolo_correct_boxes(box_xy, box_wh, input_shape, image_shape, letterbox_image)return output参考文章【SSD算法】史上最全代码解析-核心篇 - 知乎
参考文章睿智的目标检测31——非极大抑制NMS与Soft-NMS-CSDN博客
参考文章NMS非极大值抑制_nms非极大值抑制-CSDN博客