一、背景:
传统的非极大值抑制(NMS)算法在目标检测中存在一个问题,即当一个物体的检测框与具有最高得分的检测框M有重叠(在预定义的重叠阈值内)时,会将该检测框的得分设置为零,从而导致该物体可能被遗漏,降低了平均精度。为了解决这个问题,作者提出了Soft-NMS
算法。
本文将YOLOv9
默认的NMS
修改成GIoU-NMS
、DIoU-NMS
、CIoU-NMS
、SIoU-NMS
、 EIou-NMS
。
文章目录
二、原理
Soft-NMS
- 用一行代码改进对象检测
2.1 NMS原理
在目标检测等任务中,通常会产生多个可能的检测框或候选区域。这些检测框可能会有重叠,并且可能会对同一个目标进行多次检测。
NMS 的核心思想就是在这些检测结果中,找到局部最大值并抑制非极大值。
具体来说,对于一组检测结果,按照某个分数(如置信度)进行排序,然后依次选取分数最高的检测框,将其与其他检测框进行比较,如果其他检测框与该框的重叠度超过一定阈值,则认为该检测框不是最优的,将其抑制(通常是将其分数置为 0 或降低其分数)。
2.2 Soft-NMS的原理
- 传统NMS算法的问题:传统的NMS算法从具有得分S的检测框列表B开始,选择得分最高的检测框M,将其从B中移除并添加到最终检测集D中,同时移除B中与M重叠大于阈值 N t N_t Nt的任何框。该算法的主要问题是将相邻检测的得分设置为零,若物体实际上在该重叠阈值内,就会被遗漏,导致平均精度下降。
- Soft - NMS的改进:
Soft-NMS
通过修改传统NMS算法的得分衰减方式来解决这个问题。它不是将与M有高重叠的检测框的得分直接设置为零,而是根据与M的重叠程度来降低其分类得分。具体来说,提出了两种重新评分函数:- 线性衰减函数: s i = { s i , i o u ( M , b i ) < N t s i ( 1 − i o u ( M , b i ) ) , i o u ( M , b i ) ≥ N t s_i = \begin{cases} s_i, & iou(\mathcal{M}, b_i) < N_t \\ s_i(1 - iou(\mathcal{M}, b_i)), & iou(\mathcal{M}, b_i) \geq N_t \end{cases} si={si,si(1−iou(M,bi)),iou(M,bi)<Ntiou(M,bi)≥Nt,该函数将重叠阈值(N_t)以上的检测得分进行线性衰减。
- 高斯衰减函数: s i = s i e − k a ( M , b i ) 2 σ , ∀ b i ∉ D s_i = s_i e^{-\frac{ka(\mathcal{M}, b_i)^2}{\sigma}}, \forall b_i \notin \mathcal{D} si=sie−σka(M,bi)2,∀bi∈/D,对与M有重叠的检测框的得分进行高斯衰减。
- 计算复杂度:
Soft-NMS
中每一步的计算复杂度为 O ( N ) O(N) O(N)(N为检测框的数量),因为需要更新所有与M有重叠的检测框的得分,所以对于N个检测框,Soft-NMS
的计算复杂度为 O ( N 2 ) O(N^2) O(N2),与传统的贪心NMS相同。由于NMS并非应用于所有检测框(在每次迭代中会修剪具有最小阈值的框),所以这一步并不昂贵,不会影响当前检测器的运行时间。
2.3 Soft-NMS的优势
- 性能提升:在标准数据集(如PASCAL VOC2007和MS - COCO)上,通过简单地改变NMS算法为
Soft-NMS
,无需任何额外的超参数,就能使coco-style mAP指标得到持续改进。例如,在PASCAL VOC2007上,RFCN和Faster-RCNN的性能提高了1.7%;在MS-COCO上,R-FCN提高了1.3%,Faster-RCNN提高了1.1%和1.1%。使用Deformable-RFCN,Soft-NMS
将目标检测的最先进水平从39.8%提高到40.9%。 - 易于集成:
Soft-NMS
不需要任何额外的训练,且实现简单,因此可以很容易地集成到任何目标检测管道中。 - 对不同类型检测器的适用性:对于基于提议(proposal - based)的检测器,如RFCN和Faster - RCNN,Soft - NMS能显著提高性能;对于非基于提议的检测器,如SSD和YOLOv2,使用线性函数时,
Soft-NMS
也能获得一定的性能提升(约0.5%)。 - 稳定性和灵活性:通过敏感性分析可知,
Soft-NMS
的参数 σ \sigma σ在一定范围内(如0.4到0.7)性能稳定,且在不同重叠阈值下的表现优于传统NMS。低 σ \sigma σ值在较低的 O t O_t Ot下表现更好,高 σ \sigma σ值在较高的 O t O_t Ot下表现更好,并且可以通过调整 σ \sigma σ来提高检测器在不同定位情况下的性能,而传统NMS中较大的 N t N_t Nt对AP的提升非常小。 - 精度与召回率的平衡:在精度与召回率的关系上,随着 O t O_t Ot和召回率的增加,Soft - NMS在精度上有显著提升。因为传统NMS将与M重叠大于 N t N_t Nt的所有框的得分设置为零,导致许多框被遗漏,精度在召回率较高时不增加。而Soft - NMS重新评分相邻框而不是完全抑制它们,从而在召回率较高时提高了精度。在较高的 O t O_t Ot下,即使召回率较低,Soft - NMS也能获得显著的改进,因为在这种设置下,近失更有可能发生。
- 定性结果优势:从定性结果来看,
Soft-NMS
在一些情况下表现更好,例如当错误检测(false positives)与好的检测(true positive)有小重叠或低重叠时,它能降低错误检测的得分,从而抑制这些错误检测;对于一些动物图像,当NMS抑制检测框时,Soft-NMS
能为相邻框分配稍低的得分,从而能够检测到真正的阳性结果。
论文:https://openaccess.thecvf.com/content_ICCV_2017/papers/Bodla_Soft-NMS_–_Improving_ICCV_2017_paper.pdf
源码:https://github.com/bharatsingh430/soft-nms
三、添加步骤
3.1 utils/general.py
此处需要查看的文件是utils/general.py
metrics.py
中定义了全项目通用代码,相关实用函数实现。NMS
的计算方法就在其中,我们想要加入新的NMS
就只需要将代码放到这个文件内即可。
在utils/general.py
中找到non_max_suppression
函数
在此函数上方添加如下代码:
def box_iou_for_nms(box1, box2, GIoU=False, DIoU=False, CIoU=False, SIoU=False, EIou=False, eps=1e-7):
# Returns Intersection over Union (IoU) of box1(1,4) to box2(n,4)
b1_x1, b1_y1, b1_x2, b1_y2 = box1.chunk(4, -1)
b2_x1, b2_y1, b2_x2, b2_y2 = box2.chunk(4, -1)
w1, h1 = b1_x2 - b1_x1, (b1_y2 - b1_y1).clamp(eps)
w2, h2 = b2_x2 - b2_x1, (b2_y2 - b2_y1).clamp(eps)
# Intersection area
inter = (b1_x2.minimum(b2_x2) - b1_x1.maximum(b2_x1)).clamp(0) * \
(b1_y2.minimum(b2_y2) - b1_y1.maximum(b2_y1)).clamp(0)
# Union Area
union = w1 * h1 + w2 * h2 - inter + eps
# IoU
iou = inter / union
if CIoU or DIoU or GIoU or EIou:
cw = b1_x2.maximum(b2_x2) - b1_x1.minimum(b2_x1) # convex (smallest enclosing box) width
ch = b1_y2.maximum(b2_y2) - b1_y1.minimum(b2_y1) # convex height
if CIoU or DIoU or EIou: # Distance or Complete IoU https://arxiv.org/abs/1911.08287v1
c2 = cw ** 2 + ch ** 2 + eps # convex diagonal squared
rho2 = ((b2_x1 + b2_x2 - b1_x1 - b1_x2) ** 2 + (b2_y1 + b2_y2 - b1_y1 - b1_y2) ** 2) / 4 # center dist ** 2
if CIoU: # https://github.com/Zzh-tju/DIoU-SSD-pytorch/blob/master/utils/box/box_utils.py#L47
v = (4 / math.pi ** 2) * (torch.atan(w2 / h2) - torch.atan(w1 / h1)).pow(2)
with torch.no_grad():
alpha = v / (v - iou + (1 + eps))
return iou - (rho2 / c2 + v * alpha) # CIoU
elif EIou:
rho_w2 = ((b2_x2 - b2_x1) - (b1_x2 - b1_x1)) ** 2
rho_h2 = ((b2_y2 - b2_y1) - (b1_y2 - b1_y1)) ** 2
cw2 = cw ** 2 + eps
ch2 = ch ** 2 + eps
return iou - (rho2 / c2 + rho_w2 / cw2 + rho_h2 / ch2)
return iou - rho2 / c2 # DIoU
c_area = cw * ch + eps # convex area
return iou - (c_area - union) / c_area # GIoU https://arxiv.org/pdf/1902.09630.pdf
elif SIoU:
# SIoU Loss https://arxiv.org/pdf/2205.12740.pdf
s_cw = (b2_x1 + b2_x2 - b1_x1 - b1_x2) * 0.5 + eps
s_ch = (b2_y1 + b2_y2 - b1_y1 - b1_y2) * 0.5 + eps
sigma = torch.pow(s_cw ** 2 + s_ch ** 2, 0.5)
sin_alpha_1 = torch.abs(s_cw) / sigma
sin_alpha_2 = torch.abs(s_ch) / sigma
threshold = pow(2, 0.5) / 2
sin_alpha = torch.where(sin_alpha_1 > threshold, sin_alpha_2, sin_alpha_1)
angle_cost = torch.cos(torch.arcsin(sin_alpha) * 2 - math.pi / 2)
rho_x = (s_cw / cw) ** 2
rho_y = (s_ch / ch) ** 2
gamma = angle_cost - 2
distance_cost = 2 - torch.exp(gamma * rho_x) - torch.exp(gamma * rho_y)
omiga_w = torch.abs(w1 - w2) / torch.max(w1, w2)
omiga_h = torch.abs(h1 - h2) / torch.max(h1, h2)
shape_cost = torch.pow(1 - torch.exp(-1 * omiga_w), 4) + torch.pow(1 - torch.exp(-1 * omiga_h), 4)
return iou - 0.5 * (distance_cost + shape_cost)
return iou # IoU
def soft_nms(bboxes, scores, iou_thresh=0.5, sigma=0.5, score_threshold=0.25):
order = torch.arange(0, scores.size(0)).to(bboxes.device)
keep = []
while order.numel() > 1:
if order.numel() == 1:
keep.append(order[0])
break
else:
i = order[0]
keep.append(i)
# 修改成你想要使用的IOU,如果不修改默认的就是普通iou
iou = box_iou_for_nms(bboxes[i], bboxes[order[1:]], CIoU=True).squeeze()
idx = (iou > iou_thresh).nonzero().squeeze()
if idx.numel() > 0:
iou = iou[idx]
newScores = torch.exp(-torch.pow(iou, 2) / sigma)
scores[order[idx + 1]] *= newScores
newOrder = (scores[order[1:]] > score_threshold).nonzero().squeeze()
if newOrder.numel() == 0:
break
else:
maxScoreIndex = torch.argmax(scores[order[newOrder + 1]])
if maxScoreIndex != 0:
newOrder[[0, maxScoreIndex],] = newOrder[[maxScoreIndex, 0],]
order = order[newOrder + 1]
return torch.LongTensor(keep)
3.1.1 CIoU-NMS
此时,添加完成后就已经配置好了,在soft_nms
中默认配置的是CIoU-NMS
3.1.2 GIoU-NMS
在utils/general.py
中的soft_nms
函数下修改如下代码:
iou = box_iou_for_nms(bboxes[i], bboxes[order[1:]], GIoU=True).squeeze()
3.1.3 DIoU-NMS
在utils/general.py
中的soft_nms
函数下修改如下代码:
iou = box_iou_for_nms(bboxes[i], bboxes[order[1:]], DIoU=True).squeeze()
3.1.4 SIoU-NMS
在utils/general.py
中的soft_nms
函数下修改如下代码:
iou = box_iou_for_nms(bboxes[i], bboxes[order[1:]], SIoU=True).squeeze()
3.1.5 EIoU-NMS
在utils/general.py
中的soft_nms
函数下修改如下代码:
iou = box_iou_for_nms(bboxes[i], bboxes[order[1:]], EIoU=True).squeeze()
标签:GIoU,NMS,EIou,iou,torch,b1,b2,Soft
From: https://blog.csdn.net/qq_42591591/article/details/142141984