Fabrice-TIERCELIN commited on
Commit
0c6041e
1 Parent(s): 7933a16

Delete clipseg/metrics.py

Browse files
Files changed (1) hide show
  1. clipseg/metrics.py +0 -271
clipseg/metrics.py DELETED
@@ -1,271 +0,0 @@
1
- from torch.functional import Tensor
2
- from general_utils import log
3
- from collections import defaultdict
4
- import numpy as np
5
-
6
- import torch
7
- from torch.nn import functional as nnf
8
-
9
-
10
- class BaseMetric(object):
11
-
12
- def __init__(self, metric_names, pred_range=None, gt_index=0, pred_index=0, eval_intermediate=True,
13
- eval_validation=True):
14
- self._names = tuple(metric_names)
15
- self._eval_intermediate = eval_intermediate
16
- self._eval_validation = eval_validation
17
-
18
- self._pred_range = pred_range
19
- self._pred_index = pred_index
20
- self._gt_index = gt_index
21
-
22
- self.predictions = []
23
- self.ground_truths = []
24
-
25
- def eval_intermediate(self):
26
- return self._eval_intermediate
27
-
28
- def eval_validation(self):
29
- return self._eval_validation
30
-
31
- def names(self):
32
- return self._names
33
-
34
- def add(self, predictions, ground_truth):
35
- raise NotImplementedError
36
-
37
- def value(self):
38
- raise NotImplementedError
39
-
40
- def scores(self):
41
- # similar to value but returns dict
42
- value = self.value()
43
- if type(value) == dict:
44
- return value
45
- else:
46
- assert type(value) in {list, tuple}
47
- return list(zip(self.names(), self.value()))
48
-
49
- def _get_pred_gt(self, predictions, ground_truth):
50
- pred = predictions[self._pred_index]
51
- gt = ground_truth[self._gt_index]
52
-
53
- if self._pred_range is not None:
54
- pred = pred[:, self._pred_range[0]: self._pred_range[1]]
55
-
56
- return pred, gt
57
-
58
-
59
- class FixedIntervalMetrics(BaseMetric):
60
-
61
- def __init__(self, sigmoid=False, ignore_mask=False, resize_to=None,
62
- resize_pred=None, n_values=51, custom_threshold=None):
63
-
64
-
65
- super().__init__(('ap', 'best_fgiou', 'best_miou', 'fgiou0.5', 'fgiou0.1', 'mean_iou_0p5', 'mean_iou_0p1', 'best_biniou', 'biniou_0.5', 'fgiou_thresh'))
66
- self.intersections = []
67
- self.unions = []
68
- # self.threshold = threshold
69
- self.sigmoid = sigmoid
70
- self.resize_to = resize_to
71
- self.resize_pred = resize_pred # resize prediction to match ground truth
72
- self.class_count = defaultdict(lambda: 0)
73
- self.per_class = defaultdict(lambda : [0,0])
74
- self.ignore_mask = ignore_mask
75
- self.custom_threshold = custom_threshold
76
-
77
- self.scores_ap = []
78
- self.scores_iou = []
79
- self.gts, self.preds = [], []
80
- self.classes = []
81
-
82
- # [1:-1] ignores 0 and 1
83
- self.threshold_values = np.linspace(0, 1, n_values)[1:-1]
84
-
85
- self.metrics = dict(tp=[], fp=[], fn=[], tn=[])
86
-
87
- def add(self, pred, gt):
88
-
89
- pred_batch = pred[0].cpu()
90
-
91
- if self.sigmoid:
92
- pred_batch = torch.sigmoid(pred_batch)
93
-
94
- gt_batch = gt[0].cpu()
95
- mask_batch = gt[1] if len(gt) > 1 and not self.ignore_mask and gt[1].numel() > 0 else ([None] * len(pred_batch))
96
- cls_batch = gt[2] if len(gt) > 2 else [None] * len(pred_batch)
97
-
98
- if self.resize_to is not None:
99
- gt_batch = nnf.interpolate(gt_batch, self.resize_to, mode='nearest')
100
- pred_batch = nnf.interpolate(pred_batch, self.resize_to, mode='bilinear', align_corners=False)
101
-
102
- if isinstance(cls_batch, torch.Tensor):
103
- cls_batch = cls_batch.cpu().numpy().tolist()
104
-
105
- assert len(gt_batch) == len(pred_batch) == len(cls_batch), f'{len(gt_batch)} {len(pred_batch)} {len(cls_batch)}'
106
-
107
- for predictions, ground_truth, mask, cls in zip(pred_batch, gt_batch, mask_batch, cls_batch):
108
-
109
- if self.resize_pred:
110
- predictions = nnf.interpolate(predictions.unsqueeze(0).float(), size=ground_truth.size()[-2:], mode='bilinear', align_corners=True)
111
-
112
- p = predictions.flatten()
113
- g = ground_truth.flatten()
114
-
115
- assert len(p) == len(g)
116
-
117
- if mask is not None:
118
- m = mask.flatten().bool()
119
- p = p[m]
120
- g = g[m]
121
-
122
- p_sorted = p.sort()
123
- p = p_sorted.values
124
- g = g[p_sorted.indices]
125
-
126
- tps, fps, fns, tns = [], [], [], []
127
- for thresh in self.threshold_values:
128
-
129
- valid = torch.where(p > thresh)[0]
130
- if len(valid) > 0:
131
- n = int(valid[0])
132
- else:
133
- n = len(g)
134
-
135
- fn = int(g[:n].sum())
136
- tp = int(g[n:].sum())
137
- fns += [fn]
138
- tns += [n - fn]
139
- tps += [tp]
140
- fps += [len(g) - n - tp]
141
-
142
- self.metrics['tp'] += [tps]
143
- self.metrics['fp'] += [fps]
144
- self.metrics['fn'] += [fns]
145
- self.metrics['tn'] += [tns]
146
-
147
- self.classes += [cls.item() if isinstance(cls, torch.Tensor) else cls]
148
-
149
- def value(self):
150
-
151
- import time
152
- t_start = time.time()
153
-
154
- if set(self.classes) == set([None]):
155
- all_classes = None
156
- log.warning('classes were not provided, cannot compute mIoU')
157
- else:
158
- all_classes = set(int(c) for c in self.classes)
159
- # log.info(f'compute metrics for {len(all_classes)} classes')
160
-
161
- summed = {k: [sum([self.metrics[k][i][j]
162
- for i in range(len(self.metrics[k]))])
163
- for j in range(len(self.threshold_values))]
164
- for k in self.metrics.keys()}
165
-
166
- if all_classes is not None:
167
-
168
- assert len(self.classes) == len(self.metrics['tp']) == len(self.metrics['fn'])
169
- # group by class
170
- metrics_by_class = {c: {k: [] for k in self.metrics.keys()} for c in all_classes}
171
- for i in range(len(self.metrics['tp'])):
172
- for k in self.metrics.keys():
173
- metrics_by_class[self.classes[i]][k] += [self.metrics[k][i]]
174
-
175
- # sum over all instances within the classes
176
- summed_by_cls = {k: {c: np.array(metrics_by_class[c][k]).sum(0).tolist() for c in all_classes} for k in self.metrics.keys()}
177
-
178
-
179
- # Compute average precision
180
-
181
- assert (np.array(summed['fp']) + np.array(summed['tp']) ).sum(), 'no predictions is made'
182
-
183
- # only consider values where a prediction is made
184
- precisions = [summed['tp'][j] / (1 + summed['tp'][j] + summed['fp'][j]) for j in range(len(self.threshold_values))
185
- if summed['tp'][j] + summed['fp'][j] > 0]
186
- recalls = [summed['tp'][j] / (1 + summed['tp'][j] + summed['fn'][j]) for j in range(len(self.threshold_values))
187
- if summed['tp'][j] + summed['fp'][j] > 0]
188
-
189
- # remove duplicate recall-precision-pairs (and sort by recall value)
190
- recalls, precisions = zip(*sorted(list(set(zip(recalls, precisions))), key=lambda x: x[0]))
191
-
192
- from scipy.integrate import simps
193
- ap = simps(precisions, recalls)
194
-
195
- # Compute best IoU
196
- fgiou_scores = [summed['tp'][j] / (1 + summed['tp'][j] + summed['fp'][j] + summed['fn'][j]) for j in range(len(self.threshold_values))]
197
-
198
- biniou_scores = [
199
- 0.5*(summed['tp'][j] / (1 + summed['tp'][j] + summed['fp'][j] + summed['fn'][j])) +
200
- 0.5*(summed['tn'][j] / (1 + summed['tn'][j] + summed['fn'][j] + summed['fp'][j]))
201
- for j in range(len(self.threshold_values))
202
- ]
203
-
204
- index_0p5 = self.threshold_values.tolist().index(0.5)
205
- index_0p1 = self.threshold_values.tolist().index(0.1)
206
- index_0p2 = self.threshold_values.tolist().index(0.2)
207
- index_0p3 = self.threshold_values.tolist().index(0.3)
208
-
209
- if self.custom_threshold is not None:
210
- index_ct = self.threshold_values.tolist().index(self.custom_threshold)
211
-
212
- if all_classes is not None:
213
- # mean IoU
214
- mean_ious = [np.mean([summed_by_cls['tp'][c][j] / (1 + summed_by_cls['tp'][c][j] + summed_by_cls['fp'][c][j] + summed_by_cls['fn'][c][j])
215
- for c in all_classes])
216
- for j in range(len(self.threshold_values))]
217
-
218
- mean_iou_dict = {
219
- 'miou_best': max(mean_ious) if all_classes is not None else None,
220
- 'miou_0.5': mean_ious[index_0p5] if all_classes is not None else None,
221
- 'miou_0.1': mean_ious[index_0p1] if all_classes is not None else None,
222
- 'miou_0.2': mean_ious[index_0p2] if all_classes is not None else None,
223
- 'miou_0.3': mean_ious[index_0p3] if all_classes is not None else None,
224
- 'miou_best_t': self.threshold_values[np.argmax(mean_ious)],
225
- 'mean_iou_ct': mean_ious[index_ct] if all_classes is not None and self.custom_threshold is not None else None,
226
- 'mean_iou_scores': mean_ious,
227
- }
228
-
229
- print(f'metric computation on {(len(all_classes) if all_classes is not None else "no")} classes took {time.time() - t_start:.1f}s')
230
-
231
- return {
232
- 'ap': ap,
233
-
234
- # fgiou
235
- 'fgiou_best': max(fgiou_scores),
236
- 'fgiou_0.5': fgiou_scores[index_0p5],
237
- 'fgiou_0.1': fgiou_scores[index_0p1],
238
- 'fgiou_0.2': fgiou_scores[index_0p2],
239
- 'fgiou_0.3': fgiou_scores[index_0p3],
240
- 'fgiou_best_t': self.threshold_values[np.argmax(fgiou_scores)],
241
-
242
- # mean iou
243
-
244
-
245
- # biniou
246
- 'biniou_best': max(biniou_scores),
247
- 'biniou_0.5': biniou_scores[index_0p5],
248
- 'biniou_0.1': biniou_scores[index_0p1],
249
- 'biniou_0.2': biniou_scores[index_0p2],
250
- 'biniou_0.3': biniou_scores[index_0p3],
251
- 'biniou_best_t': self.threshold_values[np.argmax(biniou_scores)],
252
-
253
- # custom threshold
254
- 'fgiou_ct': fgiou_scores[index_ct] if self.custom_threshold is not None else None,
255
- 'biniou_ct': biniou_scores[index_ct] if self.custom_threshold is not None else None,
256
- 'ct': self.custom_threshold,
257
-
258
- # statistics
259
- 'fgiou_scores': fgiou_scores,
260
- 'biniou_scores': biniou_scores,
261
- 'precision_recall_curve': sorted(list(set(zip(recalls, precisions)))),
262
- 'summed_statistics': summed,
263
- 'summed_by_cls_statistics': summed_by_cls,
264
-
265
- **mean_iou_dict
266
- }
267
-
268
- # ('ap', 'best_fgiou', 'best_miou', 'fgiou0.5', 'fgiou0.1', 'mean_iou_0p5', 'mean_iou_0p1', 'best_biniou', 'biniou_0.5', 'fgiou_thresh'
269
-
270
- # return ap, best_fgiou, best_mean_iou, iou_0p5, iou_0p1, mean_iou_0p5, mean_iou_0p1, best_biniou, biniou0p5, best_fgiou_thresh, {'summed': summed, 'summed_by_cls': summed_by_cls}
271
-