NMS原理及其代码实现

1. 为什么要用NMS

YOLOv3在预测阶段, 每个目标至少会生成3个proposals, 但一个目标一般只显示一个proposal, 因此需要对proposals进行去重,
这里去重的方法是NMS. 而NMS的筛选依据是IoU.

2. NMS的步骤

  1. 先对所有proposals进行置信度(confidence)的排序, 按照置信度的大小进行降序排序(从大到小排序).

  2. 将最大置信度的proposal ( p r o p o s a l m a x {\rm proposal_{max}} proposalmax)取出来, 与剩下的proposals( p r o p o s a l s r e s t {\rm proposals_{rest}} proposalsrest)进行IoU的计算, 这里的目的是筛选后面的proposals, 意思是说:

    • 如果 p r o p o s a l s r e s t {\rm proposals_{rest}} proposalsrest中的某一个proposal与 p r o p o s a l m a x {\rm proposal_{max}} proposalmax之间计算得到的IoU的值小于设定的阈值( t h r e s h {\rm thresh} thresh), 那么就认为这个proposal是可用的(可保留的)

    • 一旦有IoU > t h r e s h {\rm thresh} thresh的proposal, 我们则认为该proposal和 p r o p o s a l m a x {\rm proposal_{max}} proposalmax预测的是同一个目标(object), 因此该框就冗余了(因为它的置信度没有 p r o p o s a l m a x {\rm proposal_{max}} proposalmax的置信度高), 需要去除.

      置信度最大的proposal是一定会保留的, 我们是在挑选剩余的proposals

  3. p r o p o s a l m a x {\rm proposal_{max}} proposalmax与所有 p r o p o s a l s r e s t {\rm proposals_{rest}} proposalsrest的IoU计算和筛选完毕后, 置信度指针指向下一个(第二高置信度的proposal)

  4. 重复2, 3 -> 递归

NMS的核心主要事项就是: IoU > 阈值 的proposals需要去除, < 阈值的proposals则保留, 目的是不影响预测其他object的proposals受到影响.

3. NMS的代码实现

import torch


def iou(proposal, proposals, isMin=False):
    """计算proposals的IoU
        在计算IoU时, 需要求二者交集和并集. 假设两个框的坐标分别为: (x_11, y_11, x_12, y_12)和(x_21, y2_1, x_22, y_22)
            交集框的坐标: (max(x_11, x_21), max(y_11, y_21), min(x_12, x_22), min(y_12, y_22))

    Args:
        proposal (_type_): 置信度最高的proposal -> [4]
        proposals (_type_): 剩余的proposals -> [N, 4]
        isMin (bool, optional): IoU的计算模式, 有两种:
                                                    1. (True) 交集 / 最小面积
                                                    2. (False -> Default) 交集 / 并集
    Return:
        IoU (float): 返回proposal与proposals的IoU
    """
    # 计算当前框的面积: proposal = [x, y, w, h]
    box_area = (proposal[2] - proposal[0]) * (proposal[3] - proposal[1])
    
    # 计算proposals中所有框的面积 proposals = [N, [x, y, w, h]]
    boxes_area = (proposals[:, 2] - proposals[:, 0]) * (proposals[:, 3] - proposals[:, 1])

    # 计算交集proposal和proposals的计算
    xx_1 = torch.maximum(proposal[0], proposals[:, 0])  # 交集的左上角x坐标
    yy_1 = torch.maximum(proposal[1], proposals[:, 1])  # 交集的左上角y坐标
    xx_2 = torch.minimum(proposal[2], proposals[:, 2])  # 交集的右下角x坐标
    yy_2 = torch.minimum(proposal[3], proposals[:, 3])  # 交集的右下角y坐标
    
    # 特殊情况: 两个框没有挨着 -> 没有交集
    w, h = torch.maximum(torch.Tensor([0]), xx_2 - xx_1), torch.maximum(torch.Tensor([0]), yy_2 - yy_1)
    
    # 获取交集的框的面积
    intersection_area = w * h
    
    if isMin:  # 如果一个框在另一框的内部
        return intersection_area / torch.min(box_area, boxes_area)
    
    else:  # 两个框相交 -> 交集 / 并集
        return intersection_area / (box_area + boxes_area - intersection_area)
    
    
