目录
二.锚框
2.1 定义
2.2 实现过程
1.锚框数量
2.实现思路
3.实现
目标检测算法,顾名思义我们需要在输入图像上检测是否存在我们关注的目标。因此我们需要在输入图像上进行大量的采样,然后进行判断是否存在目标,并调整区域边界从而更准确的预测目标的真实边框。故在图像上的大量采样所得到的不同缩放比和宽高比的边界框就称为锚框。
图示:
在李沐老师的课程中,讲解的是根据每个像素中心位置生成不同边长比 (其实应该是面积比,但是根据老师的公式推到下来得到的是边长比) 和宽高比的锚框。
输入:s = (0.3, 0.5, 0.75) r = (1, 0.5, 2),其中s为边长比, r为宽高比
如果根据每一个边长比和宽高比配对生成锚框那么每个像素应该生成 len(s) * len(r) 个锚框,如果图片的像素为480 * 480 那么就会生成480 * 480 * 3 * 3 = 2073600 这样计算量太大了 ,因此我们在锚框的配对上采取如下策略:
这样每个像素点生成 ( len(s) + len(r) - 1)的锚框,对于整个输入图像产生wh(len(s) + len(r) - 1)个锚框。
1)生成中心点坐标位置矩阵
2)生成锚框对于每个中心点的宽高矩阵
3)两者进行相加,得到锚框矩阵
第一步:生成我们需要的辅助变量
def multibox_prior(data, sizes, ratios):
# in_height, in_width 为输入的高宽
in_height, in_width = data.shape[-2:]
# num_sizes, num_ratios 为输入的边长比和宽高比数量
device, num_sizes, num_ratios = data.device, len(sizes), len(ratios)
# boxes_per_pixel 为每个像素点生成的锚框数量
boxes_per_pixel = (num_sizes + num_ratios - 1)
size_tensor = torch.tensor(sizes, device=device)
ratio_tensor = torch.tensor(ratios, device=device)
# 为了将锚点移动到像素的中心,需要设置偏移量。
# 因为一个像素的的高为1且宽为1,我们选择偏移我们的中心0.5
offset_h, offset_w = 0.5, 0.5
steps_h = 1.0 / in_height # 在y轴上缩放步长
steps_w = 1.0 / in_width # 在x轴上缩放步长
第二步:生成坐标中心矩阵
由如下代码生成每个像素点中心位置的x坐标 和 y坐标 因为共有 h * w 个像素点,因此最终得到的一维tnesor的长度为 h * w, 如下代码存在一个尺度问题,也就是x匹配y, 还是y匹配x的选择问题,如下代码中选择的是x匹配y。
eg : h = 5, w = 5 X = (x1, x2, x3, x4, x5), Y = (y1, y2, y3, y4, y5)
shift_x = (x1, x2, x3, x4, x5) * 5 , shift_y = (yi, yi, yi, yi, yi) * 5 i = [1, 5]
# 生成锚框的所有中心点
# center_h, center_w 为缩放后的y轴,x轴的中心点位置
center_h = (torch.arange(in_height, device=device) + offset_h) * steps_h
center_w = (torch.arange(in_width, device=device) + offset_w) * steps_w
# shift_y, shift_x 为 将一维的tensor转换为 h * w 的二维tensor
# 其中shift_y 的每列为center_y shift_x 的每行为center_w
shift_y, shift_x = torch.meshgrid(center_h, center_w)
# 将其拉成一维tensor shape = [in_heifht * in_width]
shift_y, shift_x = shift_y.reshape(-1), shift_x.reshape(-1)
将得到的shift_y, shift_x 进行组合得到每个像素点的坐标矩阵
out_grid = torch.stack([shift_x, shift_y, shift_x, shift_y],
dim=1).repeat_interleave(boxes_per_pixel, dim=0)
其中的难点为 tensor.repeat() 和 tensor.repeat_interleave()的区别,这里个人理解是
tensor.repeat(*size) :将tensor看作一个元素,生成*size的尺寸
tensor.repeat_interleave() : 对于输入,在指定维度上进行重复
eg:
第三步:创建锚框宽高矩阵
在李沐老师的课程中选择的锚框的宽高为 ws√r 和 hs / √r ,
但在实际的操作上, 为了保持锚框的宽高比为r,他在宽上 *( h / w) , 这样虽然锚框的宽高比为r ,但是面积比就不再是s^2,但是毕竟只是一个锚框是个人指定的大小,影响不大。因此在代码中我们选择使用李沐老师的锚框高宽,但下面对于实际的高宽进行一个推导。
锚框的高宽满足两个公式 : w'h' = WH * s^2 , w' / h' = r
解得: w' = √(WHr) * s h' = √(WH / r) * s
归一化后 w0' = √(Hr / W) * s h0' = √(W / rH) * s
当H == W时, 李沐老师的公式完全正确。
当H != W时,面积比会发生偏差h/w,如果图片的高宽比相差不是很大,结果的变动不会很大。
"""生成w, h的一维tensor"""
w = torch.cat((size_tensor * torch.sqrt(ratio_tensor[0]),
sizes[0] * torch.sqrt(ratio_tensor[1:])))\
* in_height / in_width # 处理矩形输入
h = torch.cat((size_tensor / torch.sqrt(ratio_tensor[0]),
sizes[0] / torch.sqrt(ratio_tensor[1:])))
# 除以2来获得半高和半宽
anchor_manipulations = torch.stack((-w, -h, w, h)).T.repeat(
in_height * in_width, 1) / 2
第四步:相加返回
output = out_grid + anchor_manipulations
return output.unsqueeze(0)