Spaces:
Sleeping
Sleeping
| import math | |
| import torch | |
| class IoU_Cal: | |
| ''' pred, target: x0,y0,x1,y1 | |
| monotonous: { | |
| None: origin v1 | |
| True: monotonic FM v2 | |
| False: non-monotonic FM v3 | |
| } | |
| momentum: The momentum of running mean (This can be set by the function <momentum_estimation>)''' | |
| iou_mean = 1. | |
| monotonous = True #v1:none v2:true v3:false | |
| momentum = 1 - 0.5 ** (1 / 7000) | |
| _is_train = True | |
| def momentum_estimation(cls, n, t): | |
| ''' n: Number of batches per training epoch | |
| t: The epoch when mAP's ascension slowed significantly''' | |
| time_to_real = n * t | |
| cls.momentum = 1 - pow(0.05, 1 / time_to_real) | |
| return cls.momentum | |
| def __init__(self, pred, target): | |
| self.pred, self.target = pred, target | |
| self._fget = { | |
| # x,y,w,h | |
| 'pred_xy': lambda: (self.pred[..., :2] + self.pred[..., 2: 4]) / 2, | |
| 'pred_wh': lambda: self.pred[..., 2: 4] - self.pred[..., :2], | |
| 'target_xy': lambda: (self.target[..., :2] + self.target[..., 2: 4]) / 2, | |
| 'target_wh': lambda: self.target[..., 2: 4] - self.target[..., :2], | |
| # x0,y0,x1,y1 | |
| 'min_coord': lambda: torch.minimum(self.pred[..., :4], self.target[..., :4]), | |
| 'max_coord': lambda: torch.maximum(self.pred[..., :4], self.target[..., :4]), | |
| # The overlapping region | |
| 'wh_inter': lambda: torch.relu(self.min_coord[..., 2: 4] - self.max_coord[..., :2]), | |
| 's_inter': lambda: torch.prod(self.wh_inter, dim=-1), | |
| # The area covered | |
| 's_union': lambda: torch.prod(self.pred_wh, dim=-1) + | |
| torch.prod(self.target_wh, dim=-1) - self.s_inter, | |
| # The smallest enclosing box | |
| 'wh_box': lambda: self.max_coord[..., 2: 4] - self.min_coord[..., :2], | |
| 's_box': lambda: torch.prod(self.wh_box, dim=-1), | |
| 'l2_box': lambda: torch.square(self.wh_box).sum(dim=-1), | |
| # The central points' connection of the bounding boxes | |
| 'd_center': lambda: self.pred_xy - self.target_xy, | |
| 'l2_center': lambda: torch.square(self.d_center).sum(dim=-1), | |
| # IoU | |
| 'iou': lambda: 1 - self.s_inter / self.s_union | |
| } | |
| self._update(self) | |
| def __setitem__(self, key, value): | |
| self._fget[key] = value | |
| def __getattr__(self, item): | |
| if callable(self._fget[item]): | |
| self._fget[item] = self._fget[item]() | |
| return self._fget[item] | |
| def train(cls): | |
| cls._is_train = True | |
| def eval(cls): | |
| cls._is_train = False | |
| def _update(cls, self): | |
| if cls._is_train: cls.iou_mean = (1 - cls.momentum) * cls.iou_mean + \ | |
| cls.momentum * self.iou.detach().mean().item() | |
| def _scaled_loss(self, loss, alpha=1.9, delta=3): | |
| if isinstance(self.monotonous, bool): | |
| beta = self.iou.detach() / self.iou_mean | |
| if self.monotonous: | |
| loss *= beta.sqrt() | |
| else: | |
| divisor = delta * torch.pow(alpha, beta - delta) | |
| loss *= beta / divisor | |
| return loss | |
| def IoU(cls, pred, target, self=None): | |
| self = self if self else cls(pred, target) | |
| return self.iou | |
| def WIoU(cls, pred, target, self=None): | |
| self = self if self else cls(pred, target) | |
| dist = torch.exp(self.l2_center / self.l2_box.detach()) | |
| return self._scaled_loss(dist * self.iou) | |
| def EIoU(cls, pred, target, self=None): | |
| self = self if self else cls(pred, target) | |
| penalty = self.l2_center / self.l2_box.detach() \ | |
| + torch.square(self.d_center / self.wh_box).sum(dim=-1) | |
| return self._scaled_loss(self.iou + penalty) | |
| def GIoU(cls, pred, target, self=None): | |
| self = self if self else cls(pred, target) | |
| return self._scaled_loss(self.iou + (self.s_box - self.s_union) / self.s_box) | |
| def DIoU(cls, pred, target, self=None): | |
| self = self if self else cls(pred, target) | |
| return self._scaled_loss(self.iou + self.l2_center / self.l2_box) | |
| def CIoU(cls, pred, target, eps=1e-4, self=None): | |
| self = self if self else cls(pred, target) | |
| v = 4 / math.pi ** 2 * \ | |
| (torch.atan(self.pred_wh[..., 0] / (self.pred_wh[..., 1] + eps)) - | |
| torch.atan(self.target_wh[..., 0] / (self.target_wh[..., 1] + eps))) ** 2 | |
| alpha = v / (self.iou + v) | |
| return self._scaled_loss(self.iou + self.l2_center / self.l2_box + alpha.detach() * v) | |
| def SIoU(cls, pred, target, theta=4, self=None): | |
| self = self if self else cls(pred, target) | |
| # Angle Cost | |
| angle = torch.arcsin(torch.abs(self.d_center).min(dim=-1)[0] / (self.l2_center.sqrt() + 1e-4)) | |
| angle = torch.sin(2 * angle) - 2 | |
| # Dist Cost | |
| dist = angle[..., None] * torch.square(self.d_center / self.wh_box) | |
| dist = 2 - torch.exp(dist[..., 0]) - torch.exp(dist[..., 1]) | |
| # Shape Cost | |
| d_shape = torch.abs(self.pred_wh - self.target_wh) | |
| big_shape = torch.maximum(self.pred_wh, self.target_wh) | |
| w_shape = 1 - torch.exp(- d_shape[..., 0] / big_shape[..., 0]) | |
| h_shape = 1 - torch.exp(- d_shape[..., 1] / big_shape[..., 1]) | |
| shape = w_shape ** theta + h_shape ** theta | |
| return self._scaled_loss(self.iou + (dist + shape) / 2) |