text: # -*- coding: utf-8 -*-
  
  import math
  import torch
  import torch.nn as nn
  
  from .utils import weighted_loss
  __all__ = ['IoULoss', 'GIoULoss', 'DIoULoss', 'CIoULoss']
  
  
  def bbox_overlaps(bboxes1, bboxes2, mode='iou', is_aligned=False, eps=1e-6):
      """Calculate overlap between two set of bboxes.
  
      If ``is_aligned`` is ``False``, then calculate the ious between each bbox
      of bboxes1 and bboxes2, otherwise the ious between each aligned pair of
      bboxes1 and bboxes2.
  
      Args:
          bboxes1 (Tensor): shape (m, 4) in <x1, y1, x2, y2> format or empty.
          bboxes2 (Tensor): shape (n, 4) in <x1, y1, x2, y2> format or empty.
              If is_aligned is ``True``, then m and n must be equal.
          mode (str): "iou" (intersection over union) or iof (intersection over
              foreground).
  
      Returns:
          ious(Tensor): shape (m, n) if is_aligned == False else shape (m, 1)
  
      Example:
          >>> bboxes1 = torch.FloatTensor([
          >>>     [0, 0, 10, 10],
          >>>     [10, 10, 20, 20],
          >>>     [32, 32, 38, 42],
          >>> ])
          >>> bboxes2 = torch.FloatTensor([
          >>>     [0, 0, 10, 20],
          >>>     [0, 10, 10, 19],
          >>>     [10, 10, 20, 20],
          >>> ])
          >>> bbox_overlaps(bboxes1, bboxes2)
          tensor([[0.5000, 0.0000, 0.0000],
                  [0.0000, 0.0000, 1.0000],
                  [0.0000, 0.0000, 0.0000]])
  
      Example:
          >>> empty = torch.FloatTensor([])
          >>> nonempty = torch.FloatTensor([
          >>>     [0, 0, 10, 9],
          >>> ])
          >>> assert tuple(bbox_overlaps(empty, nonempty).shape) == (0, 1)
          >>> assert tuple(bbox_overlaps(nonempty, empty).shape) == (1, 0)
          >>> assert tuple(bbox_overlaps(empty, empty).shape) == (0, 0)
      """
  
      assert mode in ['iou', 'iof']
      # Either the boxes are empty or the length of boxes's last dimenstion is 4
      assert (bboxes1.size(-1) == 4 or bboxes1.size(0) == 0)
      assert (bboxes2.size(-1) == 4 or bboxes2.size(0) == 0)
  
      rows = bboxes1.size(0)
      cols = bboxes2.size(0)
      if is_aligned:
          assert rows == cols
  
      if rows * cols == 0:
          return bboxes1.new(rows, 1) if is_aligned else bboxes1.new(rows, cols)
  
      if is_aligned:
          lt = torch.max(bboxes1[:, :2], bboxes2[:, :2])  # [rows, 2]
          rb = torch.min(bboxes1[:, 2:], bboxes2[:, 2:])  # [rows, 2]
  
          wh = (rb - lt).clamp(min=0)  # [rows, 2]
          overlap = wh[:, 0] * wh[:, 1]
          area1 = (bboxes1[:, 2] - bboxes1[:, 0]) * (
              bboxes1[:, 3] - bboxes1[:, 1])
  
          if mode == 'iou':
              area2 = (bboxes2[:, 2] - bboxes2[:, 0]) * (
                  bboxes2[:, 3] - bboxes2[:, 1])
              union = area1 + area2 - overlap
          else:
              union = area1
      else:
          lt = torch.max(bboxes1[:, None, :2], bboxes2[:, :2])  # [rows, cols, 2]
          rb = torch.min(bboxes1[:, None, 2:], bboxes2[:, 2:])  # [rows, cols, 2]
  
          wh = (rb - lt).clamp(min=0)  # [rows, cols, 2]
          overlap = wh[:, :, 0] * wh[:, :, 1]
          area1 = (bboxes1[:, 2] - bboxes1[:, 0]) * (
              bboxes1[:, 3] - bboxes1[:, 1])
  
          if mode == 'iou':
              area2 = (bboxes2[:, 2] - bboxes2[:, 0]) * (
                  bboxes2[:, 3] - bboxes2[:, 1])
              union = area1[:, None] + area2 - overlap
          else:
              union = area1[:, None]
  
      eps = union.new_tensor([eps])
      union = torch.max(union, eps)
      ious = overlap / union
  
      return ious
  
  
  @weighted_loss
  def iou_loss(pred, target, eps=1e-6):
      """IoU loss.
  
      Computing the IoU loss between a set of predicted bboxes and target bboxes.
      The loss is calculated as negative log of IoU.
  
      Args:
          pred (Tensor): Predicted bboxes of format (x1, y1, x2, y2),
              shape (n, 4).
          target (Tensor): Corresponding gt bboxes, shape (n, 4).
          eps (float): Eps to avoid log(0).
  
      Return:
          Tensor: Loss tensor.
      """
      ious = bbox_overlaps(pred, target, is_aligned=True).clamp(min=eps)
      loss = -ious.log()
      return loss
  
  
  @weighted_loss
  def giou_loss(pred, target, eps=1e-7):
      """
      Generalized Intersection over Union: A Metric and A Loss for
      Bounding Box Regression
      https://arxiv.org/abs/1902.09630
  
      code refer to:
      https://github.com/sfzhang15/ATSS/blob/master/atss_core/modeling/rpn/atss/loss.py#L36
  
      Args:
          pred (Tensor): Predicted bboxes of format (x1, y1, x2, y2),
              shape (n, 4).
          target (Tensor): Corresponding gt bboxes, shape (n, 4).
          eps (float): Eps to avoid log(0).
  
      Return:
          Tensor: Loss tensor.
      """
      # overlap
      lt = torch.max(pred[:, :2], target[:, :2])
      rb = torch.min(pred[:, 2:], target[:, 2:])
      wh = (rb - lt).clamp(min=0)
      overlap = wh[:, 0] * wh[:, 1]
  
      # union
      ap = (pred[:, 2] - pred[:, 0]) * (pred[:, 3] - pred[:, 1])
      ag = (target[:, 2] - target[:, 0]) * (target[:, 3] - target[:, 1])
      union = ap + ag - overlap + eps
  
      # IoU
      ious = overlap / union
  
      # enclose area
      enclose_x1y1 = torch.min(pred[:, :2], target[:, :2])
      enclose_x2y2 = torch.max(pred[:, 2:], target[:, 2:])
      enclose_wh = (enclose_x2y2 - enclose_x1y1).clamp(min=0)
      enclose_area = enclose_wh[:, 0] * enclose_wh[:, 1] + eps
  
      # GIoU
      gious = ious - (enclose_area - union) / enclose_area
      loss = 1 - gious
      return loss
  
  
  @weighted_loss
  def diou_loss(pred, target, eps=1e-7):
      r"""`Implementation of Distance-IoU Loss: Faster and Better
      Learning for Bounding Box Regression, https://arxiv.org/abs/1911.08287`_.
  
      Code is modified from https://github.com/Zzh-tju/DIoU.
  
      Args:
          pred (Tensor): Predicted bboxes of format (x1, y1, x2, y2),
              shape (n, 4).
          target (Tensor): Corresponding gt bboxes, shape (n, 4).
          eps (float): Eps to avoid log(0).
      Return:
          Tensor: Loss tensor.
      """
      # overlap
      lt = torch.max(pred[:, :2], target[:, :2])
      rb = torch.min(pred[:, 2:], target[:, 2:])
      wh = (rb - lt).clamp(min=0)
      overlap = wh[:, 0] * wh[:, 1]
  
      # union
      ap = (pred[:, 2] - pred[:, 0]) * (pred[:, 3] - pred[:, 1])
      ag = (target[:, 2] - target[:, 0]) * (target[:, 3] - target[:, 1])
      union = ap + ag - overlap + eps
  
      # IoU
      ious = overlap / union
  
      # enclose area
      enclose_x1y1 = torch.min(pred[:, :2], target[:, :2])
      enclose_x2y2 = torch.max(pred[:, 2:], target[:, 2:])
      enclose_wh = (enclose_x2y2 - enclose_x1y1).clamp(min=0)
  
      cw = enclose_wh[:, 0]
      ch = enclose_wh[:, 1]
  
      c2 = cw**2 + ch**2 + eps
  
      b1_x1, b1_y1 = pred[:, 0], pred[:, 1]
      b1_x2, b1_y2 = pred[:, 2], pred[:, 3]
      b2_x1, b2_y1 = target[:, 0], target[:, 1]
      b2_x2, b2_y2 = target[:, 2], target[:, 3]
  
      left = ((b2_x1 + b2_x2) - (b1_x1 + b1_x2))**2 / 4
      right = ((b2_y1 + b2_y2) - (b1_y1 + b1_y2))**2 / 4
      rho2 = left + right
  
      # DIoU
      dious = ious - rho2 / c2
      loss = 1 - dious
      return loss
  
  
  @weighted_loss
  def ciou_loss(pred, target, eps=1e-7):
      r"""`Implementation of paper `Enhancing Geometric Factors into
      Model Learning and Inference for Object Detection and Instance
      Segmentation <https://arxiv.org/abs/2005.03572>`_.
  
      Code is modified from https://github.com/Zzh-tju/CIoU.
  
      Args:
          pred (Tensor): Predicted bboxes of format (x1, y1, x2, y2),
              shape (n, 4).
          target (Tensor): Corresponding gt bboxes, shape (n, 4).
          eps (float): Eps to avoid log(0).
      Return:
          Tensor: Loss tensor.
      """
      # overlap
      lt = torch.max(pred[:, :2], target[:, :2])
      rb = torch.min(pred[:, 2:], target[:, 2:])
      wh = (rb - lt).clamp(min=0)
      overlap = wh[:, 0] * wh[:, 1]
  
      # union
      ap = (pred[:, 2] - pred[:, 0]) * (pred[:, 3] - pred[:, 1])
      ag = (target[:, 2] - target[:, 0]) * (target[:, 3] - target[:, 1])
      union = ap + ag - overlap + eps
  
      # IoU
      ious = overlap / union
  
      # enclose area
      enclose_x1y1 = torch.min(pred[:, :2], target[:, :2])
      enclose_x2y2 = torch.max(pred[:, 2:], target[:, 2:])
      enclose_wh = (enclose_x2y2 - enclose_x1y1).clamp(min=0)
  
      cw = enclose_wh[:, 0]
      ch = enclose_wh[:, 1]
  
      c2 = cw**2 + ch**2 + eps
  
      b1_x1, b1_y1 = pred[:, 0], pred[:, 1]
      b1_x2, b1_y2 = pred[:, 2], pred[:, 3]
      b2_x1, b2_y1 = target[:, 0], target[:, 1]
      b2_x2, b2_y2 = target[:, 2], target[:, 3]
  
      w1, h1 = b1_x2 - b1_x1, b1_y2 - b1_y1 + eps
      w2, h2 = b2_x2 - b2_x1, b2_y2 - b2_y1 + eps
  
      left = ((b2_x1 + b2_x2) - (b1_x1 + b1_x2))**2 / 4
      right = ((b2_y1 + b2_y2) - (b1_y1 + b1_y2))**2 / 4
      rho2 = left + right
  
      factor = 4 / math.pi**2
      v = factor * torch.pow(torch.atan(w2 / h2) - torch.atan(w1 / h1), 2)
  
      # CIoU
      cious = ious - (rho2 / c2 + v**2 / (1 - ious + v))
      loss = 1 - cious
      return loss
  
  
  class IoULoss(nn.Module):
  
      def __init__(self, eps=1e-6, reduction='mean', loss_weight=1.0):
          super(IoULoss, self).__init__()
          self.eps = eps
          self.reduction = reduction
          self.loss_weight = loss_weight
  
      def forward(self,
                  pred,
                  target,
                  weight=None,
                  avg_factor=None,
                  reduction_override=None,
                  **kwargs):
          assert reduction_override in (None, 'none', 'mean', 'sum')
          reduction = (
              reduction_override if reduction_override else self.reduction)
          if (weight is not None) and (not torch.any(weight > 0)) and (
                  reduction != 'none'):
              return (pred * weight).sum()  # 0
          if weight is not None and weight.dim() > 1:
              # TODO: remove this in the future
              # reduce the weight of shape (n, 4) to (n,) to match the
              # iou_loss of shape (n,)
              assert weight.shape == pred.shape
              weight = weight.mean(-1)
          loss = self.loss_weight * iou_loss(
              pred,
              target,
              weight,
              eps=self.eps,
              reduction=reduction,
              avg_factor=avg_factor,
              **kwargs)
          return loss
  
  
  class GIoULoss(nn.Module):
  
      def __init__(self, eps=1e-6, reduction='mean', loss_weight=1.0):
          super(GIoULoss, self).__init__()
          self.eps = eps
          self.reduction = reduction
          self.loss_weight = loss_weight
  
      def forward(self,
                  pred,
                  target,
                  weight=None,
                  avg_factor=None,
                  reduction_override=None,
                  **kwargs):
          if weight is not None and not torch.any(weight > 0):
              return (pred * weight).sum()  # 0
          assert reduction_override in (None, 'none', 'mean', 'sum')
          reduction = (reduction_override if reduction_override else self.reduction)
          if weight is not None and weight.dim() > 1:
              # TODO: remove this in the future
              # reduce the weight of shape (n, 4) to (n,) to match the
              # giou_loss of shape (n,)
              assert weight.shape == pred.shape
              weight = weight.mean(-1)
          loss = self.loss_weight * giou_loss(
              pred,
              target,
              weight,
              eps=self.eps,
              reduction=reduction,
              avg_factor=avg_factor,
              **kwargs)
          return loss
  
  
  class DIoULoss(nn.Module):
  
      def __init__(self, eps=1e-6, reduction='mean', loss_weight=1.0):
          super(DIoULoss, self).__init__()
          self.eps = eps
          self.reduction = reduction
          self.loss_weight = loss_weight
  
      def forward(self,
                  pred,
                  target,
                  weight=None,
                  avg_factor=None,
                  reduction_override=None,
                  **kwargs):
          if weight is not None and not torch.any(weight > 0):
              return (pred * weight).sum()  # 0
          assert reduction_override in (None, 'none', 'mean', 'sum')
          reduction = (
              reduction_override if reduction_override else self.reduction)
          if weight is not None and weight.dim() > 1:
              # TODO: remove this in the future
              # reduce the weight of shape (n, 4) to (n,) to match the
              # giou_loss of shape (n,)
              assert weight.shape == pred.shape
              weight = weight.mean(-1)
          loss = self.loss_weight * diou_loss(
              pred,
              target,
              weight,
              eps=self.eps,
              reduction=reduction,
              avg_factor=avg_factor,
              **kwargs)
          return loss
  
  
  class CIoULoss(nn.Module):
  
      def __init__(self, eps=1e-6, reduction='mean', loss_weight=1.0):
          super(CIoULoss, self).__init__()
          self.eps = eps
          self.reduction = reduction
          self.loss_weight = loss_weight
  
      def forward(self,
                  pred,
                  target,
                  weight=None,
                  avg_factor=None,
                  reduction_override=None,
                  **kwargs):
          if weight is not None and not torch.any(weight > 0):
              return (pred * weight).sum()  # 0
          assert reduction_override in (None, 'none', 'mean', 'sum')
          reduction = (
              reduction_override if reduction_override else self.reduction)
          if weight is not None and weight.dim() > 1:
              # TODO: remove this in the future
              # reduce the weight of shape (n, 4) to (n,) to match the
              # giou_loss of shape (n,)
              assert weight.shape == pred.shape
              weight = weight.mean(-1)
          loss = self.loss_weight * ciou_loss(
              pred,
              target,
              weight,
              eps=self.eps,
              reduction=reduction,
              avg_factor=avg_factor,
              **kwargs)
          return loss
  