valor-lite 0.33.1__tar.gz → 0.33.2__tar.gz

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.
Files changed (36) hide show
  1. {valor_lite-0.33.1 → valor_lite-0.33.2}/PKG-INFO +1 -1
  2. {valor_lite-0.33.1 → valor_lite-0.33.2}/tests/detection/test_average_precision.py +11 -0
  3. {valor_lite-0.33.1 → valor_lite-0.33.2}/tests/detection/test_average_recall.py +74 -0
  4. {valor_lite-0.33.1 → valor_lite-0.33.2}/tests/detection/test_counts.py +106 -20
  5. {valor_lite-0.33.1 → valor_lite-0.33.2}/tests/detection/test_detailed_counts.py +126 -4
  6. {valor_lite-0.33.1 → valor_lite-0.33.2}/valor_lite/detection/computation.py +142 -21
  7. {valor_lite-0.33.1 → valor_lite-0.33.2}/valor_lite/detection/manager.py +90 -5
  8. {valor_lite-0.33.1 → valor_lite-0.33.2}/valor_lite.egg-info/PKG-INFO +1 -1
  9. {valor_lite-0.33.1 → valor_lite-0.33.2}/LICENSE +0 -0
  10. {valor_lite-0.33.1 → valor_lite-0.33.2}/README.md +0 -0
  11. {valor_lite-0.33.1 → valor_lite-0.33.2}/benchmarks/.gitignore +0 -0
  12. {valor_lite-0.33.1 → valor_lite-0.33.2}/benchmarks/benchmark_objdet.py +0 -0
  13. {valor_lite-0.33.1 → valor_lite-0.33.2}/examples/.gitignore +0 -0
  14. {valor_lite-0.33.1 → valor_lite-0.33.2}/examples/coco-yolo.ipynb +0 -0
  15. {valor_lite-0.33.1 → valor_lite-0.33.2}/pyproject.toml +0 -0
  16. {valor_lite-0.33.1 → valor_lite-0.33.2}/setup.cfg +0 -0
  17. {valor_lite-0.33.1 → valor_lite-0.33.2}/tests/detection/__init__.py +0 -0
  18. {valor_lite-0.33.1 → valor_lite-0.33.2}/tests/detection/conftest.py +0 -0
  19. {valor_lite-0.33.1 → valor_lite-0.33.2}/tests/detection/test_dataloader.py +0 -0
  20. {valor_lite-0.33.1 → valor_lite-0.33.2}/tests/detection/test_evaluator.py +0 -0
  21. {valor_lite-0.33.1 → valor_lite-0.33.2}/tests/detection/test_filtering.py +0 -0
  22. {valor_lite-0.33.1 → valor_lite-0.33.2}/tests/detection/test_iou.py +0 -0
  23. {valor_lite-0.33.1 → valor_lite-0.33.2}/tests/detection/test_pr_curve.py +0 -0
  24. {valor_lite-0.33.1 → valor_lite-0.33.2}/tests/detection/test_precision.py +0 -0
  25. {valor_lite-0.33.1 → valor_lite-0.33.2}/tests/detection/test_recall.py +0 -0
  26. {valor_lite-0.33.1 → valor_lite-0.33.2}/tests/detection/test_schemas.py +0 -0
  27. {valor_lite-0.33.1 → valor_lite-0.33.2}/tests/detection/test_stability.py +0 -0
  28. {valor_lite-0.33.1 → valor_lite-0.33.2}/valor_lite/__init__.py +0 -0
  29. {valor_lite-0.33.1 → valor_lite-0.33.2}/valor_lite/detection/__init__.py +0 -0
  30. {valor_lite-0.33.1 → valor_lite-0.33.2}/valor_lite/detection/annotation.py +0 -0
  31. {valor_lite-0.33.1 → valor_lite-0.33.2}/valor_lite/detection/metric.py +0 -0
  32. {valor_lite-0.33.1 → valor_lite-0.33.2}/valor_lite/schemas.py +0 -0
  33. {valor_lite-0.33.1 → valor_lite-0.33.2}/valor_lite.egg-info/SOURCES.txt +0 -0
  34. {valor_lite-0.33.1 → valor_lite-0.33.2}/valor_lite.egg-info/dependency_links.txt +0 -0
  35. {valor_lite-0.33.1 → valor_lite-0.33.2}/valor_lite.egg-info/requires.txt +0 -0
  36. {valor_lite-0.33.1 → valor_lite-0.33.2}/valor_lite.egg-info/top_level.txt +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: valor-lite
3
- Version: 0.33.1
3
+ Version: 0.33.2
4
4
  Summary: Compute valor metrics directly in your client.
5
5
  License: MIT License
6
6
 
@@ -629,6 +629,17 @@ def test_ap_ranked_pair_ordering(detection_ranked_pair_ordering: Detection):
629
629
  loader.add_data(detections=[detection_ranked_pair_ordering])
