mmrotate代码学习之delta_xywha_rbbox_coder.py

这个文件在用l1损失的时候会用到

主要作用是,输入预测框和anchor输出偏移量,或者输入偏移量和anchor输出预测框

类定义:

class DeltaXYWHAOBBoxCoder(BaseBBoxCoder):
    """Delta XYWHA OBBox 编码器。这个编码器用于旋转对象检测
    (例如DOTA数据集的任务1)。该编码器可以将边界框
    (xc, yc, w, h, a) 编码为偏移量 (dx, dy, dw, dh, da),并且可以将偏移量
    (dx, dy, dw, dh, da) 解码回原始边界框 (xc, yc, w, h, a)。

    参数:
        target_means (Sequence[float]): 目标偏移量的去标准化均值。
        target_stds (Sequence[float]): 目标偏移量的去标准化标准差。
        angle_range (str, optional): 角度表示方式。默认为'oc'。
        norm_factor (None|float, optional): 角度的规范化因子。
        edge_swap (bool, optional): 如果宽度小于高度是否交换边。默认为False。
        proj_xy (bool, optional): 是否根据角度投影x和y。默认为False。
        add_ctr_clamp (bool): 是否添加中心夹紧,当添加时,如果预测框的中心偏离原始锚点的中心过远,则会被夹紧。仅YOLOF使用。默认为False。
        ctr_clamp (int): 最大像素偏移量夹紧。仅YOLOF使用。默认为32。
    """

    def __init__(self,
                 target_means=(0., 0., 0., 0., 0.),
                 target_stds=(1., 1., 1., 1., 1.),
                 angle_range='oc',
                 norm_factor=None,
                 edge_swap=False,
                 proj_xy=False,
                 add_ctr_clamp=False,
                 ctr_clamp=32):
        super(BaseBBoxCoder, self).__init__()
        self.means = target_means  # 目标偏移量的均值
        self.stds = target_stds  # 目标偏移量的标准差
        self.angle_range = angle_range  # 角度表示方式
        self.norm_factor = norm_factor  # 角度的规范化因子
        self.edge_swap = edge_swap  # 宽高交换标志
        self.proj_xy = proj_xy  # x和y的投影标志
        self.add_ctr_clamp = add_ctr_clamp  # 中心夹紧标志
        self.ctr_clamp = ctr_clamp  # 中心夹紧的最大像素偏移量

编码:

    def encode(self, bboxes, gt_bboxes):
        """获取可以用来将``bboxes``变换到``gt_bboxes``的边界框回归变换量。

        参数:
            bboxes (torch.Tensor): anchor
            gt_bboxes (torch.Tensor): GT

        返回:
            torch.Tensor: 边界框变换量
        """
        assert bboxes.size(0) == gt_bboxes.size(0)
        assert bboxes.size(-1) == 5
        assert gt_bboxes.size(-1) == 5
        if self.angle_range in ['oc', 'le135', 'le90']:
                    # 利用bbox2delta函数将得到的差值返回
            return bbox2delta(bboxes, gt_bboxes, self.means, self.stds,
                              self.angle_range, self.norm_factor,
                              self.edge_swap, self.proj_xy)
        else:
            raise NotImplementedError

解码:

    def decode(self,
               bboxes,
               pred_bboxes,
               max_shape=None,
               wh_ratio_clip=16 / 1000):
        """将`pred_bboxes`的变换应用于`bboxes`上。

        参数:
            bboxes (torch.Tensor): anchor。形状为(B, N, 5)或(N, 5)
            pred_bboxes (torch.Tensor): 编码偏移量
                形状可以是(B, N, num_classes * 5)或(B, N, 5)或
                (N, num_classes * 5)或(N, 5)。
            max_shape (Sequence[int] or torch.Tensor or Sequence[
               Sequence[int]], optional): 边界框的最大界限,指定
               (H, W, C)或(H, W)。如果bboxes的形状是(B, N, 5),那么
               max_shape应该是一个Sequence[Sequence[int]]
               并且max_shape的长度也应该是B。
            wh_ratio_clip (float, optional): 允许的宽高比。

        返回:
            torch.Tensor: 解码后的边界框。
        """
        assert pred_bboxes.size(0) == bboxes.size(0)
        if self.angle_range in ['oc', 'le135', 'le90']:
              #上面是判断、下面进行转换
            return delta2bbox(bboxes, pred_bboxes, self.means, self.stds,
                              max_shape, wh_ratio_clip, self.add_ctr_clamp,
                              self.ctr_clamp, self.angle_range,
                              self.norm_factor, self.edge_swap, self.proj_xy)
        else:
            raise NotImplementedError

