代码解读 FCOS网络(基于mmrotate框架)

news2024/12/27 10:49:24

文章目录

    • 1. rotated_fcos_head.py
      • 1.1 __init__函数
      • 1.2 forward_single 函数
        • 1.2.1 父类的forward_single 函数(anchor_free_head.py)
        • 1.2.2 _init_layers 函数
      • 1.3 loss 函数
        • 1.3.1 get_targets 函数
          • 1.3.1.1 _get_target_single 函数
        • 1.3.2 prior_generator.grid_priors函数
          • 1.3.2.1 single_level_grid_priors函数

1. rotated_fcos_head.py

1.1 __init__函数

这个是FCSO的初始化类

    def __init__(self,
                 num_classes,
                 in_channels,
                 regress_ranges=((-1, 64), (64, 128), (128, 256), (256, 512),
                                 (512, INF)),
                 center_sampling=False,
                 center_sample_radius=1.5,
                 norm_on_bbox=False,
                 centerness_on_reg=False,
                 separate_angle=False,
                 scale_angle=True,
                 h_bbox_coder=dict(type='DistancePointBBoxCoder'),
                 loss_cls=dict(
                     type='FocalLoss',
                     use_sigmoid=True,
                     gamma=2.0,
                     alpha=0.25,
                     loss_weight=1.0),
                 loss_bbox=dict(type='IoULoss', loss_weight=1.0),
                 loss_angle=dict(type='L1Loss', loss_weight=1.0),
                 loss_centerness=dict(
                     type='CrossEntropyLoss',
                     use_sigmoid=True,
                     loss_weight=1.0),
                 norm_cfg=dict(type='GN', num_groups=32, requires_grad=True),
                 init_cfg=dict(
                     type='Normal',
                     layer='Conv2d',
                     std=0.01,
                     override=dict(
                         type='Normal',
                         name='conv_cls',
                         std=0.01,
                         bias_prob=0.01)),
                 **kwargs):
        self.regress_ranges = regress_ranges
        self.center_sampling = center_sampling
        self.center_sample_radius = center_sample_radius
        self.norm_on_bbox = norm_on_bbox
        self.centerness_on_reg = centerness_on_reg
        self.separate_angle = separate_angle
        self.is_scale_angle = scale_angle
        super().__init__(
            num_classes,
            in_channels,
            loss_cls=loss_cls,
            loss_bbox=loss_bbox,
            norm_cfg=norm_cfg,
            init_cfg=init_cfg,
            **kwargs)
        self.loss_centerness = build_loss(loss_centerness)
        if self.separate_angle:
            self.loss_angle = build_loss(loss_angle)
            self.h_bbox_coder = build_bbox_coder(h_bbox_coder)
num_classes(int):目标类别的数量。
in_channels(int):输入特征图的通道数。
regress_ranges(tuple of tuples):定义不同级别的回归范围。每个元组表示一个回归范围的上下限。
center_sampling(bool):是否在计算目标时对中心进行采样。
center_sample_radius(float):中心采样的半径。
norm_on_bbox(bool):是否在归一化回归目标时考虑缩放因子。
centerness_on_reg(bool):是否将中心度应用于回归。
separate_angle(bool):是否在预测目标角度时进行分离。
scale_angle(bool):是否缩放角度预测。
h_bbox_coder(dict):边界框编码器的配置。
loss_cls(dict):分类损失函数的配置。
loss_bbox(dict):回归损失函数的配置。
loss_angle(dict):角度损失函数的配置(如果 separate_angle 为真)。
loss_centerness(dict):中心度损失函数的配置。
norm_cfg(dict):归一化的配置。
init_cfg(dict):初始化的配置。
kwargs:额外的关键字参数。

1.2 forward_single 函数

# 通过调用父类的 'forward_single' 方法来获取类别得分,边界框预测和分类回归特征
cls_score, bbox_pred, cls_feat, reg_feat = super().forward_single(x)   详见1.2.1

在这里插入图片描述

# 检查是否使用中心度计算在回归特征或分类特征上
 if self.centerness_on_reg:
     centerness = self.conv_centerness(reg_feat)	默认
 else:
     centerness = self.conv_centerness(cls_feat)    详见1.2.2

在这里插入图片描述

 # 使用提供的 'scale' 模块对不同级别的边界框预测进行调整并转换为浮点数
 bbox_pred = scale(bbox_pred).float()