630
630
  evaluator = loader.finalize()
631
631
 
632
+ assert evaluator.metadata == {
633
+ "ignored_prediction_labels": [
634
+ ("class", "label4"),
635
+ ],
636
+ "missing_prediction_labels": [],
637
+ "n_datums": 1,
638
+ "n_groundtruths": 3,
639
+ "n_labels": 4,
640
+ "n_predictions": 4,
641
+ }
642
+
632
643
  metrics = evaluator.evaluate(iou_thresholds=[0.5, 0.75])
633
644
 
634
645
  actual_metrics = [m.to_dict() for m in metrics[MetricType.AP]]
@@ -432,3 +432,77 @@ def test_ar_true_positive_deassignment(
432
432
  assert m in expected_metrics
433
433
  for m in expected_metrics:
434
434
  assert m in actual_metrics
435
+
436
+
437
+ def test_ar_ranked_pair_ordering(detection_ranked_pair_ordering: Detection):
438
+
439
+ loader = DataLoader()
440
+ loader.add_data(detections=[detection_ranked_pair_ordering])
441
+ evaluator = loader.finalize()
442
+
443
+ assert evaluator.metadata == {
444
+ "ignored_prediction_labels": [
445
+ ("class", "label4"),
446
+ ],
447
+ "missing_prediction_labels": [],
448
+ "n_datums": 1,
449
+ "n_groundtruths": 3,
450
+ "n_labels": 4,
451
+ "n_predictions": 4,
452
+ }
453
+
454
+ metrics = evaluator.evaluate(
455
+ iou_thresholds=[0.5, 0.75], score_thresholds=[0.0]
456
+ )
457
+
458
+ actual_metrics = [m.to_dict() for m in metrics[MetricType.AR]]
459
+ expected_metrics = expected_metrics = [
460
+ {
461
+ "type": "AR",
462
+ "value": 1.0,
463
+ "parameters": {
464
+ "score_threshold": 0.0,
465
+ "iou_thresholds": [0.5, 0.75],
466
+ "label": {"key": "class", "value": "label1"},
467
+ },
468
+ },
469
+ {
470
+ "type": "AR",
471
+ "value": 1.0,
472
+ "parameters": {
473
+ "score_threshold": 0.0,
474
+ "iou_thresholds": [0.5, 0.75],
475
+ "label": {"key": "class", "value": "label2"},
476
+ },
477
+ },
478
+ {
479
+ "type": "AR",
480
+ "value": 0.0,
481
+ "parameters": {
482
+ "score_threshold": 0.0,
483
+ "iou_thresholds": [0.5, 0.75],
484
+ "label": {"key": "class", "value": "label3"},
485
+ },
486
+ },
487
+ ]
488
+ for m in actual_metrics:
489
+ assert m in expected_metrics
490
+ for m in expected_metrics:
491
+ assert m in actual_metrics
492
+
493
+ actual_metrics = [m.to_dict() for m in metrics[MetricType.mAR]]
494
+ expected_metrics = expected_metrics = [
495
+ {
496
+ "type": "mAR",
497
+ "value": 0.6666666666666666,
498
+ "parameters": {
499
+ "score_threshold": 0.0,
500
+ "iou_thresholds": [0.5, 0.75],
501
+ "label_key": "class",
502
+ },
503
+ },
504
+ ]
505
+ for m in actual_metrics:
506
+ assert m in expected_metrics
507
+ for m in expected_metrics:
508
+ assert m in actual_metrics
@@ -41,52 +41,52 @@ def test_counts_metrics(basic_detections: list[Detection]):
41
41
  {
42
42
  "type": "Counts",
43
43
  "value": {
44
- "tp": 0,
45
- "fp": 1,
44
+ "tp": 1,
45
+ "fp": 0,
46
46
  "fn": 1,
47
47
  },
48
48
  "parameters": {
49
49
  "iou_threshold": 0.1,
50
50
  "score_threshold": 0.0,
51
- "label": {"key": "k2", "value": "v2"},
51
+ "label": {"key": "k1", "value": "v1"},
52
52
  },
53
53
  },
54
54
  {
55
55
  "type": "Counts",
56
56
  "value": {
57
- "tp": 0,
58
- "fp": 1,
57
+ "tp": 1,
58
+ "fp": 0,
59
59
  "fn": 1,
60
60
  },
61
61
  "parameters": {
62
62
  "iou_threshold": 0.6,
63
63
  "score_threshold": 0.0,
64
- "label": {"key": "k2", "value": "v2"},
64
+ "label": {"key": "k1", "value": "v1"},
65
65
  },
66
66
  },
67
67
  {
68
68
  "type": "Counts",
69
69
  "value": {
70
- "tp": 1,
70
+ "tp": 0,
71
71
  "fp": 0,
72
- "fn": 1,
72
+ "fn": 2,
73
73
  },
74
74
  "parameters": {
75
75
  "iou_threshold": 0.1,
76
- "score_threshold": 0.0,
76
+ "score_threshold": 0.5,
77
77
  "label": {"key": "k1", "value": "v1"},
78
78
  },
79
79
  },
80
80
  {
81
81
  "type": "Counts",
82
82
  "value": {
83
- "tp": 1,
83
+ "tp": 0,
84
84
  "fp": 0,
85
- "fn": 1,
85
+ "fn": 2,
86
86
  },
87
87
  "parameters": {
88
88
  "iou_threshold": 0.6,
89
- "score_threshold": 0.0,
89
+ "score_threshold": 0.5,
90
90
  "label": {"key": "k1", "value": "v1"},
91
91
  },
92
92
  },
@@ -99,7 +99,7 @@ def test_counts_metrics(basic_detections: list[Detection]):
99
99
  },
