import os
import sys
sys.path.append(os.getcwd() + "/facerecognition/PyFaceRecClient/simple-faster-rcnn-pytorch/")

import torchvision
import torch
import numpy as np
from models.utils.bbox_tools import bbox2loc, bbox_iou, loc2bbox
from models.utils.nms import non_maximum_suppression

'''
class ProposalTargetCreator(object):
    """Assign ground truth bounding boxes to given RoIs.

    The :meth:`__call__` of this class generates training targets
    for each object proposal.
    This is used to train Faster RCNN [#]_.

    .. [#] <NAME>, <NAME>, <NAME>, <NAME>. \
    Faster R-CNN: Towards Real-Time Object Detection with \
    Region Proposal Networks. NIPS 2015.

    Args: #
        n_sample (int): The number of sampled regions.
        pos_ratio (float): Fraction of regions that is labeled as a
            foreground.
        pos_iou_thresh (float): IoU threshold for a RoI to be considered as a
            foreground.
        neg_iou_thresh_hi (float): RoI is considered to be the background
            if IoU is in
            [:obj:`neg_iou_thresh_hi`, :obj:`neg_iou_thresh_hi`).
        neg_iou_thresh_lo (float): See above.
    """

    
    # [:obj:`neg_iou_thresh_hi`, :obj:`neg_iou_thresh_hi`) = [0.0, 0.5)
    # so regions are classified into two groups:
    # label with 1 = foreground(pos)
    # label with -1 = background(neg)
    def __init__(self,
                 n_sample=128,
                 pos_ratio=0.25, pos_iou_thresh=0.5,
                 neg_iou_thresh_hi=0.5, neg_iou_thresh_lo=0.0
                 ):
        self.n_sample = n_sample
        
        # I don't see a reason why pos_ratio is needed.
        # -> yes, we are using only (default)25% of the sampled foreground.
        self.pos_ratio = pos_ratio
        self.pos_iou_thresh = pos_iou_thresh
        self.neg_iou_thresh_hi = neg_iou_thresh_hi
        self.neg_iou_thresh_lo = neg_iou_thresh_lo  # NOTE:default 0.1 in py-faster-rcnn

    def __call__(self, roi, bbox, label,
                 loc_normalize_mean=(0., 0., 0., 0.),
                 loc_normalize_std=(0.1, 0.1, 0.2, 0.2)):
        """Assigns ground truth to sampled proposals.

        This function samples total of :obj:`self.n_sample` RoIs
        from the combination of :obj:`roi` and :obj:`bbox`.
        The RoIs are assigned with the ground truth class labels as well as
        bounding box offsets and scales to match the ground truth bounding
        boxes. As many as :obj:`pos_ratio * self.n_sample` RoIs are
        sampled as foregrounds.

        Offsets and scales of bounding boxes are calculated using
        :func:`model.utils.bbox_tools.bbox2loc`.
        Also, types of input arrays and output arrays are same.

        Here are notations.

        * :math:`S` is the total number of sampled RoIs, which equals \
            :obj:`self.n_sample`.
        * :math:`L` is number of object classes possibly including the \
            background.

        Args:
            roi (array): Region of Interests (RoIs) from which we sample.
                Its shape is :math:`(R, 4)`
            bbox (array): The coordinates of ground truth bounding boxes.
                Its shape is :math:`(R', 4)`.
            label (array): Ground truth bounding box labels. Its shape
                is :math:`(R',)`. Its range is :math:`[0, L - 1]`, where
                :math:`L` is the number of foreground classes.
            loc_normalize_mean (tuple of four floats): Mean values to normalize
                coordinates of bouding boxes.
            loc_normalize_std (tupler of four floats): Standard deviation of
                the coordinates of bounding boxes.

        Returns:
            (array, array, array):

            * **sample_roi**: Regions of interests that are sampled. \
                Its shape is :math:`(S, 4)`.
            * **gt_roi_loc**: Offsets and scales to match \
                the sampled RoIs to the ground truth bounding boxes. \
                Its shape is :math:`(S, 4)`.
            * **gt_roi_label**: Labels assigned to sampled RoIs. Its shape is \
                :math:`(S,)`. Its range is :math:`[0, L]`. The label with \
                value 0 is the background.

        """
        # the number of ground truth bounding boxes
        n_bbox, _ = bbox.shape

        # put everything together(axis = 0). roi and (GT) bounding boxes
        roi = np.concatenate((roi, bbox), axis=0)

        pos_roi_per_image = np.round(self.n_sample * self.pos_ratio)
        iou = bbox_iou(roi, bbox)
        gt_assignment = iou.argmax(axis=1)
        max_iou = iou.max(axis=1)
        # Offset range of classes from [0, n_fg_class - 1] to [1, n_fg_class].
        # The label with value 0 is the background.
        gt_roi_label = label[gt_assignment] + 1

        # Select foreground RoIs as those with >= pos_iou_thresh IoU.
        pos_index = np.where(max_iou >= self.pos_iou_thresh)[0]
        pos_roi_per_this_image = int(min(pos_roi_per_image, pos_index.size))
        if pos_index.size > 0:
            pos_index = np.random.choice(
                pos_index, size=pos_roi_per_this_image, replace=False)

        # Select background RoIs as those within
        # [neg_iou_thresh_lo, neg_iou_thresh_hi).
        neg_index = np.where((max_iou < self.neg_iou_thresh_hi) &
                             (max_iou >= self.neg_iou_thresh_lo))[0]
        neg_roi_per_this_image = self.n_sample - pos_roi_per_this_image
        neg_roi_per_this_image = int(min(neg_roi_per_this_image,
                                         neg_index.size))
        if neg_index.size > 0:
            neg_index = np.random.choice(
                neg_index, size=neg_roi_per_this_image, replace=False)

        # The indices that we're selecting (both positive and negative).
        keep_index = np.append(pos_index, neg_index)
        gt_roi_label = gt_roi_label[keep_index]
        gt_roi_label[pos_roi_per_this_image:] = 0  # negative labels --> 0
        sample_roi = roi[keep_index]

        # Compute offsets and scales to match sampled RoIs to the GTs.
        gt_roi_loc = bbox2loc(sample_roi, bbox[gt_assignment[keep_index]])
        gt_roi_loc = ((gt_roi_loc - np.array(loc_normalize_mean, np.float32)
                       ) / np.array(loc_normalize_std, np.float32))

        return sample_roi, gt_roi_loc, gt_roi_label
'''




