euler-preprocess 3.1.0__tar.gz → 3.3.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.1.0/euler_preprocess.egg-info → euler_preprocess-3.3.0}/PKG-INFO +23 -1
- euler_preprocess-3.1.0/PKG-INFO → euler_preprocess-3.3.0/README.md +22 -14
- euler_preprocess-3.3.0/euler_preprocess/fog/__init__.py +11 -0
- {euler_preprocess-3.1.0 → euler_preprocess-3.3.0}/euler_preprocess/fog/atmospheric_light.py +8 -5
- {euler_preprocess-3.1.0 → euler_preprocess-3.3.0}/euler_preprocess/fog/capture.py +335 -0
- euler_preprocess-3.3.0/euler_preprocess/fog/inference.py +379 -0
- {euler_preprocess-3.1.0 → euler_preprocess-3.3.0}/euler_preprocess/fog/transform.py +151 -25
- euler_preprocess-3.1.0/README.md → euler_preprocess-3.3.0/euler_preprocess.egg-info/PKG-INFO +36 -0
- {euler_preprocess-3.1.0 → euler_preprocess-3.3.0}/euler_preprocess.egg-info/SOURCES.txt +1 -0
- {euler_preprocess-3.1.0 → euler_preprocess-3.3.0}/pyproject.toml +1 -1
- {euler_preprocess-3.1.0 → euler_preprocess-3.3.0}/tests/test_fog_aux_outputs.py +341 -0
- euler_preprocess-3.1.0/euler_preprocess/sky_depth/__init__.py +0 -0
- {euler_preprocess-3.1.0 → euler_preprocess-3.3.0}/euler_preprocess/__init__.py +0 -0
- {euler_preprocess-3.1.0 → euler_preprocess-3.3.0}/euler_preprocess/cli.py +0 -0
- {euler_preprocess-3.1.0 → euler_preprocess-3.3.0}/euler_preprocess/common/__init__.py +0 -0
- {euler_preprocess-3.1.0 → euler_preprocess-3.3.0}/euler_preprocess/common/dataset.py +0 -0
- {euler_preprocess-3.1.0 → euler_preprocess-3.3.0}/euler_preprocess/common/device.py +0 -0
- {euler_preprocess-3.1.0 → euler_preprocess-3.3.0}/euler_preprocess/common/intrinsics.py +0 -0
- {euler_preprocess-3.1.0 → euler_preprocess-3.3.0}/euler_preprocess/common/io.py +0 -0
- {euler_preprocess-3.1.0 → euler_preprocess-3.3.0}/euler_preprocess/common/logging.py +0 -0
- {euler_preprocess-3.1.0 → euler_preprocess-3.3.0}/euler_preprocess/common/noise.py +0 -0
- {euler_preprocess-3.1.0 → euler_preprocess-3.3.0}/euler_preprocess/common/normalize.py +0 -0
- {euler_preprocess-3.1.0 → euler_preprocess-3.3.0}/euler_preprocess/common/output.py +0 -0
- {euler_preprocess-3.1.0 → euler_preprocess-3.3.0}/euler_preprocess/common/sampling.py +0 -0
- {euler_preprocess-3.1.0 → euler_preprocess-3.3.0}/euler_preprocess/common/transform.py +0 -0
- {euler_preprocess-3.1.0 → euler_preprocess-3.3.0}/euler_preprocess/fog/airlight_from_sky.py +0 -0
- {euler_preprocess-3.1.0 → euler_preprocess-3.3.0}/euler_preprocess/fog/augmentations.py +0 -0
- {euler_preprocess-3.1.0 → euler_preprocess-3.3.0}/euler_preprocess/fog/dcp_airlight.py +0 -0
- {euler_preprocess-3.1.0 → euler_preprocess-3.3.0}/euler_preprocess/fog/dcp_airlight_torch.py +0 -0
- {euler_preprocess-3.1.0 → euler_preprocess-3.3.0}/euler_preprocess/fog/dcp_heuristic_airlight.py +0 -0
- {euler_preprocess-3.1.0 → euler_preprocess-3.3.0}/euler_preprocess/fog/dcp_heuristic_airlight_torch.py +0 -0
- {euler_preprocess-3.1.0 → euler_preprocess-3.3.0}/euler_preprocess/fog/foggify.py +0 -0
- {euler_preprocess-3.1.0 → euler_preprocess-3.3.0}/euler_preprocess/fog/foggify_logging.py +0 -0
- {euler_preprocess-3.1.0 → euler_preprocess-3.3.0}/euler_preprocess/fog/logging.py +0 -0
- {euler_preprocess-3.1.0 → euler_preprocess-3.3.0}/euler_preprocess/fog/models.py +0 -0
- {euler_preprocess-3.1.0 → euler_preprocess-3.3.0}/euler_preprocess/fog/pipeline.py +0 -0
- {euler_preprocess-3.1.0/euler_preprocess/fog → euler_preprocess-3.3.0/euler_preprocess/radial}/__init__.py +0 -0
- {euler_preprocess-3.1.0 → euler_preprocess-3.3.0}/euler_preprocess/radial/transform.py +0 -0
- {euler_preprocess-3.1.0/euler_preprocess/radial → euler_preprocess-3.3.0/euler_preprocess/sky_depth}/__init__.py +0 -0
- {euler_preprocess-3.1.0 → euler_preprocess-3.3.0}/euler_preprocess/sky_depth/transform.py +0 -0
- {euler_preprocess-3.1.0 → euler_preprocess-3.3.0}/euler_preprocess.egg-info/dependency_links.txt +0 -0
- {euler_preprocess-3.1.0 → euler_preprocess-3.3.0}/euler_preprocess.egg-info/entry_points.txt +0 -0
- {euler_preprocess-3.1.0 → euler_preprocess-3.3.0}/euler_preprocess.egg-info/requires.txt +0 -0
- {euler_preprocess-3.1.0 → euler_preprocess-3.3.0}/euler_preprocess.egg-info/top_level.txt +0 -0
- {euler_preprocess-3.1.0 → euler_preprocess-3.3.0}/setup.cfg +0 -0
- {euler_preprocess-3.1.0 → euler_preprocess-3.3.0}/tests/test_airlight_fallback.py +0 -0
- {euler_preprocess-3.1.0 → euler_preprocess-3.3.0}/tests/test_cli_sample_selection.py +0 -0
- {euler_preprocess-3.1.0 → euler_preprocess-3.3.0}/tests/test_dcp_heuristic_airlight.py +0 -0
- {euler_preprocess-3.1.0 → euler_preprocess-3.3.0}/tests/test_foggify_integration.py +0 -0
- {euler_preprocess-3.1.0 → euler_preprocess-3.3.0}/tests/test_radial.py +0 -0
- {euler_preprocess-3.1.0 → euler_preprocess-3.3.0}/tests/test_sky_depth.py +0 -0
- {euler_preprocess-3.1.0 → euler_preprocess-3.3.0}/tests/test_source_backed_output.py +0 -0
- {euler_preprocess-3.1.0 → euler_preprocess-3.3.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.3.0
|
|
4
4
|
Summary: Physics-based preprocessing (fog, etc.) for RGB+depth datasets
|
|
5
5
|
Requires-Python: >=3.9
|
|
6
6
|
Description-Content-Type: text/markdown
|
|
@@ -323,6 +323,28 @@ noise is fine-grained rather than blocky. `black_noise_floor` with
|
|
|
323
323
|
noise in near-clipped black regions, so the strongest visible noise sits in
|
|
324
324
|
dim-but-readable shadows.
|
|
325
325
|
|
|
326
|
+
Set `sensor.noise_adjustment` for relative, scenario-level noise controls on
|
|
327
|
+
top of the selected camera/condition profile. `level: 1.0` leaves the authored
|
|
328
|
+
profile unchanged; lower values suppress read/static/chroma noise and higher
|
|
329
|
+
values amplify it. `static_chroma_bias` ranges from `-1.0` for more fixed
|
|
330
|
+
pattern, row/column, banding, and bad-pixel noise to `1.0` for more
|
|
331
|
+
chromatic/high-ISO-looking shadow noise:
|
|
332
|
+
|
|
333
|
+
```json
|
|
334
|
+
{
|
|
335
|
+
"capture_overrides": {
|
|
336
|
+
"sensor": {
|
|
337
|
+
"condition_profile": "nominal_gloom",
|
|
338
|
+
"noise_adjustment": {
|
|
339
|
+
"enabled": true,
|
|
340
|
+
"level": 1.25,
|
|
341
|
+
"static_chroma_bias": 0.35
|
|
342
|
+
}
|
|
343
|
+
}
|
|
344
|
+
}
|
|
345
|
+
}
|
|
346
|
+
```
|
|
347
|
+
|
|
326
348
|
Any stage can define `condition_profiles` to sample coherent per-image settings
|
|
327
349
|
before the stage runs. This is useful for exposure states where ISO, exposure
|
|
328
350
|
gain, read noise, banding, and dark/fog noise modulation should move together:
|
|
@@ -1,17 +1,3 @@
|
|
|
1
|
-
Metadata-Version: 2.4
|
|
2
|
-
Name: euler-preprocess
|
|
3
|
-
Version: 3.1.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
1
|
# euler-preprocess
|
|
16
2
|
|
|
17
3
|
Physics-based preprocessing transforms for multi-modal RGB+depth datasets. Built on top of [euler-loading](https://github.com/d-rothen/euler-loading) and [ds-crawler](https://github.com/d-rothen/ds-crawler).
|
|
@@ -323,6 +309,28 @@ noise is fine-grained rather than blocky. `black_noise_floor` with
|
|
|
323
309
|
noise in near-clipped black regions, so the strongest visible noise sits in
|
|
324
310
|
dim-but-readable shadows.
|
|
325
311
|
|
|
312
|
+
Set `sensor.noise_adjustment` for relative, scenario-level noise controls on
|
|
313
|
+
top of the selected camera/condition profile. `level: 1.0` leaves the authored
|
|
314
|
+
profile unchanged; lower values suppress read/static/chroma noise and higher
|
|
315
|
+
values amplify it. `static_chroma_bias` ranges from `-1.0` for more fixed
|
|
316
|
+
pattern, row/column, banding, and bad-pixel noise to `1.0` for more
|
|
317
|
+
chromatic/high-ISO-looking shadow noise:
|
|
318
|
+
|
|
319
|
+
```json
|
|
320
|
+
{
|
|
321
|
+
"capture_overrides": {
|
|
322
|
+
"sensor": {
|
|
323
|
+
"condition_profile": "nominal_gloom",
|
|
324
|
+
"noise_adjustment": {
|
|
325
|
+
"enabled": true,
|
|
326
|
+
"level": 1.25,
|
|
327
|
+
"static_chroma_bias": 0.35
|
|
328
|
+
}
|
|
329
|
+
}
|
|
330
|
+
}
|
|
331
|
+
}
|
|
332
|
+
```
|
|
333
|
+
|
|
326
334
|
Any stage can define `condition_profiles` to sample coherent per-image settings
|
|
327
335
|
before the stage runs. This is useful for exposure states where ISO, exposure
|
|
328
336
|
gain, read noise, banding, and dark/fog noise modulation should move together:
|
|
@@ -155,12 +155,13 @@ class AtmosphericLightResolver:
|
|
|
155
155
|
items: list[dict],
|
|
156
156
|
device: Any,
|
|
157
157
|
contrast_threshold_default: float,
|
|
158
|
+
method: str | None = None,
|
|
158
159
|
) -> tuple[list[float], "torch.Tensor"]:
|
|
159
160
|
"""Resolve beta and dampened base L_s for a uniform-model torch batch."""
|
|
160
161
|
al_spec = items[0]["model_cfg"].get("atmospheric_light", "from_sky")
|
|
161
162
|
airlight_is_estimated = uses_estimated_airlight(al_spec)
|
|
162
163
|
if airlight_is_estimated:
|
|
163
|
-
ls_base = self._estimate_batch_torch(rgb_batch, items, device)
|
|
164
|
+
ls_base = self._estimate_batch_torch(rgb_batch, items, device, method)
|
|
164
165
|
ls_base = normalize_atmospheric_light_torch(ls_base)
|
|
165
166
|
else:
|
|
166
167
|
ls_values = []
|
|
@@ -209,17 +210,19 @@ class AtmosphericLightResolver:
|
|
|
209
210
|
rgb_batch: "torch.Tensor",
|
|
210
211
|
items: list[dict],
|
|
211
212
|
device: Any,
|
|
213
|
+
method: str | None = None,
|
|
212
214
|
) -> "torch.Tensor":
|
|
213
|
-
|
|
215
|
+
resolved_method = method or self.method
|
|
216
|
+
if resolved_method == "from_sky":
|
|
214
217
|
return self._estimate_from_sky_batch_torch(rgb_batch, items, device)
|
|
215
|
-
estimator = self._get_estimator_torch(
|
|
218
|
+
estimator = self._get_estimator_torch(resolved_method)
|
|
216
219
|
if estimator is None:
|
|
217
220
|
raise RuntimeError(
|
|
218
|
-
f"Torch airlight estimator unavailable for method '{
|
|
221
|
+
f"Torch airlight estimator unavailable for method '{resolved_method}'."
|
|
219
222
|
)
|
|
220
223
|
al_list = []
|
|
221
224
|
for idx, item in enumerate(items):
|
|
222
|
-
if
|
|
225
|
+
if resolved_method == "dcp":
|
|
223
226
|
al_list.append(estimator.compute(rgb_batch[idx]))
|
|
224
227
|
else:
|
|
225
228
|
sky_mask_t = torch.from_numpy(item["sky_mask"]).to(
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
|
+
from copy import deepcopy
|
|
3
4
|
import io
|
|
4
5
|
import math
|
|
5
6
|
from dataclasses import dataclass, field
|
|
@@ -522,6 +523,7 @@ class SensorStage(ConfiguredCaptureStage):
|
|
|
522
523
|
rng,
|
|
523
524
|
manual_exposure_gain=exposure,
|
|
524
525
|
)
|
|
526
|
+
config = _apply_sensor_noise_adjustment(config, rng)
|
|
525
527
|
wb = _sample_triplet(config.get("white_balance", [1.0, 1.0, 1.0]), rng)
|
|
526
528
|
wb_jitter = _sample_float(config, "white_balance_jitter", 0.0, rng)
|
|
527
529
|
if wb_jitter > 0.0:
|
|
@@ -717,6 +719,7 @@ class SensorStage(ConfiguredCaptureStage):
|
|
|
717
719
|
rng,
|
|
718
720
|
manual_exposure_gain=exposure,
|
|
719
721
|
)
|
|
722
|
+
config = _apply_sensor_noise_adjustment(config, rng)
|
|
720
723
|
wb = _sample_triplet(config.get("white_balance", [1.0, 1.0, 1.0]), rng)
|
|
721
724
|
wb_jitter = _sample_float(config, "white_balance_jitter", 0.0, rng)
|
|
722
725
|
if wb_jitter > 0.0:
|
|
@@ -2152,6 +2155,338 @@ def _resolve_electron_capacity_torch(
|
|
|
2152
2155
|
)
|
|
2153
2156
|
|
|
2154
2157
|
|
|
2158
|
+
def _apply_sensor_noise_adjustment(
|
|
2159
|
+
config: Mapping[str, Any],
|
|
2160
|
+
rng: np.random.Generator,
|
|
2161
|
+
) -> Mapping[str, Any]:
|
|
2162
|
+
cfg = _config_block(config, "noise_adjustment")
|
|
2163
|
+
if not cfg or not _bool_value(cfg.get("enabled", True)):
|
|
2164
|
+
return config
|
|
2165
|
+
|
|
2166
|
+
level = max(
|
|
2167
|
+
_sample_float_from_keys(
|
|
2168
|
+
cfg,
|
|
2169
|
+
("level", "amount", "factor", "noise_level"),
|
|
2170
|
+
1.0,
|
|
2171
|
+
rng,
|
|
2172
|
+
),
|
|
2173
|
+
0.0,
|
|
2174
|
+
)
|
|
2175
|
+
bias = float(
|
|
2176
|
+
np.clip(
|
|
2177
|
+
_sample_float_from_keys(
|
|
2178
|
+
cfg,
|
|
2179
|
+
("static_chroma_bias", "character_bias", "chroma_bias"),
|
|
2180
|
+
0.0,
|
|
2181
|
+
rng,
|
|
2182
|
+
),
|
|
2183
|
+
-1.0,
|
|
2184
|
+
1.0,
|
|
2185
|
+
)
|
|
2186
|
+
)
|
|
2187
|
+
groups = _config_block(cfg, "groups")
|
|
2188
|
+
limits = _config_block(cfg, "limits")
|
|
2189
|
+
|
|
2190
|
+
min_factor = max(_sample_float(limits, "min_factor", 0.0, rng), 0.0)
|
|
2191
|
+
max_factor = max(_sample_float(limits, "max_factor", 8.0, rng), min_factor)
|
|
2192
|
+
|
|
2193
|
+
def group_factor(name: str, base: float) -> float:
|
|
2194
|
+
factor = base * max(_sample_float(groups, name, 1.0, rng), 0.0)
|
|
2195
|
+
return float(np.clip(factor, min_factor, max_factor))
|
|
2196
|
+
|
|
2197
|
+
read_factor = group_factor("read", level * math.pow(2.0, 0.35 * bias))
|
|
2198
|
+
static_factor = group_factor("static", level * math.pow(2.0, -bias))
|
|
2199
|
+
banding_factor = group_factor("banding", static_factor)
|
|
2200
|
+
bad_pixel_factor = group_factor("bad_pixels", static_factor)
|
|
2201
|
+
chroma_factor = group_factor("chroma", level * math.pow(2.0, bias))
|
|
2202
|
+
shadow_luma_factor = group_factor("shadow_luma", level)
|
|
2203
|
+
modulation_factor = group_factor("modulation", 1.0 + (level - 1.0) * 0.75)
|
|
2204
|
+
|
|
2205
|
+
if (
|
|
2206
|
+
abs(read_factor - 1.0) < 1e-9
|
|
2207
|
+
and abs(static_factor - 1.0) < 1e-9
|
|
2208
|
+
and abs(banding_factor - 1.0) < 1e-9
|
|
2209
|
+
and abs(bad_pixel_factor - 1.0) < 1e-9
|
|
2210
|
+
and abs(chroma_factor - 1.0) < 1e-9
|
|
2211
|
+
and abs(shadow_luma_factor - 1.0) < 1e-9
|
|
2212
|
+
and abs(modulation_factor - 1.0) < 1e-9
|
|
2213
|
+
):
|
|
2214
|
+
return config
|
|
2215
|
+
|
|
2216
|
+
adjusted = deepcopy(dict(config))
|
|
2217
|
+
|
|
2218
|
+
_scale_config_path(adjusted, ("read_noise_electrons",), read_factor, min_value=0.0)
|
|
2219
|
+
_scale_config_path(adjusted, ("read_noise_sigma",), read_factor, min_value=0.0)
|
|
2220
|
+
|
|
2221
|
+
_scale_config_path(adjusted, ("fixed_pattern_sigma",), static_factor, min_value=0.0)
|
|
2222
|
+
_scale_config_path(adjusted, ("row_noise_sigma",), banding_factor, min_value=0.0)
|
|
2223
|
+
_scale_config_path(adjusted, ("column_noise_sigma",), banding_factor, min_value=0.0)
|
|
2224
|
+
_scale_config_path(
|
|
2225
|
+
adjusted,
|
|
2226
|
+
("banding_modulation",),
|
|
2227
|
+
banding_factor,
|
|
2228
|
+
min_value=0.0,
|
|
2229
|
+
max_value=1.0,
|
|
2230
|
+
)
|
|
2231
|
+
|
|
2232
|
+
max_bad_pixel_probability = _sample_float(
|
|
2233
|
+
limits,
|
|
2234
|
+
"max_bad_pixel_probability",
|
|
2235
|
+
0.05,
|
|
2236
|
+
rng,
|
|
2237
|
+
)
|
|
2238
|
+
_scale_config_path(
|
|
2239
|
+
adjusted,
|
|
2240
|
+
("hot_pixel_probability",),
|
|
2241
|
+
bad_pixel_factor,
|
|
2242
|
+
min_value=0.0,
|
|
2243
|
+
max_value=max_bad_pixel_probability,
|
|
2244
|
+
)
|
|
2245
|
+
_scale_config_path(
|
|
2246
|
+
adjusted,
|
|
2247
|
+
("dead_pixel_probability",),
|
|
2248
|
+
bad_pixel_factor,
|
|
2249
|
+
min_value=0.0,
|
|
2250
|
+
max_value=max_bad_pixel_probability,
|
|
2251
|
+
)
|
|
2252
|
+
|
|
2253
|
+
for key in ("dark_gain", "depth_gain", "fog_gain"):
|
|
2254
|
+
_scale_config_path(
|
|
2255
|
+
adjusted,
|
|
2256
|
+
("noise_modulation", key),
|
|
2257
|
+
modulation_factor,
|
|
2258
|
+
min_value=0.0,
|
|
2259
|
+
)
|
|
2260
|
+
_scale_config_path(
|
|
2261
|
+
adjusted,
|
|
2262
|
+
("noise_modulation", "max_gain"),
|
|
2263
|
+
modulation_factor,
|
|
2264
|
+
anchor=1.0,
|
|
2265
|
+
min_value=1.0,
|
|
2266
|
+
)
|
|
2267
|
+
|
|
2268
|
+
_scale_config_path(
|
|
2269
|
+
adjusted,
|
|
2270
|
+
("shadow_recovery_noise", "luma_sigma"),
|
|
2271
|
+
shadow_luma_factor,
|
|
2272
|
+
min_value=0.0,
|
|
2273
|
+
)
|
|
2274
|
+
_scale_config_path(
|
|
2275
|
+
adjusted,
|
|
2276
|
+
("shadow_recovery_noise", "chroma_sigma"),
|
|
2277
|
+
chroma_factor,
|
|
2278
|
+
min_value=0.0,
|
|
2279
|
+
)
|
|
2280
|
+
_scale_config_path(
|
|
2281
|
+
adjusted,
|
|
2282
|
+
("shadow_recovery_noise", "blotch_sigma"),
|
|
2283
|
+
chroma_factor,
|
|
2284
|
+
min_value=0.0,
|
|
2285
|
+
)
|
|
2286
|
+
_scale_config_path(
|
|
2287
|
+
adjusted,
|
|
2288
|
+
("shadow_recovery_noise", "red_chroma_gain"),
|
|
2289
|
+
chroma_factor,
|
|
2290
|
+
anchor=1.0,
|
|
2291
|
+
min_value=0.0,
|
|
2292
|
+
)
|
|
2293
|
+
_scale_config_path(
|
|
2294
|
+
adjusted,
|
|
2295
|
+
("shadow_recovery_noise", "blue_chroma_gain"),
|
|
2296
|
+
chroma_factor,
|
|
2297
|
+
anchor=1.0,
|
|
2298
|
+
min_value=0.0,
|
|
2299
|
+
)
|
|
2300
|
+
_scale_config_path(
|
|
2301
|
+
adjusted,
|
|
2302
|
+
("shadow_recovery_noise", "chroma_axis_correlation"),
|
|
2303
|
+
chroma_factor,
|
|
2304
|
+
min_value=-0.95,
|
|
2305
|
+
max_value=0.95,
|
|
2306
|
+
)
|
|
2307
|
+
return adjusted
|
|
2308
|
+
|
|
2309
|
+
|
|
2310
|
+
def _sample_float_from_keys(
|
|
2311
|
+
config: Mapping[str, Any],
|
|
2312
|
+
keys: tuple[str, ...],
|
|
2313
|
+
default: float,
|
|
2314
|
+
rng: np.random.Generator,
|
|
2315
|
+
) -> float:
|
|
2316
|
+
for key in keys:
|
|
2317
|
+
if config.get(key) is not None:
|
|
2318
|
+
return _sample_float(config, key, default, rng)
|
|
2319
|
+
return float(default)
|
|
2320
|
+
|
|
2321
|
+
|
|
2322
|
+
def _scale_config_path(
|
|
2323
|
+
config: dict[str, Any],
|
|
2324
|
+
path: tuple[str, ...],
|
|
2325
|
+
factor: float,
|
|
2326
|
+
*,
|
|
2327
|
+
anchor: float = 0.0,
|
|
2328
|
+
min_value: float | None = None,
|
|
2329
|
+
max_value: float | None = None,
|
|
2330
|
+
) -> None:
|
|
2331
|
+
parent: dict[str, Any] = config
|
|
2332
|
+
for key in path[:-1]:
|
|
2333
|
+
value = parent.get(key)
|
|
2334
|
+
if not isinstance(value, dict):
|
|
2335
|
+
return
|
|
2336
|
+
parent = value
|
|
2337
|
+
key = path[-1]
|
|
2338
|
+
if key not in parent or parent[key] is None:
|
|
2339
|
+
return
|
|
2340
|
+
parent[key] = _scale_numeric_spec(
|
|
2341
|
+
parent[key],
|
|
2342
|
+
factor,
|
|
2343
|
+
anchor=anchor,
|
|
2344
|
+
min_value=min_value,
|
|
2345
|
+
max_value=max_value,
|
|
2346
|
+
)
|
|
2347
|
+
|
|
2348
|
+
|
|
2349
|
+
def _scale_numeric_spec(
|
|
2350
|
+
spec: Any,
|
|
2351
|
+
factor: float,
|
|
2352
|
+
*,
|
|
2353
|
+
anchor: float = 0.0,
|
|
2354
|
+
min_value: float | None = None,
|
|
2355
|
+
max_value: float | None = None,
|
|
2356
|
+
) -> Any:
|
|
2357
|
+
if isinstance(spec, bool) or spec is None:
|
|
2358
|
+
return spec
|
|
2359
|
+
if isinstance(spec, (int, float)):
|
|
2360
|
+
return _clamp_scaled_number(
|
|
2361
|
+
anchor + (float(spec) - anchor) * factor,
|
|
2362
|
+
min_value,
|
|
2363
|
+
max_value,
|
|
2364
|
+
)
|
|
2365
|
+
if isinstance(spec, list):
|
|
2366
|
+
return [
|
|
2367
|
+
_scale_numeric_spec(
|
|
2368
|
+
value,
|
|
2369
|
+
factor,
|
|
2370
|
+
anchor=anchor,
|
|
2371
|
+
min_value=min_value,
|
|
2372
|
+
max_value=max_value,
|
|
2373
|
+
)
|
|
2374
|
+
for value in spec
|
|
2375
|
+
]
|
|
2376
|
+
if isinstance(spec, tuple):
|
|
2377
|
+
return tuple(
|
|
2378
|
+
_scale_numeric_spec(
|
|
2379
|
+
value,
|
|
2380
|
+
factor,
|
|
2381
|
+
anchor=anchor,
|
|
2382
|
+
min_value=min_value,
|
|
2383
|
+
max_value=max_value,
|
|
2384
|
+
)
|
|
2385
|
+
for value in spec
|
|
2386
|
+
)
|
|
2387
|
+
if not isinstance(spec, dict):
|
|
2388
|
+
return spec
|
|
2389
|
+
|
|
2390
|
+
scaled = dict(spec)
|
|
2391
|
+
dist = scaled.get("dist")
|
|
2392
|
+
if dist is None:
|
|
2393
|
+
if "value" in scaled:
|
|
2394
|
+
scaled["value"] = _scale_numeric_spec(
|
|
2395
|
+
scaled["value"],
|
|
2396
|
+
factor,
|
|
2397
|
+
anchor=anchor,
|
|
2398
|
+
min_value=min_value,
|
|
2399
|
+
max_value=max_value,
|
|
2400
|
+
)
|
|
2401
|
+
return scaled
|
|
2402
|
+
|
|
2403
|
+
if dist == "constant":
|
|
2404
|
+
scaled["value"] = _scale_numeric_spec(
|
|
2405
|
+
scaled.get("value", 0.0),
|
|
2406
|
+
factor,
|
|
2407
|
+
anchor=anchor,
|
|
2408
|
+
min_value=min_value,
|
|
2409
|
+
max_value=max_value,
|
|
2410
|
+
)
|
|
2411
|
+
elif dist == "uniform":
|
|
2412
|
+
scaled["min"] = _scale_numeric_spec(
|
|
2413
|
+
scaled["min"],
|
|
2414
|
+
factor,
|
|
2415
|
+
anchor=anchor,
|
|
2416
|
+
min_value=min_value,
|
|
2417
|
+
max_value=max_value,
|
|
2418
|
+
)
|
|
2419
|
+
scaled["max"] = _scale_numeric_spec(
|
|
2420
|
+
scaled["max"],
|
|
2421
|
+
factor,
|
|
2422
|
+
anchor=anchor,
|
|
2423
|
+
min_value=min_value,
|
|
2424
|
+
max_value=max_value,
|
|
2425
|
+
)
|
|
2426
|
+
if scaled["min"] > scaled["max"]:
|
|
2427
|
+
scaled["min"], scaled["max"] = scaled["max"], scaled["min"]
|
|
2428
|
+
elif dist == "normal":
|
|
2429
|
+
scaled["mean"] = _scale_numeric_spec(
|
|
2430
|
+
scaled["mean"],
|
|
2431
|
+
factor,
|
|
2432
|
+
anchor=anchor,
|
|
2433
|
+
min_value=min_value,
|
|
2434
|
+
max_value=max_value,
|
|
2435
|
+
)
|
|
2436
|
+
scaled["std"] = max(float(scaled.get("std", 0.0)) * abs(factor), 0.0)
|
|
2437
|
+
for key in ("min", "max"):
|
|
2438
|
+
if key in scaled and scaled[key] is not None:
|
|
2439
|
+
scaled[key] = _scale_numeric_spec(
|
|
2440
|
+
scaled[key],
|
|
2441
|
+
factor,
|
|
2442
|
+
anchor=anchor,
|
|
2443
|
+
min_value=min_value,
|
|
2444
|
+
max_value=max_value,
|
|
2445
|
+
)
|
|
2446
|
+
if scaled.get("min") is not None and scaled.get("max") is not None:
|
|
2447
|
+
if scaled["min"] > scaled["max"]:
|
|
2448
|
+
scaled["min"], scaled["max"] = scaled["max"], scaled["min"]
|
|
2449
|
+
elif dist == "lognormal":
|
|
2450
|
+
if anchor == 0.0 and factor > 0.0:
|
|
2451
|
+
scaled["mean"] = float(scaled.get("mean", 0.0)) + math.log(factor)
|
|
2452
|
+
for key in ("min", "max"):
|
|
2453
|
+
if key in scaled and scaled[key] is not None:
|
|
2454
|
+
scaled[key] = _scale_numeric_spec(
|
|
2455
|
+
scaled[key],
|
|
2456
|
+
factor,
|
|
2457
|
+
anchor=anchor,
|
|
2458
|
+
min_value=min_value,
|
|
2459
|
+
max_value=max_value,
|
|
2460
|
+
)
|
|
2461
|
+
if scaled.get("min") is not None and scaled.get("max") is not None:
|
|
2462
|
+
if scaled["min"] > scaled["max"]:
|
|
2463
|
+
scaled["min"], scaled["max"] = scaled["max"], scaled["min"]
|
|
2464
|
+
elif dist == "choice":
|
|
2465
|
+
scaled["values"] = [
|
|
2466
|
+
_scale_numeric_spec(
|
|
2467
|
+
value,
|
|
2468
|
+
factor,
|
|
2469
|
+
anchor=anchor,
|
|
2470
|
+
min_value=min_value,
|
|
2471
|
+
max_value=max_value,
|
|
2472
|
+
)
|
|
2473
|
+
for value in scaled.get("values", [])
|
|
2474
|
+
]
|
|
2475
|
+
return scaled
|
|
2476
|
+
|
|
2477
|
+
|
|
2478
|
+
def _clamp_scaled_number(
|
|
2479
|
+
value: float,
|
|
2480
|
+
min_value: float | None,
|
|
2481
|
+
max_value: float | None,
|
|
2482
|
+
) -> float:
|
|
2483
|
+
if min_value is not None:
|
|
2484
|
+
value = max(float(min_value), value)
|
|
2485
|
+
if max_value is not None:
|
|
2486
|
+
value = min(float(max_value), value)
|
|
2487
|
+
return float(value)
|
|
2488
|
+
|
|
2489
|
+
|
|
2155
2490
|
def _sensor_noise_modulation(
|
|
2156
2491
|
signal: np.ndarray,
|
|
2157
2492
|
context: CaptureContext,
|