multibox_prior函数锚框生成部分个人理解

在最近自学李沐沐神的《动手学深度学习》中,一直在研究这个方法里的代码,属实是差点把我整崩溃了,在网上找了许多参考还是看的很崩溃,在近一周的折磨中找到了一个理解的方法,那就是设置自定义初始值去逐句分析,怕自己忘记,来发一个自己理解的笔记。

import torch
height,width = 3,5
sizes=[0.25,0.5,0.75]
ratios=[1,2,0.5]
num_sizes,num_ratios=len(sizes),len(ratios)
boxes_per_pixel=(num_sizes+num_ratios-1)

导入包,定义初始高宽为3x5,缩放比(占原图的大小)和宽高比
boxes_per_pixel即每组锚框的数量,也就是每个基本单位——像素生成的锚框数量,原因在视频里都有讲到

size_tensor = torch.tensor(sizes)
ratio_tensor = torch.tensor(ratios)

将列表转换成tensor格式

size_tensor,ratio_tensor
'''输出'''
(tensor([0.2500, 0.5000, 0.7500]), tensor([1.0000, 2.0000, 0.5000]))

每个像素的大小是1,那么第一个像素的中心即为(0.5,0.5)
可以认为是在每个像素的中心生成锚框

offset_h,offset_w=0.5,0.5

之后的操作像是归一化

steps_h = 1.0/height
#  x轴
steps_w = 1.0/width
steps_h,steps_w
'''结果'''
(0.3333333333333333, 0.2)

经过这一步后,steps_h,steps_w就保存了高宽归一化之后每走一个像素需要移动的距离
比如宽是5,就把1平均分成五份,距离就是0.2


下一步是这个操作

center_h=(torch.arange(height)+offset_h)*steps_h
center_w=(torch.arange(width)+offset_w)*steps_w

torch.arange方法是生成[0,1,…i-1]的方法,具体的可以百度,这里就只讲这段代码执行后的结果
我们先看前面一部分

center_h=(torch.arange(height)+offset_h)
center_w=(torch.arange(width)+offset_w)
center_h,center_w
'''结果'''
(tensor([0.5000, 1.5000, 2.5000]),
 tensor([0.5000, 1.5000, 2.5000, 3.5000, 4.5000]))

很显然,保存了高宽方向的每个像素中心点的坐标
例如高为3,那么每个像素中心的纵坐标一定是0.5、1.5或者2.5

center_h*=steps_h
center_w*=steps_w

再经过以上代码,就把这些中心坐标也进行了归一化

center_h,center_w
'''结果'''
(tensor([0.1667, 0.5000, 0.8333]),
 tensor([0.1000, 0.3000, 0.5000, 0.7000, 0.9000]))

之后的代码更像是要为最后的组合做准备

shift_y,shift_x=torch.meshgrid(center_h,center_w,indexing='ij')

这一步将每个锚框中心点坐标的高宽以一种特殊的样子保存了下来

shift_y,shift_x
'''结果'''
(tensor([[0.1667, 0.1667, 0.1667, 0.1667, 0.1667],
         [0.5000, 0.5000, 0.5000, 0.5000, 0.5000],
         [0.8333, 0.8333, 0.8333, 0.8333, 0.8333]]),
 tensor([[0.1000, 0.3000, 0.5000, 0.7000, 0.9000],
         [0.1000, 0.3000, 0.5000, 0.7000, 0.9000],
         [0.1000, 0.3000, 0.5000, 0.7000, 0.9000]]))

首先形状都与输入的形状相同,3x5,第一个张量每行相同,共三行;第二个张量每列相同,共五列。至于为什么这么做,在后面有我的理解。

下一步是:

w = torch.cat((size_tensor * torch.sqrt(ratio_tensor[0]),
                   sizes[0] * torch.sqrt(ratio_tensor[1:])))
h = torch.cat((size_tensor/torch.sqrt(ratio_tensor[0]),
				   size_tensor[0]/torch.sqrt(ratio_tensor[1:])))

torch.cat是组合张量的方法
锚框的宽高计算式分别是hs根号r,hs/根号r,具体证明过程不做介绍
在上面的代码中,仅仅计算了s
根号r和s/根号r这一部分,也就是还在归一化中,之后恢复正常大小时再乘h
至于为什么要用sizes与ratios的第一个数相乘+sizes的第一个与ratios去掉第一个相乘,再课程中都有讲过,也就是这里boxes_per_pixel的由来

w,h
'''结果'''
tensor([0.2500, 0.5000, 0.7500, 0.3536, 0.1768])
tensor([0.2500, 0.5000, 0.7500, 0.1768, 0.3536])

就出现了五个锚框的宽和高


