Faster R-CNN源码阅读之六:Faster R-CNN/lib/fast_rcnn/bbox_transform.py

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/DaVinciL/article/details/81902079
  1. Faster R-CNN源码阅读之零:写在前面
  2. Faster R-CNN源码阅读之一:Faster R-CNN/lib/networks/network.py
  3. Faster R-CNN源码阅读之二:Faster R-CNN/lib/networks/factory.py
  4. Faster R-CNN源码阅读之三:Faster R-CNN/lib/networks/VGGnet_test.py
  5. Faster R-CNN源码阅读之四:Faster R-CNN/lib/rpn_msr/generate_anchors.py
  6. Faster R-CNN源码阅读之五:Faster R-CNN/lib/rpn_msr/proposal_layer_tf.py
  7. Faster R-CNN源码阅读之六:Faster R-CNN/lib/fast_rcnn/bbox_transform.py
  8. Faster R-CNN源码阅读之七:Faster R-CNN/lib/rpn_msr/anchor_target_layer_tf.py
  9. Faster R-CNN源码阅读之八:Faster R-CNN/lib/rpn_msr/proposal_target_layer_tf.py
  10. Faster R-CNN源码阅读之九:Faster R-CNN/tools/train_net.py
  11. Faster R-CNN源码阅读之十:Faster R-CNN/lib/fast_rcnn/train.py
  12. Faster R-CNN源码阅读之十一:Faster R-CNN预测demo代码补完
  13. Faster R-CNN源码阅读之十二:写在最后

一、介绍
   本demo由Faster R-CNN官方提供,我只是在官方的代码上增加了注释,一方面方便我自己学习,另一方面贴出来和大家一起交流。
   该文件中的函数都是与anchors的变换相关,包括正向变换,反向变换。
二、代码以及注释

# -*- coding:utf-8 -*-
# --------------------------------------------------------
# Fast R-CNN
# Copyright (c) 2015 Microsoft
# Licensed under The MIT License [see LICENSE for details]
# Written by Ross Girshick
# --------------------------------------------------------

import numpy as np

'''
该文件中的函数都是与anchors的变换相关,包括正向变换,反向变换。
'''

def bbox_transform(ex_rois, gt_rois):
    '''
    计算两个N * 4的矩阵之间的相关回归矩阵。
    本质上是在求解每一个anchor相对于它的对应gt box的(dx, dy, dw, dh)的四个回归值,返回结果的shape为[N, 4]。
    :param ex_rois: shape为[N, 4]的数组,一般传入的anchors的信息。
    :param gt_rois: shape为[N, 4]的数组,一般传入的gt boxes(ground truth boxes)的信息。每一个gt roi都与一个ex roi相对应。
    :return: 本质上是在求解每一个anchor相对于它的对应gt box的(dx, dy, dw, dh)的四个回归值,返回结果的shape为[N, 4]。
    '''

    # 求出每一个ex_roi的宽度高度和中心坐标
    ex_widths = ex_rois[:, 2] - ex_rois[:, 0] + 1.0
    ex_heights = ex_rois[:, 3] - ex_rois[:, 1] + 1.0
    ex_ctr_x = ex_rois[:, 0] + 0.5 * ex_widths
    ex_ctr_y = ex_rois[:, 1] + 0.5 * ex_heights

    # 求解每一个gt box的宽度高度和中心坐标
    gt_widths = gt_rois[:, 2] - gt_rois[:, 0] + 1.0
    gt_heights = gt_rois[:, 3] - gt_rois[:, 1] + 1.0
    gt_ctr_x = gt_rois[:, 0] + 0.5 * gt_widths
    gt_ctr_y = gt_rois[:, 1] + 0.5 * gt_heights

    # 这里本质上是在反向求解RPN网络应该生成的数据,即bbox进行回归操作需要的4个变量。
    # 参考bbox_transform_inv在demo中的使用,可以看出,targets本质上就是bbox_transform_inv参数中的deltas。
    # 在bbox_transform_inv中,正向使用了deltas,而在该函数中,我们需要根据gt boxes信息,反向求解出deltas。
    # 我们所需要的就是RPN(rpn_cls_pred)根据我们返回的目标targets产生合适的deltas。
    # 下面是bbox_transform_inv中的anchors的变换代码,正好和targets的生成代码相反。
    # pred_ctr_x = dx * widths[:, np.newaxis] + ctr_x[:, np.newaxis]
    # pred_ctr_y = dy * heights[:, np.newaxis] + ctr_y[:, np.newaxis]
    # pred_w = np.exp(dw) * widths[:, np.newaxis]
    # pred_h = np.exp(dh) * heights[:, np.newaxis]

    targets_dx = (gt_ctr_x - ex_ctr_x) / ex_widths
    targets_dy = (gt_ctr_y - ex_ctr_y) / ex_heights
    targets_dw = np.log(gt_widths / ex_widths)
    targets_dh = np.log(gt_heights / ex_heights)

    # 将所有的信息组合成一个矩阵返回
    # vstack将所有的向量组合成shape为[4, N]的矩阵,最后transpose,矩阵的shape变成[N, 4]。
    targets = np.vstack(
        (targets_dx, targets_dy, targets_dw, targets_dh)).transpose()

    # 返回
    return targets