def nms(proposals, thresh=0.3, isMin=False):
    """非极大值抑制用来去除冗余的proposals

    Args:
        proposals (torch.tensor): 网络推理得到的proposals -> [conf, x, y, w, h]
        thresh (float, optional): NMS筛选的阈值. Defaults to 0.3.
        isMin (bool, optional): IoU的计算方式, 默认为交集/并集. Defaults to False.
    """
    
    # 根据proposals的置信度进行降序排序
    sorted_proposals = proposals[proposals[:, 0].argsort(descending=True)]

    # 定义一个ls, 用来保存需要保留的proposals
    keep_boxes = []
    
    while len(sorted_proposals) > 0:
        # 取出置信度最高的proposal并存放到ls中
        _box = sorted_proposals[0]  
        keep_boxes.append(_box)
        
        if len(sorted_proposals) > 1:
            # 取出剩余的proposals
            _boxes = sorted_proposals[1:]
            
            # 置信度最高的proposal与其他proposals进行IoU的计算
            """
                需要注意的是, NMS在筛选的时候是保留IoU小于thresh的. 为什么?
                    两个proposal的IoU越小, 说明两个proposal框起来的对象越不一样, 别忘了, NMS是为了去重, 所以需要保留小于IoU的proposals

                torch.where(条件): 返回符合条件的索引
            """
            sorted_proposals = _boxes[torch.where(iou(_box, _boxes, isMin) < thresh)]
        
        # 当剩下最后一个时候, 就不进行IoU计算了(自己与自己计算IoU没有意义)
        else:
            break
    
    # 将ls转换为高维的tensor
    return torch.stack(keep_boxes)


