"""
RPN:
data =
    {'data': [num_images, c, h, w],
     'im_info': [num_images, 4] (optional)}
label =
    {'gt_boxes': [num_boxes, 5] (optional),
     'label': [batch_size, 1] <- [batch_size, num_anchors, feat_height, feat_width],
     'bbox_target': [batch_size, num_anchors, feat_height, feat_width],
     'bbox_weight': [batch_size, num_anchors, feat_height, feat_width]}
"""

from __future__ import print_function
import sys
import logging
import datetime
import numpy as np
import numpy.random as npr

from ..logger import logger
from ..config import config
from .image import get_image, tensor_vstack, get_crop_image
from ..processing.generate_anchor import generate_anchors, anchors_plane
from ..processing.bbox_transform import bbox_overlaps, bbox_transform, landmark_transform

STAT = {0: 0, 8: 0, 16: 0, 32: 0}


def get_rpn_testbatch(roidb):
    """
    return a dict of testbatch
    :param roidb: ['image', 'flipped']
    :return: data, label, im_info
    """
    assert len(roidb) == 1, 'Single batch only'
    imgs, roidb = get_image(roidb)
    im_array = imgs[0]
    im_info = np.array([roidb[0]['im_info']], dtype=np.float32)

    data = {'data': im_array, 'im_info': im_info}
    label = {}

    return data, label, im_info


def get_rpn_batch(roidb):
    """
    prototype for rpn batch: data, im_info, gt_boxes
    :param roidb: ['image', 'flipped'] + ['gt_boxes', 'boxes', 'gt_classes']
    :return: data, label
    """
    assert len(roidb) == 1, 'Single batch only'
    imgs, roidb = get_image(roidb)
    im_array = imgs[0]
    im_info = np.array([roidb[0]['im_info']], dtype=np.float32)

    # gt boxes: (x1, y1, x2, y2, cls)
    if roidb[0]['gt_classes'].size > 0:
        gt_inds = np.where(roidb[0]['gt_classes'] != 0)[0]
        gt_boxes = np.empty((roidb[0]['boxes'].shape[0], 5), dtype=np.float32)
        gt_boxes[:, 0:4] = roidb[0]['boxes'][gt_inds, :]
        gt_boxes[:, 4] = roidb[0]['gt_classes'][gt_inds]
    else:
        gt_boxes = np.empty((0, 5), dtype=np.float32)

    data = {'data': im_array, 'im_info': im_info}
    label = {'gt_boxes': gt_boxes}

    return data, label


def get_crop_batch(roidb):
    """
    prototype for rpn batch: data, im_info, gt_boxes
    :param roidb: ['image', 'flipped'] + ['gt_boxes', 'boxes', 'gt_classes']
    :return: data, label
    """
    #assert len(roidb) == 1, 'Single batch only'
    data_list = []
    label_list = []
    imgs, roidb = get_crop_image(roidb)
    assert len(imgs) == len(roidb)
    for i in range(len(imgs)):
        im_array = imgs[i]
        im_info = np.array([roidb[i]['im_info']], dtype=np.float32)

        # gt boxes: (x1, y1, x2, y2, cls)
        if roidb[i]['gt_classes'].size > 0:
            gt_inds = np.where(roidb[i]['gt_classes'] != 0)[0]
            gt_boxes = np.empty((roidb[i]['boxes'].shape[0], 5),
                                dtype=np.float32)
            gt_boxes[:, 0:4] = roidb[i]['boxes'][gt_inds, :]
            gt_boxes[:, 4] = roidb[i]['gt_classes'][gt_inds]
            if config.USE_BLUR:
                gt_blur = roidb[i]['blur']
            if config.FACE_LANDMARK:
                #gt_landmarks = np.empty((roidb[i]['landmarks'].shape[0], 11), dtype=np.float32)
                gt_landmarks = roidb[i]['landmarks'][gt_inds, :, :]
            if config.HEAD_BOX:
                gt_boxes_head = np.empty((roidb[i]['boxes_head'].shape[0], 5),
                                         dtype=np.float32)
                gt_boxes_head[:, 0:4] = roidb[i]['boxes_head'][gt_inds, :]
                gt_boxes_head[:, 4] = roidb[i]['gt_classes'][gt_inds]
        else:
            gt_boxes = np.empty((0, 5), dtype=np.float32)
            if config.USE_BLUR:
                gt_blur = np.empty((0, ), dtype=np.float32)
            if config.FACE_LANDMARK:
                gt_landmarks = np.empty((0, 5, 3), dtype=np.float32)
            if config.HEAD_BOX:
                gt_boxes_head = np.empty((0, 5), dtype=np.float32)

        data = {'data': im_array, 'im_info': im_info}
        label = {'gt_boxes': gt_boxes}
        if config.USE_BLUR:
            label['gt_blur'] = gt_blur
        if config.FACE_LANDMARK:
            label['gt_landmarks'] = gt_landmarks
        if config.HEAD_BOX:
            label['gt_boxes_head'] = gt_boxes_head
        data_list.append(data)
        label_list.append(label)

    return data_list, label_list


