euler-preprocess 3.2.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.2.0/euler_preprocess.egg-info → euler_preprocess-3.3.0}/PKG-INFO +23 -1
- euler_preprocess-3.2.0/PKG-INFO → euler_preprocess-3.3.0/README.md +22 -14
- {euler_preprocess-3.2.0 → euler_preprocess-3.3.0}/euler_preprocess/fog/capture.py +335 -0
- euler_preprocess-3.2.0/README.md → euler_preprocess-3.3.0/euler_preprocess.egg-info/PKG-INFO +36 -0
- {euler_preprocess-3.2.0 → euler_preprocess-3.3.0}/pyproject.toml +1 -1
- {euler_preprocess-3.2.0 → euler_preprocess-3.3.0}/tests/test_fog_aux_outputs.py +89 -0
- {euler_preprocess-3.2.0 → euler_preprocess-3.3.0}/euler_preprocess/__init__.py +0 -0
- {euler_preprocess-3.2.0 → euler_preprocess-3.3.0}/euler_preprocess/cli.py +0 -0
- {euler_preprocess-3.2.0 → euler_preprocess-3.3.0}/euler_preprocess/common/__init__.py +0 -0
- {euler_preprocess-3.2.0 → euler_preprocess-3.3.0}/euler_preprocess/common/dataset.py +0 -0
- {euler_preprocess-3.2.0 → euler_preprocess-3.3.0}/euler_preprocess/common/device.py +0 -0
- {euler_preprocess-3.2.0 → euler_preprocess-3.3.0}/euler_preprocess/common/intrinsics.py +0 -0
- {euler_preprocess-3.2.0 → euler_preprocess-3.3.0}/euler_preprocess/common/io.py +0 -0
- {euler_preprocess-3.2.0 → euler_preprocess-3.3.0}/euler_preprocess/common/logging.py +0 -0
- {euler_preprocess-3.2.0 → euler_preprocess-3.3.0}/euler_preprocess/common/noise.py +0 -0
- {euler_preprocess-3.2.0 → euler_preprocess-3.3.0}/euler_preprocess/common/normalize.py +0 -0
- {euler_preprocess-3.2.0 → euler_preprocess-3.3.0}/euler_preprocess/common/output.py +0 -0
- {euler_preprocess-3.2.0 → euler_preprocess-3.3.0}/euler_preprocess/common/sampling.py +0 -0
- {euler_preprocess-3.2.0 → euler_preprocess-3.3.0}/euler_preprocess/common/transform.py +0 -0
- {euler_preprocess-3.2.0 → euler_preprocess-3.3.0}/euler_preprocess/fog/__init__.py +0 -0
- {euler_preprocess-3.2.0 → euler_preprocess-3.3.0}/euler_preprocess/fog/airlight_from_sky.py +0 -0
- {euler_preprocess-3.2.0 → euler_preprocess-3.3.0}/euler_preprocess/fog/atmospheric_light.py +0 -0
- {euler_preprocess-3.2.0 → euler_preprocess-3.3.0}/euler_preprocess/fog/augmentations.py +0 -0
- {euler_preprocess-3.2.0 → euler_preprocess-3.3.0}/euler_preprocess/fog/dcp_airlight.py +0 -0
- {euler_preprocess-3.2.0 → euler_preprocess-3.3.0}/euler_preprocess/fog/dcp_airlight_torch.py +0 -0
- {euler_preprocess-3.2.0 → euler_preprocess-3.3.0}/euler_preprocess/fog/dcp_heuristic_airlight.py +0 -0
- {euler_preprocess-3.2.0 → euler_preprocess-3.3.0}/euler_preprocess/fog/dcp_heuristic_airlight_torch.py +0 -0
- {euler_preprocess-3.2.0 → euler_preprocess-3.3.0}/euler_preprocess/fog/foggify.py +0 -0
- {euler_preprocess-3.2.0 → euler_preprocess-3.3.0}/euler_preprocess/fog/foggify_logging.py +0 -0
- {euler_preprocess-3.2.0 → euler_preprocess-3.3.0}/euler_preprocess/fog/inference.py +0 -0
- {euler_preprocess-3.2.0 → euler_preprocess-3.3.0}/euler_preprocess/fog/logging.py +0 -0
- {euler_preprocess-3.2.0 → euler_preprocess-3.3.0}/euler_preprocess/fog/models.py +0 -0
- {euler_preprocess-3.2.0 → euler_preprocess-3.3.0}/euler_preprocess/fog/pipeline.py +0 -0
- {euler_preprocess-3.2.0 → euler_preprocess-3.3.0}/euler_preprocess/fog/transform.py +0 -0
- {euler_preprocess-3.2.0 → euler_preprocess-3.3.0}/euler_preprocess/radial/__init__.py +0 -0
- {euler_preprocess-3.2.0 → euler_preprocess-3.3.0}/euler_preprocess/radial/transform.py +0 -0
- {euler_preprocess-3.2.0 → euler_preprocess-3.3.0}/euler_preprocess/sky_depth/__init__.py +0 -0
- {euler_preprocess-3.2.0 → euler_preprocess-3.3.0}/euler_preprocess/sky_depth/transform.py +0 -0
- {euler_preprocess-3.2.0 → euler_preprocess-3.3.0}/euler_preprocess.egg-info/SOURCES.txt +0 -0
- {euler_preprocess-3.2.0 → euler_preprocess-3.3.0}/euler_preprocess.egg-info/dependency_links.txt +0 -0
- {euler_preprocess-3.2.0 → euler_preprocess-3.3.0}/euler_preprocess.egg-info/entry_points.txt +0 -0
- {euler_preprocess-3.2.0 → euler_preprocess-3.3.0}/euler_preprocess.egg-info/requires.txt +0 -0
- {euler_preprocess-3.2.0 → euler_preprocess-3.3.0}/euler_preprocess.egg-info/top_level.txt +0 -0
- {euler_preprocess-3.2.0 → euler_preprocess-3.3.0}/setup.cfg +0 -0
- {euler_preprocess-3.2.0 → euler_preprocess-3.3.0}/tests/test_airlight_fallback.py +0 -0
- {euler_preprocess-3.2.0 → euler_preprocess-3.3.0}/tests/test_cli_sample_selection.py +0 -0
- {euler_preprocess-3.2.0 → euler_preprocess-3.3.0}/tests/test_dcp_heuristic_airlight.py +0 -0
- {euler_preprocess-3.2.0 → euler_preprocess-3.3.0}/tests/test_foggify_integration.py +0 -0
- {euler_preprocess-3.2.0 → euler_preprocess-3.3.0}/tests/test_radial.py +0 -0
- {euler_preprocess-3.2.0 → euler_preprocess-3.3.0}/tests/test_sky_depth.py +0 -0
- {euler_preprocess-3.2.0 → euler_preprocess-3.3.0}/tests/test_source_backed_output.py +0 -0
- {euler_preprocess-3.2.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.2.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:
|
|
@@ -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,
|
euler_preprocess-3.2.0/README.md → euler_preprocess-3.3.0/euler_preprocess.egg-info/PKG-INFO
RENAMED
|
@@ -1,3 +1,17 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: euler-preprocess
|
|
3
|
+
Version: 3.3.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
|
+
|
|
1
15
|
# euler-preprocess
|
|
2
16
|
|
|
3
17
|
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).
|
|
@@ -309,6 +323,28 @@ noise is fine-grained rather than blocky. `black_noise_floor` with
|
|
|
309
323
|
noise in near-clipped black regions, so the strongest visible noise sits in
|
|
310
324
|
dim-but-readable shadows.
|
|
311
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
|
+
|
|
312
348
|
Any stage can define `condition_profiles` to sample coherent per-image settings
|
|
313
349
|
before the stage runs. This is useful for exposure states where ISO, exposure
|
|
314
350
|
gain, read noise, banding, and dark/fog noise modulation should move together:
|
|
@@ -1652,6 +1652,95 @@ def _deterministic_sensor_pipeline(
|
|
|
1652
1652
|
)
|
|
1653
1653
|
|
|
1654
1654
|
|
|
1655
|
+
def test_sensor_noise_adjustment_scales_authored_noise_groups() -> None:
|
|
1656
|
+
config = {
|
|
1657
|
+
"read_noise_sigma": {"dist": "uniform", "min": 0.01, "max": 0.02},
|
|
1658
|
+
"fixed_pattern_sigma": 0.001,
|
|
1659
|
+
"row_noise_sigma": 0.002,
|
|
1660
|
+
"column_noise_sigma": 0.0015,
|
|
1661
|
+
"banding_modulation": 0.2,
|
|
1662
|
+
"noise_modulation": {
|
|
1663
|
+
"dark_gain": 1.0,
|
|
1664
|
+
"depth_gain": 0.4,
|
|
1665
|
+
"fog_gain": 0.6,
|
|
1666
|
+
"max_gain": 2.0,
|
|
1667
|
+
},
|
|
1668
|
+
"shadow_recovery_noise": {
|
|
1669
|
+
"luma_sigma": 0.003,
|
|
1670
|
+
"chroma_sigma": 0.004,
|
|
1671
|
+
"red_chroma_gain": 0.8,
|
|
1672
|
+
"blue_chroma_gain": 2.0,
|
|
1673
|
+
"chroma_axis_correlation": 0.2,
|
|
1674
|
+
"blotch_sigma": 0.005,
|
|
1675
|
+
},
|
|
1676
|
+
"hot_pixel_probability": 0.0002,
|
|
1677
|
+
"dead_pixel_probability": 0.0001,
|
|
1678
|
+
}
|
|
1679
|
+
|
|
1680
|
+
static = capture_module._apply_sensor_noise_adjustment(
|
|
1681
|
+
{
|
|
1682
|
+
**config,
|
|
1683
|
+
"noise_adjustment": {
|
|
1684
|
+
"level": 1.0,
|
|
1685
|
+
"static_chroma_bias": -1.0,
|
|
1686
|
+
},
|
|
1687
|
+
},
|
|
1688
|
+
np.random.default_rng(1),
|
|
1689
|
+
)
|
|
1690
|
+
chroma = capture_module._apply_sensor_noise_adjustment(
|
|
1691
|
+
{
|
|
1692
|
+
**config,
|
|
1693
|
+
"noise_adjustment": {
|
|
1694
|
+
"level": 1.0,
|
|
1695
|
+
"static_chroma_bias": 1.0,
|
|
1696
|
+
},
|
|
1697
|
+
},
|
|
1698
|
+
np.random.default_rng(1),
|
|
1699
|
+
)
|
|
1700
|
+
|
|
1701
|
+
assert static["row_noise_sigma"] > chroma["row_noise_sigma"]
|
|
1702
|
+
assert static["fixed_pattern_sigma"] > chroma["fixed_pattern_sigma"]
|
|
1703
|
+
assert static["hot_pixel_probability"] > chroma["hot_pixel_probability"]
|
|
1704
|
+
assert (
|
|
1705
|
+
static["shadow_recovery_noise"]["chroma_sigma"]
|
|
1706
|
+
< chroma["shadow_recovery_noise"]["chroma_sigma"]
|
|
1707
|
+
)
|
|
1708
|
+
assert (
|
|
1709
|
+
static["shadow_recovery_noise"]["blue_chroma_gain"]
|
|
1710
|
+
< chroma["shadow_recovery_noise"]["blue_chroma_gain"]
|
|
1711
|
+
)
|
|
1712
|
+
assert static["read_noise_sigma"]["min"] < chroma["read_noise_sigma"]["min"]
|
|
1713
|
+
assert static["noise_modulation"]["max_gain"] == chroma["noise_modulation"]["max_gain"]
|
|
1714
|
+
|
|
1715
|
+
|
|
1716
|
+
def test_sensor_noise_adjustment_level_changes_output_noise() -> None:
|
|
1717
|
+
base_overrides = {
|
|
1718
|
+
"demosaic": False,
|
|
1719
|
+
"read_noise_sigma": 0.01,
|
|
1720
|
+
}
|
|
1721
|
+
low = _deterministic_sensor_pipeline(
|
|
1722
|
+
auto_exposure={"enabled": False},
|
|
1723
|
+
sensor_overrides={
|
|
1724
|
+
**base_overrides,
|
|
1725
|
+
"noise_adjustment": {"level": 0.0, "static_chroma_bias": 0.0},
|
|
1726
|
+
},
|
|
1727
|
+
)
|
|
1728
|
+
high = _deterministic_sensor_pipeline(
|
|
1729
|
+
auto_exposure={"enabled": False},
|
|
1730
|
+
sensor_overrides={
|
|
1731
|
+
**base_overrides,
|
|
1732
|
+
"noise_adjustment": {"level": 2.0, "static_chroma_bias": 0.0},
|
|
1733
|
+
},
|
|
1734
|
+
)
|
|
1735
|
+
image = np.full((48, 64, 3), 0.5, dtype=np.float32)
|
|
1736
|
+
|
|
1737
|
+
low_result = low.apply_np(image, CaptureContext(rng=np.random.default_rng(7)))
|
|
1738
|
+
high_result = high.apply_np(image, CaptureContext(rng=np.random.default_rng(7)))
|
|
1739
|
+
|
|
1740
|
+
assert float((low_result - image).std()) < 1e-6
|
|
1741
|
+
assert float((high_result - image).std()) > 0.012
|
|
1742
|
+
|
|
1743
|
+
|
|
1655
1744
|
def test_sensor_auto_exposure_meters_rendered_luminance() -> None:
|
|
1656
1745
|
pipeline = _deterministic_sensor_pipeline(
|
|
1657
1746
|
auto_exposure={
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{euler_preprocess-3.2.0 → euler_preprocess-3.3.0}/euler_preprocess/fog/dcp_airlight_torch.py
RENAMED
|
File without changes
|
{euler_preprocess-3.2.0 → euler_preprocess-3.3.0}/euler_preprocess/fog/dcp_heuristic_airlight.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{euler_preprocess-3.2.0 → euler_preprocess-3.3.0}/euler_preprocess.egg-info/dependency_links.txt
RENAMED
|
File without changes
|
{euler_preprocess-3.2.0 → euler_preprocess-3.3.0}/euler_preprocess.egg-info/entry_points.txt
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|