euler-preprocess 3.15.0__tar.gz → 3.17.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_preprocess-3.15.0 → euler_preprocess-3.17.0}/PKG-INFO +30 -12
- euler_preprocess-3.15.0/euler_preprocess.egg-info/PKG-INFO → euler_preprocess-3.17.0/README.md +28 -30
- {euler_preprocess-3.15.0 → euler_preprocess-3.17.0}/euler_preprocess/cli.py +8 -1
- euler_preprocess-3.17.0/euler_preprocess/common/dataset.py +224 -0
- {euler_preprocess-3.15.0 → euler_preprocess-3.17.0}/euler_preprocess/common/intrinsics.py +3 -2
- {euler_preprocess-3.15.0 → euler_preprocess-3.17.0}/euler_preprocess/common/normalize.py +14 -1
- {euler_preprocess-3.15.0 → euler_preprocess-3.17.0}/euler_preprocess/common/transform.py +14 -0
- {euler_preprocess-3.15.0 → euler_preprocess-3.17.0}/euler_preprocess/fog/atmospheric_light.py +2 -2
- {euler_preprocess-3.15.0 → euler_preprocess-3.17.0}/euler_preprocess/fog/inference.py +4 -3
- {euler_preprocess-3.15.0 → euler_preprocess-3.17.0}/euler_preprocess/fog/logging.py +25 -0
- {euler_preprocess-3.15.0 → euler_preprocess-3.17.0}/euler_preprocess/fog/models.py +23 -5
- {euler_preprocess-3.15.0 → euler_preprocess-3.17.0}/euler_preprocess/fog/transform.py +42 -2
- {euler_preprocess-3.15.0 → euler_preprocess-3.17.0}/euler_preprocess/radial/transform.py +3 -3
- {euler_preprocess-3.15.0 → euler_preprocess-3.17.0}/euler_preprocess/sky_depth/transform.py +2 -2
- {euler_preprocess-3.15.0 → euler_preprocess-3.17.0}/euler_preprocess/spatial.py +9 -1
- euler_preprocess-3.15.0/README.md → euler_preprocess-3.17.0/euler_preprocess.egg-info/PKG-INFO +48 -10
- {euler_preprocess-3.15.0 → euler_preprocess-3.17.0}/euler_preprocess.egg-info/SOURCES.txt +2 -0
- {euler_preprocess-3.15.0 → euler_preprocess-3.17.0}/euler_preprocess.egg-info/requires.txt +1 -1
- {euler_preprocess-3.15.0 → euler_preprocess-3.17.0}/pyproject.toml +2 -2
- {euler_preprocess-3.15.0 → euler_preprocess-3.17.0}/tests/test_fog_aux_outputs.py +2 -0
- euler_preprocess-3.17.0/tests/test_fog_input_normalization.py +259 -0
- euler_preprocess-3.17.0/tests/test_modality_roles.py +192 -0
- {euler_preprocess-3.15.0 → euler_preprocess-3.17.0}/tests/test_sky_depth.py +11 -0
- {euler_preprocess-3.15.0 → euler_preprocess-3.17.0}/tests/test_spatial.py +11 -0
- euler_preprocess-3.15.0/euler_preprocess/common/dataset.py +0 -73
- {euler_preprocess-3.15.0 → euler_preprocess-3.17.0}/CHANGELOG.md +0 -0
- {euler_preprocess-3.15.0 → euler_preprocess-3.17.0}/LICENSE +0 -0
- {euler_preprocess-3.15.0 → euler_preprocess-3.17.0}/MANIFEST.in +0 -0
- {euler_preprocess-3.15.0 → euler_preprocess-3.17.0}/docs/spatial.md +0 -0
- {euler_preprocess-3.15.0 → euler_preprocess-3.17.0}/euler_preprocess/__init__.py +0 -0
- {euler_preprocess-3.15.0 → euler_preprocess-3.17.0}/euler_preprocess/common/__init__.py +0 -0
- {euler_preprocess-3.15.0 → euler_preprocess-3.17.0}/euler_preprocess/common/color.py +0 -0
- {euler_preprocess-3.15.0 → euler_preprocess-3.17.0}/euler_preprocess/common/device.py +0 -0
- {euler_preprocess-3.15.0 → euler_preprocess-3.17.0}/euler_preprocess/common/io.py +0 -0
- {euler_preprocess-3.15.0 → euler_preprocess-3.17.0}/euler_preprocess/common/logging.py +0 -0
- {euler_preprocess-3.15.0 → euler_preprocess-3.17.0}/euler_preprocess/common/noise.py +0 -0
- {euler_preprocess-3.15.0 → euler_preprocess-3.17.0}/euler_preprocess/common/output.py +0 -0
- {euler_preprocess-3.15.0 → euler_preprocess-3.17.0}/euler_preprocess/common/sampling.py +0 -0
- {euler_preprocess-3.15.0 → euler_preprocess-3.17.0}/euler_preprocess/fog/__init__.py +0 -0
- {euler_preprocess-3.15.0 → euler_preprocess-3.17.0}/euler_preprocess/fog/airlight_from_sky.py +0 -0
- {euler_preprocess-3.15.0 → euler_preprocess-3.17.0}/euler_preprocess/fog/augmentations.py +0 -0
- {euler_preprocess-3.15.0 → euler_preprocess-3.17.0}/euler_preprocess/fog/capture.py +0 -0
- {euler_preprocess-3.15.0 → euler_preprocess-3.17.0}/euler_preprocess/fog/dcp_airlight.py +0 -0
- {euler_preprocess-3.15.0 → euler_preprocess-3.17.0}/euler_preprocess/fog/dcp_airlight_torch.py +0 -0
- {euler_preprocess-3.15.0 → euler_preprocess-3.17.0}/euler_preprocess/fog/dcp_heuristic_airlight.py +0 -0
- {euler_preprocess-3.15.0 → euler_preprocess-3.17.0}/euler_preprocess/fog/dcp_heuristic_airlight_torch.py +0 -0
- {euler_preprocess-3.15.0 → euler_preprocess-3.17.0}/euler_preprocess/fog/pipeline.py +0 -0
- {euler_preprocess-3.15.0 → euler_preprocess-3.17.0}/euler_preprocess/radial/__init__.py +0 -0
- {euler_preprocess-3.15.0 → euler_preprocess-3.17.0}/euler_preprocess/sky_depth/__init__.py +0 -0
- {euler_preprocess-3.15.0 → euler_preprocess-3.17.0}/euler_preprocess.egg-info/dependency_links.txt +0 -0
- {euler_preprocess-3.15.0 → euler_preprocess-3.17.0}/euler_preprocess.egg-info/entry_points.txt +0 -0
- {euler_preprocess-3.15.0 → euler_preprocess-3.17.0}/euler_preprocess.egg-info/top_level.txt +0 -0
- {euler_preprocess-3.15.0 → euler_preprocess-3.17.0}/examples/spatial-five-field.json +0 -0
- {euler_preprocess-3.15.0 → euler_preprocess-3.17.0}/setup.cfg +0 -0
- {euler_preprocess-3.15.0 → euler_preprocess-3.17.0}/tests/test_airlight_fallback.py +0 -0
- {euler_preprocess-3.15.0 → euler_preprocess-3.17.0}/tests/test_cli_sample_selection.py +0 -0
- {euler_preprocess-3.15.0 → euler_preprocess-3.17.0}/tests/test_dcp_heuristic_airlight.py +0 -0
- {euler_preprocess-3.15.0 → euler_preprocess-3.17.0}/tests/test_dense_gloomy_daylight_config.py +0 -0
- {euler_preprocess-3.15.0 → euler_preprocess-3.17.0}/tests/test_fog_aware_auto_exposure.py +0 -0
- {euler_preprocess-3.15.0 → euler_preprocess-3.17.0}/tests/test_fog_cpu_gpu_parity.py +0 -0
- {euler_preprocess-3.15.0 → euler_preprocess-3.17.0}/tests/test_fog_integration.py +0 -0
- {euler_preprocess-3.15.0 → euler_preprocess-3.17.0}/tests/test_radial.py +0 -0
- {euler_preprocess-3.15.0 → euler_preprocess-3.17.0}/tests/test_real_drive_sim_scenario_profiles.py +0 -0
- {euler_preprocess-3.15.0 → euler_preprocess-3.17.0}/tests/test_scene_illumination.py +0 -0
- {euler_preprocess-3.15.0 → euler_preprocess-3.17.0}/tests/test_sensor_identity.py +0 -0
- {euler_preprocess-3.15.0 → euler_preprocess-3.17.0}/tests/test_source_backed_output.py +0 -0
- {euler_preprocess-3.15.0 → euler_preprocess-3.17.0}/tests/test_tone_map_lut.py +0 -0
- {euler_preprocess-3.15.0 → euler_preprocess-3.17.0}/tests/test_zip_output.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: euler-preprocess
|
|
3
|
-
Version: 3.
|
|
3
|
+
Version: 3.17.0
|
|
4
4
|
Summary: Physics-based preprocessing (fog, etc.) for RGB+depth datasets
|
|
5
5
|
Author-email: Daniel Rothenpieler <rothenpielerdaniel@gmail.com>
|
|
6
6
|
License-Expression: MIT
|
|
@@ -9,7 +9,7 @@ Description-Content-Type: text/markdown
|
|
|
9
9
|
License-File: LICENSE
|
|
10
10
|
Requires-Dist: numpy
|
|
11
11
|
Requires-Dist: Pillow
|
|
12
|
-
Requires-Dist: euler-loading>=2.
|
|
12
|
+
Requires-Dist: euler-loading>=2.29.0
|
|
13
13
|
Requires-Dist: ds-crawler>=2.11.0
|
|
14
14
|
Requires-Dist: euler-dataset-contract>=0.5.0
|
|
15
15
|
Provides-Extra: gpu
|
|
@@ -94,9 +94,7 @@ loader.
|
|
|
94
94
|
"modalities": {
|
|
95
95
|
"rgb": {"path": "/path/to/rgb", "split": "train"},
|
|
96
96
|
"depth": "/path/to/depth",
|
|
97
|
-
"semantic_segmentation": "/path/to/classSegmentation"
|
|
98
|
-
},
|
|
99
|
-
"hierarchical_modalities": {
|
|
97
|
+
"semantic_segmentation": "/path/to/classSegmentation",
|
|
100
98
|
"intrinsics": {"path": "/path/to/intrinsics"}
|
|
101
99
|
}
|
|
102
100
|
}
|
|
@@ -109,17 +107,36 @@ loader.
|
|
|
109
107
|
| `output_slot` | Optional slot selector when `pipeline.output_targets` has several entries. Defaults to `rgb` for `fog` and `depth` for `sky-depth` / `radial`. |
|
|
110
108
|
| `sample` | Optional 0-based dataset index. Only `dataset[sample]` is transformed — useful for small benchmark slices of large datasets. |
|
|
111
109
|
| `samples` | Optional multi-sample selector: a list of indices (`[0, 10, 20]`) or a slice object (`{"start": 0, "stop": 1000, "step": 2, "count": 100}`). `stop` is exclusive, `count` caps the result. Mutually exclusive with `sample`. |
|
|
112
|
-
| `modalities` |
|
|
113
|
-
| `hierarchical_modalities` |
|
|
110
|
+
| `modalities` | All input modalities. Each value is a path string or an object with `path` and optional `split`. Whether a modality is per-sample or hierarchical is inferred (see below). |
|
|
111
|
+
| `hierarchical_modalities` | Legacy, optional. Same format; entries are pooled with `modalities` and resolved the same way. |
|
|
114
112
|
| `pipeline` | Optional runtime routing block compatible with `euler-inference`. |
|
|
115
113
|
|
|
116
114
|
**Required modalities per transform:**
|
|
117
115
|
|
|
118
|
-
| Transform |
|
|
116
|
+
| Transform | Required | Optional |
|
|
119
117
|
|---|---|---|
|
|
120
|
-
| `fog` | `rgb`, `depth`, `semantic_segmentation` | `intrinsics
|
|
118
|
+
| `fog` | `rgb`, `depth`, `semantic_segmentation` | `intrinsics`; used for radial depth conversion and camera-profile optics |
|
|
121
119
|
| `sky-depth` | `depth`, `semantic_segmentation` | — |
|
|
122
|
-
| `radial` | `depth
|
|
120
|
+
| `radial` | `depth`, `intrinsics` | — |
|
|
121
|
+
|
|
122
|
+
`fog` and `sky-depth` need `semantic_segmentation` as a boolean sky mask. They load
|
|
123
|
+
it with the `sky_mask` function of the euler-loading module that the index's
|
|
124
|
+
dataset head names, so a `class_segmentation` index works as is: the sky is the
|
|
125
|
+
head's `skyclass` (Synscapes `[23, 23, 23]`, Real Drive Sim `[29, 0, 0]`, VKITTI2
|
|
126
|
+
`[90, 200, 255]`), and the log names the loader and the sky definition in use.
|
|
127
|
+
Set `"function"` in the modality entry to use another loader function, or
|
|
128
|
+
`"function": null` for the one the head declares. A non-binary mask raises an
|
|
129
|
+
error rather than marking every non-zero class as sky.
|
|
130
|
+
|
|
131
|
+
**Per-sample vs. hierarchical modalities.** You don't declare a modality's role:
|
|
132
|
+
it is inferred from the ds-crawler indexes. A modality whose qualified IDs
|
|
133
|
+
(hierarchy path plus file ID) match the transform's per-sample inputs is joined per
|
|
134
|
+
sample, e.g. Synscapes' per-image `meta/<id>.json` intrinsics. A modality whose
|
|
135
|
+
files share no sample IDs but sit at or above the samples' hierarchy level is
|
|
136
|
+
matched hierarchically and shared by every sample beneath it, e.g. one intrinsics
|
|
137
|
+
file per scene or camera. A modality that is neither raises an error, and the
|
|
138
|
+
inferred roles are logged. Listing a per-sample modality under
|
|
139
|
+
`hierarchical_modalities` logs a warning and loads it per sample.
|
|
123
140
|
|
|
124
141
|
When a modality directory contains ds-crawler split files
|
|
125
142
|
(`.ds_crawler/split_<name>.json`), set `split` on that modality to select a subset.
|
|
@@ -659,7 +676,8 @@ where sky depth is encoded as zero or infinity and needs a large finite value.
|
|
|
659
676
|
}
|
|
660
677
|
```
|
|
661
678
|
|
|
662
|
-
`sky_depth_value` defaults to `1000.0`.
|
|
679
|
+
`sky_depth_value` defaults to `1000.0`. The sky mask comes from the dataset head as
|
|
680
|
+
described under [Dataset Config](#dataset-config).
|
|
663
681
|
|
|
664
682
|
CLI runs write a source-backed depth dataset mirroring the input depth modality's paths,
|
|
665
683
|
filenames, extensions, and metadata. Standalone `SkyDepthTransform(...)` usage keeps the
|
|
@@ -677,7 +695,7 @@ d_radial(u, v) = d_planar(u, v) * sqrt(((u - cx)/fx)^2 + ((v - cy)/fy)^2 + 1)
|
|
|
677
695
|
```
|
|
678
696
|
|
|
679
697
|
The transform config takes no parameters (`{}`); intrinsics are read from the
|
|
680
|
-
`intrinsics`
|
|
698
|
+
`intrinsics` modality, stored either per sample or per scene/camera.
|
|
681
699
|
|
|
682
700
|
CLI runs write a source-backed depth dataset mirroring the input depth modality's layout
|
|
683
701
|
and writer metadata, with `meta.radial_depth` set to `true` in the emitted `index.json`.
|
euler_preprocess-3.15.0/euler_preprocess.egg-info/PKG-INFO → euler_preprocess-3.17.0/README.md
RENAMED
|
@@ -1,23 +1,3 @@
|
|
|
1
|
-
Metadata-Version: 2.4
|
|
2
|
-
Name: euler-preprocess
|
|
3
|
-
Version: 3.15.0
|
|
4
|
-
Summary: Physics-based preprocessing (fog, etc.) for RGB+depth datasets
|
|
5
|
-
Author-email: Daniel Rothenpieler <rothenpielerdaniel@gmail.com>
|
|
6
|
-
License-Expression: MIT
|
|
7
|
-
Requires-Python: >=3.9
|
|
8
|
-
Description-Content-Type: text/markdown
|
|
9
|
-
License-File: LICENSE
|
|
10
|
-
Requires-Dist: numpy
|
|
11
|
-
Requires-Dist: Pillow
|
|
12
|
-
Requires-Dist: euler-loading>=2.24.0
|
|
13
|
-
Requires-Dist: ds-crawler>=2.11.0
|
|
14
|
-
Requires-Dist: euler-dataset-contract>=0.5.0
|
|
15
|
-
Provides-Extra: gpu
|
|
16
|
-
Requires-Dist: torch; extra == "gpu"
|
|
17
|
-
Provides-Extra: progress
|
|
18
|
-
Requires-Dist: tqdm; extra == "progress"
|
|
19
|
-
Dynamic: license-file
|
|
20
|
-
|
|
21
1
|
<!-- euler header — shared across the euler packages.
|
|
22
2
|
Per package, change only: the <h1>, the tagline, and the badge URLs. -->
|
|
23
3
|
<p align="center">
|
|
@@ -94,9 +74,7 @@ loader.
|
|
|
94
74
|
"modalities": {
|
|
95
75
|
"rgb": {"path": "/path/to/rgb", "split": "train"},
|
|
96
76
|
"depth": "/path/to/depth",
|
|
97
|
-
"semantic_segmentation": "/path/to/classSegmentation"
|
|
98
|
-
},
|
|
99
|
-
"hierarchical_modalities": {
|
|
77
|
+
"semantic_segmentation": "/path/to/classSegmentation",
|
|
100
78
|
"intrinsics": {"path": "/path/to/intrinsics"}
|
|
101
79
|
}
|
|
102
80
|
}
|
|
@@ -109,17 +87,36 @@ loader.
|
|
|
109
87
|
| `output_slot` | Optional slot selector when `pipeline.output_targets` has several entries. Defaults to `rgb` for `fog` and `depth` for `sky-depth` / `radial`. |
|
|
110
88
|
| `sample` | Optional 0-based dataset index. Only `dataset[sample]` is transformed — useful for small benchmark slices of large datasets. |
|
|
111
89
|
| `samples` | Optional multi-sample selector: a list of indices (`[0, 10, 20]`) or a slice object (`{"start": 0, "stop": 1000, "step": 2, "count": 100}`). `stop` is exclusive, `count` caps the result. Mutually exclusive with `sample`. |
|
|
112
|
-
| `modalities` |
|
|
113
|
-
| `hierarchical_modalities` |
|
|
90
|
+
| `modalities` | All input modalities. Each value is a path string or an object with `path` and optional `split`. Whether a modality is per-sample or hierarchical is inferred (see below). |
|
|
91
|
+
| `hierarchical_modalities` | Legacy, optional. Same format; entries are pooled with `modalities` and resolved the same way. |
|
|
114
92
|
| `pipeline` | Optional runtime routing block compatible with `euler-inference`. |
|
|
115
93
|
|
|
116
94
|
**Required modalities per transform:**
|
|
117
95
|
|
|
118
|
-
| Transform |
|
|
96
|
+
| Transform | Required | Optional |
|
|
119
97
|
|---|---|---|
|
|
120
|
-
| `fog` | `rgb`, `depth`, `semantic_segmentation` | `intrinsics
|
|
98
|
+
| `fog` | `rgb`, `depth`, `semantic_segmentation` | `intrinsics`; used for radial depth conversion and camera-profile optics |
|
|
121
99
|
| `sky-depth` | `depth`, `semantic_segmentation` | — |
|
|
122
|
-
| `radial` | `depth
|
|
100
|
+
| `radial` | `depth`, `intrinsics` | — |
|
|
101
|
+
|
|
102
|
+
`fog` and `sky-depth` need `semantic_segmentation` as a boolean sky mask. They load
|
|
103
|
+
it with the `sky_mask` function of the euler-loading module that the index's
|
|
104
|
+
dataset head names, so a `class_segmentation` index works as is: the sky is the
|
|
105
|
+
head's `skyclass` (Synscapes `[23, 23, 23]`, Real Drive Sim `[29, 0, 0]`, VKITTI2
|
|
106
|
+
`[90, 200, 255]`), and the log names the loader and the sky definition in use.
|
|
107
|
+
Set `"function"` in the modality entry to use another loader function, or
|
|
108
|
+
`"function": null` for the one the head declares. A non-binary mask raises an
|
|
109
|
+
error rather than marking every non-zero class as sky.
|
|
110
|
+
|
|
111
|
+
**Per-sample vs. hierarchical modalities.** You don't declare a modality's role:
|
|
112
|
+
it is inferred from the ds-crawler indexes. A modality whose qualified IDs
|
|
113
|
+
(hierarchy path plus file ID) match the transform's per-sample inputs is joined per
|
|
114
|
+
sample, e.g. Synscapes' per-image `meta/<id>.json` intrinsics. A modality whose
|
|
115
|
+
files share no sample IDs but sit at or above the samples' hierarchy level is
|
|
116
|
+
matched hierarchically and shared by every sample beneath it, e.g. one intrinsics
|
|
117
|
+
file per scene or camera. A modality that is neither raises an error, and the
|
|
118
|
+
inferred roles are logged. Listing a per-sample modality under
|
|
119
|
+
`hierarchical_modalities` logs a warning and loads it per sample.
|
|
123
120
|
|
|
124
121
|
When a modality directory contains ds-crawler split files
|
|
125
122
|
(`.ds_crawler/split_<name>.json`), set `split` on that modality to select a subset.
|
|
@@ -659,7 +656,8 @@ where sky depth is encoded as zero or infinity and needs a large finite value.
|
|
|
659
656
|
}
|
|
660
657
|
```
|
|
661
658
|
|
|
662
|
-
`sky_depth_value` defaults to `1000.0`.
|
|
659
|
+
`sky_depth_value` defaults to `1000.0`. The sky mask comes from the dataset head as
|
|
660
|
+
described under [Dataset Config](#dataset-config).
|
|
663
661
|
|
|
664
662
|
CLI runs write a source-backed depth dataset mirroring the input depth modality's paths,
|
|
665
663
|
filenames, extensions, and metadata. Standalone `SkyDepthTransform(...)` usage keeps the
|
|
@@ -677,7 +675,7 @@ d_radial(u, v) = d_planar(u, v) * sqrt(((u - cx)/fx)^2 + ((v - cy)/fy)^2 + 1)
|
|
|
677
675
|
```
|
|
678
676
|
|
|
679
677
|
The transform config takes no parameters (`{}`); intrinsics are read from the
|
|
680
|
-
`intrinsics`
|
|
678
|
+
`intrinsics` modality, stored either per sample or per scene/camera.
|
|
681
679
|
|
|
682
680
|
CLI runs write a source-backed depth dataset mirroring the input depth modality's layout
|
|
683
681
|
and writer metadata, with `meta.radial_depth` set to `true` in the emitted `index.json`.
|
|
@@ -178,7 +178,14 @@ def _run_transform(args: argparse.Namespace, transform_class: type) -> int:
|
|
|
178
178
|
|
|
179
179
|
required_modalities = transform_class.REQUIRED_MODALITIES
|
|
180
180
|
required_hierarchical = transform_class.REQUIRED_HIERARCHICAL_MODALITIES or None
|
|
181
|
-
|
|
181
|
+
# euler-loading logs which loader (and sky definition) each modality uses.
|
|
182
|
+
get_logger("euler_loading")
|
|
183
|
+
dataset = build_dataset(
|
|
184
|
+
config,
|
|
185
|
+
required_modalities,
|
|
186
|
+
required_hierarchical,
|
|
187
|
+
transform_class.MODALITY_FUNCTIONS,
|
|
188
|
+
)
|
|
182
189
|
output_backends = prepare_output_backends(config, dataset, transform_class)
|
|
183
190
|
primary_backend = next(iter(output_backends.values()))
|
|
184
191
|
samples = _select_configured_samples(config, dataset, logger)
|
|
@@ -0,0 +1,224 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from collections import Counter
|
|
4
|
+
|
|
5
|
+
from euler_preprocess.common.logging import get_logger
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
def _make_modality(entry: str | dict, function: str | None = None):
|
|
9
|
+
"""Build a ``Modality``; an entry's own ``function`` replaces *function*."""
|
|
10
|
+
from euler_loading import Modality
|
|
11
|
+
|
|
12
|
+
if isinstance(entry, str):
|
|
13
|
+
return Modality(entry, function=function)
|
|
14
|
+
options = {
|
|
15
|
+
key: entry[key]
|
|
16
|
+
for key in (
|
|
17
|
+
"split",
|
|
18
|
+
"metadata_scope",
|
|
19
|
+
"collapse_single",
|
|
20
|
+
"cache",
|
|
21
|
+
"applies_to",
|
|
22
|
+
"hierarchy_scope",
|
|
23
|
+
"function",
|
|
24
|
+
)
|
|
25
|
+
if key in entry
|
|
26
|
+
}
|
|
27
|
+
options.setdefault("function", function)
|
|
28
|
+
return Modality(entry["path"], **options)
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
def _load_index_tree(name: str, modality) -> dict:
|
|
32
|
+
"""Load a modality's ds-crawler tree the same way ``MultiModalDataset`` does."""
|
|
33
|
+
from ds_crawler import index_dataset_from_path
|
|
34
|
+
from euler_loading._ds_crawler_utils import load_index_output
|
|
35
|
+
from euler_loading.dataset import _get_index_tree, _resolve_metadata_scope
|
|
36
|
+
|
|
37
|
+
modality = _resolve_metadata_scope(name, modality)
|
|
38
|
+
index = load_index_output(
|
|
39
|
+
modality.path,
|
|
40
|
+
split=modality.split,
|
|
41
|
+
metadata_scope=modality.metadata_scope,
|
|
42
|
+
index_dataset_from_path_fn=index_dataset_from_path,
|
|
43
|
+
)
|
|
44
|
+
return _get_index_tree(index)
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
def _check_attachment(
|
|
48
|
+
name: str, seed: str, tree: dict, samples_per_level: Counter
|
|
49
|
+
) -> None:
|
|
50
|
+
"""Check that hierarchical modality *name* applies to the samples by tree position."""
|
|
51
|
+
from euler_loading.indexing import collect_hierarchical_files, match_hierarchical_files
|
|
52
|
+
|
|
53
|
+
files_by_level = collect_hierarchical_files(tree)
|
|
54
|
+
attached = {
|
|
55
|
+
level: len(match_hierarchical_files(level, files_by_level))
|
|
56
|
+
for level in samples_per_level
|
|
57
|
+
}
|
|
58
|
+
covered = sum(count for level, count in samples_per_level.items() if attached[level])
|
|
59
|
+
if not covered:
|
|
60
|
+
raise ValueError(
|
|
61
|
+
f"Modality '{name}' shares no sample IDs with '{seed}' and none of its "
|
|
62
|
+
"files sit at or above the hierarchy level of any sample, so it can be "
|
|
63
|
+
"neither joined per sample nor matched hierarchically. Check that its "
|
|
64
|
+
f"ds-crawler IDs and hierarchy follow the same conventions as '{seed}'."
|
|
65
|
+
)
|
|
66
|
+
logger = get_logger()
|
|
67
|
+
total = sum(samples_per_level.values())
|
|
68
|
+
log = logger.info if covered == total else logger.warning
|
|
69
|
+
log(
|
|
70
|
+
"Modality '%s' is hierarchical (no sample IDs shared with '%s'); it applies "
|
|
71
|
+
"to %d/%d samples by hierarchy position.",
|
|
72
|
+
name,
|
|
73
|
+
seed,
|
|
74
|
+
covered,
|
|
75
|
+
total,
|
|
76
|
+
)
|
|
77
|
+
most = max(attached.values())
|
|
78
|
+
if most > 1:
|
|
79
|
+
logger.warning(
|
|
80
|
+
"Hierarchical modality '%s' attaches up to %d files to one sample. If it "
|
|
81
|
+
"holds per-sample data, its ds-crawler IDs do not match '%s'.",
|
|
82
|
+
name,
|
|
83
|
+
most,
|
|
84
|
+
seed,
|
|
85
|
+
)
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
def _resolve_roles(
|
|
89
|
+
modalities: dict, per_sample: set[str]
|
|
90
|
+
) -> tuple[list[str], list[str]]:
|
|
91
|
+
"""Split modality names into regular and hierarchical ones.
|
|
92
|
+
|
|
93
|
+
The role follows from the ds-crawler indexes. A modality is regular
|
|
94
|
+
(joined per sample) when its qualified file IDs -- hierarchy path plus
|
|
95
|
+
file ID -- overlap those of the seed modality. Otherwise it is
|
|
96
|
+
hierarchical (matched by tree position, like per-scene intrinsics) and
|
|
97
|
+
must sit at or above the hierarchy level of at least one sample.
|
|
98
|
+
|
|
99
|
+
*per_sample* modalities are regular by contract and the first one is the
|
|
100
|
+
seed. Without one, the seed is the modality aligned with the most others,
|
|
101
|
+
then the one with the most files, then the first listed.
|
|
102
|
+
"""
|
|
103
|
+
from euler_loading.indexing import collect_files
|
|
104
|
+
|
|
105
|
+
names = list(modalities)
|
|
106
|
+
if len(names) < 2 or per_sample.issuperset(names):
|
|
107
|
+
return names, []
|
|
108
|
+
|
|
109
|
+
trees = {name: _load_index_tree(name, modalities[name]) for name in names}
|
|
110
|
+
# Qualified sample ID -> hierarchy path, keyed like euler-loading's lookups.
|
|
111
|
+
sample_ids = {}
|
|
112
|
+
for name, tree in trees.items():
|
|
113
|
+
sample_ids[name] = {
|
|
114
|
+
"/".join(r.hierarchy_path + (r.file_entry["id"],)): r.hierarchy_path
|
|
115
|
+
for r in collect_files(tree)
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
def aligned(name: str, other: str) -> bool:
|
|
119
|
+
return not sample_ids[name].keys().isdisjoint(sample_ids[other].keys())
|
|
120
|
+
|
|
121
|
+
anchors = [name for name in names if name in per_sample]
|
|
122
|
+
seed = anchors[0] if anchors else max(
|
|
123
|
+
names,
|
|
124
|
+
key=lambda name: (
|
|
125
|
+
sum(aligned(other, name) for other in names),
|
|
126
|
+
len(sample_ids[name]),
|
|
127
|
+
),
|
|
128
|
+
)
|
|
129
|
+
regular = [
|
|
130
|
+
name
|
|
131
|
+
for name in names
|
|
132
|
+
if name in per_sample or name == seed or aligned(name, seed)
|
|
133
|
+
]
|
|
134
|
+
hierarchical = [name for name in names if name not in regular]
|
|
135
|
+
get_logger().info(
|
|
136
|
+
"Modality roles inferred from ds-crawler indexes: per-sample %s; "
|
|
137
|
+
"hierarchical %s",
|
|
138
|
+
", ".join(regular),
|
|
139
|
+
", ".join(hierarchical) or "none",
|
|
140
|
+
)
|
|
141
|
+
|
|
142
|
+
# Without common samples MultiModalDataset raises its own, clearer error.
|
|
143
|
+
common = set(sample_ids[seed]).intersection(*(sample_ids[name] for name in regular))
|
|
144
|
+
if common:
|
|
145
|
+
samples_per_level = Counter(sample_ids[seed][sample_id] for sample_id in common)
|
|
146
|
+
for name in hierarchical:
|
|
147
|
+
_check_attachment(name, seed, trees[name], samples_per_level)
|
|
148
|
+
return regular, hierarchical
|
|
149
|
+
|
|
150
|
+
|
|
151
|
+
def build_dataset(
|
|
152
|
+
config: dict,
|
|
153
|
+
required_modalities: set[str],
|
|
154
|
+
required_hierarchical: set[str] | None = None,
|
|
155
|
+
modality_functions: dict[str, str] | None = None,
|
|
156
|
+
):
|
|
157
|
+
"""Build a ``MultiModalDataset`` from a config dict.
|
|
158
|
+
|
|
159
|
+
Each modality entry is either a plain path string or a dict with
|
|
160
|
+
``path`` and optional ``split``. Loader resolution (which function to
|
|
161
|
+
call, which module to use) is handled by euler-loading via the
|
|
162
|
+
ds-crawler index at each path — point the config at a path whose
|
|
163
|
+
index declares the function you want (e.g. a ``sky_mask`` index for
|
|
164
|
+
boolean sky masks vs. a ``class_segmentation`` index for raw class
|
|
165
|
+
id maps).
|
|
166
|
+
|
|
167
|
+
Every modality can be listed under ``modalities``: whether it is joined
|
|
168
|
+
per sample or matched hierarchically (e.g. per-scene intrinsics) is
|
|
169
|
+
inferred from the ds-crawler indexes. Entries under the legacy
|
|
170
|
+
``hierarchical_modalities`` key are pooled with them and resolved the
|
|
171
|
+
same way. *required_modalities* are per-sample inputs and anchor that
|
|
172
|
+
inference; *required_hierarchical* names further required modalities,
|
|
173
|
+
whose role is inferred like any other.
|
|
174
|
+
|
|
175
|
+
*modality_functions* maps a modality to the loader function to use from
|
|
176
|
+
the module its dataset head names (a transform's ``MODALITY_FUNCTIONS``),
|
|
177
|
+
e.g. ``sky_mask`` for a ``class_segmentation`` index; the sky then comes
|
|
178
|
+
from the head's ``skyclass``. A modality entry's own ``function`` key
|
|
179
|
+
overrides it, and ``"function": null`` keeps the head's function.
|
|
180
|
+
"""
|
|
181
|
+
from euler_loading import MultiModalDataset, SamplePreprocessor
|
|
182
|
+
|
|
183
|
+
raw_modalities = config.get("modalities", {})
|
|
184
|
+
raw_hierarchical = config.get("hierarchical_modalities", {})
|
|
185
|
+
|
|
186
|
+
duplicated = raw_modalities.keys() & raw_hierarchical.keys()
|
|
187
|
+
if duplicated:
|
|
188
|
+
raise ValueError(
|
|
189
|
+
"Modalities listed under both 'modalities' and "
|
|
190
|
+
f"'hierarchical_modalities': {', '.join(sorted(duplicated))}"
|
|
191
|
+
)
|
|
192
|
+
configured = {**raw_modalities, **raw_hierarchical}
|
|
193
|
+
|
|
194
|
+
required = set(required_modalities) | set(required_hierarchical or ())
|
|
195
|
+
missing = required - configured.keys()
|
|
196
|
+
if missing:
|
|
197
|
+
raise ValueError(
|
|
198
|
+
f"Missing required modalities in config: {', '.join(sorted(missing))}. "
|
|
199
|
+
f"'modalities' must contain at least: {', '.join(sorted(required))}"
|
|
200
|
+
)
|
|
201
|
+
|
|
202
|
+
functions = modality_functions or {}
|
|
203
|
+
modalities = {
|
|
204
|
+
name: _make_modality(entry, functions.get(name))
|
|
205
|
+
for name, entry in configured.items()
|
|
206
|
+
}
|
|
207
|
+
regular, hierarchical = _resolve_roles(modalities, set(required_modalities))
|
|
208
|
+
logger = get_logger()
|
|
209
|
+
for name in regular:
|
|
210
|
+
if name in raw_hierarchical:
|
|
211
|
+
logger.warning(
|
|
212
|
+
"Modality '%s' is listed under 'hierarchical_modalities' but holds "
|
|
213
|
+
"per-sample data; loading it per sample.",
|
|
214
|
+
name,
|
|
215
|
+
)
|
|
216
|
+
|
|
217
|
+
return MultiModalDataset(
|
|
218
|
+
modalities={name: modalities[name] for name in regular},
|
|
219
|
+
hierarchical_modalities={name: modalities[name] for name in hierarchical}
|
|
220
|
+
or None,
|
|
221
|
+
transforms=[SamplePreprocessor.from_config(config["preprocessing"])]
|
|
222
|
+
if config.get("preprocessing")
|
|
223
|
+
else None,
|
|
224
|
+
)
|
|
@@ -13,8 +13,9 @@ except ImportError:
|
|
|
13
13
|
def extract_intrinsics(sample: dict) -> np.ndarray | None:
|
|
14
14
|
"""Extract the 3x3 intrinsics matrix from a sample dict.
|
|
15
15
|
|
|
16
|
-
|
|
17
|
-
|
|
16
|
+
Per-sample intrinsics are stored directly as ``sample["intrinsics"]``.
|
|
17
|
+
Hierarchical modalities are stored as ``sample[name][file_id]``; for
|
|
18
|
+
intrinsics the convention is ``sample["intrinsics"]["intrinsics"]``.
|
|
18
19
|
"""
|
|
19
20
|
intr_dict = sample.get("intrinsics")
|
|
20
21
|
if intr_dict is None:
|
|
@@ -98,7 +98,9 @@ def normalize_sky_mask(sky_mask) -> np.ndarray:
|
|
|
98
98
|
"""Normalise a sky mask to a 2-D boolean numpy array ``(H, W)``.
|
|
99
99
|
|
|
100
100
|
Handles torch tensors (GPU loaders) and the ``(1, H, W)`` channel-first
|
|
101
|
-
layout produced by euler-loading's GPU loaders.
|
|
101
|
+
layout produced by euler-loading's GPU loaders. A non-boolean input may
|
|
102
|
+
only hold 0/1: casting class IDs to bool would mark every non-zero class
|
|
103
|
+
as sky.
|
|
102
104
|
"""
|
|
103
105
|
mask = _to_numpy(sky_mask)
|
|
104
106
|
# CHW → take first channel: (C, H, W) → (H, W)
|
|
@@ -109,4 +111,15 @@ def normalize_sky_mask(sky_mask) -> np.ndarray:
|
|
|
109
111
|
mask = mask[0]
|
|
110
112
|
if mask.ndim == 3 and mask.shape[-1] == 1:
|
|
111
113
|
mask = mask[..., 0]
|
|
114
|
+
if mask.dtype != np.bool_ and np.any((mask != 0) & (mask != 1)):
|
|
115
|
+
values = np.unique(mask)
|
|
116
|
+
shown = ", ".join(str(value) for value in values[:8])
|
|
117
|
+
if values.size > 8:
|
|
118
|
+
shown += ", ..."
|
|
119
|
+
raise ValueError(
|
|
120
|
+
"semantic_segmentation must be a boolean sky mask, got a "
|
|
121
|
+
f"{mask.dtype} array with values [{shown}]. Load it with the "
|
|
122
|
+
"dataset's euler-loading sky_mask loader (the CLI selects it "
|
|
123
|
+
"automatically) or pass a boolean mask."
|
|
124
|
+
)
|
|
112
125
|
return mask.astype(bool)
|
|
@@ -12,6 +12,19 @@ class Transform(ABC):
|
|
|
12
12
|
Subclasses declare the modalities they need via class variables and
|
|
13
13
|
implement :meth:`run` to process samples.
|
|
14
14
|
|
|
15
|
+
Required modalities:
|
|
16
|
+
:attr:`REQUIRED_MODALITIES` are per-sample inputs (RGB, depth, masks);
|
|
17
|
+
they are always joined per sample and anchor the role inference of
|
|
18
|
+
every other modality. :attr:`REQUIRED_HIERARCHICAL_MODALITIES` are
|
|
19
|
+
further required inputs, such as camera intrinsics, that a dataset may
|
|
20
|
+
store per sample or once per hierarchy level; their role is inferred
|
|
21
|
+
from the ds-crawler indexes (see
|
|
22
|
+
:func:`~euler_preprocess.common.dataset.build_dataset`).
|
|
23
|
+
:attr:`MODALITY_FUNCTIONS` maps a modality to the euler-loading loader
|
|
24
|
+
function the transform needs, taken from the loader module its
|
|
25
|
+
dataset head names (``{"semantic_segmentation": "sky_mask"}`` reads a
|
|
26
|
+
class-segmentation index as sky masks).
|
|
27
|
+
|
|
15
28
|
Output slots:
|
|
16
29
|
Most transforms produce a single output (the *primary* slot, declared
|
|
17
30
|
via :attr:`OUTPUT_SLOT`). Transforms that produce additional auxiliary
|
|
@@ -23,6 +36,7 @@ class Transform(ABC):
|
|
|
23
36
|
|
|
24
37
|
REQUIRED_MODALITIES: ClassVar[set[str]] = set()
|
|
25
38
|
REQUIRED_HIERARCHICAL_MODALITIES: ClassVar[set[str]] = set()
|
|
39
|
+
MODALITY_FUNCTIONS: ClassVar[dict[str, str]] = {}
|
|
26
40
|
SOURCE_MODALITY: ClassVar[str | None] = None
|
|
27
41
|
OUTPUT_SLOT: ClassVar[str | None] = None
|
|
28
42
|
OUTPUT_SLOTS: ClassVar[tuple[str, ...]] = ()
|
{euler_preprocess-3.15.0 → euler_preprocess-3.17.0}/euler_preprocess/fog/atmospheric_light.py
RENAMED
|
@@ -124,7 +124,7 @@ class AtmosphericLightResolver:
|
|
|
124
124
|
airlight_is_estimated = uses_estimated_airlight(al_spec)
|
|
125
125
|
if airlight_is_estimated:
|
|
126
126
|
raw_ls_base = normalize_atmospheric_light_torch(
|
|
127
|
-
estimated_airlight_t
|
|
127
|
+
estimated_airlight_t, allow_8bit=False
|
|
128
128
|
).squeeze(0)
|
|
129
129
|
else:
|
|
130
130
|
sampled_al = sample_value(al_spec, rng)
|
|
@@ -156,7 +156,7 @@ class AtmosphericLightResolver:
|
|
|
156
156
|
airlight_is_estimated = uses_estimated_airlight(al_spec)
|
|
157
157
|
if airlight_is_estimated:
|
|
158
158
|
ls_base = self._estimate_batch_torch(rgb_batch, items, device, method)
|
|
159
|
-
ls_base = normalize_atmospheric_light_torch(ls_base)
|
|
159
|
+
ls_base = normalize_atmospheric_light_torch(ls_base, allow_8bit=False)
|
|
160
160
|
else:
|
|
161
161
|
ls_values = []
|
|
162
162
|
for item in items:
|
|
@@ -58,7 +58,7 @@ def render_fog_image(
|
|
|
58
58
|
mode: FogInferenceMode = "cpu",
|
|
59
59
|
sample_id: str | None = None,
|
|
60
60
|
sample_index: int = 0,
|
|
61
|
-
sky_class: Sequence[int | float] | None =
|
|
61
|
+
sky_class: Sequence[int | float] | None = None,
|
|
62
62
|
) -> np.ndarray:
|
|
63
63
|
"""Render and return only the RGB image for one in-memory sample."""
|
|
64
64
|
|
|
@@ -89,14 +89,15 @@ def render_fog_sample(
|
|
|
89
89
|
mode: FogInferenceMode = "cpu",
|
|
90
90
|
sample_id: str | None = None,
|
|
91
91
|
sample_index: int = 0,
|
|
92
|
-
sky_class: Sequence[int | float] | None =
|
|
92
|
+
sky_class: Sequence[int | float] | None = None,
|
|
93
93
|
) -> FogInferenceResult:
|
|
94
94
|
"""Render fog/camera artifacts for a single in-memory sample.
|
|
95
95
|
|
|
96
96
|
The call accepts the four modalities used by :class:`FogTransform` without
|
|
97
97
|
requiring a dataset reader or output backend. ``semantic_segmentation`` may
|
|
98
98
|
be either a boolean sky mask or an RGB semantic label map; RGB maps are
|
|
99
|
-
converted using ``sky_class
|
|
99
|
+
converted using ``sky_class``, the map's sky colour as its dataset head
|
|
100
|
+
declares it in ``skyclass`` (e.g. ``[29, 0, 0]`` for Real Drive Sim).
|
|
100
101
|
"""
|
|
101
102
|
|
|
102
103
|
if mode not in ("cpu", "gpu"):
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
3
|
import logging
|
|
4
|
+
from collections.abc import Sequence
|
|
5
|
+
from typing import Any
|
|
4
6
|
|
|
5
7
|
|
|
6
8
|
def log_config(
|
|
@@ -18,7 +20,14 @@ def log_config(
|
|
|
18
20
|
resize_depth: bool,
|
|
19
21
|
seed,
|
|
20
22
|
contrast_threshold_default: float,
|
|
23
|
+
scenarios: Sequence[tuple[str, Any, Any]] = (),
|
|
24
|
+
scenario_scope: str = "sample",
|
|
21
25
|
) -> None:
|
|
26
|
+
"""Log the resolved run settings.
|
|
27
|
+
|
|
28
|
+
*scenarios* holds ``(name, weight, model)`` per scenario profile, with
|
|
29
|
+
``model`` ``None`` when the profile defers to ``selection``.
|
|
30
|
+
"""
|
|
22
31
|
logger.info("Loaded config from %s -> output %s", config_path, out_path)
|
|
23
32
|
logger.info(
|
|
24
33
|
"Device: requested=%s active=%s use_gpu=%s torch=%s",
|
|
@@ -36,6 +45,22 @@ def log_config(
|
|
|
36
45
|
seed,
|
|
37
46
|
contrast_threshold_default,
|
|
38
47
|
)
|
|
48
|
+
if scenarios:
|
|
49
|
+
logger.info(
|
|
50
|
+
"Model selection: %d scenario_profiles (scope=%s) choose the model; "
|
|
51
|
+
"'selection' only applies to profiles without one",
|
|
52
|
+
len(scenarios),
|
|
53
|
+
scenario_scope,
|
|
54
|
+
)
|
|
55
|
+
for name, weight, model in scenarios:
|
|
56
|
+
logger.info(
|
|
57
|
+
"Scenario %s: weight=%s model=%s",
|
|
58
|
+
name,
|
|
59
|
+
weight,
|
|
60
|
+
"<selection>" if model is None else model,
|
|
61
|
+
)
|
|
62
|
+
if all(model is not None for _name, _weight, model in scenarios):
|
|
63
|
+
return
|
|
39
64
|
selection = config.get("selection")
|
|
40
65
|
if selection:
|
|
41
66
|
mode = selection.get("mode", "fixed")
|
|
@@ -160,18 +160,34 @@ def resolve_scattering_coefficient(
|
|
|
160
160
|
)
|
|
161
161
|
|
|
162
162
|
|
|
163
|
-
def normalize_atmospheric_light(
|
|
163
|
+
def normalize_atmospheric_light(
|
|
164
|
+
value: np.ndarray, *, allow_8bit: bool = True
|
|
165
|
+
) -> np.ndarray:
|
|
166
|
+
"""Return ``value`` as an RGB triple clipped to ``[0, 1]``.
|
|
167
|
+
|
|
168
|
+
With *allow_8bit*, values above 1 are read as an 8-bit colour and divided
|
|
169
|
+
by 255. That only suits configured colours: estimated airlight comes from
|
|
170
|
+
``[0, 1]`` RGB and may overshoot 1 slightly after the estimators' luminance
|
|
171
|
+
rescaling, so estimate callers pass ``allow_8bit=False`` to clip instead.
|
|
172
|
+
"""
|
|
164
173
|
value = np.asarray(value, dtype=np.float32)
|
|
165
174
|
if value.ndim == 0:
|
|
166
175
|
value = np.array([value, value, value], dtype=np.float32)
|
|
167
176
|
if value.ndim != 1 or value.shape[0] != 3:
|
|
168
177
|
raise ValueError("atmospheric_light must be scalar or length-3 list")
|
|
169
|
-
if value.max() > 1.0:
|
|
178
|
+
if allow_8bit and value.max() > 1.0:
|
|
170
179
|
value = value / 255.0
|
|
171
180
|
return np.clip(value, 0.0, 1.0)
|
|
172
181
|
|
|
173
182
|
|
|
174
|
-
def normalize_atmospheric_light_torch(
|
|
183
|
+
def normalize_atmospheric_light_torch(
|
|
184
|
+
value: "torch.Tensor", *, allow_8bit: bool = True
|
|
185
|
+
) -> "torch.Tensor":
|
|
186
|
+
"""Torch counterpart of :func:`normalize_atmospheric_light`.
|
|
187
|
+
|
|
188
|
+
The 8-bit check uses the maximum over every row, so stacked estimates must
|
|
189
|
+
pass ``allow_8bit=False`` or one overshooting sample rescales the batch.
|
|
190
|
+
"""
|
|
175
191
|
value_t = value.to(torch.float32)
|
|
176
192
|
if value_t.ndim == 0:
|
|
177
193
|
value_t = value_t.repeat(3)
|
|
@@ -180,7 +196,7 @@ def normalize_atmospheric_light_torch(value: "torch.Tensor") -> "torch.Tensor":
|
|
|
180
196
|
if value_t.ndim != 2 or value_t.shape[-1] != 3:
|
|
181
197
|
raise ValueError("atmospheric_light must be scalar or length-3 list")
|
|
182
198
|
max_val = float(value_t.max().item()) if value_t.numel() else 0.0
|
|
183
|
-
if max_val > 1.0:
|
|
199
|
+
if allow_8bit and max_val > 1.0:
|
|
184
200
|
value_t = value_t / 255.0
|
|
185
201
|
return torch.clamp(value_t, 0.0, 1.0)
|
|
186
202
|
|
|
@@ -1653,7 +1669,9 @@ def apply_model(
|
|
|
1653
1669
|
al_spec = model_cfg.get("atmospheric_light", "from_sky")
|
|
1654
1670
|
airlight_is_estimated = uses_estimated_airlight(al_spec)
|
|
1655
1671
|
if airlight_is_estimated:
|
|
1656
|
-
raw_ls_base = normalize_atmospheric_light(
|
|
1672
|
+
raw_ls_base = normalize_atmospheric_light(
|
|
1673
|
+
estimated_airlight, allow_8bit=False
|
|
1674
|
+
)
|
|
1657
1675
|
else:
|
|
1658
1676
|
sampled_al = sample_value(al_spec, rng)
|
|
1659
1677
|
raw_ls_base = normalize_atmospheric_light(np.asarray(sampled_al))
|