def assign_anchor_fpn(feat_shape,
                      gt_label,
                      im_info,
                      landmark=False,
                      prefix='face',
                      select_stride=0):
    """
    assign ground truth boxes to anchor positions
    :param feat_shape: infer output shape
    :param gt_boxes: assign ground truth
    :param im_info: filter out anchors overlapped with edges
    :return: tuple
    labels: of shape (batch_size, 1) <- (batch_size, num_anchors, feat_height, feat_width)
    bbox_targets: of shape (batch_size, num_anchors * 4, feat_height, feat_width)
    bbox_weights: mark the assigned anchors
    """
    def _unmap(data, count, inds, fill=0):
        """" unmap a subset inds of data into original data of size count """
        if len(data.shape) == 1:
            ret = np.empty((count, ), dtype=np.float32)
            ret.fill(fill)
            ret[inds] = data
        else:
            ret = np.empty((count, ) + data.shape[1:], dtype=np.float32)
            ret.fill(fill)
            ret[inds, :] = data
        return ret

    global STAT
    DEBUG = False

    im_info = im_info[0]
    gt_boxes = gt_label['gt_boxes']
    # clean up boxes
    nonneg = np.where(gt_boxes[:, 4] != -1)[0]
    gt_boxes = gt_boxes[nonneg]
    if config.USE_BLUR:
        gt_blur = gt_label['gt_blur']
        gt_blur = gt_blur[nonneg]
    if landmark:
        gt_landmarks = gt_label['gt_landmarks']
        gt_landmarks = gt_landmarks[nonneg]
        assert gt_boxes.shape[0] == gt_landmarks.shape[0]
    #scales = np.array(scales, dtype=np.float32)
    feat_strides = config.RPN_FEAT_STRIDE
    bbox_pred_len = 4
    landmark_pred_len = 10
    if config.USE_BLUR:
        gt_boxes[:, 4] = gt_blur
        bbox_pred_len = 5
    if config.USE_OCCLUSION:
        landmark_pred_len = 15

    anchors_list = []
    anchors_num_list = []
    inds_inside_list = []
    feat_infos = []
    A_list = []
    for i in range(len(feat_strides)):
        stride = feat_strides[i]
        sstride = str(stride)
        base_size = config.RPN_ANCHOR_CFG[sstride]['BASE_SIZE']
        allowed_border = config.RPN_ANCHOR_CFG[sstride]['ALLOWED_BORDER']
        ratios = config.RPN_ANCHOR_CFG[sstride]['RATIOS']
        scales = config.RPN_ANCHOR_CFG[sstride]['SCALES']
        base_anchors = generate_anchors(base_size=base_size,
                                        ratios=list(ratios),
                                        scales=np.array(scales,
                                                        dtype=np.float32),
                                        stride=stride,
                                        dense_anchor=config.DENSE_ANCHOR)
        num_anchors = base_anchors.shape[0]
        feat_height, feat_width = feat_shape[i][-2:]
        feat_stride = feat_strides[i]
        feat_infos.append([feat_height, feat_width])

        A = num_anchors
        A_list.append(A)
        K = feat_height * feat_width

        all_anchors = anchors_plane(feat_height, feat_width, feat_stride,
                                    base_anchors)
        all_anchors = all_anchors.reshape((K * A, 4))
        #print('anchor0', stride, all_anchors[0])

        total_anchors = int(K * A)
        anchors_num_list.append(total_anchors)
        # only keep anchors inside the image
        inds_inside = np.where(
            (all_anchors[:, 0] >= -allowed_border)
            & (all_anchors[:, 1] >= -allowed_border)
            & (all_anchors[:, 2] < im_info[1] + allowed_border)
            & (all_anchors[:, 3] < im_info[0] + allowed_border))[0]
        if DEBUG:
            print('total_anchors', total_anchors)
            print('inds_inside', len(inds_inside))

        # keep only inside anchors
        anchors = all_anchors[inds_inside, :]
        #print('AA', anchors.shape, len(inds_inside))

        anchors_list.append(anchors)
        inds_inside_list.append(inds_inside)

    # Concat anchors from each level
    anchors = np.concatenate(anchors_list)
    for i in range(1, len(inds_inside_list)):
        inds_inside_list[i] = inds_inside_list[i] + sum(anchors_num_list[:i])
    inds_inside = np.concatenate(inds_inside_list)
    total_anchors = sum(anchors_num_list)
    #print('total_anchors', anchors.shape[0], len(inds_inside), file=sys.stderr)

    # label: 1 is positive, 0 is negative, -1 is dont care
    labels = np.empty((len(inds_inside), ), dtype=np.float32)
    labels.fill(-1)
    #print('BB', anchors.shape, len(inds_inside))
    #print('gt_boxes', gt_boxes.shape, file=sys.stderr)

    if gt_boxes.size > 0:
        # overlap between the anchors and the gt boxes
        # overlaps (ex, gt)
        overlaps = bbox_overlaps(anchors.astype(np.float),
                                 gt_boxes.astype(np.float))
        argmax_overlaps = overlaps.argmax(axis=1)
        #print('AAA', argmax_overlaps.shape)
        max_overlaps = overlaps[np.arange(len(inds_inside)), argmax_overlaps]
        gt_argmax_overlaps = overlaps.argmax(axis=0)
        gt_max_overlaps = overlaps[gt_argmax_overlaps,
                                   np.arange(overlaps.shape[1])]
        gt_argmax_overlaps = np.where(overlaps == gt_max_overlaps)[0]

        if not config.TRAIN.RPN_CLOBBER_POSITIVES:
            # assign bg labels first so that positive labels can clobber them
            labels[max_overlaps < config.TRAIN.RPN_NEGATIVE_OVERLAP] = 0

        # fg label: for each gt, anchor with highest overlap
        if config.TRAIN.RPN_FORCE_POSITIVE:
            labels[gt_argmax_overlaps] = 1

        # fg label: above threshold IoU
        labels[max_overlaps >= config.TRAIN.RPN_POSITIVE_OVERLAP] = 1

        if config.TRAIN.RPN_CLOBBER_POSITIVES:
            # assign bg labels last so that negative labels can clobber positives
            labels[max_overlaps < config.TRAIN.RPN_NEGATIVE_OVERLAP] = 0
    else:
        labels[:] = 0
    fg_inds = np.where(labels == 1)[0]
    #print('fg count', len(fg_inds))

    # subsample positive labels if we have too many
    if config.TRAIN.RPN_ENABLE_OHEM == 0:
        fg_inds = np.where(labels == 1)[0]
        num_fg = int(config.TRAIN.RPN_FG_FRACTION *
                     config.TRAIN.RPN_BATCH_SIZE)
        if len(fg_inds) > num_fg:
            disable_inds = npr.choice(fg_inds,
                                      size=(len(fg_inds) - num_fg),
                                      replace=False)
            if DEBUG:
                disable_inds = fg_inds[:(len(fg_inds) - num_fg)]
            labels[disable_inds] = -1

        # subsample negative labels if we have too many
        num_bg = config.TRAIN.RPN_BATCH_SIZE - np.sum(labels == 1)
        bg_inds = np.where(labels == 0)[0]
        if len(bg_inds) > num_bg:
            disable_inds = npr.choice(bg_inds,
                                      size=(len(bg_inds) - num_bg),
                                      replace=False)
            if DEBUG:
                disable_inds = bg_inds[:(len(bg_inds) - num_bg)]
            labels[disable_inds] = -1

        #fg_inds = np.where(labels == 1)[0]
        #num_fg = len(fg_inds)
        #num_bg = num_fg*int(1.0/config.TRAIN.RPN_FG_FRACTION-1)

        #bg_inds = np.where(labels == 0)[0]
        #if len(bg_inds) > num_bg:
        #    disable_inds = npr.choice(bg_inds, size=(len(bg_inds) - num_bg), replace=False)
        #    if DEBUG:
        #        disable_inds = bg_inds[:(len(bg_inds) - num_bg)]
        #    labels[disable_inds] = -1
    else:
        fg_inds = np.where(labels == 1)[0]
        num_fg = len(fg_inds)
        bg_inds = np.where(labels == 0)[0]
        num_bg = len(bg_inds)

    #print('anchor stat', num_fg, num_bg)

    bbox_targets = np.zeros((len(inds_inside), bbox_pred_len),
                            dtype=np.float32)
    if gt_boxes.size > 0:
        #print('GT', gt_boxes.shape, gt_boxes[argmax_overlaps, :4].shape)
        bbox_targets[:, :] = bbox_transform(anchors,
                                            gt_boxes[argmax_overlaps, :])
        #bbox_targets[:,4] = gt_blur

    bbox_weights = np.zeros((len(inds_inside), bbox_pred_len),
                            dtype=np.float32)
    #bbox_weights[labels == 1, :] = np.array(config.TRAIN.RPN_BBOX_WEIGHTS)
    bbox_weights[labels == 1, 0:4] = 1.0
    if bbox_pred_len > 4:
        bbox_weights[labels == 1, 4:bbox_pred_len] = 0.1

    if landmark:
        landmark_targets = np.zeros((len(inds_inside), landmark_pred_len),
                                    dtype=np.float32)
        #landmark_weights = np.zeros((len(inds_inside), 10), dtype=np.float32)
        landmark_weights = np.zeros((len(inds_inside), landmark_pred_len),
                                    dtype=np.float32)
        #landmark_weights[labels == 1, :] = np.array(config.TRAIN.RPN_LANDMARK_WEIGHTS)
        if landmark_pred_len == 10:
            landmark_weights[labels == 1, :] = 1.0
        elif landmark_pred_len == 15:
            v = [1.0, 1.0, 0.1] * 5
            assert len(v) == 15
            landmark_weights[labels == 1, :] = np.array(v)
        else:
            assert False
        #TODO here
        if gt_landmarks.size > 0:
            #print('AAA',argmax_overlaps)
            a_landmarks = gt_landmarks[argmax_overlaps, :, :]
            landmark_targets[:] = landmark_transform(anchors, a_landmarks)
            invalid = np.where(a_landmarks[:, 0, 2] < 0.0)[0]
            #assert len(invalid)==0
            #landmark_weights[invalid, :] = np.array(config.TRAIN.RPN_INVALID_LANDMARK_WEIGHTS)
            landmark_weights[invalid, :] = 0.0

    #if DEBUG:
    #    _sums = bbox_targets[labels == 1, :].sum(axis=0)
    #    _squared_sums = (bbox_targets[labels == 1, :] ** 2).sum(axis=0)
    #    _counts = np.sum(labels == 1)
    #    means = _sums / (_counts + 1e-14)
    #    stds = np.sqrt(_squared_sums / _counts - means ** 2)
    #    print 'means', means
    #    print 'stdevs', stds
    # map up to original set of anchors
    #print(labels.shape, total_anchors, inds_inside.shape, inds_inside[0], inds_inside[-1])
    labels = _unmap(labels, total_anchors, inds_inside, fill=-1)
    bbox_targets = _unmap(bbox_targets, total_anchors, inds_inside, fill=0)
    bbox_weights = _unmap(bbox_weights, total_anchors, inds_inside, fill=0)
    if landmark:
        landmark_targets = _unmap(landmark_targets,
                                  total_anchors,
                                  inds_inside,
                                  fill=0)
        landmark_weights = _unmap(landmark_weights,
                                  total_anchors,
                                  inds_inside,
                                  fill=0)
    #print('CC', anchors.shape, len(inds_inside))

    #if DEBUG:
    #    if gt_boxes.size > 0:
    #        print 'rpn: max max_overlaps', np.max(max_overlaps)
    #    print 'rpn: num_positives', np.sum(labels == 1)
    #    print 'rpn: num_negatives', np.sum(labels == 0)
    #    _fg_sum = np.sum(labels == 1)
    #    _bg_sum = np.sum(labels == 0)
    #    _count = 1
    #    print 'rpn: num_positive avg', _fg_sum / _count
    #    print 'rpn: num_negative avg', _bg_sum / _count

    # resahpe
    label_list = list()
    bbox_target_list = list()
    bbox_weight_list = list()
    if landmark:
        landmark_target_list = list()
        landmark_weight_list = list()
    anchors_num_range = [0] + anchors_num_list
    label = {}
    for i in range(len(feat_strides)):
        stride = feat_strides[i]
        feat_height, feat_width = feat_infos[i]
        A = A_list[i]
        _label = labels[sum(anchors_num_range[:i +
                                              1]):sum(anchors_num_range[:i +
                                                                        1]) +
                        anchors_num_range[i + 1]]
        if select_stride > 0 and stride != select_stride:
            #print('set', stride, select_stride)
            _label[:] = -1
        #print('_label', _label.shape, select_stride)
        #_fg_inds = np.where(_label == 1)[0]
        #n_fg = len(_fg_inds)
        #STAT[0]+=1
        #STAT[stride]+=n_fg
        #if STAT[0]%100==0:
        #  print('rpn_stat', STAT, file=sys.stderr)
        bbox_target = bbox_targets[sum(anchors_num_range[:i + 1]
                                       ):sum(anchors_num_range[:i + 1]) +
                                   anchors_num_range[i + 1]]
        bbox_weight = bbox_weights[sum(anchors_num_range[:i + 1]
                                       ):sum(anchors_num_range[:i + 1]) +
                                   anchors_num_range[i + 1]]
        if landmark:
            landmark_target = landmark_targets[
                sum(anchors_num_range[:i + 1]):sum(anchors_num_range[:i + 1]) +
                anchors_num_range[i + 1]]
            landmark_weight = landmark_weights[
                sum(anchors_num_range[:i + 1]):sum(anchors_num_range[:i + 1]) +
                anchors_num_range[i + 1]]

        _label = _label.reshape(
            (1, feat_height, feat_width, A)).transpose(0, 3, 1, 2)
        _label = _label.reshape((1, A * feat_height * feat_width))
        bbox_target = bbox_target.reshape(
            (1, feat_height * feat_width,
             A * bbox_pred_len)).transpose(0, 2, 1)
        bbox_weight = bbox_weight.reshape(
            (1, feat_height * feat_width, A * bbox_pred_len)).transpose(
                (0, 2, 1))
        label['%s_label_stride%d' % (prefix, stride)] = _label
        label['%s_bbox_target_stride%d' % (prefix, stride)] = bbox_target
        label['%s_bbox_weight_stride%d' % (prefix, stride)] = bbox_weight
        if landmark:
            landmark_target = landmark_target.reshape(
                (1, feat_height * feat_width,
                 A * landmark_pred_len)).transpose(0, 2, 1)
            landmark_weight = landmark_weight.reshape(
                (1, feat_height * feat_width,
                 A * landmark_pred_len)).transpose((0, 2, 1))
            label['%s_landmark_target_stride%d' %
                  (prefix, stride)] = landmark_target
            label['%s_landmark_weight_stride%d' %
                  (prefix, stride)] = landmark_weight
        #print('in_rpn', stride,_label.shape, bbox_target.shape, bbox_weight.shape, file=sys.stderr)
        label_list.append(_label)
        #print('DD', _label.shape)
        bbox_target_list.append(bbox_target)
        bbox_weight_list.append(bbox_weight)
        if landmark:
            landmark_target_list.append(landmark_target)
            landmark_weight_list.append(landmark_weight)

    label_concat = np.concatenate(label_list, axis=1)
    bbox_target_concat = np.concatenate(bbox_target_list, axis=2)
    bbox_weight_concat = np.concatenate(bbox_weight_list, axis=2)
    #fg_inds = np.where(label_concat[0] == 1)[0]
    #print('fg_inds_in_rpn2', fg_inds, file=sys.stderr)

    label.update({
        '%s_label' % prefix: label_concat,
        '%s_bbox_target' % prefix: bbox_target_concat,
        '%s_bbox_weight' % prefix: bbox_weight_concat
    })
    if landmark:
        landmark_target_concat = np.concatenate(landmark_target_list, axis=2)
        landmark_weight_concat = np.concatenate(landmark_weight_list, axis=2)
        label['%s_landmark_target' % prefix] = landmark_target_concat
        label['%s_landmark_weight' % prefix] = landmark_weight_concat
    return label


