饭后时间(四)---SSD先验框的尺寸及计算源码(含代码ssd_anchor.py)

b站:连翘春风冻傻抱蚁人https://www.bilibili.com/video/av45660456

net->
ssd_vgg_300.py

本文研究核心

准备先验框的尺寸:因为SSD在每一层特征图上面都有不同尺寸的先验框
1 特征图到了相应的层再去计算
2 而先验框大小是计算好了的,存起来,用到了再调出来

1先验框尺寸计算讲解

1 正方形,先验框尺寸关系

sk'    指的是当前层正方形的边长
   sk     指的是当前层正方形的边长
   sk+1 指的是后一层正方形的边长
sk' = sqart(sk * sk+1)


红色表示栅格,蓝色表示大小正方形先验框

2 长方形,先验框尺寸关系

   sk表示本层先验框的尺寸

3 函数def ssd_anchor_one_layer的步长step说明

对下面函数def ssd_anchor_one_layer  中step的解释:
特征图(38*38)与原图(300*300)的对应关系,
300/38 = 7.8 向上取整 = 8
特征图中的一个小格(1*1)对应原图(8*8)中一大块 这个叫步长step

offset说明

辅助到达栅格中心点

 

 

def ssd_size_bounds_to_values(size_bounds,  # 缩放比 [0.15, 0.9]也就是sk
                              n_feat_layers,  # 特征图的层数 m=6
                              img_shape=(300, 300)):
    """
    1 对应相对边界,来计算anchor的先验框的大小。
    Compute the reference sizes of the anchor boxes from relative bounds.
    2 根据网络默认的大小(300像素),绝对值以像素为单位测量的
    The absolute values are measured in pixels, based on the network default size (300
    pixels).
    3 这个函数遵循原始在在Caffe中实现SSD的计算模式。
    This function follows the computation performed in the original implementation of SSD
    in Caffe.

    返回:
    列表列出了包含每个尺寸的绝对大小。
    对于每一个尺寸大小, 比率只适用于第一个值。
    Return:
      list of list containing the absolute sizes at each scale.
      For each scale, the ratios only apply to the first value.
    """

    assert img_shape[0] == img_shape[1]
    # assert是检查条件, 如果不符合就终止, 检验是否是正方形
    img_size = img_shape[0]  # 把尺寸赋给img_size=300
    # sk = smin + (smax - smin)*(k-1)/(m-1)
    # 下面操作是为了上式计算方便,为了取整,在原始图像上面有像素点的匹配,所以*100然后/100
    min_ratio = int(size_bounds[0] * 100)  # 缩放比:smin*100 = 0.15 * 100 = 15
    max_ratio = int(size_bounds[1] * 100)  # smax*100 = 0.9 * 100 = 90
    step = int(math.floor((max_ratio - min_ratio) / (n_feat_layers - 2)))
    # (90-15)/(6-2) = 18.75,
    # int(18.75)=18
    # 这里相当于(smax - smin)/(m-1). 层数-2是因为第一层不纳入, 缩放..

    # Start with the following smallest sizes. 从最小的size开始
    sizes = [[img_size * size_bounds[0] / 2, img_size * size_bounds[0]]]  # [22.4, 45]
    # img_size * size_bounds[0]/2当前层先验框尺寸, img_size * size_bounds[0]后一层先验框尺寸
    # =>第一层的缩放比比较特殊,特征图比较大, 使用了更加小的缩放比进行计算, /2 ,如下:
    # img_size * size_bounds[0]/2 = 0.15/2  这就是第一层先验框的尺寸

    for ratio in range(min_ratio, max_ratio + 1, step):  # min_ratio=15, max_ratio=90, step=18
        # 以步长来计算每一层的先验框的尺寸, 并记录在size中
        # step = 18,计算方法:(smax - smin) / m-2,  这里(0.15, 0.9)被分为4部分,=>因为第一层特殊
        sizes.append((img_size * ratio / 100.,
                      img_size * (ratio + step) / 100.))
        # (300*15,300*33)/100 = (45., 99.)    ratio = 15
        # (300*33,300*51)/100 = (99., 153.)    ratio = 33
        # (300*51,300*69)/100 = (153., 207.)    ratio = 51
        # (300*69,300*87)/100 = (207., 261.)    ratio = 69
        # (300*87,300*105)/100 = (261., 315.)    ratio = 87
    # sizes = [[22.4, 45],
    #          [45., 99.],  45 = 300*0.15
    #          [99., 153.],
    #          [153., 207.],
    #          [207., 261.],
    #          [261., 315.]]/100   261 = 300*0.87
    # sizes列表中的[x,y]分表代表本层的先验框尺寸,和后一层的先验框尺寸
    # sizes列表中的[x,y]分表代表每层先验框的小正方形,大正方形的尺寸
    return sizes
