Faster-Rcnn检测框架主要分为两个部分,第一部分是Region Proposal Network(RPN)以及第二部分对Proposals进行分类和回归。
本文主要回顾RPN在图片上产生约2000个Proposals后,如何根据ground-truth选取其中的256个,并为每个proposal分配标签参与第二阶段的训练。
大致流程如下:
- 计算每个ground-truth与所有proposals的交并比IOU,为每个proposal找到与其匹配最好的ground-truth
- IOU大于0.7作为正例,小于0.3作为负例,中间的忽略
- 随机采样256个proposals用于第二阶段的训练,并为其分配对应的ground-truth标签
def label_and_sample_proposals(
self, proposals: List[Instances], targets: List[Instances]
) -> List[Instances]:
"""
Region Proposal Network(RPN)生成候选区域(Proposals)之后,需要将其用于训练分类和回归头,统称为(ROI heads)。
这个函数就是用于给每个proposal分配对应的训练标签。
通过候选区域和真实框的情况,随机采样self.batch_size_per_image(256)个候选区域参与第二阶段的训练,
其中正例的比例不超过self.positive_fraction(0.5)
Args:
proposals (list[Instances]): 长度为`N`的`Instances`列表。第i个
`Instances` 包含在第i张输入图片上生成的候选区域,其中 fields包括
"proposal_boxes" and "objectness_logits"。
targets (list[Instances], optional): 长度为`N`的`Instances`列表。第i个
`Instances` 包含第i张输入图片上真实框的每个实例标注。
`targets` 仅在训练阶段产生。
它可能包含以下fields:
- gt_boxes: 每个实例的边界框位置.
- gt_classes: 每个实例的类别标签,类别范围在[0, #class].
- gt_masks:
- gt_keypoints:
Returns:
list[Instances]:
`Instances`包含采样的需要参加训练的候选区域。
每个`Instances`有以下fields:
- proposal_boxes: 候选区域的位置
- gt_boxes: 候选区域被分配的对应的真实框的坐标
其他的fields例如"gt_classes", "gt_masks"等,这些都是被包含在`targets`中。
"""
# 图片上所有真实框的坐标
gt_boxes = [x.gt_boxes for x in targets]
# 通过真实框对候选区域进行增强。
# 当训练刚开始的时候,由于RPN网络参数是随机初始化的,导致生成的候选区域质量较差。
# 这可能导致没有一个候选区域与真实框有足够大的交并比,作为正例参与第二阶段分类和回归头的训练。
# 添加真实框到候选区域集合中,确保在最初训练的时候第二阶段有一定的正例。
# 对RPN而言,这个增强加快收敛并在COCO上AP提升约0.5个点。
if self.proposal_append_gt:
proposals = add_ground_truth_to_proposals(gt_boxes, proposals)
# 函数需要返回的带有标签的候选区域列表
proposals_with_gt = []
# 遍历每张图片
for proposals_per_image, targets_per_image in zip(proposals, targets):
has_gt = len(targets_per_image) > 0
# 计算每个真实框与候选区域的交并比iou
# 图片上真实框的数量为NG,生成的候选区域数量为PG,则计算的矩阵为(NG,PG)
match_quality_matrix = pairwise_iou(
targets_per_image.gt_boxes, proposals_per_image.proposal_boxes
)
# 如果iou大于0.7,则该侯选区域被记为正类,小于0.3被记为负类
# matched_idxs:保存与每个候选区域匹配最好的真实框的索引
# matched_labels: 保存每个候选区域的标记。0:背景 -1:忽略
matched_idxs, matched_labels = self.proposal_matcher(match_quality_matrix)
# 根据得到的候选区域以及对应的真实框,选择其中128或256个候选区域并给其分配类别标签
# 其中,背景类标签为num_classes
sampled_idxs, gt_classes = self._sample_proposals(
matched_idxs, matched_labels, targets_per_image.gt_classes
)
# 给采样的候选区域分配类别标签
proposals_per_image = proposals_per_image[sampled_idxs]
proposals_per_image.gt_classes = gt_classes
# 目前候选区域只分配标签"gt_classes", 但真实框还有一些标签例如masks, keypoints等。
# 所以遍历真实框标签的所有关键字,找到所有🕐"gt"开头并且在候选区域的标签中没有的,将这些标签添加到候选区域中
if has_gt:
# 选取的候选区域对应的真实框索引
sampled_targets = matched_idxs[sampled_idxs]
for (trg_name, trg_value) in targets_per_image.get_fields().items():
if trg_name.startswith("gt_") and not proposals_per_image.has(trg_name):
proposals_per_image.set(trg_name, trg_value[sampled_targets])
# 如果图片上没有真实的对象框,则候选区域的坐标全部设为0
else:
gt_boxes = Boxes(
targets_per_image.gt_boxes.tensor.new_zeros((len(sampled_idxs), 4))
)
proposals_per_image.gt_boxes = gt_boxes
proposals_with_gt.append(proposals_per_image)
return proposals_with_gt
代码源于detectron2