euler-eval 2.29.0__tar.gz → 2.30.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.
- {euler_eval-2.29.0/euler_eval.egg-info → euler_eval-2.30.0}/PKG-INFO +1 -1
- {euler_eval-2.29.0 → euler_eval-2.30.0}/docs/README.md +6 -0
- euler_eval-2.30.0/docs/feature-request-unified-evaluation-framework.md +381 -0
- {euler_eval-2.29.0 → euler_eval-2.30.0}/euler_eval/evaluate.py +49 -0
- {euler_eval-2.29.0 → euler_eval-2.30.0/euler_eval.egg-info}/PKG-INFO +1 -1
- {euler_eval-2.29.0 → euler_eval-2.30.0}/euler_eval.egg-info/SOURCES.txt +1 -0
- {euler_eval-2.29.0 → euler_eval-2.30.0}/pyproject.toml +1 -1
- {euler_eval-2.29.0 → euler_eval-2.30.0}/tests/test_depth_alignment_output.py +20 -0
- {euler_eval-2.29.0 → euler_eval-2.30.0}/tests/test_evaluate_helpers.py +28 -0
- {euler_eval-2.29.0 → euler_eval-2.30.0}/LICENSE +0 -0
- {euler_eval-2.29.0 → euler_eval-2.30.0}/MANIFEST.in +0 -0
- {euler_eval-2.29.0 → euler_eval-2.30.0}/README.md +0 -0
- {euler_eval-2.29.0 → euler_eval-2.30.0}/docs/alignment.md +0 -0
- {euler_eval-2.29.0 → euler_eval-2.30.0}/docs/cli.md +0 -0
- {euler_eval-2.29.0 → euler_eval-2.30.0}/docs/configuration.md +0 -0
- {euler_eval-2.29.0 → euler_eval-2.30.0}/docs/distributions.md +0 -0
- {euler_eval-2.29.0 → euler_eval-2.30.0}/docs/metrics.md +0 -0
- {euler_eval-2.29.0 → euler_eval-2.30.0}/docs/output.md +0 -0
- {euler_eval-2.29.0 → euler_eval-2.30.0}/docs/validation.md +0 -0
- {euler_eval-2.29.0 → euler_eval-2.30.0}/euler_eval/THIRD_PARTY_NOTICES.md +0 -0
- {euler_eval-2.29.0 → euler_eval-2.30.0}/euler_eval/__init__.py +0 -0
- {euler_eval-2.29.0 → euler_eval-2.30.0}/euler_eval/calibration.py +0 -0
- {euler_eval-2.29.0 → euler_eval-2.30.0}/euler_eval/cli.py +0 -0
- {euler_eval-2.29.0 → euler_eval-2.30.0}/euler_eval/config_paths.py +0 -0
- {euler_eval-2.29.0 → euler_eval-2.30.0}/euler_eval/data.py +0 -0
- {euler_eval-2.29.0 → euler_eval-2.30.0}/euler_eval/distribution_contract.py +0 -0
- {euler_eval-2.29.0 → euler_eval-2.30.0}/euler_eval/distributions.py +0 -0
- {euler_eval-2.29.0 → euler_eval-2.30.0}/euler_eval/metric_sets.py +0 -0
- {euler_eval-2.29.0 → euler_eval-2.30.0}/euler_eval/metrics/__init__.py +0 -0
- {euler_eval-2.29.0 → euler_eval-2.30.0}/euler_eval/metrics/_natural_scene_models.py +0 -0
- {euler_eval-2.29.0 → euler_eval-2.30.0}/euler_eval/metrics/absrel.py +0 -0
- {euler_eval-2.29.0 → euler_eval-2.30.0}/euler_eval/metrics/daniel_error.py +0 -0
- {euler_eval-2.29.0 → euler_eval-2.30.0}/euler_eval/metrics/depth_binned_error.py +0 -0
- {euler_eval-2.29.0 → euler_eval-2.30.0}/euler_eval/metrics/depth_edge_f1.py +0 -0
- {euler_eval-2.29.0 → euler_eval-2.30.0}/euler_eval/metrics/depth_standard.py +0 -0
- {euler_eval-2.29.0 → euler_eval-2.30.0}/euler_eval/metrics/fade.py +0 -0
- {euler_eval-2.29.0 → euler_eval-2.30.0}/euler_eval/metrics/fid_kid.py +0 -0
- {euler_eval-2.29.0 → euler_eval-2.30.0}/euler_eval/metrics/gpu_depth_batch.py +0 -0
- {euler_eval-2.29.0 → euler_eval-2.30.0}/euler_eval/metrics/gpu_image_batch.py +0 -0
- {euler_eval-2.29.0 → euler_eval-2.30.0}/euler_eval/metrics/high_freq_energy.py +0 -0
- {euler_eval-2.29.0 → euler_eval-2.30.0}/euler_eval/metrics/lpips_metric.py +0 -0
- {euler_eval-2.29.0 → euler_eval-2.30.0}/euler_eval/metrics/niqe.py +0 -0
- {euler_eval-2.29.0 → euler_eval-2.30.0}/euler_eval/metrics/normal_consistency.py +0 -0
- {euler_eval-2.29.0 → euler_eval-2.30.0}/euler_eval/metrics/points3d_cloud.py +0 -0
- {euler_eval-2.29.0 → euler_eval-2.30.0}/euler_eval/metrics/points3d_decomposition.py +0 -0
- {euler_eval-2.29.0 → euler_eval-2.30.0}/euler_eval/metrics/points3d_distance.py +0 -0
- {euler_eval-2.29.0 → euler_eval-2.30.0}/euler_eval/metrics/points3d_geometry.py +0 -0
- {euler_eval-2.29.0 → euler_eval-2.30.0}/euler_eval/metrics/psnr.py +0 -0
- {euler_eval-2.29.0 → euler_eval-2.30.0}/euler_eval/metrics/rgb_edge_f1.py +0 -0
- {euler_eval-2.29.0 → euler_eval-2.30.0}/euler_eval/metrics/rgb_lpips.py +0 -0
- {euler_eval-2.29.0 → euler_eval-2.30.0}/euler_eval/metrics/rgb_psnr_ssim.py +0 -0
- {euler_eval-2.29.0 → euler_eval-2.30.0}/euler_eval/metrics/rho_a.py +0 -0
- {euler_eval-2.29.0 → euler_eval-2.30.0}/euler_eval/metrics/rmse.py +0 -0
- {euler_eval-2.29.0 → euler_eval-2.30.0}/euler_eval/metrics/scale_invariant_log.py +0 -0
- {euler_eval-2.29.0 → euler_eval-2.30.0}/euler_eval/metrics/ssim.py +0 -0
- {euler_eval-2.29.0 → euler_eval-2.30.0}/euler_eval/metrics/tail_errors.py +0 -0
- {euler_eval-2.29.0 → euler_eval-2.30.0}/euler_eval/metrics/utils.py +0 -0
- {euler_eval-2.29.0 → euler_eval-2.30.0}/euler_eval/sanity_checker.py +0 -0
- {euler_eval-2.29.0 → euler_eval-2.30.0}/euler_eval/schemas/distribution-v1.schema.json +0 -0
- {euler_eval-2.29.0 → euler_eval-2.30.0}/euler_eval/utils/hierarchy_parser.py +0 -0
- {euler_eval-2.29.0 → euler_eval-2.30.0}/euler_eval/validation.py +0 -0
- {euler_eval-2.29.0 → euler_eval-2.30.0}/euler_eval.egg-info/dependency_links.txt +0 -0
- {euler_eval-2.29.0 → euler_eval-2.30.0}/euler_eval.egg-info/entry_points.txt +0 -0
- {euler_eval-2.29.0 → euler_eval-2.30.0}/euler_eval.egg-info/requires.txt +0 -0
- {euler_eval-2.29.0 → euler_eval-2.30.0}/euler_eval.egg-info/top_level.txt +0 -0
- {euler_eval-2.29.0 → euler_eval-2.30.0}/init_cache.py +0 -0
- {euler_eval-2.29.0 → euler_eval-2.30.0}/scripts/generate_distribution_fixtures.py +0 -0
- {euler_eval-2.29.0 → euler_eval-2.30.0}/setup.cfg +0 -0
- {euler_eval-2.29.0 → euler_eval-2.30.0}/tests/__init__.py +0 -0
- {euler_eval-2.29.0 → euler_eval-2.30.0}/tests/conftest.py +0 -0
- {euler_eval-2.29.0 → euler_eval-2.30.0}/tests/distribution_contract_utils.py +0 -0
- {euler_eval-2.29.0 → euler_eval-2.30.0}/tests/fixtures/distributions/README.md +0 -0
- {euler_eval-2.29.0 → euler_eval-2.30.0}/tests/fixtures/distributions/counts-depth.eval.json +0 -0
- {euler_eval-2.29.0 → euler_eval-2.30.0}/tests/fixtures/distributions/counts-points3d.eval.json +0 -0
- {euler_eval-2.29.0 → euler_eval-2.30.0}/tests/fixtures/distributions/counts-sparsedepth.eval.json +0 -0
- {euler_eval-2.29.0 → euler_eval-2.30.0}/tests/fixtures/distributions/future-sum-by-depth.eval.json +0 -0
- {euler_eval-2.29.0 → euler_eval-2.30.0}/tests/fixtures/distributions/histogram-only.eval.json +0 -0
- {euler_eval-2.29.0 → euler_eval-2.30.0}/tests/fixtures/distributions/per-file-only.eval.json +0 -0
- {euler_eval-2.29.0 → euler_eval-2.30.0}/tests/fixtures/distributions/scalar-only.eval.json +0 -0
- {euler_eval-2.29.0 → euler_eval-2.30.0}/tests/mock_files/calibration_output.json +0 -0
- {euler_eval-2.29.0 → euler_eval-2.30.0}/tests/mock_files/depth_output.json +0 -0
- {euler_eval-2.29.0 → euler_eval-2.30.0}/tests/mock_files/rgb_output.json +0 -0
- {euler_eval-2.29.0 → euler_eval-2.30.0}/tests/mock_files/segmentation_output.json +0 -0
- {euler_eval-2.29.0 → euler_eval-2.30.0}/tests/test_alignment.py +0 -0
- {euler_eval-2.29.0 → euler_eval-2.30.0}/tests/test_cli_device.py +0 -0
- {euler_eval-2.29.0 → euler_eval-2.30.0}/tests/test_cli_sparse_outputs.py +0 -0
- {euler_eval-2.29.0 → euler_eval-2.30.0}/tests/test_config.py +0 -0
- {euler_eval-2.29.0 → euler_eval-2.30.0}/tests/test_data.py +0 -0
- {euler_eval-2.29.0 → euler_eval-2.30.0}/tests/test_data_builders.py +0 -0
- {euler_eval-2.29.0 → euler_eval-2.30.0}/tests/test_depth_standard.py +0 -0
- {euler_eval-2.29.0 → euler_eval-2.30.0}/tests/test_distribution_contract.py +0 -0
- {euler_eval-2.29.0 → euler_eval-2.30.0}/tests/test_distributions.py +0 -0
- {euler_eval-2.29.0 → euler_eval-2.30.0}/tests/test_domain_metrics.py +0 -0
- {euler_eval-2.29.0 → euler_eval-2.30.0}/tests/test_init_cache.py +0 -0
- {euler_eval-2.29.0 → euler_eval-2.30.0}/tests/test_integration.py +0 -0
- {euler_eval-2.29.0 → euler_eval-2.30.0}/tests/test_meta_output.py +0 -0
- {euler_eval-2.29.0 → euler_eval-2.30.0}/tests/test_metrics_utils.py +0 -0
- {euler_eval-2.29.0 → euler_eval-2.30.0}/tests/test_normal_consistency.py +0 -0
- {euler_eval-2.29.0 → euler_eval-2.30.0}/tests/test_points_3d.py +0 -0
- {euler_eval-2.29.0 → euler_eval-2.30.0}/tests/test_points_3d_sparse.py +0 -0
- {euler_eval-2.29.0 → euler_eval-2.30.0}/tests/test_rgb_fid_output.py +0 -0
- {euler_eval-2.29.0 → euler_eval-2.30.0}/tests/test_rho_a.py +0 -0
- {euler_eval-2.29.0 → euler_eval-2.30.0}/tests/test_save_results.py +0 -0
- {euler_eval-2.29.0 → euler_eval-2.30.0}/tests/test_sparse_depth.py +0 -0
- {euler_eval-2.29.0 → euler_eval-2.30.0}/tests/test_validation.py +0 -0
|
@@ -10,6 +10,12 @@
|
|
|
10
10
|
| [Results & output](output.md) | Where files land, the anatomy of `eval.json`, per-file metrics, the sanity-check report |
|
|
11
11
|
| [In-training validation](validation.md) | The programmatic API for scoring in-memory predictions during training |
|
|
12
12
|
|
|
13
|
+
## Proposed features
|
|
14
|
+
|
|
15
|
+
- [Unified evaluation framework](feature-request-unified-evaluation-framework.md):
|
|
16
|
+
a shared evaluation lifecycle with explicit modality-specific preparation,
|
|
17
|
+
metric dependencies, populations, aggregation and output compatibility.
|
|
18
|
+
|
|
13
19
|
## Concepts in one page
|
|
14
20
|
|
|
15
21
|
**A dataset is an indexed root, not a folder of files.** Every path in the
|
|
@@ -0,0 +1,381 @@
|
|
|
1
|
+
# Feature request: a unified evaluation framework
|
|
2
|
+
|
|
3
|
+
Status: proposed for a future release. This document specifies an architectural
|
|
4
|
+
change; it does not introduce a new API or change evaluation behavior.
|
|
5
|
+
|
|
6
|
+
## Recommendation
|
|
7
|
+
|
|
8
|
+
Use a **modality-agnostic evaluation lifecycle with modality-aware preparation,
|
|
9
|
+
observations and metric definitions**. Represent each evaluation as an explicit
|
|
10
|
+
plan of typed steps and dependencies. The same plan should drive computation,
|
|
11
|
+
aggregation, explanations and output metadata.
|
|
12
|
+
|
|
13
|
+
Depth, RGB, rays and point clouds can share this lifecycle without sharing one
|
|
14
|
+
preprocessing function, validity mask or mathematical reduction. A metric may
|
|
15
|
+
consume several modalities, a whole image, two point sets, a group of frames or
|
|
16
|
+
dataset-wide features. Those requirements belong in its definition.
|
|
17
|
+
|
|
18
|
+
Start with ordinary Python data classes, protocols and a small dependency
|
|
19
|
+
executor. Keep the current CLI and library entry points as compatibility
|
|
20
|
+
wrappers while moving their computation into the shared framework.
|
|
21
|
+
|
|
22
|
+
## Problem and motivation
|
|
23
|
+
|
|
24
|
+
Today, adding a core metric requires wiring computation and storage in
|
|
25
|
+
[`evaluate.py`](../euler_eval/evaluate.py), descriptions and paths in
|
|
26
|
+
[`cli.py`](../euler_eval/cli.py), and sometimes separate per-file and dataset
|
|
27
|
+
serialization logic. Deferred CPU/GPU batch results also patch mutable output
|
|
28
|
+
dictionaries. The distribution contract is attached through a later tree walk
|
|
29
|
+
in [`distribution_contract.py`](../euler_eval/distribution_contract.py).
|
|
30
|
+
|
|
31
|
+
This makes several questions difficult to answer from one place:
|
|
32
|
+
|
|
33
|
+
- Which transformed prediction and GT does a metric consume?
|
|
34
|
+
- Which observations are included, and when is that selection applied?
|
|
35
|
+
- Is a dataset result a mean of image scores, a pooled observation statistic,
|
|
36
|
+
or a computation over the dataset as a whole?
|
|
37
|
+
- Do a scalar and its associated distribution describe the same population?
|
|
38
|
+
- When is a deferred result complete, and which accumulators receive it?
|
|
39
|
+
|
|
40
|
+
The recent depth benchmark-range fix is a concrete example: the default
|
|
41
|
+
distribution needed its selection explicitly connected to the benchmark
|
|
42
|
+
population after sky-depth processing. Shared population definitions should
|
|
43
|
+
make that relationship part of the plan.
|
|
44
|
+
|
|
45
|
+
As a metric author, I should be able to declare inputs, computation, supported
|
|
46
|
+
reductions and result semantics once. As a user, I should be able to inspect
|
|
47
|
+
how any emitted result was produced. As a consumer, euler-view should receive
|
|
48
|
+
stable output with enough provenance to distinguish incompatible evaluations.
|
|
49
|
+
|
|
50
|
+
## Unified flow
|
|
51
|
+
|
|
52
|
+
```mermaid
|
|
53
|
+
flowchart TD
|
|
54
|
+
C[Configuration and selected metric sets] --> P[Build and validate evaluation plan]
|
|
55
|
+
M[Input metadata and available capabilities] --> P
|
|
56
|
+
P --> S[Resolve required inputs and sample or group identities]
|
|
57
|
+
S --> A[Prepare typed artifacts through declared adapter steps]
|
|
58
|
+
A --> V[Resolve views, populations and correspondences]
|
|
59
|
+
V --> K[Compute declared observations, scores or features]
|
|
60
|
+
K --> I[Completed item contributions]
|
|
61
|
+
I --> R[Reducers or retained dataset inputs]
|
|
62
|
+
R --> F[Finalize dataset or group computations]
|
|
63
|
+
K --> N[Optional dependent stages]
|
|
64
|
+
F --> N
|
|
65
|
+
N --> O[Typed result collection]
|
|
66
|
+
I --> O
|
|
67
|
+
F --> O
|
|
68
|
+
O --> E[Compatibility output adapter]
|
|
69
|
+
P -.-> X[Plan explanation and execution provenance]
|
|
70
|
+
O -.-> X
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
This is a lifecycle overview. An individual plan is a directed acyclic graph:
|
|
74
|
+
preparation may itself require correspondence construction before alignment,
|
|
75
|
+
and later metrics may consume artifacts or results from earlier stages. The
|
|
76
|
+
diagram does not mandate that every metric executes every box.
|
|
77
|
+
|
|
78
|
+
For example, RGB plus GT depth can produce depth-binned photometric scores;
|
|
79
|
+
depth plus calibration can produce a point map; a future reconstruction stage
|
|
80
|
+
can consume several frames and produce a cloud for geometric evaluation.
|
|
81
|
+
Each dependency must be explicit, including its coordinate system and scope.
|
|
82
|
+
|
|
83
|
+
## Responsibilities and contracts
|
|
84
|
+
|
|
85
|
+
The initial implementation should formalize these concepts. Names below are
|
|
86
|
+
design suggestions, not a promised public Python API.
|
|
87
|
+
|
|
88
|
+
| Concept | Required responsibility |
|
|
89
|
+
|---|---|
|
|
90
|
+
| `EvaluationPlan` and step definitions | Requested outputs, typed dependencies, execution scope, effective policies and implementation versions. Validate unsupported combinations and cycles before expensive work. |
|
|
91
|
+
| Artifact specification and reference | Payload kind, GT/prediction role, source identity, units, coordinate frame, spatial layout, transformation lineage and ownership. A reference may point to memory or managed storage. |
|
|
92
|
+
| View and population definitions | Evaluated representation, eligibility predicates, selection order, correspondence policy and observation unit. Record distinct source and target populations where needed. |
|
|
93
|
+
| `MetricSpec` | Required artifacts or observations, computation, validity requirements, parameters, supported scopes/reducers and semantic descriptions. Metric sets select these definitions. |
|
|
94
|
+
| `ReducerSpec` | Accepted contribution type, state, update/finalize operations, merge capability, weighting, precision and retention requirements. |
|
|
95
|
+
| Contribution and result records | Stable request/item identity, payload, status, counts and provenance references. Results are completed values; pending work belongs to execution state. |
|
|
96
|
+
|
|
97
|
+
The shared core owns planning, dependency resolution, resource lifetime,
|
|
98
|
+
batch completion, aggregation and result collection. It dispatches by declared
|
|
99
|
+
capabilities and contracts. Modality-specific adapters and metric kernels own
|
|
100
|
+
geometry, image preprocessing, feature extraction and domain-specific math.
|
|
101
|
+
|
|
102
|
+
Metric definitions should supply descriptions, units and allowed reductions to
|
|
103
|
+
the exporter. Adding a metric using existing capabilities should not require
|
|
104
|
+
new modality conditionals in the executor or manual CLI output-tree assembly.
|
|
105
|
+
|
|
106
|
+
### Inputs and execution scope
|
|
107
|
+
|
|
108
|
+
Treat the required inputs as named roles, rather than assuming every operation
|
|
109
|
+
takes a `(prediction, GT)` array pair. Support declarations for:
|
|
110
|
+
|
|
111
|
+
- One item: paired images, predicted image alone, ray maps, or two point sets.
|
|
112
|
+
- A group: a scene, sequence, temporal window or another explicitly identified
|
|
113
|
+
collection, including any ordering requirement.
|
|
114
|
+
- A dataset: feature collections, accumulated statistics or other retained
|
|
115
|
+
inputs needed after the item stream completes.
|
|
116
|
+
|
|
117
|
+
An execution batch is a performance choice, not a statistical grouping. A
|
|
118
|
+
batch size must not silently define a sample, population or dataset reducer.
|
|
119
|
+
|
|
120
|
+
Each request identifies its matching/join policy and required modalities. A
|
|
121
|
+
missing optional modality should affect the requests that need it; it should
|
|
122
|
+
not silently shrink unrelated evaluation populations. Preserve existing
|
|
123
|
+
matching behavior during migration, and make any change to that behavior an
|
|
124
|
+
explicit, separately tested semantic change.
|
|
125
|
+
|
|
126
|
+
NIQE/FADE kernels require predictions only, although today's RGB evaluation
|
|
127
|
+
workflow pairs GT and predictions. Express that kernel requirement now;
|
|
128
|
+
enabling a standalone prediction-only CLI workflow can be a separate feature.
|
|
129
|
+
|
|
130
|
+
### Artifacts, views and populations
|
|
131
|
+
|
|
132
|
+
An artifact must carry enough information to reject invalid combinations:
|
|
133
|
+
metres versus unspecified relative values, incompatible coordinate frames,
|
|
134
|
+
unaligned image grids, or sparse samples supplied to dense-neighbourhood
|
|
135
|
+
metrics. Calibration assumptions and inferred input spaces must be recorded.
|
|
136
|
+
|
|
137
|
+
A population is a named selection over a declared observation domain. It need
|
|
138
|
+
not be a dense Boolean mask: it may be projected correspondences, valid
|
|
139
|
+
directions, selected images, or a directed set of nearest-neighbour queries.
|
|
140
|
+
Two populations with the same display name are not automatically compatible.
|
|
141
|
+
|
|
142
|
+
Separate fitting and scoring populations. For depth, the current intended
|
|
143
|
+
order must remain explicit:
|
|
144
|
+
|
|
145
|
+
1. Spatial preparation, calibration handling and planar/radial conversion.
|
|
146
|
+
2. Alignment fitting on its declared fitting population, where requested.
|
|
147
|
+
3. GT/prediction sky-depth capping and predicted-sky filling under the existing
|
|
148
|
+
policy, with GT sky exclusion recorded separately.
|
|
149
|
+
4. Scoring validity and benchmark membership on evaluated GT. The benchmark
|
|
150
|
+
`all` interval includes both endpoints.
|
|
151
|
+
5. Scalar and distribution computations for the selected space/population.
|
|
152
|
+
|
|
153
|
+
An unrestricted scalar and a benchmark-filtered histogram may intentionally
|
|
154
|
+
coexist, as they currently do. Their internal identities must expose that
|
|
155
|
+
difference. Scalar and distribution requests for the *same* population must
|
|
156
|
+
consume the same selected observations.
|
|
157
|
+
|
|
158
|
+
Additional requirements such as positive inputs for logarithms or a valid
|
|
159
|
+
neighbourhood for normals belong to the relevant computation. Explicitly name
|
|
160
|
+
the resulting population or support policy. Likewise, RGB sky filling and
|
|
161
|
+
RGB pixel exclusion are different operations; the framework must preserve
|
|
162
|
+
that distinction and the current unfilled inputs used for NIQE/FADE.
|
|
163
|
+
|
|
164
|
+
## Mapping current modalities into the framework
|
|
165
|
+
|
|
166
|
+
| Evaluation family | Preparation and dependencies | Computation and scope |
|
|
167
|
+
|---|---|---|
|
|
168
|
+
| Dense depth | Spatial alignment, intrinsics, radial/planar conversion, optional affine fitting, sky policy and benchmark populations | Corresponding-pixel errors and standard reducers; dense geometry; image scores; dataset feature metrics |
|
|
169
|
+
| Sparse depth | Point-cloud transforms, projection, visibility/collision policy, depth preparation and alignment | Errors at evaluated projected correspondences; dense-neighbourhood metrics remain unavailable |
|
|
170
|
+
| RGB | Image layout/range handling, spatial alignment and metric-specific sky policy | Pixel errors, local/image scores, prediction-only quality kernels and dataset FID |
|
|
171
|
+
| RGB with depth | RGB inputs plus explicitly registered GT depth and calibration | Photometric observations selected by GT depth; demonstrates an existing metric with multiple modalities |
|
|
172
|
+
| Rays | Direction preparation, spatial alignment, validity and resolved FoV policy | Angular observations, thresholds and accuracy curves; declared exact/approximate summaries |
|
|
173
|
+
| Dense points-3D | Point maps or unprojected depth, coordinate-frame handling and optional gauge fitting | Correspondence errors, radial/lateral decomposition, dense geometry and separate cloud comparisons |
|
|
174
|
+
| Sparse points-3D | Sparse cloud visibility and projection plus predicted points or the existing programmatic depth conversion | Correspondence errors and cloud queries with explicitly declared directions |
|
|
175
|
+
|
|
176
|
+
Preserve current entry-point eligibility: representing an existing library
|
|
177
|
+
conversion in the plan does not automatically enable an additional CLI result
|
|
178
|
+
family. For point-map metrics, also preserve computations intentionally made in
|
|
179
|
+
the native frame even when aligned results are available.
|
|
180
|
+
|
|
181
|
+
Correspondence error and cloud distance require different observations.
|
|
182
|
+
Nearest-neighbour cloud queries must declare their source, target, direction
|
|
183
|
+
and subsampling policy. Current sparse fixed-threshold diagnostics are
|
|
184
|
+
GT-to-prediction completeness/recall, while optional F-score AUC also needs the
|
|
185
|
+
reverse direction. Keep those requirements separate. Do not pool points from
|
|
186
|
+
unrelated frames before nearest-neighbour matching; a scene-level comparison
|
|
187
|
+
would require an explicit scene artifact and common coordinate frame.
|
|
188
|
+
|
|
189
|
+
The ray evaluator already uses a histogram approximation for its pooled
|
|
190
|
+
median. Migrating it must preserve and disclose that approximation instead of
|
|
191
|
+
implicitly promising an exact quantile.
|
|
192
|
+
|
|
193
|
+
## Observations and aggregation
|
|
194
|
+
|
|
195
|
+
Share computations only when their full input, view, population and numerical
|
|
196
|
+
policies match. Reuse references or chunks where practical; the framework
|
|
197
|
+
must not require retaining all pixel errors in memory.
|
|
198
|
+
|
|
199
|
+
A scalar, histogram and curve are different outputs derived from declared
|
|
200
|
+
observations. A depth histogram associated with `rmse` bins absolute residuals;
|
|
201
|
+
the points-3D counterpart bins Euclidean correspondence errors. Neither is a
|
|
202
|
+
distribution of repeated estimates of dataset RMSE.
|
|
203
|
+
|
|
204
|
+
| Requested result | Contribution/state | Final computation |
|
|
205
|
+
|---|---|---|
|
|
206
|
+
| Pixel-pool depth RMSE | Squared-error sum and valid count | `sqrt(sum_sq / count)` |
|
|
207
|
+
| Mean per-image RMSE | Valid image RMSE sum and image count | Divide; preserve equal image weighting |
|
|
208
|
+
| Error-magnitude histogram | Fixed-bin counts | Elementwise sum |
|
|
209
|
+
| Error sum by GT-depth bin | Fixed-bin error sums | Elementwise sum |
|
|
210
|
+
| RMSE by GT-depth bin | Squared-error sums and counts for matching bins | Square root of each bin's mean squared error |
|
|
211
|
+
| Exact quantile | Retained values or another exact supported state | Quantile over the declared population |
|
|
212
|
+
| LPIPS dataset mean | Completed per-image scores and validity | Declared image-score reducer |
|
|
213
|
+
| FID | Compatible GT/prediction feature collections, or sufficient feature moments if supported by that backend | One dataset-level calculation after ingestion |
|
|
214
|
+
| KID | Feature collections plus declared subset selection/seed policy | Dataset-level estimator with its required retained state |
|
|
215
|
+
|
|
216
|
+
All reducers have `update` and `finalize`; only reducers declaring a compatible
|
|
217
|
+
merge operation may combine partial states. State may be sufficient statistics,
|
|
218
|
+
retained values or ordered/global inputs. Do not force all reducers into sums
|
|
219
|
+
or average finalized shard scores. In particular, per-shard FID values cannot
|
|
220
|
+
be averaged to obtain dataset FID. Backend-specific feature preprocessing,
|
|
221
|
+
model weights and covariance conventions are part of FID compatibility.
|
|
222
|
+
|
|
223
|
+
Define merge compatibility from the complete resolved semantics: observation,
|
|
224
|
+
units, view, population policy, reducer, bin edges/intervals, implementation
|
|
225
|
+
and estimator settings. Registry IDs and matching output paths alone are
|
|
226
|
+
insufficient. Check partition identity separately so overlapping inputs are
|
|
227
|
+
not double counted. Integer counts must remain exact; floating accumulation
|
|
228
|
+
and backend equivalence require declared tolerances.
|
|
229
|
+
|
|
230
|
+
For matching populations and fixed bins, dataset histogram counts must equal
|
|
231
|
+
the sum of item contributions. Benchmark subsets and canonical aliases must
|
|
232
|
+
not be accumulated again as additional observations. Exact RMSE continues to
|
|
233
|
+
use sufficient statistics, independently of histogram resolution.
|
|
234
|
+
|
|
235
|
+
## Execution and later stages
|
|
236
|
+
|
|
237
|
+
Compile selected requests into explicit dependencies. Resolve metadata-based
|
|
238
|
+
choices early; leave data-dependent choices marked unresolved until the
|
|
239
|
+
required input is available, then record the decision and its policy. Reject
|
|
240
|
+
missing required capabilities and incompatible artifacts with useful messages.
|
|
241
|
+
|
|
242
|
+
CPU kernels, GPU batches and dataset computations should emit typed completed
|
|
243
|
+
contributions into the same coordinator. Completion is tracked per logical
|
|
244
|
+
request and item/group; each contribution reaches its reducer once. A failed
|
|
245
|
+
or retried batch must not duplicate counts. Exported dictionaries must not be
|
|
246
|
+
used as pending-result storage.
|
|
247
|
+
|
|
248
|
+
Define `ok`, `no_observations`, `skipped`, `unavailable` and `failed` outcomes
|
|
249
|
+
with reasons and relevant counts. Empty or failed measurements must not enter
|
|
250
|
+
means as zero. The plan declares whether a failure stops the run or leaves a
|
|
251
|
+
result unavailable, preserving existing failure behavior during migration.
|
|
252
|
+
|
|
253
|
+
Use bounded queues and explicit artifact ownership. Release intermediates
|
|
254
|
+
after their consumers finish; declare temporary-file/feature retention and
|
|
255
|
+
clean it up on completion or failure. Expensive optional metrics should only
|
|
256
|
+
load models and compute features when selected. Shared preparation should not
|
|
257
|
+
force an entire dataset or all modalities into memory.
|
|
258
|
+
|
|
259
|
+
Allow a later stage to consume an earlier artifact or typed result through a
|
|
260
|
+
declared dependency. Examples include frame fusion followed by cloud scoring,
|
|
261
|
+
temporal metrics over ordered frame groups, and summaries derived from
|
|
262
|
+
completed dataset results. Specify group membership, ordering, frame transforms
|
|
263
|
+
and missing-input behavior. These examples are extension requirements, not
|
|
264
|
+
requests to implement reconstruction or temporal metrics in this refactor.
|
|
265
|
+
|
|
266
|
+
Initially, a local dependency executor is sufficient. A distributed scheduler,
|
|
267
|
+
plugin marketplace and user-authored workflow language are outside this
|
|
268
|
+
feature's scope. Merge contracts prepare suitable computations for later
|
|
269
|
+
parallel execution without making distribution a prerequisite.
|
|
270
|
+
|
|
271
|
+
## Transparency and output compatibility
|
|
272
|
+
|
|
273
|
+
Provide a proposed `--explain` mode and a programmatic plan description. They
|
|
274
|
+
should identify inputs, transforms, fitting/scoring populations, computation
|
|
275
|
+
dependencies, scope, reducer, approximation and requested output paths.
|
|
276
|
+
An explanation that does not evaluate data must mark inferred settings as
|
|
277
|
+
pending. Runtime provenance records resolved choices and contribution counts;
|
|
278
|
+
it should be possible to follow an output back to its plan definition without
|
|
279
|
+
dumping arrays or masks.
|
|
280
|
+
|
|
281
|
+
Use a structured internal result identity containing evaluation family, view,
|
|
282
|
+
population, measurement, reducer, scope/group and output kind, referencing the
|
|
283
|
+
resolved semantic definitions. A path is an export representation of that
|
|
284
|
+
identity. Canonical aliases reference existing results and perform no work.
|
|
285
|
+
|
|
286
|
+
Keep the current `eval.json` envelope, per-modality files, metric paths, aliases,
|
|
287
|
+
library return shapes, and distribution v1 leaves/registry during migration.
|
|
288
|
+
One serialization layer should translate typed results to those contracts.
|
|
289
|
+
Generate descriptions from metric definitions rather than rediscovering
|
|
290
|
+
semantics through dictionary names. Keep transport validation, including
|
|
291
|
+
JavaScript-safe integer counts and nonfinite-value handling, at that boundary.
|
|
292
|
+
|
|
293
|
+
Preserve existing `distributionPopulation` metadata. A later, separately
|
|
294
|
+
versioned provenance extension can describe per-result views and populations
|
|
295
|
+
for all output kinds, including scalar results. Distinguish evaluation
|
|
296
|
+
semantics versions from wire-schema versions: an internal refactor must not
|
|
297
|
+
silently change metric meaning, and a new meaning does not justify pretending
|
|
298
|
+
older results used the new policy.
|
|
299
|
+
|
|
300
|
+
euler-view should be able to ingest compatibility output without changes.
|
|
301
|
+
Future provenance can support richer explanations and comparison checks, but
|
|
302
|
+
this feature request contains no euler-view implementation work.
|
|
303
|
+
|
|
304
|
+
## Delivery plan
|
|
305
|
+
|
|
306
|
+
1. **Characterize existing behavior.** Inventory current metric definitions,
|
|
307
|
+
requirements, populations, reduction rules, aliases and exceptions. Capture
|
|
308
|
+
representative existing outputs and numerical behavior, including known
|
|
309
|
+
policy differences between CLI and training validation.
|
|
310
|
+
2. **Introduce the contracts through depth.** Extract prepared observations,
|
|
311
|
+
explicit populations and standard/distribution reducers. Reuse the
|
|
312
|
+
sufficient-statistic foundation in
|
|
313
|
+
[`depth_standard.py`](../euler_eval/metrics/depth_standard.py) and the typed
|
|
314
|
+
sample/aggregator approach in [`validation.py`](../euler_eval/validation.py).
|
|
315
|
+
Move dense and sparse depth plus training validation onto the same core
|
|
316
|
+
where semantics match. Keep heavyweight optional metrics outside the
|
|
317
|
+
lightweight validation API.
|
|
318
|
+
3. **Validate the design against different computations.** Migrate RGB image
|
|
319
|
+
and prediction-only kernels, LPIPS completion, dataset FID/KID paths where
|
|
320
|
+
supported, and RGB-with-depth metrics. This milestone must demonstrate
|
|
321
|
+
image, dataset and multiple-modality dependencies before treating the
|
|
322
|
+
abstraction as stable.
|
|
323
|
+
4. **Migrate rays and points.** Preserve frame choices, directional cloud
|
|
324
|
+
comparisons, sparse eligibility and approximation policies. Share declared
|
|
325
|
+
preparation where compatible and retain specialized kernels.
|
|
326
|
+
5. **Complete explanations and remove duplicate wiring.** Generate result
|
|
327
|
+
descriptions and compatibility output from definitions. Document how to
|
|
328
|
+
add a metric, adapter and reducer. Stabilize extension contracts only after
|
|
329
|
+
the current modalities exercise them.
|
|
330
|
+
|
|
331
|
+
Each milestone must leave the existing entry points usable. Numerical or
|
|
332
|
+
selection-policy corrections discovered during migration should be reviewed,
|
|
333
|
+
tested and release-noted separately from structural changes.
|
|
334
|
+
|
|
335
|
+
## Acceptance criteria
|
|
336
|
+
|
|
337
|
+
- All current evaluation families use the same planning, contribution and
|
|
338
|
+
result lifecycle while preserving modality-specific preparation.
|
|
339
|
+
- A metric using existing artifact and reducer types can be added through its
|
|
340
|
+
kernel and declaration without editing the executor or CLI tree-building
|
|
341
|
+
logic. Its descriptions and supported output forms come from that definition.
|
|
342
|
+
- Plans cover a pixel metric, an image metric, a prediction-only kernel, a
|
|
343
|
+
dataset feature metric and a metric requiring RGB plus depth. A small test
|
|
344
|
+
stage can consume an upstream artifact/result without adding a scheduler
|
|
345
|
+
branch for that modality.
|
|
346
|
+
- Depth parity cases cover dense/sparse inputs, native/aligned spaces,
|
|
347
|
+
planar/radial conversion, benchmark boundaries, sky capping/filling,
|
|
348
|
+
excluded/invalid inputs and empty populations. Fitting and scoring
|
|
349
|
+
populations are independently inspectable.
|
|
350
|
+
- Scalars and distributions requesting the same population agree on selected
|
|
351
|
+
observations. Fixed-bin dataset counts equal summed item counts, and
|
|
352
|
+
aliases/subset summaries do not double count.
|
|
353
|
+
- Reducer tests distinguish pooled RMSE from mean image RMSE, verify compatible
|
|
354
|
+
partition merging, reject incompatible state merges and preserve exact or
|
|
355
|
+
approximate quantile policies. Dataset metrics are finalized at their
|
|
356
|
+
declared scope instead of averaging arbitrary batch/shard scores.
|
|
357
|
+
- Cloud tests preserve coordinate frames, correspondence/nearest-neighbour
|
|
358
|
+
distinctions, sparse directions and subsampling. RGB tests preserve filled
|
|
359
|
+
versus unfilled inputs and dependencies on optional depth.
|
|
360
|
+
- CPU and batched implementations agree within metric-specific tolerances;
|
|
361
|
+
integer histograms agree exactly. Completion-order tests detect lost or
|
|
362
|
+
duplicated contributions, including failures and empty results. Stochastic
|
|
363
|
+
estimator checks use declared seed/subset policies.
|
|
364
|
+
- Public CLI/library compatibility fixtures retain result paths, aliases,
|
|
365
|
+
values within the existing tolerances, metadata and distribution v1 schema
|
|
366
|
+
validity. Changes in eligibility, weights or semantics are explicit changes.
|
|
367
|
+
- Explanation output identifies effective policies and unresolved choices;
|
|
368
|
+
result provenance includes resolved dependencies, counts and outcome reasons.
|
|
369
|
+
- Streaming checks demonstrate bounded intermediate memory for streaming
|
|
370
|
+
reducers and cleanup of retained resources. Existing tests remain usable
|
|
371
|
+
without datasets, network access or a GPU; backend stubs can exercise
|
|
372
|
+
scheduling and feature contracts.
|
|
373
|
+
|
|
374
|
+
## Design boundary
|
|
375
|
+
|
|
376
|
+
The framework should make the computation inspectable and extensible through
|
|
377
|
+
shared contracts. It should preserve specialized mathematical kernels and
|
|
378
|
+
make their differences visible. Its success is measured by fewer independent
|
|
379
|
+
places to wire a metric, demonstrable scalar/distribution population agreement,
|
|
380
|
+
and the ability to express the current non-pixel and dataset computations
|
|
381
|
+
within the same lifecycle.
|
|
@@ -547,6 +547,35 @@ def _get_sky_mask(sample: dict) -> Optional[np.ndarray]:
|
|
|
547
547
|
return ~sky # invert: True = non-sky = valid
|
|
548
548
|
|
|
549
549
|
|
|
550
|
+
class _SkyMaskCoverage:
|
|
551
|
+
"""Count the samples whose GT sky mask removed any pixel.
|
|
552
|
+
|
|
553
|
+
A wrong ``skyclass`` in the segmentation's dataset head, or segmentation
|
|
554
|
+
that matches no sample, yields masks without sky, and metrics then include
|
|
555
|
+
the sky without any error.
|
|
556
|
+
"""
|
|
557
|
+
|
|
558
|
+
def __init__(self) -> None:
|
|
559
|
+
self.samples = 0
|
|
560
|
+
self.with_sky = 0
|
|
561
|
+
|
|
562
|
+
def add(self, sky_valid: Optional[np.ndarray]) -> None:
|
|
563
|
+
"""Record one sample's :func:`_get_sky_mask` result."""
|
|
564
|
+
self.samples += 1
|
|
565
|
+
if sky_valid is not None and not np.all(sky_valid):
|
|
566
|
+
self.with_sky += 1
|
|
567
|
+
|
|
568
|
+
def warn_if_empty(self, modality: str) -> None:
|
|
569
|
+
if self.samples and not self.with_sky:
|
|
570
|
+
print(
|
|
571
|
+
f"Warning: --mask-sky found no sky in any of the {self.samples} "
|
|
572
|
+
f"{modality} samples, so no sky was masked. Check that the GT "
|
|
573
|
+
"segmentation matches these samples and that its dataset head "
|
|
574
|
+
"declares the right 'skyclass' (`euler-loading <segmentation "
|
|
575
|
+
"path>` reports the sky it decodes)."
|
|
576
|
+
)
|
|
577
|
+
|
|
578
|
+
|
|
550
579
|
def _get_prediction_sky_mask(
|
|
551
580
|
sample: dict, depth_pred: np.ndarray
|
|
552
581
|
) -> Optional[np.ndarray]:
|
|
@@ -1134,6 +1163,7 @@ def evaluate_depth_samples(
|
|
|
1134
1163
|
try:
|
|
1135
1164
|
print("Computing per-image depth metrics...")
|
|
1136
1165
|
depth_iter = _prefetched_iter(dataset, num_workers)
|
|
1166
|
+
sky_coverage = _SkyMaskCoverage()
|
|
1137
1167
|
for i, sample in tqdm(
|
|
1138
1168
|
depth_iter,
|
|
1139
1169
|
total=num_samples,
|
|
@@ -1181,6 +1211,7 @@ def evaluate_depth_samples(
|
|
|
1181
1211
|
sky_valid = None
|
|
1182
1212
|
if sky_mask_enabled:
|
|
1183
1213
|
sky_valid = _get_sky_mask(sample)
|
|
1214
|
+
sky_coverage.add(sky_valid)
|
|
1184
1215
|
if (
|
|
1185
1216
|
sky_valid is not None
|
|
1186
1217
|
and sky_valid.shape[:2] != depth_pred.shape[:2]
|
|
@@ -1600,6 +1631,8 @@ def evaluate_depth_samples(
|
|
|
1600
1631
|
entry_id,
|
|
1601
1632
|
)
|
|
1602
1633
|
|
|
1634
|
+
sky_coverage.warn_if_empty("depth")
|
|
1635
|
+
|
|
1603
1636
|
if depth_metrics_batcher is not None:
|
|
1604
1637
|
print("Computing batched GPU depth metrics (tail flush)...")
|
|
1605
1638
|
depth_metrics_batcher.finalize()
|
|
@@ -1946,6 +1979,7 @@ def evaluate_sparse_depth_samples(
|
|
|
1946
1979
|
try:
|
|
1947
1980
|
print("Computing sparse pointcloud depth metrics...")
|
|
1948
1981
|
depth_iter = _prefetched_iter(dataset, num_workers)
|
|
1982
|
+
sky_coverage = _SkyMaskCoverage()
|
|
1949
1983
|
for i, sample in tqdm(
|
|
1950
1984
|
depth_iter,
|
|
1951
1985
|
total=num_samples,
|
|
@@ -2013,6 +2047,7 @@ def evaluate_sparse_depth_samples(
|
|
|
2013
2047
|
sky_valid = None
|
|
2014
2048
|
if sky_mask_enabled:
|
|
2015
2049
|
sky_valid = _get_sky_mask(sample)
|
|
2050
|
+
sky_coverage.add(sky_valid)
|
|
2016
2051
|
if sky_valid is not None and sky_valid.shape[:2] != depth_pred.shape[:2]:
|
|
2017
2052
|
sky_valid = align_to_prediction(sky_valid, depth_pred)
|
|
2018
2053
|
|
|
@@ -2200,6 +2235,8 @@ def evaluate_sparse_depth_samples(
|
|
|
2200
2235
|
if np.isfinite(sv):
|
|
2201
2236
|
sanity_checker.validate_depth_silog(sv, entry_id)
|
|
2202
2237
|
|
|
2238
|
+
sky_coverage.warn_if_empty("sparse depth")
|
|
2239
|
+
|
|
2203
2240
|
print("Aggregating sparse depth results...")
|
|
2204
2241
|
|
|
2205
2242
|
emit_native = alignment_applied or normalized_predictions
|
|
@@ -2457,6 +2494,7 @@ def evaluate_rgb_samples(
|
|
|
2457
2494
|
try:
|
|
2458
2495
|
print("Computing per-image RGB metrics...")
|
|
2459
2496
|
rgb_iter = _prefetched_iter(dataset, num_workers)
|
|
2497
|
+
sky_coverage = _SkyMaskCoverage()
|
|
2460
2498
|
for i, sample in tqdm(
|
|
2461
2499
|
rgb_iter,
|
|
2462
2500
|
total=num_samples,
|
|
@@ -2489,6 +2527,7 @@ def evaluate_rgb_samples(
|
|
|
2489
2527
|
sky_valid = None
|
|
2490
2528
|
if sky_mask_enabled:
|
|
2491
2529
|
sky_valid = _get_sky_mask(sample)
|
|
2530
|
+
sky_coverage.add(sky_valid)
|
|
2492
2531
|
if sky_valid is not None and sky_valid.shape[:2] != img_pred.shape[:2]:
|
|
2493
2532
|
sky_valid = align_to_prediction(sky_valid, img_pred)
|
|
2494
2533
|
|
|
@@ -2748,6 +2787,8 @@ def evaluate_rgb_samples(
|
|
|
2748
2787
|
if depth_binned_entry is not None:
|
|
2749
2788
|
sanity_checker.validate_depth_binned(depth_binned_entry, entry_id)
|
|
2750
2789
|
|
|
2790
|
+
sky_coverage.warn_if_empty("RGB")
|
|
2791
|
+
|
|
2751
2792
|
if rgb_lpips_batcher is not None:
|
|
2752
2793
|
print("Computing batched RGB LPIPS (tail flush)...")
|
|
2753
2794
|
rgb_lpips_batcher.finalize()
|
|
@@ -3371,6 +3412,7 @@ def evaluate_points_3d_samples(
|
|
|
3371
3412
|
try:
|
|
3372
3413
|
print("Computing per-image points_3d metrics...")
|
|
3373
3414
|
points_iter = _prefetched_iter(dataset, num_workers)
|
|
3415
|
+
sky_coverage = _SkyMaskCoverage()
|
|
3374
3416
|
for i, sample in tqdm(
|
|
3375
3417
|
points_iter,
|
|
3376
3418
|
total=num_samples,
|
|
@@ -3429,6 +3471,7 @@ def evaluate_points_3d_samples(
|
|
|
3429
3471
|
valid = finite & (gt_norm > 0)
|
|
3430
3472
|
if sky_mask_enabled:
|
|
3431
3473
|
sky_valid = _get_sky_mask(sample)
|
|
3474
|
+
sky_coverage.add(sky_valid)
|
|
3432
3475
|
if sky_valid is not None:
|
|
3433
3476
|
if sky_valid.shape[:2] != pts_pred.shape[:2]:
|
|
3434
3477
|
sky_valid = align_to_prediction(sky_valid, pts_pred)
|
|
@@ -3613,6 +3656,8 @@ def evaluate_points_3d_samples(
|
|
|
3613
3656
|
{"id": entry_id, "metrics": metrics_dict},
|
|
3614
3657
|
)
|
|
3615
3658
|
|
|
3659
|
+
sky_coverage.warn_if_empty("points_3d")
|
|
3660
|
+
|
|
3616
3661
|
print("Aggregating points_3d results...")
|
|
3617
3662
|
if _agg_total > 0:
|
|
3618
3663
|
cumsum = np.cumsum(_agg_hist)
|
|
@@ -4018,6 +4063,7 @@ def evaluate_points_3d_sparse_samples(
|
|
|
4018
4063
|
try:
|
|
4019
4064
|
print("Computing sparse points_3d metrics...")
|
|
4020
4065
|
points_iter = _prefetched_iter(dataset, num_workers)
|
|
4066
|
+
sky_coverage = _SkyMaskCoverage()
|
|
4021
4067
|
for i, sample in tqdm(
|
|
4022
4068
|
points_iter,
|
|
4023
4069
|
total=num_samples,
|
|
@@ -4096,6 +4142,7 @@ def evaluate_points_3d_sparse_samples(
|
|
|
4096
4142
|
sky_valid = None
|
|
4097
4143
|
if sky_mask_enabled:
|
|
4098
4144
|
sky_valid = _get_sky_mask(sample)
|
|
4145
|
+
sky_coverage.add(sky_valid)
|
|
4099
4146
|
if sky_valid is not None and sky_valid.shape[:2] != pred_shape:
|
|
4100
4147
|
ref = depth_pred if pred_is_depth else pts_pred
|
|
4101
4148
|
sky_valid = align_to_prediction(sky_valid, ref)
|
|
@@ -4340,6 +4387,8 @@ def evaluate_points_3d_sparse_samples(
|
|
|
4340
4387
|
{"id": entry_id, "metrics": metrics_dict},
|
|
4341
4388
|
)
|
|
4342
4389
|
|
|
4390
|
+
sky_coverage.warn_if_empty("sparse points_3d")
|
|
4391
|
+
|
|
4343
4392
|
print("Aggregating sparse points_3d results...")
|
|
4344
4393
|
if _agg_total > 0:
|
|
4345
4394
|
cumsum = np.cumsum(_agg_hist)
|
|
@@ -534,6 +534,26 @@ def test_prediction_sky_fill_still_respects_gt_sky_exclusion(monkeypatch):
|
|
|
534
534
|
assert result["depth_metric"]["standard"]["pixel_pool"]["mae"] == 0.0
|
|
535
535
|
|
|
536
536
|
|
|
537
|
+
@pytest.mark.parametrize("has_sky, warned", [(False, True), (True, False)])
|
|
538
|
+
def test_sky_masking_that_masks_nothing_is_reported(
|
|
539
|
+
monkeypatch, capsys, has_sky, warned
|
|
540
|
+
):
|
|
541
|
+
# A wrong 'skyclass' in the segmentation head decodes to masks without sky.
|
|
542
|
+
_patch_depth_metrics(monkeypatch)
|
|
543
|
+
sample = {
|
|
544
|
+
"id": "frame",
|
|
545
|
+
"gt": np.array([[10, 20], [30, 40]], dtype=np.float32),
|
|
546
|
+
"pred": np.array([[10, 20], [30, 40]], dtype=np.float32),
|
|
547
|
+
"segmentation": np.array([[False, False], [False, has_sky]]),
|
|
548
|
+
}
|
|
549
|
+
eval_mod.evaluate_depth_samples(
|
|
550
|
+
_DummyDepthDataset([sample]), is_radial=True, device="cpu",
|
|
551
|
+
num_workers=0, alignment_mode="none", sky_mask_enabled=True,
|
|
552
|
+
)
|
|
553
|
+
out = capsys.readouterr().out
|
|
554
|
+
assert ("--mask-sky found no sky in any of the 1 depth samples" in out) is warned
|
|
555
|
+
|
|
556
|
+
|
|
537
557
|
@pytest.mark.parametrize("sky_depth", [None, 50.0])
|
|
538
558
|
def test_cli_uses_prediction_sky_mask_and_records_provenance(
|
|
539
559
|
monkeypatch, indexed_sky_prediction, tmp_path, sky_depth
|
|
@@ -9,6 +9,7 @@ from euler_eval.evaluate import (
|
|
|
9
9
|
_get_intrinsics_K,
|
|
10
10
|
_get_prediction_sky_mask,
|
|
11
11
|
_get_sky_mask,
|
|
12
|
+
_SkyMaskCoverage,
|
|
12
13
|
)
|
|
13
14
|
|
|
14
15
|
# ---------------------------------------------------------------------------
|
|
@@ -76,6 +77,33 @@ class TestGetSkyMask:
|
|
|
76
77
|
np.testing.assert_array_equal(result, ~sky_a)
|
|
77
78
|
|
|
78
79
|
|
|
80
|
+
# ---------------------------------------------------------------------------
|
|
81
|
+
# _SkyMaskCoverage
|
|
82
|
+
# ---------------------------------------------------------------------------
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
class TestSkyMaskCoverage:
|
|
86
|
+
def test_warns_when_no_sample_has_sky(self, capsys):
|
|
87
|
+
coverage = _SkyMaskCoverage()
|
|
88
|
+
coverage.add(np.ones((2, 2), dtype=bool)) # all valid: no sky
|
|
89
|
+
coverage.add(None) # no segmentation matched the sample
|
|
90
|
+
coverage.warn_if_empty("depth")
|
|
91
|
+
out = capsys.readouterr().out
|
|
92
|
+
assert "--mask-sky found no sky in any of the 2 depth samples" in out
|
|
93
|
+
assert "'skyclass'" in out
|
|
94
|
+
|
|
95
|
+
def test_silent_when_any_sample_has_sky(self, capsys):
|
|
96
|
+
coverage = _SkyMaskCoverage()
|
|
97
|
+
coverage.add(np.ones((2, 2), dtype=bool))
|
|
98
|
+
coverage.add(np.array([[False, True], [True, True]]))
|
|
99
|
+
coverage.warn_if_empty("depth")
|
|
100
|
+
assert capsys.readouterr().out == ""
|
|
101
|
+
|
|
102
|
+
def test_silent_when_sky_masking_never_ran(self, capsys):
|
|
103
|
+
_SkyMaskCoverage().warn_if_empty("depth")
|
|
104
|
+
assert capsys.readouterr().out == ""
|
|
105
|
+
|
|
106
|
+
|
|
79
107
|
# ---------------------------------------------------------------------------
|
|
80
108
|
# _get_intrinsics_K
|
|
81
109
|
# ---------------------------------------------------------------------------
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{euler_eval-2.29.0 → euler_eval-2.30.0}/tests/fixtures/distributions/counts-points3d.eval.json
RENAMED
|
File without changes
|
{euler_eval-2.29.0 → euler_eval-2.30.0}/tests/fixtures/distributions/counts-sparsedepth.eval.json
RENAMED
|
File without changes
|
{euler_eval-2.29.0 → euler_eval-2.30.0}/tests/fixtures/distributions/future-sum-by-depth.eval.json
RENAMED
|
File without changes
|
{euler_eval-2.29.0 → euler_eval-2.30.0}/tests/fixtures/distributions/histogram-only.eval.json
RENAMED
|
File without changes
|
{euler_eval-2.29.0 → euler_eval-2.30.0}/tests/fixtures/distributions/per-file-only.eval.json
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|