"""
     feat_shapes = [(38, 38), (19, 19), (10, 10), (5, 5), (3, 3), (1, 1)],
         anchor_size_bounds=[0.15, 0.9]
         anchor_sizes=[(21., 45.),
                       (45., 99.),
                       (99., 153.),
                       (153., 207.)
                       [207., 261.],
    #                  [261., 315.]]/100
"""


def ssd_feat_shapes_from_net(predictions, default_shapes=None):
    """Try to obtain the feature shapes from the prediction layers. The latter
    can be either a Tensor or Numpy ndarray.
    Return:
      list of feature shapes. Default values if predictions shape not fully
      determined.
    """
    feat_shapes = []
    for l in predictions:
        # Get the shape, from either a np array or a tensor.
        if isinstance(l, np.ndarray):
            shape = l.shape
        else:
            shape = l.get_shape().as_list()
        shape = shape[1:4]
        # Problem: undetermined shape...
        if None in shape:
            return default_shapes
        else:
            feat_shapes.append(shape)
    return feat_shapes


def ssd_anchor_one_layer(img_shape,  # 原始图像
                         feat_shape,  # 特征图的尺寸
                         sizes,  # 当前一个先验框的尺寸:一组先验框中,中心的小正方形的边长 ?=?
                         ratios,  # 缩放比
                         step,  # 步长,假设第一层特征图38*38对应原图300*300, 向上取整[300/38] = [7.8] = 8 = step
                                # 不同于前面的步长,前面的step = (90-15)/4 = 18
                         offset=0.5,  # 偏置值,为了让一个栅格左上角的起始点,到达栅格的中心点
                         dtype=np.float32):
    """
    1 为每个特征层计算SSD的先验框
    Computer SSD default anchor boxes for one feature layer.
    2 确定网格中心的相对位置,和网格的相对宽度w和高度h
    Determine the relative position grid of the centers, and the relative width and height.
    3 参数
      Arguments:
      1)feat_shape: 特征图的shape,用于计算相对位置网格
      feat_shape: Feature shape, used for computing relative position grids;
      2)size: 绝对参考尺寸
      size: Absolute reference sizes;
      3)ratio: 比例,为了使用这些特征
      ratios: Ratios to use on these features;
      4)image_shape:图片形状,用于计算下面画对于former的高度h,宽度w
      img_shape: Image shape, used for computing height, width relatively to the former;
      5) offset: 网格偏移量
      offset: Grid offset.
    4 返回值:
    Return:
      y, x, h, w: Relative x and y grids, and height and width.
    """
    # Compute the position grid: simple way.
    # y, x = np.mgrid[0:feat_shape[0], 0:feat_shape[1]]
    # y = (y.astype(dtype) + offset) / feat_shape[0]
    # x = (x.astype(dtype) + offset) / feat_shape[1]
    # Weird SSD-Caffe computation using steps values...
    y, x = np.mgrid[0:feat_shape[0], 0:feat_shape[1]]
    '''
    如果feat_shap[0] feat_shape[1]都是38的话
    print(x), print(y)都是下面输出
    [[ 0  1  2 ... 35 36 37]
     [ 0  1  2 ... 35 36 37]
     [ 0  1  2 ... 35 36 37]
     ...
     [ 0  1  2 ... 35 36 37]
     [ 0  1  2 ... 35 36 37]
     [ 0  1  2 ... 35 36 37]]
    '''
    # 对于第一个特征图(block4:38*38):
    y = (y.astype(dtype) + offset) * step / img_shape[0]  # (y+0.5)*(300/38)/300 = (y+0.5)/38 这里归一化, 缩放到0 1之间
    x = (x.astype(dtype) + offset) * step / img_shape[1]  # dtype = np.float32
    # x = (x.astype(dtype) + offset) * step 这里相当于是把特征图里面的一个点对应到了原图的相应位置(y+0.5)*(300/38)
    # x = (x.astype(dtype) + offset) * step / img_shape[1]  然后除以原图大小300,,归一化(y+0.5)*(300/38)/300
    # 得到在原图上,相对原图比例大小的每个锚点的中心坐标x,y

    # Expand dims to support easy broadcasting.
    y = np.expand_dims(y, axis=-1)  # 在最后增加了一个维度通道数,为了后面计算方便, 现在是38*38*1
    x = np.expand_dims(x, axis=-1)  # -1 表示最后一维[输入,高度,宽度,通道]

    # Compute relative height and width. 计算相对高度和宽度
    # Tries to follow the original implementation of SSD for the order. 常识遵循原始的SSD的实现顺序
    # 38*38 3*3 1*1这三层有4个先验框,其他层6个先验框
    '''为什么不直接给出的原因:
    # sizes列表中的[x,y]分表代表本层的先验框尺寸,和后一层的先验框尺寸
    # sizes列表中的[x,y]分表代表每层先验框的中心得小正方形,和外面的大正方形的尺寸
    len(sizes)代表正方形的个数,为 2
    len(ratios)代表对中间的小正方形进行变形,变成长方形的个数, 为 2
    '''
    num_anchors = len(sizes) + len(ratios)  # 计算当前一层一共有多少个先验框, 2+2 = 4个先验框
    h = np.zeros((num_anchors,), dtype=dtype)  # 为后面 记录高和宽的值 做准备
    w = np.zeros((num_anchors,), dtype=dtype)
    '''
    >>> num_anchors = 4
    >>> h = np.zeros((num_anchors,), dtype=np.float32)
    >>> print(h)
    [0. 0. 0. 0.]
    第一位代表中间的小正方形, 用当层的先验框的尺寸处理原始图像的尺寸,可以高的尺寸缩放到0-1之间,全部以比例表示
    '''
    # Add first anchor boxes with ratio=1.
    # =>添加第1个先验框
    h[0] = sizes[0] / img_shape[0]  # 缩放到0-1之间, 需要一个尺寸的时候直接乘原始图像,得到真实尺寸
    w[0] = sizes[0] / img_shape[1]
    di = 1  # 锚点宽个数偏移?=?. 可以看成一个计录器, =1代表第一个先验框的尺寸就记录完成了
    # ?=?测试下sizes里面内容

    # =>添加第2个先验框
    if len(sizes) > 1:  # 检验有多少个正方形的先验框,因为第一层有两个正方形的先验框,所以肯定大于一
        h[1] = math.sqrt(sizes[0] * sizes[1]) / img_shape[0]  # 计算外面的大正方形的先验框与原始图像的比例
        w[1] = math.sqrt(sizes[0] * sizes[1]) / img_shape[1]  # /img_shape 300变成比例值
        # 大正方形边长 = sqart(sk * sk+1), 即对sqart(本层小正方形边长*下层小正方形边长)
        di += 1

    # =>添加第3个先验框
    for i, r in enumerate(ratios):
        # 关于长方形先验框的边长的计算
        # h = sk/sqart(ar)
        # w = sk*sqart(ar)
        h[i + di] = sizes[0] / img_shape[0] / math.sqrt(r)  # sk/300/sqrt(ar), 先除以αr, 再除以真实图像边长, 归一
        w[i + di] = sizes[0] / img_shape[1] * math.sqrt(r)  # sk/300 * sqrt(ar),
    return y, x, h, w