class AA:
    def __init__(self, feat_shape):
        self.feat_shape = feat_shape
        feat_strides = config.RPN_FEAT_STRIDE
        anchors_list = []
        anchors_num_list = []
        inds_inside_list = []
        feat_infos = []
        A_list = []
        DEBUG = False
        for i in range(len(feat_strides)):
            stride = feat_strides[i]
            sstride = str(stride)
            base_size = config.RPN_ANCHOR_CFG[sstride]['BASE_SIZE']
            allowed_border = config.RPN_ANCHOR_CFG[sstride]['ALLOWED_BORDER']
            ratios = config.RPN_ANCHOR_CFG[sstride]['RATIOS']
            scales = config.RPN_ANCHOR_CFG[sstride]['SCALES']
            base_anchors = generate_anchors(base_size=base_size,
                                            ratios=list(ratios),
                                            scales=np.array(scales,
                                                            dtype=np.float32),
                                            stride=stride,
                                            dense_anchor=config.DENSE_ANCHOR)
            num_anchors = base_anchors.shape[0]
            feat_height, feat_width = feat_shape[i][-2:]
            feat_stride = feat_strides[i]
            feat_infos.append([feat_height, feat_width])

            A = num_anchors
            A_list.append(A)
            K = feat_height * feat_width

            all_anchors = anchors_plane(feat_height, feat_width, feat_stride,
                                        base_anchors)
            all_anchors = all_anchors.reshape((K * A, 4))
            #print('anchor0', stride, all_anchors[0])

            total_anchors = int(K * A)
            anchors_num_list.append(total_anchors)
            # only keep anchors inside the image
            inds_inside = np.where(
                (all_anchors[:, 0] >= -allowed_border)
                & (all_anchors[:, 1] >= -allowed_border)
                & (all_anchors[:, 2] < config.SCALES[0][1] + allowed_border) &
                (all_anchors[:, 3] < config.SCALES[0][1] + allowed_border))[0]
            if DEBUG:
                print('total_anchors', total_anchors)
                print('inds_inside', len(inds_inside))

            # keep only inside anchors
            anchors = all_anchors[inds_inside, :]
            #print('AA', anchors.shape, len(inds_inside))

            anchors_list.append(anchors)
            inds_inside_list.append(inds_inside)
        anchors = np.concatenate(anchors_list)
        for i in range(1, len(inds_inside_list)):
            inds_inside_list[i] = inds_inside_list[i] + sum(
                anchors_num_list[:i])
        inds_inside = np.concatenate(inds_inside_list)
        #self.anchors_list = anchors_list
        #self.inds_inside_list = inds_inside_list
        self.anchors = anchors
        self.inds_inside = inds_inside
        self.anchors_num_list = anchors_num_list
        self.feat_infos = feat_infos
        self.A_list = A_list
        self._times = [0.0, 0.0, 0.0, 0.0]

    @staticmethod
    def _unmap(data, count, inds, fill=0):
        """" unmap a subset inds of data into original data of size count """
        if len(data.shape) == 1:
            ret = np.empty((count, ), dtype=np.float32)
            ret.fill(fill)
            ret[inds] = data
        else:
            ret = np.empty((count, ) + data.shape[1:], dtype=np.float32)
            ret.fill(fill)
            ret[inds, :] = data
        return ret

    def assign_anchor_fpn(self,
                          gt_label,
                          im_info,
                          landmark=False,
                          prefix='face',
                          select_stride=0):

        #ta = datetime.datetime.now()
        im_info = im_info[0]
        gt_boxes = gt_label['gt_boxes']
        # clean up boxes
        nonneg = np.where(gt_boxes[:, 4] != -1)[0]
        gt_boxes = gt_boxes[nonneg]
        if config.USE_BLUR:
            gt_blur = gt_label['gt_blur']
            gt_blur = gt_blur[nonneg]
        if landmark:
            gt_landmarks = gt_label['gt_landmarks']
            gt_landmarks = gt_landmarks[nonneg]
            assert gt_boxes.shape[0] == gt_landmarks.shape[0]
        #scales = np.array(scales, dtype=np.float32)
        feat_strides = config.RPN_FEAT_STRIDE
        bbox_pred_len = 4
        landmark_pred_len = 10
        if config.USE_BLUR:
            gt_boxes[:, 4] = gt_blur
            bbox_pred_len = 5
        if config.USE_OCCLUSION:
            landmark_pred_len = 15

        #anchors_list = self.anchors_list
        #inds_inside_list = self.inds_inside_list
        anchors = self.anchors
        inds_inside = self.inds_inside
        anchors_num_list = self.anchors_num_list
        feat_infos = self.feat_infos
        A_list = self.A_list

        total_anchors = sum(anchors_num_list)
        #print('total_anchors', anchors.shape[0], len(inds_inside), file=sys.stderr)

        # label: 1 is positive, 0 is negative, -1 is dont care
        labels = np.empty((len(inds_inside), ), dtype=np.float32)
        labels.fill(-1)
        #print('BB', anchors.shape, len(inds_inside))
        #print('gt_boxes', gt_boxes.shape, file=sys.stderr)
        #tb = datetime.datetime.now()
        #self._times[0] += (tb-ta).total_seconds()
        #ta = datetime.datetime.now()

        if gt_boxes.size > 0:
            # overlap between the anchors and the gt boxes
            # overlaps (ex, gt)
            overlaps = bbox_overlaps(anchors.astype(np.float),
                                     gt_boxes.astype(np.float))
            argmax_overlaps = overlaps.argmax(axis=1)
            #print('AAA', argmax_overlaps.shape)
            max_overlaps = overlaps[np.arange(len(inds_inside)),
                                    argmax_overlaps]
            gt_argmax_overlaps = overlaps.argmax(axis=0)
            gt_max_overlaps = overlaps[gt_argmax_overlaps,
                                       np.arange(overlaps.shape[1])]
            gt_argmax_overlaps = np.where(overlaps == gt_max_overlaps)[0]

            if not config.TRAIN.RPN_CLOBBER_POSITIVES:
                # assign bg labels first so that positive labels can clobber them
                labels[max_overlaps < config.TRAIN.RPN_NEGATIVE_OVERLAP] = 0

            # fg label: for each gt, anchor with highest overlap
            if config.TRAIN.RPN_FORCE_POSITIVE:
                labels[gt_argmax_overlaps] = 1

            # fg label: above threshold IoU
            labels[max_overlaps >= config.TRAIN.RPN_POSITIVE_OVERLAP] = 1

            if config.TRAIN.RPN_CLOBBER_POSITIVES:
                # assign bg labels last so that negative labels can clobber positives
                labels[max_overlaps < config.TRAIN.RPN_NEGATIVE_OVERLAP] = 0
        else:
            labels[:] = 0
        fg_inds = np.where(labels == 1)[0]
        #print('fg count', len(fg_inds))

        # subsample positive labels if we have too many
        if config.TRAIN.RPN_ENABLE_OHEM == 0:
            fg_inds = np.where(labels == 1)[0]
            num_fg = int(config.TRAIN.RPN_FG_FRACTION *
                         config.TRAIN.RPN_BATCH_SIZE)
            if len(fg_inds) > num_fg:
                disable_inds = npr.choice(fg_inds,
                                          size=(len(fg_inds) - num_fg),
                                          replace=False)
                if DEBUG:
                    disable_inds = fg_inds[:(len(fg_inds) - num_fg)]
                labels[disable_inds] = -1

            # subsample negative labels if we have too many
            num_bg = config.TRAIN.RPN_BATCH_SIZE - np.sum(labels == 1)
            bg_inds = np.where(labels == 0)[0]
            if len(bg_inds) > num_bg:
                disable_inds = npr.choice(bg_inds,
                                          size=(len(bg_inds) - num_bg),
                                          replace=False)
                if DEBUG:
                    disable_inds = bg_inds[:(len(bg_inds) - num_bg)]
                labels[disable_inds] = -1

            #fg_inds = np.where(labels == 1)[0]
            #num_fg = len(fg_inds)
            #num_bg = num_fg*int(1.0/config.TRAIN.RPN_FG_FRACTION-1)

            #bg_inds = np.where(labels == 0)[0]
            #if len(bg_inds) > num_bg:
            #    disable_inds = npr.choice(bg_inds, size=(len(bg_inds) - num_bg), replace=False)
            #    if DEBUG:
            #        disable_inds = bg_inds[:(len(bg_inds) - num_bg)]
            #    labels[disable_inds] = -1
        else:
            fg_inds = np.where(labels == 1)[0]
            num_fg = len(fg_inds)
            bg_inds = np.where(labels == 0)[0]
            num_bg = len(bg_inds)

        #print('anchor stat', num_fg, num_bg)

        bbox_targets = np.zeros((len(inds_inside), bbox_pred_len),
                                dtype=np.float32)
        if gt_boxes.size > 0:
            #print('GT', gt_boxes.shape, gt_boxes[argmax_overlaps, :4].shape)
            bbox_targets[:, :] = bbox_transform(anchors,
                                                gt_boxes[argmax_overlaps, :])
            #bbox_targets[:,4] = gt_blur
        #tb = datetime.datetime.now()
        #self._times[1] += (tb-ta).total_seconds()
        #ta = datetime.datetime.now()

        bbox_weights = np.zeros((len(inds_inside), bbox_pred_len),
                                dtype=np.float32)
        #bbox_weights[labels == 1, :] = np.array(config.TRAIN.RPN_BBOX_WEIGHTS)
        bbox_weights[labels == 1, 0:4] = 1.0
        if bbox_pred_len > 4:
            bbox_weights[labels == 1, 4:bbox_pred_len] = 0.1

        if landmark:
            landmark_targets = np.zeros((len(inds_inside), landmark_pred_len),
                                        dtype=np.float32)
            #landmark_weights = np.zeros((len(inds_inside), 10), dtype=np.float32)
            landmark_weights = np.zeros((len(inds_inside), landmark_pred_len),
                                        dtype=np.float32)
            #landmark_weights[labels == 1, :] = np.array(config.TRAIN.RPN_LANDMARK_WEIGHTS)
            if landmark_pred_len == 10:
                landmark_weights[labels == 1, :] = 1.0
            elif landmark_pred_len == 15:
                v = [1.0, 1.0, 0.1] * 5
                assert len(v) == 15
                landmark_weights[labels == 1, :] = np.array(v)
            else:
                assert False
            #TODO here
            if gt_landmarks.size > 0:
                #print('AAA',argmax_overlaps)
                a_landmarks = gt_landmarks[argmax_overlaps, :, :]
                landmark_targets[:] = landmark_transform(anchors, a_landmarks)
                invalid = np.where(a_landmarks[:, 0, 2] < 0.0)[0]
                #assert len(invalid)==0
                #landmark_weights[invalid, :] = np.array(config.TRAIN.RPN_INVALID_LANDMARK_WEIGHTS)
                landmark_weights[invalid, :] = 0.0
        #tb = datetime.datetime.now()
        #self._times[2] += (tb-ta).total_seconds()
        #ta = datetime.datetime.now()

        #if DEBUG:
        #    _sums = bbox_targets[labels == 1, :].sum(axis=0)
        #    _squared_sums = (bbox_targets[labels == 1, :] ** 2).sum(axis=0)
        #    _counts = np.sum(labels == 1)
        #    means = _sums / (_counts + 1e-14)
        #    stds = np.sqrt(_squared_sums / _counts - means ** 2)
        #    print 'means', means
        #    print 'stdevs', stds
        # map up to original set of anchors
        #print(labels.shape, total_anchors, inds_inside.shape, inds_inside[0], inds_inside[-1])
        labels = AA._unmap(labels, total_anchors, inds_inside, fill=-1)
        bbox_targets = AA._unmap(bbox_targets,
                                 total_anchors,
                                 inds_inside,
                                 fill=0)
        bbox_weights = AA._unmap(bbox_weights,
                                 total_anchors,
                                 inds_inside,
                                 fill=0)
        if landmark:
            landmark_targets = AA._unmap(landmark_targets,
                                         total_anchors,
                                         inds_inside,
                                         fill=0)
            landmark_weights = AA._unmap(landmark_weights,
                                         total_anchors,
                                         inds_inside,
                                         fill=0)
        #print('CC', anchors.shape, len(inds_inside))

        bbox_targets[:,
                     0::4] = bbox_targets[:, 0::4] / config.TRAIN.BBOX_STDS[0]
        bbox_targets[:,
                     1::4] = bbox_targets[:, 1::4] / config.TRAIN.BBOX_STDS[1]
        bbox_targets[:,
                     2::4] = bbox_targets[:, 2::4] / config.TRAIN.BBOX_STDS[2]
        bbox_targets[:,
                     3::4] = bbox_targets[:, 3::4] / config.TRAIN.BBOX_STDS[3]
        landmark_targets /= config.TRAIN.LANDMARK_STD
        #print('applied STD')

        #if DEBUG:
        #    if gt_boxes.size > 0:
        #        print 'rpn: max max_overlaps', np.max(max_overlaps)
        #    print 'rpn: num_positives', np.sum(labels == 1)
        #    print 'rpn: num_negatives', np.sum(labels == 0)
        #    _fg_sum = np.sum(labels == 1)
        #    _bg_sum = np.sum(labels == 0)
        #    _count = 1
        #    print 'rpn: num_positive avg', _fg_sum / _count
        #    print 'rpn: num_negative avg', _bg_sum / _count

        # resahpe
        label_list = list()
        bbox_target_list = list()
        bbox_weight_list = list()
        if landmark:
            landmark_target_list = list()
            landmark_weight_list = list()
        anchors_num_range = [0] + anchors_num_list
        label = {}
        for i in range(len(feat_strides)):
            stride = feat_strides[i]
            feat_height, feat_width = feat_infos[i]
            A = A_list[i]
            _label = labels[sum(anchors_num_range[:i + 1]
                                ):sum(anchors_num_range[:i + 1]) +
                            anchors_num_range[i + 1]]
            if select_stride > 0 and stride != select_stride:
                #print('set', stride, select_stride)
                _label[:] = -1
            #print('_label', _label.shape, select_stride)
            #_fg_inds = np.where(_label == 1)[0]
            #n_fg = len(_fg_inds)
            #STAT[0]+=1
            #STAT[stride]+=n_fg
            #if STAT[0]%100==0:
            #  print('rpn_stat', STAT, file=sys.stderr)
            bbox_target = bbox_targets[sum(anchors_num_range[:i + 1]
                                           ):sum(anchors_num_range[:i + 1]) +
                                       anchors_num_range[i + 1]]
            bbox_weight = bbox_weights[sum(anchors_num_range[:i + 1]
                                           ):sum(anchors_num_range[:i + 1]) +
                                       anchors_num_range[i + 1]]
            if landmark:
                landmark_target = landmark_targets[
                    sum(anchors_num_range[:i +
                                          1]):sum(anchors_num_range[:i + 1]) +
                    anchors_num_range[i + 1]]
                landmark_weight = landmark_weights[
                    sum(anchors_num_range[:i +
                                          1]):sum(anchors_num_range[:i + 1]) +
                    anchors_num_range[i + 1]]

            _label = _label.reshape(
                (1, feat_height, feat_width, A)).transpose(0, 3, 1, 2)
            _label = _label.reshape((1, A * feat_height * feat_width))
            bbox_target = bbox_target.reshape(
                (1, feat_height * feat_width,
                 A * bbox_pred_len)).transpose(0, 2, 1)
            bbox_weight = bbox_weight.reshape(
                (1, feat_height * feat_width, A * bbox_pred_len)).transpose(
                    (0, 2, 1))
            label['%s_label_stride%d' % (prefix, stride)] = _label
            label['%s_bbox_target_stride%d' % (prefix, stride)] = bbox_target
            label['%s_bbox_weight_stride%d' % (prefix, stride)] = bbox_weight
            if landmark:
                landmark_target = landmark_target.reshape(
                    (1, feat_height * feat_width,
                     A * landmark_pred_len)).transpose(0, 2, 1)
                landmark_weight = landmark_weight.reshape(
                    (1, feat_height * feat_width,
                     A * landmark_pred_len)).transpose((0, 2, 1))
                label['%s_landmark_target_stride%d' %
                      (prefix, stride)] = landmark_target
                label['%s_landmark_weight_stride%d' %
                      (prefix, stride)] = landmark_weight
            #print('in_rpn', stride,_label.shape, bbox_target.shape, bbox_weight.shape, file=sys.stderr)
            label_list.append(_label)
            #print('DD', _label.shape)
            bbox_target_list.append(bbox_target)
            bbox_weight_list.append(bbox_weight)
            if landmark:
                landmark_target_list.append(landmark_target)
                landmark_weight_list.append(landmark_weight)

        label_concat = np.concatenate(label_list, axis=1)
        bbox_target_concat = np.concatenate(bbox_target_list, axis=2)
        bbox_weight_concat = np.concatenate(bbox_weight_list, axis=2)
        #fg_inds = np.where(label_concat[0] == 1)[0]
        #print('fg_inds_in_rpn2', fg_inds, file=sys.stderr)

        label.update({
            '%s_label' % prefix: label_concat,
            '%s_bbox_target' % prefix: bbox_target_concat,
            '%s_bbox_weight' % prefix: bbox_weight_concat
        })
        if landmark:
            landmark_target_concat = np.concatenate(landmark_target_list,
                                                    axis=2)
            landmark_weight_concat = np.concatenate(landmark_weight_list,
                                                    axis=2)
            label['%s_landmark_target' % prefix] = landmark_target_concat
            label['%s_landmark_weight' % prefix] = landmark_weight_concat
        #tb = datetime.datetime.now()
        #self._times[3] += (tb-ta).total_seconds()
        #ta = datetime.datetime.now()
        #print(self._times)
        return label
