-
Notifications
You must be signed in to change notification settings - Fork 0
/
iou.py
142 lines (125 loc) · 5.74 KB
/
iou.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
import math
import torch
class IoU_Cal:
''' pred, target: x0,y0,x1,y1
monotonous: {
None: origin
True: monotonic FM
False: non-monotonic FM
}
momentum: The momentum of running mean (This can be set by the function <momentum_estimation>)'''
iou_mean = 1.
monotonous = False
momentum = 1 - pow(0.05, 1 / (890 * 34))
_is_train = True
@classmethod
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 __getattr__(self, item):
if callable(self._fget[item]):
self._fget[item] = self._fget[item]()
return self._fget[item]
@classmethod
def train(cls):
cls._is_train = True
@classmethod
def eval(cls):
cls._is_train = False
@classmethod
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
@classmethod
def IoU(cls, pred, target, self=None):
self = self if self else cls(pred, target)
return self.iou
@classmethod
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)
@classmethod
def EIoU(cls, pred, target, self=None):
self = self if self else cls(pred, target)
penalty = self.l2_center / self.l2_box \
+ torch.square(self.d_center / self.wh_box).sum(dim=-1)
return self._scaled_loss(self.iou + penalty)
@classmethod
def GIoU(cls, pred, target, self=None):
self = self if self else cls(pred, target)
return (self.iou + (self.s_box - self.s_union) / self.s_box)
@classmethod
def Proposed(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)
giou = (self.s_box - self.s_union) / self.s_box
ciou = (self.l2_center / self.l2_box + alpha.detach() * v)
return self._scaled_loss((self.iou + ((giou + ciou)/8)))
@classmethod
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)
@classmethod
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)