def ssd_anchors_all_layers(img_shape,
                           layers_shape,  # 每个特征层形状尺寸
                           anchor_sizes,  # 起始特征途中框的长宽size
                           anchor_ratios,  # 锚点框长宽比列表
                           anchor_steps,  # 锚点框相对原图缩放比
                           offset=0.5,
                           dtype=np.float32):
    """Compute anchor boxes for all feature layers.
    检测所有特征图中锚点框的4个坐标信息; 输入原始图大小
    """
    layers_anchors = []  # 用于存放所有特征图中锚点框位置尺寸信息
    for i, s in enumerate(layers_shape):
        # 分别计算每个特征图中锚点框的位置尺寸信息:
        '''下面调用了前面的函数
        def ssd_anchor_one_layer(img_shape,  # 原始图像
                         feat_shape,  # 特征图的尺寸
                         sizes,  # 当前一个先验框的尺寸:一组先验框中,中心的小正方形的边长 ?=?
                         ratios,  # 缩放比
                         step,  # 步长,假设第一层特征图38*38对应原图300*300, 向上取整[300/38] = [7.8] = 8 = step
                                # 不同于前面的步长,前面的step = (90-15)/4 = 18
                         offset=0.5,  # 偏置值,为了让一个栅格左上角的起始点,到达栅格的中心点
                         dtype=np.float32):
        '''
        # 调用函数的返回数据全部放在了下面anchor_bboxes
        anchor_bboxes = ssd_anchor_one_layer(img_shape,
                                             s,
                                             anchor_sizes[i],
                                             anchor_ratios[i],
                                             anchor_steps[i],
                                             offset=offset, dtype=dtype)
        # 输入:第i个特征图中起始锚点框大小:如第0个是(21., 45.)  不是22.4吗 ?=?
        # 输入:第i个特征图中锚点框长宽比列表:如第0个是[2, .5]
        # 输入:第i个特征图中锚点框相对与原始图像的缩放比:如第0个是8  [300/38] = [7.8] = 8 = step
        layers_anchors.append(anchor_bboxes)  # 再将得到结果放到列表中
    return layers_anchors

 

  • 6
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

计算机视觉-Archer

图像分割没有团队的同学可加群

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值