valor-lite 0.33.7__py3-none-any.whl → 0.33.9__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- valor_lite/LICENSE +21 -0
- valor_lite/classification/annotation.py +30 -2
- valor_lite/classification/computation.py +31 -52
- valor_lite/classification/manager.py +230 -323
- valor_lite/classification/metric.py +273 -50
- valor_lite/object_detection/annotation.py +274 -0
- valor_lite/{detection → object_detection}/computation.py +130 -92
- valor_lite/{detection → object_detection}/manager.py +425 -769
- valor_lite/object_detection/metric.py +789 -0
- valor_lite/semantic_segmentation/__init__.py +27 -0
- valor_lite/semantic_segmentation/annotation.py +96 -0
- valor_lite/semantic_segmentation/computation.py +186 -0
- valor_lite/semantic_segmentation/manager.py +549 -0
- valor_lite/semantic_segmentation/metric.py +278 -0
- valor_lite/text_generation/__init__.py +0 -0
- valor_lite-0.33.9.dist-info/METADATA +179 -0
- valor_lite-0.33.9.dist-info/RECORD +24 -0
- valor_lite/detection/annotation.py +0 -98
- valor_lite/detection/metric.py +0 -408
- valor_lite-0.33.7.dist-info/METADATA +0 -41
- valor_lite-0.33.7.dist-info/RECORD +0 -17
- /valor_lite/{detection → object_detection}/__init__.py +0 -0
- {valor_lite-0.33.7.dist-info → valor_lite-0.33.9.dist-info}/LICENSE +0 -0
- {valor_lite-0.33.7.dist-info → valor_lite-0.33.9.dist-info}/WHEEL +0 -0
- {valor_lite-0.33.7.dist-info → valor_lite-0.33.9.dist-info}/top_level.txt +0 -0
|
@@ -1,17 +1,18 @@
|
|
|
1
1
|
from collections import defaultdict
|
|
2
2
|
from dataclasses import dataclass
|
|
3
|
+
from typing import Type
|
|
3
4
|
|
|
4
5
|
import numpy as np
|
|
6
|
+
import valor_lite.object_detection.annotation as annotation
|
|
5
7
|
from numpy.typing import NDArray
|
|
6
|
-
from shapely.geometry import Polygon as ShapelyPolygon
|
|
7
8
|
from tqdm import tqdm
|
|
8
|
-
from valor_lite.
|
|
9
|
+
from valor_lite.object_detection.annotation import (
|
|
9
10
|
Bitmask,
|
|
10
11
|
BoundingBox,
|
|
11
12
|
Detection,
|
|
12
13
|
Polygon,
|
|
13
14
|
)
|
|
14
|
-
from valor_lite.
|
|
15
|
+
from valor_lite.object_detection.computation import (
|
|
15
16
|
compute_bbox_iou,
|
|
16
17
|
compute_bitmask_iou,
|
|
17
18
|
compute_confusion_matrix,
|
|
@@ -19,7 +20,7 @@ from valor_lite.detection.computation import (
|
|
|
19
20
|
compute_polygon_iou,
|
|
20
21
|
compute_ranked_pairs,
|
|
21
22
|
)
|
|
22
|
-
from valor_lite.
|
|
23
|
+
from valor_lite.object_detection.metric import (
|
|
23
24
|
AP,
|
|
24
25
|
AR,
|
|
25
26
|
F1,
|
|
@@ -59,103 +60,6 @@ filtered_metrics = evaluator.evaluate(iou_thresholds=[0.5], filter_mask=filter_m
|
|
|
59
60
|
"""
|
|
60
61
|
|
|
61
62
|
|
|
62
|
-
def _get_valor_dict_annotation_key(
|
|
63
|
-
annotation_type: type[BoundingBox] | type[Polygon] | type[Bitmask],
|
|
64
|
-
) -> str:
|
|
65
|
-
"""Get the correct JSON key to extract a given annotation type."""
|
|
66
|
-
|
|
67
|
-
if issubclass(annotation_type, BoundingBox):
|
|
68
|
-
return "bounding_box"
|
|
69
|
-
if issubclass(annotation_type, Polygon):
|
|
70
|
-
return "polygon"
|
|
71
|
-
else:
|
|
72
|
-
return "raster"
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
def _get_annotation_representation(
|
|
76
|
-
annotation_type: type[BoundingBox] | type[Polygon] | type[Bitmask],
|
|
77
|
-
) -> str:
|
|
78
|
-
"""Get the correct representation of an annotation object."""
|
|
79
|
-
|
|
80
|
-
representation = (
|
|
81
|
-
"extrema"
|
|
82
|
-
if issubclass(annotation_type, BoundingBox)
|
|
83
|
-
else ("mask" if issubclass(annotation_type, Bitmask) else "shape")
|
|
84
|
-
)
|
|
85
|
-
|
|
86
|
-
return representation
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
def _get_annotation_representation_from_valor_dict(
|
|
90
|
-
data: list,
|
|
91
|
-
annotation_type: type[BoundingBox] | type[Polygon] | type[Bitmask],
|
|
92
|
-
) -> tuple[float, float, float, float] | ShapelyPolygon | NDArray[np.bool_]:
|
|
93
|
-
"""Get the correct representation of an annotation object from a valor dictionary."""
|
|
94
|
-
|
|
95
|
-
if issubclass(annotation_type, BoundingBox):
|
|
96
|
-
x = [point[0] for shape in data for point in shape]
|
|
97
|
-
y = [point[1] for shape in data for point in shape]
|
|
98
|
-
return (min(x), max(x), min(y), max(y))
|
|
99
|
-
if issubclass(annotation_type, Polygon):
|
|
100
|
-
return ShapelyPolygon(data)
|
|
101
|
-
else:
|
|
102
|
-
return np.array(data)
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
def _get_annotation_data(
|
|
106
|
-
keyed_groundtruths: dict,
|
|
107
|
-
keyed_predictions: dict,
|
|
108
|
-
annotation_type: type[BoundingBox] | type[Polygon] | type[Bitmask] | None,
|
|
109
|
-
key=int,
|
|
110
|
-
) -> np.ndarray:
|
|
111
|
-
"""Create an array of annotation pairs for use when calculating IOU. Needed because we unpack bounding box representations, but not bitmask or polygon representations."""
|
|
112
|
-
if annotation_type == BoundingBox:
|
|
113
|
-
return np.array(
|
|
114
|
-
[
|
|
115
|
-
np.array([*gextrema, *pextrema])
|
|
116
|
-
for _, _, _, pextrema in keyed_predictions[key]
|
|
117
|
-
for _, _, gextrema in keyed_groundtruths[key]
|
|
118
|
-
]
|
|
119
|
-
)
|
|
120
|
-
else:
|
|
121
|
-
return np.array(
|
|
122
|
-
[
|
|
123
|
-
np.array([groundtruth_obj, prediction_obj])
|
|
124
|
-
for _, _, _, prediction_obj in keyed_predictions[key]
|
|
125
|
-
for _, _, groundtruth_obj in keyed_groundtruths[key]
|
|
126
|
-
]
|
|
127
|
-
)
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
def compute_iou(
|
|
131
|
-
data: NDArray[np.floating],
|
|
132
|
-
annotation_type: type[BoundingBox] | type[Polygon] | type[Bitmask],
|
|
133
|
-
) -> NDArray[np.floating]:
|
|
134
|
-
"""
|
|
135
|
-
Computes intersection-over-union (IoU) calculations for various annotation types.
|
|
136
|
-
|
|
137
|
-
Parameters
|
|
138
|
-
----------
|
|
139
|
-
data : NDArray[np.floating]
|
|
140
|
-
A sorted array of bounding box, bitmask, or polygon pairs.
|
|
141
|
-
annotation_type: type[BoundingBox] | type[Polygon] | type[Bitmask]
|
|
142
|
-
The type of annotation contained in the data.
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
Returns
|
|
146
|
-
-------
|
|
147
|
-
NDArray[np.floating]
|
|
148
|
-
Computed IoU's.
|
|
149
|
-
"""
|
|
150
|
-
|
|
151
|
-
if annotation_type == BoundingBox:
|
|
152
|
-
return compute_bbox_iou(data=data)
|
|
153
|
-
elif annotation_type == Bitmask:
|
|
154
|
-
return compute_bitmask_iou(data=data)
|
|
155
|
-
else:
|
|
156
|
-
return compute_polygon_iou(data=data)
|
|
157
|
-
|
|
158
|
-
|
|
159
63
|
@dataclass
|
|
160
64
|
class Filter:
|
|
161
65
|
ranked_indices: NDArray[np.int32]
|
|
@@ -185,22 +89,17 @@ class Evaluator:
|
|
|
185
89
|
self.prediction_examples: dict[int, NDArray[np.float16]] = dict()
|
|
186
90
|
|
|
187
91
|
# label reference
|
|
188
|
-
self.label_to_index: dict[
|
|
189
|
-
self.index_to_label: dict[int,
|
|
190
|
-
|
|
191
|
-
# label key reference
|
|
192
|
-
self.index_to_label_key: dict[int, str] = dict()
|
|
193
|
-
self.label_key_to_index: dict[str, int] = dict()
|
|
194
|
-
self.label_index_to_label_key_index: dict[int, int] = dict()
|
|
92
|
+
self.label_to_index: dict[str, int] = dict()
|
|
93
|
+
self.index_to_label: dict[int, str] = dict()
|
|
195
94
|
|
|
196
95
|
# computation caches
|
|
197
|
-
self._detailed_pairs: NDArray[np.
|
|
198
|
-
self._ranked_pairs: NDArray[np.
|
|
96
|
+
self._detailed_pairs: NDArray[np.float64] = np.array([])
|
|
97
|
+
self._ranked_pairs: NDArray[np.float64] = np.array([])
|
|
199
98
|
self._label_metadata: NDArray[np.int32] = np.array([])
|
|
200
99
|
self._label_metadata_per_datum: NDArray[np.int32] = np.array([])
|
|
201
100
|
|
|
202
101
|
@property
|
|
203
|
-
def ignored_prediction_labels(self) -> list[
|
|
102
|
+
def ignored_prediction_labels(self) -> list[str]:
|
|
204
103
|
"""
|
|
205
104
|
Prediction labels that are not present in the ground truth set.
|
|
206
105
|
"""
|
|
@@ -211,7 +110,7 @@ class Evaluator:
|
|
|
211
110
|
]
|
|
212
111
|
|
|
213
112
|
@property
|
|
214
|
-
def missing_prediction_labels(self) -> list[
|
|
113
|
+
def missing_prediction_labels(self) -> list[str]:
|
|
215
114
|
"""
|
|
216
115
|
Ground truth labels that are not present in the prediction set.
|
|
217
116
|
"""
|
|
@@ -238,8 +137,7 @@ class Evaluator:
|
|
|
238
137
|
def create_filter(
|
|
239
138
|
self,
|
|
240
139
|
datum_uids: list[str] | NDArray[np.int32] | None = None,
|
|
241
|
-
labels: list[
|
|
242
|
-
label_keys: list[str] | NDArray[np.int32] | None = None,
|
|
140
|
+
labels: list[str] | NDArray[np.int32] | None = None,
|
|
243
141
|
) -> Filter:
|
|
244
142
|
"""
|
|
245
143
|
Creates a filter that can be passed to an evaluation.
|
|
@@ -248,10 +146,8 @@ class Evaluator:
|
|
|
248
146
|
----------
|
|
249
147
|
datum_uids : list[str] | NDArray[np.int32], optional
|
|
250
148
|
An optional list of string uids or a numpy array of uid indices.
|
|
251
|
-
labels : list[
|
|
149
|
+
labels : list[str] | NDArray[np.int32], optional
|
|
252
150
|
An optional list of labels or a numpy array of label indices.
|
|
253
|
-
label_keys : list[str] | NDArray[np.int32], optional
|
|
254
|
-
An optional list of label keys or a numpy array of label key indices.
|
|
255
151
|
|
|
256
152
|
Returns
|
|
257
153
|
-------
|
|
@@ -296,24 +192,6 @@ class Evaluator:
|
|
|
296
192
|
] = False
|
|
297
193
|
mask_labels[~np.isin(np.arange(n_labels), labels)] = False
|
|
298
194
|
|
|
299
|
-
if label_keys is not None:
|
|
300
|
-
if isinstance(label_keys, list):
|
|
301
|
-
label_keys = np.array(
|
|
302
|
-
[self.label_key_to_index[key] for key in label_keys]
|
|
303
|
-
)
|
|
304
|
-
label_indices = (
|
|
305
|
-
np.where(np.isclose(self._label_metadata[:, 2], label_keys))[0]
|
|
306
|
-
if label_keys.size > 0
|
|
307
|
-
else np.array([])
|
|
308
|
-
)
|
|
309
|
-
mask_ranked[
|
|
310
|
-
~np.isin(self._ranked_pairs[:, 4].astype(int), label_indices)
|
|
311
|
-
] = False
|
|
312
|
-
mask_detailed[
|
|
313
|
-
~np.isin(self._detailed_pairs[:, 4].astype(int), label_indices)
|
|
314
|
-
] = False
|
|
315
|
-
mask_labels[~np.isin(np.arange(n_labels), label_indices)] = False
|
|
316
|
-
|
|
317
195
|
mask_label_metadata = (
|
|
318
196
|
mask_datums[:, np.newaxis] & mask_labels[np.newaxis, :]
|
|
319
197
|
)
|
|
@@ -321,13 +199,12 @@ class Evaluator:
|
|
|
321
199
|
label_metadata_per_datum[:, ~mask_label_metadata] = 0
|
|
322
200
|
|
|
323
201
|
label_metadata = np.zeros_like(self._label_metadata, dtype=np.int32)
|
|
324
|
-
label_metadata
|
|
202
|
+
label_metadata = np.transpose(
|
|
325
203
|
np.sum(
|
|
326
204
|
label_metadata_per_datum,
|
|
327
205
|
axis=1,
|
|
328
206
|
)
|
|
329
207
|
)
|
|
330
|
-
label_metadata[:, 2] = self._label_metadata[:, 2]
|
|
331
208
|
|
|
332
209
|
return Filter(
|
|
333
210
|
ranked_indices=np.where(mask_ranked)[0],
|
|
@@ -335,245 +212,22 @@ class Evaluator:
|
|
|
335
212
|
label_metadata=label_metadata,
|
|
336
213
|
)
|
|
337
214
|
|
|
338
|
-
def
|
|
339
|
-
self,
|
|
340
|
-
|
|
341
|
-
iou_thresholds: list[float] = [0.5, 0.75, 0.9],
|
|
342
|
-
score_thresholds: list[float] = [0.5],
|
|
343
|
-
number_of_examples: int = 0,
|
|
344
|
-
filter_: Filter | None = None,
|
|
345
|
-
as_dict: bool = False,
|
|
346
|
-
) -> dict[MetricType, list]:
|
|
215
|
+
def _convert_example_to_dict(
|
|
216
|
+
self, box: NDArray[np.float16]
|
|
217
|
+
) -> dict[str, float]:
|
|
347
218
|
"""
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
Parameters
|
|
351
|
-
----------
|
|
352
|
-
metrics_to_return : list[MetricType]
|
|
353
|
-
A list of metrics to return in the results.
|
|
354
|
-
iou_thresholds : list[float]
|
|
355
|
-
A list of IoU thresholds to compute metrics over.
|
|
356
|
-
score_thresholds : list[float]
|
|
357
|
-
A list of score thresholds to compute metrics over.
|
|
358
|
-
number_of_examples : int, default=0
|
|
359
|
-
Maximum number of annotation examples to return in ConfusionMatrix.
|
|
360
|
-
filter_ : Filter, optional
|
|
361
|
-
An optional filter object.
|
|
362
|
-
as_dict : bool, default=False
|
|
363
|
-
An option to return metrics as dictionaries.
|
|
364
|
-
|
|
365
|
-
Returns
|
|
366
|
-
-------
|
|
367
|
-
dict[MetricType, list]
|
|
368
|
-
A dictionary mapping MetricType enumerations to lists of computed metrics.
|
|
219
|
+
Converts a cached bounding box example to dictionary format.
|
|
369
220
|
"""
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
detailed_pairs = detailed_pairs[filter_.detailed_indices]
|
|
377
|
-
label_metadata = filter_.label_metadata
|
|
378
|
-
|
|
379
|
-
(
|
|
380
|
-
(
|
|
381
|
-
average_precision,
|
|
382
|
-
mean_average_precision,
|
|
383
|
-
average_precision_average_over_ious,
|
|
384
|
-
mean_average_precision_average_over_ious,
|
|
385
|
-
),
|
|
386
|
-
(
|
|
387
|
-
average_recall,
|
|
388
|
-
mean_average_recall,
|
|
389
|
-
average_recall_averaged_over_scores,
|
|
390
|
-
mean_average_recall_averaged_over_scores,
|
|
391
|
-
),
|
|
392
|
-
precision_recall,
|
|
393
|
-
pr_curves,
|
|
394
|
-
) = compute_metrics(
|
|
395
|
-
data=ranked_pairs,
|
|
396
|
-
label_metadata=label_metadata,
|
|
397
|
-
iou_thresholds=np.array(iou_thresholds),
|
|
398
|
-
score_thresholds=np.array(score_thresholds),
|
|
399
|
-
)
|
|
400
|
-
|
|
401
|
-
metrics = defaultdict(list)
|
|
402
|
-
|
|
403
|
-
metrics[MetricType.AP] = [
|
|
404
|
-
AP(
|
|
405
|
-
value=average_precision[iou_idx][label_idx],
|
|
406
|
-
iou_threshold=iou_thresholds[iou_idx],
|
|
407
|
-
label=self.index_to_label[label_idx],
|
|
408
|
-
)
|
|
409
|
-
for iou_idx in range(average_precision.shape[0])
|
|
410
|
-
for label_idx in range(average_precision.shape[1])
|
|
411
|
-
if int(label_metadata[label_idx, 0]) > 0
|
|
412
|
-
]
|
|
413
|
-
|
|
414
|
-
metrics[MetricType.mAP] = [
|
|
415
|
-
mAP(
|
|
416
|
-
value=mean_average_precision[iou_idx][label_key_idx],
|
|
417
|
-
iou_threshold=iou_thresholds[iou_idx],
|
|
418
|
-
label_key=self.index_to_label_key[label_key_idx],
|
|
419
|
-
)
|
|
420
|
-
for iou_idx in range(mean_average_precision.shape[0])
|
|
421
|
-
for label_key_idx in range(mean_average_precision.shape[1])
|
|
422
|
-
]
|
|
423
|
-
|
|
424
|
-
metrics[MetricType.APAveragedOverIOUs] = [
|
|
425
|
-
APAveragedOverIOUs(
|
|
426
|
-
value=average_precision_average_over_ious[label_idx],
|
|
427
|
-
iou_thresholds=iou_thresholds,
|
|
428
|
-
label=self.index_to_label[label_idx],
|
|
429
|
-
)
|
|
430
|
-
for label_idx in range(self.n_labels)
|
|
431
|
-
if int(label_metadata[label_idx, 0]) > 0
|
|
432
|
-
]
|
|
433
|
-
|
|
434
|
-
metrics[MetricType.mAPAveragedOverIOUs] = [
|
|
435
|
-
mAPAveragedOverIOUs(
|
|
436
|
-
value=mean_average_precision_average_over_ious[label_key_idx],
|
|
437
|
-
iou_thresholds=iou_thresholds,
|
|
438
|
-
label_key=self.index_to_label_key[label_key_idx],
|
|
439
|
-
)
|
|
440
|
-
for label_key_idx in range(
|
|
441
|
-
mean_average_precision_average_over_ious.shape[0]
|
|
442
|
-
)
|
|
443
|
-
]
|
|
444
|
-
|
|
445
|
-
metrics[MetricType.AR] = [
|
|
446
|
-
AR(
|
|
447
|
-
value=average_recall[score_idx][label_idx],
|
|
448
|
-
iou_thresholds=iou_thresholds,
|
|
449
|
-
score_threshold=score_thresholds[score_idx],
|
|
450
|
-
label=self.index_to_label[label_idx],
|
|
451
|
-
)
|
|
452
|
-
for score_idx in range(average_recall.shape[0])
|
|
453
|
-
for label_idx in range(average_recall.shape[1])
|
|
454
|
-
if int(label_metadata[label_idx, 0]) > 0
|
|
455
|
-
]
|
|
456
|
-
|
|
457
|
-
metrics[MetricType.mAR] = [
|
|
458
|
-
mAR(
|
|
459
|
-
value=mean_average_recall[score_idx][label_key_idx],
|
|
460
|
-
iou_thresholds=iou_thresholds,
|
|
461
|
-
score_threshold=score_thresholds[score_idx],
|
|
462
|
-
label_key=self.index_to_label_key[label_key_idx],
|
|
463
|
-
)
|
|
464
|
-
for score_idx in range(mean_average_recall.shape[0])
|
|
465
|
-
for label_key_idx in range(mean_average_recall.shape[1])
|
|
466
|
-
]
|
|
467
|
-
|
|
468
|
-
metrics[MetricType.ARAveragedOverScores] = [
|
|
469
|
-
ARAveragedOverScores(
|
|
470
|
-
value=average_recall_averaged_over_scores[label_idx],
|
|
471
|
-
score_thresholds=score_thresholds,
|
|
472
|
-
iou_thresholds=iou_thresholds,
|
|
473
|
-
label=self.index_to_label[label_idx],
|
|
474
|
-
)
|
|
475
|
-
for label_idx in range(self.n_labels)
|
|
476
|
-
if int(label_metadata[label_idx, 0]) > 0
|
|
477
|
-
]
|
|
478
|
-
|
|
479
|
-
metrics[MetricType.mARAveragedOverScores] = [
|
|
480
|
-
mARAveragedOverScores(
|
|
481
|
-
value=mean_average_recall_averaged_over_scores[label_key_idx],
|
|
482
|
-
score_thresholds=score_thresholds,
|
|
483
|
-
iou_thresholds=iou_thresholds,
|
|
484
|
-
label_key=self.index_to_label_key[label_key_idx],
|
|
485
|
-
)
|
|
486
|
-
for label_key_idx in range(
|
|
487
|
-
mean_average_recall_averaged_over_scores.shape[0]
|
|
488
|
-
)
|
|
489
|
-
]
|
|
490
|
-
|
|
491
|
-
metrics[MetricType.PrecisionRecallCurve] = [
|
|
492
|
-
PrecisionRecallCurve(
|
|
493
|
-
precision=list(pr_curves[iou_idx][label_idx]),
|
|
494
|
-
iou_threshold=iou_threshold,
|
|
495
|
-
label=label,
|
|
496
|
-
)
|
|
497
|
-
for iou_idx, iou_threshold in enumerate(iou_thresholds)
|
|
498
|
-
for label_idx, label in self.index_to_label.items()
|
|
499
|
-
if int(label_metadata[label_idx, 0]) > 0
|
|
500
|
-
]
|
|
501
|
-
|
|
502
|
-
for label_idx, label in self.index_to_label.items():
|
|
503
|
-
|
|
504
|
-
if label_metadata[label_idx, 0] == 0:
|
|
505
|
-
continue
|
|
506
|
-
|
|
507
|
-
for score_idx, score_threshold in enumerate(score_thresholds):
|
|
508
|
-
for iou_idx, iou_threshold in enumerate(iou_thresholds):
|
|
509
|
-
|
|
510
|
-
row = precision_recall[iou_idx][score_idx][label_idx]
|
|
511
|
-
kwargs = {
|
|
512
|
-
"label": label,
|
|
513
|
-
"iou_threshold": iou_threshold,
|
|
514
|
-
"score_threshold": score_threshold,
|
|
515
|
-
}
|
|
516
|
-
metrics[MetricType.Counts].append(
|
|
517
|
-
Counts(
|
|
518
|
-
tp=int(row[0]),
|
|
519
|
-
fp=int(row[1]),
|
|
520
|
-
fn=int(row[2]),
|
|
521
|
-
**kwargs,
|
|
522
|
-
)
|
|
523
|
-
)
|
|
524
|
-
|
|
525
|
-
metrics[MetricType.Precision].append(
|
|
526
|
-
Precision(
|
|
527
|
-
value=row[3],
|
|
528
|
-
**kwargs,
|
|
529
|
-
)
|
|
530
|
-
)
|
|
531
|
-
metrics[MetricType.Recall].append(
|
|
532
|
-
Recall(
|
|
533
|
-
value=row[4],
|
|
534
|
-
**kwargs,
|
|
535
|
-
)
|
|
536
|
-
)
|
|
537
|
-
metrics[MetricType.F1].append(
|
|
538
|
-
F1(
|
|
539
|
-
value=row[5],
|
|
540
|
-
**kwargs,
|
|
541
|
-
)
|
|
542
|
-
)
|
|
543
|
-
metrics[MetricType.Accuracy].append(
|
|
544
|
-
Accuracy(
|
|
545
|
-
value=row[6],
|
|
546
|
-
**kwargs,
|
|
547
|
-
)
|
|
548
|
-
)
|
|
549
|
-
|
|
550
|
-
if MetricType.ConfusionMatrix in metrics_to_return:
|
|
551
|
-
metrics[
|
|
552
|
-
MetricType.ConfusionMatrix
|
|
553
|
-
] = self._compute_confusion_matrix(
|
|
554
|
-
data=detailed_pairs,
|
|
555
|
-
label_metadata=label_metadata,
|
|
556
|
-
iou_thresholds=iou_thresholds,
|
|
557
|
-
score_thresholds=score_thresholds,
|
|
558
|
-
number_of_examples=number_of_examples,
|
|
559
|
-
)
|
|
560
|
-
|
|
561
|
-
for metric in set(metrics.keys()):
|
|
562
|
-
if metric not in metrics_to_return:
|
|
563
|
-
del metrics[metric]
|
|
564
|
-
|
|
565
|
-
if as_dict:
|
|
566
|
-
return {
|
|
567
|
-
mtype: [metric.to_dict() for metric in mvalues]
|
|
568
|
-
for mtype, mvalues in metrics.items()
|
|
569
|
-
}
|
|
570
|
-
|
|
571
|
-
return metrics
|
|
221
|
+
return {
|
|
222
|
+
"xmin": float(box[0]),
|
|
223
|
+
"xmax": float(box[1]),
|
|
224
|
+
"ymin": float(box[2]),
|
|
225
|
+
"ymax": float(box[3]),
|
|
226
|
+
}
|
|
572
227
|
|
|
573
228
|
def _unpack_confusion_matrix(
|
|
574
229
|
self,
|
|
575
|
-
confusion_matrix: NDArray[np.
|
|
576
|
-
label_key_idx: int,
|
|
230
|
+
confusion_matrix: NDArray[np.float64],
|
|
577
231
|
number_of_labels: int,
|
|
578
232
|
number_of_examples: int,
|
|
579
233
|
) -> dict[
|
|
@@ -586,7 +240,7 @@ class Evaluator:
|
|
|
586
240
|
| list[
|
|
587
241
|
dict[
|
|
588
242
|
str,
|
|
589
|
-
str |
|
|
243
|
+
str | dict[str, float] | float,
|
|
590
244
|
]
|
|
591
245
|
],
|
|
592
246
|
],
|
|
@@ -629,8 +283,8 @@ class Evaluator:
|
|
|
629
283
|
)
|
|
630
284
|
|
|
631
285
|
return {
|
|
632
|
-
self.index_to_label[gt_label_idx]
|
|
633
|
-
self.index_to_label[pd_label_idx]
|
|
286
|
+
self.index_to_label[gt_label_idx]: {
|
|
287
|
+
self.index_to_label[pd_label_idx]: {
|
|
634
288
|
"count": max(
|
|
635
289
|
int(confusion_matrix[gt_label_idx, pd_label_idx, 0]),
|
|
636
290
|
0,
|
|
@@ -642,7 +296,7 @@ class Evaluator:
|
|
|
642
296
|
gt_label_idx, pd_label_idx, example_idx
|
|
643
297
|
)
|
|
644
298
|
],
|
|
645
|
-
"groundtruth":
|
|
299
|
+
"groundtruth": self._convert_example_to_dict(
|
|
646
300
|
self.groundtruth_examples[
|
|
647
301
|
datum_idx(
|
|
648
302
|
gt_label_idx,
|
|
@@ -655,9 +309,9 @@ class Evaluator:
|
|
|
655
309
|
pd_label_idx,
|
|
656
310
|
example_idx,
|
|
657
311
|
)
|
|
658
|
-
]
|
|
312
|
+
]
|
|
659
313
|
),
|
|
660
|
-
"prediction":
|
|
314
|
+
"prediction": self._convert_example_to_dict(
|
|
661
315
|
self.prediction_examples[
|
|
662
316
|
datum_idx(
|
|
663
317
|
gt_label_idx,
|
|
@@ -670,7 +324,7 @@ class Evaluator:
|
|
|
670
324
|
pd_label_idx,
|
|
671
325
|
example_idx,
|
|
672
326
|
)
|
|
673
|
-
]
|
|
327
|
+
]
|
|
674
328
|
),
|
|
675
329
|
"score": score_idx(
|
|
676
330
|
gt_label_idx, pd_label_idx, example_idx
|
|
@@ -682,30 +336,20 @@ class Evaluator:
|
|
|
682
336
|
],
|
|
683
337
|
}
|
|
684
338
|
for pd_label_idx in range(number_of_labels)
|
|
685
|
-
if (
|
|
686
|
-
self.label_index_to_label_key_index[pd_label_idx]
|
|
687
|
-
== label_key_idx
|
|
688
|
-
)
|
|
689
339
|
}
|
|
690
340
|
for gt_label_idx in range(number_of_labels)
|
|
691
|
-
if (
|
|
692
|
-
self.label_index_to_label_key_index[gt_label_idx]
|
|
693
|
-
== label_key_idx
|
|
694
|
-
)
|
|
695
341
|
}
|
|
696
342
|
|
|
697
343
|
def _unpack_hallucinations(
|
|
698
344
|
self,
|
|
699
|
-
hallucinations: NDArray[np.
|
|
700
|
-
label_key_idx: int,
|
|
345
|
+
hallucinations: NDArray[np.float64],
|
|
701
346
|
number_of_labels: int,
|
|
702
347
|
number_of_examples: int,
|
|
703
348
|
) -> dict[
|
|
704
349
|
str,
|
|
705
350
|
dict[
|
|
706
351
|
str,
|
|
707
|
-
int
|
|
708
|
-
| list[dict[str, str | float | tuple[float, float, float, float]]],
|
|
352
|
+
int | list[dict[str, str | float | dict[str, float]]],
|
|
709
353
|
],
|
|
710
354
|
]:
|
|
711
355
|
"""
|
|
@@ -740,7 +384,7 @@ class Evaluator:
|
|
|
740
384
|
)
|
|
741
385
|
|
|
742
386
|
return {
|
|
743
|
-
self.index_to_label[pd_label_idx]
|
|
387
|
+
self.index_to_label[pd_label_idx]: {
|
|
744
388
|
"count": max(
|
|
745
389
|
int(hallucinations[pd_label_idx, 0]),
|
|
746
390
|
0,
|
|
@@ -750,12 +394,10 @@ class Evaluator:
|
|
|
750
394
|
"datum": self.index_to_uid[
|
|
751
395
|
datum_idx(pd_label_idx, example_idx)
|
|
752
396
|
],
|
|
753
|
-
"prediction":
|
|
397
|
+
"prediction": self._convert_example_to_dict(
|
|
754
398
|
self.prediction_examples[
|
|
755
399
|
datum_idx(pd_label_idx, example_idx)
|
|
756
|
-
][
|
|
757
|
-
prediction_idx(pd_label_idx, example_idx)
|
|
758
|
-
].tolist()
|
|
400
|
+
][prediction_idx(pd_label_idx, example_idx)]
|
|
759
401
|
),
|
|
760
402
|
"score": score_idx(pd_label_idx, example_idx),
|
|
761
403
|
}
|
|
@@ -764,25 +406,14 @@ class Evaluator:
|
|
|
764
406
|
],
|
|
765
407
|
}
|
|
766
408
|
for pd_label_idx in range(number_of_labels)
|
|
767
|
-
if (
|
|
768
|
-
self.label_index_to_label_key_index[pd_label_idx]
|
|
769
|
-
== label_key_idx
|
|
770
|
-
)
|
|
771
409
|
}
|
|
772
410
|
|
|
773
411
|
def _unpack_missing_predictions(
|
|
774
412
|
self,
|
|
775
413
|
missing_predictions: NDArray[np.int32],
|
|
776
|
-
label_key_idx: int,
|
|
777
414
|
number_of_labels: int,
|
|
778
415
|
number_of_examples: int,
|
|
779
|
-
) -> dict[
|
|
780
|
-
str,
|
|
781
|
-
dict[
|
|
782
|
-
str,
|
|
783
|
-
int | list[dict[str, str | tuple[float, float, float, float]]],
|
|
784
|
-
],
|
|
785
|
-
]:
|
|
416
|
+
) -> dict[str, dict[str, int | list[dict[str, str | dict[str, float]]]]]:
|
|
786
417
|
"""
|
|
787
418
|
Unpacks a numpy array of missing prediction counts and examples.
|
|
788
419
|
"""
|
|
@@ -806,7 +437,7 @@ class Evaluator:
|
|
|
806
437
|
)
|
|
807
438
|
|
|
808
439
|
return {
|
|
809
|
-
self.index_to_label[gt_label_idx]
|
|
440
|
+
self.index_to_label[gt_label_idx]: {
|
|
810
441
|
"count": max(
|
|
811
442
|
int(missing_predictions[gt_label_idx, 0]),
|
|
812
443
|
0,
|
|
@@ -816,12 +447,10 @@ class Evaluator:
|
|
|
816
447
|
"datum": self.index_to_uid[
|
|
817
448
|
datum_idx(gt_label_idx, example_idx)
|
|
818
449
|
],
|
|
819
|
-
"groundtruth":
|
|
450
|
+
"groundtruth": self._convert_example_to_dict(
|
|
820
451
|
self.groundtruth_examples[
|
|
821
452
|
datum_idx(gt_label_idx, example_idx)
|
|
822
|
-
][
|
|
823
|
-
groundtruth_idx(gt_label_idx, example_idx)
|
|
824
|
-
].tolist()
|
|
453
|
+
][groundtruth_idx(gt_label_idx, example_idx)]
|
|
825
454
|
),
|
|
826
455
|
}
|
|
827
456
|
for example_idx in range(number_of_examples)
|
|
@@ -829,51 +458,253 @@ class Evaluator:
|
|
|
829
458
|
],
|
|
830
459
|
}
|
|
831
460
|
for gt_label_idx in range(number_of_labels)
|
|
832
|
-
if (
|
|
833
|
-
self.label_index_to_label_key_index[gt_label_idx]
|
|
834
|
-
== label_key_idx
|
|
835
|
-
)
|
|
836
461
|
}
|
|
837
462
|
|
|
838
|
-
def
|
|
463
|
+
def compute_precision_recall(
|
|
839
464
|
self,
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
) -> list[ConfusionMatrix]:
|
|
465
|
+
iou_thresholds: list[float] = [0.5, 0.75, 0.9],
|
|
466
|
+
score_thresholds: list[float] = [0.5],
|
|
467
|
+
filter_: Filter | None = None,
|
|
468
|
+
as_dict: bool = False,
|
|
469
|
+
) -> dict[MetricType, list]:
|
|
846
470
|
"""
|
|
847
|
-
Computes
|
|
471
|
+
Computes all metrics except for ConfusionMatrix
|
|
848
472
|
|
|
849
473
|
Parameters
|
|
850
474
|
----------
|
|
851
|
-
data : NDArray[np.floating]
|
|
852
|
-
An array containing detailed pairs of detections.
|
|
853
|
-
label_metadata : NDArray[np.int32]
|
|
854
|
-
An array containing label metadata.
|
|
855
475
|
iou_thresholds : list[float]
|
|
856
|
-
|
|
476
|
+
A list of IoU thresholds to compute metrics over.
|
|
857
477
|
score_thresholds : list[float]
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
478
|
+
A list of score thresholds to compute metrics over.
|
|
479
|
+
filter_ : Filter, optional
|
|
480
|
+
An optional filter object.
|
|
481
|
+
as_dict : bool, default=False
|
|
482
|
+
An option to return metrics as dictionaries.
|
|
861
483
|
|
|
862
484
|
Returns
|
|
863
485
|
-------
|
|
864
|
-
|
|
865
|
-
|
|
486
|
+
dict[MetricType, list]
|
|
487
|
+
A dictionary mapping MetricType enumerations to lists of computed metrics.
|
|
866
488
|
"""
|
|
867
489
|
|
|
868
|
-
|
|
869
|
-
|
|
490
|
+
ranked_pairs = self._ranked_pairs
|
|
491
|
+
label_metadata = self._label_metadata
|
|
492
|
+
if filter_ is not None:
|
|
493
|
+
ranked_pairs = ranked_pairs[filter_.ranked_indices]
|
|
494
|
+
label_metadata = filter_.label_metadata
|
|
870
495
|
|
|
871
496
|
(
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
|
|
497
|
+
(
|
|
498
|
+
average_precision,
|
|
499
|
+
mean_average_precision,
|
|
500
|
+
average_precision_average_over_ious,
|
|
501
|
+
mean_average_precision_average_over_ious,
|
|
502
|
+
),
|
|
503
|
+
(
|
|
504
|
+
average_recall,
|
|
505
|
+
mean_average_recall,
|
|
506
|
+
average_recall_averaged_over_scores,
|
|
507
|
+
mean_average_recall_averaged_over_scores,
|
|
508
|
+
),
|
|
509
|
+
precision_recall,
|
|
510
|
+
pr_curves,
|
|
511
|
+
) = compute_metrics(
|
|
512
|
+
data=ranked_pairs,
|
|
513
|
+
label_metadata=label_metadata,
|
|
514
|
+
iou_thresholds=np.array(iou_thresholds),
|
|
515
|
+
score_thresholds=np.array(score_thresholds),
|
|
516
|
+
)
|
|
517
|
+
|
|
518
|
+
metrics = defaultdict(list)
|
|
519
|
+
|
|
520
|
+
metrics[MetricType.AP] = [
|
|
521
|
+
AP(
|
|
522
|
+
value=float(average_precision[iou_idx][label_idx]),
|
|
523
|
+
iou_threshold=iou_thresholds[iou_idx],
|
|
524
|
+
label=self.index_to_label[label_idx],
|
|
525
|
+
)
|
|
526
|
+
for iou_idx in range(average_precision.shape[0])
|
|
527
|
+
for label_idx in range(average_precision.shape[1])
|
|
528
|
+
if int(label_metadata[label_idx, 0]) > 0
|
|
529
|
+
]
|
|
530
|
+
|
|
531
|
+
metrics[MetricType.mAP] = [
|
|
532
|
+
mAP(
|
|
533
|
+
value=float(mean_average_precision[iou_idx]),
|
|
534
|
+
iou_threshold=iou_thresholds[iou_idx],
|
|
535
|
+
)
|
|
536
|
+
for iou_idx in range(mean_average_precision.shape[0])
|
|
537
|
+
]
|
|
538
|
+
|
|
539
|
+
metrics[MetricType.APAveragedOverIOUs] = [
|
|
540
|
+
APAveragedOverIOUs(
|
|
541
|
+
value=float(average_precision_average_over_ious[label_idx]),
|
|
542
|
+
iou_thresholds=iou_thresholds,
|
|
543
|
+
label=self.index_to_label[label_idx],
|
|
544
|
+
)
|
|
545
|
+
for label_idx in range(self.n_labels)
|
|
546
|
+
if int(label_metadata[label_idx, 0]) > 0
|
|
547
|
+
]
|
|
548
|
+
|
|
549
|
+
metrics[MetricType.mAPAveragedOverIOUs] = [
|
|
550
|
+
mAPAveragedOverIOUs(
|
|
551
|
+
value=float(mean_average_precision_average_over_ious),
|
|
552
|
+
iou_thresholds=iou_thresholds,
|
|
553
|
+
)
|
|
554
|
+
]
|
|
555
|
+
|
|
556
|
+
metrics[MetricType.AR] = [
|
|
557
|
+
AR(
|
|
558
|
+
value=float(average_recall[score_idx][label_idx]),
|
|
559
|
+
iou_thresholds=iou_thresholds,
|
|
560
|
+
score_threshold=score_thresholds[score_idx],
|
|
561
|
+
label=self.index_to_label[label_idx],
|
|
562
|
+
)
|
|
563
|
+
for score_idx in range(average_recall.shape[0])
|
|
564
|
+
for label_idx in range(average_recall.shape[1])
|
|
565
|
+
if int(label_metadata[label_idx, 0]) > 0
|
|
566
|
+
]
|
|
567
|
+
|
|
568
|
+
metrics[MetricType.mAR] = [
|
|
569
|
+
mAR(
|
|
570
|
+
value=float(mean_average_recall[score_idx]),
|
|
571
|
+
iou_thresholds=iou_thresholds,
|
|
572
|
+
score_threshold=score_thresholds[score_idx],
|
|
573
|
+
)
|
|
574
|
+
for score_idx in range(mean_average_recall.shape[0])
|
|
575
|
+
]
|
|
576
|
+
|
|
577
|
+
metrics[MetricType.ARAveragedOverScores] = [
|
|
578
|
+
ARAveragedOverScores(
|
|
579
|
+
value=float(average_recall_averaged_over_scores[label_idx]),
|
|
580
|
+
score_thresholds=score_thresholds,
|
|
581
|
+
iou_thresholds=iou_thresholds,
|
|
582
|
+
label=self.index_to_label[label_idx],
|
|
583
|
+
)
|
|
584
|
+
for label_idx in range(self.n_labels)
|
|
585
|
+
if int(label_metadata[label_idx, 0]) > 0
|
|
586
|
+
]
|
|
587
|
+
|
|
588
|
+
metrics[MetricType.mARAveragedOverScores] = [
|
|
589
|
+
mARAveragedOverScores(
|
|
590
|
+
value=float(mean_average_recall_averaged_over_scores),
|
|
591
|
+
score_thresholds=score_thresholds,
|
|
592
|
+
iou_thresholds=iou_thresholds,
|
|
593
|
+
)
|
|
594
|
+
]
|
|
595
|
+
|
|
596
|
+
metrics[MetricType.PrecisionRecallCurve] = [
|
|
597
|
+
PrecisionRecallCurve(
|
|
598
|
+
precision=pr_curves[iou_idx][label_idx].astype(float).tolist(),
|
|
599
|
+
iou_threshold=iou_threshold,
|
|
600
|
+
label=label,
|
|
601
|
+
)
|
|
602
|
+
for iou_idx, iou_threshold in enumerate(iou_thresholds)
|
|
603
|
+
for label_idx, label in self.index_to_label.items()
|
|
604
|
+
if int(label_metadata[label_idx, 0]) > 0
|
|
605
|
+
]
|
|
606
|
+
|
|
607
|
+
for label_idx, label in self.index_to_label.items():
|
|
608
|
+
|
|
609
|
+
if label_metadata[label_idx, 0] == 0:
|
|
610
|
+
continue
|
|
611
|
+
|
|
612
|
+
for score_idx, score_threshold in enumerate(score_thresholds):
|
|
613
|
+
for iou_idx, iou_threshold in enumerate(iou_thresholds):
|
|
614
|
+
|
|
615
|
+
row = precision_recall[iou_idx][score_idx][label_idx]
|
|
616
|
+
kwargs = {
|
|
617
|
+
"label": label,
|
|
618
|
+
"iou_threshold": iou_threshold,
|
|
619
|
+
"score_threshold": score_threshold,
|
|
620
|
+
}
|
|
621
|
+
metrics[MetricType.Counts].append(
|
|
622
|
+
Counts(
|
|
623
|
+
tp=int(row[0]),
|
|
624
|
+
fp=int(row[1]),
|
|
625
|
+
fn=int(row[2]),
|
|
626
|
+
**kwargs,
|
|
627
|
+
)
|
|
628
|
+
)
|
|
629
|
+
|
|
630
|
+
metrics[MetricType.Precision].append(
|
|
631
|
+
Precision(
|
|
632
|
+
value=float(row[3]),
|
|
633
|
+
**kwargs,
|
|
634
|
+
)
|
|
635
|
+
)
|
|
636
|
+
metrics[MetricType.Recall].append(
|
|
637
|
+
Recall(
|
|
638
|
+
value=float(row[4]),
|
|
639
|
+
**kwargs,
|
|
640
|
+
)
|
|
641
|
+
)
|
|
642
|
+
metrics[MetricType.F1].append(
|
|
643
|
+
F1(
|
|
644
|
+
value=float(row[5]),
|
|
645
|
+
**kwargs,
|
|
646
|
+
)
|
|
647
|
+
)
|
|
648
|
+
metrics[MetricType.Accuracy].append(
|
|
649
|
+
Accuracy(
|
|
650
|
+
value=float(row[6]),
|
|
651
|
+
**kwargs,
|
|
652
|
+
)
|
|
653
|
+
)
|
|
654
|
+
|
|
655
|
+
if as_dict:
|
|
656
|
+
return {
|
|
657
|
+
mtype: [metric.to_dict() for metric in mvalues]
|
|
658
|
+
for mtype, mvalues in metrics.items()
|
|
659
|
+
}
|
|
660
|
+
|
|
661
|
+
return metrics
|
|
662
|
+
|
|
663
|
+
def compute_confusion_matrix(
|
|
664
|
+
self,
|
|
665
|
+
iou_thresholds: list[float] = [0.5, 0.75, 0.9],
|
|
666
|
+
score_thresholds: list[float] = [0.5],
|
|
667
|
+
number_of_examples: int = 0,
|
|
668
|
+
filter_: Filter | None = None,
|
|
669
|
+
as_dict: bool = False,
|
|
670
|
+
) -> list:
|
|
671
|
+
"""
|
|
672
|
+
Computes confusion matrices at various thresholds.
|
|
673
|
+
|
|
674
|
+
Parameters
|
|
675
|
+
----------
|
|
676
|
+
iou_thresholds : list[float]
|
|
677
|
+
A list of IoU thresholds to compute metrics over.
|
|
678
|
+
score_thresholds : list[float]
|
|
679
|
+
A list of score thresholds to compute metrics over.
|
|
680
|
+
number_of_examples : int, default=0
|
|
681
|
+
Maximum number of annotation examples to return in ConfusionMatrix.
|
|
682
|
+
filter_ : Filter, optional
|
|
683
|
+
An optional filter object.
|
|
684
|
+
as_dict : bool, default=False
|
|
685
|
+
An option to return metrics as dictionaries.
|
|
686
|
+
|
|
687
|
+
Returns
|
|
688
|
+
-------
|
|
689
|
+
list[ConfusionMatrix] | list[dict]
|
|
690
|
+
List of confusion matrices per threshold pair.
|
|
691
|
+
"""
|
|
692
|
+
|
|
693
|
+
detailed_pairs = self._detailed_pairs
|
|
694
|
+
label_metadata = self._label_metadata
|
|
695
|
+
if filter_ is not None:
|
|
696
|
+
detailed_pairs = detailed_pairs[filter_.detailed_indices]
|
|
697
|
+
label_metadata = filter_.label_metadata
|
|
698
|
+
|
|
699
|
+
if detailed_pairs.size == 0:
|
|
700
|
+
return list()
|
|
701
|
+
|
|
702
|
+
(
|
|
703
|
+
confusion_matrix,
|
|
704
|
+
hallucinations,
|
|
705
|
+
missing_predictions,
|
|
706
|
+
) = compute_confusion_matrix(
|
|
707
|
+
data=detailed_pairs,
|
|
877
708
|
label_metadata=label_metadata,
|
|
878
709
|
iou_thresholds=np.array(iou_thresholds),
|
|
879
710
|
score_thresholds=np.array(score_thresholds),
|
|
@@ -881,23 +712,20 @@ class Evaluator:
|
|
|
881
712
|
)
|
|
882
713
|
|
|
883
714
|
n_ious, n_scores, n_labels, _, _ = confusion_matrix.shape
|
|
884
|
-
|
|
715
|
+
matrices = [
|
|
885
716
|
ConfusionMatrix(
|
|
886
717
|
iou_threshold=iou_thresholds[iou_idx],
|
|
887
718
|
score_threshold=score_thresholds[score_idx],
|
|
888
|
-
label_key=label_key,
|
|
889
719
|
number_of_examples=number_of_examples,
|
|
890
720
|
confusion_matrix=self._unpack_confusion_matrix(
|
|
891
721
|
confusion_matrix=confusion_matrix[
|
|
892
722
|
iou_idx, score_idx, :, :, :
|
|
893
723
|
],
|
|
894
|
-
label_key_idx=label_key_idx,
|
|
895
724
|
number_of_labels=n_labels,
|
|
896
725
|
number_of_examples=number_of_examples,
|
|
897
726
|
),
|
|
898
727
|
hallucinations=self._unpack_hallucinations(
|
|
899
728
|
hallucinations=hallucinations[iou_idx, score_idx, :, :],
|
|
900
|
-
label_key_idx=label_key_idx,
|
|
901
729
|
number_of_labels=n_labels,
|
|
902
730
|
number_of_examples=number_of_examples,
|
|
903
731
|
),
|
|
@@ -905,16 +733,62 @@ class Evaluator:
|
|
|
905
733
|
missing_predictions=missing_predictions[
|
|
906
734
|
iou_idx, score_idx, :, :
|
|
907
735
|
],
|
|
908
|
-
label_key_idx=label_key_idx,
|
|
909
736
|
number_of_labels=n_labels,
|
|
910
737
|
number_of_examples=number_of_examples,
|
|
911
738
|
),
|
|
912
739
|
)
|
|
913
|
-
for label_key_idx, label_key in self.index_to_label_key.items()
|
|
914
740
|
for iou_idx in range(n_ious)
|
|
915
741
|
for score_idx in range(n_scores)
|
|
916
742
|
]
|
|
917
743
|
|
|
744
|
+
if as_dict:
|
|
745
|
+
return [m.to_dict() for m in matrices]
|
|
746
|
+
return matrices
|
|
747
|
+
|
|
748
|
+
def evaluate(
|
|
749
|
+
self,
|
|
750
|
+
iou_thresholds: list[float] = [0.5, 0.75, 0.9],
|
|
751
|
+
score_thresholds: list[float] = [0.5],
|
|
752
|
+
number_of_examples: int = 0,
|
|
753
|
+
filter_: Filter | None = None,
|
|
754
|
+
as_dict: bool = False,
|
|
755
|
+
) -> dict[MetricType, list]:
|
|
756
|
+
"""
|
|
757
|
+
Computes all avaiable metrics.
|
|
758
|
+
|
|
759
|
+
Parameters
|
|
760
|
+
----------
|
|
761
|
+
iou_thresholds : list[float]
|
|
762
|
+
A list of IoU thresholds to compute metrics over.
|
|
763
|
+
score_thresholds : list[float]
|
|
764
|
+
A list of score thresholds to compute metrics over.
|
|
765
|
+
number_of_examples : int, default=0
|
|
766
|
+
Maximum number of annotation examples to return in ConfusionMatrix.
|
|
767
|
+
filter_ : Filter, optional
|
|
768
|
+
An optional filter object.
|
|
769
|
+
as_dict : bool, default=False
|
|
770
|
+
An option to return metrics as dictionaries.
|
|
771
|
+
|
|
772
|
+
Returns
|
|
773
|
+
-------
|
|
774
|
+
dict[MetricType, list]
|
|
775
|
+
A dictionary mapping metric type to a list of metrics.
|
|
776
|
+
"""
|
|
777
|
+
results = self.compute_precision_recall(
|
|
778
|
+
iou_thresholds=iou_thresholds,
|
|
779
|
+
score_thresholds=score_thresholds,
|
|
780
|
+
filter_=filter_,
|
|
781
|
+
as_dict=as_dict,
|
|
782
|
+
)
|
|
783
|
+
results[MetricType.ConfusionMatrix] = self.compute_confusion_matrix(
|
|
784
|
+
iou_thresholds=iou_thresholds,
|
|
785
|
+
score_thresholds=score_thresholds,
|
|
786
|
+
number_of_examples=number_of_examples,
|
|
787
|
+
filter_=filter_,
|
|
788
|
+
as_dict=as_dict,
|
|
789
|
+
)
|
|
790
|
+
return results
|
|
791
|
+
|
|
918
792
|
|
|
919
793
|
class DataLoader:
|
|
920
794
|
"""
|
|
@@ -923,7 +797,7 @@ class DataLoader:
|
|
|
923
797
|
|
|
924
798
|
def __init__(self):
|
|
925
799
|
self._evaluator = Evaluator()
|
|
926
|
-
self.pairs: list[NDArray[np.
|
|
800
|
+
self.pairs: list[NDArray[np.float64]] = list()
|
|
927
801
|
self.groundtruth_count = defaultdict(lambda: defaultdict(int))
|
|
928
802
|
self.prediction_count = defaultdict(lambda: defaultdict(int))
|
|
929
803
|
|
|
@@ -947,51 +821,36 @@ class DataLoader:
|
|
|
947
821
|
self._evaluator.index_to_uid[index] = uid
|
|
948
822
|
return self._evaluator.uid_to_index[uid]
|
|
949
823
|
|
|
950
|
-
def _add_label(self, label:
|
|
824
|
+
def _add_label(self, label: str) -> int:
|
|
951
825
|
"""
|
|
952
826
|
Helper function for adding a label to the cache.
|
|
953
827
|
|
|
954
828
|
Parameters
|
|
955
829
|
----------
|
|
956
|
-
label :
|
|
957
|
-
The label
|
|
830
|
+
label : str
|
|
831
|
+
The label associated with the annotation.
|
|
958
832
|
|
|
959
833
|
Returns
|
|
960
834
|
-------
|
|
961
835
|
int
|
|
962
836
|
Label index.
|
|
963
|
-
int
|
|
964
|
-
Label key index.
|
|
965
837
|
"""
|
|
966
838
|
|
|
967
839
|
label_id = len(self._evaluator.index_to_label)
|
|
968
|
-
label_key_id = len(self._evaluator.index_to_label_key)
|
|
969
840
|
if label not in self._evaluator.label_to_index:
|
|
970
841
|
self._evaluator.label_to_index[label] = label_id
|
|
971
842
|
self._evaluator.index_to_label[label_id] = label
|
|
972
843
|
|
|
973
|
-
# update label key index
|
|
974
|
-
if label[0] not in self._evaluator.label_key_to_index:
|
|
975
|
-
self._evaluator.label_key_to_index[label[0]] = label_key_id
|
|
976
|
-
self._evaluator.index_to_label_key[label_key_id] = label[0]
|
|
977
|
-
label_key_id += 1
|
|
978
|
-
|
|
979
|
-
self._evaluator.label_index_to_label_key_index[
|
|
980
|
-
label_id
|
|
981
|
-
] = self._evaluator.label_key_to_index[label[0]]
|
|
982
844
|
label_id += 1
|
|
983
845
|
|
|
984
|
-
return
|
|
985
|
-
self._evaluator.label_to_index[label],
|
|
986
|
-
self._evaluator.label_key_to_index[label[0]],
|
|
987
|
-
)
|
|
846
|
+
return self._evaluator.label_to_index[label]
|
|
988
847
|
|
|
989
848
|
def _compute_ious_and_cache_pairs(
|
|
990
849
|
self,
|
|
991
850
|
uid_index: int,
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
annotation_type:
|
|
851
|
+
groundtruths: list,
|
|
852
|
+
predictions: list,
|
|
853
|
+
annotation_type: Type[BoundingBox] | Type[Polygon] | Type[Bitmask],
|
|
995
854
|
) -> None:
|
|
996
855
|
"""
|
|
997
856
|
Compute IOUs between groundtruths and preditions before storing as pairs.
|
|
@@ -1000,122 +859,92 @@ class DataLoader:
|
|
|
1000
859
|
----------
|
|
1001
860
|
uid_index: int
|
|
1002
861
|
The index of the detection.
|
|
1003
|
-
|
|
1004
|
-
A
|
|
1005
|
-
|
|
1006
|
-
A
|
|
862
|
+
groundtruths: list
|
|
863
|
+
A list of groundtruths.
|
|
864
|
+
predictions: list
|
|
865
|
+
A list of predictions.
|
|
1007
866
|
annotation_type: type[BoundingBox] | type[Polygon] | type[Bitmask]
|
|
1008
867
|
The type of annotation to compute IOUs for.
|
|
1009
868
|
"""
|
|
1010
|
-
gt_keys = set(keyed_groundtruths.keys())
|
|
1011
|
-
pd_keys = set(keyed_predictions.keys())
|
|
1012
|
-
joint_keys = gt_keys.intersection(pd_keys)
|
|
1013
|
-
gt_unique_keys = gt_keys - pd_keys
|
|
1014
|
-
pd_unique_keys = pd_keys - gt_keys
|
|
1015
869
|
|
|
1016
870
|
pairs = list()
|
|
1017
|
-
|
|
1018
|
-
|
|
1019
|
-
n_groundtruths = len(keyed_groundtruths[key])
|
|
1020
|
-
data = _get_annotation_data(
|
|
1021
|
-
keyed_groundtruths=keyed_groundtruths,
|
|
1022
|
-
keyed_predictions=keyed_predictions,
|
|
1023
|
-
key=key,
|
|
1024
|
-
annotation_type=annotation_type,
|
|
1025
|
-
)
|
|
1026
|
-
ious = compute_iou(data=data, annotation_type=annotation_type)
|
|
1027
|
-
mask_nonzero_iou = (ious > 1e-9).reshape(
|
|
1028
|
-
(n_predictions, n_groundtruths)
|
|
1029
|
-
)
|
|
1030
|
-
mask_ious_halluc = ~(mask_nonzero_iou.any(axis=1))
|
|
1031
|
-
mask_ious_misprd = ~(mask_nonzero_iou.any(axis=0))
|
|
871
|
+
n_predictions = len(predictions)
|
|
872
|
+
n_groundtruths = len(groundtruths)
|
|
1032
873
|
|
|
1033
|
-
|
|
1034
|
-
|
|
1035
|
-
|
|
1036
|
-
|
|
1037
|
-
|
|
1038
|
-
|
|
1039
|
-
|
|
1040
|
-
|
|
1041
|
-
|
|
1042
|
-
|
|
1043
|
-
|
|
1044
|
-
|
|
1045
|
-
|
|
1046
|
-
|
|
1047
|
-
|
|
1048
|
-
|
|
1049
|
-
|
|
1050
|
-
|
|
1051
|
-
|
|
1052
|
-
|
|
1053
|
-
|
|
1054
|
-
|
|
1055
|
-
|
|
1056
|
-
|
|
1057
|
-
|
|
1058
|
-
|
|
1059
|
-
|
|
1060
|
-
|
|
1061
|
-
|
|
1062
|
-
|
|
1063
|
-
|
|
1064
|
-
|
|
1065
|
-
|
|
1066
|
-
|
|
1067
|
-
|
|
1068
|
-
|
|
1069
|
-
|
|
1070
|
-
|
|
1071
|
-
|
|
1072
|
-
|
|
1073
|
-
|
|
1074
|
-
|
|
1075
|
-
|
|
1076
|
-
|
|
1077
|
-
|
|
1078
|
-
|
|
1079
|
-
|
|
1080
|
-
|
|
1081
|
-
|
|
1082
|
-
|
|
1083
|
-
|
|
1084
|
-
|
|
1085
|
-
|
|
1086
|
-
|
|
1087
|
-
|
|
1088
|
-
|
|
1089
|
-
|
|
1090
|
-
|
|
1091
|
-
|
|
1092
|
-
|
|
1093
|
-
|
|
1094
|
-
|
|
1095
|
-
|
|
1096
|
-
|
|
1097
|
-
|
|
1098
|
-
|
|
1099
|
-
|
|
1100
|
-
|
|
1101
|
-
|
|
1102
|
-
|
|
1103
|
-
|
|
1104
|
-
|
|
1105
|
-
|
|
1106
|
-
|
|
1107
|
-
float(uid_index),
|
|
1108
|
-
-1.0,
|
|
1109
|
-
float(pidx),
|
|
1110
|
-
0.0,
|
|
1111
|
-
-1.0,
|
|
1112
|
-
float(plabel),
|
|
1113
|
-
float(score),
|
|
1114
|
-
]
|
|
1115
|
-
)
|
|
1116
|
-
for pidx, plabel, score, _ in keyed_predictions[key]
|
|
1117
|
-
]
|
|
1118
|
-
)
|
|
874
|
+
all_pairs = np.array(
|
|
875
|
+
[
|
|
876
|
+
np.array([gann, pann])
|
|
877
|
+
for _, _, _, pann in predictions
|
|
878
|
+
for _, _, gann in groundtruths
|
|
879
|
+
]
|
|
880
|
+
)
|
|
881
|
+
|
|
882
|
+
match annotation_type:
|
|
883
|
+
case annotation.BoundingBox:
|
|
884
|
+
ious = compute_bbox_iou(all_pairs)
|
|
885
|
+
case annotation.Polygon:
|
|
886
|
+
ious = compute_polygon_iou(all_pairs)
|
|
887
|
+
case annotation.Bitmask:
|
|
888
|
+
ious = compute_bitmask_iou(all_pairs)
|
|
889
|
+
case _:
|
|
890
|
+
raise ValueError(
|
|
891
|
+
f"Invalid annotation type `{annotation_type}`."
|
|
892
|
+
)
|
|
893
|
+
|
|
894
|
+
ious = ious.reshape(n_predictions, n_groundtruths)
|
|
895
|
+
predictions_with_iou_of_zero = np.where((ious < 1e-9).all(axis=1))[0]
|
|
896
|
+
groundtruths_with_iou_of_zero = np.where((ious < 1e-9).all(axis=0))[0]
|
|
897
|
+
|
|
898
|
+
pairs.extend(
|
|
899
|
+
[
|
|
900
|
+
np.array(
|
|
901
|
+
[
|
|
902
|
+
float(uid_index),
|
|
903
|
+
float(gidx),
|
|
904
|
+
float(pidx),
|
|
905
|
+
ious[pidx, gidx],
|
|
906
|
+
float(glabel),
|
|
907
|
+
float(plabel),
|
|
908
|
+
float(score),
|
|
909
|
+
]
|
|
910
|
+
)
|
|
911
|
+
for pidx, plabel, score, _ in predictions
|
|
912
|
+
for gidx, glabel, _ in groundtruths
|
|
913
|
+
if ious[pidx, gidx] >= 1e-9
|
|
914
|
+
]
|
|
915
|
+
)
|
|
916
|
+
pairs.extend(
|
|
917
|
+
[
|
|
918
|
+
np.array(
|
|
919
|
+
[
|
|
920
|
+
float(uid_index),
|
|
921
|
+
-1.0,
|
|
922
|
+
float(predictions[index][0]),
|
|
923
|
+
0.0,
|
|
924
|
+
-1.0,
|
|
925
|
+
float(predictions[index][1]),
|
|
926
|
+
float(predictions[index][2]),
|
|
927
|
+
]
|
|
928
|
+
)
|
|
929
|
+
for index in predictions_with_iou_of_zero
|
|
930
|
+
]
|
|
931
|
+
)
|
|
932
|
+
pairs.extend(
|
|
933
|
+
[
|
|
934
|
+
np.array(
|
|
935
|
+
[
|
|
936
|
+
float(uid_index),
|
|
937
|
+
float(groundtruths[index][0]),
|
|
938
|
+
-1.0,
|
|
939
|
+
0.0,
|
|
940
|
+
float(groundtruths[index][1]),
|
|
941
|
+
-1.0,
|
|
942
|
+
-1.0,
|
|
943
|
+
]
|
|
944
|
+
)
|
|
945
|
+
for index in groundtruths_with_iou_of_zero
|
|
946
|
+
]
|
|
947
|
+
)
|
|
1119
948
|
|
|
1120
949
|
self.pairs.append(np.array(pairs))
|
|
1121
950
|
|
|
@@ -1157,12 +986,8 @@ class DataLoader:
|
|
|
1157
986
|
)
|
|
1158
987
|
|
|
1159
988
|
# cache labels and annotations
|
|
1160
|
-
|
|
1161
|
-
|
|
1162
|
-
|
|
1163
|
-
representation_property = _get_annotation_representation(
|
|
1164
|
-
annotation_type=annotation_type
|
|
1165
|
-
)
|
|
989
|
+
groundtruths = list()
|
|
990
|
+
predictions = list()
|
|
1166
991
|
|
|
1167
992
|
for gidx, gann in enumerate(detection.groundtruths):
|
|
1168
993
|
if not isinstance(gann, annotation_type):
|
|
@@ -1170,26 +995,17 @@ class DataLoader:
|
|
|
1170
995
|
f"Expected {annotation_type}, but annotation is of type {type(gann)}."
|
|
1171
996
|
)
|
|
1172
997
|
|
|
1173
|
-
|
|
1174
|
-
|
|
1175
|
-
|
|
1176
|
-
] = getattr(gann, representation_property)
|
|
1177
|
-
else:
|
|
1178
|
-
converted_box = gann.to_box()
|
|
1179
|
-
self._evaluator.groundtruth_examples[uid_index][gidx] = (
|
|
1180
|
-
getattr(converted_box, "extrema")
|
|
1181
|
-
if converted_box is not None
|
|
1182
|
-
else None
|
|
1183
|
-
)
|
|
998
|
+
self._evaluator.groundtruth_examples[uid_index][
|
|
999
|
+
gidx
|
|
1000
|
+
] = gann.extrema
|
|
1184
1001
|
for glabel in gann.labels:
|
|
1185
|
-
label_idx
|
|
1002
|
+
label_idx = self._add_label(glabel)
|
|
1186
1003
|
self.groundtruth_count[label_idx][uid_index] += 1
|
|
1187
|
-
|
|
1188
|
-
keyed_groundtruths[label_key_idx].append(
|
|
1004
|
+
groundtruths.append(
|
|
1189
1005
|
(
|
|
1190
1006
|
gidx,
|
|
1191
1007
|
label_idx,
|
|
1192
|
-
|
|
1008
|
+
gann.annotation,
|
|
1193
1009
|
)
|
|
1194
1010
|
)
|
|
1195
1011
|
|
|
@@ -1199,36 +1015,25 @@ class DataLoader:
|
|
|
1199
1015
|
f"Expected {annotation_type}, but annotation is of type {type(pann)}."
|
|
1200
1016
|
)
|
|
1201
1017
|
|
|
1202
|
-
|
|
1203
|
-
|
|
1204
|
-
|
|
1205
|
-
] = getattr(pann, representation_property)
|
|
1206
|
-
else:
|
|
1207
|
-
converted_box = pann.to_box()
|
|
1208
|
-
self._evaluator.prediction_examples[uid_index][pidx] = (
|
|
1209
|
-
getattr(converted_box, "extrema")
|
|
1210
|
-
if converted_box is not None
|
|
1211
|
-
else None
|
|
1212
|
-
)
|
|
1018
|
+
self._evaluator.prediction_examples[uid_index][
|
|
1019
|
+
pidx
|
|
1020
|
+
] = pann.extrema
|
|
1213
1021
|
for plabel, pscore in zip(pann.labels, pann.scores):
|
|
1214
|
-
label_idx
|
|
1022
|
+
label_idx = self._add_label(plabel)
|
|
1215
1023
|
self.prediction_count[label_idx][uid_index] += 1
|
|
1216
|
-
|
|
1217
|
-
pann, representation_property
|
|
1218
|
-
)
|
|
1219
|
-
keyed_predictions[label_key_idx].append(
|
|
1024
|
+
predictions.append(
|
|
1220
1025
|
(
|
|
1221
1026
|
pidx,
|
|
1222
1027
|
label_idx,
|
|
1223
1028
|
pscore,
|
|
1224
|
-
|
|
1029
|
+
pann.annotation,
|
|
1225
1030
|
)
|
|
1226
1031
|
)
|
|
1227
1032
|
|
|
1228
1033
|
self._compute_ious_and_cache_pairs(
|
|
1229
1034
|
uid_index=uid_index,
|
|
1230
|
-
|
|
1231
|
-
|
|
1035
|
+
groundtruths=groundtruths,
|
|
1036
|
+
predictions=predictions,
|
|
1232
1037
|
annotation_type=annotation_type,
|
|
1233
1038
|
)
|
|
1234
1039
|
|
|
@@ -1295,150 +1100,6 @@ class DataLoader:
|
|
|
1295
1100
|
annotation_type=Bitmask,
|
|
1296
1101
|
)
|
|
1297
1102
|
|
|
1298
|
-
def _add_data_from_valor_dict(
|
|
1299
|
-
self,
|
|
1300
|
-
detections: list[tuple[dict, dict]],
|
|
1301
|
-
annotation_type: type[Bitmask] | type[BoundingBox] | type[Polygon],
|
|
1302
|
-
show_progress: bool = False,
|
|
1303
|
-
):
|
|
1304
|
-
"""
|
|
1305
|
-
Adds Valor-format detections to the cache.
|
|
1306
|
-
|
|
1307
|
-
Parameters
|
|
1308
|
-
----------
|
|
1309
|
-
detections : list[tuple[dict, dict]]
|
|
1310
|
-
A list of groundtruth, prediction pairs in Valor-format dictionaries.
|
|
1311
|
-
annotation_type : type[Bitmask] | type[BoundingBox] | type[Polygon]
|
|
1312
|
-
The annotation type to process.
|
|
1313
|
-
show_progress : bool, default=False
|
|
1314
|
-
Toggle for tqdm progress bar.
|
|
1315
|
-
"""
|
|
1316
|
-
|
|
1317
|
-
disable_tqdm = not show_progress
|
|
1318
|
-
for groundtruth, prediction in tqdm(detections, disable=disable_tqdm):
|
|
1319
|
-
# update metadata
|
|
1320
|
-
self._evaluator.n_datums += 1
|
|
1321
|
-
self._evaluator.n_groundtruths += len(groundtruth["annotations"])
|
|
1322
|
-
self._evaluator.n_predictions += len(prediction["annotations"])
|
|
1323
|
-
|
|
1324
|
-
# update datum uid index
|
|
1325
|
-
uid_index = self._add_datum(uid=groundtruth["datum"]["uid"])
|
|
1326
|
-
|
|
1327
|
-
# initialize bounding box examples
|
|
1328
|
-
self._evaluator.groundtruth_examples[uid_index] = np.zeros(
|
|
1329
|
-
(len(groundtruth["annotations"]), 4), dtype=np.float16
|
|
1330
|
-
)
|
|
1331
|
-
self._evaluator.prediction_examples[uid_index] = np.zeros(
|
|
1332
|
-
(len(prediction["annotations"]), 4), dtype=np.float16
|
|
1333
|
-
)
|
|
1334
|
-
|
|
1335
|
-
# cache labels and annotations
|
|
1336
|
-
keyed_groundtruths = defaultdict(list)
|
|
1337
|
-
keyed_predictions = defaultdict(list)
|
|
1338
|
-
|
|
1339
|
-
annotation_key = _get_valor_dict_annotation_key(
|
|
1340
|
-
annotation_type=annotation_type
|
|
1341
|
-
)
|
|
1342
|
-
invalid_keys = list(
|
|
1343
|
-
filter(
|
|
1344
|
-
lambda x: x != annotation_key,
|
|
1345
|
-
["bounding_box", "raster", "polygon"],
|
|
1346
|
-
)
|
|
1347
|
-
)
|
|
1348
|
-
|
|
1349
|
-
for gidx, gann in enumerate(groundtruth["annotations"]):
|
|
1350
|
-
if (gann[annotation_key] is None) or any(
|
|
1351
|
-
[gann[k] is not None for k in invalid_keys]
|
|
1352
|
-
):
|
|
1353
|
-
raise ValueError(
|
|
1354
|
-
f"Input JSON doesn't contain {annotation_type} data, or contains data for multiple annotation types."
|
|
1355
|
-
)
|
|
1356
|
-
if annotation_type == BoundingBox:
|
|
1357
|
-
self._evaluator.groundtruth_examples[uid_index][
|
|
1358
|
-
gidx
|
|
1359
|
-
] = np.array(
|
|
1360
|
-
_get_annotation_representation_from_valor_dict(
|
|
1361
|
-
gann[annotation_key],
|
|
1362
|
-
annotation_type=annotation_type,
|
|
1363
|
-
),
|
|
1364
|
-
)
|
|
1365
|
-
|
|
1366
|
-
for valor_label in gann["labels"]:
|
|
1367
|
-
glabel = (valor_label["key"], valor_label["value"])
|
|
1368
|
-
label_idx, label_key_idx = self._add_label(glabel)
|
|
1369
|
-
self.groundtruth_count[label_idx][uid_index] += 1
|
|
1370
|
-
keyed_groundtruths[label_key_idx].append(
|
|
1371
|
-
(
|
|
1372
|
-
gidx,
|
|
1373
|
-
label_idx,
|
|
1374
|
-
_get_annotation_representation_from_valor_dict(
|
|
1375
|
-
gann[annotation_key],
|
|
1376
|
-
annotation_type=annotation_type,
|
|
1377
|
-
),
|
|
1378
|
-
)
|
|
1379
|
-
)
|
|
1380
|
-
for pidx, pann in enumerate(prediction["annotations"]):
|
|
1381
|
-
if (pann[annotation_key] is None) or any(
|
|
1382
|
-
[pann[k] is not None for k in invalid_keys]
|
|
1383
|
-
):
|
|
1384
|
-
raise ValueError(
|
|
1385
|
-
f"Input JSON doesn't contain {annotation_type} data, or contains data for multiple annotation types."
|
|
1386
|
-
)
|
|
1387
|
-
|
|
1388
|
-
if annotation_type == BoundingBox:
|
|
1389
|
-
self._evaluator.prediction_examples[uid_index][
|
|
1390
|
-
pidx
|
|
1391
|
-
] = np.array(
|
|
1392
|
-
_get_annotation_representation_from_valor_dict(
|
|
1393
|
-
pann[annotation_key],
|
|
1394
|
-
annotation_type=annotation_type,
|
|
1395
|
-
)
|
|
1396
|
-
)
|
|
1397
|
-
for valor_label in pann["labels"]:
|
|
1398
|
-
plabel = (valor_label["key"], valor_label["value"])
|
|
1399
|
-
pscore = valor_label["score"]
|
|
1400
|
-
label_idx, label_key_idx = self._add_label(plabel)
|
|
1401
|
-
self.prediction_count[label_idx][uid_index] += 1
|
|
1402
|
-
keyed_predictions[label_key_idx].append(
|
|
1403
|
-
(
|
|
1404
|
-
pidx,
|
|
1405
|
-
label_idx,
|
|
1406
|
-
pscore,
|
|
1407
|
-
_get_annotation_representation_from_valor_dict(
|
|
1408
|
-
pann[annotation_key],
|
|
1409
|
-
annotation_type=annotation_type,
|
|
1410
|
-
),
|
|
1411
|
-
)
|
|
1412
|
-
)
|
|
1413
|
-
|
|
1414
|
-
self._compute_ious_and_cache_pairs(
|
|
1415
|
-
uid_index=uid_index,
|
|
1416
|
-
keyed_groundtruths=keyed_groundtruths,
|
|
1417
|
-
keyed_predictions=keyed_predictions,
|
|
1418
|
-
annotation_type=annotation_type,
|
|
1419
|
-
)
|
|
1420
|
-
|
|
1421
|
-
def add_bounding_boxes_from_valor_dict(
|
|
1422
|
-
self,
|
|
1423
|
-
detections: list[tuple[dict, dict]],
|
|
1424
|
-
show_progress: bool = False,
|
|
1425
|
-
):
|
|
1426
|
-
"""
|
|
1427
|
-
Adds Valor-format bounding box detections to the cache.
|
|
1428
|
-
|
|
1429
|
-
Parameters
|
|
1430
|
-
----------
|
|
1431
|
-
detections : list[tuple[dict, dict]]
|
|
1432
|
-
A list of groundtruth, prediction pairs in Valor-format dictionaries.
|
|
1433
|
-
show_progress : bool, default=False
|
|
1434
|
-
Toggle for tqdm progress bar.
|
|
1435
|
-
"""
|
|
1436
|
-
return self._add_data_from_valor_dict(
|
|
1437
|
-
detections=detections,
|
|
1438
|
-
show_progress=show_progress,
|
|
1439
|
-
annotation_type=BoundingBox,
|
|
1440
|
-
)
|
|
1441
|
-
|
|
1442
1103
|
def finalize(self) -> Evaluator:
|
|
1443
1104
|
"""
|
|
1444
1105
|
Performs data finalization and some preprocessing steps.
|
|
@@ -1494,11 +1155,6 @@ class DataLoader:
|
|
|
1494
1155
|
]
|
|
1495
1156
|
)
|
|
1496
1157
|
),
|
|
1497
|
-
float(
|
|
1498
|
-
self._evaluator.label_index_to_label_key_index[
|
|
1499
|
-
label_idx
|
|
1500
|
-
]
|
|
1501
|
-
),
|
|
1502
1158
|
]
|
|
1503
1159
|
for label_idx in range(n_labels)
|
|
1504
1160
|
]
|