if self.norm_on_bbox:
        # 如果 self.norm_on_bbox 为 True,则对 bbox_pred 进行调整
        	将所有小于0的值设置为0。这个操作确保边界框的坐标预测不会出现负值
        bbox_pred = bbox_pred.clamp(min=0)
        
        # 如果不处于训练模式下,根据步长对 bbox_pred 进行调整
        if not self.training:
            bbox_pred *= stride
    else:
        # 如果 self.norm_on_bbox 为 False,则对 bbox_pred 进行指数函数处理
        bbox_pred = bbox_pred.exp()

在这里插入图片描述

# 使用 'conv_angle' 层计算角度的预测
    angle_pred = self.conv_angle(reg_feat)		详见1.2.2
    
    if self.is_scale_angle:						详见1.2.2
        # 如果 self.is_scale_angle 为 True,则对角度预测进行缩放处理
        angle_pred = self.scale_angle(angle_pred).float()
    
     # 返回类别得分,边界框预测,角度预测和中心度预测
    return cls_score, bbox_pred, angle_pred, centerness

在这里插入图片描述

1.2.1 父类的forward_single 函数(anchor_free_head.py)

在这里插入图片描述

def forward_single(self, x):
    """处理单个尺度级别的特征图的前向传播。

    Args:
        x (Tensor): 指定尺度级别的FPN特征图。

    Returns:
        tuple: 每个类别的得分,边界框预测,经过分类和回归卷积层后的特征图,
        某些模型(例如FCOS)需要这些特征图。
    """
    # 将输入特征图用于分类和回归特征
    cls_feat = x
    reg_feat = x

    # 通过遍历分类卷积层对分类特征进行前向传播
    for cls_layer in self.cls_convs:
        cls_feat = cls_layer(cls_feat)
    # 使用 'conv_cls' 层计算类别得分
    cls_score = self.conv_cls(cls_feat)

    # 通过遍历回归卷积层对回归特征进行前向传播
    for reg_layer in self.reg_convs:
        reg_feat = reg_layer(reg_feat)
    # 使用 'conv_reg' 层计算边界框预测
    bbox_pred = self.conv_reg(reg_feat)
    
    # 返回类别得分,边界框预测,分类特征和回归特征
    return cls_score, bbox_pred, cls_feat, reg_feat

在这里插入图片描述

1.2.2 _init_layers 函数

def _init_layers(self):
    """Initialize layers of the head."""
    super()._init_layers()
    self.conv_centerness = nn.Conv2d(self.feat_channels, 1, 3, padding=1)
    self.conv_angle = nn.Conv2d(self.feat_channels, 1, 3, padding=1)
    self.scales = nn.ModuleList([Scale(1.0) for _ in self.strides])
    if self.is_scale_angle:
        self.scale_angle = Scale(1.0)

1.3 loss 函数

def loss(self,
             cls_scores,
             bbox_preds,
             angle_preds,
             centernesses,
             gt_bboxes,
             gt_labels,
             img_metas,
             gt_bboxes_ignore=None):

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

assert len(cls_scores) == len(bbox_preds) \
           == len(angle_preds) == len(centernesses)
featmap_sizes = [featmap.size()[-2:] for featmap in cls_scores]

使用断言来确保所有的预测结果张量(cls_scores、bbox_preds、angle_preds 和 centernesses)具有相同的长度
算了每个特征层的空间尺寸(高度和宽度)。featmap_sizes 是一个列表,其中包含了每个特征层的尺寸。

在这里插入图片描述

all_level_points = self.prior_generator.grid_priors(
    featmap_sizes,
    dtype=bbox_preds[0].dtype,
    device=bbox_preds[0].device)

使用 self.prior_generator 的 grid_priors 方法,根据特征层的尺寸生成一组先验框的坐标

在这里插入图片描述

labels, bbox_targets, angle_targets = self.get_targets(
    all_level_points, gt_bboxes, gt_labels)

调用了 self.get_targets 方法,根据生成的先验框坐标、真实边界框和真实标签来计算分类、边界框和角度的目标。

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

num_imgs = cls_scores[0].size(0)
取了输入张量中的图片数量

flatten_cls_scores = [
    cls_score.permute(0, 2, 3, 1).reshape(-1, self.cls_out_channels)
    for cls_score in cls_scores
]
flatten_bbox_preds = [
    bbox_pred.permute(0, 2, 3, 1).reshape(-1, 4)
    for bbox_pred in bbox_preds
]
flatten_angle_preds = [
    angle_pred.permute(0, 2, 3, 1).reshape(-1, 1)
    for angle_pred in angle_preds
]
flatten_centerness = [
    centerness.permute(0, 2, 3, 1).reshape(-1)
    for centerness in centernesses
]
将每个特征图的分类得分、边界框预测、角度预测和中心度预测展平

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