计算GT和anchor的偏移量

def bbox2delta(proposals,
               gt,
               means=(0., 0., 0., 0., 0.),
               stds=(1., 1., 1., 1., 1.),
               angle_range='oc',
               norm_factor=None,
               edge_swap=False,
               proj_xy=False):
    """
    计算提议框相对于真实边界框的偏移量(deltas)。

    参数:
        proposals (torch.Tensor): 要转换的边界框,形状为(N, ..., 5)
        gt (torch.Tensor): 作为基准的真实边界框,形状为(N, ..., 5)
        means (Sequence[float]): 偏移量坐标的去标准化均值
        stds (Sequence[float]): 偏移量坐标的去标准化标准差
        angle_range (str, optional): 角度表示方式,默认为'oc'
        norm_factor (None|float, optional): 角度的规范化因子
        edge_swap (bool, optional): 如果宽度小于高度是否交换边,默认为False
        proj_xy (bool, optional): 是否根据角度投影x和y,默认为False

    返回:
        Tensor: 形状为(N, 5)的偏移量,其中列代表dx, dy, dw, dh, da。
    """
    # 确保提议框和真实边界框的尺寸相同
    assert proposals.size() == gt.size()
    # 转换为浮点类型
    proposals = proposals.float()
    gt = gt.float()
    # 解绑提议框和真实边界框的坐标和角度
    px, py, pw, ph, pa = proposals.unbind(dim=-1)
    gx, gy, gw, gh, ga = gt.unbind(dim=-1)

    # 根据是否进行x和y的角度投影来计算dx和dy
    if proj_xy:
        dx = (torch.cos(pa) * (gx - px) + torch.sin(pa) * (gy - py)) / pw
        dy = (-torch.sin(pa) * (gx - px) + torch.cos(pa) * (gy - py)) / ph
    else:
        dx = (gx - px) / pw
        dy = (gy - py) / ph

    # 如果启用了边缘交换
    if edge_swap:
        # 计算角度差,并判断使用哪个角度差
        dtheta1 = norm_angle(ga - pa, angle_range)
        dtheta2 = norm_angle(ga - pa + np.pi / 2, angle_range)
        abs_dtheta1 = torch.abs(dtheta1)
        abs_dtheta2 = torch.abs(dtheta2)
        # 根据角度差调整宽高
        gw_regular = torch.where(abs_dtheta1 < abs_dtheta2, gw, gh)
        gh_regular = torch.where(abs_dtheta1 < abs_dtheta2, gh, gw)
        da = torch.where(abs_dtheta1 < abs_dtheta2, dtheta1, dtheta2)
        dw = torch.log(gw_regular / pw)
        dh = torch.log(gh_regular / ph)
    else:
        # 不交换边缘时直接计算角度差和宽高比
        da = norm_angle(ga - pa, angle_range)
        dw = torch.log(gw / pw)
        dh = torch.log(gh / ph)

    # 如果有角度的规范化因子,对角度差进行规范化
    if norm_factor:
        da /= norm_factor * np.pi

    # 组合dx, dy, dw, dh, da为偏移量
    deltas = torch.stack([dx, dy, dw, dh, da], dim=-1)
    # 应用去标准化均值和标准差
    means = deltas.new_tensor(means).unsqueeze(0)
    stds = deltas.new_tensor(stds).unsqueeze(0)
    deltas = deltas.sub_(means).div_(stds)

    return deltas

由偏移量计算bbox