100
100
  "parameters": {
101
101
  "iou_threshold": 0.1,
102
- "score_threshold": 0.5,
102
+ "score_threshold": 0.0,
103
103
  "label": {"key": "k2", "value": "v2"},
104
104
  },
105
105
  },
@@ -112,7 +112,7 @@ def test_counts_metrics(basic_detections: list[Detection]):
112
112
  },
113
113
  "parameters": {
114
114
  "iou_threshold": 0.6,
115
- "score_threshold": 0.5,
115
+ "score_threshold": 0.0,
116
116
  "label": {"key": "k2", "value": "v2"},
117
117
  },
118
118
  },
@@ -120,26 +120,26 @@ def test_counts_metrics(basic_detections: list[Detection]):
120
120
  "type": "Counts",
121
121
  "value": {
122
122
  "tp": 0,
123
- "fp": 0,
124
- "fn": 2,
123
+ "fp": 1,
124
+ "fn": 1,
125
125
  },
126
126
  "parameters": {
127
127
  "iou_threshold": 0.1,
128
128
  "score_threshold": 0.5,
129
- "label": {"key": "k1", "value": "v1"},
129
+ "label": {"key": "k2", "value": "v2"},
130
130
  },
131
131
  },
132
132
  {
133
133
  "type": "Counts",
134
134
  "value": {
135
135
  "tp": 0,
136
- "fp": 0,
137
- "fn": 2,
136
+ "fp": 1,
137
+ "fn": 1,
138
138
  },
139
139
  "parameters": {
140
140
  "iou_threshold": 0.6,
141
141
  "score_threshold": 0.5,
142
- "label": {"key": "k1", "value": "v1"},
142
+ "label": {"key": "k2", "value": "v2"},
143
143
  },
144
144
  },
145
145
  ]
@@ -455,3 +455,89 @@ def test_counts_false_negatives_two_datums_one_only_with_different_class_high_co
455
455
  assert m in actual_metrics
456
456
  for m in actual_metrics:
457
457
  assert m in expected_metrics