flatten_cls_scores = torch.cat(flatten_cls_scores)
flatten_bbox_preds = torch.cat(flatten_bbox_preds)
flatten_angle_preds = torch.cat(flatten_angle_preds)
flatten_centerness = torch.cat(flatten_centerness)

将前面得到的展平后的张量列表通过 torch.cat 操作进行连接,得到完整的展平后的张量。

在这里插入图片描述

flatten_labels = torch.cat(labels)
flatten_bbox_targets = torch.cat(bbox_targets)
flatten_angle_targets = torch.cat(angle_targets)

将分类标签、边界框目标和角度目标也展平为一维张量
flatten_points = torch.cat(
    [points.repeat(num_imgs, 1) for points in all_level_points])

将每个特征层上的先验框坐标 all_level_points 重复 num_imgs 次,从而使得坐标与之前展平的结果 flatten_bbox_preds 对齐
bg_class_ind = self.num_classes
将背景类的索引设置为 num_classes

pos_inds = ((flatten_labels >= 0)
            & (flatten_labels < bg_class_ind)).nonzero().reshape(-1)
用于获取正样本的索引

num_pos = torch.tensor(
    len(pos_inds), dtype=torch.float, device=bbox_preds[0].device)
num_pos = max(reduce_mean(num_pos), 1.0)
计算了正样本的数量,并且将其转换为张量 num_pos,后使用 reduce_mean 函数来计算正样本数量的平均值,并使用 max 函数确保这个平均值至少为1.0

在这里插入图片描述

loss_cls = self.loss_cls(
    flatten_cls_scores, flatten_labels, avg_factor=num_pos)
使用分类损失函数 self.loss_cls 来计算分类损失

在这里插入图片描述

pos_bbox_preds = flatten_bbox_preds[pos_inds]
pos_angle_preds = flatten_angle_preds[pos_inds]
pos_centerness = flatten_centerness[pos_inds]
pos_bbox_targets = flatten_bbox_targets[pos_inds]
pos_angle_targets = flatten_angle_targets[pos_inds]
pos_centerness_targets = self.centerness_target(pos_bbox_targets)

通过索引 pos_inds 从之前展平的张量中提取了正样本对应的
	边界框预测、角度预测、中心度预测、边界框目标、角度目标和中心度目标

centerness_denorm = max(
    reduce_mean(pos_centerness_targets.sum().detach()), 1e-6)
计算了正样本的中心度目标之和,并通过 reduce_mean 函数计算平均值。然后使用 max 函数确保分母至少为1e-6

在这里插入图片描述
在这里插入图片描述

if len(pos_inds) > 0:
    # 如果存在正样本
    pos_points = flatten_points[pos_inds]
    # 从所有点坐标中提取正样本的点坐标

    if self.separate_angle:
        # 如果模型设置了分离角度
        bbox_coder = self.h_bbox_coder
        # 使用分离角度的边界框编码器
    else:
        bbox_coder = self.bbox_coder
        # 否则使用常规边界框编码器

        pos_bbox_preds = torch.cat([pos_bbox_preds, pos_angle_preds],
                                   dim=-1)
        pos_bbox_targets = torch.cat(
            [pos_bbox_targets, pos_angle_targets], dim=-1)
        # 如果不分离角度,则将边界框预测和角度预测在最后一个维度上连接,
        # 并将边界框目标和角度目标连接起来

在这里插入图片描述
在这里插入图片描述

    pos_decoded_bbox_preds = bbox_coder.decode(pos_points,
                                               pos_bbox_preds)
    pos_decoded_target_preds = bbox_coder.decode(
        pos_points, pos_bbox_targets)
    # 使用边界框编码器解码正样本的边界框预测和目标

在这里插入图片描述

    loss_bbox = self.loss_bbox(
        pos_decoded_bbox_preds,
        pos_decoded_target_preds,
        weight=pos_centerness_targets,
        avg_factor=centerness_denorm)
    # 计算边界框损失,使用解码后的边界框预测和目标值,
    # 并根据中心度目标值进行加权平均

    if self.separate_angle:
        loss_angle = self.loss_angle(
            pos_angle_preds, pos_angle_targets, avg_factor=num_pos)
        # 如果分离角度,计算角度损失
    loss_centerness = self.loss_centerness(
        pos_centerness, pos_centerness_targets, avg_factor=num_pos)
    # 计算中心度损失

在这里插入图片描述