class ProposalCreator:
    """Proposal regions are generated by calling this object.

    The :meth:`__call__` of this object outputs object detection proposals by
    applying estimated bounding box offsets
    to a set of anchors.

    This class takes parameters to control number of bounding boxes to
    pass to NMS and keep after NMS.
    If the paramters are negative, it uses all the bounding boxes supplied
    or keep all the bounding boxes returned by NMS.

    This class is used for Region Proposal Networks introduced in
    Faster R-CNN [#]_.

    .. [#] <NAME>, <NAME>, <NAME>, <NAME>. \
    Faster R-CNN: Towards Real-Time Object Detection with \
    Region Proposal Networks. NIPS 2015.

    Args:
        nms_thresh (float): Threshold value used when calling NMS.
        n_train_pre_nms (int): Number of top scored bounding boxes
            to keep before passing to NMS in train mode.
        n_train_post_nms (int): Number of top scored bounding boxes
            to keep after passing to NMS in train mode.
        n_test_pre_nms (int): Number of top scored bounding boxes
            to keep before passing to NMS in test mode.
        n_test_post_nms (int): Number of top scored bounding boxes
            to keep after passing to NMS in test mode.
        force_cpu_nms (bool): If this is :obj:`True`,
            always use NMS in CPU mode. If :obj:`False`,
            the NMS mode is selected based on the type of inputs.
        min_size (int): A paramter to determine the threshold on
            discarding bounding boxes based on their sizes.
    """

    def __init__(self,
                 parent_model,
                 nms_thresh=0.5,
                 n_train_pre_nms=12000,
                 n_train_post_nms=300,
                 n_test_pre_nms=6000,
                 n_test_post_nms=300,
                 min_size=16
                 ):
        self.parent_model = parent_model # NOTE: check this how it looks
        self.nms_thresh = nms_thresh
        self.n_train_pre_nms = n_train_pre_nms
        self.n_train_post_nms = n_train_post_nms
        self.n_test_pre_nms = n_test_pre_nms
        self.n_test_post_nms = n_test_post_nms
        self.min_size = min_size

    def __call__(self, loc, score,
                 anchor, img_size, scale=1.):
        """
        input should be ndarray Propose RoIs.

        Inputs :obj:`loc, score, anchor` refer to the same anchor when indexed
        by the same index.

        On notations, :math:`R` is the total number of anchors. This is equal
        to product of the height and the width of an image and the number of
        anchor bases per pixel.

        Type of the output is same as the inputs.

        Args:
            loc (array): Predicted offsets and scaling to anchors.
                Its shape is :math:`(R, 4)`.
            score (array): Predicted foreground probability for anchors.
                Its shape is :math:`(R,)`.
            anchor (array): Coordinates of anchors. Its shape is
                :math:`(R, 4)`.
            img_size (tuple of ints): A tuple :obj:`height, width`,
                which contains image size after scaling.
            scale (float): The scaling factor used to scale an image after
                reading it from a file.

        Returns:
            array:
            An array of coordinates of proposal boxes.
            Its shape is :math:`(S, 4)`. :math:`S` is less than
            :obj:`self.n_test_post_nms` in test time and less than
            :obj:`self.n_train_post_nms` in train time. :math:`S` depends on
            the size of the predicted bounding boxes and the number of
            bounding boxes discarded by NMS.
        """
        # NOTE: when test, remember
        # faster_rcnn.eval()
        # to set self.traing = False
        if self.parent_model.training:
            n_pre_nms = self.n_train_pre_nms
            n_post_nms = self.n_train_post_nms
        else:
            n_pre_nms = self.n_test_pre_nms
            n_post_nms = self.n_test_post_nms

        # Convert anchors into proposal via bbox transformations.
        # roi = loc2bbox(anchor, loc)
        # anchor: all the anchor bases at all pixels
        # loc: all the location infos at all pixels
        roi = loc2bbox(anchor, loc)
        # Now roi has all the roi candidates!
        

        # np.clip() clips predicted boxes to image.
        # this truncates all the predicted boxes that exceeds the
        # boundary of the original image.
        #
        # slice(start, end, step)

        # clip y coordinates 
        roi[:, slice(0, 4, 2)] = np.clip(
            roi[:, slice(0, 4, 2)], 0, img_size[0])
        # clip x coordinates
        roi[:, slice(1, 4, 2)] = np.clip(
            roi[:, slice(1, 4, 2)], 0, img_size[1])

        # Remove predicted boxes with either height or width < threshold.
        min_size = self.min_size * scale
        hs = roi[:, 2] - roi[:, 0]
        ws = roi[:, 3] - roi[:, 1]
        keep = np.where((hs >= min_size) & (ws >= min_size))[0]
        roi = roi[keep, :]
        score = score[keep]

        # Sort all (proposal, score) pairs by score from highest to lowest.
        # Take top pre_nms_topN (e.g. 6000).
        # ravel() returns a contiguous array.
        order = score.ravel().argsort()[::-1]
        if n_pre_nms > 0:
            order = order[:n_pre_nms]
        roi = roi[order, :]
        score = score[order]
        

        # Apply nms (e.g. threshold = 0.7).
        # Take after_nms_topN (e.g. 300).

        # unNOTE: somthing is wrong here!
        # TODO: remove cuda.to_gpu
        keep = torchvision.ops.nms(torch.from_numpy(roi), torch.from_numpy(score), 0.6)
        '''
        keep = non_maximum_suppression(
            np.ascontiguousarray(np.asarray(roi)),
            threshold=0.5)
        '''
        # remember that keep is tuple: (indices, indices_num)
        if n_post_nms > 0 and keep[1] > n_post_nms:
            keep = keep[0][:n_post_nms]
        roi = roi[keep, :]
        return roi
