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

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

一、介绍

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

二、代码以及注释
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
# -*- 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

Comments

Your browser is out-of-date!

Update your browser to view this website correctly. Update my browser now

×