else:
    # 如果没有正样本
    loss_bbox = pos_bbox_preds.sum()
    # 边界框损失设置为边界框预测值的和
    loss_centerness = pos_centerness.sum()
    # 中心度损失设置为中心度预测值的和
    if self.separate_angle:
        loss_angle = pos_angle_preds.sum()
        # 如果分离角度,角度损失设置为角度预测值的和
if self.separate_angle:
    return dict(
        loss_cls=loss_cls,
        loss_bbox=loss_bbox,
        loss_angle=loss_angle,
        loss_centerness=loss_centerness)
else:
    return dict(
        loss_cls=loss_cls,
        loss_bbox=loss_bbox,
        loss_centerness=loss_centerness)

1.3.1 get_targets 函数

def get_targets(self, points, gt_bboxes_list, gt_labels_list):
    """Compute regression, classification and centerness targets for points
    in multiple images.

    Args:
        points (list[Tensor]): Points of each fpn level, each has shape
            (num_points, 2).
        gt_bboxes_list (list[Tensor]): Ground truth bboxes of each image,
            each has shape (num_gt, 4).
        gt_labels_list (list[Tensor]): Ground truth labels of each box,
            each has shape (num_gt,).
    Returns:
        tuple:
            concat_lvl_labels (list[Tensor]): Labels of each level. \
            concat_lvl_bbox_targets (list[Tensor]): BBox targets of each \
                level.
            concat_lvl_angle_targets (list[Tensor]): Angle targets of \
                each level.
        """

在这里插入图片描述

assert len(points) == len(self.regress_ranges)
num_levels = len(points)

# 将回归范围扩展以与点对齐,points[i].new_tensor(self.regress_ranges[i]) 创建一个与 points[i] 具有相同数据类型和设备的新张量,其值为 self.regress_ranges[i]
expanded_regress_ranges = [
    points[i].new_tensor(self.regress_ranges[i])[None].expand_as(
        points[i]) for i in range(num_levels)
]

在这里插入图片描述

# 连接所有级别的点和回归范围
concat_regress_ranges = torch.cat(expanded_regress_ranges, dim=0)
concat_points = torch.cat(points, dim=0)

#num_points 列表中存储了每个级别中的点的数量
num_points = [center.size(0) for center in points]

在这里插入图片描述
在这里插入图片描述

labels_list, bbox_targets_list, angle_targets_list = multi_apply(   详见1.3.1.1
            self._get_target_single,
            gt_bboxes_list,
            gt_labels_list,
            points=concat_points,
            regress_ranges=concat_regress_ranges,
            num_points_per_lvl=num_points)

将 _get_target_single 方法应用到多个图像上,以计算每个图像中的回归、分类和角度目标
gt_bboxes_list 是一个列表,每个元素是一个张量,表示一个图像中的真实边界框。
gt_labels_list 是一个列表,每个元素是一个张量,表示一个图像中的真实类别标签。
concat_points 是一个张量,表示连接所有级别的点。
concat_regress_ranges 是一个张量,表示连接所有级别的回归范围。
num_points_per_lvl 是一个列表,每个元素表示每个级别中点的数量。

在这里插入图片描述
在这里插入图片描述

# 将目标分割为每个图像的每个级别
labels_list = [labels.split(num_points, 0) for labels in labels_list]
bbox_targets_list = [
    bbox_targets.split(num_points, 0)
    for bbox_targets in bbox_targets_list
]
angle_targets_list = [
    angle_targets.split(num_points, 0)
    for angle_targets in angle_targets_list
]

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

# 连接每个级别中每个图像的目标
concat_lvl_labels = []
concat_lvl_bbox_targets = []
concat_lvl_angle_targets = []
for i in range(num_levels):
    concat_lvl_labels.append(
        torch.cat([labels[i] for labels in labels_list]))
    bbox_targets = torch.cat(
        [bbox_targets[i] for bbox_targets in bbox_targets_list])
    angle_targets = torch.cat(
        [angle_targets[i] for angle_targets in angle_targets_list])
    if self.norm_on_bbox:
        bbox_targets = bbox_targets / self.strides[i]
    concat_lvl_bbox_targets.append(bbox_targets)
    concat_lvl_angle_targets.append(angle_targets)

# 返回包含连接后的每个级别的分类标签、回归目标和角度目标的元组
return (concat_lvl_labels, concat_lvl_bbox_targets, concat_lvl_angle_targets)

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

1.3.1.1 _get_target_single 函数
def _get_target_single(self, gt_bboxes, gt_labels, points, regress_ranges,
                       num_points_per_lvl):
    """Compute regression, classification and angle targets for a single
    image."""

在这里插入图片描述

# 获取当前级别的点数和ground truth框数
num_points = points.size(0)
num_gts = gt_labels.size(0)