import os
import sys
sys.path.append(os.getcwd() + "/facerecognition/PyFaceRecClient/simple-faster-rcnn-pytorch/")

import torchvision
import torch
import numpy as np
from models.utils.bbox_tools import bbox2loc, bbox_iou, loc2bbox
from models.utils.nms import non_maximum_suppression

'''
class ProposalTargetCreator(object):
    """Assign ground truth bounding boxes to given RoIs.

    The :meth:`__call__` of this class generates training targets
    for each object proposal.
    This is used to train Faster RCNN [#]_.

    .. [#] <NAME>, <NAME>, <NAME>, <NAME>. \
    Faster R-CNN: Towards Real-Time Object Detection with \
    Region Proposal Networks. NIPS 2015.

    Args: #
        n_sample (int): The number of sampled regions.
        pos_ratio (float): Fraction of regions that is labeled as a
            foreground.
        pos_iou_thresh (float): IoU threshold for a RoI to be considered as a
            foreground.
        neg_iou_thresh_hi (float): RoI is considered to be the background
            if IoU is in
            [:obj:`neg_iou_thresh_hi`, :obj:`neg_iou_thresh_hi`).
        neg_iou_thresh_lo (float): See above.
    """

    
    # [:obj:`neg_iou_thresh_hi`, :obj:`neg_iou_thresh_hi`) = [0.0, 0.5)
    # so regions are classified into two groups:
    # label with 1 = foreground(pos)
    # label with -1 = background(neg)
    def __init__(self,
                 n_sample=128,
                 pos_ratio=0.25, pos_iou_thresh=0.5,
                 neg_iou_thresh_hi=0.5, neg_iou_thresh_lo=0.0
                 ):
        self.n_sample = n_sample
        
        # I don't see a reason why pos_ratio is needed.
        # -> yes, we are using only (default)25% of the sampled foreground.
        self.pos_ratio = pos_ratio
        self.pos_iou_thresh = pos_iou_thresh
        self.neg_iou_thresh_hi = neg_iou_thresh_hi
        self.neg_iou_thresh_lo = neg_iou_thresh_lo  # NOTE:default 0.1 in py-faster-rcnn

    def __call__(self, roi, bbox, label,
                 loc_normalize_mean=(0., 0., 0., 0.),
                 loc_normalize_std=(0.1, 0.1, 0.2, 0.2)):
        """Assigns ground truth to sampled proposals.

        This function samples total of :obj:`self.n_sample` RoIs
        from the combination of :obj:`roi` and :obj:`bbox`.
        The RoIs are assigned with the ground truth class labels as well as
        bounding box offsets and scales to match the ground truth bounding
        boxes. As many as :obj:`pos_ratio * self.n_sample` RoIs are
        sampled as foregrounds.

        Offsets and scales of bounding boxes are calculated using
        :func:`model.utils.bbox_tools.bbox2loc`.
        Also, types of input arrays and output arrays are same.

        Here are notations.

        * :math:`S` is the total number of sampled RoIs, which equals \
            :obj:`self.n_sample`.
        * :math:`L` is number of object classes possibly including the \
            background.

        Args:
            roi (array): Region of Interests (RoIs) from which we sample.
                Its shape is :math:`(R, 4)`
            bbox (array): The coordinates of ground truth bounding boxes.
                Its shape is :math:`(R', 4)`.
            label (array): Ground truth bounding box labels. Its shape
                is :math:`(R',)`. Its range is :math:`[0, L - 1]`, where
                :math:`L` is the number of foreground classes.
            loc_normalize_mean (tuple of four floats): Mean values to normalize
                coordinates of bouding boxes.
            loc_normalize_std (tupler of four floats): Standard deviation of
                the coordinates of bounding boxes.

        Returns:
            (array, array, array):

            * **sample_roi**: Regions of interests that are sampled. \
                Its shape is :math:`(S, 4)`.
            * **gt_roi_loc**: Offsets and scales to match \
                the sampled RoIs to the ground truth bounding boxes. \
                Its shape is :math:`(S, 4)`.
            * **gt_roi_label**: Labels assigned to sampled RoIs. Its shape is \
                :math:`(S,)`. Its range is :math:`[0, L]`. The label with \
                value 0 is the background.

        """
        # the number of ground truth bounding boxes
        n_bbox, _ = bbox.shape

        # put everything together(axis = 0). roi and (GT) bounding boxes
        roi = np.concatenate((roi, bbox), axis=0)

        pos_roi_per_image = np.round(self.n_sample * self.pos_ratio)
        iou = bbox_iou(roi, bbox)
        gt_assignment = iou.argmax(axis=1)
        max_iou = iou.max(axis=1)
        # Offset range of classes from [0, n_fg_class - 1] to [1, n_fg_class].
        # The label with value 0 is the background.
        gt_roi_label = label[gt_assignment] + 1

        # Select foreground RoIs as those with >= pos_iou_thresh IoU.
        pos_index = np.where(max_iou >= self.pos_iou_thresh)[0]
        pos_roi_per_this_image = int(min(pos_roi_per_image, pos_index.size))
        if pos_index.size > 0:
            pos_index = np.random.choice(
                pos_index, size=pos_roi_per_this_image, replace=False)

        # Select background RoIs as those within
        # [neg_iou_thresh_lo, neg_iou_thresh_hi).
        neg_index = np.where((max_iou < self.neg_iou_thresh_hi) &
                             (max_iou >= self.neg_iou_thresh_lo))[0]
        neg_roi_per_this_image = self.n_sample - pos_roi_per_this_image
        neg_roi_per_this_image = int(min(neg_roi_per_this_image,
                                         neg_index.size))
        if neg_index.size > 0:
            neg_index = np.random.choice(
                neg_index, size=neg_roi_per_this_image, replace=False)

        # The indices that we're selecting (both positive and negative).
        keep_index = np.append(pos_index, neg_index)
        gt_roi_label = gt_roi_label[keep_index]
        gt_roi_label[pos_roi_per_this_image:] = 0  # negative labels --> 0
        sample_roi = roi[keep_index]

        # Compute offsets and scales to match sampled RoIs to the GTs.
        gt_roi_loc = bbox2loc(sample_roi, bbox[gt_assignment[keep_index]])
        gt_roi_loc = ((gt_roi_loc - np.array(loc_normalize_mean, np.float32)
                       ) / np.array(loc_normalize_std, np.float32))

        return sample_roi, gt_roi_loc, gt_roi_label
'''