假设每个锚框的中心在原点,那么这个锚框的右上角的坐标为(-w/2,h/2)
那下面的代码就好理解了

anchor_manipulations = torch.stack((-w, -h, w, h)).T.repeat(
                                        height * width, 1) / 2
anchor_manipulations
'''结果'''
tensor([[-0.1250, -0.1250,  0.1250,  0.1250],
        [-0.2500, -0.2500,  0.2500,  0.2500],
        [-0.3750, -0.3750,  0.3750,  0.3750],
        [-0.1768, -0.0884,  0.1768,  0.0884],
        [-0.0884, -0.1768,  0.0884,  0.1768],
        [-0.1250, -0.1250,  0.1250,  0.1250],
        [-0.2500, -0.2500,  0.2500,  0.2500],
        [-0.3750, -0.3750,  0.3750,  0.3750],
        [-0.1768, -0.0884,  0.1768,  0.0884],
        [-0.0884, -0.1768,  0.0884,  0.1768],
        [-0.1250, -0.1250,  0.1250,  0.1250],
        [-0.2500, -0.2500,  0.2500,  0.2500],
.........

每行都是一个锚框的左宽,下高,右宽,上高(中心为原点)
每五行一个循环,代表每个像素有五个锚框
torch.stack((-w, -h, w, h)).T生成了五行张量,每行为一个锚框的信息,repeat(height * width, 1)将这五行复制了height * width次,因为height * width的图片中就有height * width个像素。


在下一步操作之前我们先来看这一行代码

out_grid = torch.stack([shift_x, shift_y, shift_x, shift_y],
                dim=1)
out_grid
'''结果'''
tensor([[0.1000, 0.1667, 0.1000, 0.1667],
        [0.3000, 0.1667, 0.3000, 0.1667],
        [0.5000, 0.1667, 0.5000, 0.1667],
        [0.7000, 0.1667, 0.7000, 0.1667],
        [0.9000, 0.1667, 0.9000, 0.1667],
        [0.1000, 0.5000, 0.1000, 0.5000],
        [0.3000, 0.5000, 0.3000, 0.5000],
        [0.5000, 0.5000, 0.5000, 0.5000],
        [0.7000, 0.5000, 0.7000, 0.5000],
        [0.9000, 0.5000, 0.9000, 0.5000],
        [0.1000, 0.8333, 0.1000, 0.8333],
        [0.3000, 0.8333, 0.3000, 0.8333],
        [0.5000, 0.8333, 0.5000, 0.8333],
        [0.7000, 0.8333, 0.7000, 0.8333],
        [0.9000, 0.8333, 0.9000, 0.8333]])

结合前文中求得的shift_x与shift_y(分别代表了锚框的宽和高),可以看出这一步恰好就表示出来3*5=15个像素的中心点位置的横坐标、纵坐标、横坐标、纵坐标
再用下面的方法将每个像素坐标乘5

out_grid = out_grid.repeat_interleave(boxes_per_pixel, dim=0)
out_grid
'''结果'''
tensor([[0.1000, 0.1667, 0.1000, 0.1667],
        [0.1000, 0.1667, 0.1000, 0.1667],
        [0.1000, 0.1667, 0.1000, 0.1667],
        [0.1000, 0.1667, 0.1000, 0.1667],
        [0.1000, 0.1667, 0.1000, 0.1667],
        [0.3000, 0.1667, 0.3000, 0.1667],
        [0.3000, 0.1667, 0.3000, 0.1667],
        [0.3000, 0.1667, 0.3000, 0.1667],
        [0.3000, 0.1667, 0.3000, 0.1667],
        [0.3000, 0.1667, 0.3000, 0.1667],
        [0.5000, 0.1667, 0.5000, 0.1667],
        [0.5000, 0.1667, 0.5000, 0.1667],
        [0.5000, 0.1667, 0.5000, 0.1667],
        [0.5000, 0.1667, 0.5000, 0.1667],
        [0.5000, 0.1667, 0.5000, 0.1667],
        [0.7000, 0.1667, 0.7000, 0.1667],
        [0.7000, 0.1667, 0.7000, 0.1667],
        ......

每五行代表了一个像素的坐标
最后

output = out_grid+anchor_manipulations

像素的中点横坐标减去锚框的宽,中点的纵坐标减去锚框的高,便是锚框的左下坐标;
像素的中点横坐标加上锚框的宽,中点的纵坐标加上锚框的高,便是锚框的右上坐标。
output 的形状便是(boxes_per_pixel,4)
以上便是我对这个函数研究一周时间的个人理解,肯定包含了许多错误的地方,希望能帮助到大家的同时大家也能指出我的错误。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值