# 如果没有ground truth框,返回全零的分类标签、回归目标和角度目标
if num_gts == 0:
    return gt_labels.new_full((num_points,), self.num_classes), \
           gt_bboxes.new_zeros((num_points, 4)), \
           gt_bboxes.new_zeros((num_points, 1))

在这里插入图片描述

# 计算ground truth框的面积
areas = gt_bboxes[:, 2] * gt_bboxes[:, 3]

# 将面积扩展为和点数一致的形状
areas = areas[None].repeat(num_points, 1)

在这里插入图片描述
在这里插入图片描述

# 扩展回归范围,使其和点数、ground truth框数一致
regress_ranges = regress_ranges[:, None, :].expand(
    num_points, num_gts, 2)

# 扩展points,使其和点数、ground truth框数一致
points = points[:, None, :].expand(num_points, num_gts, 2)

# 扩展gt_bboxes,使其和点数、ground truth框数一致
gt_bboxes = gt_bboxes[None].expand(num_points, num_gts, 5)

在这里插入图片描述
在这里插入图片描述

# 分别获取ground truth框的中心点、宽高和角度信息
gt_ctr, gt_wh, gt_angle = torch.split(gt_bboxes, [2, 2, 1], dim=2)

# 计算cos和sin角度值
cos_angle, sin_angle = torch.cos(gt_angle), torch.sin(gt_angle)

# 构建旋转矩阵
rot_matrix = torch.cat([cos_angle, sin_angle, -sin_angle, cos_angle],
                       dim=-1).reshape(num_points, num_gts, 2, 2)

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

# 计算点到中心点的偏移,并在旋转矩阵的作用下,得到相对于中心点的偏移
offset = points - gt_ctr
offset = torch.matmul(rot_matrix, offset[..., None])
offset = offset.squeeze(-1)

在这里插入图片描述

# 获取宽和高信息
w, h = gt_wh[..., 0], gt_wh[..., 1]

# 获取x和y方向上的偏移
offset_x, offset_y = offset[..., 0], offset[..., 1]

# 计算回归目标的左、右、上、下边界值
left = w / 2 + offset_x
right = w / 2 - offset_x
top = h / 2 + offset_y
bottom = h / 2 - offset_y

# 将左、右、上、下边界值拼接成一个张量
bbox_targets = torch.stack((left, top, right, bottom), -1)

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述在这里插入图片描述

# 判断每个点是否在ground truth框内部
inside_gt_bbox_mask = bbox_targets.min(-1)[0] > 0

在这里插入图片描述

	#检查模型是否启用了中心采样
		创建一个形状与偏移量(offset)相同的全零张量
		将预定义的中心采样半径赋值给变量radiu
	if self.center_sampling:
	    # condition1: 判断是否在中心区域内
	    radius = self.center_sample_radius
	    stride = offset.new_zeros(offset.shape)  
	
	    # 通过循环遍历每个层级的索引(lvl_idx)以及每个层级的点数
	    	计算当前层级的终止索引
	    	将当前层级内的点的偏移设置为当前层级的步幅(stride)乘以中心采样半径
	    	将终止索引更新为下一个层级的起始索引,以便继续迭代下一个层级
	    lvl_begin = 0
	    for lvl_idx, num_points_lvl in enumerate(num_points_per_lvl):
	        lvl_end = lvl_begin + num_points_lvl
	        stride[lvl_begin:lvl_end] = self.strides[lvl_idx] * radius
	        lvl_begin = lvl_end

在这里插入图片描述
在这里插入图片描述

		# 判断是否在中心框内
	    inside_center_bbox_mask = (abs(offset) < stride).all(dim=-1)
	    # 将中心框内的点同时满足在ground truth框内
	    inside_gt_bbox_mask = torch.logical_and(inside_center_bbox_mask,
	                                            inside_gt_bbox_mask)

在这里插入图片描述

	# condition2: 限制每个位置的回归范围
	计算每个点的预测回归距离中的最大值
	根据预定义的回归范围,生成一个布尔张量inside_regress_range
	max_regress_distance = bbox_targets.max(-1)[0]
	inside_regress_range = (
	    (max_regress_distance >= regress_ranges[..., 0])
	    & (max_regress_distance <= regress_ranges[..., 1]))

在这里插入图片描述
在这里插入图片描述

	# 对于一个位置仍然有多个对象的情况,
	# 我们选择面积最小的一个对象作为目标
	areas[inside_gt_bbox_mask == 0] = INF
	areas[inside_regress_range == 0] = INF
	min_area, min_area_inds = areas.min(dim=1)