def bbox_transform_inv(boxes, deltas):
    '''
    将boxes使用rpn网络产生的deltas进行变换处理,求出变换后的boxes,即预测的proposals。
    此处boxes一般表示原始anchors,即未经任何处理仅仅是经过平移之后产生测anchors。
    :param boxes: 一般表示原始anchors,即未经任何处理仅仅是经过平移之后产生测anchors,shape为[N, 4],N表示anchors的数目。
    :param deltas: RPN网络产生的数据,shape为[N, (1 + classes) * 4],classes表示类别数目,1 表示背景,N表示anchors的数目。
    :return: 预测的变换之后的proposals(或者叫anchors)
    '''
    if boxes.shape[0] == 0:
        return np.zeros((0, deltas.shape[1]), dtype=deltas.dtype)

    # 进行类型转换
    boxes = boxes.astype(deltas.dtype, copy=False)

    # 求出每一个box的宽度高度和中心点,下面四个变量的shape均为[N],(一位数组,长度为N),N为anchors的数目
    widths = boxes[:, 2] - boxes[:, 0] + 1.0
    heights = boxes[:, 3] - boxes[:, 1] + 1.0
    ctr_x = boxes[:, 0] + 0.5 * widths
    ctr_y = boxes[:, 1] + 0.5 * heights

    # 获取每一个类别的deltas的信息,每一个类别的deltas的信息是顺序存储的,
    # 即第一个类别的四个信息(dx, dy, dw, dh)存储完成后才接着另一个类别。
    # 下面四个变量的shape均为[N, classes + 1],N表示anchors数目,classes表示类别数目(此处为20),1表示背景。
    dx = deltas[:, 0::4]
    dy = deltas[:, 1::4]
    dw = deltas[:, 2::4]
    dh = deltas[:, 3::4]

    # 具体的变换过程,这里采用了以e为底数的指数运算方式,(据说)方便求导。
    # 下面四个变量的shape均为[N, classes + 1],N表示anchors数目,classes表示类别数目(此处为20),1表示背景。
    # 对于每一个box(anchor),都对其预测每一个类别,即一个anchor对应于每一个类别。
    pred_ctr_x = dx * widths[:, np.newaxis] + ctr_x[:, np.newaxis]
    pred_ctr_y = dy * heights[:, np.newaxis] + ctr_y[:, np.newaxis]
    pred_w = np.exp(dw) * widths[:, np.newaxis]
    pred_h = np.exp(dh) * heights[:, np.newaxis]

    # 变换完成后,对每一个box(anchor)都重新生成预测之后的每一个类别的左上角和右下角的坐标。
    # pred_boxes和deltas的shape相同,均为[N, (1 + classes) * 4],classes表示类别数目,1 表示背景,N表示anchors的数目。
    pred_boxes = np.zeros(deltas.shape, dtype=deltas.dtype)

    # 进行赋值操作
    # 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
    pred_boxes[:, 2::4] = pred_ctr_x + 0.5 * pred_w
    # y2
    pred_boxes[:, 3::4] = pred_ctr_y + 0.5 * pred_h

    # 返回
    return pred_boxes


def clip_boxes(boxes, im_shape):
    """
    Clip boxes to image boundaries.
    将boxes的边框进行裁剪,使得每一条边框都在图片大小的范围之内。
    :param boxes: 等待裁剪的boxes,一般是一系列的anchors。
    :param im_shape: 图片的大小形状
    :return: 裁减之后的boxes,每一条边都在图片大小的范围内
    """

    # x1 >= 0
    boxes[:, 0::4] = np.maximum(np.minimum(boxes[:, 0::4], im_shape[1] - 1), 0)
    # y1 >= 0
    boxes[:, 1::4] = np.maximum(np.minimum(boxes[:, 1::4], im_shape[0] - 1), 0)
    # x2 < im_shape[1]
    boxes[:, 2::4] = np.maximum(np.minimum(boxes[:, 2::4], im_shape[1] - 1), 0)
    # y2 < im_shape[0]
    boxes[:, 3::4] = np.maximum(np.minimum(boxes[:, 3::4], im_shape[0] - 1), 0)
    return boxes

猜你喜欢

转载自blog.csdn.net/DaVinciL/article/details/81902079