anchor_generator.py
yolov6\assigners\anchor_generator.py
目录
1.所需的库和模块
import torch
2.def generate_anchors(feats, fpn_strides, grid_cell_size=5.0, grid_cell_offset=0.5, device='cpu', is_eval=False, mode='af'):
# 1. feats:一个列表,包含了模型中每个特征层(feature map)的尺寸。这些尺寸通常由模型的网络结构决定,用于确定在每个特征层上生成锚点的数量和尺寸。
# 2. fpn_strides:一个列表,包含了每个特征层的步幅(stride)。步幅决定了特征图上每个单元对应原始图像的尺寸,这对于将锚点从特征图尺度转换到原始图像尺度至关重要。
# 3. grid_cell_size(默认值为 5.0 ):一个浮点数,表示每个网格单元的尺寸。这个参数用于确定在特征图的每个单元上生成多少个锚点。
# 4. grid_cell_offset(默认值为 0.5 ):一个浮点数,表示网格单元的偏移量。这个参数通常用于确定锚点在特征图单元内的确切位置。
# 5. device(默认值为 'cpu' ):一个字符串,指定了锚点将被生成在哪个设备上,例如 'cpu' 或 'cuda' 。这决定了生成的锚点张量将存储在 CPU 还是 GPU 上。
# 6. is_eval(默认值为 False ):一个布尔值,指示当前是否处于评估(evaluation)模式。在评估模式下,某些参数的处理方式可能会有所不同。
# 7. mode(默认值为 'af' ):一个字符串,指定了生成锚点的模式。 'af' 代表 "anchor-free",表示 YOLOv6 使用无锚(anchor-free)范式。在这种模式下,可能会根据 feature map 的大小自动生成先验框,而不是使用预先定义的锚点集。
def generate_anchors(feats, fpn_strides, grid_cell_size=5.0, grid_cell_offset=0.5, device='cpu', is_eval=False, mode='af'): # grid_cell网格单元
# 根据特征生成锚点。
'''Generate anchors from features.'''
anchors = []
anchor_points = []
stride_tensor = []
num_anchors_list = []
assert feats is not None # 如果有特征图。 feats :指的是特征图(feature maps)。
if is_eval:
for i, stride in enumerate(fpn_strides):
_, _, h, w = feats[i].shape
# torch.arange(start=0, end, step=1, *, out=None, dtype=None, layout=torch.strided, device=None, requires_grad=False) -> Tensor
# 返回大小为(end-start)/strp的一维张量,其值介于区间 [ start , end ),以 step 为步长等间隔取值。
# dtype :期望的返回张量的数据类型。
# device :返回张量的期望设备。
# 1. torch.arange(end=w) :生成一个从0到 w-1 的一维张量,其中 w 是特征图的宽度。这个张量将被用作特征图上每个单元格的x坐标。
# 2. device :指定了生成的张量应该位于哪个设备上(例如,CPU或GPU)。这样可以确保计算可以在正确的设备上执行,从而提高性能。
# 3. + grid_cell_offset :将 grid_cell_offset 加到每个x坐标上。 grid_cell_offset 可能是一个偏移量,用于将网格点的坐标从网格单元的中心移动到网格单元的左上角或其他位置。这个偏移量通常用于确保预测的边界框坐标与实际的边界框坐标对齐。
# 最终, shift_x 将包含特征图上每个单元格的x坐标,这些坐标可以用于计算边界框的中心点坐标。
# 在目标检测模型中,通常会对特征图的每个维度(宽度和高度)都生成这样的坐标,然后使用 torch.meshgrid 函数将它们组合成一个二维网格。
shift_x = torch.arange(end=w, device=device) + grid_cell_offset # shift_x 将包含特征图上每个单元格的x坐标,这些坐标可以用于计算边界框的中心点坐标。
shift_y = torch.arange(end=h, device=device) + grid_cell_offset # 同上, shift_y 将包含特征图上每个单元格的y坐标,这些坐标可以用于计算边界框的中心点坐标。
# torch.meshgrid(*tensors, indexing='xy') -> Sequence[Tensor]
# torch.meshgrid 函数用于创建一个坐标网格。这个函数的主要用途是在多维空间中生成一个坐标矩阵,这在图像处理、机器学习模型的输入数据准备等方面非常有用。
# tensors :一个或多个张量,它们将被用来生成网格。
# indexing :指定索引的顺序,可以是'xy'(笛卡尔坐标系,默认值)或'ij'(矩阵索引)。
# 输出 :两个 tensor 数据(两个tensor的行数为第一个输入张量的元素个数,列数为第二个输入张量的元素个数)。
# 1. 参数 indexing='‘ij’'时:
# 当两个输入tensor数据类型不同或维度不是一维时会报错。其中第一个输出张量填充第一个输入张量中的元素,各行元素相同;第二个输出张量填充第二个输入张量中的元素,各列元素相同。
# 2. 参数 indexing='‘xy’'时:
# 则输出两个 tensor 的第一个维度对应于第二个输入的元素个数,第二个维度对应于第一个输入的元素个数。
# 其中第一个输出张量填充第一个输入张量中的元素,各列元素相同;第二个输出张量填充第二个输入张量中的元素,各行元素相同,与 indexing='‘ij’' 输出结果情况相反。
# 当使用 torch.meshgrid 函数来组合 shift_x 和 shift_y 时,你实际上是在创建一个坐标网格,这个网格可以用来索引特征图上的每个位置。这个网格对于计算边界框的预测是非常有用的,因为它允许模型为每个单元格生成多个边界框预测。
# 在YOLOv6中,这些坐标可能还会加上一个偏移量( grid_cell_offset ),以确保边界框的中心点与实际目标的中心对齐。这个偏移量通常是单元格大小的一半,因为特征图的坐标通常是从每个单元格的左上角开始计算的。
shift_y, shift_x = torch.meshgrid(shift_y, shift_x)
# torch.stack(tensors, dim=0, out=None) → Tensor
# torch.stack 函数用于将一系列张量沿着一个新的维度连接起来。与 torch.cat (concatenate)不同, torch.stack 会创建一个新的维度来存放输入张量,而不是在已有的维度上进行连接。
# tensors :一个序列(如元组或列表)的张量,它们需要有相同的形状。
# dim :沿着哪个维度堆叠张量。默认为0,表示在最前面添加一个新的维度。
# out :一个可选的张量,用于存储输出结果。
# 返回值:
# 返回一个新的张量,它是输入张量沿着 dim 维度堆叠的结果。
anchor_point = torch.stack(
[shift_x, shift_y], axis=-1).to(torch.float)
# 这一行检查一个名为 mode 的变量是否等于字符串 'af' 。这可能是一个标志,用来指示当前处理的是与“anchor free”(无锚点)相对的“anchor based”(基于锚点)的方法。
if mode == 'af': # anchor-free 无锚框
# anchor_points.append(anchor_point.reshape([-1, 2])):
# 这里, anchor_point 是一个张量,它包含了在当前特征图上的锚点坐标。 reshape([-1, 2]) 将 anchor_point 张量重塑为一个二维张量,其中 -1 表示让PyTorch自动计算这一维度的大小,以便保持总元素数量不变。 2 表示每个锚点有两个坐标(通常是x和y坐标)。
# 然后,这个重塑后的张量被添加到 anchor_points 列表中。
anchor_points.append(anchor_point.reshape([-1, 2]))
# torch.full(size, fill_value, *, out=None, dtype=None, layout=torch.strided, device=None, requires_grad=False) → Tensor
# 返回创建 size 大小的维度,里面元素全部填充为 fill_value 。
# 这里, torch.full 创建了一个填充有特定值 stride 的新张量。 h * w 计算了特征图上的单元格数量, stride 是当前特征图的步长,表示特征图上每个单元格对应原始图像上的大小。
# 这个新创建的张量的形状是 (h * w, 1) ,其中 h 和 w 分别是特征图的高度和宽度。 dtype=torch.float 指定了张量的数据类型为浮点数, device 参数确保张量被创建在正确的设备上(例如CPU或GPU)。然后,这个张量被添加到 stride_tensor 列表中。
# 这段代码是在构建一个目标检测模型的特征图上锚点和步长的表示,这将用于后续的边界框(bounding box)预测。
stride_tensor.append(
torch.full(
(h * w, 1), stride, dtype=torch.float, device=device))
elif mode == 'ab': # anchor-based 基于锚框
anchor_points.append(anchor_point.reshape([-1, 2]).repeat(3,1)) # 如果基于锚框,则需要将锚框重复3遍。
stride_tensor.append(
torch.full(
(h * w, 1), stride, dtype=torch.float, device=device).repeat(3,1))
# anchor_points 是一个包含多个张量的列表,每个张量都包含了特定特征图层上所有锚点(anchor points)的坐标。这些锚点用于定义在特征图的每个位置可能存在的边界框的中心。
anchor_points = torch.cat(anchor_points)
stride_tensor = torch.cat(stride_tensor)
# 2. anchor_points : 这是一个张量,包含了所有特征图层上每个单元格的中心点坐标。这些坐标用于确定锚点的中心位置,通常用于anchor-free的检测方法中。每个点由两个值组成: (x_center, y_center) 。
# 4. stride_tensor : 这是一个张量,包含了每个特征图层的步长(stride)。步长定义了特征图上每个单元格对应于输入图像的实际像素大小。这个信息对于将特征图坐标转换为原始图像坐标至关重要。
return anchor_points, stride_tensor
else: # 没有特征图。
for i, stride in enumerate(fpn_strides):
_, _, h, w = feats[i].shape
cell_half_size = grid_cell_size * stride * 0.5
# 1. torch.arange(end=w, device=device) : 这个函数生成一个从0到 w-1 的一维张量,其中 w 是特征图的宽度。 device 参数确保张量被创建在正确的设备上(例如CPU或GPU)。
# 2. + grid_cell_offset : grid_cell_offset 是一个偏移量,用于将网格点的坐标从网格单元的中心移动到网格单元的左上角或其他位置。 这个偏移量通常用于确保预测的边界框坐标与实际的边界框坐标对齐。
# 3. * stride : stride 是特征图的步长,表示特征图上每个单元格对应原始图像上的大小。 将偏移后的坐标乘以步长,将特征图的坐标转换为原始图像的坐标。
# 最终, shift_x 将包含特征图上每个单元格的中心点的x坐标,这些坐标可以用于计算边界框的中心点坐标。在目标检测模型中,通常会对特征图的每个维度(宽度和高度)都生成这样的坐标,然后使用 torch.meshgrid 函数将它们组合成一个二维网格。
shift_x = (torch.arange(end=w, device=device) + grid_cell_offset) * stride # shift_x 将包含特征图上每个单元格的中心点的x坐标。
shift_y = (torch.arange(end=h, device=device) + grid_cell_offset) * stride # 同理,shift_y 将包含特征图上每个单元格的中心点的y坐标。
# 当使用 torch.meshgrid 函数来组合 shift_x 和 shift_y 时,你实际上是在创建一个坐标网格,这个网格可以用来索引特征图上的每个位置。这个网格对于计算边界框的预测是非常有用的,因为它允许模型为每个单元格生成多个边界框预测。
shift_y, shift_x = torch.meshgrid(shift_y, shift_x)
anchor = torch.stack(
[
shift_x - cell_half_size, shift_y - cell_half_size,
shift_x + cell_half_size, shift_y + cell_half_size
],
axis=-1).clone().to(feats[0].dtype)
# 生成锚框。
anchor_point = torch.stack(
[shift_x, shift_y], axis=-1).clone().to(feats[0].dtype)
if mode == 'af': # anchor-free 无锚框
# 在"anchor free"模式下,每个锚点 anchor 被重塑为 [-1, 4] 的形状,其中 -1 表示自动计算行数以保持总元素数量不变, 4 表示每个锚点有四个坐标(通常是边界框的x、y、宽度和高度)。
# 同样,每个锚点位置 anchor_point 被重塑为 [-1, 2] 的形状,其中 2 表示每个锚点位置有两个坐标(通常是x和y坐标)。
# 些重塑后的张量被添加到 anchors 和 anchor_points 列表中。
anchors.append(anchor.reshape([-1, 4]))
anchor_points.append(anchor_point.reshape([-1, 2]))
elif mode == 'ab': # anchor-based 基于锚框 "anchor based"(AB)
# 在"anchor based"模式下,每个锚点 anchor 同样被重塑为 [-1, 4] 的形状,但随后使用 .repeat(3,1) 在第一个维度(锚点数量)上重复3次。这意味着每个锚点被复制3次,通常是为了适应不同尺度或比例的边界框。
# 同样,每个锚点位置 anchor_point 被重塑为 [-1, 2] 的形状,并使用 .repeat(3,1) 在第一个维度上重复3次。
# 这些重复后的张量被添加到 anchors 和 anchor_points 列表中。
anchors.append(anchor.reshape([-1, 4]).repeat(3,1)) # 如果基于锚框,则需要将锚点重复3遍。
anchor_points.append(anchor_point.reshape([-1, 2]).repeat(3,1))
num_anchors_list.append(len(anchors[-1]))
stride_tensor.append(
torch.full(
[num_anchors_list[-1], 1], stride, dtype=feats[0].dtype))
anchors = torch.cat(anchors)
anchor_points = torch.cat(anchor_points).to(device)
stride_tensor = torch.cat(stride_tensor).to(device)
# 1. anchors : 这是一个张量,包含了所有特征图层上每个单元格的锚点的宽度和高度。在YOLOv6中,由于采用了anchor-free的设计,这些锚点可能是动态生成的,而不是预先定义的。
# 每个锚点通常由四个值组成: (x_center, y_center, width, height) ,其中 (x_center, y_center) 是锚点的中心坐标, width 和 height 是锚点的宽度和高度。
# 2. anchor_points : 这是一个张量,包含了所有特征图层上每个单元格的中心点坐标。这些坐标用于确定锚点的中心位置,通常用于anchor-free的检测方法中。每个点由两个值组成: (x_center, y_center) 。
# 3. num_anchors_list : 这是一个列表,包含了每个特征图层上每个单元格的锚点数量。在多尺度特征图层中,不同的层可能有不同的锚点数量,这个列表记录了这些信息。
# 4. stride_tensor : 这是一个张量,包含了每个特征图层的步长(stride)。步长定义了特征图上每个单元格对应于输入图像的实际像素大小。这个信息对于将特征图坐标转换为原始图像坐标至关重要。
return anchors, anchor_points, num_anchors_list, stride_tensor
标签:YOLOv6,4.0,generator,shift,torch,张量,坐标,anchor,锚点
From: https://blog.csdn.net/m0_58169876/article/details/143315729