Yolo 推测的候选框NMS笔记

本文详细解析了YOLOv3目标检测算法中的非极大值抑制(NMS)过程,包括预测框的置信度处理、坐标转换及NMS算法的具体实现步骤。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

                                       YOLOV3 NMS笔记

1 参数 

   假设当前网络输入大小为416*416 ,分类为3个 [a,b,c] ,每个YOLO层3个anchor

   输入图像:img=1*3*416*416  (代表这个批次内一张图片,通道为3)

  经过降采样最大的YOLO层后(yolov3 有多个yolo层) ,输出

  prediction=1*24*13*13,其中13*13 是最后的特征图的大小,24代表(5+3)*3 

 

  2 对yolo层的输出prediction做置信度处理和NMS非最大值抑制

   (1)对prediction的 shape变换,这样为了更方便的进行非极大值抑制

     1*24*13*13 变换为1*507*8  其中8 维的数据代表 [centerx1,centery1,w,h,confidence.c1,c2,c3],c1,c2,c3识别三个类别的得分

     其中507是13*13*3(3个anchors)

    (2)将confidence<0.5 阈值的候选框移除       

    conf_mask = (prediction[:,:,4] > confidence).float().unsqueeze(2)

    #此处没有移除这些推测框,而是将他们的值全变为0,也可以在此处就把所有的不符合confidence的候选框全部去掉

     prediction = prediction*conf_mask

     # 将坐标(centerx,centery,w,h)转换为 (x1,y1,x2,y2)的格式

     box_corner = prediction.new(prediction.shape)

     box_corner[:,:,0] = (prediction[:,:,0] - prediction[:,:,2]/2)

     box_corner[:,:,1] = (prediction[:,:,1] - prediction[:,:,3]/2)

     box_corner[:,:,2] = (prediction[:,:,0] + prediction[:,:,2]/2) 

     box_corner[:,:,3] = (prediction[:,:,1] + prediction[:,:,3]/2)

     prediction[:,:,:4] = box_corner[:,:,:4]

     (3)非极大值NMS抑制

              

firstflag=False

for ind in range(batch_size): #对批次内的图像逐个处理

      image_pred = prediction[ind]          #取出一张图片的候推测框

      #计算推测的框所属的类别的得分 max_conf_score 和 max_conf_class分别代表类别和得分

      #          
      max_conf_score, max_conf_class = torch.max(image_pred[:,5:5+ num_classes], 1)

      max_conf_class = max_conf_class.float().unsqueeze(1)

      max_conf_score = max_conf_score.float().unsqueeze(1)
      
      #seq 组合需要的数值会作为返回值
      # 7维的数据分别为 x1,y1,x2,y2,confidence,classscore(类别得分),class(所属类)

      seq = (image_pred[:,:5],max_conf_score, max_conf_class)

      image_pred = torch.cat(seq, 1)

      #去除confidence等于0的预测框

      non_zero_ind =  (torch.nonzero(image_pred[:,4]))

       try:

           image_pred_ = image_pred[non_zero_ind.squeeze(),:].view(-1,7)

       except: #异常说明没有一个检测的正确

           continue 

       #没有检测的合法的物体

       if image_pred_.shape[0] == 0:

           continue             

  

        #获取识别框的中存在的类别,下面要按照类别进行独立的NMS

        # image_pred 第二维的最后一个数是类别

        img_classes = unique(image_pred_[:,-1])  

        #对每个类别单独处理,不通类别的IOU即使很大也不能去除任何一个

        for cls in img_classes:

              #获取这个类别所有的候选框

              cls_mask = image_pred_*((image_pred_[:,-1] == cls).float().unsqueeze(1))

              class_mask_ind = torch.nonzero(cls_mask[:,-2]).squeeze()

              image_pred_class = image_pred_[class_mask_ind].view(-1,7)

              #按照类别得分进行倒叙排序    
              conf_sort_index = torch.sort(image_pred_class[:,4], descending = True )[1]
              image_pred_class = image_pred_class[conf_sort_index]
              
               idx = image_pred_class.size(0)   #候选框的个数

               #这个循环进行非极大值抑制

               for i in range(idx):

                    try:

                     #计算一个与后面所有的框的IOU
                       ious=bbox_iou(image_pred_class[i].unsqueeze(0),\
                               image_pred_class[i+1:])
                    except Exception as e:#只有一个框会异常不用计算

                              break

                      #所有小于nms_conf阈值的得到保留,其他的都变成0

                      iou_mask = (ious < nms_conf).float().unsqueeze(1)

                      image_pred_class[i+1:] *= iou_mask      #注意此处是把当前框后面的重合大的变成0

                       #Remove the non-zero entries

                       non_zero_ind = torch.nonzero(image_pred_class[:,4]).squeeze()

                        #取出剩下的候选框

                       image_pred_class = image_pred_class[non_zero_ind].view(-1,7)

                

          #给预测框的最前面添加一个代表批次中第几张图片的索引号

               
           batch_ind = image_pred_class.new(image_pred_class.size(0), 1).fill_(ind)     

           seq = batch_ind, image_pred_class

            
           #下面的代码按实际情况灵活处理返回
           if not firstflag:

               output = torch.cat(seq,1)

               firstflag = True

           else:

                out = torch.cat(seq,1)

                output = torch.cat((output,out))

 

  输出的 output 是剩下的候选框n*8

  格式: 图片索引号,x1,y1,x2,y2,confidence,classscore,class

 