def delta2bbox(rois, deltas, means=(0., 0., 0., 0., 0.), stds=(1., 1., 1., 1., 1.), max_shape=None, wh_ratio_clip=16 / 1000, add_ctr_clamp=False, ctr_clamp=32, angle_range='oc', norm_factor=None, edge_swap=False, proj_xy=False):
    """
    将偏移量应用于基础边界框以进行平移/缩放。典型地,rois是锚框或提议的边界框,
    deltas是网络输出用于平移/缩放这些框的。

    参数:
        rois (torch.Tensor): 要转换的边界框,形状为(N, 5)。
        deltas (torch.Tensor): 相对于每个roi的编码偏移量,形状为(N, num_classes * 5)或(N, 5)。
        means (Sequence[float]): 偏移坐标的去标准化均值,默认(0., 0., 0., 0., 0.)。
        stds (Sequence[float]): 偏移坐标的去标准化标准差,默认(1., 1., 1., 1., 1.)。
        max_shape (Sequence[int] or torch.Tensor or Sequence[Sequence[int]], optional): 边界框的最大界限。
        wh_ratio_clip (float): 边界框的最大宽高比,默认16/1000。
        add_ctr_clamp (bool): 是否添加中心夹紧,默认False。
        ctr_clamp (int): 中心夹紧的最大像素偏移量,默认32。
        angle_range (str, optional): 角度表示方式,默认为'oc'。
        norm_factor (None|float, optional): 角度的规范化因子。
        edge_swap (bool, optional): 如果宽度小于高度是否交换边,默认为False。
        proj_xy (bool, optional): 是否根据角度投影x和y,默认为False。

    返回:
        Tensor: 形状为(N, num_classes * 5)或(N, 5)的边界框,5代表cx, cy, w, h, a。
    """
    # 对偏移量进行去标准化
    means = deltas.new_tensor(means).view(1, -1).repeat(1, deltas.size(1) // 5)
    stds = deltas.new_tensor(stds).view(1, -1).repeat(1, deltas.size(1) // 5)
    denorm_deltas = deltas * stds + means
    # 提取各个偏移量
    dx, dy, dw, dh, da = denorm_deltas[:, 0::5], denorm_deltas[:, 1::5], denorm_deltas[:, 2::5], denorm_deltas[:, 3::5], denorm_deltas[:, 4::5]
    # 角度规范化
    if norm_factor:
        da *= norm_factor * np.pi
    # 计算roi的中心、宽度、高度和角度
    px, py, pw, ph, pa = rois[:, 0].unsqueeze(1), rois[:, 1].unsqueeze(1), rois[:, 2].unsqueeze(1), rois[:, 3].unsqueeze(1), rois[:, 4].unsqueeze(1)
    # 应用偏移量
    dx_width, dy_height = pw * dx, ph * dy
    # 宽高比限制和中心夹紧
    max_ratio = np.abs(np.log(wh_ratio_clip))
    if add_ctr_clamp:
        dx_width, dy_height = torch.clamp(dx_width, min=-ctr_clamp, max=ctr_clamp), torch.clamp(dy_height, min=-ctr_clamp, max=ctr_clamp)
    dw, dh = dw.clamp(min=-max_ratio, max=max_ratio), dh.clamp(min=-max_ratio, max=max_ratio)
    # 缩放
    gw, gh = pw * dw.exp(), ph * dh.exp()
    # 平移
    if proj_xy:
        gx, gy = dx * pw * torch.cos(pa) - dy * ph * torch.sin(pa) + px, dx * pw * torch.sin(pa) + dy * ph * torch.cos(pa) + py
    else:
        gx, gy = px + dx_width, py + dy_height
    # 计算角度
    ga = norm_angle(pa + da, angle_range)
    # 边界限制
    if max_shape is not None:
        gx, gy = gx.clamp(min=0, max=max_shape[1] - 1), gy.clamp(min=0, max=max_shape[0] - 1)
    # 边缘交换
    if edge_swap:
        w_regular, h_regular = torch.where(gw > gh, gw, gh), torch.where(gw > gh, gh, gw)
        theta_regular = torch.where(gw > gh, ga, ga + np.pi / 2)
        return torch.stack([gx, gy, w_regular, h_regular, norm_angle(theta_regular, angle_range)], dim=-1)
    else:
        return torch.stack([gx, gy, gw, gh, ga], dim=-1)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值