一、简介
ProposalLayer层的主要作用:
①、对几十万个候选框过滤,按照前景得分进行排序,选出有前景概率较大的候选框。
②、利用得分值取几千个个得分高的候选框出来,同时利用之前得到的每个候选框的回归值进行微调,获得更加准确的侯选位置。
③、通过NMS(非极大抑制)进行再一次过滤,筛选出较为准确包含物体的候选框。
二、代码解读
Proposal代码以及注释
class ProposalLayer(KE.Layer):
def __init__(self, proposal_count, nms_threshold, anchors,
config=None, **kwargs):
"""
anchors: [N, (y1, x1, y2, x2)] anchors defined in image coordinates
"""
super(ProposalLayer, self).__init__(**kwargs)
self.config = config
self.proposal_count = proposal_count
self.nms_threshold = nms_threshold
self.anchors = anchors.astype(np.float32)
def call(self, inputs):
#先拿出RPN得分结果
scores = inputs[0][:, :, 1]
#得到偏移量
deltas = inputs[1]
deltas = deltas * np.reshape(self.config.RPN_BBOX_STD_DEV, [1, 1, 4])
#得到候选框
anchors = self.anchors
#判断保留多少个候选框,自己指定
pre_nms_limit = min(8000, self.anchors.shape[0])
ix = tf.nn.top_k(scores, pre_nms_limit, sorted=True,
name="top_anchors").indices
#拿到这几千个候选框得分值
scores = utils.batch_slice([scores, ix], lambda x, y: tf.gather(x, y),
self.config.IMAGES_PER_GPU)
#拿到候选框回归指标
deltas = utils.batch_slice([deltas, ix], lambda x, y: tf.gather(x, y),
self.config.IMAGES_PER_GPU)
anchors = utils.batch_slice(ix, lambda x: tf.gather(anchors, x),
self.config.IMAGES_PER_GPU,
names=["pre_nms_anchors"])
# 进行边框的微调
boxes = utils.batch_slice([anchors, deltas],
lambda x, y: apply_box_deltas_graph(x, y),
self.config.IMAGES_PER_GPU,
names=["refined_anchors"])
#删除越界的框部分,window为固定的框,重新修剪,得到不越界的那一部分。
height, width = self.config.IMAGE_SHAPE[:2]
window = np.array([0, 0, height, width]).astype(np.float32)
boxes = utils.batch_slice(boxes,
lambda x: clip_boxes_graph(x, window),
self.config.IMAGES_PER_GPU,
names=["refined_anchors_clipped"])
#进行归一化,最终比较的是归一化的结果
normalized_boxes = boxes / np.array([[height, width, height, width]])
#非极大值抑制,看边界框的重叠比例,若重叠比例大于某一个阈值,就删除一个。
def nms(normalized_boxes, scores):
#tf内置的NMS函数。
indices = tf.image.non_max_suppression(
normalized_boxes, scores, self.proposal_count,
self.nms_threshold, name="rpn_non_max_suppression")
proposals = tf.gather(normalized_boxes, indices)
padding = tf.maximum(self.proposal_count - tf.shape(proposals)[0], 0)
proposals = tf.pad(proposals, [(0, padding), (0, 0)])
return proposals
proposals = utils.batch_slice([normalized_boxes, scores], nms,
self.config.IMAGES_PER_GPU)
#返回结果候选框
return proposals
进行微调的代码
def apply_box_deltas_graph(boxes, deltas):
#传入候选框和偏移量
#得到候选框长宽和中心点的坐标
height = boxes[:, 2] - boxes[:, 0]
width = boxes[:, 3] - boxes[:, 1]
center_y = boxes[:, 0] + 0.5 * height
center_x = boxes[:, 1] + 0.5 * width
#执行微调,按照传入的偏移量进行微调
center_y += deltas[:, 0] * height
center_x += deltas[:, 1] * width
height *= tf.exp(deltas[:, 2])
width *= tf.exp(deltas[:, 3])
#把坐标点进行还原
y1 = center_y - 0.5 * height
x1 = center_x - 0.5 * width
y2 = y1 + height
x2 = x1 + width
result = tf.stack([y1, x1, y2, x2], axis=1, name="apply_box_deltas_out")
#返回微调后的结果
return result