balanced_positive_negative_sampler.py
在 rpn/loss.py
文件中的 class RPNLossComputation(object)
类使用了 class BalancedPositiveNegativeSampler(object)
类的实例对象作为函数参数, 下面我们就来看看该类的具体实现:
1 | # ../maskrcnn_benchmark/modeling/balanced_positive_negative_sampler.py |
box_coder.py
在 ./maskrcnn_benchmark/modeling/rpn/rpn.py
文件中, 使用了下面的语句创建 class BoxCoder(object)
实例:1
rpn_box_coder = BoxCoder(weights=(1.0, 1.0, 1.0, 1.0))
这个类位于 ./maskrcnn_benchmark/modeling/box_coder.py
文件中, 在对这个类的定义展开详细解析前, 我们首先对 R-CNN 中 bounding box 的回归任务的坐标编码方式进行介绍. 假设我们具有一个候选区域框, 用 $P=(P_x, P_y, P_w, P_h)$ 表示, 它对应的真实框用 $G=(G_x, G_y, G_w, G_h)$ 表示, 那么, 我们的目标是希望回归器能够学习到一个从 $P$ 到 $G$ 的转化(transformation), 假设此时模型从 $P$ 中预测得到的框为 $\hat G = (\hat G_x, \hat G_y, \hat G_h, \hat G_w)$, 那么我们可以定义出如下的从 $P$ 到 $\hat G$ 的转化:
上式中的 $d_x(P), d_y(P), d_w(P), d_h(P)$ 就是我们要学习的参数, 那么我们的学习目标就是使得这些参数可以满足 $\hat G = G$, 也就是说, 我们的学习目标就是令参数 $d_x(P), d_y(P), d_w(P), d_h(P)$ 无限近似于下面的 $(t_x, t_y, t_w, t_h)$:
BoxCoder
类的代码解析如下所示: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# ./maskrcnn_benchmark/modeling/box_coder.py
class BoxCoder(object):
# 这个类负责将一系列的 bounding boxes 编码, 使之可以被用于训练对应的回归器
def __init__(self, weights, bbox_xform_clip=math.log(1000. / 16)):
# weights (4-element tuple)
# bbox_xform_clip (float): 用于对 dw 和 dh 剪枝
# 将参数置为成员变量
self.weights = weights
self.bbox_xform_clip = bbox_xform_clip
def encode(self, reference_boxes, proposals):
# 根据给定的 reference_boxes 对一系列的 proposals 进行编码
# reference_boxes(Tensor)
# proposals(Tensor): 待编码的 boxes
TO_REMOVE = 1
# 获取宽度(x2-x1)
ex_widths = proposals[:, 2] - proposals[:, 0] + TO_REMOVE
# 获取高度(y2-y1)
ex_heights = proposals[:, 3] - proposals[:, 1] + TO_REMOVE
# 获取中心点坐标
ex_ctr_x = proposals[:, 0] + 0.5 * ex_widths
ex_ctr_y = proposals[:, 1] + 0.5 * ex_heights
# 同样的计算逻辑, 获取真实框的宽高和重心坐标
gt_widths = reference_boxes[:, 2] - reference_boxes[:, 0] + TO_REMOVE
gt_heights = reference_boxes[:, 3] - reference_boxes[:, 1] + TO_REMOVE
gt_ctr_x = reference_boxes[:, 0] + 0.5 * gt_widths
gt_ctr_y = reference_boxes[:, 1] + 0.5 * gt_heights
# 计算当前 proposals box 的训练目标, 注意, 不同的 proposals 具有不同的训练目标
# 最终返回的 tensor 的 shape 为 [n, 4], n 为 proposals 的大小.
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):
# 将编码后的 transformation 形式的 box 转化成 (x1, y1, x2, y2) 形式
# rel_codes (Tensor): encoded boxes
# boxes (Tensor): reference boxes
boxes = boxes.to(rel_codes.dtype)
TO_REMOVE = 1
# 获取真实 box 的宽高和中心坐标
widths = boxes[:, 2] - boxes[:, 0] + TO_REMOVE
heights = boxes[:, 3] - boxes[:, 1] + TO_REMOVE
ctr_x = boxes[:, 0] + 0.5 * widths
ctr_y = boxes[:, 1] + 0.5 * heights
# 先对权重解码
wx, wy, ww, wh = self.weights
dx = rel_codes[:, 0::4] / wx # 步长为4
dy = rel_codes[:, 1::4] / wy
dw = rel_codes[:, 2::4] / ww
dh = rel_codes[:, 3::4] / wh
# 对 dw, 和 dh 剪枝, 防止向 troch.exp() 传送过大的值
dw = torch.clamp(dw, max=self.bbox_xform_clip)
dh = torch.clamp(dh, max=self.bbox_xform_clip)
# 计算预测的 box 的中心坐标和宽高, 公式说明参见前面的原理讲解
pred_ctr_x = dx * widths[:, None] + ctr_x[:, None]
pred_ctr_y = dy * heights[:, None] + ctr_y[:, None]
pred_w = torch.exp(dw) * widths[:, None]
pred_h = torch.exp(dh) * heights[:, None]
# 将中心坐标和宽高转化成 (x1, y1, x2, y2) 的表示形式, 然后将其返回
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
pred_boxes[:, 2::4] = pred_ctr_x + 0.5 * pred_w - 1
# y2
pred_boxes[:, 3::4] = pred_ctr_y + 0.5 * pred_h - 1
return pred_boxes
matcher.py
在 rpn/loss.py
文件中的 make_rpn_loss_evaluator()
函数中利用如下语句创建了一个 class Matcher(object)
实例:1
2
3
4
5
6
7# ./maskrcnn_benchmark/modeling/rpn/loss.py
matcher = Matcher(
cfg.MODEL.RPN.FG_IOU_THRESHOLD,
cfg.MODEL.RPN.BG_IOU_THRESHOLD,
allow_low_quality_matches=True,
)
下面我们就来看看该类的具体实现:
1 | # ./maskrcnn_benchmark/modeling/matcher.py |
poolers.py
registry.py