class_docstr: Assign anchors to ground-truth targets. Produces anchor classification
  labels and bounding-box regression targets.
class_docstr_tok: ["Assign","anchors","to","ground-truth","targets",".","Produces","anchor","classification","labels","and","bounding-box","regression","targets","."]
code: def forward(
      self, arguments, outputs, device=None, outputs_to_retain=None
  ):
      # Algorithm:
      #
      # for each (H, W) location i
      #   generate 9 anchor boxes centered on cell i
      #   apply predicted bbox deltas at cell i to each of the 9 anchors
      # filter out-of-image anchors
      # measure GT overlap
  
      bottom = arguments
  
      # map of shape (..., H, W)
      height, width = bottom[0].shape[-2:]
      # GT boxes (x1, y1, x2, y2, label)
      gt_boxes = bottom[1][0, :]
      # im_info
      im_info = bottom[2][0]
  
      # remove zero padded ground truth boxes
      keep = np.where(
          ((gt_boxes[:, 2] - gt_boxes[:, 0]) > 0)
          & ((gt_boxes[:, 3] - gt_boxes[:, 1]) > 0)
      )
      gt_boxes = gt_boxes[keep]
  
      if DEBUG:
          print("")
          # im_info = (pad_width, pad_height, scaled_image_width, scaled_image_height, orig_img_width, orig_img_height)
          # e.g.(1000, 1000, 1000, 600, 500, 300) for an original image of 600x300 that is scaled and padded to 1000x1000
          print("im_size: ({}, {})".format(im_info[0], im_info[1]))
          print(
              "scaled im_size: ({}, {})".format(im_info[2], im_info[3])
          )
          print(
              "original im_size: ({}, {})".format(
                  im_info[4], im_info[5]
              )
          )
          print("height, width: ({}, {})".format(height, width))
          print("rpn: gt_boxes.shape", gt_boxes.shape)
          # print ('rpn: gt_boxes', gt_boxes)
  
      # 1. Generate proposals from bbox deltas and shifted anchors
      shift_x = np.arange(0, width) * self._feat_stride
      shift_y = np.arange(0, height) * self._feat_stride
      shift_x, shift_y = np.meshgrid(shift_x, shift_y)
      shifts = np.vstack(
          (
              shift_x.ravel(),
              shift_y.ravel(),
              shift_x.ravel(),
              shift_y.ravel(),
          )
      ).transpose()
      # add A anchors (1, A, 4) to
      # cell K shifts (K, 1, 4) to get
      # shift anchors (K, A, 4)
      # reshape to (K*A, 4) shifted anchors
      A = self._num_anchors
      K = shifts.shape[0]
      all_anchors = self._anchors.reshape((1, A, 4)) + shifts.reshape(
          (1, K, 4)
      ).transpose((1, 0, 2))
      all_anchors = all_anchors.reshape((K * A, 4))
      total_anchors = int(K * A)
  
      # only keep anchors inside the image
      padded_wh = im_info[0:2]
      scaled_wh = im_info[2:4]
      xy_offset = (padded_wh - scaled_wh) / 2
      xy_min = xy_offset
      xy_max = xy_offset + scaled_wh
  
      inds_inside = np.where(
          (all_anchors[:, 0] >= xy_min[0] - self._allowed_border)
          & (all_anchors[:, 1] >= xy_min[1] - self._allowed_border)
          & (all_anchors[:, 2] < xy_max[0] + self._allowed_border)
          & (  # width
              all_anchors[:, 3] < xy_max[1] + self._allowed_border
          )  # height
      )[0]
  
      if DEBUG:
          print("total_anchors", total_anchors)
          print("inds_inside", len(inds_inside))
  
      # keep only inside anchors
      anchors = all_anchors[inds_inside, :]
      if DEBUG:
          print("anchors.shape", anchors.shape)
          print("gt_boxes.shape", gt_boxes.shape)
  
      # label: 1 is positive, 0 is negative, -1 is dont care
      labels = np.empty((len(inds_inside),), dtype=np.float32)
      labels.fill(-1)
  
      # overlaps between the anchors and the gt boxes
      # overlaps (ex, gt)
      overlaps = bbox_overlaps(
          np.ascontiguousarray(anchors, dtype=np.float),
          np.ascontiguousarray(gt_boxes, dtype=np.float),
      )
      argmax_overlaps = overlaps.argmax(axis=1)
      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 self._clobber_positives:
          # assign bg labels first so that positive labels can clobber them
          labels[max_overlaps < self._negative_overlap] = 0
  
      # fg label: for each gt, anchor with highest overlap
      labels[gt_argmax_overlaps] = 1
  
      # fg label: above threshold IOU
      labels[max_overlaps >= self._positive_overlap] = 1
  
      if self._clobber_positives:
          # assign bg labels last so that negative labels can clobber positives
          labels[max_overlaps < self._negative_overlap] = 0
  
      # subsample positive labels if we have too many
      num_fg = int(self._rpn_fg_fraction * self._rpn_batch_size)
      fg_inds = np.where(labels == 1)[0]
      if len(fg_inds) > num_fg:
          if self._determininistic_mode:
              disable_inds = fg_inds[: (len(fg_inds) - num_fg)]
          else:
              disable_inds = npr.choice(
                  fg_inds, size=(len(fg_inds) - num_fg), replace=False
              )
          labels[disable_inds] = -1
  
      # subsample negative labels if we have too many
      num_bg = self._rpn_batch_size - np.sum(labels == 1)
      bg_inds = np.where(labels == 0)[0]
      if len(bg_inds) > num_bg:
          if self._determininistic_mode:
              disable_inds = bg_inds[: (len(bg_inds) - num_bg)]
          else:
              disable_inds = npr.choice(
                  bg_inds, size=(len(bg_inds) - num_bg), replace=False
              )
          labels[disable_inds] = -1
  
      bbox_targets = np.zeros((len(inds_inside), 4), dtype=np.float32)
      bbox_targets = _compute_targets(
          anchors, gt_boxes[argmax_overlaps, :]
      )
  
      bbox_inside_weights = np.zeros(
          (len(inds_inside), 4), dtype=np.float32
      )
      bbox_inside_weights[labels == 1, :] = np.array(
          (1.0, 1.0, 1.0, 1.0)
      )
  
      if DEBUG:
          self._sums += bbox_targets[labels == 1, :].sum(axis=0)
          self._squared_sums += (bbox_targets[labels == 1, :] ** 2).sum(
              axis=0
          )
          self._counts += np.sum(labels == 1)
          means = self._sums / self._counts
          stds = np.sqrt(self._squared_sums / self._counts - means**2)
          print("means:")
          print(means)
          print("stdevs:")
          print(stds)
  
      # map up to original set of anchors
      labels = _unmap(labels, total_anchors, inds_inside, fill=-1)
      bbox_targets = _unmap(
          bbox_targets, total_anchors, inds_inside, fill=0
      )
      bbox_inside_weights = _unmap(
          bbox_inside_weights, total_anchors, inds_inside, fill=0
      )
  
      if DEBUG:
          print("rpn: max max_overlap", np.max(max_overlaps))
          print("rpn: num_positive", np.sum(labels == 1))
          print("rpn: num_negative", np.sum(labels == 0))
          self._fg_sum += np.sum(labels == 1)
          self._bg_sum += np.sum(labels == 0)
          self._count += 1
          print("rpn: num_positive avg", self._fg_sum / self._count)
          print("rpn: num_negative avg", self._bg_sum / self._count)
  
      # labels
      labels = labels.reshape((1, height, width, A)).transpose(
          0, 3, 1, 2
      )
      outputs[self.outputs[0]] = np.ascontiguousarray(labels)
  
      # bbox_targets
      bbox_targets = bbox_targets.reshape(
          (1, height, width, A * 4)
      ).transpose(0, 3, 1, 2)
      outputs[self.outputs[1]] = np.ascontiguousarray(bbox_targets)
  
      # bbox_inside_weights
      bbox_inside_weights = bbox_inside_weights.reshape(
          (1, height, width, A * 4)
      ).transpose(0, 3, 1, 2)
      assert bbox_inside_weights.shape[2] == height
      assert bbox_inside_weights.shape[3] == width
      outputs[self.outputs[2]] = np.ascontiguousarray(
          bbox_inside_weights
      )
  
      # No state needs to be passed to backward() so we just pass None
      return None
  
imports: import os
  from cntk import output_variable
  from cntk.ops.functions import UserFunction
  import yaml
  import numpy
  import numpy.random
  from utils.rpn.generate_anchors import generate_anchors
  from utils.rpn.bbox_transform import bbox_transform
  from utils.cython_modules.cython_bbox import bbox_overlaps
label_desc: Use image node_id 3 for calling the AnchorTargetLayer obj's underlying member method code with example usage: obj.forward(arguments, outputs, device, outputs_to_retain) and returns: None