euler-preprocess 3.9.0__tar.gz → 3.11.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.11.0/PKG-INFO +655 -0
- euler_preprocess-3.11.0/README.md +641 -0
- {euler_preprocess-3.9.0 → euler_preprocess-3.11.0}/euler_preprocess/cli.py +0 -16
- {euler_preprocess-3.9.0 → euler_preprocess-3.11.0}/euler_preprocess/common/io.py +0 -11
- {euler_preprocess-3.9.0 → euler_preprocess-3.11.0}/euler_preprocess/fog/atmospheric_light.py +0 -6
- {euler_preprocess-3.9.0 → euler_preprocess-3.11.0}/euler_preprocess/fog/dcp_airlight_torch.py +0 -2
- {euler_preprocess-3.9.0 → euler_preprocess-3.11.0}/euler_preprocess/fog/dcp_heuristic_airlight.py +0 -4
- {euler_preprocess-3.9.0 → euler_preprocess-3.11.0}/euler_preprocess/fog/dcp_heuristic_airlight_torch.py +0 -2
- {euler_preprocess-3.9.0 → euler_preprocess-3.11.0}/euler_preprocess/fog/models.py +173 -1
- {euler_preprocess-3.9.0 → euler_preprocess-3.11.0}/euler_preprocess/fog/pipeline.py +3 -0
- {euler_preprocess-3.9.0 → euler_preprocess-3.11.0}/euler_preprocess/fog/transform.py +57 -90
- {euler_preprocess-3.9.0 → euler_preprocess-3.11.0}/euler_preprocess/radial/transform.py +0 -2
- euler_preprocess-3.11.0/euler_preprocess.egg-info/PKG-INFO +655 -0
- {euler_preprocess-3.9.0 → euler_preprocess-3.11.0}/euler_preprocess.egg-info/SOURCES.txt +1 -3
- {euler_preprocess-3.9.0 → euler_preprocess-3.11.0}/pyproject.toml +1 -1
- {euler_preprocess-3.9.0 → euler_preprocess-3.11.0}/tests/test_airlight_fallback.py +21 -61
- {euler_preprocess-3.9.0 → euler_preprocess-3.11.0}/tests/test_dcp_heuristic_airlight.py +8 -8
- {euler_preprocess-3.9.0 → euler_preprocess-3.11.0}/tests/test_fog_aux_outputs.py +103 -27
- euler_preprocess-3.11.0/tests/test_fog_integration.py +82 -0
- {euler_preprocess-3.9.0 → euler_preprocess-3.11.0}/tests/test_radial.py +0 -1
- {euler_preprocess-3.9.0 → euler_preprocess-3.11.0}/tests/test_sky_depth.py +0 -1
- {euler_preprocess-3.9.0 → euler_preprocess-3.11.0}/tests/test_zip_output.py +0 -1
- euler_preprocess-3.9.0/PKG-INFO +0 -831
- euler_preprocess-3.9.0/README.md +0 -817
- euler_preprocess-3.9.0/euler_preprocess/fog/foggify.py +0 -28
- euler_preprocess-3.9.0/euler_preprocess/fog/foggify_logging.py +0 -10
- euler_preprocess-3.9.0/euler_preprocess.egg-info/PKG-INFO +0 -831
- euler_preprocess-3.9.0/tests/test_foggify_integration.py +0 -119
- {euler_preprocess-3.9.0 → euler_preprocess-3.11.0}/MANIFEST.in +0 -0
- {euler_preprocess-3.9.0 → euler_preprocess-3.11.0}/euler_preprocess/__init__.py +0 -0
- {euler_preprocess-3.9.0 → euler_preprocess-3.11.0}/euler_preprocess/common/__init__.py +0 -0
- {euler_preprocess-3.9.0 → euler_preprocess-3.11.0}/euler_preprocess/common/color.py +0 -0
- {euler_preprocess-3.9.0 → euler_preprocess-3.11.0}/euler_preprocess/common/dataset.py +0 -0
- {euler_preprocess-3.9.0 → euler_preprocess-3.11.0}/euler_preprocess/common/device.py +0 -0
- {euler_preprocess-3.9.0 → euler_preprocess-3.11.0}/euler_preprocess/common/intrinsics.py +0 -0
- {euler_preprocess-3.9.0 → euler_preprocess-3.11.0}/euler_preprocess/common/logging.py +0 -0
- {euler_preprocess-3.9.0 → euler_preprocess-3.11.0}/euler_preprocess/common/noise.py +0 -0
- {euler_preprocess-3.9.0 → euler_preprocess-3.11.0}/euler_preprocess/common/normalize.py +0 -0
- {euler_preprocess-3.9.0 → euler_preprocess-3.11.0}/euler_preprocess/common/output.py +0 -0
- {euler_preprocess-3.9.0 → euler_preprocess-3.11.0}/euler_preprocess/common/sampling.py +0 -0
- {euler_preprocess-3.9.0 → euler_preprocess-3.11.0}/euler_preprocess/common/transform.py +0 -0
- {euler_preprocess-3.9.0 → euler_preprocess-3.11.0}/euler_preprocess/fog/__init__.py +0 -0
- {euler_preprocess-3.9.0 → euler_preprocess-3.11.0}/euler_preprocess/fog/airlight_from_sky.py +0 -0
- {euler_preprocess-3.9.0 → euler_preprocess-3.11.0}/euler_preprocess/fog/augmentations.py +0 -0
- {euler_preprocess-3.9.0 → euler_preprocess-3.11.0}/euler_preprocess/fog/capture.py +0 -0
- {euler_preprocess-3.9.0 → euler_preprocess-3.11.0}/euler_preprocess/fog/dcp_airlight.py +0 -0
- {euler_preprocess-3.9.0 → euler_preprocess-3.11.0}/euler_preprocess/fog/inference.py +0 -0
- {euler_preprocess-3.9.0 → euler_preprocess-3.11.0}/euler_preprocess/fog/logging.py +0 -0
- {euler_preprocess-3.9.0 → euler_preprocess-3.11.0}/euler_preprocess/radial/__init__.py +0 -0
- {euler_preprocess-3.9.0 → euler_preprocess-3.11.0}/euler_preprocess/sky_depth/__init__.py +0 -0
- {euler_preprocess-3.9.0 → euler_preprocess-3.11.0}/euler_preprocess/sky_depth/transform.py +0 -0
- {euler_preprocess-3.9.0 → euler_preprocess-3.11.0}/euler_preprocess.egg-info/dependency_links.txt +0 -0
- {euler_preprocess-3.9.0 → euler_preprocess-3.11.0}/euler_preprocess.egg-info/entry_points.txt +0 -0
- {euler_preprocess-3.9.0 → euler_preprocess-3.11.0}/euler_preprocess.egg-info/requires.txt +0 -0
- {euler_preprocess-3.9.0 → euler_preprocess-3.11.0}/euler_preprocess.egg-info/top_level.txt +0 -0
- {euler_preprocess-3.9.0 → euler_preprocess-3.11.0}/setup.cfg +0 -0
- {euler_preprocess-3.9.0 → euler_preprocess-3.11.0}/tests/test_cli_sample_selection.py +0 -0
- {euler_preprocess-3.9.0 → euler_preprocess-3.11.0}/tests/test_dense_gloomy_daylight_config.py +0 -0
- {euler_preprocess-3.9.0 → euler_preprocess-3.11.0}/tests/test_fog_aware_auto_exposure.py +0 -0
- {euler_preprocess-3.9.0 → euler_preprocess-3.11.0}/tests/test_fog_cpu_gpu_parity.py +0 -0
- {euler_preprocess-3.9.0 → euler_preprocess-3.11.0}/tests/test_real_drive_sim_scenario_profiles.py +0 -0
- {euler_preprocess-3.9.0 → euler_preprocess-3.11.0}/tests/test_scene_illumination.py +0 -0
- {euler_preprocess-3.9.0 → euler_preprocess-3.11.0}/tests/test_sensor_identity.py +0 -0
- {euler_preprocess-3.9.0 → euler_preprocess-3.11.0}/tests/test_source_backed_output.py +0 -0
- {euler_preprocess-3.9.0 → euler_preprocess-3.11.0}/tests/test_tone_map_lut.py +0 -0
|
@@ -0,0 +1,655 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: euler-preprocess
|
|
3
|
+
Version: 3.11.0
|
|
4
|
+
Summary: Physics-based preprocessing (fog, etc.) for RGB+depth datasets
|
|
5
|
+
Requires-Python: >=3.9
|
|
6
|
+
Description-Content-Type: text/markdown
|
|
7
|
+
Requires-Dist: numpy
|
|
8
|
+
Requires-Dist: Pillow
|
|
9
|
+
Requires-Dist: euler-loading
|
|
10
|
+
Provides-Extra: gpu
|
|
11
|
+
Requires-Dist: torch; extra == "gpu"
|
|
12
|
+
Provides-Extra: progress
|
|
13
|
+
Requires-Dist: tqdm; extra == "progress"
|
|
14
|
+
|
|
15
|
+
# euler-preprocess
|
|
16
|
+
|
|
17
|
+
Physics-based preprocessing transforms for multi-modal RGB+depth datasets — synthetic
|
|
18
|
+
fog, sky-depth normalisation, and planar-to-radial depth conversion. Built on
|
|
19
|
+
[euler-loading](https://github.com/d-rothen/euler-loading) and
|
|
20
|
+
[ds-crawler](https://github.com/d-rothen/ds-crawler).
|
|
21
|
+
|
|
22
|
+

|
|
23
|
+
|
|
24
|
+
*One input frame rendered at 100 m, 70 m, 40 m, 20 m and 10 m meteorological visibility
|
|
25
|
+
(`configs/fog_stepped_config.json`). Fog density follows the depth map, so the
|
|
26
|
+
attenuation is scene-consistent rather than a flat overlay.*
|
|
27
|
+
|
|
28
|
+

|
|
29
|
+
|
|
30
|
+
*The same frame through four `scenario_profiles` from
|
|
31
|
+
`configs/dense_gloomy_daylight_fog_camera.json`. Each profile samples fog density,
|
|
32
|
+
airlight, exposure, sensor noise and compression together, so weather and camera
|
|
33
|
+
response stay correlated.*
|
|
34
|
+
|
|
35
|
+
Both sets were rendered with `tools/run_fog_qualitative_samples.py`, which runs a
|
|
36
|
+
folder of RGB/depth/segmentation samples through the transform and writes images for
|
|
37
|
+
qualitative review.
|
|
38
|
+
|
|
39
|
+
| Command | Description |
|
|
40
|
+
|---|---|
|
|
41
|
+
| `euler-preprocess fog` | Synthetic fog via the Koschmieder atmospheric scattering model |
|
|
42
|
+
| `euler-preprocess sky-depth` | Override depth values in sky regions with a constant |
|
|
43
|
+
| `euler-preprocess radial` | Convert planar (z-buffer) depth to radial (Euclidean) depth |
|
|
44
|
+
|
|
45
|
+
## Installation
|
|
46
|
+
|
|
47
|
+
```bash
|
|
48
|
+
uv pip install "euler-preprocess[gpu,progress]"
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
## Usage
|
|
52
|
+
|
|
53
|
+
```bash
|
|
54
|
+
euler-preprocess fog -c configs/example_dataset_config.json
|
|
55
|
+
euler-preprocess sky-depth -c configs/sky_depth_dataset_config.json
|
|
56
|
+
euler-preprocess radial -c configs/radial_dataset_config.json
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
## Dataset Config
|
|
60
|
+
|
|
61
|
+
Every subcommand takes a **dataset config** that points to the input data and to a
|
|
62
|
+
**transform config**. Each modality path must be a directory indexed by
|
|
63
|
+
[ds-crawler](https://github.com/d-rothen/ds-crawler) with an `euler_loading` property
|
|
64
|
+
naming the loader and function, so euler-loading can auto-select the dataset-specific
|
|
65
|
+
loader.
|
|
66
|
+
|
|
67
|
+
```json
|
|
68
|
+
{
|
|
69
|
+
"transform_config_path": "fog_config.json",
|
|
70
|
+
"output_path": "/path/to/output",
|
|
71
|
+
"modalities": {
|
|
72
|
+
"rgb": {"path": "/path/to/rgb", "split": "train"},
|
|
73
|
+
"depth": "/path/to/depth",
|
|
74
|
+
"semantic_segmentation": "/path/to/classSegmentation"
|
|
75
|
+
},
|
|
76
|
+
"hierarchical_modalities": {
|
|
77
|
+
"intrinsics": {"path": "/path/to/intrinsics"}
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
| Field | Description |
|
|
83
|
+
|---|---|
|
|
84
|
+
| `transform_config_path` | Path to the transform-specific config, relative to this file. `fog_config_path` is also accepted. |
|
|
85
|
+
| `output_path` | Output root used when no pipeline target overrides it. Optional if `pipeline.output_root` or `pipeline.output_targets[].path` supplies the destination. |
|
|
86
|
+
| `output_slot` | Optional slot selector when `pipeline.output_targets` has several entries. Defaults to `rgb` for `fog` and `depth` for `sky-depth` / `radial`. |
|
|
87
|
+
| `sample` | Optional 0-based dataset index. Only `dataset[sample]` is transformed — useful for small benchmark slices of large datasets. |
|
|
88
|
+
| `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`. |
|
|
89
|
+
| `modalities` | Modalities that participate in sample-ID intersection. Each value is a path string or an object with `path` and optional `split`. |
|
|
90
|
+
| `hierarchical_modalities` | Per-scene data (e.g. intrinsics), same format. Loaded once per scene and cached. |
|
|
91
|
+
| `pipeline` | Optional runtime routing block compatible with `euler-inference`. |
|
|
92
|
+
|
|
93
|
+
**Required modalities per transform:**
|
|
94
|
+
|
|
95
|
+
| Transform | `modalities` | `hierarchical_modalities` |
|
|
96
|
+
|---|---|---|
|
|
97
|
+
| `fog` | `rgb`, `depth`, `semantic_segmentation` | `intrinsics` when available; used for radial depth conversion and camera-profile optics |
|
|
98
|
+
| `sky-depth` | `depth`, `semantic_segmentation` | — |
|
|
99
|
+
| `radial` | `depth` | `intrinsics` |
|
|
100
|
+
|
|
101
|
+
When a modality directory contains ds-crawler split files
|
|
102
|
+
(`.ds_crawler/split_<name>.json`), set `split` on that modality to select a subset.
|
|
103
|
+
Sample IDs are intersected across modalities, so a split on one modality restricts the
|
|
104
|
+
whole dataset.
|
|
105
|
+
|
|
106
|
+
### Pipeline Runtime Block
|
|
107
|
+
|
|
108
|
+
`pipeline` follows the same shape as `euler-inference`:
|
|
109
|
+
|
|
110
|
+
```json
|
|
111
|
+
{
|
|
112
|
+
"pipeline": {
|
|
113
|
+
"output_root": "/pipeline/output",
|
|
114
|
+
"outputs_manifest_path": "/pipeline/output/.euler_pipeline/pipeline_outputs.json",
|
|
115
|
+
"output_targets": [
|
|
116
|
+
{
|
|
117
|
+
"slot": "depth",
|
|
118
|
+
"datasetType": "depth",
|
|
119
|
+
"relativePath": "radial_depth.zip",
|
|
120
|
+
"path": "/pipeline/output/radial_depth.zip",
|
|
121
|
+
"storage": "zip"
|
|
122
|
+
}
|
|
123
|
+
]
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
- `output_root` is only a fallback when `output_path` is omitted.
|
|
129
|
+
- A matching `output_targets[].slot` overrides the write root for that run.
|
|
130
|
+
- `output_targets[].modelModalityId` is optional and passed through when present.
|
|
131
|
+
- `storage` is `"directory"` or `"zip"`; `"file"` parses but is rejected at runtime.
|
|
132
|
+
- When `outputs_manifest_path` is set and a target matches, finalization writes
|
|
133
|
+
`.euler_pipeline/pipeline_outputs.json` in the `euler-inference` manifest shape.
|
|
134
|
+
|
|
135
|
+
---
|
|
136
|
+
|
|
137
|
+
## Fog Transform
|
|
138
|
+
|
|
139
|
+
### Fog Config
|
|
140
|
+
|
|
141
|
+
```json
|
|
142
|
+
{
|
|
143
|
+
"airlight": "from_sky",
|
|
144
|
+
"seed": 1337,
|
|
145
|
+
"depth_scale": 1.0,
|
|
146
|
+
"resize_depth": true,
|
|
147
|
+
"contrast_threshold": 0.05,
|
|
148
|
+
"render_input_space": "srgb",
|
|
149
|
+
"mode": "sample",
|
|
150
|
+
"device": "cpu",
|
|
151
|
+
"gpu_batch_size": 4,
|
|
152
|
+
"capture": { "preset": "camera" },
|
|
153
|
+
"camera_profile": "dashcam",
|
|
154
|
+
"selection": { },
|
|
155
|
+
"models": { }
|
|
156
|
+
}
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
| Field | Description |
|
|
160
|
+
|---|---|
|
|
161
|
+
| `airlight` | **Required.** `"from_sky"` (mean sky colour), `"dcp"` (dark channel prior), or `"dcp_heuristic"` (robust DCP with sky-guided colouring). |
|
|
162
|
+
| `seed` | Random seed for reproducibility; `null` for non-deterministic. |
|
|
163
|
+
| `depth_scale` | Multiplier applied to depth values after loading. |
|
|
164
|
+
| `resize_depth` | Bilinearly resize the depth map to the RGB resolution. |
|
|
165
|
+
| `contrast_threshold` | Threshold *C_t* in the visibility-to-attenuation conversion (default `0.05`). |
|
|
166
|
+
| `render_input_space` | Colour space of the input RGB. `"srgb"` for display-encoded images (fog is mixed in scene-linear RGB); `"linear"` for already-linear radiance. |
|
|
167
|
+
| `mode` | `"sample"` (default) renders one sampled scenario per image; `"progressive"` renders every scenario step for every image. |
|
|
168
|
+
| `device` | `"cpu"`, `"cuda"`, `"mps"`, or `"gpu"` (alias for cuda). |
|
|
169
|
+
| `gpu_batch_size` | Batch size on GPU. Uniform-model samples are batched; heterogeneous ones run individually. |
|
|
170
|
+
| `capture` | Post-fog camera artifact pipeline. Omit or use `{"stages": []}` for a no-op; `true` or `{"preset": "camera"}` enables the recommended stack. |
|
|
171
|
+
| `camera_profile` | Named or inline camera profile merged into the capture stack before per-stage overrides. Built-ins: `"default"`, `"generic"`, `"dashcam"`, `"low_light_fog"`. |
|
|
172
|
+
| `camera_profiles` | Map of project-specific named profiles for calibrated lens/sensor/ISP/transport settings. |
|
|
173
|
+
| `scenario_profiles` | Top-level correlated condition sampler (see below). |
|
|
174
|
+
| `selection` | Per-image fog model selection (see below). |
|
|
175
|
+
| `augmentations` | Stepped augmentation set. Every input produces every configured variant. |
|
|
176
|
+
|
|
177
|
+
### Fog Model
|
|
178
|
+
|
|
179
|
+
The core equation is the **Koschmieder model**:
|
|
180
|
+
|
|
181
|
+
```
|
|
182
|
+
I_fog(x) = I(x) * t(x) + L_s * (1 - t(x))
|
|
183
|
+
```
|
|
184
|
+
|
|
185
|
+
- **I(x)** — original RGB colour at pixel *x*
|
|
186
|
+
- **t(x) = exp(-k * d(x))** — transmittance, falling exponentially with depth *d*
|
|
187
|
+
- **L_s** — atmospheric light (airlight): the colour of light scattered toward the camera
|
|
188
|
+
- **k** — attenuation, derived from meteorological visibility *V* as `k = -ln(C_t) / V`
|
|
189
|
+
|
|
190
|
+
Distant objects are attenuated more (`t` approaches 0) and replaced by airlight, just
|
|
191
|
+
as in real fog.
|
|
192
|
+
|
|
193
|
+
Rendering happens in two phases. First the *ideal scene* is rendered: physics-based fog
|
|
194
|
+
plus auxiliary `scattering_coefficient` and `atmospheric_light` maps. Then *capture
|
|
195
|
+
artifacts* are applied to the rendered RGB only. Physical fog maps therefore stay
|
|
196
|
+
stable while the RGB output can receive exposure shifts, lens blur, sensor noise, ISP
|
|
197
|
+
processing, and compression.
|
|
198
|
+
|
|
199
|
+
### How Each Modality Is Used
|
|
200
|
+
|
|
201
|
+
**RGB** — the clean scene image, normalised to float32 in [0, 1]. This is *I(x)*.
|
|
202
|
+
|
|
203
|
+
**Depth** — a per-pixel depth map in **metres**, providing *d(x)*. Invalid values (NaN,
|
|
204
|
+
inf, negative) are clamped to zero, i.e. treated as infinitely close and receiving no
|
|
205
|
+
fog. Depth stays authoritative even where the semantic map says sky, unless
|
|
206
|
+
`sky_fog_path` is configured.
|
|
207
|
+
|
|
208
|
+
**Semantic Segmentation** — a per-pixel semantic map from which a boolean sky mask is
|
|
209
|
+
derived. Used for airlight estimation when `airlight` is `"from_sky"`: the mean RGB of
|
|
210
|
+
all sky pixels becomes *L_s*.
|
|
211
|
+
|
|
212
|
+
**Intrinsics** *(optional)* — when present, planar (z-buffer) depth is converted to
|
|
213
|
+
radial (Euclidean) depth before fog is applied.
|
|
214
|
+
|
|
215
|
+
For a bounded valley-fog treatment of sky, add `sky_fog_path` inside a model config:
|
|
216
|
+
|
|
217
|
+
```json
|
|
218
|
+
"sky_fog_path": {
|
|
219
|
+
"mode": "layer",
|
|
220
|
+
"camera_height_m": 1.6,
|
|
221
|
+
"fog_valley_peak_m": 80.0,
|
|
222
|
+
"camera_pitch_deg": 0.0,
|
|
223
|
+
"camera_roll_deg": 0.0,
|
|
224
|
+
"density_profile": "linear_fade",
|
|
225
|
+
"transition_height_m": 10.0,
|
|
226
|
+
"max_path_m": 1000.0
|
|
227
|
+
}
|
|
228
|
+
```
|
|
229
|
+
|
|
230
|
+
Pixel rays are reconstructed from the intrinsics: near-horizon rays accumulate long
|
|
231
|
+
paths through the fog layer while upward rays exit it sooner. `linear_fade` keeps full
|
|
232
|
+
density below `transition_height_m` and decreases linearly to zero at
|
|
233
|
+
`fog_valley_peak_m`; `uniform` keeps full density up to the top. Positive
|
|
234
|
+
`camera_pitch_deg` points the optical axis upward, and `max_path_m` bounds horizon
|
|
235
|
+
rays. The original depth is retained for non-sky pixels and capture effects. Enabling
|
|
236
|
+
this without intrinsics raises an error rather than silently falling back.
|
|
237
|
+
|
|
238
|
+
### Airlight Estimation
|
|
239
|
+
|
|
240
|
+
| Method | Description |
|
|
241
|
+
|---|---|
|
|
242
|
+
| `from_sky` | Mean RGB of sky pixels. Falls back to white `[1, 1, 1]` when no sky pixels exist. |
|
|
243
|
+
| `dcp` | Dark Channel Prior — brightest pixel (by channel sum) among the top 0.1% darkest-channel pixels. |
|
|
244
|
+
| `dcp_heuristic` | Robust DCP — pools the brighter half of the top 0.1% darkest-channel pixels; when sky pixels exist their brightest colours act as a chromaticity prior while DCP-derived luminance is preserved. |
|
|
245
|
+
|
|
246
|
+
GPU-native implementations are selected automatically when running on GPU. With
|
|
247
|
+
`dcp_heuristic` you can add:
|
|
248
|
+
|
|
249
|
+
```json
|
|
250
|
+
"dcp_heuristic": {
|
|
251
|
+
"patch_size": 15,
|
|
252
|
+
"top_percent": 0.001,
|
|
253
|
+
"white_bias": 0.1,
|
|
254
|
+
"cool_bias": 0.15,
|
|
255
|
+
"cool_target": [0.93, 0.97, 1.0]
|
|
256
|
+
}
|
|
257
|
+
```
|
|
258
|
+
|
|
259
|
+
`white_bias` mixes the result toward neutral white and `cool_bias` toward a
|
|
260
|
+
sky-relative cool target derived from `cool_target`; their sum must be `<= 1`. The tint
|
|
261
|
+
bias preserves luminance, so it shifts colour without changing fog density.
|
|
262
|
+
|
|
263
|
+
#### Intensity dampening
|
|
264
|
+
|
|
265
|
+
Estimated airlight is dampened by default as fog density increases, keeping strong fog
|
|
266
|
+
closer to the low grey lighting seen in real in-car footage instead of washing toward
|
|
267
|
+
white. Each model can override the curve:
|
|
268
|
+
|
|
269
|
+
```json
|
|
270
|
+
"airlight_dampening": {
|
|
271
|
+
"enabled": true,
|
|
272
|
+
"apply_to": "estimated",
|
|
273
|
+
"reference_visibility_m": 80.0,
|
|
274
|
+
"min_factor": 0.45,
|
|
275
|
+
"max_factor": 1.0,
|
|
276
|
+
"strength": 1.0
|
|
277
|
+
}
|
|
278
|
+
```
|
|
279
|
+
|
|
280
|
+
The factor is
|
|
281
|
+
`min_factor + (max_factor - min_factor) / (1 + strength * beta / reference_beta)`, where
|
|
282
|
+
`reference_beta` comes from `reference_scattering_coefficient` / `reference_beta` or is
|
|
283
|
+
derived from `reference_visibility_m`. Values above `1.0` are allowed when you want to
|
|
284
|
+
brighten estimated airlight; the final RGB is still clamped. The default applies only
|
|
285
|
+
to estimated airlight methods — literal RGB `atmospheric_light` values stay exact
|
|
286
|
+
unless `apply_to` is `"all"`. Set `"enabled": false` or `apply_to: "none"` to disable.
|
|
287
|
+
|
|
288
|
+
For `heterogeneous_ls` and `heterogeneous_k_ls`, the Perlin atmospheric-light field is
|
|
289
|
+
sampled around the dampened base airlight.
|
|
290
|
+
|
|
291
|
+
### Model Selection
|
|
292
|
+
|
|
293
|
+
```json
|
|
294
|
+
"selection": {
|
|
295
|
+
"mode": "weighted",
|
|
296
|
+
"weights": {
|
|
297
|
+
"uniform": 0.25,
|
|
298
|
+
"heterogeneous_k": 0.35,
|
|
299
|
+
"heterogeneous_ls": 0.25,
|
|
300
|
+
"heterogeneous_k_ls": 0.15
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
```
|
|
304
|
+
|
|
305
|
+
`fixed` mode always uses a single named model; `weighted` picks one per image according
|
|
306
|
+
to normalised weights.
|
|
307
|
+
|
|
308
|
+
| Model | Description |
|
|
309
|
+
|---|---|
|
|
310
|
+
| `uniform` | Constant *k* and *L_s*. Standard homogeneous fog. |
|
|
311
|
+
| `heterogeneous_k` | Spatially-varying *k*, constant *L_s*. Patchy fog / fog banks. |
|
|
312
|
+
| `heterogeneous_ls` | Constant *k*, spatially-varying *L_s*. Scattered-light colour variation. |
|
|
313
|
+
| `heterogeneous_k_ls` | Both vary spatially. Most expressive model. |
|
|
314
|
+
|
|
315
|
+
Each model samples a `visibility_m` distribution per image:
|
|
316
|
+
|
|
317
|
+
| `dist` | Parameters |
|
|
318
|
+
|---|---|
|
|
319
|
+
| `constant` | `value` |
|
|
320
|
+
| `uniform` | `min`, `max` |
|
|
321
|
+
| `normal` | `mean`, `std`, optional `min`/`max` |
|
|
322
|
+
| `lognormal` | `mean`, `sigma`, optional `min`/`max` |
|
|
323
|
+
| `choice` | `values`, optional `weights` |
|
|
324
|
+
|
|
325
|
+
The sampled visibility *V* becomes `k = -ln(C_t) / V`, once per output image. For the
|
|
326
|
+
heterogeneous-*k* models that value is the base coefficient, which the noise field then
|
|
327
|
+
modulates spatially.
|
|
328
|
+
|
|
329
|
+
### Heterogeneous Noise Fields
|
|
330
|
+
|
|
331
|
+
`k_hetero` and `ls_hetero` use Perlin FBM to generate spatially-varying factor fields.
|
|
332
|
+
For realistic fog, prefer the smooth mode: Perlin wavelengths stay tied to the image
|
|
333
|
+
size, noise contrast is reduced, and an optional blur is applied before mapping noise
|
|
334
|
+
to physical factors.
|
|
335
|
+
|
|
336
|
+
```json
|
|
337
|
+
"k_hetero": {
|
|
338
|
+
"scales": "smooth_auto",
|
|
339
|
+
"correlation_length_fraction": 0.25,
|
|
340
|
+
"octaves": 3,
|
|
341
|
+
"max_scale": null,
|
|
342
|
+
"min_factor": 0.65,
|
|
343
|
+
"max_factor": 1.45,
|
|
344
|
+
"contrast": 0.65,
|
|
345
|
+
"smooth_sigma_fraction": 0.0,
|
|
346
|
+
"normalize_to_mean": true
|
|
347
|
+
}
|
|
348
|
+
```
|
|
349
|
+
|
|
350
|
+
| Parameter | Effect |
|
|
351
|
+
|---|---|
|
|
352
|
+
| `min_factor` / `max_factor` | Range of the multiplicative factor. |
|
|
353
|
+
| `normalize_to_mean` | Rescale factors so the image-wide mean equals the base value. Recommended for `k_hetero`. |
|
|
354
|
+
| `scales: "smooth_auto"` | Build low-frequency Perlin scales from the image size. |
|
|
355
|
+
| `correlation_length_fraction` | Smallest fog feature size as a fraction of the shorter image side. Larger is smoother. |
|
|
356
|
+
| `octaves` / `lacunarity` / `max_scale` | How many increasingly broad Perlin components are mixed. |
|
|
357
|
+
| `contrast` | Compress or expand the Perlin range before mapping to factors. Below 1 recommended. |
|
|
358
|
+
| `smooth_sigma` / `smooth_sigma_fraction` | Optional final Gaussian blur, in pixels or as a fraction of the shorter side. |
|
|
359
|
+
| `ls_gradient` | Optional `L_s` top-to-bottom or left-to-right factor field. Keep it weak and probabilistic so it does not become an image-position shortcut. |
|
|
360
|
+
|
|
361
|
+
The noise field (in [0, 1]) maps to
|
|
362
|
+
`factor(x) = min_factor + (max_factor - min_factor) * noise(x)`, and with heterogeneous
|
|
363
|
+
*k* the result is `k(x) = k_sampled * factor(x)`. With `normalize_to_mean: true` the
|
|
364
|
+
arithmetic mean of the per-pixel *k* map equals `k_sampled` (the median is not forced to
|
|
365
|
+
match); with `false`, the map mean shifts by the mean of the factor field.
|
|
366
|
+
|
|
367
|
+
`ls_hetero` can add a weak view-direction illumination prior that modulates the
|
|
368
|
+
atmospheric-light field itself, so the rendered effect is still gated by transmittance:
|
|
369
|
+
|
|
370
|
+
```json
|
|
371
|
+
"ls_hetero": {
|
|
372
|
+
"ls_gradient": {
|
|
373
|
+
"enabled": true,
|
|
374
|
+
"probability": 0.65,
|
|
375
|
+
"axis": "vertical",
|
|
376
|
+
"top_factor": {"dist": "uniform", "min": 1.03, "max": 1.14},
|
|
377
|
+
"bottom_factor": {"dist": "uniform", "min": 0.88, "max": 0.99},
|
|
378
|
+
"gamma": {"dist": "uniform", "min": 0.85, "max": 1.6},
|
|
379
|
+
"normalize_to_mean": true,
|
|
380
|
+
"fog_opacity_weight": 0.65
|
|
381
|
+
}
|
|
382
|
+
}
|
|
383
|
+
```
|
|
384
|
+
|
|
385
|
+
### Scene Illumination
|
|
386
|
+
|
|
387
|
+
For gloomy conditions, add `scene_illumination` inside a fog model config. It darkens
|
|
388
|
+
pre-fog scene radiance *I(x)* before the scattering equation, so near objects become
|
|
389
|
+
plausibly overcast or storm-lit instead of passing through unchanged. `global_ev`
|
|
390
|
+
applies to the whole non-sky scene, `near_ev` adds near-field darkening with
|
|
391
|
+
`near_decay_depth_m`, `fog_coupled_ev` adds a term proportional to local fog opacity,
|
|
392
|
+
and `sky_weight: 0.0` preserves sky pixels when a sky mask is available.
|
|
393
|
+
|
|
394
|
+
### Capture Artifact Stack
|
|
395
|
+
|
|
396
|
+
Enable the recommended camera stack with `"capture": {"preset": "camera"}` (or
|
|
397
|
+
`"capture": true`). For tighter control, list explicit stages in camera order:
|
|
398
|
+
|
|
399
|
+
```json
|
|
400
|
+
"capture": {
|
|
401
|
+
"stages": [
|
|
402
|
+
{
|
|
403
|
+
"type": "optics",
|
|
404
|
+
"blur_sigma": {"dist": "uniform", "min": 0.2, "max": 0.8},
|
|
405
|
+
"vignetting_strength": 0.15,
|
|
406
|
+
"windshield_haze": {"enabled": true, "probability": 0.4}
|
|
407
|
+
},
|
|
408
|
+
{
|
|
409
|
+
"type": "sensor",
|
|
410
|
+
"input_space": "srgb",
|
|
411
|
+
"exposure_gain": {"dist": "uniform", "min": 0.85, "max": 1.2},
|
|
412
|
+
"row_noise_sigma": 0.003
|
|
413
|
+
},
|
|
414
|
+
{"type": "isp", "tone_map": "reinhard", "gamma": "srgb", "sharpen_amount": 0.2},
|
|
415
|
+
{
|
|
416
|
+
"type": "transport",
|
|
417
|
+
"jpeg": {"enabled": true, "quality": {"dist": "uniform", "min": 65, "max": 92}},
|
|
418
|
+
"bit_depth": 8
|
|
419
|
+
}
|
|
420
|
+
]
|
|
421
|
+
}
|
|
422
|
+
```
|
|
423
|
+
|
|
424
|
+
| Stage | Main effects |
|
|
425
|
+
|---|---|
|
|
426
|
+
| `optics` | Defocus/MTF blur, motion blur, bloom, veiling glare, vignetting, chromatic aberration, lens distortion, windshield haze, optional droplets. |
|
|
427
|
+
| `sensor` | Image-driven or sampled exposure, white balance, camera matrix, Bayer mosaic, shot/read noise, fixed-pattern noise, row/column banding, shadow-local recovery noise, hot/dead pixels, bilinear demosaic. |
|
|
428
|
+
| `isp` | Denoising, colour correction, tone mapping, sRGB/gamma, local contrast, sharpening halos, saturation shifts. |
|
|
429
|
+
| `transport` | Crop/resize, bit-depth quantization, JPEG round-trip compression. |
|
|
430
|
+
| `exposure` | Lightweight standalone exposure and white-balance stage for simple custom chains. |
|
|
431
|
+
|
|
432
|
+
`camera_profiles` holds reusable named versions of the same settings; see
|
|
433
|
+
`configs/dense_gloomy_daylight_fog_camera.json` for a fully specified dashcam profile.
|
|
434
|
+
|
|
435
|
+
Four opt-in `sensor` blocks cover most of the realism tuning:
|
|
436
|
+
|
|
437
|
+
| Block | Purpose | Key settings |
|
|
438
|
+
|---|---|---|
|
|
439
|
+
| `auto_exposure` | Meters the rendered image before raw sensor sampling. `exposure_gain` still applies on top as scenario compensation. | `target_luminance`, `metering`, `highlight_*`, gain bounds, `resolve_iso` (raises ISO from metering pressure, dark pixel fraction, and fog opacity) |
|
|
440
|
+
| `sensor_identity` | Persistent sensor structure across frames, deterministic per `sensor_id` / `seed` / shape / Bayer pattern. | `prnu_sigma` (pixel-response non-uniformity before shot noise), `dsnu_sigma`, `persistent_row_sigma`, `persistent_column_sigma`, persistent hot/dead pixel probabilities |
|
|
441
|
+
| `shadow_recovery_noise` | Corrupts luma and chroma only where pre-exposure luminance was low — less global grain, visibly noisy lifted shadows. | `luma_sigma`, `chroma_sigma`, `chroma_mode`, `red_chroma_gain`, `blue_chroma_gain`, `chroma_axis_correlation`, `black_noise_floor`, `black_suppression_*` |
|
|
442
|
+
| `noise_adjustment` | Scales the selected profile's noise relatively. | `level` (`1.0` = unchanged), `static_chroma_bias` from `-1.0` (fixed-pattern, banding, bad pixels) to `1.0` (chromatic high-ISO shadow noise) |
|
|
443
|
+
|
|
444
|
+
The fog-aware metering modes (`"fog_aware_center_weighted"`,
|
|
445
|
+
`"sky_aware_center_weighted"`) additionally read `CaptureContext.depth_m`, `k_map`, fog
|
|
446
|
+
opacity, and `attributes.sky_mask`. Tune `sky_suppression`, `fog_meter_suppression`,
|
|
447
|
+
`depth_meter_decay_m`, and `min_meter_weight` to keep bright sky or dense far-field
|
|
448
|
+
airlight from dominating the meter; legacy metering modes are unchanged unless these
|
|
449
|
+
keys are present.
|
|
450
|
+
|
|
451
|
+
For dark high-ISO scenes, keep `shadow_recovery_noise.luma_sigma` well below
|
|
452
|
+
`chroma_sigma`, use `chroma_mode: "balanced"`, and leave
|
|
453
|
+
`chroma_luminance_preservation` near `1.0` so the corruption reads as colour noise
|
|
454
|
+
rather than black speckle. Keep `chroma_spatial_sigma` near `0` for fine-grained rather
|
|
455
|
+
than blocky noise.
|
|
456
|
+
|
|
457
|
+
**Condition profiles.** Any stage can define `condition_profiles` to sample coherent
|
|
458
|
+
per-image settings before it runs — useful when ISO, exposure gain, read noise, banding,
|
|
459
|
+
and dark/fog modulation should move together:
|
|
460
|
+
|
|
461
|
+
```json
|
|
462
|
+
{
|
|
463
|
+
"type": "sensor",
|
|
464
|
+
"condition_profiles": [
|
|
465
|
+
{"name": "clean_daylight", "weight": 0.25, "exposure_gain": 1.0, "iso": 100},
|
|
466
|
+
{"name": "underexposed_noisy", "weight": 0.25, "exposure_gain": 0.65, "iso": 1600}
|
|
467
|
+
]
|
|
468
|
+
}
|
|
469
|
+
```
|
|
470
|
+
|
|
471
|
+
**Tone mapping.** `isp.tone_map` supports `"reinhard"`, `"aces"`, `"clip"`, and
|
|
472
|
+
`"lut"`. The LUT mode interpolates a cheap 1D camera-response curve given by
|
|
473
|
+
`tone_map_lut`, scaled by `tone_map_strength` and interpreted in
|
|
474
|
+
`tone_map_lut_domain`.
|
|
475
|
+
|
|
476
|
+
### Scenario Profiles
|
|
477
|
+
|
|
478
|
+
Top-level `scenario_profiles` sample one latent scene/camera condition before rendering.
|
|
479
|
+
The selected scenario is merged over the root config, so it can drive fog density,
|
|
480
|
+
atmospheric light, camera profile, capture overrides, ISP, and compression together:
|
|
481
|
+
|
|
482
|
+
```json
|
|
483
|
+
"scenario_profiles": [
|
|
484
|
+
{
|
|
485
|
+
"name": "underexposed_dense_gloom",
|
|
486
|
+
"weight": 0.25,
|
|
487
|
+
"model": "heterogeneous_k_ls",
|
|
488
|
+
"airlight_method": "dcp_heuristic",
|
|
489
|
+
"models": {
|
|
490
|
+
"heterogeneous_k_ls": {
|
|
491
|
+
"visibility_m": {"dist": "uniform", "min": 18.0, "max": 55.0},
|
|
492
|
+
"scene_illumination": {
|
|
493
|
+
"enabled": true,
|
|
494
|
+
"global_ev": {"dist": "uniform", "min": 0.25, "max": 0.85},
|
|
495
|
+
"near_ev": {"dist": "uniform", "min": 0.35, "max": 1.20},
|
|
496
|
+
"near_decay_depth_m": {"dist": "uniform", "min": 10.0, "max": 22.0},
|
|
497
|
+
"fog_coupled_ev": {"dist": "uniform", "min": 0.10, "max": 0.45},
|
|
498
|
+
"sky_weight": 0.0
|
|
499
|
+
}
|
|
500
|
+
}
|
|
501
|
+
},
|
|
502
|
+
"capture_overrides": {
|
|
503
|
+
"sensor": {
|
|
504
|
+
"condition_profile": "underexposed_noisy",
|
|
505
|
+
"auto_exposure": {
|
|
506
|
+
"enabled": true,
|
|
507
|
+
"metering": "fog_aware_center_weighted",
|
|
508
|
+
"target_luminance": {"dist": "uniform", "min": 0.13, "max": 0.20},
|
|
509
|
+
"sky_suppression": 0.85,
|
|
510
|
+
"fog_meter_suppression": 0.65
|
|
511
|
+
}
|
|
512
|
+
},
|
|
513
|
+
"transport": {"jpeg": {"quality": {"dist": "uniform", "min": 54, "max": 78}}}
|
|
514
|
+
}
|
|
515
|
+
}
|
|
516
|
+
]
|
|
517
|
+
```
|
|
518
|
+
|
|
519
|
+
`capture_overrides` is merged after camera-profile and stage settings. Use
|
|
520
|
+
`condition_profile` to force one named profile from a stage's `condition_profiles`; if
|
|
521
|
+
omitted, the stage keeps sampling its own weights locally.
|
|
522
|
+
`configs/dense_gloomy_daylight_fog_camera.json` contains a complete six-profile set
|
|
523
|
+
covering clear weather through severe sensor stress — the profiles shown in the header
|
|
524
|
+
images.
|
|
525
|
+
|
|
526
|
+
### Progressive Mode
|
|
527
|
+
|
|
528
|
+
Set `"mode": "progressive"` to emit every configured scenario for every input image
|
|
529
|
+
instead of sampling one. Each scenario accepts `"steps"` and `"progressive_weight"`
|
|
530
|
+
(aliases: `"max_weight"`, `"weight"`); the transform writes steps from weight `0`
|
|
531
|
+
through the scenario's configured weight, where weight `1` matches the original
|
|
532
|
+
scenario. Fog density progresses in scattering-coefficient space while numeric
|
|
533
|
+
camera/config values blend from the base config toward the scenario config. Blends
|
|
534
|
+
clamp probability-like values and non-negative physical factors back into valid domains,
|
|
535
|
+
so extrapolated weights above `1` cannot produce invalid render parameters.
|
|
536
|
+
Source-backed outputs are written as `fog_progression` variants under each source file
|
|
537
|
+
id.
|
|
538
|
+
|
|
539
|
+
### Stepped Augmentations
|
|
540
|
+
|
|
541
|
+
For benchmark generation, set `augmentations`. The fog transform then produces one
|
|
542
|
+
output per configured variant instead of one sampled output per input:
|
|
543
|
+
|
|
544
|
+
```json
|
|
545
|
+
{
|
|
546
|
+
"airlight": "from_sky",
|
|
547
|
+
"seed": 1337,
|
|
548
|
+
"contrast_threshold": 0.05,
|
|
549
|
+
"augmentations": {
|
|
550
|
+
"file_id_hierarchy_name": "file_id",
|
|
551
|
+
"attribute_key": "fog_augmentation",
|
|
552
|
+
"models": ["uniform"],
|
|
553
|
+
"visibility_m": [10, 20, 40, 70, 100],
|
|
554
|
+
"airlight_methods": ["from_sky"]
|
|
555
|
+
}
|
|
556
|
+
}
|
|
557
|
+
```
|
|
558
|
+
|
|
559
|
+
The matrix form expands as the Cartesian product of `models`, `visibility_m` (MOR in
|
|
560
|
+
metres), optional `scattering_coefficients` / `beta`, and airlight choices.
|
|
561
|
+
`file_id_hierarchy_name` names the inserted hierarchy level when the ds-crawler writer
|
|
562
|
+
has a hierarchy separator; the directory name is the source file id either way. For
|
|
563
|
+
tighter control, use explicit `variants` instead:
|
|
564
|
+
|
|
565
|
+
```json
|
|
566
|
+
"augmentations": {
|
|
567
|
+
"variants": [
|
|
568
|
+
{
|
|
569
|
+
"id": "mor_010m_sky",
|
|
570
|
+
"model": "uniform",
|
|
571
|
+
"visibility_m": 10,
|
|
572
|
+
"airlight_method": "from_sky"
|
|
573
|
+
},
|
|
574
|
+
{
|
|
575
|
+
"id": "beta_0.15_white",
|
|
576
|
+
"model": "heterogeneous_k",
|
|
577
|
+
"scattering_coefficient": 0.15,
|
|
578
|
+
"atmospheric_light": [1.0, 1.0, 1.0],
|
|
579
|
+
"k_hetero": {"scales": "smooth_auto", "min_factor": 0.65, "max_factor": 1.45}
|
|
580
|
+
}
|
|
581
|
+
]
|
|
582
|
+
}
|
|
583
|
+
```
|
|
584
|
+
|
|
585
|
+
Each output receives per-file ds-crawler attributes under `fog_augmentation` — the
|
|
586
|
+
augmentation id, source id and full id, model, actual scattering coefficient, actual
|
|
587
|
+
atmospheric light, and configured MOR/beta descriptors. euler-loading exposes these as
|
|
588
|
+
`sample["attributes"]["rgb"]["fog_augmentation"]`.
|
|
589
|
+
|
|
590
|
+
### Output
|
|
591
|
+
|
|
592
|
+
CLI runs write a source-backed RGB dataset that keeps the source dataset's relative
|
|
593
|
+
paths, basenames, extensions, and ds-crawler metadata, so the result stays loadable by
|
|
594
|
+
euler-loading:
|
|
595
|
+
|
|
596
|
+
```
|
|
597
|
+
<output_path>/
|
|
598
|
+
.ds_crawler/dataset-head.json
|
|
599
|
+
.ds_crawler/ds-crawler.json
|
|
600
|
+
.ds_crawler/index.json
|
|
601
|
+
Scene01/Camera_0/00000.png
|
|
602
|
+
```
|
|
603
|
+
|
|
604
|
+
With `augmentations` enabled, outputs are written one level below the source file id:
|
|
605
|
+
|
|
606
|
+
```
|
|
607
|
+
<output_path>/
|
|
608
|
+
Scene01/Camera_0/00000/
|
|
609
|
+
mor_10m_airlight_from_sky.png
|
|
610
|
+
mor_20m_airlight_from_sky.png
|
|
611
|
+
```
|
|
612
|
+
|
|
613
|
+
Auxiliary `scattering_coefficient` and `atmospheric_light` pipeline targets use the same
|
|
614
|
+
file-id hierarchy and write matching `.npy` files.
|
|
615
|
+
|
|
616
|
+
When a pipeline target is present, `pipeline.output_targets[].path` replaces
|
|
617
|
+
`output_path` entirely. Standalone `FogTransform(...)` usage without the CLI keeps the
|
|
618
|
+
legacy per-model layout with `config.json` sidecars.
|
|
619
|
+
|
|
620
|
+
---
|
|
621
|
+
|
|
622
|
+
## Sky-Depth Transform
|
|
623
|
+
|
|
624
|
+
Overrides depth values in sky regions with a configurable constant. Useful for datasets
|
|
625
|
+
where sky depth is encoded as zero or infinity and needs a large finite value.
|
|
626
|
+
|
|
627
|
+
```json
|
|
628
|
+
{
|
|
629
|
+
"sky_depth_value": 1000.0
|
|
630
|
+
}
|
|
631
|
+
```
|
|
632
|
+
|
|
633
|
+
`sky_depth_value` defaults to `1000.0`.
|
|
634
|
+
|
|
635
|
+
CLI runs write a source-backed depth dataset mirroring the input depth modality's paths,
|
|
636
|
+
filenames, extensions, and metadata. Standalone `SkyDepthTransform(...)` usage keeps the
|
|
637
|
+
legacy `.npy` output behaviour.
|
|
638
|
+
|
|
639
|
+
---
|
|
640
|
+
|
|
641
|
+
## Radial Transform
|
|
642
|
+
|
|
643
|
+
Converts planar (z-buffer) depth to radial (Euclidean) depth using camera intrinsics.
|
|
644
|
+
For each pixel *(u, v)*:
|
|
645
|
+
|
|
646
|
+
```
|
|
647
|
+
d_radial(u, v) = d_planar(u, v) * sqrt(((u - cx)/fx)^2 + ((v - cy)/fy)^2 + 1)
|
|
648
|
+
```
|
|
649
|
+
|
|
650
|
+
The transform config takes no parameters (`{}`); intrinsics are read from the
|
|
651
|
+
`intrinsics` hierarchical modality.
|
|
652
|
+
|
|
653
|
+
CLI runs write a source-backed depth dataset mirroring the input depth modality's layout
|
|
654
|
+
and writer metadata, with `meta.radial_depth` set to `true` in the emitted `index.json`.
|
|
655
|
+
Standalone `RadialTransform(...)` usage keeps the legacy `.npy` output behaviour.
|