458
+
459
+
460
+ def test_counts_ranked_pair_ordering(
461
+ detection_ranked_pair_ordering: Detection,
462
+ ):
463
+
464
+ loader = DataLoader()
465
+ loader.add_data(detections=[detection_ranked_pair_ordering])
466
+ evaluator = loader.finalize()
467
+
468
+ assert evaluator.metadata == {
469
+ "ignored_prediction_labels": [
470
+ ("class", "label4"),
471
+ ],
472
+ "missing_prediction_labels": [],
473
+ "n_datums": 1,
474
+ "n_groundtruths": 3,
475
+ "n_labels": 4,
476
+ "n_predictions": 4,
477
+ }
478
+
479
+ metrics = evaluator.evaluate(
480
+ iou_thresholds=[0.5, 0.75], score_thresholds=[0.0]
481
+ )
482
+
483
+ actual_metrics = [m.to_dict() for m in metrics[MetricType.Counts]]
484
+ expected_metrics = [
485
+ {
486
+ "type": "Counts",
487
+ "value": {"tp": 1, "fp": 0, "fn": 0},
488
+ "parameters": {
489
+ "iou_threshold": 0.5,
490
+ "score_threshold": 0.0,
491
+ "label": {"key": "class", "value": "label1"},
492
+ },
493
+ },
494
+ {
495
+ "type": "Counts",
496
+ "value": {"tp": 1, "fp": 0, "fn": 0},
497
+ "parameters": {
498
+ "iou_threshold": 0.75,
499
+ "score_threshold": 0.0,
500
+ "label": {"key": "class", "value": "label1"},
501
+ },
502
+ },
503
+ {
504
+ "type": "Counts",
505
+ "value": {"tp": 1, "fp": 0, "fn": 0},
506
+ "parameters": {
507
+ "iou_threshold": 0.5,
508
+ "score_threshold": 0.0,
509
+ "label": {"key": "class", "value": "label2"},
510
+ },
511
+ },
512
+ {
513
+ "type": "Counts",
514
+ "value": {"tp": 1, "fp": 0, "fn": 0},
515
+ "parameters": {
516
+ "iou_threshold": 0.75,
517
+ "score_threshold": 0.0,
518
+ "label": {"key": "class", "value": "label2"},
519
+ },
520
+ },
521
+ {
522
+ "type": "Counts",
523
+ "value": {"tp": 0, "fp": 1, "fn": 1},
524
+ "parameters": {
525
+ "iou_threshold": 0.5,
526
+ "score_threshold": 0.0,
527
+ "label": {"key": "class", "value": "label3"},
528
+ },
529
+ },
530
+ {
531
+ "type": "Counts",
532
+ "value": {"tp": 0, "fp": 1, "fn": 1},
533
+ "parameters": {
534
+ "iou_threshold": 0.75,
535
+ "score_threshold": 0.0,
536
+ "label": {"key": "class", "value": "label3"},
537
+ },
538
+ },
539
+ ]
540
+ for m in actual_metrics:
541
+ assert m in expected_metrics
542
+ for m in expected_metrics:
543
+ assert m in actual_metrics
@@ -1478,7 +1478,7 @@ def test_detailed_counts_using_torch_metrics_example(
1478
1478
  "tp": [9, 6, 4, 3, 2, 1, 1, 0],
1479
1479
  "fp_misclassification": [0, 0, 0, 0, 0, 0, 0, 0],
1480
1480
  "fp_hallucination": [0, 0, 0, 0, 0, 0, 0, 0],
1481
- "fn_misclassification": [0, 1, 1, 1, 0, 0, 0, 0],
1481
+ "fn_misclassification": [0, 0, 0, 0, 0, 0, 0, 0],
1482
1482
  "fn_missing_prediction": [1, 4, 6, 7, 8, 9, 9, 10],
1483
1483
  "tn": None,
1484
1484
  "tp_examples": [
@@ -1504,9 +1504,9 @@ def test_detailed_counts_using_torch_metrics_example(
1504
1504
  "fp_hallucination_examples": [[], [], [], [], [], [], [], []],
1505
1505
  "fn_misclassification_examples": [
1506
1506
  [],
1507
- ["3"],
1508
- ["3"],
1509
- ["3"],
1507
+ [],
1508
+ [],
1509
+ [],
1510
1510
  [],
1511
1511
  [],
1512
1512
  [],
@@ -1828,3 +1828,125 @@ def test_detailed_counts_fp_hallucination_edge_case(
1828
1828
  assert m in expected_metrics
1829
1829
  for m in expected_metrics:
1830
1830
  assert m in actual_metrics
1831
+
1832
+
1833
+ def test_detailed_counts_ranked_pair_ordering(
1834
+ detection_ranked_pair_ordering: Detection,
1835
+ ):
1836
+
1837
+ loader = DataLoader()
1838
+ loader.add_data(detections=[detection_ranked_pair_ordering])
1839
+ evaluator = loader.finalize()
1840
+
1841
+ assert evaluator.metadata == {
1842
+ "ignored_prediction_labels": [
1843
+ ("class", "label4"),
1844
+ ],
1845
+ "missing_prediction_labels": [],
1846
+ "n_datums": 1,
1847
+ "n_groundtruths": 3,
1848
+ "n_labels": 4,
1849
+ "n_predictions": 4,
1850
+ }
1851
+
1852
+ metrics = evaluator.compute_detailed_counts(
1853
+ iou_thresholds=[0.5],
1854
+ score_thresholds=[0.0],
1855
+ n_samples=0,
1856
+ )
1857
+
1858
+ actual_metrics = [mm.to_dict() for m in metrics for mm in m]
1859
+ expected_metrics = [
1860
+ {
1861
+ "type": "DetailedCounts",
1862
+ "value": {
1863
+ "tp": [1],
1864
+ "fp_misclassification": [0],
1865
+ "fp_hallucination": [0],
1866
+ "fn_misclassification": [0],
1867
+ "fn_missing_prediction": [0],
1868
+ "tn": None,
1869
+ "tp_examples": [[]],
1870
+ "fp_misclassification_examples": [[]],
1871
+ "fp_hallucination_examples": [[]],
1872
+ "fn_misclassification_examples": [[]],
1873
+ "fn_missing_prediction_examples": [[]],
1874
+ "tn_examples": None,
1875
+ },
1876
+ "parameters": {
1877
+ "score_thresholds": [0.0],
1878
+ "iou_threshold": 0.5,
1879
+ "label": {"key": "class", "value": "label1"},
1880
+ },
1881
+ },
1882
+ {
1883
+ "type": "DetailedCounts",
1884
+ "value": {
1885
+ "tp": [1],
1886
+ "fp_misclassification": [0],
1887
+ "fp_hallucination": [0],
1888
+ "fn_misclassification": [0],
1889
+ "fn_missing_prediction": [0],
1890
+ "tn": None,
1891
+ "tp_examples": [[]],
1892
+ "fp_misclassification_examples": [[]],
1893
+ "fp_hallucination_examples": [[]],
1894
+ "fn_misclassification_examples": [[]],
1895
+ "fn_missing_prediction_examples": [[]],
1896
+ "tn_examples": None,
1897
+ },
1898
+ "parameters": {
1899
+ "score_thresholds": [0.0],
1900
+ "iou_threshold": 0.5,
1901
+ "label": {"key": "class", "value": "label2"},
1902
+ },
1903
+ },
1904
+ {
1905
+ "type": "DetailedCounts",
1906
+ "value": {
1907
+ "tp": [0],
1908
+ "fp_misclassification": [0],
1909
+ "fp_hallucination": [1],
1910
+ "fn_misclassification": [1],
1911
+ "fn_missing_prediction": [0],
1912
+ "tn": None,
1913
+ "tp_examples": [[]],
1914
+ "fp_misclassification_examples": [[]],
1915
+ "fp_hallucination_examples": [[]],
1916
+ "fn_misclassification_examples": [[]],
1917
+ "fn_missing_prediction_examples": [[]],
1918
+ "tn_examples": None,
1919
+ },
1920
+ "parameters": {
1921
+ "score_thresholds": [0.0],
1922
+ "iou_threshold": 0.5,
1923
+ "label": {"key": "class", "value": "label3"},
1924
+ },
1925
+ },
1926
+ {
1927
+ "type": "DetailedCounts",
1928
+ "value": {
1929
+ "tp": [0],
1930
+ "fp_misclassification": [0],
1931
+ "fp_hallucination": [1],
1932
+ "fn_misclassification": [0],
1933
+ "fn_missing_prediction": [0],
1934
+ "tn": None,
1935
+ "tp_examples": [[]],
1936
+ "fp_misclassification_examples": [[]],
1937
+ "fp_hallucination_examples": [[]],
1938
+ "fn_misclassification_examples": [[]],
1939
+ "fn_missing_prediction_examples": [[]],
1940
+ "tn_examples": None,
1941
+ },
1942
+ "parameters": {
1943
+ "score_thresholds": [0.0],
1944
+ "iou_threshold": 0.5,
1945
+ "label": {"key": "class", "value": "label4"},
1946
+ },
1947
+ },
1948
+ ]
1949
+ for m in actual_metrics:
1950
+ assert m in expected_metrics
1951
+ for m in expected_metrics:
1952
+ assert m in actual_metrics
@@ -1,16 +1,38 @@
1
1
  import numpy as np
2
2
  from numpy.typing import NDArray
3
3
 
4
- # datum id 0
5
- # gt 1
6
- # pd 2
7
- # iou 3
8
- # gt label 4
9
- # pd label 5
10
- # score 6
11
-
12
4
 
13
5
  def compute_iou(data: NDArray[np.floating]) -> NDArray[np.floating]:
6
+ """
7
+ Computes intersection-over-union (IoU) for axis-aligned bounding boxes.
8
+
9
+ Takes data with shape (N, 8):
10
+
11
+ Index 0 - xmin for Box 1
12
+ Index 1 - xmax for Box 1
13
+ Index 2 - ymin for Box 1
14
+ Index 3 - ymax for Box 1
15
+ Index 4 - xmin for Box 2
16
+ Index 5 - xmax for Box 2
17
+ Index 6 - ymin for Box 2
18
+ Index 7 - ymax for Box 2
19
+
20
+ Returns data with shape (N, 1):
21
+
22
+ Index 0 - IoU
23
+
24
+ Parameters
25
+ ----------
26
+ data : NDArray[np.floating]
27
+ A sorted array of classification pairs.
28
+ label_metadata : NDArray[np.int32]
29
+ An array containing metadata related to labels.
30
+
31
+ Returns
32
+ -------
33
+ NDArray[np.floating]
34
+ Compute IoU's.
35
+ """
14
36
 
15
37
  xmin1, xmax1, ymin1, ymax1 = (
16
38
  data[:, 0],
@@ -93,6 +115,33 @@ def compute_ranked_pairs(
93
115
  data: list[NDArray[np.floating]],
94
116
  label_metadata: NDArray[np.integer],
95
117
  ) -> NDArray[np.floating]:
118
+ """
119
+ Performs pair ranking on input data.
120
+
121
+ Takes data with shape (N, 7):
122
+
123
+ Index 0 - Datum Index
124
+ Index 1 - GroundTruth Index
125
+ Index 2 - Prediction Index
126
+ Index 3 - IoU
127
+ Index 4 - GroundTruth Label Index
128
+ Index 5 - Prediction Label Index
129
+ Index 6 - Score
130
+
131
+ Returns data with shape (N - M, 7)
132
+
133
+ Parameters
134
+ ----------
135
+ data : NDArray[np.floating]
136
+ A sorted array of classification pairs.
137
+ label_metadata : NDArray[np.int32]
138
+ An array containing metadata related to labels.
139
+
140
+ Returns
141
+ -------
142
+ NDArray[np.floating]
143
+ A filtered array containing only ranked pairs.
144
+ """
96
145
  pairs = np.concatenate(
97
146
  [
98
147
  _compute_ranked_pairs_for_datum(
@@ -136,6 +185,27 @@ def compute_metrics(
136
185
  """
137
186
  Computes Object Detection metrics.
138
187
 
188
+ Takes data with shape (N, 7):
189
+
190
+ Index 0 - Datum Index
191
+ Index 1 - GroundTruth Index
192
+ Index 2 - Prediction Index
193
+ Index 3 - IoU
194
+ Index 4 - GroundTruth Label Index
195
+ Index 5 - Prediction Label Index
196
+ Index 6 - Score
197
+
198
+ Parameters
199
+ ----------
200
+ data : NDArray[np.floating]
201
+ A sorted array of classification pairs.
202
+ label_metadata : NDArray[np.int32]
203
+ An array containing metadata related to labels.
204
+ iou_thresholds : NDArray[np.floating]
205
+ A 1-D array containing IoU thresholds.
206
+ score_thresholds : NDArray[np.floating]
207
+ A 1-D array containing score thresholds.
208
+
139
209
  Returns
140
210
  -------
141
211
  tuple[NDArray, NDArray, NDArray NDArray]
@@ -155,7 +225,7 @@ def compute_metrics(
155
225
 
156
226
  average_precision = np.zeros((n_ious, n_labels))
157
227
  average_recall = np.zeros((n_scores, n_labels))
158
- precision_recall = np.zeros((n_ious, n_scores, n_labels, 7))
228
+ counts = np.zeros((n_ious, n_scores, n_labels, 7))
159
229
 
160
230
  pd_labels = data[:, 5].astype(int)
161
231
  unique_pd_labels = np.unique(pd_labels)
@@ -245,7 +315,7 @@ def compute_metrics(
245
315
  out=accuracy,
246
316
  )
247
317
 
248
- precision_recall[iou_idx][score_idx] = np.concatenate(
318
+ counts[iou_idx][score_idx] = np.concatenate(
249
319
  (
250
320
  tp_count[:, np.newaxis],
251
321
  fp_count[:, np.newaxis],
@@ -353,7 +423,7 @@ def compute_metrics(
353
423
  return (
354
424
  ap_results,
355
425
  ar_results,
356
- precision_recall,
426
+ counts,
357
427
  pr_curve,
358
428
  )
359
429
 
@@ -365,16 +435,49 @@ def compute_detailed_counts(
365
435
  score_thresholds: np.ndarray,
366
436
  n_samples: int,
367
437
  ) -> np.ndarray:
368
-
369
438
  """
370
- 0 label
371
- 1 tp
372
- ...
373
- 2 fp - 1
374
- 3 fp - 2
375
- 4 fn - misclassification
376
- 5 fn - hallucination
377
- 6 tn
439
+ Compute detailed counts.
440
+
441
+ Takes data with shape (N, 7):
442
+
443
+ Index 0 - Datum Index
444
+ Index 1 - GroundTruth Index
445
+ Index 2 - Prediction Index
446
+ Index 3 - IoU
447
+ Index 4 - GroundTruth Label Index
448
+ Index 5 - Prediction Label Index
449
+ Index 6 - Score
450
+
451
+ Outputs an array with shape (N_IoUs, N_Score, N_Labels, 5 * n_samples + 5):
452
+
453
+ Index 0 - True Positive Count
454
+ ... Datum ID Examples
455
+ Index n_samples + 1 - False Positive Misclassification Count
456
+ ... Datum ID Examples
457
+ Index 2 * n_samples + 2 - False Positive Hallucination Count
458
+ ... Datum ID Examples
459
+ Index 3 * n_samples + 3 - False Negative Misclassification Count
460
+ ... Datum ID Examples
461
+ Index 4 * n_samples + 4 - False Negative Missing Prediction Count
462
+ ... Datum ID Examples
463
+
464
+ Parameters
465
+ ----------
466
+ data : NDArray[np.floating]
467
+ A sorted array of classification pairs.
468
+ label_metadata : NDArray[np.int32]
469
+ An array containing metadata related to labels.
470
+ iou_thresholds : NDArray[np.floating]
471
+ A 1-D array containing IoU thresholds.
472
+ score_thresholds : NDArray[np.floating]
473
+ A 1-D array containing score thresholds.
474
+ n_samples : int
475
+ The number of examples to return per count.
476
+
477
+ Returns
478
+ -------
479
+ NDArray[np.floating]
480
+ The detailed counts with optional examples.
378
481
  """
379
482
 
380
483
  n_labels = label_metadata.shape[0]
@@ -466,12 +569,30 @@ def compute_detailed_counts(
466
569
  | mask_groundtruths_without_passing_score
467
570
  )
468
571
 
469
- tp = np.unique(data[mask_tp][:, [0, 2, 5]], axis=0)
572
+ tp_pds = np.unique(data[mask_tp][:, [0, 2, 5]], axis=0)
573
+ tp_gts = np.unique(data[mask_tp][:, [0, 1, 4]], axis=0)
470
574
  fp_misclf = np.unique(data[mask_fp_misclf][:, [0, 2, 5]], axis=0)
471
575
  fp_halluc = np.unique(data[mask_fp_halluc][:, [0, 2, 5]], axis=0)
472
576
  fn_misclf = np.unique(data[mask_fn_misclf][:, [0, 1, 4]], axis=0)
473
577
  fn_misprd = np.unique(data[mask_fn_misprd][:, [0, 1, 4]], axis=0)
474
578
 
579
+ mask_fp_misclf_is_tp = (
580
+ (fp_misclf.reshape(-1, 1, 3) == tp_pds.reshape(1, -1, 3))
581
+ .all(axis=2)
582
+ .any(axis=1)
583
+ )
584
+ mask_fn_misclf_is_tp = (
585
+ (fn_misclf.reshape(-1, 1, 3) == tp_gts.reshape(1, -1, 3))
586
+ .all(axis=2)
587
+ .any(axis=1)
588
+ )
589
+
590
+ tp = tp_pds
591
+ fp_misclf = fp_misclf[~mask_fp_misclf_is_tp]
592
+ fp_halluc = fp_halluc
593
+ fn_misclf = fn_misclf[~mask_fn_misclf_is_tp]
594
+ fn_misprd = fn_misprd
595
+
475
596
  tp_count = np.bincount(tp[:, 2].astype(int), minlength=n_labels)
476
597
  fp_misclf_count = np.bincount(
477
598
  fp_misclf[:, 2].astype(int), minlength=n_labels
@@ -58,6 +58,10 @@ class Filter:
58
58
 
59
59
 
60
60
  class Evaluator:
61
+ """
62
+ Object Detection Evaluator
63
+ """
64
+
61
65
  def __init__(self):
62
66
 
63
67
  # metadata
@@ -87,6 +91,9 @@ class Evaluator:
87
91
 
88
92
  @property
89
93
  def ignored_prediction_labels(self) -> list[tuple[str, str]]:
94
+ """
95
+ Prediction labels that are not present in the ground truth set.
96
+ """
90
97
  glabels = set(np.where(self._label_metadata[:, 0] > 0)[0])
91
98
  plabels = set(np.where(self._label_metadata[:, 1] > 0)[0])
92
99
  return [
@@ -95,6 +102,9 @@ class Evaluator:
95
102
 
96
103
  @property
97
104
  def missing_prediction_labels(self) -> list[tuple[str, str]]:
105
+ """
106
+ Ground truth labels that are not present in the prediction set.
107
+ """
98
108
  glabels = set(np.where(self._label_metadata[:, 0] > 0)[0])
99
109
  plabels = set(np.where(self._label_metadata[:, 1] > 0)[0])
100
110
  return [
@@ -103,6 +113,9 @@ class Evaluator:
103
113
 
104
114
  @property
105
115
  def metadata(self) -> dict:
116
+ """
117
+ Evaluation metadata.
118
+ """
106
119
  return {
107
120
  "n_datums": self.n_datums,
108
121
  "n_groundtruths": self.n_groundtruths,
@@ -216,16 +229,21 @@ class Evaluator:
216
229
  filter_: Filter | None = None,
217
230
  ) -> dict[MetricType, list]:
218
231
  """
219
- Runs evaluation over cached data.
232
+ Performs an evaluation and returns metrics.
220
233
 
221
234
  Parameters
222
235
  ----------
223
236
  iou_thresholds : list[float]
224
- A list of iou thresholds to compute over.
237
+ A list of IoU thresholds to compute metrics over.
225
238
  score_thresholds : list[float]
226
- A list of score thresholds to compute over.
227
- filter_mask : NDArray[bool], optional
228
- A boolean mask that filters the cached data.
239
+ A list of score thresholds to compute metrics over.
240
+ filter_ : Filter, optional
241
+ An optional filter object.
242
+
243
+ Returns
244
+ -------
245
+ dict[MetricType, list]
246
+ A dictionary mapping MetricType enumerations to lists of computed metrics.
229
247
  """
230
248
 
231
249
  data = self._ranked_pairs
@@ -360,6 +378,10 @@ class Evaluator:
360
378
  for label_idx, label in self.index_to_label.items():
361
379
  for score_idx, score_threshold in enumerate(score_thresholds):
362
380
  for iou_idx, iou_threshold in enumerate(iou_thresholds):
381
+
382
+ if label_metadata[label_idx, 0] == 0:
383
+ continue
384
+
363
385
  row = precision_recall[iou_idx][score_idx][label_idx]
364
386
  kwargs = {
365
387
  "label": label,
@@ -374,6 +396,7 @@ class Evaluator:
374
396
  **kwargs,
375
397
  )
376
398
  )
399
+
377
400
  metrics[MetricType.Precision].append(
378
401
  Precision(
379
402
  value=row[3],
@@ -532,6 +555,10 @@ class Evaluator:
532
555
 
533
556
 
534
557
  class DataLoader:
558
+ """
559
+ Object Detection DataLoader
560
+ """
561
+
535
562
  def __init__(self):
536
563
  self._evaluator = Evaluator()
537
564
  self.pairs = list()
@@ -539,6 +566,19 @@ class DataLoader:
539
566
  self.prediction_count = defaultdict(lambda: defaultdict(int))
540
567
 
541
568
  def _add_datum(self, uid: str) -> int:
569
+ """
570
+ Helper function for adding a datum to the cache.
571
+
572
+ Parameters
573
+ ----------
574
+ uid : str
575
+ The datum uid.
576
+
577
+ Returns
578
+ -------
579
+ int
580
+ The datum index.
581
+ """
542
582
  if uid not in self._evaluator.uid_to_index:
543
583
  index = len(self._evaluator.uid_to_index)
544
584
  self._evaluator.uid_to_index[uid] = index
@@ -546,6 +586,22 @@ class DataLoader:
546
586
  return self._evaluator.uid_to_index[uid]
547
587
 
548
588
  def _add_label(self, label: tuple[str, str]) -> tuple[int, int]:
589
+ """
590
+ Helper function for adding a label to the cache.
591
+
592
+ Parameters
593
+ ----------
594
+ label : tuple[str, str]
595
+ The label as a tuple in format (key, value).
596
+
597
+ Returns
598
+ -------
599
+ int
600
+ Label index.
601
+ int
602
+ Label key index.
603
+ """
604
+
549
605
  label_id = len(self._evaluator.index_to_label)
550
606
  label_key_id = len(self._evaluator.index_to_label_key)
551
607
  if label not in self._evaluator.label_to_index:
@@ -573,6 +629,16 @@ class DataLoader:
573
629
  detections: list[Detection],
574
630
  show_progress: bool = False,
575
631
  ):
632
+ """
633
+ Adds detections to the cache.
634
+
635
+ Parameters
636
+ ----------
637
+ detections : list[Detection]
638
+ A list of Detection objects.
639
+ show_progress : bool, default=False
640
+ Toggle for tqdm progress bar.
641
+ """
576
642
  disable_tqdm = not show_progress
577
643
  for detection in tqdm(detections, disable=disable_tqdm):
578
644
 
@@ -688,6 +754,17 @@ class DataLoader:
688
754
  detections: list[tuple[dict, dict]],
689
755
  show_progress: bool = False,
690
756
  ):
757
+ """
758
+ Adds Valor-format detections to the cache.
759
+
760
+ Parameters
761
+ ----------
762
+ detections : list[tuple[dict, dict]]
763
+ A list of groundtruth, prediction pairs in Valor-format dictionaries.
764
+ show_progress : bool, default=False
765
+ Toggle for tqdm progress bar.
766
+ """
767
+
691
768
  def _get_bbox_extrema(
692
769
  data: list[list[list[float]]],
693
770
  ) -> tuple[float, float, float, float]:
@@ -809,6 +886,14 @@ class DataLoader:
809
886
  self.pairs.append(np.array(pairs))
810
887
 
811
888
  def finalize(self) -> Evaluator:
889
+ """
890
+ Performs data finalization and some preprocessing steps.
891
+
892
+ Returns
893
+ -------
894
+ Evaluator
895
+ A ready-to-use evaluator object.
896
+ """
812
897
 
813
898
  self.pairs = [pair for pair in self.pairs if pair.size > 0]
814
899
  if len(self.pairs) == 0:
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: valor-lite
3
- Version: 0.33.1
3
+ Version: 0.33.2
4
4
  Summary: Compute valor metrics directly in your client.
5
5
  License: MIT License
6
6
 
File without changes
File without changes
File without changes
File without changes