class ProposalCreator:
    """Proposal regions are generated by calling this object.

    The :meth:`__call__` of this object outputs object detection proposals by
    applying estimated bounding box offsets
    to a set of anchors.

    This class takes parameters to control number of bounding boxes to
    pass to NMS and keep after NMS.
    If the paramters are negative, it uses all the bounding boxes supplied
    or keep all the bounding boxes returned by NMS.

    This class is used for Region Proposal Networks introduced in
    Faster R-CNN [#]_.

    .. [#] <NAME>, <NAME>, <NAME>, <NAME>. \
    Faster R-CNN: Towards Real-Time Object Detection with \
    Region Proposal Networks. NIPS 2015.

    Args:
        nms_thresh (float): Threshold value used when calling NMS.
        n_train_pre_nms (int): Number of top scored bounding boxes
            to keep before passing to NMS in train mode.
        n_train_post_nms (int): Number of top scored bounding boxes
            to keep after passing to NMS in train mode.
        n_test_pre_nms (int): Number of top scored bounding boxes
            to keep before passing to NMS in test mode.
        n_test_post_nms (int): Number of top scored bounding boxes
            to keep after passing to NMS in test mode.
        force_cpu_nms (bool): If this is :obj:`True`,
            always use NMS in CPU mode. If :obj:`False`,
            the NMS mode is selected based on the type of inputs.
        min_size (int): A paramter to determine the threshold on
            discarding bounding boxes based on their sizes.
    """

    def __init__(self,
                 parent_model,
                 nms_thresh=0.5,
                 n_train_pre_nms=12000,
                 n_train_post_nms=300,
                 n_test_pre_nms=6000,
                 n_test_post_nms=300,
                 min_size=16
                 ):
        self.parent_model = parent_model # NOTE: check this how it looks
        self.nms_thresh = nms_thresh
        self.n_train_pre_nms = n_train_pre_nms
        self.n_train_post_nms = n_train_post_nms
        self.n_test_pre_nms = n_test_pre_nms
        self.n_test_post_nms = n_test_post_nms
        self.min_size = min_size

    def __call__(self, loc, score,
                 anchor, img_size, scale=1.):
        """
        input should be ndarray Propose RoIs.

        Inputs :obj:`loc, score, anchor` refer to the same anchor when indexed
        by the same index.

        On notations, :math:`R` is the total number of anchors. This is equal
        to product of the height and the width of an image and the number of
        anchor bases per pixel.

        Type of the output is same as the inputs.

        Args:
            loc (array): Predicted offsets and scaling to anchors.
                Its shape is :math:`(R, 4)`.
            score (array): Predicted foreground probability for anchors.
                Its shape is :math:`(R,)`.
            anchor (array): Coordinates of anchors. Its shape is
                :math:`(R, 4)`.
            img_size (tuple of ints): A tuple :obj:`height, width`,
                which contains image size after scaling.
            scale (float): The scaling factor used to scale an image after
                reading it from a file.

        Returns:
            array:
            An array of coordinates of proposal boxes.
            Its shape is :math:`(S, 4)`. :math:`S` is less than
            :obj:`self.n_test_post_nms` in test time and less than
            :obj:`self.n_train_post_nms` in train time. :math:`S` depends on
            the size of the predicted bounding boxes and the number of
            bounding boxes discarded by NMS.
        """
        # NOTE: when test, remember
        # faster_rcnn.eval()
        # to set self.traing = False
        if self.parent_model.training:
            n_pre_nms = self.n_train_pre_nms
            n_post_nms = self.n_train_post_nms
        else:
            n_pre_nms = self.n_test_pre_nms
            n_post_nms = self.n_test_post_nms

        # Convert anchors into proposal via bbox transformations.
        # roi = loc2bbox(anchor, loc)
        # anchor: all the anchor bases at all pixels
        # loc: all the location infos at all pixels
        roi = loc2bbox(anchor, loc)
        # Now roi has all the roi candidates!
        

        # np.clip() clips predicted boxes to image.
        # this truncates all the predicted boxes that exceeds the
        # boundary of the original image.
        #
        # slice(start, end, step)

        # clip y coordinates 
        roi[:, slice(0, 4, 2)] = np.clip(
            roi[:, slice(0, 4, 2)], 0, img_size[0])
        # clip x coordinates
        roi[:, slice(1, 4, 2)] = np.clip(
            roi[:, slice(1, 4, 2)], 0, img_size[1])

        # Remove predicted boxes with either height or width < threshold.
        min_size = self.min_size * scale
        hs = roi[:, 2] - roi[:, 0]
        ws = roi[:, 3] - roi[:, 1]
        keep = np.where((hs >= min_size) & (ws >= min_size))[0]
        roi = roi[keep, :]
        score = score[keep]

        # Sort all (proposal, score) pairs by score from highest to lowest.
        # Take top pre_nms_topN (e.g. 6000).
        # ravel() returns a contiguous array.
        order = score.ravel().argsort()[::-1]
        if n_pre_nms > 0:
            order = order[:n_pre_nms]
        roi = roi[order, :]
        score = score[order]
        

        # Apply nms (e.g. threshold = 0.7).
        # Take after_nms_topN (e.g. 300).

        # unNOTE: somthing is wrong here!
        # TODO: remove cuda.to_gpu
        keep = torchvision.ops.nms(torch.from_numpy(roi), torch.from_numpy(score), 0.6)
        '''
        keep = non_maximum_suppression(
            np.ascontiguousarray(np.asarray(roi)),
            threshold=0.5)
        '''
        # remember that keep is tuple: (indices, indices_num)
        if n_post_nms > 0 and keep[1] > n_post_nms:
            keep = keep[0][:n_post_nms]
        roi = roi[keep, :]
        return roi