在这里插入图片描述

	# 获取每个点的标签
	labels = gt_labels[min_area_inds]
	# 如果最小面积为INF,将其标签设为背景类别
	labels[min_area == INF] = self.num_classes
	# 获取每个点的回归目标
	bbox_targets = bbox_targets[range(num_points), min_area_inds]
	# 获取每个点的角度目标
	angle_targets = gt_angle[range(num_points), min_area_inds]
	
	# 返回每个点的标签、回归目标和角度目标
	return labels, bbox_targets, angle_targets

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

1.3.2 prior_generator.grid_priors函数

def grid_priors(self,
                    featmap_sizes,
                    dtype=torch.float32,
                    device='cuda',
                    with_stride=False):
        """Generate grid points of multiple feature levels.
          Args:
            featmap_sizes (list[tuple]): List of feature map sizes in
                multiple feature levels, each size arrange as
                as (h, w).
            dtype (:obj:`dtype`): Dtype of priors. Default: torch.float32.
            device (str): The device where the anchors will be put on.
            with_stride (bool): Whether to concatenate the stride to
                the last dimension of points.

        Return:
            list[torch.Tensor]: Points of  multiple feature levels.
            The sizes of each tensor should be (N, 2) when with stride is
            ``False``, where N = width * height, width and height
            are the sizes of the corresponding feature level,
            and the last dimension 2 represent (coord_x, coord_y),
            otherwise the shape should be (N, 4),
            and the last dimension 4 represent
            (coord_x, coord_y, stride_w, stride_h).
        """

在这里插入图片描述

  #断言语句确保 self.num_levels(设置的尺度级别数量)与 featmap_sizes(特征图大小列表)的长度相同
  assert self.num_levels == len(featmap_sizes)
  multi_level_priors = []
  for i in range(self.num_levels):
      priors = self.single_level_grid_priors(
          featmap_sizes[i],
          level_idx=i,
          dtype=dtype,
          device=device,
          with_stride=with_stride)
      multi_level_priors.append(priors)
  return multi_level_priors

调用 self.single_level_grid_priors 函数生成单个尺度级别的先验框

在这里插入图片描述

1.3.2.1 single_level_grid_priors函数
  def single_level_grid_priors(self,
                                 featmap_size,
                                 level_idx,
                                 dtype=torch.float32,
                                 device='cuda',
                                 with_stride=False):
        """Generate grid Points of a single level.

        Note:
            This function is usually called by method ``self.grid_priors``.

        Args:
            featmap_size (tuple[int]): Size of the feature maps, arrange as
                (h, w).
            level_idx (int): The index of corresponding feature map level.
            dtype (:obj:`dtype`): Dtype of priors. Default: torch.float32.
            device (str, optional): The device the tensor will be put on.
                Defaults to 'cuda'.
            with_stride (bool): Concatenate the stride to the last dimension
                of points.

        Return:
            Tensor: Points of single feature levels.
            The shape of tensor should be (N, 2) when with stride is
            ``False``, where N = width * height, width and height
            are the sizes of the corresponding feature level,
            and the last dimension 2 represent (coord_x, coord_y),
            otherwise the shape should be (N, 4),
            and the last dimension 4 represent
            (coord_x, coord_y, stride_w, stride_h).
        """
feat_h, feat_w = featmap_size
stride_w, stride_h = self.strides[level_idx]
shift_x = (torch.arange(0, feat_w, device=device) +
           self.offset) * stride_w

从 featmap_size 中解包出特征图的高度和宽度,分别赋值给 feat_h 和 feat_w。
从 self.strides 中解包出当前尺度级别的水平和垂直步幅(stride),分别赋值给 stride_w 和 stride_h。
创建一个在 [0, feat_w) 范围内的张量,表示特征图上的水平坐标
	将 self.offset (0.5) 添加到上述水平坐标中,并乘以水平步幅 stride_w,以计算出先验框的水平偏移

在这里插入图片描述

# keep featmap_size as Tensor instead of int, so that we
# can convert to ONNX correctly
 shift_x = shift_x.to(dtype)

 shift_y = (torch.arange(0, feat_h, device=device) +
            self.offset) * stride_h
            
 # keep featmap_size as Tensor instead of int, so that we
        # can convert to ONNX correctly
        shift_y = shift_y.to(dtype)
            
用 torch.arange 函数创建一个在 [0, feat_h) 范围内的张量
将 self.offset 添加到上述垂直坐标中,并乘以垂直步幅 stride_h,以计算出先验框的垂直偏移

在这里插入图片描述

