'Converts first stage prediction tensors from the RPN to proposals.
This function decodes the raw RPN predictions, runs non-max suppression
on the result.
Note that the behavior of this function is slightly modified during
training --- specifically, we stop the gradient from passing through the
proposal boxes and we only return a balanced sampled subset of proposals
with size `second_stage_batch_size`.
Args:
rpn_box_encodings_batch: A 3-D float32 tensor of shape
[batch_size, num_anchors, self._box_coder.code_size] containing
predicted proposal box encodings.
rpn_objectness_predictions_with_background_batch: A 3-D float tensor of
shape [batch_size, num_anchors, 2] containing objectness predictions
(logits) for each of the anchors with 0 corresponding to background
and 1 corresponding to object.
anchors: A 2-D tensor of shape [num_anchors, 4] representing anchors
for the first stage RPN.  Note that `num_anchors` can differ depending
on whether the model is created in training or inference mode.
image_shape: A 1-D tensor representing the input image shape.
Returns:
proposal_boxes: A float tensor with shape
[batch_size, max_num_proposals, 4] representing the (potentially zero
padded) proposal boxes for all images in the batch.  These boxes are
represented as normalized coordinates.
proposal_scores:  A float tensor with shape
[batch_size, max_num_proposals] representing the (potentially zero
padded) proposal objectness scores for all images in the batch.
num_proposals: A Tensor of type `int32`. A 1-D tensor of shape [batch]
representing the number of proposals predicted for each image in
the batch.'

def _postprocess_rpn(self, rpn_box_encodings_batch, rpn_objectness_predictions_with_background_batch, anchors, image_shape):

    rpn_box_encodings_batch = tf.expand_dims(rpn_box_encodings_batch, axis=2)
    rpn_encodings_shape = shape_utils.combined_static_and_dynamic_shape(rpn_box_encodings_batch)
    tiled_anchor_boxes = tf.tile(tf.expand_dims(anchors, 0), [rpn_encodings_shape[0], 1, 1])
    proposal_boxes = self._batch_decode_boxes(rpn_box_encodings_batch, tiled_anchor_boxes)
    proposal_boxes = tf.squeeze(proposal_boxes, axis=2)
    rpn_objectness_softmax_without_background = tf.nn.softmax(rpn_objectness_predictions_with_background_batch)[:, :, 1]
    clip_window = tf.to_float(tf.stack([0, 0, image_shape[1], image_shape[2]]))
    (proposal_boxes, proposal_scores, _, _, num_proposals) = post_processing.batch_multiclass_non_max_suppression(tf.expand_dims(proposal_boxes, axis=2), tf.expand_dims(rpn_objectness_softmax_without_background, axis=2), self._first_stage_nms_score_threshold, self._first_stage_nms_iou_threshold, self._first_stage_max_proposals, self._first_stage_max_proposals, clip_window=clip_window)
    if self._is_training:
        proposal_boxes = tf.stop_gradient(proposal_boxes)
        if (not self._hard_example_miner):
            (groundtruth_boxlists, groundtruth_classes_with_background_list) = self._format_groundtruth_data(image_shape)
            (proposal_boxes, proposal_scores, num_proposals) = self._unpad_proposals_and_sample_box_classifier_batch(proposal_boxes, proposal_scores, num_proposals, groundtruth_boxlists, groundtruth_classes_with_background_list)
    proposal_boxes_reshaped = tf.reshape(proposal_boxes, [(-1), 4])
    normalized_proposal_boxes_reshaped = box_list_ops.to_normalized_coordinates(box_list.BoxList(proposal_boxes_reshaped), image_shape[1], image_shape[2], check_range=False).get()
    proposal_boxes = tf.reshape(normalized_proposal_boxes_reshaped, [(-1), proposal_boxes.shape[1].value, 4])
    return (proposal_boxes, proposal_scores, num_proposals)