class ProposalTargetCreator(object):     """Assign ground truth bounding boxes to given RoIs.      The :meth:`__call__` of this class generates training targets     for each object proposal.     This is used to train Faster RCNN [#]_.      .. [#] <NAME>, <NAME>, <NAME>, <NAME>. \     Faster R-CNN: Towards Real-Time Object Detection with \     Region Proposal Networks. NIPS 2015.      Args: #         n_sample (int): The number of sampled regions.         pos_ratio (float): Fraction of regions that is labeled as a             foreground.         pos_iou_thresh (float): IoU threshold for a RoI to be considered as a             foreground.         neg_iou_thresh_hi (float): RoI is considered to be the background             if IoU is in             [:obj:`neg_iou_thresh_hi`, :obj:`neg_iou_thresh_hi`).         neg_iou_thresh_lo (float): See above.     """           # [:obj:`neg_iou_thresh_hi`, :obj:`neg_iou_thresh_hi`) = [0.0, 0.5)     # so regions are classified into two groups:     # label with 1 = foreground(pos)     # label with -1 = background(neg)     def __init__(self,                  n_sample=128,                  pos_ratio=0.25, pos_iou_thresh=0.5,                  neg_iou_thresh_hi=0.5, neg_iou_thresh_lo=0.0                  ):         self.n_sample = n_sample                  # I don't see a reason why pos_ratio is needed.         # -> yes, we are using only (default)25% of the sampled foreground.         self.pos_ratio = pos_ratio         self.pos_iou_thresh = pos_iou_thresh         self.neg_iou_thresh_hi = neg_iou_thresh_hi         self.neg_iou_thresh_lo = neg_iou_thresh_lo  # NOTE:default 0.1 in py-faster-rcnn      def __call__(self, roi, bbox, label,                  loc_normalize_mean=(0., 0., 0., 0.),                  loc_normalize_std=(0.1, 0.1, 0.2, 0.2)):         """Assigns ground truth to sampled proposals.          This function samples total of :obj:`self.n_sample` RoIs         from the combination of :obj:`roi` and :obj:`bbox`.         The RoIs are assigned with the ground truth class labels as well as         bounding box offsets and scales to match the ground truth bounding         boxes. As many as :obj:`pos_ratio * self.n_sample` RoIs are         sampled as foregrounds.          Offsets and scales of bounding boxes are calculated using         :func:`model.utils.bbox_tools.bbox2loc`.         Also, types of input arrays and output arrays are same.          Here are notations.          * :math:`S` is the total number of sampled RoIs, which equals \             :obj:`self.n_sample`.         * :math:`L` is number of object classes possibly including the \             background.          Args:             roi (array): Region of Interests (RoIs) from which we sample.                 Its shape is :math:`(R, 4)`             bbox (array): The coordinates of ground truth bounding boxes.                 Its shape is :math:`(R', 4)`.             label (array): Ground truth bounding box labels. Its shape                 is :math:`(R',)`. Its range is :math:`[0, L - 1]`, where                 :math:`L` is the number of foreground classes.             loc_normalize_mean (tuple of four floats): Mean values to normalize                 coordinates of bouding boxes.             loc_normalize_std (tupler of four floats): Standard deviation of                 the coordinates of bounding boxes.          Returns:             (array, array, array):              * **sample_roi**: Regions of interests that are sampled. \                 Its shape is :math:`(S, 4)`.             * **gt_roi_loc**: Offsets and scales to match \                 the sampled RoIs to the ground truth bounding boxes. \                 Its shape is :math:`(S, 4)`.             * **gt_roi_label**: Labels assigned to sampled RoIs. Its shape is \                 :math:`(S,)`. Its range is :math:`[0, L]`. The label with \                 value 0 is the background.          """         # the number of ground truth bounding boxes         n_bbox, _ = bbox.shape          # put everything together(axis = 0). roi and (GT) bounding boxes         roi = np.concatenate((roi, bbox), axis=0)          pos_roi_per_image = np.round(self.n_sample * self.pos_ratio)         iou = bbox_iou(roi, bbox)         gt_assignment = iou.argmax(axis=1)         max_iou = iou.max(axis=1)         # Offset range of classes from [0, n_fg_class - 1] to [1, n_fg_class].         # The label with value 0 is the background.         gt_roi_label = label[gt_assignment] + 1          # Select foreground RoIs as those with >= pos_iou_thresh IoU.         pos_index = np.where(max_iou >= self.pos_iou_thresh)[0]         pos_roi_per_this_image = int(min(pos_roi_per_image, pos_index.size))         if pos_index.size > 0:             pos_index = np.random.choice(                 pos_index, size=pos_roi_per_this_image, replace=False)          # Select background RoIs as those within         # [neg_iou_thresh_lo, neg_iou_thresh_hi).         neg_index = np.where((max_iou < self.neg_iou_thresh_hi) &                              (max_iou >= self.neg_iou_thresh_lo))[0]         neg_roi_per_this_image = self.n_sample - pos_roi_per_this_image         neg_roi_per_this_image = int(min(neg_roi_per_this_image,                                          neg_index.size))         if neg_index.size > 0:             neg_index = np.random.choice(                 neg_index, size=neg_roi_per_this_image, replace=False)          # The indices that we're selecting (both positive and negative).         keep_index = np.append(pos_index, neg_index)         gt_roi_label = gt_roi_label[keep_index]         gt_roi_label[pos_roi_per_this_image:] = 0  # negative labels --> 0         sample_roi = roi[keep_index]          # Compute offsets and scales to match sampled RoIs to the GTs.         gt_roi_loc = bbox2loc(sample_roi, bbox[gt_assignment[keep_index]])         gt_roi_loc = ((gt_roi_loc - np.array(loc_normalize_mean, np.float32)                        ) / np.array(loc_normalize_std, np.float32))          return sample_roi, gt_roi_loc, gt_roi_label Proposal regions are generated by calling this object.      The :meth:`__call__` of this object outputs object detection proposals by     applying estimated bounding box offsets     to a set of anchors.      This class takes parameters to control number of bounding boxes to     pass to NMS and keep after NMS.     If the paramters are negative, it uses all the bounding boxes supplied     or keep all the bounding boxes returned by NMS.      This class is used for Region Proposal Networks introduced in     Faster R-CNN [#]_.      .. [#] <NAME>, <NAME>, <NAME>, <NAME>. \     Faster R-CNN: Towards Real-Time Object Detection with \     Region Proposal Networks. NIPS 2015.      Args:         nms_thresh (float): Threshold value used when calling NMS.         n_train_pre_nms (int): Number of top scored bounding boxes             to keep before passing to NMS in train mode.         n_train_post_nms (int): Number of top scored bounding boxes             to keep after passing to NMS in train mode.         n_test_pre_nms (int): Number of top scored bounding boxes             to keep before passing to NMS in test mode.         n_test_post_nms (int): Number of top scored bounding boxes             to keep after passing to NMS in test mode.         force_cpu_nms (bool): If this is :obj:`True`,             always use NMS in CPU mode. If :obj:`False`,             the NMS mode is selected based on the type of inputs.         min_size (int): A paramter to determine the threshold on             discarding bounding boxes based on their sizes. # NOTE: check this how it looks input should be ndarray Propose RoIs.          Inputs :obj:`loc, score, anchor` refer to the same anchor when indexed         by the same index.          On notations, :math:`R` is the total number of anchors. This is equal         to product of the height and the width of an image and the number of         anchor bases per pixel.          Type of the output is same as the inputs.          Args:             loc (array): Predicted offsets and scaling to anchors.                 Its shape is :math:`(R, 4)`.             score (array): Predicted foreground probability for anchors.                 Its shape is :math:`(R,)`.             anchor (array): Coordinates of anchors. Its shape is                 :math:`(R, 4)`.             img_size (tuple of ints): A tuple :obj:`height, width`,                 which contains image size after scaling.             scale (float): The scaling factor used to scale an image after                 reading it from a file.          Returns:             array:             An array of coordinates of proposal boxes.             Its shape is :math:`(S, 4)`. :math:`S` is less than             :obj:`self.n_test_post_nms` in test time and less than             :obj:`self.n_train_post_nms` in train time. :math:`S` depends on             the size of the predicted bounding boxes and the number of             bounding boxes discarded by NMS. # NOTE: when test, remember # faster_rcnn.eval() # to set self.traing = False # Convert anchors into proposal via bbox transformations. # roi = loc2bbox(anchor, loc) # anchor: all the anchor bases at all pixels # loc: all the location infos at all pixels # Now roi has all the roi candidates! # np.clip() clips predicted boxes to image. # this truncates all the predicted boxes that exceeds the # boundary of the original image. # # slice(start, end, step) # clip y coordinates # clip x coordinates # Remove predicted boxes with either height or width < threshold. # Sort all (proposal, score) pairs by score from highest to lowest. # Take top pre_nms_topN (e.g. 6000). # ravel() returns a contiguous array. # Apply nms (e.g. threshold = 0.7). # Take after_nms_topN (e.g. 300). # unNOTE: somthing is wrong here! # TODO: remove cuda.to_gpu keep = non_maximum_suppression(             np.ascontiguousarray(np.asarray(roi)),             threshold=0.5) # remember that keep is tuple: (indices, indices_num)