maskrcnn-benchmark 代码详解之 box_coder.py

前言

    box_coder.py主要用于候选边框(proposal)的编码和解码,即求解RCNN论文中回归目标中的t_*以及预测边框。其主要针对的是RCNN和faster RCNN中的Bounding-box regression部分的操作。

1b-box 回归(RCNN)

   假设现有获得的候选框(proposal)为P^i = (P_x^i)\text{P}^i = (\text{P}_x^i, \text{P}_y^i, \text{P}_w^i, \text{P}_h^i), 而基准框(ground-truth)为\text{G}^i = (\text{G}_x^i, \text{G}_y^i, \text{G}_w^i, \text{G}_h^i), 我们需要找一种映射方式使得我们选取出来的候选框\text{P}^i能够变换成或者映射到\text{G}^i. 针对每一个边框涉及到的x, y, w, h即边框中心坐标的x,y,以及边框的宽和高,我们分别设定有d_x(P), d_y(P), d_w(P), d_h(P)这四种映射方式,分别可以帮助将\text{P}^i的x, y, w, h变换成\text{G}^i或者接近\text{G}^i的x, y, w, h。

 我们设定有d_x(P), d_y(P), d_w(P), d_h(P)中,d_x(P)d_y(P)只是尺度方面的变换,即对坐标进行变大或变小以次来移动边框的中心坐标。而针对d_w(P)d_h(P)采用对数空间的变幻,以此来控制边框高和宽的变换。因此候选框proposal)可以通过d_x(P), d_y(P), d_w(P), d_h(P)这四个映射函数变换到接近或者等于基准框(ground-truth),我们称之为基于候选框proposal)的预测框(predicted ground-truth box)\widehat{G}.

     那么\widehat{G}的公式就可以获得:

maskrcnn-benchmark 代码详解之 box_coder.py_第1张图片

   到目前位置,我们假定的四个映射函数d_x(P), d_y(P), d_w(P), d_h(P)都还没有具体的值,都是假设有这么一些函数可以完成这样的功能。接下来,我们就要定义一些固定的衡量尺度t_*(包括t_x, t_y, t_w, t_z,),t_*直接衡量候选框(proposal)\text{P}^i和基准框(ground-truth)\text{G}^i在x, y, w, h上的差距。我们将t_*规定为:

maskrcnn-benchmark 代码详解之 box_coder.py_第2张图片

规定完这些差距之后,候选框(proposal)\text{P}^i可以通过逆t_*变换就可以得到基准框(ground-truth)\text{G}^i,这是我们想要得到的变换函数。于是,我们就让d_x(P), d_y(P), d_w(P), d_h(P)无限的逼近t_x, t_y, t_w, t_z,,去拟合正确的映射函数。在卷积神经网络中,数据的映射关系是通过卷积层或者全连接层实现的,也就是说d_x(P), d_y(P), d_w(P), d_h(P)可以由卷积神经网络模拟出来,用一个全连接层或者一个卷基层就可以,这个操作等价于数据的线性变换。

  设候选区域提取出来的特征图为F^i, 那么d_*(P) = w_*^TF^i,即我们文章一开始提到的d_x(P), d_y(P), d_w(P), d_h(P)可以由特征图的卷积操作实现,我们只需要调整卷积过程的权重w_*(也就是卷积核),让d_x(P), d_y(P), d_w(P), d_h(P)无限的逼近t_x, t_y, t_w, t_z,,去拟合正确的映射函数,这就不用再提了,卷积神经网络的梯度反传会实现这一过程,因此边框回归也就完成了。

   之所以称之为边框回归(Bounding-box regression),是因为论文采用的是最小二乘的形式,将这个特征变换做成了回归的形式,即:

bbox regression

2代码解析