def unique(tensor):
    tensor_np = tensor.cpu().numpy()
    unique_np = np.unique(tensor_np)
    unique_tensor = torch.from_numpy(unique_np)
    tensor_res = tensor.new(unique_tensor.shape)
    tensor_res.copy_(unique_tensor)
    return tensor_res

 

 

计算IOU的代码,要求box1 ,box2都是(x1,y1,x2,y2)的格式

def bbox_iou(box1, box2):
    """
    Returns the IoU of two bounding boxes 
    """
    #Get the coordinates of bounding boxes
    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]
    #get the corrdinates of the intersection rectangle
    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)
    #Intersection area
    inter_area = torch.clamp(inter_rect_x2 - inter_rect_x1 + 1, min=0) * torch.clamp(inter_rect_y2 - inter_rect_y1 + 1, min=0)

    #Union Area
    b1_area = (b1_x2 - b1_x1 + 1)*(b1_y2 - b1_y1 + 1)
    b2_area = (b2_x2 - b2_x1 + 1)*(b2_y2 - b2_y1 + 1)
    iou = inter_area / (b1_area + b2_area - inter_area)
  
    return iou

  

### 如何在YOLO中引入Soft-NMS #### 背景理解 非极大值抑制(NMS)用于减少冗余的目标检测框,而Soft-NMS是对传统NMS的一种改进方法。通过调整置信度分数而非直接移除重叠的候选框,Soft-NMS能够更精细地处理多个预测框之间的关系。 #### 实现细节 为了在YOLO框架内集成Soft-NMS,需替换原有的硬性阈值裁剪逻辑为基于得分衰减机制的新策略。具体来说,在`general.py`文件中的nms函数部分加入如下改动: ```python def soft_nms(boxes, scores, sigma=0.5, thresh=0.001): """ Implementation of Soft-NMS. Args: boxes (Tensor): Bounding box coordinates. scores (Tensor): Confidence score for each bounding box. sigma (float): Sigma value used to calculate the Gaussian decay factor. thresh (float): Threshold below which we terminate the search. Returns: Tensor: Indices of kept detections sorted by decreasing confidence_scores. """ N = len(scores) indexes = torch.arange(N) for i in range(len(scores)): temp_sigma = np.exp(-(scores[i] ** 2) / (2 * sigma)) overlap = bbox_iou(boxes[i], boxes).view(-1) # Apply gaussian penalty according to IoU overlaps with higher scoring boxes scores *= ((overlap < 1e-6).type_as(scores)) + \ (((temp_sigma - 1.) * overlap + 1.).clamp(min=thresh)) keep = nms(boxes, scores, iou_threshold=0.45)[0] return indexes[keep].cpu().numpy() ``` 此代码片段展示了如何定义并应用Soft-NMS算法到现有的YOLO架构之中[^2]。注意这里采用了高斯权重来平滑化相邻边界框间的评分差异,从而使得最终保留下来的检测结果更加合理可靠。 #### 集成至YOLO流程 完成上述功能编写之后,还需要确保该新版本的NMS能够在整个推理过程中被调用。通常情况下这涉及到更新模型推断脚本内的相应位置,使其指向新的soft_nms实现而不是默认的传统方式。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值