shift_xx, shift_yy = self._meshgrid(shift_x, shift_y)

用于根据给定的水平和垂直偏移值 shift_x 和 shift_y 创建一个网格矩阵
_meshgrid 函数会接收两个一维的张量作为输入,网格矩阵的大小是 len(shift_x) 行乘以 len(shift_y)

在这里插入图片描述

if not with_stride:
    shifts = torch.stack([shift_xx, shift_yy], dim=-1)
else:
    # use `shape[0]` instead of `len(shift_xx)` for ONNX export
    stride_w = shift_xx.new_full((shift_xx.shape[0], ),
                                 stride_w).to(dtype)
    stride_h = shift_xx.new_full((shift_yy.shape[0], ),
                                 stride_h).to(dtype)
    shifts = torch.stack([shift_xx, shift_yy, stride_w, stride_h],
                         dim=-1)
all_points = shifts.to(device)
return all_points

如果 with_stride 为 False (默认),表示不需要包含步幅信息,那么就会将之前生成的 shift_xx 和 shift_yy 合并成一个张量 shifts
如果 with_stride 为 True,表示需要包含步幅信息,那么会为每个中心坐标 (x, y) 额外添加步幅信息

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/954047.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

vr城乡规划建筑设计元宇宙平台提高工作协同效率

随着科技的发展&#xff0c;元宇宙正逐渐渗透到各个行业领域中&#xff0c;特别是建筑设计&#xff0c;那么在建筑设计中&#xff0c;利用元宇宙平台有哪些特点及优势呢? 面对建筑行业日益突出的高消耗、高风险、高投入、低利润的问题&#xff0c;如何提升企业经营管理水平&am…

Python Pyecharts 制图

