evaluators 3.0.0__tar.gz → 3.2.0__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.
- evaluators-3.2.0/PKG-INFO +432 -0
- evaluators-3.2.0/README.md +388 -0
- evaluators-3.2.0/pyproject.toml +248 -0
- evaluators-3.0.0/pyproject.toml → evaluators-3.2.0/pyproject.toml.orig +22 -4
- {evaluators-3.0.0 → evaluators-3.2.0}/sources/evaluators/_engine.py +18 -4
- evaluators-3.2.0/sources/evaluators/cli/__init__.py +8 -0
- {evaluators-3.0.0 → evaluators-3.2.0}/sources/evaluators/cli/compute.py +8 -5
- {evaluators-3.0.0 → evaluators-3.2.0}/sources/evaluators/cli/memory.py +3 -1
- {evaluators-3.0.0 → evaluators-3.2.0}/sources/evaluators/cli/third_party.py +1 -1
- evaluators-3.2.0/sources/evaluators/integrations/__init__.py +7 -0
- {evaluators-3.0.0 → evaluators-3.2.0}/sources/evaluators/integrations/lightning.py +17 -3
- {evaluators-3.0.0 → evaluators-3.2.0}/sources/evaluators/memory/backends/lmdb/_writer.py +17 -7
- {evaluators-3.0.0 → evaluators-3.2.0}/sources/evaluators/memory/backends/memmap/_reader.py +11 -0
- {evaluators-3.0.0 → evaluators-3.2.0}/sources/evaluators/memory/backends/memmap/_writer.py +108 -19
- {evaluators-3.0.0 → evaluators-3.2.0}/sources/evaluators/memory/codecs/_blosc.py +14 -1
- {evaluators-3.0.0 → evaluators-3.2.0}/sources/evaluators/memory/schema.py +4 -0
- evaluators-3.2.0/sources/evaluators/metrics/domain/__init__.py +19 -0
- evaluators-3.2.0/sources/evaluators/metrics/domain/_ap.py +171 -0
- evaluators-3.2.0/sources/evaluators/metrics/domain/_greedy_match.py +106 -0
- evaluators-3.2.0/sources/evaluators/metrics/domain/_keys.py +105 -0
- evaluators-3.2.0/sources/evaluators/metrics/domain/_matching.py +105 -0
- evaluators-3.2.0/sources/evaluators/metrics/domain/box3d/__init__.py +16 -0
- evaluators-3.2.0/sources/evaluators/metrics/domain/box3d/_ap_metric.py +318 -0
- evaluators-3.2.0/sources/evaluators/metrics/domain/box3d/_similarity.py +163 -0
- evaluators-3.2.0/sources/evaluators/metrics/domain/box3d/_types.py +72 -0
- {evaluators-3.0.0 → evaluators-3.2.0}/sources/evaluators/metrics/domain/depth.py +228 -10
- evaluators-3.2.0/sources/evaluators/metrics/domain/dvps.py +1346 -0
- evaluators-3.2.0/sources/evaluators/metrics/domain/occupancy.py +196 -0
- {evaluators-3.0.0 → evaluators-3.2.0}/sources/evaluators/metrics/domain/segmentation/__init__.py +2 -0
- {evaluators-3.0.0 → evaluators-3.2.0}/sources/evaluators/metrics/domain/segmentation/panoptic.py +104 -22
- {evaluators-3.0.0 → evaluators-3.2.0}/sources/evaluators/metrics/domain/segmentation/semantic.py +139 -42
- evaluators-3.2.0/sources/evaluators/metrics/domain/sensorsim/__init__.py +26 -0
- evaluators-3.2.0/sources/evaluators/metrics/domain/sensorsim/_cross_modal_alignment.py +106 -0
- evaluators-3.2.0/sources/evaluators/metrics/domain/sensorsim/_image_quality.py +113 -0
- evaluators-3.2.0/sources/evaluators/metrics/domain/sensorsim/_point_cloud_quality.py +151 -0
- evaluators-3.2.0/sources/evaluators/metrics/domain/sensorsim/_radar_quality.py +156 -0
- evaluators-3.2.0/sources/evaluators/metrics/domain/sensorsim/_temporal_consistency.py +117 -0
- {evaluators-3.0.0 → evaluators-3.2.0}/sources/evaluators/metrics/domain/step.py +177 -90
- {evaluators-3.0.0 → evaluators-3.2.0}/sources/evaluators/metrics/domain/tracking/_base.py +6 -3
- {evaluators-3.0.0 → evaluators-3.2.0}/sources/evaluators/metrics/domain/tracking/_clear.py +2 -2
- {evaluators-3.0.0 → evaluators-3.2.0}/sources/evaluators/metrics/domain/tracking/_hota.py +2 -2
- {evaluators-3.0.0 → evaluators-3.2.0}/sources/evaluators/metrics/domain/tracking/_identity.py +17 -2
- {evaluators-3.0.0 → evaluators-3.2.0}/sources/evaluators/metrics/domain/tracking/_preprocess.py +2 -2
- evaluators-3.2.0/sources/evaluators/metrics/domain/tracking/_torchmatch_adapter.py +40 -0
- evaluators-3.2.0/sources/evaluators/metrics/domain/trajectory/__init__.py +15 -0
- evaluators-3.2.0/sources/evaluators/metrics/domain/trajectory/_types.py +79 -0
- evaluators-3.2.0/sources/evaluators/metrics/domain/vectormap/__init__.py +21 -0
- evaluators-3.2.0/sources/evaluators/metrics/domain/vectormap/_ap_metric.py +326 -0
- evaluators-3.2.0/sources/evaluators/metrics/domain/vectormap/_similarity.py +112 -0
- evaluators-3.2.0/sources/evaluators/metrics/domain/vectormap/_types.py +89 -0
- {evaluators-3.0.0 → evaluators-3.2.0}/sources/evaluators/presets/cityscapes.py +28 -34
- {evaluators-3.0.0 → evaluators-3.2.0}/sources/evaluators/presets/semkitti.py +28 -31
- {evaluators-3.0.0 → evaluators-3.2.0}/sources/evaluators/types.py +27 -1
- evaluators-3.0.0/PKG-INFO +0 -313
- evaluators-3.0.0/README.md +0 -271
- evaluators-3.0.0/sources/evaluators/metrics/domain/dvps.py +0 -886
- {evaluators-3.0.0 → evaluators-3.2.0}/sources/evaluators/__init__.py +0 -0
- {evaluators-3.0.0 → evaluators-3.2.0}/sources/evaluators/_streams.py +0 -0
- {evaluators-3.0.0 → evaluators-3.2.0}/sources/evaluators/benchmarks/__init__.py +0 -0
- {evaluators-3.0.0 → evaluators-3.2.0}/sources/evaluators/benchmarks/coordinator.py +0 -0
- {evaluators-3.0.0 → evaluators-3.2.0}/sources/evaluators/benchmarks/scenarios.py +0 -0
- {evaluators-3.0.0 → evaluators-3.2.0}/sources/evaluators/benchmarks/sys_info.py +0 -0
- {evaluators-3.0.0 → evaluators-3.2.0}/sources/evaluators/benchmarks/worker.py +0 -0
- {evaluators-3.0.0 → evaluators-3.2.0}/sources/evaluators/cli/__main__.py +0 -0
- {evaluators-3.0.0 → evaluators-3.2.0}/sources/evaluators/constants.py +0 -0
- {evaluators-3.0.0 → evaluators-3.2.0}/sources/evaluators/io/__init__.py +0 -0
- {evaluators-3.0.0 → evaluators-3.2.0}/sources/evaluators/io/mot_challenge.py +0 -0
- {evaluators-3.0.0 → evaluators-3.2.0}/sources/evaluators/memory/__init__.py +0 -0
- {evaluators-3.0.0 → evaluators-3.2.0}/sources/evaluators/memory/backends/__init__.py +0 -0
- {evaluators-3.0.0 → evaluators-3.2.0}/sources/evaluators/memory/backends/_registry.py +0 -0
- {evaluators-3.0.0 → evaluators-3.2.0}/sources/evaluators/memory/backends/filesystem/__init__.py +0 -0
- {evaluators-3.0.0 → evaluators-3.2.0}/sources/evaluators/memory/backends/filesystem/_reader.py +0 -0
- {evaluators-3.0.0 → evaluators-3.2.0}/sources/evaluators/memory/backends/filesystem/_writer.py +0 -0
- {evaluators-3.0.0 → evaluators-3.2.0}/sources/evaluators/memory/backends/gds/__init__.py +0 -0
- {evaluators-3.0.0 → evaluators-3.2.0}/sources/evaluators/memory/backends/gds/reader.py +0 -0
- {evaluators-3.0.0 → evaluators-3.2.0}/sources/evaluators/memory/backends/gds/utils.py +0 -0
- {evaluators-3.0.0 → evaluators-3.2.0}/sources/evaluators/memory/backends/gds/writer.py +0 -0
- {evaluators-3.0.0 → evaluators-3.2.0}/sources/evaluators/memory/backends/lmdb/__init__.py +0 -0
- {evaluators-3.0.0 → evaluators-3.2.0}/sources/evaluators/memory/backends/lmdb/_reader.py +0 -0
- {evaluators-3.0.0 → evaluators-3.2.0}/sources/evaluators/memory/backends/memmap/__init__.py +0 -0
- {evaluators-3.0.0 → evaluators-3.2.0}/sources/evaluators/memory/backends/memmap/_buffers.py +0 -0
- {evaluators-3.0.0 → evaluators-3.2.0}/sources/evaluators/memory/codecs/__init__.py +0 -0
- {evaluators-3.0.0 → evaluators-3.2.0}/sources/evaluators/memory/codecs/_png.py +0 -0
- {evaluators-3.0.0 → evaluators-3.2.0}/sources/evaluators/memory/codecs/_raw.py +0 -0
- {evaluators-3.0.0 → evaluators-3.2.0}/sources/evaluators/memory/codecs/_tiff.py +0 -0
- {evaluators-3.0.0 → evaluators-3.2.0}/sources/evaluators/memory/config.py +0 -0
- {evaluators-3.0.0 → evaluators-3.2.0}/sources/evaluators/memory/interfaces.py +0 -0
- {evaluators-3.0.0 → evaluators-3.2.0}/sources/evaluators/metrics/__init__.py +0 -0
- {evaluators-3.0.0 → evaluators-3.2.0}/sources/evaluators/metrics/adapters/coco.py +0 -0
- {evaluators-3.0.0 → evaluators-3.2.0}/sources/evaluators/metrics/adapters/trackeval.py +0 -0
- {evaluators-3.0.0 → evaluators-3.2.0}/sources/evaluators/metrics/domain/segmentation/_utils.py +0 -0
- {evaluators-3.0.0 → evaluators-3.2.0}/sources/evaluators/metrics/domain/segmentation/vendor/__init__.py +0 -0
- {evaluators-3.0.0 → evaluators-3.2.0}/sources/evaluators/metrics/domain/segmentation/vendor/panopticapi.py +0 -0
- {evaluators-3.0.0 → evaluators-3.2.0}/sources/evaluators/metrics/domain/tracking/__init__.py +0 -0
- {evaluators-3.0.0 → evaluators-3.2.0}/sources/evaluators/metrics/domain/tracking/_classcomb.py +0 -0
- {evaluators-3.0.0 → evaluators-3.2.0}/sources/evaluators/metrics/domain/tracking/_combine.py +0 -0
- {evaluators-3.0.0 → evaluators-3.2.0}/sources/evaluators/metrics/domain/tracking/_seqdata.py +0 -0
- {evaluators-3.0.0 → evaluators-3.2.0}/sources/evaluators/metrics/domain/tracking/_similarity.py +0 -0
- {evaluators-3.0.0 → evaluators-3.2.0}/sources/evaluators/metrics/stats.py +0 -0
- {evaluators-3.0.0 → evaluators-3.2.0}/sources/evaluators/presets/__init__.py +0 -0
- {evaluators-3.0.0 → evaluators-3.2.0}/sources/evaluators/presets/kitti.py +0 -0
- {evaluators-3.0.0 → evaluators-3.2.0}/sources/evaluators/presets/mot.py +0 -0
- {evaluators-3.0.0 → evaluators-3.2.0}/sources/evaluators/scheduling/__init__.py +0 -0
- {evaluators-3.0.0 → evaluators-3.2.0}/sources/evaluators/scheduling/_catalog.py +0 -0
- {evaluators-3.0.0 → evaluators-3.2.0}/sources/evaluators/scheduling/_partitioner.py +0 -0
- {evaluators-3.0.0 → evaluators-3.2.0}/sources/evaluators/third_party/__init__.py +0 -0
- {evaluators-3.0.0 → evaluators-3.2.0}/sources/evaluators/third_party/__main__.py +0 -0
- {evaluators-3.0.0 → evaluators-3.2.0}/sources/evaluators/third_party/adapters/__init__.py +0 -0
- {evaluators-3.0.0 → evaluators-3.2.0}/sources/evaluators/third_party/adapters/coco.py +0 -0
- {evaluators-3.0.0 → evaluators-3.2.0}/sources/evaluators/third_party/adapters/dvpq.py +0 -0
- {evaluators-3.0.0 → evaluators-3.2.0}/sources/evaluators/third_party/adapters/pq.py +0 -0
- {evaluators-3.0.0 → evaluators-3.2.0}/sources/evaluators/utils/comms.py +0 -0
- {evaluators-3.0.0 → evaluators-3.2.0}/sources/evaluators/utils/configs.py +0 -0
- {evaluators-3.0.0 → evaluators-3.2.0}/sources/evaluators/utils/tensor_ops.py +0 -0
- {evaluators-3.0.0 → evaluators-3.2.0}/sources/evaluators/visuals/__init__.py +0 -0
- {evaluators-3.0.0 → evaluators-3.2.0}/sources/evaluators/visuals/metric.py +0 -0
- {evaluators-3.0.0 → evaluators-3.2.0}/sources/evaluators/visuals/tools/__init__.py +0 -0
- {evaluators-3.0.0 → evaluators-3.2.0}/sources/evaluators/visuals/tools/_visualizer.py +0 -0
- {evaluators-3.0.0 → evaluators-3.2.0}/sources/evaluators/visuals/tools/transform.py +0 -0
|
@@ -0,0 +1,432 @@
|
|
|
1
|
+
Metadata-Version: 2.3
|
|
2
|
+
Name: evaluators
|
|
3
|
+
Version: 3.2.0
|
|
4
|
+
Summary: Various scene understanding and perception evaluation metrics.
|
|
5
|
+
Author: Kurt Stolle
|
|
6
|
+
Author-email: Kurt Stolle <kurt@computer.org>
|
|
7
|
+
Classifier: Development Status :: 4 - Beta
|
|
8
|
+
Classifier: Intended Audience :: Developers
|
|
9
|
+
Classifier: Intended Audience :: Science/Research
|
|
10
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
11
|
+
Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
|
|
12
|
+
Classifier: Topic :: Scientific/Engineering :: Image Recognition
|
|
13
|
+
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
14
|
+
Classifier: Programming Language :: Python
|
|
15
|
+
Classifier: Programming Language :: Python :: 3
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
18
|
+
Classifier: Operating System :: OS Independent
|
|
19
|
+
Requires-Dist: torch>2.7
|
|
20
|
+
Requires-Dist: torchvision
|
|
21
|
+
Requires-Dist: torchmetrics
|
|
22
|
+
Requires-Dist: tensordict>=0.11.0
|
|
23
|
+
Requires-Dist: numpy>=2.0.0
|
|
24
|
+
Requires-Dist: matplotlib>=3.9.0
|
|
25
|
+
Requires-Dist: rich>=13.9.4
|
|
26
|
+
Requires-Dist: pydantic
|
|
27
|
+
Requires-Dist: opencv-python-headless>=4.13.0.92
|
|
28
|
+
Requires-Dist: omegaconf>=2.3.0
|
|
29
|
+
Requires-Dist: hydra-zen>=0.16.0
|
|
30
|
+
Requires-Dist: scipy>=1.14.0
|
|
31
|
+
Requires-Dist: shapely>=2.0.0
|
|
32
|
+
Requires-Dist: torchmatch>=1.0.4
|
|
33
|
+
Requires-Dist: blosc2>=4.1.2 ; extra == 'blosc2'
|
|
34
|
+
Requires-Dist: lightning>=2.6.1 ; extra == 'lightning'
|
|
35
|
+
Requires-Dist: lmdb>=2.2.0 ; extra == 'lmdb'
|
|
36
|
+
Requires-Dist: imagecodecs>=2026.3.6 ; extra == 'tiff'
|
|
37
|
+
Requires-Dist: tifffile>=2026.3.3 ; extra == 'tiff'
|
|
38
|
+
Requires-Python: >=3.13, <3.14
|
|
39
|
+
Provides-Extra: blosc2
|
|
40
|
+
Provides-Extra: lightning
|
|
41
|
+
Provides-Extra: lmdb
|
|
42
|
+
Provides-Extra: tiff
|
|
43
|
+
Description-Content-Type: text/markdown
|
|
44
|
+
|
|
45
|
+
# Scalable Distributed Evaluation for Computer Vision
|
|
46
|
+
|
|
47
|
+
[](https://pypi.org/project/evaluators/)
|
|
48
|
+
[](https://pypi.org/project/evaluators/)
|
|
49
|
+
[](https://github.com/khwstolle/evaluators/blob/main/LICENSE)
|
|
50
|
+
[](https://github.com/khwstolle/evaluators/actions)
|
|
51
|
+
|
|
52
|
+
Metrics like panoptic quality over video (VPQ, DVPQ), depth-aware segmentation-and-tracking quality (DSTQ), and multi-object tracking metrics (HOTA, CLEAR, IDF1) are computed over whole sequences, not independent per-frame samples: window/tube matching across consecutive frames, or track-identity consistency across an entire video. A validation loop doesn't hand a metric a whole sequence at once. It hands it one batch at a time, and a given sequence's frames can land in different steps, different dataloader workers, and different distributed ranks depending on how the loader shards them. Standard online metric aggregation, `update()` per step, reduce across ranks at `compute()`, works when each sample's contribution reduces independently of order. It breaks down when a metric needs contiguous, ordered access to every frame of a sequence before it can produce a result.
|
|
53
|
+
|
|
54
|
+
The usual workaround is to write every prediction to disk during the validation epoch and run the official reference script by hand afterward, outside the training loop. Evaluators automates that instead. Predictions still stream to a storage backend (Memory Map or LMDB) during inference, with no metric computation in the hot path. Once the epoch or run ends, the pipeline reassembles complete, correctly-ordered sequences, regardless of how their frames were scattered across steps and ranks, and computes the metric against the whole sequence automatically. Dropping `DistributedEvaluationCallback` into a PyTorch Lightning `Trainer` is the common case: whole-sequence metrics get logged like any other validation metric, with no manual step in between.
|
|
55
|
+
|
|
56
|
+
Because predictions are persisted as a side effect, the same pipeline also supports deferred, offline re-evaluation: save the config during inference, then compute later, on a different machine, without re-running the model. That capability is real and useful, particularly for reproducibility, but it follows from the design; it isn't the reason the library exists.
|
|
57
|
+
|
|
58
|
+
The key features are:
|
|
59
|
+
|
|
60
|
+
- **Whole-sequence metrics, computed automatically.**
|
|
61
|
+
Handles metrics that standard per-step online aggregation cannot: window/tube-based video panoptic quality (VPQ, DVPQ, DSTQ) and identity-consistency tracking metrics (HOTA, CLEAR, IDF1), reassembled and computed correctly regardless of how a sequence's frames were split across steps and ranks.
|
|
62
|
+
|
|
63
|
+
- **Drops into a training loop.**
|
|
64
|
+
`DistributedEvaluationCallback` binds to a PyTorch Lightning `Trainer` and handles the write, reassembly, and compute stages automatically. Predictions are also written to disk, which supports deferred, offline re-evaluation on stored results.
|
|
65
|
+
|
|
66
|
+
- **Distributed by design.**
|
|
67
|
+
Synchronizes metric computation across multiple nodes and GPUs using `torchmetrics`, `torchmatch` (Hungarian assignment for the tracking metrics), and custom scheduling logic.
|
|
68
|
+
|
|
69
|
+
- **Explicit Memory Schemas.**
|
|
70
|
+
Uses Pydantic-based schemas to define data formats and encodings (PNG, TIFF, Raw) up front, ensuring type safety and storage efficiency.
|
|
71
|
+
|
|
72
|
+
- **Lazy loading.**
|
|
73
|
+
Supports referencing ground truth data from disk rather than duplicating it in memory caches, enabling evaluation of terabyte-scale datasets.
|
|
74
|
+
|
|
75
|
+
- **Multi-domain.**
|
|
76
|
+
Includes verified implementations for:
|
|
77
|
+
- Segmentation: Panoptic quality (PQ), semantic mIoU.
|
|
78
|
+
- DVPS: Video panoptic quality (VPQ), depth-aware video panoptic quality (DVPQ).
|
|
79
|
+
- Depth: Eigen et al. metrics (AbsRel, RMSE).
|
|
80
|
+
- Tracking: HOTA, CLEAR (MOTA/MOTP), identity F1 (IDF1).
|
|
81
|
+
- STEP: Segmentation and tracking quality (STQ), depth-aware STQ (DSTQ).
|
|
82
|
+
- Occupancy: per-class IoU/mIoU, occupied-vs-free precision/recall/F1.
|
|
83
|
+
- 3D detection: per-class/per-threshold AP and mAP (Shapely-based 3D IoU or center-distance matching), optional TP-error decomposition and AOS.
|
|
84
|
+
- Vector maps: per-class/per-threshold AP and mAP (Chamfer or Fréchet matching), optional topology accuracy.
|
|
85
|
+
- Sensor simulation: paired image/point-cloud/radar quality, plus single-stream self-consistency (temporal, cross-modal).
|
|
86
|
+
|
|
87
|
+
- **CLI.**
|
|
88
|
+
A command-line interface to inspect, index, and query saved inference results.
|
|
89
|
+
|
|
90
|
+
---
|
|
91
|
+
|
|
92
|
+
## Installation
|
|
93
|
+
|
|
94
|
+
```shell
|
|
95
|
+
pip install evaluators
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
## Quick start
|
|
99
|
+
|
|
100
|
+
### In the loop (PyTorch Lightning)
|
|
101
|
+
|
|
102
|
+
This is the common case: a whole-sequence metric (here `SemanticMetric`; see
|
|
103
|
+
`examples/03_video_dvps.py` for the DVPQ case this pipeline exists for) is
|
|
104
|
+
declared on the `LightningModule`, and `DistributedEvaluationCallback` handles
|
|
105
|
+
writing, sequence reassembly, and computation automatically at the end of
|
|
106
|
+
validation.
|
|
107
|
+
|
|
108
|
+
```python
|
|
109
|
+
import lightning as L
|
|
110
|
+
from tensordict import TensorDict
|
|
111
|
+
from evaluators import MemorySchema, MetricStream, TensorField
|
|
112
|
+
from evaluators.integrations.lightning import DistributedEvaluationCallback
|
|
113
|
+
from evaluators.metrics.domain.segmentation import SemanticKeyMap, SemanticMetric
|
|
114
|
+
|
|
115
|
+
|
|
116
|
+
class SegmentationModel(L.LightningModule):
|
|
117
|
+
def __init__(self):
|
|
118
|
+
super().__init__()
|
|
119
|
+
self.layer = ... # the actual model
|
|
120
|
+
|
|
121
|
+
keys: SemanticKeyMap = {
|
|
122
|
+
"pred": "pred_sem",
|
|
123
|
+
"target": "target_sem",
|
|
124
|
+
"sequence_id": "sequence_id",
|
|
125
|
+
"frame_index": "frame_index",
|
|
126
|
+
}
|
|
127
|
+
self.metric = SemanticMetric(key_map=keys, num_classes=19, ignore_index=255)
|
|
128
|
+
|
|
129
|
+
# Both prediction and ground truth are written to storage here; see
|
|
130
|
+
# examples/02_lazy_loading.py for fetching ground truth from the
|
|
131
|
+
# dataset instead, via `target_source`.
|
|
132
|
+
schema = MemorySchema(fields={
|
|
133
|
+
"pred_sem": TensorField(dtype="int64", shape=(1024, 2048)),
|
|
134
|
+
"target_sem": TensorField(dtype="int64", shape=(1024, 2048)),
|
|
135
|
+
"sequence_id": TensorField(dtype="int64", shape=()),
|
|
136
|
+
"frame_index": TensorField(dtype="int64", shape=()),
|
|
137
|
+
})
|
|
138
|
+
self.eval_stream = MetricStream(metrics=[self.metric], name="semantic", schema=schema)
|
|
139
|
+
|
|
140
|
+
def validation_step(self, batch, batch_idx):
|
|
141
|
+
# `batch` already provides "target_sem", "sequence_id", and
|
|
142
|
+
# "frame_index" from the dataset.
|
|
143
|
+
preds = self.layer(batch["image"]).argmax(dim=1)
|
|
144
|
+
batch["pred_sem"] = preds
|
|
145
|
+
# A leading time dimension is required even for single-frame batches.
|
|
146
|
+
self.eval_stream.update(
|
|
147
|
+
TensorDict(batch, batch_size=[preds.shape[0]]).unsqueeze(1)
|
|
148
|
+
)
|
|
149
|
+
|
|
150
|
+
|
|
151
|
+
trainer = L.Trainer(callbacks=[DistributedEvaluationCallback(output_dir="./eval_cache")])
|
|
152
|
+
trainer.fit(SegmentationModel(), datamodule)
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
Results are logged to the trainer's loggers like any other validation metric.
|
|
156
|
+
See `examples/01_getting_started.py` for a complete, runnable version.
|
|
157
|
+
|
|
158
|
+
### Manual (without Lightning)
|
|
159
|
+
|
|
160
|
+
This is what `DistributedEvaluationCallback` does under the hood. Use it
|
|
161
|
+
directly outside a Lightning loop, or to re-evaluate predictions that were
|
|
162
|
+
already written to disk without re-running the model.
|
|
163
|
+
|
|
164
|
+
The core abstractions are `MetricStream` (for writing) and `run_offline_evaluation` (for computing).
|
|
165
|
+
|
|
166
|
+
#### Step 1: Inference (online)
|
|
167
|
+
|
|
168
|
+
```python
|
|
169
|
+
import torch
|
|
170
|
+
from tensordict import TensorDict
|
|
171
|
+
from evaluators import MetricStream, MemorySchema, TensorField, MemmapTemporalWriter
|
|
172
|
+
from evaluators.metrics.domain.segmentation import SemanticKeyMap, SemanticMetric
|
|
173
|
+
```
|
|
174
|
+
|
|
175
|
+
##### 1. Configure metrics and schema
|
|
176
|
+
|
|
177
|
+
```python
|
|
178
|
+
### Define the source for ground truth (lazy loading)
|
|
179
|
+
|
|
180
|
+
dataset = CityscapesDataset(...)
|
|
181
|
+
keys: SemanticKeyMap = {
|
|
182
|
+
"pred": "pred_sem",
|
|
183
|
+
"target": "target_sem",
|
|
184
|
+
"sequence_id": "sequence_id",
|
|
185
|
+
"frame_index": "frame_index",
|
|
186
|
+
}
|
|
187
|
+
metric = SemanticMetric(
|
|
188
|
+
key_map=keys,
|
|
189
|
+
num_classes=19,
|
|
190
|
+
ignore_index=255,
|
|
191
|
+
target_source=dataset,
|
|
192
|
+
)
|
|
193
|
+
|
|
194
|
+
### Define the explicit memory schema
|
|
195
|
+
### Only the prediction and metadata are written; ground truth is fetched
|
|
196
|
+
### from `dataset` during the Compute stage.
|
|
197
|
+
|
|
198
|
+
schema = MemorySchema(fields={
|
|
199
|
+
"pred_sem": TensorField(dtype="int64", shape=(1024, 2048)),
|
|
200
|
+
"sequence_id": TensorField(dtype="int64", shape=()),
|
|
201
|
+
"frame_index": TensorField(dtype="int64", shape=()),
|
|
202
|
+
})
|
|
203
|
+
```
|
|
204
|
+
|
|
205
|
+
##### 2. Initialize stream and writer
|
|
206
|
+
|
|
207
|
+
```python
|
|
208
|
+
# Create a writer (backend)
|
|
209
|
+
writer = MemmapTemporalWriter(output_dir="./inference_cache/semantic", schema=schema)
|
|
210
|
+
|
|
211
|
+
# Create a stream and bind the writer
|
|
212
|
+
stream = MetricStream(
|
|
213
|
+
metrics=[metric],
|
|
214
|
+
name="semantic",
|
|
215
|
+
schema=schema
|
|
216
|
+
)
|
|
217
|
+
stream.bind(writer)
|
|
218
|
+
```
|
|
219
|
+
|
|
220
|
+
##### 3. Run inference loop
|
|
221
|
+
|
|
222
|
+
```python
|
|
223
|
+
for batch in dataloader: # Model forward pass
|
|
224
|
+
preds = model(batch["image"])
|
|
225
|
+
batch_size = preds.shape[0]
|
|
226
|
+
|
|
227
|
+
# Push to stream (non-blocking). A TensorDict is required, with a
|
|
228
|
+
# leading time dimension even for single-frame batches.
|
|
229
|
+
stream.update(
|
|
230
|
+
TensorDict(
|
|
231
|
+
{
|
|
232
|
+
"pred_sem": preds,
|
|
233
|
+
"sequence_id": batch["sequence_id"],
|
|
234
|
+
"frame_index": batch["frame_index"],
|
|
235
|
+
},
|
|
236
|
+
batch_size=[batch_size],
|
|
237
|
+
).unsqueeze(1)
|
|
238
|
+
)
|
|
239
|
+
|
|
240
|
+
# Finalize
|
|
241
|
+
writer.close()
|
|
242
|
+
```
|
|
243
|
+
|
|
244
|
+
> Note: `sequence_id` and `frame_index` must be `torch.int64`.
|
|
245
|
+
|
|
246
|
+
##### 4. Finalize and compute
|
|
247
|
+
|
|
248
|
+
```python
|
|
249
|
+
from evaluators import run_offline_evaluation
|
|
250
|
+
|
|
251
|
+
# Syncs workers, builds catalog, and runs metrics
|
|
252
|
+
results = run_offline_evaluation(
|
|
253
|
+
metrics=[metric],
|
|
254
|
+
artifact_dir="./inference_cache/semantic"
|
|
255
|
+
)
|
|
256
|
+
print(results["SemanticMetric"]["mIoU"])
|
|
257
|
+
```
|
|
258
|
+
|
|
259
|
+
#### Step 2: Re-evaluation (offline)
|
|
260
|
+
|
|
261
|
+
Because predictions are persisted, metrics can be re-calculated or added without re-running the model.
|
|
262
|
+
|
|
263
|
+
```python
|
|
264
|
+
# Run evaluation on existing artifacts
|
|
265
|
+
results = run_offline_evaluation(
|
|
266
|
+
metrics=[new_metric],
|
|
267
|
+
artifact_dir="./inference_cache/semantic"
|
|
268
|
+
)
|
|
269
|
+
```
|
|
270
|
+
|
|
271
|
+
### CLI tools
|
|
272
|
+
|
|
273
|
+
The library includes a CLI for managing the inference cache.
|
|
274
|
+
|
|
275
|
+
_List stored sequences._
|
|
276
|
+
|
|
277
|
+
```shell
|
|
278
|
+
evaluators memory ls ./inference_cache
|
|
279
|
+
```
|
|
280
|
+
|
|
281
|
+
_Inspect specific tensor shapes._
|
|
282
|
+
|
|
283
|
+
```shell
|
|
284
|
+
evaluators memory inspect ./inference_cache frankfurt_000001
|
|
285
|
+
```
|
|
286
|
+
|
|
287
|
+
_Export to standard PyTorch file._
|
|
288
|
+
|
|
289
|
+
```shell
|
|
290
|
+
evaluators memory export ./inference_cache frankfurt_000001 --out my_video.pt
|
|
291
|
+
```
|
|
292
|
+
|
|
293
|
+
---
|
|
294
|
+
|
|
295
|
+
## Supported metrics
|
|
296
|
+
|
|
297
|
+
### Depth estimation
|
|
298
|
+
|
|
299
|
+
Implements standard error metrics (AbsRel, SqRel, RMSE, RMSElog) and threshold accuracies ($\delta < 1.25^n$).
|
|
300
|
+
|
|
301
|
+
### Segmentation
|
|
302
|
+
|
|
303
|
+
- _Semantic:_ Mean intersection over union (mIoU).
|
|
304
|
+
- _Panoptic:_ Panoptic quality (PQ), segmentation quality (SQ), recognition quality (RQ). Supports "Thing" and "Stuff" splits.
|
|
305
|
+
|
|
306
|
+
### Depth-aware video panoptic segmentation (DVPS)
|
|
307
|
+
|
|
308
|
+
Implements VPQ (video panoptic quality) and its depth-aware extension, DVPQ. Both evaluate spatio-temporal consistency using sliding window tubes over consecutive frames; DVPQ additionally gates matches by pixel-wise depth accuracy against one or more relative-error thresholds.
|
|
309
|
+
|
|
310
|
+
### Tracking
|
|
311
|
+
|
|
312
|
+
Multi-object tracking metrics: HOTA, CLEAR (MOTA/MOTP), and identity F1 (IDF1). Assignment (frame-level detection-to-track matching) is solved by `torchmatch` rather than `scipy.optimize.linear_sum_assignment`; see [OFFLINE_EVALUATION.md](OFFLINE_EVALUATION.md) for the migration note.
|
|
313
|
+
|
|
314
|
+
### STEP
|
|
315
|
+
|
|
316
|
+
Segmentation and tracking quality (STQ), and its depth-aware variant (DSTQ).
|
|
317
|
+
|
|
318
|
+
### Occupancy
|
|
319
|
+
|
|
320
|
+
Per-class IoU/mIoU and occupied-vs-free precision/recall/F1 over voxel-grid occupancy predictions, reusing the same statistics as `SemanticMetric`. `OccupancyMetric` (`evaluators.metrics.domain.occupancy`).
|
|
321
|
+
|
|
322
|
+
### 3D detection
|
|
323
|
+
|
|
324
|
+
Per-class, per-threshold average precision (AP) and mAP over 3D bounding boxes. Matching is a pluggable strategy (`IoU3DSimilarity`, backed by Shapely polygon intersection, or `CenterDistanceSimilarity`, the nuScenes convention), and curve integration is a pluggable rule (COCO's 101-point, KITTI's 40-point, or nuScenes' clipped-recall convention). Optional true-positive error decomposition (mATE, mAOE) and an orientation-aware AOS aggregate. `Box3DAveragePrecisionMetric` (`evaluators.metrics.domain.box3d`).
|
|
325
|
+
|
|
326
|
+
### Vector maps
|
|
327
|
+
|
|
328
|
+
Per-class, per-threshold AP and mAP over polylines (lane/vector-map elements), matched by a pluggable similarity strategy (`ChamferSimilarity` or `FrechetSimilarity`) and integrated by the same curve-integration rules as 3D detection. Optional topology accuracy over the matched polyline graph. `PolylineAveragePrecisionMetric` (`evaluators.metrics.domain.vectormap`).
|
|
329
|
+
|
|
330
|
+
### Sensor simulation
|
|
331
|
+
|
|
332
|
+
Paired reference-vs-simulated quality for images, point clouds, and radar (`SensorImageQualityMetric`, `PointCloudQualityMetric`, `RadarQualityMetric`), plus single-stream self-consistency metrics that need no paired reference (`TemporalConsistencyMetric`, `CrossModalAlignmentMetric`). All five live under `evaluators.metrics.domain.sensorsim`.
|
|
333
|
+
|
|
334
|
+
> As with `SemanticMetric`/`PanopticQualityMetric`, none of the classes above are re-exported from the top-level `evaluators` package; import them from their domain submodule as shown.
|
|
335
|
+
|
|
336
|
+
---
|
|
337
|
+
|
|
338
|
+
## Architecture
|
|
339
|
+
|
|
340
|
+
Reassembling whole sequences out of frames scattered across steps and ranks
|
|
341
|
+
takes three stages, run automatically by `DistributedEvaluationCallback`
|
|
342
|
+
during validation, or manually via the CLI.
|
|
343
|
+
|
|
344
|
+
1. **Write.**
|
|
345
|
+
Each GPU writes predictions to locally sharded files (e.g. `.memmap` or `.lmdb`). No communication occurs.
|
|
346
|
+
2. **Schedule.**
|
|
347
|
+
A synchronization barrier is reached. The main process aggregates metadata manifests from all shards to build a Global Catalog. It partitions the workload (videos, not individual frames) among workers using a greedy strategy to balance duration, so each worker gets complete, ordered sequences.
|
|
348
|
+
3. **Compute.**
|
|
349
|
+
Workers iterate through their assigned Virtual Sequences. Data is streamed from disk, processed by torchmetrics, and reduced globally.
|
|
350
|
+
|
|
351
|
+
See [OFFLINE_EVALUATION.md](OFFLINE_EVALUATION.md) for driving this manually via the CLI (`compute`, plus `results`, `memory`, and `visualize` for reading, inspecting, and rendering stored predictions), and detailed design principles.
|
|
352
|
+
|
|
353
|
+
---
|
|
354
|
+
|
|
355
|
+
## Performance
|
|
356
|
+
|
|
357
|
+
`evaluators` is built for high-throughput I/O. The following benchmarks were conducted using the **Comprehensive Memory Evaluation Suite (CMES)** on a mobile workstation (i7-12700H, NVMe SSD).
|
|
358
|
+
|
|
359
|
+
### Throughput (FPS)
|
|
360
|
+
|
|
361
|
+
| Backend | Codec | Resolution | Write FPS | Read FPS | Compression |
|
|
362
|
+
| :--- | :--- | :--- | :--- | :--- | :--- |
|
|
363
|
+
| **Memmap** | Raw | 512x1024 | 250.8 | 501.6 | 1.0x |
|
|
364
|
+
| **Memmap** | Blosc | 512x1024 | 59.7 | 119.3 | 0.85x |
|
|
365
|
+
| **LMDB** | Raw | 512x1024 | 13.3 | 26.7 | 1.0x |
|
|
366
|
+
| **LMDB** | PNG | 512x1024 | 27.2 | 13.6 | 0.62x |
|
|
367
|
+
| **Filesystem**| TIFF | 512x1024 | 124.9 | 249.7 | 0.89x |
|
|
368
|
+
|
|
369
|
+
### Insights
|
|
370
|
+
|
|
371
|
+
- **Memmap is king for raw throughput:** The `MemmapTemporalWriter` achieves >500 FPS for read operations on mid-resolution video frames, making it ideal for fast metric computation.
|
|
372
|
+
- **Blosc provides the best balance:** Using `BloscCodec` with `Memmap` offers significant storage savings with minimal CPU overhead compared to PNG/TIFF.
|
|
373
|
+
- **LMDB for stability:** While slower for sequential video access, LMDB provides robust ACID compliance and is preferred for random-access metadata or small feature vectors.
|
|
374
|
+
|
|
375
|
+
> Full benchmark reports, including memory usage (Peak RSS) and CPU overhead plots, are available in `docs-assets/benchmarks/` (published at `/benchmarks/` on the docs site).
|
|
376
|
+
|
|
377
|
+
---
|
|
378
|
+
|
|
379
|
+
## Development
|
|
380
|
+
|
|
381
|
+
This project uses modern Python tooling for dependency management and quality assurance.
|
|
382
|
+
|
|
383
|
+
### Setup
|
|
384
|
+
|
|
385
|
+
We use `uv` for fast dependency management.
|
|
386
|
+
|
|
387
|
+
```shell
|
|
388
|
+
# Install dependencies
|
|
389
|
+
uv sync --all-extras
|
|
390
|
+
```
|
|
391
|
+
|
|
392
|
+
### Testing
|
|
393
|
+
|
|
394
|
+
Tests are managed by `pytest`.
|
|
395
|
+
|
|
396
|
+
```shell
|
|
397
|
+
# Run all tests
|
|
398
|
+
uv run pytest
|
|
399
|
+
|
|
400
|
+
# Run with coverage
|
|
401
|
+
uv run pytest --cov=evaluators
|
|
402
|
+
```
|
|
403
|
+
|
|
404
|
+
### Linting & Formatting
|
|
405
|
+
|
|
406
|
+
We use `ruff` for all linting and formatting needs.
|
|
407
|
+
|
|
408
|
+
```shell
|
|
409
|
+
# Check code style
|
|
410
|
+
uv run ruff check .
|
|
411
|
+
|
|
412
|
+
# Format code
|
|
413
|
+
uv run ruff format .
|
|
414
|
+
```
|
|
415
|
+
|
|
416
|
+
---
|
|
417
|
+
|
|
418
|
+
## Contributing
|
|
419
|
+
|
|
420
|
+
Contributions are welcome. Please ensure that:
|
|
421
|
+
|
|
422
|
+
1. New features are covered by tests.
|
|
423
|
+
2. Code passes all static analysis checks (`ruff`).
|
|
424
|
+
3. Architecture changes are discussed in an issue first.
|
|
425
|
+
|
|
426
|
+
## Acknowledgements
|
|
427
|
+
|
|
428
|
+
This work was developed at the Mobile Perception Systems (MPS) lab at Eindhoven University of Technology.
|
|
429
|
+
|
|
430
|
+
## License
|
|
431
|
+
|
|
432
|
+
This project is licensed under the MIT License.
|