text: # coding:utf-8
  import numpy as np
  import pdb
  import math
  from . import data_generators
  import copy
  
  
  def calc_iou(R, img_data, C, class_mapping):  # R is object proposals ,  R是bbox左上角与右下角的xy坐标
      bboxes = img_data['bboxes']
      (width, height) = (img_data['width'], img_data['height'])
      # get image dimensions for resizing
      (resized_width, resized_height) = data_generators.get_new_img_size(width, height, C.im_size)
  
      gta = np.zeros((len(bboxes), 4))
  
      for bbox_num, bbox in enumerate(bboxes):
          # get the GT box coordinates, and resize to account for image resizing
          gta[bbox_num, 0] = int(round(bbox['x1'] * (resized_width / float(width)) / C.rpn_stride))
          gta[bbox_num, 1] = int(round(bbox['x2'] * (resized_width / float(width)) / C.rpn_stride))
          gta[bbox_num, 2] = int(round(bbox['y1'] * (resized_height / float(height)) / C.rpn_stride))
          gta[bbox_num, 3] = int(round(bbox['y2'] * (resized_height / float(height)) / C.rpn_stride))
  
      x_roi = []
      y_class_num = []
      y_class_regr_coords = []
      y_class_regr_label = []
      IoUs = []  # for debugging only
  
      for ix in range(R.shape[0]):
          (x1, y1, x2, y2) = R[ix, :]
          x1 = int(round(x1))
          y1 = int(round(y1))
          x2 = int(round(x2))
          y2 = int(round(y2))
  
          best_iou = 0.0
          best_bbox = -1
          for bbox_num in range(len(bboxes)):
              curr_iou = data_generators.iou([gta[bbox_num, 0], gta[bbox_num, 2], gta[bbox_num, 1], gta[bbox_num, 3]],
                                             [x1, y1, x2, y2])
              if curr_iou > best_iou:
                  best_iou = curr_iou
                  best_bbox = bbox_num
  
          if best_iou < C.classifier_min_overlap:
              continue
          else:
              w = x2 - x1
              h = y2 - y1
              x_roi.append([x1, y1, w, h])
              IoUs.append(best_iou)
  
              if C.classifier_min_overlap <= best_iou < C.classifier_max_overlap:
                  # hard negative example
                  cls_name = 'bg'
              elif C.classifier_max_overlap <= best_iou:
                  cls_name = bboxes[best_bbox]['class']
                  cxg = (gta[best_bbox, 0] + gta[best_bbox, 1]) / 2.0
                  cyg = (gta[best_bbox, 2] + gta[best_bbox, 3]) / 2.0
  
                  cx = x1 + w / 2.0
                  cy = y1 + h / 2.0
  
                  tx = (cxg - cx) / float(w)
                  ty = (cyg - cy) / float(h)
                  tw = np.log((gta[best_bbox, 1] - gta[best_bbox, 0]) / float(w))
                  th = np.log((gta[best_bbox, 3] - gta[best_bbox, 2]) / float(h))
              else:
                  print('roi = {}'.format(best_iou))
                  raise RuntimeError
  
          class_num = class_mapping[cls_name]
          class_label = len(class_mapping) * [0]
          class_label[class_num] = 1
          y_class_num.append(copy.deepcopy(class_label))
          coords = [0] * 4 * (len(class_mapping) - 1)
          labels = [0] * 4 * (len(class_mapping) - 1)
          if cls_name != 'bg':
              label_pos = 4 * class_num
              sx, sy, sw, sh = C.classifier_regr_std   # todo: what's the function of classifier_regr_std
              coords[label_pos:4 + label_pos] = [sx * tx, sy * ty, sw * tw, sh * th]
              labels[label_pos:4 + label_pos] = [1, 1, 1, 1]
              y_class_regr_coords.append(copy.deepcopy(coords))
              y_class_regr_label.append(copy.deepcopy(labels))
          else:
              y_class_regr_coords.append(copy.deepcopy(coords))
              y_class_regr_label.append(copy.deepcopy(labels))
  
      if len(x_roi) == 0:
          return None, None, None, None
  
      X = np.array(x_roi)
      Y1 = np.array(y_class_num)
      Y2 = np.concatenate([np.array(y_class_regr_label), np.array(y_class_regr_coords)], axis=1)
  
      return np.expand_dims(X, axis=0), np.expand_dims(Y1, axis=0), np.expand_dims(Y2, axis=0), IoUs
  
  
  def apply_regr(x, y, w, h, tx, ty, tw, th):  # x, y , w , h in pixel, tx, th is normalized to [-1, 1]
      # 将相对于anchor基准的归一化的t坐标转换为
      # x,y,w,h， 并且x,y是bbox的左上角坐标
      try:
          cx = x + w / 2.
          cy = y + h / 2.
          cx1 = tx * w + cx
          cy1 = ty * h + cy
          w1 = math.exp(tw) * w
          h1 = math.exp(th) * h
          x1 = cx1 - w1 / 2.
          y1 = cy1 - h1 / 2.
          x1 = int(round(x1))
          y1 = int(round(y1))
          w1 = int(round(w1))
          h1 = int(round(h1))
  
          return x1, y1, w1, h1
  
      except ValueError:
          return x, y, w, h
      except OverflowError:
          return x, y, w, h
      except Exception as e:
          print(e)
          return x, y, w, h
  
  
  def apply_regr_np(X, T):
      # 将相对于anchor基准的归一化的t坐标转换为
      # x,y,w,h， 并且x,y是bbox的左上角坐标
      try:
          x = X[0, :, :]
          y = X[1, :, :]
          w = X[2, :, :]
          h = X[3, :, :]
  
          tx = T[0, :, :]
          ty = T[1, :, :]
          tw = T[2, :, :]
          th = T[3, :, :]
  
          cx = x + w / 2.
          cy = y + h / 2.
          cx1 = tx * w + cx
          cy1 = ty * h + cy  # cx1, cx2是bbox的中心坐标
  
          w1 = np.exp(tw.astype(np.float64)) * w
          h1 = np.exp(th.astype(np.float64)) * h
          x1 = cx1 - w1 / 2.
          y1 = cy1 - h1 / 2.   # bbox的左上角坐标
  
          x1 = np.round(x1)
          y1 = np.round(y1)
          w1 = np.round(w1)
          h1 = np.round(h1)
          return np.stack([x1, y1, w1, h1])
      except Exception as e:
          print(e)
          return X
  
  
  def non_max_suppression_fast(boxes, probs, overlap_thresh=0.9, max_boxes=300):  # 非极大值抑制, 大于thresh的框被抑制（删除）
      # code used from here: http://www.pyimagesearch.com/2015/02/16/faster-non-maximum-suppression-python/
      # if there are no boxes, return an empty list
      if len(boxes) == 0:
          return []
  
      # grab the coordinates of the bounding boxes
      x1 = boxes[:, 0]
      y1 = boxes[:, 1]
      x2 = boxes[:, 2]
      y2 = boxes[:, 3]
  
      np.testing.assert_array_less(x1, x2)  # 图像坐标系，左上角比右下角的坐标值小
      #  Given two array_like objects, check that the shape is equal and all elements of the first object are strictly
      # smaller than those of the second object.
      np.testing.assert_array_less(y1, y2)
  
      # if the bounding boxes integers, convert them to floats --
      # this is important since we'll be doing a bunch of divisions
      if boxes.dtype.kind == "i":
          boxes = boxes.astype("float")
  
      # initialize the list of picked indexes
      pick = []
  
      # calculate the areas
      area = (x2 - x1) * (y2 - y1)
  
      # sort the bounding boxes
      idxs = np.argsort(probs)
      # 从下到大排列
      # keep looping while some indexes still remain in the indexes
      # list
      while len(idxs) > 0:
          # grab the last index in the indexes list and add the
          # index value to the list of picked indexes
          last = len(idxs) - 1
          i = idxs[last]  # 无条件保留每类得分最高的boundingbox
          pick.append(i)
  
          # find the intersection  交叉点
  
          xx1_int = np.maximum(x1[i], x1[idxs[:last]])
          yy1_int = np.maximum(y1[i], y1[idxs[:last]])
          xx2_int = np.minimum(x2[i], x2[idxs[:last]])
          yy2_int = np.minimum(y2[i], y2[idxs[:last]])
  
          ww_int = np.maximum(0, xx2_int - xx1_int)
          hh_int = np.maximum(0, yy2_int - yy1_int)
  
          area_int = ww_int * hh_int  # is a vector
  
          # find the union
          area_union = area[i] + area[idxs[:last]] - area_int   # 矩阵化的计算，并行加速
  
          # compute the ratio of overlap
          overlap = area_int / (area_union + 1e-6)
  
          # delete all indexes from the index list that have
          idxs = np.delete(idxs, np.concatenate(([last], np.where(overlap > overlap_thresh)[0])))
  
          if len(pick) >= max_boxes:
              break
  
      # return only the bounding boxes that were picked using the integer data type
      boxes = boxes[pick].astype("int")
      probs = probs[pick]
      return boxes, probs
  
  
  import time
  
  # 参考 https://github.com/rbgirshick/py-faster-rcnn/blob/master/lib/rpn/proposal_layer.py
  def rpn_to_roi(rpn_layer, regr_layer, C, dim_ordering, use_regr=True, max_boxes=300, overlap_thresh=0.9):
      regr_layer = regr_layer / C.std_scaling  # todo what's the use of std_scaling
  
      anchor_sizes = C.anchor_box_scales
      anchor_ratios = C.anchor_box_ratios
  
      assert rpn_layer.shape[0] == 1   # 每次只能处理一张图片，因此N=1
  
      if dim_ordering == 'th':
          (rows, cols) = rpn_layer.shape[2:]
  
      elif dim_ordering == 'tf':
          (rows, cols) = rpn_layer.shape[1:3]
  
      curr_layer = 0
      if dim_ordering == 'tf':
          A = np.zeros((4, rpn_layer.shape[1], rpn_layer.shape[2], rpn_layer.shape[3]))
      elif dim_ordering == 'th':
          A = np.zeros((4, rpn_layer.shape[2], rpn_layer.shape[3], rpn_layer.shape[1]))
  
      for anchor_size in anchor_sizes:
          for anchor_ratio in anchor_ratios:  # 每个anchor都有len(anchor_size)*len(anchor_ratio)=3*3=9个框
  
              anchor_x = (anchor_size * anchor_ratio[0]) / C.rpn_stride  # todo
              anchor_y = (anchor_size * anchor_ratio[1]) / C.rpn_stride
              if dim_ordering == 'th':
                  regr = regr_layer[0, 4 * curr_layer:4 * curr_layer + 4, :, :]
              else:
                  regr = regr_layer[0, :, :, 4 * curr_layer:4 * curr_layer + 4]
                  regr = np.transpose(regr, (2, 0, 1))
  
              X, Y = np.meshgrid(np.arange(cols), np.arange(rows))
  
              A[0, :, :, curr_layer] = X - anchor_x / 2  # A的格式为第一维代表x,y,w,h， 二、三维代表feature map网格的长、宽
              A[1, :, :, curr_layer] = Y - anchor_y / 2  # 第四维代表每一个anchor有9个框
              A[2, :, :, curr_layer] = anchor_x
              A[3, :, :, curr_layer] = anchor_y   # 计算得到的坐标格式为左上角x,y 和方框的长度w,h
  
              if use_regr:
                  A[:, :, :, curr_layer] = apply_regr_np(A[:, :, :, curr_layer], regr)  # 将相对于anchor基准的归一化的t坐标转换为
                  # x,y,w,h， 并且x,y是bbox的左上角坐标.   A是anchor的坐标，regr是预测的roi相对于anchor的坐标
  
              A[2, :, :, curr_layer] = np.maximum(1, A[2, :, :, curr_layer])
              A[3, :, :, curr_layer] = np.maximum(1, A[3, :, :, curr_layer])
              A[2, :, :, curr_layer] += A[0, :, :, curr_layer]
              A[3, :, :, curr_layer] += A[1, :, :, curr_layer]   # 变成右下角坐标
  
              A[0, :, :, curr_layer] = np.maximum(0, A[0, :, :, curr_layer])    # 防止得到的框的区域越界限（不超过feature map大小）
              A[1, :, :, curr_layer] = np.maximum(0, A[1, :, :, curr_layer])
              A[2, :, :, curr_layer] = np.minimum(cols - 1, A[2, :, :, curr_layer])
              A[3, :, :, curr_layer] = np.minimum(rows - 1, A[3, :, :, curr_layer])  # 得到的是在feature map 坐标系下的坐标
  
              curr_layer += 1
  
      all_boxes = np.reshape(A.transpose((0, 3, 1, 2)), (4, -1)).transpose((1, 0))
      all_probs = rpn_layer.transpose((0, 3, 1, 2)).reshape((-1))
  
      x1 = all_boxes[:, 0]
      y1 = all_boxes[:, 1]
      x2 = all_boxes[:, 2]
      y2 = all_boxes[:, 3]
  
      idxs = np.where((x1 - x2 >= 0) | (y1 - y2 >= 0))
  
      all_boxes = np.delete(all_boxes, idxs, 0)
      all_probs = np.delete(all_probs, idxs, 0)
  
      result = non_max_suppression_fast(all_boxes, all_probs, overlap_thresh=overlap_thresh, max_boxes=max_boxes)[0]
  
      return result
  