前言
沐神的代码看了很久、B站上的视频也刷了很多遍,感叹下自己的基础确实不怎么扎实,锚框部分的底层代码几乎是一行行撸过来的,因此对整个学习的部分做一个总结先放上沐神的b站和电子教程,以示敬意!
沐神的b站
锚框
以SSD(单发多框检测)为例,在输入图像上,我们会以图片的每一个像素为中心,生成多个缩放比(Size)和宽高比(Ratio)的边界框,这些边界框被称为锚框(anchor box)
锚框的生成
锚框的生成是以图像像素为中心,生成不同形状的锚框,对于每一张图片,均有宽度w和高度h,对宽度和高度进行缩放,决定了锚框的缩放比,而缩放后的锚框,通过更改宽高比,从而使得锚框对不同比例的物体有更好的适应能力。
如下列代码所示,size表示分别覆盖了20%——96%左右的图像区域;ratios表示每个缩放后的锚框的长和宽的比值;
#sizes——锚框在特征图上的覆盖范围
#ratios——锚框长和宽的比例组合
sizes = [[0.2,0.272],
[0.3,0.447],
[0.54,0.619],
[0.71,0.79],
[0.88,0.961]]
ratios = [[1,2,0.5]]*5
沐神在动手学深度学习的第二版中,还提到了一些锚框生成的条件,由于SSD目标检测的思想是通过每个像素来生成N个锚框,对于一张480320的图片而言,如果我们假定买个像素生成3个锚框,我们在一张图像上将会得到480320*3 = 460800个锚框,很容易计算量过大;因此在具体实践中,我们只考虑在相同缩放比下,不同宽高比的组合或者是相同宽高比下,不同缩放比的组合
假设图像的高度为h,宽度为w,缩放比的个数为n,宽高比的个数为m,则对于每一个像素来说,我们能够生成的锚框的数量为
n+m-1 既 缩放比个数+宽高比个数-1
具体代码实现如下,可以参考李沐动手学深度学习中的代码,本人仅根据个人理解和实际运行,做了抽取和注释
def multibox_prior(data, sizes, ratios):
"""生成以每个像素为中心具有不同形状的锚框"""
# 输入图像的高宽,输入数据数据格式为(批次,通道数,高,宽)
in_height, in_width = data.shape[-2:]
# 提供size和ratio的数量,s为缩放比,r为宽高比
num_sizes, num_ratios = len(sizes), len(ratios)
# 每个像素生成锚框数量的计算,该例为3+3-1
boxes_per_pixel = (num_sizes + num_ratios - 1)
# 生成缩放比和宽高比的tensor
size_tensor = torch.tensor(sizes)
ratio_tensor = torch.tensor(ratios)
# 为了将锚点移动到像素的中心,需要设置偏移量。
# 因为一个像素的的高为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轴上缩放步长
# 生成锚框的所有中心点
# 在高度和宽度中,对每个像素加上偏移量,并进行尺度缩放,在每个像素中心点生成锚框
center_h = (torch.arange(in_height) + offset_h) * steps_h
center_w = (torch.arange(in_width) + offset_w) * steps_w
shift_y, shift_x = torch.meshgrid(center_h, center_w)
shift_y, shift_x = shift_y.reshape(-1), shift_x.reshape(-1)
# 生成“boxes_per_pixel”个高和宽,
# 之后用于创建锚框的四角坐标(xmin,xmax,ymin,ymax)
# 锚框的宽度:w√sr;锚框的高度:hs/√r,
# 固定缩放比,调整宽高比进行锚框宽高组合;固定宽高比,调整缩放比进行锚框宽高组合;
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
# 每个中心点都将有“boxes_per_pixel”个锚框,
# 所以生成含所有锚框中心的网格,重复了“boxes_per_pixel”次
out_grid = torch.stack([shift_x, shift_y, shift_x, shift_y],
dim=1).repeat_interleave(boxes_per_pixel, dim=0)
output = out_grid + anchor_manipulations
return output.unsqueeze(0)
需要注意的是,在沐神的代码中,锚框的生成做了缩放,这样做的好处是,可以根据任意比例大小的图片的宽高,进行锚框的生成,也能够基于此进行预测,我们给定一组测试数据,对该方法进行验证
h,w = 561,728
test_data = torch.rand(size = (1,3,h,w))
test_sample = multibox_prior(test_data , sizes=[0.75, 0.5, 0.25], ratios=[1, 2, 0.5])
test_sample.shape
以下结果表明,每一张561*728分辨率的图片,根据宽高比和缩放比生成的锚框约有204万个,每一个锚框包含四个值,分别是以单个像素为中心缩放和偏移后的锚框的左上角的x,y以及右下角的x,y
torch.Size([1, 2042040, 4])
通过数组下标对单个像素生成的锚框进行访问,我们先通过reshape张量的形状,以便我们进行访问,输入以下代码后,可以得出该像素下缩放+偏移后锚框的坐标:
# 转变数据为(图像高、图像宽,-1,坐标点)的形式
boxes = test_sample.reshape(h, w, -1, 4)
# 访问(250,250)像素点生成的第一个锚框的坐标值
boxes[250, 250, 0, :]
tensor([0.06, 0.07, 0.63, 0.82])
若想还原在图像上的真实坐标点,可以采用如下方法,返回该像素点所生成的锚框在原始图片上的真实坐标信息,变量box_scale为原始图像的宽和高:
boxes = test_sample.reshape(h, w, -1, 4)
box_scale = torch.tensor((w,h,w,h))
boxes[250, 250, :, :]*box_scale
tensor([[ 40.13, 40.12, 460.88, 460.87],
[110.25, 110.25, 390.75, 390.75],
[180.38, 180.38, 320.62, 320.62],
[-47.02, 101.74, 548.02, 399.26],
[101.74, -47.02, 399.26, 548.02]]
锚框的绘制
在生成锚框后,我们可以结合沐神的d2l包,在样例图片上绘制我们得到的锚框,我们所选择的是图片上坐标为(250,250)的像素所生成的锚框:
def show_bboxes(axes, bboxes, labels=None, colors=None):
"""显示所有边界框"""
def _make_list(obj, default_values=None):
if obj is None:
obj = default_values
elif not isinstance(obj, (list, tuple)):
obj = [obj]
return obj
labels = _make_list(labels)
colors = _make_list(colors, ['b', 'g', 'r', 'm', 'c'])
for i, bbox in enumerate(bboxes):
color = colors[i % len(colors)]
rect = d2l.bbox_to_rect(bbox.detach().numpy(), color)
axes.add_patch(rect)
if labels and len(labels) > i:
text_color = 'k' if color == 'w' else 'w'
axes.text(rect.xy[0], rect.xy[1], labels[i],
va='center', ha='center', fontsize=9, color=text_color,
bbox=dict(facecolor=color, lw=0))
d2l.set_figsize()
bbox_scale = torch.tensor((w, h, w, h))
fig = d2l.plt.imshow(img)
show_bboxes(fig.axes, boxes[250, 250, :, :] * bbox_scale,
['s=0.75, r=1', 's=0.5, r=1', 's=0.25, r=1', 's=0.75, r=2',
's=0.75, r=0.5'])
版权声明:本文为CSDN博主「zyf_freashman」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/qq_32374357/article/details/122745861
暂无评论