if __name__ == "__main__":
    proposal = torch.tensor(data=[0, 0, 4, 4])
    proposals = torch.tensor(data=[[4, 4, 5, 5],   # 没有交集
                                   [1, 1, 5, 5]])  # 有交集
    
    print(iou(proposal, proposals))  # tensor([0.0000, 0.3913])

    
    boxes = torch.tensor(data=[
                               [0.5, 1, 1, 10, 10],
                               [0.9, 1, 1, 11, 11],  # 和上面那个很相似
                               [0.4, 8, 8, 12, 12]  # 和上面两个都不相似
    ])
    
    print(nms(boxes, thresh=0.1))
    """仅保留了2个
    tensor([[ 0.9000,  1.0000,  1.0000, 11.0000, 11.0000],
            [ 0.4000,  8.0000,  8.0000, 12.0000, 12.0000]])
    """
    
    print(nms(boxes, thresh=0.3))
    
    """全部都保留了
    tensor([[ 0.9000,  1.0000,  1.0000, 11.0000, 11.0000],
            [ 0.5000,  1.0000,  1.0000, 10.0000, 10.0000],
            [ 0.4000,  8.0000,  8.0000, 12.0000, 12.0000]])
    """
    ```
  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: soft-nms pytorch源代码是一个基于PyTorch框架实现的软非极大值抑制算法的源代码。该算法可以在目标检测中用于抑制重叠的检测框,从而提高检测的准确性和效率。该源代码提供了一个简单易用的接口,可以方便地集成到现有的目标检测系统中。 ### 回答2: Soft-NMS是一种针对目标检测中非极大值抑制(Non-Maximum Suppression, NMS)的改进算法。Soft-NMS的目的是尽可能保留更多的检测结果,其中,保留哪些检测结果以及去除哪些检测结果是通过计算每个检测框和其他检测框的IoU(Intersection over Union)来决定的。 PyTorch是一个流行的深度学习框架,在PyTorch中实现Soft-NMS非常简单。下面是PyTorch官方提供的Soft-NMS代码: ```python def soft_nms(dets, sigma=0.5, Nt=0.3, threshold=0.001, method=2): """ :param dets: [[x1, y1, x2, y2, score], ...] :param sigma: :param Nt: :param threshold: :param method: :return: indexes to keep """ N = dets.shape[0] indexes = np.arange(N) for i in range(N): pos = i + 1 if i != N - 1: maximum = np.argmax(dets[pos:, 4]) if dets[i, 4] < dets[maximum + pos, 4]: dets[[i, maximum + pos], :] = dets[[maximum + pos, i], :] indexes[[i, maximum + pos]] = indexes[[maximum + pos, i]] t_bbox, t_score = dets[i, :4], dets[i, 4] if method == 1: weight = np.exp(-(np.power((dets[i + 1:, :4] - t_bbox), 2).sum(1) / sigma)) else: weight = np.zeros((N - i - 1,)) _idx = np.where(dets[i + 1:, 4] >= Nt)[0] + i + 1 if len(_idx) > 0: ex_dets = dets[_idx, :] overlap = iou(t_bbox.reshape(1, -1), ex_dets[:, :4]) weight[_idx - (i + 1)] = overlap weight = np.exp(-(weight * weight) / sigma) dets[i + 1:, 4] *= weight score_mask = dets[i + 1:, 4] >= threshold indexes_mask = indexes[i + 1:][score_mask] dets[i + 1:, :] = dets[indexes_mask, :] indexes[i + 1:] = indexes[indexes_mask] if len(indexes_mask) == 0: break keep = np.zeros(N, dtype=np.intp) keep[indexes] = 1 return np.where(keep == 1)[0] ``` 这里,soft_nms函数接受一个包含检测结果的二维数组dets,其中每个元素包含四个坐标[x1, y1, x2, y2]和一个分数score。函数的其他参数包括sigma、Nt、threshold和method等。 对于每个检测框,软NMS算法首先检查其余检测框的IoU。如果另一个检测框的IoU高于给定的Nt阈值,则它的分数将被调整到小于原始分数因子*(1-IoU)。然后,score_mask掩码被用来删掉具有较低分数的检测框,直到达到需要的最终物体数或所有框都不再死亡。最后,保留下来的检测框的索引被返回给用户。 这个PyTorch源代码是一个很好的例子,可以帮助我们了解如何使用Python来自定义各种优化算法,包括Soft-NMS。 ### 回答3: Soft-NMS是一种基于非极大抑制(NMS)的目标检测算法。与传统的NMS算法不同,Soft-NMS通过对重叠框之间的分数进行加权来降低框的置信度,而不是直接将框丢弃。这样可以在一定程度上保留重叠框中的信息。 PyTorch是一种流行的深度学习框架,提供了供用户使用的许多预定义算法和函数。PyTorch实现了Soft-NMS的源代码,这使得用户可以直接使用这种功能而不必手动实现。 在PyTorch中,Soft-NMS实现是在torchvision.ops.nms.soft_nms中完成的。它有以下四个参数: - boxes: 包含所有检测框的tensor。 - scores: 所有检测框的分数。 - iou_threshold: 重叠IOU阈值。如果两个框之间的IOU低于此阈值,则不会执行Soft-NMS。 - score_threshold: 分数阈值。如果框的分数低于此阈值,则不会执行Soft-NMS。 整个NMS算法的流程如下: 1. 将分数从高到低排序,并将相应的边界框记录下来。 2. 选择分数最高的框,并记录下该框的索引号。 3. 计算该框与所有其他框之间的IOU值,并使用Soft-NMS算法对其进行加权。 4. 删除其余重叠框,并在剩下的框中重复步骤2-4,直到所有框都被处理。 调用soft_nms函数后,将返回保留下来框的索引列表。然后可以使用这个索引列表来关联boxes和scores,并对检测到的目标进行可视化或其他后处理操作。 在使用Soft-NMS时,需要根据具体情况调整两个阈值。如果两个阈值设置得太低,可能会误保留一些低质量的检测框。如果设置得过高,可能会丢失一些真实的目标。因此,建议在数据集上进行测量和调整。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值