<commit_before>import tensorflow as tf
import sonnet as snt
import numpy as np

from .utils.bbox_transform import bbox_transform, unmap
from .utils.bbox import bbox_overlaps


class RCNNTarget(snt.AbstractModule):
    """
    Generate RCNN target tensors for both probabilities and bounding boxes.

    TODO: We should unify this module with AnchorTarget.
    """
    def __init__(self, num_classes, name='rcnn_proposal'):
        super(RCNNTarget, self).__init__(name=name)
        self._num_classes = num_classes
        self._foreground_fraction = 0.25
        self._batch_size = 64
        self._foreground_threshold = 0.7
        self._background_threshold_high = 0.4
        self._background_threshold_low = 0.1

    def _build(self, proposals, scores, gt_boxes, im_shape):
        """
        Returns:
            TODO: Review implementetion with py-faster-rcnn ProposalTargetLayer
            TODO: It is possible to have two correct classes for a proposal?
        """
        (proposals_label, bbox_targets) = tf.py_func(
            self.proposal_target_layer,
            [proposals, scores, gt_boxes, im_shape],
            [tf.float32] * 2
         )

        return proposals_label, bbox_targets

    def proposal_target_layer(self, proposals, scores, gt_boxes, im_shape):
        """
        First we need to calculate the true class of proposals based on gt_boxes.
        """
        np.random.seed(0)  # TODO: For reproducibility.

        # Remove batch id from proposals
        all_proposals = proposals[:,1:]

        # only keep anchors inside the image
        # TODO: We should do this when anchors are original generated in
        # network or does it fuck with our dimensions.
        inds_inside = np.where(
            (all_proposals[:, 0] >= 0) &
            (all_proposals[:, 1] >= 0) &
            (all_proposals[:, 2] < im_shape[1]) &
            (all_proposals[:, 3] < im_shape[0])
        )[0]

        # keep only inside all_proposals
        proposals = all_proposals[inds_inside, :]

        overlaps = bbox_overlaps(
            # We need to use float and ascontiguousarray because of Cython
            # implementation of bbox_overlap
            np.ascontiguousarray(proposals, dtype=np.float),
            np.ascontiguousarray(gt_boxes, dtype=np.float)
        )

        # overlaps returns (num_proposals, num_gt_boxes) with the IoU of
        # proposal P and ground truth box G in overlaps[P, G]

        # We are going to label each proposal based on the IoU with `gt_boxes`.
        proposals_label = np.empty((proposals.shape[0], ), dtype=np.float32)
        proposals_label.fill(-1)

        # For each overlap there is three possible outcomes for labelling:
        #  if max(iou) < 0.1 then we ignore
        #  elif max(iou) <= 0.5 then we label background
        #  elif max(iou) > 0.5 then we label with the highest IoU in overlap
        max_overlaps = overlaps.max(axis=1)

        # Label background
        proposals_label[(max_overlaps > 0.1) & (max_overlaps <= 0.5)] = 0

        # Filter proposal that have labels
        overlaps_with_label = max_overlaps > 0.5
        # Get label for proposal with labels
        overlaps_best_label = overlaps.argmax(axis=1)
        # Having the index of the gt bbox with the best label we need to get the label for
        # each gt box and sum it one because 0 is used for background.
        # we only assign to proposals with `overlaps_with_label`.
        proposals_label[overlaps_with_label] = (gt_boxes[:,4][overlaps_best_label] + 1)[overlaps_with_label]

        # proposals_label now has [0, num_classes + 1] for proposals we are
        # going to use and -1 for the ones we should ignore.

        # Now we subsample labels and mark them as -1 in order to ignore them.
        # Our batch of N should be: F% foreground (label > 0)
        num_fg = int(self._foreground_fraction * self._batch_size)
        fg_inds = np.where(proposals_label >= 1)[0]
        if len(fg_inds) > num_fg:
            disable_inds = np.random.choice(
                fg_inds, size=(len(fg_inds) - num_fg), replace=False
            )
            # We disable them with their negatives just to be able to debug
            # down the road.
            proposals_label[disable_inds] = - proposals_label[disable_inds]

        if len(fg_inds) < num_fg:
            tf.logging.warning(
                'We\'ve got only {} foreground samples instead of {}.'.format(<commit_msg>Use recommended values for IoU in RCNN<commit_after>import tensorflow as tf
import sonnet as snt
import numpy as np

from .utils.bbox_transform import bbox_transform, unmap
from .utils.bbox import bbox_overlaps


class RCNNTarget(snt.AbstractModule):
    """
    Generate RCNN target tensors for both probabilities and bounding boxes.

    TODO: We should unify this module with AnchorTarget.
    """
    def __init__(self, num_classes, name='rcnn_proposal'):
        super(RCNNTarget, self).__init__(name=name)
        self._num_classes = num_classes
        self._foreground_fraction = 0.25
        self._batch_size = 64
        self._foreground_threshold = 0.5
        self._background_threshold_high = 0.5
        self._background_threshold_low = 0.1

    def _build(self, proposals, scores, gt_boxes, im_shape):
        """
        Returns:
            TODO: Review implementetion with py-faster-rcnn ProposalTargetLayer
            TODO: It is possible to have two correct classes for a proposal?
        """
        (proposals_label, bbox_targets) = tf.py_func(
            self.proposal_target_layer,
            [proposals, scores, gt_boxes, im_shape],
            [tf.float32] * 2
         )

        return proposals_label, bbox_targets

    def proposal_target_layer(self, proposals, scores, gt_boxes, im_shape):
        """
        First we need to calculate the true class of proposals based on gt_boxes.
        """
        np.random.seed(0)  # TODO: For reproducibility.

        # Remove batch id from proposals
        all_proposals = proposals[:,1:]

        # only keep anchors inside the image
        # TODO: We should do this when anchors are original generated in
        # network or does it fuck with our dimensions.
        inds_inside = np.where(
            (all_proposals[:, 0] >= 0) &
            (all_proposals[:, 1] >= 0) &
            (all_proposals[:, 2] < im_shape[1]) &
            (all_proposals[:, 3] < im_shape[0])
        )[0]

        # keep only inside all_proposals
        proposals = all_proposals[inds_inside, :]

        overlaps = bbox_overlaps(
            # We need to use float and ascontiguousarray because of Cython
            # implementation of bbox_overlap
            np.ascontiguousarray(proposals, dtype=np.float),
            np.ascontiguousarray(gt_boxes, dtype=np.float)
        )

        # overlaps returns (num_proposals, num_gt_boxes) with the IoU of
        # proposal P and ground truth box G in overlaps[P, G]

        # We are going to label each proposal based on the IoU with `gt_boxes`.
        # Start by filling the labels with -1, marking them as ignored.
        proposals_label = np.empty((proposals.shape[0], ), dtype=np.float32)
        proposals_label.fill(-1)

        # For each overlap there is three possible outcomes for labelling:
        #  if max(iou) < 0.1 then we ignore
        #  elif max(iou) <= 0.5 then we label background
        #  elif max(iou) > 0.5 then we label with the highest IoU in overlap
        max_overlaps = overlaps.max(axis=1)

        # Label background
        proposals_label[(max_overlaps > self._background_threshold_low) & (max_overlaps < self._background_threshold_high)] = 0

        # Filter proposal that have labels
        overlaps_with_label = max_overlaps >= self._foreground_threshold
        # Get label for proposal with labels
        overlaps_best_label = overlaps.argmax(axis=1)
        # Having the index of the gt bbox with the best label we need to get the label for
        # each gt box and sum it one because 0 is used for background.
        # we only assign to proposals with `overlaps_with_label`.
        proposals_label[overlaps_with_label] = (gt_boxes[:,4][overlaps_best_label] + 1)[overlaps_with_label]

        # proposals_label now has [0, num_classes + 1] for proposals we are
        # going to use and -1 for the ones we should ignore.

        # Now we subsample labels and mark them as -1 in order to ignore them.
        # Our batch of N should be: F% foreground (label > 0)
        num_fg = int(self._foreground_fraction * self._batch_size)
        fg_inds = np.where(proposals_label >= 1)[0]
        if len(fg_inds) > num_fg:
            disable_inds = np.random.choice(
                fg_inds, size=(len(fg_inds) - num_fg), replace=False
            )
            # We disable them with their negatives just to be able to debug
            # down the road.
            proposals_label[disable_inds] = - proposals_label[disable_inds]

        if len(fg_inds) < num_fg:
            tf.logging.warning(
                'We\'ve got only {} foreground samples instead of {}.'.format(
{'fraction_non_alphanumeric': 0.08043072189869245, 'fraction_numerical': 0.01131743764421492, 'mean_word_length': 2.395001864975755, 'pattern_counts': {'":': 0, '<': 15, '<?xml version=': 0, '>': 19, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 2, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '1637661', 'n_tokens_mistral': 2781, 'n_tokens_neox': 2687, 'n_words': 850}