Pytorch目标检测算法(3)(基于李沐老师的课程)

目录

三.锚框的选择

3.1 交并比(IOU)

1. 定义

​2.实现思路

3.实现

3.2 锚框标号

1.定义

2.实现思路

3.实现


三.锚框的选择

3.1 交并比(IOU)

1. 定义

对于生成的锚框,我们应当采取一种量化手段来评价当前锚框对于真实边界框匹配度,等价于衡量锚框于真实边界框之间的相似性。因此,我们引入了交并比的概念。(即通过像素集的杰卡德系数来测量锚框和真实边框的相似性)。交并比的取值范围为[0, 1] , 0表示完全不重合, 1表示完全重合。

图示:

Pytorch目标检测算法(3)(基于李沐老师的课程)_第1张图片

2.实现思路

1)得到锚框和真实边框的面积

2)得到交集的左上和右下顶点坐标

3)计算每一个锚框和所有真实边款的交集,并集面积

4)将得到的面积与并集相除得到IOU值矩阵

3.实现

第一步:得到锚框和真实边框的面积

def box_iou(boxes1, boxes2):
    """计算两个锚框或边界框列表中成对的交并比
        boxes1 : 锚框
        boxes2 : 真实边框
    """
    box_area = lambda boxes: ((boxes[:, 2] - boxes[:, 0]) *
                              (boxes[:, 3] - boxes[:, 1]))
    # boxes1,boxes2,areas1,areas2的形状:
    # boxes1:(boxes1的数量,4),
    # boxes2:(boxes2的数量,4),
    # areas1:(boxes1的数量,),
    # areas2:(boxes2的数量,)
    areas1 = box_area(boxes1)
    areas2 = box_area(boxes2)

 第二步:交集的左上和右下顶点坐标

    """
        这里运用的广播机制
        boxes1.shape : [anchors_num, 4]
        boxes2.shape : [classes_num, 4]
        boxes1[:, None, :2].shape : [anchors_num, 1, 2]
        torch.max(boxes1[:, None, :2], boxes2[:, :2]).shape 
        = [anchors_num, classes_num, 2]

        通过广播机制能够将每个锚框与所有的真是边框进行计算,也就是一个锚框与classes_num种      
        真实边框进行计算。

    """
    inter_upperlefts = torch.max(boxes1[:, None, :2], boxes2[:, :2])
    inter_lowerrights = torch.min(boxes1[:, None, 2:], boxes2[:, 2:])

第三步:计算交集,并集面积 

    # tensor.clamp(min=0)的意思是如果值为负数则设为0 ,因为得到的交集的宽度和高度 >= 0
    inters = (inter_lowerrights - inter_upperlefts).clamp(min=0)

    # inter_areas的形状:(boxes1的数量,boxes2的数量)
    """
    inters.shape : [anchors_num, class_num, 2]
    提取宽度:inters[:, :, 0].shape : [anchors_num, classes_num]
    提取高度:inters[:, :, 1].shape : [anchors_num, classes_num]
    其中每一行为一个锚框与每个真实边框交集的宽高
    """
    # 计算交集面积
    inter_areas = inters[:, :, 0] * inters[:, :, 1]
    
    # 计算并集面积
    """
    这里依然运用了广播机制,将一个锚框和这个锚框与每种真实边框的交集相加
    union_areas.shape : [anchors_num, classes_num]
    """
    union_areas = areas1[:, None] + areas2 - inter_areas

第四步:返回IOU值矩阵 

    return inter_areas / union_areas

3.2 锚框标号

1.定义

在拥有了对锚框的量化标准后,就可以通过算法来进行锚框的选择和标号。

在锚框的标号中,我们采取两步

a.选出当前IOU矩阵的最大值,将其下标进行存储,然后删除所在的行和列,循环执行

图示:

Pytorch目标检测算法(3)(基于李沐老师的课程)_第2张图片

b) 设置IOU阈值,将高于IOU阈值的锚框下标进行存储。

如果不进行b步那么就会只有真实锚框个数个正类锚框, 其余全部是负类锚框。

2.实现思路

 1)用一个一维tensor来保存分配结果,下标表示第几个锚框,值表示列(类别)

 2)由于找到最大的过程会修改IOU值矩阵,因此先找到大于阈值的锚框进行标号存储

 3)循环找到全局最大值,进行存储

3.实现

第一步:准备需要的数据

def assign_anchor_to_bbox(ground_truth, anchors, device=None, iou_thread=0.5):
    """
    将最接近的真实边框分配给锚框
    :param ground_truth:  真实框
    :param anchors: 所有锚框
    :param device: 设备
    :param iou_thread: iou限度
    :return: 锚框列表 索引为i 值为j
    """
    # num_anchors : 锚框数量 num_get_boxes : 真实边框数量
    num_anchors, num_get_boxes = anchors.shape[0], ground_truth.shape[0]
    
    # 每个锚框与真实框的iou值
    jaccard = box_iou(anchors, ground_truth)
    
    # 生成初始一维tensor用来保存锚框标号 初始值为-1 长度为锚框数量
    anchors_bbox_map = torch.full((num_anchors,), -1, dtype=torch.long,
                                  device=device)

第二步:找到所有IOU值大于阈值的锚框进行标号 

代码采取的是找到每行中的最大值(即每个锚框最有可能的类别)

    # 返回一行的最大值 和 索引
    max_ious, indices = torch.max(jaccard, dim=1)  
    
    # nonzero 得到非0元素的下标
    # 得到每一行iou值大于0.5的行索引
    anc_i = torch.nonzero(max_ious >= 0.5).reshape(-1)  
    
    # 类别索引
    box_j = indices[max_ious >= 0.5]  
    
    # 保存相对应锚框的类别
    anchors_bbox_map[anc_i] = box_j

代码难点:

1)torth.max(input, dim=None)  : 返回的是dim维度下的最大值,和其维度索引

     补充:torch.argmax(input, dim=None) : 仅仅返回索引

     eg : 

        Pytorch目标检测算法(3)(基于李沐老师的课程)_第3张图片

2) torch.nonzero : 返回非0元素的索引 ,按列布局。

eg:

Pytorch目标检测算法(3)(基于李沐老师的课程)_第4张图片

第三步:循环找取全局最大值,并存储

    # 由于IOU值的范围为[0, 1], 因此我们仅需将选中的最大值信息保存后,
    # 将其所在行列的值修改为-1,就等价于删除
    col_discard = torch.full((num_anchors,), -1)  # 按照行数生成-1
    row_discard = torch.full((num_gt_boxes,), -1)  # 按照列数生成-1
    
    # 循环寻找最大值,由于我们仅需要找到每种类别的最大值因此仅循环真实锚框数目次
    for _ in range(num_gt_boxes):

        # 将IOU矩阵 flatte然后得到全局最大值
        max_idx = torch.argmax(jaccard)
        
        # 与真实锚框数取余得到列索引, 除真实锚框书得行索引
        box_idx = (max_idx % num_gt_boxes).long()
        anc_idx = (max_idx / num_gt_boxes).long()
        
        # 修改对应得存储信息
        anchors_bbox_map[anc_idx] = box_idx
        
        # 修改其所在行例得值为-1
        jaccard[:, box_idx] = col_discard
        jaccard[anc_idx, :] = row_discard
    

第四步: 返回结果

    return anchors_bbox_map

你可能感兴趣的:(pytorch,目标检测,深度学习)