class BoxCoder(object):
    """
    This class encodes and decodes a set of bounding boxes into
    the representation used for training the regressors.
    BoxCoder主要用于编码和解码基准边框(bounding boxes),并将其应用到回归训练中
    本BoxCoder主要用于解决RCNN论文里提到的Bounding-box regression
    """

    def __init__(self, weights, bbox_xform_clip=math.log(1000. / 16)):
        """
        Arguments:
            weights (4-element tuple)
            weights:表示的是x, y, w, h在运算中所占的权重   
            bbox_xform_clip (float)
        """
        self.weights = weights
        # 边框的长和宽的最高值
        self.bbox_xform_clip = bbox_xform_clip

    def encode(self, reference_boxes, proposals):
        """
        Encode a set of proposals with respect to some
        reference boxes

        Arguments:
            reference_boxes (Tensor): reference boxes
            proposals (Tensor): boxes to be encoded
        """
        """
        这个Encode的作用是实现RCNN中提到的边框回归,其中的回归目标(regression target)t*
        的计算,主要是计算候选框与与之相关的基准框的偏差
        参数:
            proposals:候选边框,由一定规则选出来的可能含有目标的目标边框
            reference_boxes:与候选边框重叠度最高的基准边框gt
        """

        # 计算两个数之间的真实距离,需要相减之后加1
        TO_REMOVE = 1  # TODO remove
        # 计算候选框的宽度
        ex_widths = proposals[:, 2] - proposals[:, 0] + TO_REMOVE
        # 计算候选框的高度
        ex_heights = proposals[:, 3] - proposals[:, 1] + TO_REMOVE
        # 计算候选框中心的x坐标
        ex_ctr_x = proposals[:, 0] + 0.5 * ex_widths
        # 计算候选框中心的y坐标
        ex_ctr_y = proposals[:, 1] + 0.5 * ex_heights

        # 计算基准边框(ground truth)的宽度
        gt_widths = reference_boxes[:, 2] - reference_boxes[:, 0] + TO_REMOVE
        # 计算基准边框(ground truth)的高度
        gt_heights = reference_boxes[:, 3] - reference_boxes[:, 1] + TO_REMOVE
        # 计算基准边框(ground truth)中心的x坐标
        gt_ctr_x = reference_boxes[:, 0] + 0.5 * gt_widths
        # 计算基准边框(ground truth)中心的y坐标
        gt_ctr_y = reference_boxes[:, 1] + 0.5 * gt_heights

        # 得到计算回归目标时各个部分的权重
        wx, wy, ww, wh = self.weights
        # 计算带有权重的回归目标的各个部分
        targets_dx = wx * (gt_ctr_x - ex_ctr_x) / ex_widths
        targets_dy = wy * (gt_ctr_y - ex_ctr_y) / ex_heights
        targets_dw = ww * torch.log(gt_widths / ex_widths)
        targets_dh = wh * torch.log(gt_heights / ex_heights)

        # 将回归目标的各个部分合并为一个元组,并依次保存到一个栈里
        targets = torch.stack((targets_dx, targets_dy, targets_dw, targets_dh), dim=1)
        return targets

    def decode(self, rel_codes, boxes):
        """
        From a set of original boxes and encoded relative box offsets,
        get the decoded boxes.

        Arguments:
            rel_codes (Tensor): encoded boxes
            boxes (Tensor): reference boxes.
        """
        """
        根据得到的候选框以及与之对应的中心x,y宽和高的各部分的回归值和得到预测边框
        参数:
            rel_codes:根据候选框与基准边框(ground truth)的差距计算出来的候选边框中心x,y宽和高的各部分的变差回归值
            boxes:候选边框
        """
        # 将候选边框的数据类型设置成回归目标值一样的类型
        boxes = boxes.to(rel_codes.dtype)

        # 计算两个数之间的真实距离,需要相减之后加1
        TO_REMOVE = 1  # TODO remove
        # 计算候选框的宽度
        widths = boxes[:, 2] - boxes[:, 0] + TO_REMOVE
        # 计算候选框的高度
        heights = boxes[:, 3] - boxes[:, 1] + TO_REMOVE
        # 计算候选框中心的x坐标
        ctr_x = boxes[:, 0] + 0.5 * widths
        # 计算候选框中心的y坐标
        ctr_y = boxes[:, 1] + 0.5 * heights

        # 得到计算回归目标时各个部分的权重
        wx, wy, ww, wh = self.weights
        # 计算去除权重的回归目标的各个部分
        dx = rel_codes[:, 0::4] / wx
        dy = rel_codes[:, 1::4] / wy
        dw = rel_codes[:, 2::4] / ww
        dh = rel_codes[:, 3::4] / wh

        # Prevent sending too large values into torch.exp()
        # 防止候选框的长和宽过大影响到torch.exp(),控制其大小
        dw = torch.clamp(dw, max=self.bbox_xform_clip)
        dh = torch.clamp(dh, max=self.bbox_xform_clip)

        # 计算预测框中心的x坐标
        pred_ctr_x = dx * widths[:, None] + ctr_x[:, None]
        # 计算预测框中心的y坐标
        pred_ctr_y = dy * heights[:, None] + ctr_y[:, None]
        # 计算预测框的宽度
        pred_w = torch.exp(dw) * widths[:, None]
        # 计算预测框的高度
        pred_h = torch.exp(dh) * heights[:, None]

        # 将预测狂转换为标准边框格式
        pred_boxes = torch.zeros_like(rel_codes)
        # x1
        pred_boxes[:, 0::4] = pred_ctr_x - 0.5 * pred_w
        # y1
        pred_boxes[:, 1::4] = pred_ctr_y - 0.5 * pred_h
        # x2 (note: "- 1" is correct; don't be fooled by the asymmetry)
        pred_boxes[:, 2::4] = pred_ctr_x + 0.5 * pred_w - 1
        # y2 (note: "- 1" is correct; don't be fooled by the asymmetry)
        pred_boxes[:, 3::4] = pred_ctr_y + 0.5 * pred_h - 1

        return pred_boxes

 

你可能感兴趣的:(maskrcnn,benchmark,maskrcnn,benchmark,box_coder.py,b-box,回归,代码详解,目标检测)