基本图表 - pyecharts - A Python Echarts Plotting Library built with love. from pyecharts import options as opts from pyecharts.charts import Pie from pyecharts.faker import Fakerc (Pie().add("",[list(z) for z in zip(["7室1厅", "5…

基于KNN算法的鸢尾花种类预测

K近邻法算法思想 K近邻法&#xff08;K-Nearest Neighbor&#xff0c;KNN&#xff09;是一种基本的分类和回归方法&#xff0c;是监督学习方法里的一种常用方法。K近邻算法用一句通俗的古语来说就是&#xff1a;“物以类聚&#xff0c;人以群分”。有人说看一个人什么样&#…

【Unity3D游戏魔坦之争】游戏结束流程封装实现【七】

&#x1f468;‍&#x1f4bb;个人主页&#xff1a;元宇宙-秩沅 &#x1f468;‍&#x1f4bb; hallo 欢迎 点赞&#x1f44d; 收藏⭐ 留言&#x1f4dd; 加关注✅! &#x1f468;‍&#x1f4bb; 本文由 秩沅 原创 &#x1f468;‍&#x1f4bb; 收录于专栏&#xff1a;uni…

虹科分享 | 温度边缘效应对冻干成品含水量的影响(下)——优化和总结

上一篇文章中介绍到借助虹科Ellab的温度记录仪观察到由于冻干机壁面温度的影响&#xff0c;形成的边缘效应导致同一隔板的不同区域冻干饼块的干燥程度不均匀&#xff0c;含水量不同。 06 初次试验结果&#xff1a; 二次干燥中的产品温度显示&#xff1a; 放置在搁板中间的产品…

Linux内核源码分析 (A)常见内核面试题

Linux内核源码分析 (A)常见内核面试题 文章目录 Linux内核源码分析 (A)常见内核面试题调用 schedule() 进行进程切换的方式有几种CFS调度器vruntime的计算方式 网站收集面试题集合1 调用 schedule() 进行进程切换的方式有几种 系统调用 do_fork()&#xff1a;copy_process()定…

供热管网安全运行监测,提升供热管网安全性能

城市管网是城市的“生命线”之一&#xff0c;是城市赖以生存和发展的基础&#xff0c;在城市基础设施高质量发展中发挥着重要作用。供热管网作为城市生命线中连接供热管线与热用户的桥梁&#xff0c;担负着向企业和居民用户直接供热的重要职责。随着城市热力需求的急剧增加&…

【SpringBoot】Swagger和knife4j的使用

文章目录 前言1.什么是Swagger和Knife4j2.Swagger和Knife4j怎么用2.1 引入依赖2.2 设置配置类2.3 启动验证 3.完结撒花 前言 springboot笔记集合: springboot笔记合计 没用的废话理论不多说&#xff0c;会用就完了 1.什么是Swagger和Knife4j Swagger是一种开源的API描述语言…

ranger无法同步用户问题解决

1.首先就是定位日志,日志目录 cd /var/log/ranger/usersync 定位到问题报错如下: LdapDeltaUserGroupBuilder.getUsers() failed with exception:java.naming.AuthticationExceptiom :[LDAP:error code 49 - Invalid Credentials]:remaing name ‘ouPeople,dc*.dccom’ 解决办法…

frida动态调试入门01——定位关键代码

说明 frida是一款Python工具可以方便对内存进行hook修改代码逻辑在移动端安全和逆向过程中常用到。 实战 嘟嘟牛登录页面hook 使用到的工具 1&#xff0c;jadx-gui 2&#xff0c;frida 定位关键代码 使用jadx-gui 进行模糊搜索&#xff0c;例如搜索encyrpt之类的加密关键…

Python Qt(八)Treeview

源代码&#xff1a; # -*- coding: utf-8 -*-# Form implementation generated from reading ui file qt_treeview.ui # # Created by: PyQt5 UI code generator 5.15.9 # # WARNING: Any manual changes made to this file will be lost when pyuic5 is # run again. Do not…

vnc与windows之间的复制粘贴

【原创】VNC怎么和宿主机共享粘贴板 假设目标主机是linux&#xff0c;终端主机是windows&#xff08;就是在windows上使用VNC登陆linux&#xff09; 在linux中执行 vncconfig -nowin& 在linux选中文字后&#xff0c;无需其他按键&#xff0c;直接在windows中可以黏贴。 …

2023-8-31 Dijkstra求最短路(二)

题目链接&#xff1a;Dijkstra求最短路 II #include <iostream> #include <cstring> #include <algorithm> #include <vector> #include <queue>using namespace std;typedef pair<int, int> PII;const int N 150010;int n, m; int h[N…

利用GeoServer进行跨图层空间查询

Cross-layer filtering 跨层过滤提供了从层A中查找与层B中的特征具有特定关系的特征的能力。例如&#xff0c;这可以用于查找距离指定商店给定距离内的所有公交车站&#xff0c;或者查找指定城区内的所有咖啡店。 querylayer模块添加了实现跨层过滤的过滤功能。这些功能通过查…

十一、装饰器模式

一、什么是装饰器模式 装饰器&#xff08;Decorator&#xff09;模式的定义&#xff1a;指在不改变现有对象结构的情况下&#xff0c;动态地给该对象增加一些职责&#xff08;即增加其额外功能&#xff09;的模式&#xff0c;它属于对象结构型模式。 装饰器模式主要包含以下角色…

你的家乡清晰可见,全国卫星影像100%覆盖!

向上数三代&#xff0c;祖辈皆农民&#xff01; 无论是坐在高楼大厦里的白领&#xff0c;各种机构的公职人员&#xff0c;还是奔跑在大街小巷的外卖小哥&#xff0c;他们的根几乎皆在农村&#xff0c;当然应该也包括你和我&#xff01; 如何通过高清卫星影像看家乡的变化&…

企业如何充分借助大数据下精准营销?

技术的发展和智能终端的普及移动互联网用户的大规模增长使移动互联网快速发展&#xff0c;使中国移动互联网软件进入移动互联网时代越来越多地涉及到改变生活大家习惯。移动互联网时代的到来也意味着大数据时代的到来。精准营销数据方法&#xff0c;移动互联网和大数据的兴起不…

java八股文面试[多线程]——AQS 详细介绍

线程同步除了Synchronized Volatile ReentranLock 之外&#xff0c;还有其他一些用来进行同步的机制。 AQS 简单介绍 AQS 的全称为&#xff08;AbstractQueuedSynchronizer&#xff09;&#xff0c;这个类在 java.util.concurrent.locks 包下面。 AQS 是一个用来构建锁和同步器…

git submodule 子模块的基本使用

背景 浏览开源库的时候经常会看到如下子模块的引用情况。 子模块通常是项目比较复杂&#xff0c;需要对项目进行拆分&#xff0c;而项目又有引用关系时会使用。通常拆分项目后&#xff0c;我只需要关注自己的项目更改&#xff0c;不需要关注引用的项目都做了哪些更改。 通常…

为什么删除Windows 11上的Bloatware可以帮助加快你的电脑速度

如果你感觉你的电脑迟钝&#xff0c;彻底清除软件会有所帮助&#xff0c;而且这个过程对Windows用户来说越来越容易。 微软正在使删除以前难以删除的其他预装Windows应用程序成为可能。专家表示&#xff0c;这项新功能可能会改变用户的游戏规则。 科技公司Infatica的主管Vlad…