text: import copy
  import warnings
  
  import torch
  import torch.nn as nn
  import torch.nn.functional as F
  from mmcv import ConfigDict
  from mmcv.cnn import normal_init
  from mmcv.ops import nms
  
  from ..builder import HEADS
  from .guided_anchor_head import GuidedAnchorHead
  from .rpn_test_mixin import RPNTestMixin
  
  
  @HEADS.register_module()
  class GARPNHead(RPNTestMixin, GuidedAnchorHead):
      """Guided-Anchor-based RPN head."""
  
      def __init__(self, in_channels, **kwargs):
          super(GARPNHead, self).__init__(1, in_channels, **kwargs)
  
      def _init_layers(self):
          """Initialize layers of the head."""
          self.rpn_conv = nn.Conv2d(
              self.in_channels, self.feat_channels, 3, padding=1)
          super(GARPNHead, self)._init_layers()
  
      def init_weights(self):
          """Initialize weights of the head."""
          normal_init(self.rpn_conv, std=0.01)
          super(GARPNHead, self).init_weights()
  
      def forward_single(self, x):
          """Forward feature of a single scale level."""
  
          x = self.rpn_conv(x)
          x = F.relu(x, inplace=True)
          (cls_score, bbox_pred, shape_pred,
           loc_pred) = super(GARPNHead, self).forward_single(x)
          return cls_score, bbox_pred, shape_pred, loc_pred
  
      def loss(self,
               cls_scores,
               bbox_preds,
               shape_preds,
               loc_preds,
               gt_bboxes,
               img_metas,
               gt_bboxes_ignore=None):
          losses = super(GARPNHead, self).loss(
              cls_scores,
              bbox_preds,
              shape_preds,
              loc_preds,
              gt_bboxes,
              None,
              img_metas,
              gt_bboxes_ignore=gt_bboxes_ignore)
          return dict(
              loss_rpn_cls=losses['loss_cls'],
              loss_rpn_bbox=losses['loss_bbox'],
              loss_anchor_shape=losses['loss_shape'],
              loss_anchor_loc=losses['loss_loc'])
  
      def _get_bboxes_single(self,
                             cls_scores,
                             bbox_preds,
                             mlvl_anchors,
                             mlvl_masks,
                             img_shape,
                             scale_factor,
                             cfg,
                             rescale=False):
          cfg = self.test_cfg if cfg is None else cfg
  
          cfg = copy.deepcopy(cfg)
  
          # deprecate arguments warning
          if 'nms' not in cfg or 'max_num' in cfg or 'nms_thr' in cfg:
              warnings.warn(
                  'In rpn_proposal or test_cfg, '
                  'nms_thr has been moved to a dict named nms as '
                  'iou_threshold, max_num has been renamed as max_per_img, '
                  'name of original arguments and the way to specify '
                  'iou_threshold of NMS will be deprecated.')
          if 'nms' not in cfg:
              cfg.nms = ConfigDict(dict(type='nms', iou_threshold=cfg.nms_thr))
          if 'max_num' in cfg:
              if 'max_per_img' in cfg:
                  assert cfg.max_num == cfg.max_per_img, f'You ' \
                      f'set max_num and max_per_img at the same time, ' \
                      f'but get {cfg.max_num} ' \
                      f'and {cfg.max_per_img} respectively' \
                      'Please delete max_num which will be deprecated.'
              else:
                  cfg.max_per_img = cfg.max_num
          if 'nms_thr' in cfg:
              assert cfg.nms.iou_threshold == cfg.nms_thr, f'You set ' \
                  f'iou_threshold in nms and ' \
                  f'nms_thr at the same time, but get ' \
                  f'{cfg.nms.iou_threshold} and {cfg.nms_thr}' \
                  f' respectively. Please delete the ' \
                  f'nms_thr which will be deprecated.'
  
          assert cfg.nms.get('type', 'nms') == 'nms', 'GARPNHead only support ' \
              'naive nms.'
  
          mlvl_proposals = []
          for idx in range(len(cls_scores)):
              rpn_cls_score = cls_scores[idx]
              rpn_bbox_pred = bbox_preds[idx]
              anchors = mlvl_anchors[idx]
              mask = mlvl_masks[idx]
              assert rpn_cls_score.size()[-2:] == rpn_bbox_pred.size()[-2:]
              # if no location is kept, end.
              if mask.sum() == 0:
                  continue
              rpn_cls_score = rpn_cls_score.permute(1, 2, 0)
              if self.use_sigmoid_cls:
                  rpn_cls_score = rpn_cls_score.reshape(-1)
                  scores = rpn_cls_score.sigmoid()
              else:
                  rpn_cls_score = rpn_cls_score.reshape(-1, 2)
                  # remind that we set FG labels to [0, num_class-1]
                  # since mmdet v2.0
                  # BG cat_id: num_class
                  scores = rpn_cls_score.softmax(dim=1)[:, :-1]
              # filter scores, bbox_pred w.r.t. mask.
              # anchors are filtered in get_anchors() beforehand.
              scores = scores[mask]
              rpn_bbox_pred = rpn_bbox_pred.permute(1, 2, 0).reshape(-1,
                                                                     4)[mask, :]
              if scores.dim() == 0:
                  rpn_bbox_pred = rpn_bbox_pred.unsqueeze(0)
                  anchors = anchors.unsqueeze(0)
                  scores = scores.unsqueeze(0)
              # filter anchors, bbox_pred, scores w.r.t. scores
              if cfg.nms_pre > 0 and scores.shape[0] > cfg.nms_pre:
                  _, topk_inds = scores.topk(cfg.nms_pre)
                  rpn_bbox_pred = rpn_bbox_pred[topk_inds, :]
                  anchors = anchors[topk_inds, :]
                  scores = scores[topk_inds]
              # get proposals w.r.t. anchors and rpn_bbox_pred
              proposals = self.bbox_coder.decode(
                  anchors, rpn_bbox_pred, max_shape=img_shape)
              # filter out too small bboxes
              if cfg.min_bbox_size > 0:
                  w = proposals[:, 2] - proposals[:, 0]
                  h = proposals[:, 3] - proposals[:, 1]
                  valid_inds = torch.nonzero(
                      (w >= cfg.min_bbox_size) & (h >= cfg.min_bbox_size),
                      as_tuple=False).squeeze()
                  proposals = proposals[valid_inds, :]
                  scores = scores[valid_inds]
              # NMS in current level
              proposals, _ = nms(proposals, scores, cfg.nms.iou_threshold)
              proposals = proposals[:cfg.nms_post, :]
              mlvl_proposals.append(proposals)
          proposals = torch.cat(mlvl_proposals, 0)
          if cfg.get('nms_across_levels', False):
              # NMS across multi levels
              proposals, _ = nms(proposals[:, :4], proposals[:, -1],
                                 cfg.nms.iou_threshold)
              proposals = proposals[:cfg.max_per_img, :]
          else:
              scores = proposals[:, 4]
              num = min(cfg.max_per_img, proposals.shape[0])
              _, topk_inds = scores.topk(num)
              proposals = proposals[topk_inds, :]
          return proposals
  