euler-preprocess 3.0.0__tar.gz → 3.2.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.
Files changed (54) hide show
  1. {euler_preprocess-3.0.0 → euler_preprocess-3.2.0}/PKG-INFO +93 -2
  2. {euler_preprocess-3.0.0 → euler_preprocess-3.2.0}/README.md +92 -1
  3. euler_preprocess-3.2.0/euler_preprocess/fog/__init__.py +11 -0
  4. {euler_preprocess-3.0.0 → euler_preprocess-3.2.0}/euler_preprocess/fog/atmospheric_light.py +8 -5
  5. euler_preprocess-3.2.0/euler_preprocess/fog/capture.py +4017 -0
  6. euler_preprocess-3.2.0/euler_preprocess/fog/inference.py +379 -0
  7. {euler_preprocess-3.0.0 → euler_preprocess-3.2.0}/euler_preprocess/fog/pipeline.py +11 -3
  8. {euler_preprocess-3.0.0 → euler_preprocess-3.2.0}/euler_preprocess/fog/transform.py +380 -45
  9. {euler_preprocess-3.0.0 → euler_preprocess-3.2.0}/euler_preprocess.egg-info/PKG-INFO +93 -2
  10. {euler_preprocess-3.0.0 → euler_preprocess-3.2.0}/euler_preprocess.egg-info/SOURCES.txt +1 -0
  11. {euler_preprocess-3.0.0 → euler_preprocess-3.2.0}/pyproject.toml +1 -1
  12. {euler_preprocess-3.0.0 → euler_preprocess-3.2.0}/tests/test_fog_aux_outputs.py +1205 -2
  13. euler_preprocess-3.0.0/euler_preprocess/fog/capture.py +0 -1806
  14. euler_preprocess-3.0.0/euler_preprocess/sky_depth/__init__.py +0 -0
  15. {euler_preprocess-3.0.0 → euler_preprocess-3.2.0}/euler_preprocess/__init__.py +0 -0
  16. {euler_preprocess-3.0.0 → euler_preprocess-3.2.0}/euler_preprocess/cli.py +0 -0
  17. {euler_preprocess-3.0.0 → euler_preprocess-3.2.0}/euler_preprocess/common/__init__.py +0 -0
  18. {euler_preprocess-3.0.0 → euler_preprocess-3.2.0}/euler_preprocess/common/dataset.py +0 -0
  19. {euler_preprocess-3.0.0 → euler_preprocess-3.2.0}/euler_preprocess/common/device.py +0 -0
  20. {euler_preprocess-3.0.0 → euler_preprocess-3.2.0}/euler_preprocess/common/intrinsics.py +0 -0
  21. {euler_preprocess-3.0.0 → euler_preprocess-3.2.0}/euler_preprocess/common/io.py +0 -0
  22. {euler_preprocess-3.0.0 → euler_preprocess-3.2.0}/euler_preprocess/common/logging.py +0 -0
  23. {euler_preprocess-3.0.0 → euler_preprocess-3.2.0}/euler_preprocess/common/noise.py +0 -0
  24. {euler_preprocess-3.0.0 → euler_preprocess-3.2.0}/euler_preprocess/common/normalize.py +0 -0
  25. {euler_preprocess-3.0.0 → euler_preprocess-3.2.0}/euler_preprocess/common/output.py +0 -0
  26. {euler_preprocess-3.0.0 → euler_preprocess-3.2.0}/euler_preprocess/common/sampling.py +0 -0
  27. {euler_preprocess-3.0.0 → euler_preprocess-3.2.0}/euler_preprocess/common/transform.py +0 -0
  28. {euler_preprocess-3.0.0 → euler_preprocess-3.2.0}/euler_preprocess/fog/airlight_from_sky.py +0 -0
  29. {euler_preprocess-3.0.0 → euler_preprocess-3.2.0}/euler_preprocess/fog/augmentations.py +0 -0
  30. {euler_preprocess-3.0.0 → euler_preprocess-3.2.0}/euler_preprocess/fog/dcp_airlight.py +0 -0
  31. {euler_preprocess-3.0.0 → euler_preprocess-3.2.0}/euler_preprocess/fog/dcp_airlight_torch.py +0 -0
  32. {euler_preprocess-3.0.0 → euler_preprocess-3.2.0}/euler_preprocess/fog/dcp_heuristic_airlight.py +0 -0
  33. {euler_preprocess-3.0.0 → euler_preprocess-3.2.0}/euler_preprocess/fog/dcp_heuristic_airlight_torch.py +0 -0
  34. {euler_preprocess-3.0.0 → euler_preprocess-3.2.0}/euler_preprocess/fog/foggify.py +0 -0
  35. {euler_preprocess-3.0.0 → euler_preprocess-3.2.0}/euler_preprocess/fog/foggify_logging.py +0 -0
  36. {euler_preprocess-3.0.0 → euler_preprocess-3.2.0}/euler_preprocess/fog/logging.py +0 -0
  37. {euler_preprocess-3.0.0 → euler_preprocess-3.2.0}/euler_preprocess/fog/models.py +0 -0
  38. {euler_preprocess-3.0.0/euler_preprocess/fog → euler_preprocess-3.2.0/euler_preprocess/radial}/__init__.py +0 -0
  39. {euler_preprocess-3.0.0 → euler_preprocess-3.2.0}/euler_preprocess/radial/transform.py +0 -0
  40. {euler_preprocess-3.0.0/euler_preprocess/radial → euler_preprocess-3.2.0/euler_preprocess/sky_depth}/__init__.py +0 -0
  41. {euler_preprocess-3.0.0 → euler_preprocess-3.2.0}/euler_preprocess/sky_depth/transform.py +0 -0
  42. {euler_preprocess-3.0.0 → euler_preprocess-3.2.0}/euler_preprocess.egg-info/dependency_links.txt +0 -0
  43. {euler_preprocess-3.0.0 → euler_preprocess-3.2.0}/euler_preprocess.egg-info/entry_points.txt +0 -0
  44. {euler_preprocess-3.0.0 → euler_preprocess-3.2.0}/euler_preprocess.egg-info/requires.txt +0 -0
  45. {euler_preprocess-3.0.0 → euler_preprocess-3.2.0}/euler_preprocess.egg-info/top_level.txt +0 -0
  46. {euler_preprocess-3.0.0 → euler_preprocess-3.2.0}/setup.cfg +0 -0
  47. {euler_preprocess-3.0.0 → euler_preprocess-3.2.0}/tests/test_airlight_fallback.py +0 -0
  48. {euler_preprocess-3.0.0 → euler_preprocess-3.2.0}/tests/test_cli_sample_selection.py +0 -0
  49. {euler_preprocess-3.0.0 → euler_preprocess-3.2.0}/tests/test_dcp_heuristic_airlight.py +0 -0
  50. {euler_preprocess-3.0.0 → euler_preprocess-3.2.0}/tests/test_foggify_integration.py +0 -0
  51. {euler_preprocess-3.0.0 → euler_preprocess-3.2.0}/tests/test_radial.py +0 -0
  52. {euler_preprocess-3.0.0 → euler_preprocess-3.2.0}/tests/test_sky_depth.py +0 -0
  53. {euler_preprocess-3.0.0 → euler_preprocess-3.2.0}/tests/test_source_backed_output.py +0 -0
  54. {euler_preprocess-3.0.0 → euler_preprocess-3.2.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.0.0
3
+ Version: 3.2.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
@@ -170,6 +170,7 @@ Controls the fog simulation.
170
170
  | `capture` / `capture_artifacts` | Optional post-fog camera artifact pipeline. Omit it or set `{"stages": []}` for the legacy no-op path. Set `true`, `{"preset": "camera"}`, or a custom `stages` list to enable optics, raw sensor, ISP, and compression artifacts. |
171
171
  | `camera_profile` | Optional named or inline camera profile whose stage defaults are merged into the capture stack before per-stage overrides. Built-ins are `"default"`, `"generic"`, `"dashcam"`, and `"low_light_fog"`. |
172
172
  | `camera_profiles` | Optional map of project-specific named profiles. Use this for calibrated lens, sensor, ISP, and transport settings. |
173
+ | `scenario_profiles` | Optional top-level correlated condition sampler. Each sampled scenario can choose the fog model, override model parameters, select the airlight method, switch camera profile/settings, and force named capture-stage condition profiles. |
173
174
  | `augmentations` | Optional stepped augmentation set. When present, every input sample produces every configured augmentation and uses the file-id hierarchy output layout described below. |
174
175
 
175
176
  ### Processing Pipeline
@@ -214,8 +215,32 @@ For tighter control, provide explicit stages in camera order:
214
215
  "bayer_pattern": "RGGB",
215
216
  "iso": {"dist": "choice", "values": [200, 400, 800]},
216
217
  "base_iso": 100,
218
+ "auto_exposure": {
219
+ "enabled": true,
220
+ "metering": "center_weighted",
221
+ "target_luminance": {"dist": "uniform", "min": 0.16, "max": 0.24},
222
+ "highlight_protection": 0.7,
223
+ "resolve_iso": true,
224
+ "max_iso": 1600
225
+ },
217
226
  "full_well_electrons": [14000, 12000, 13000],
218
227
  "read_noise_electrons": {"dist": "uniform", "min": 2.0, "max": 6.0},
228
+ "shadow_recovery_noise": {
229
+ "enabled": true,
230
+ "luminance_threshold": {"dist": "uniform", "min": 0.16, "max": 0.24},
231
+ "luma_sigma": {"dist": "uniform", "min": 0.0, "max": 0.001},
232
+ "chroma_sigma": {"dist": "uniform", "min": 0.012, "max": 0.028},
233
+ "chroma_mode": "balanced",
234
+ "red_chroma_gain": {"dist": "uniform", "min": 0.7, "max": 1.0},
235
+ "blue_chroma_gain": {"dist": "uniform", "min": 1.7, "max": 2.7},
236
+ "chroma_axis_correlation": {"dist": "uniform", "min": 0.05, "max": 0.25},
237
+ "chroma_spatial_sigma": 0.0,
238
+ "chroma_fine_fraction": 1.0,
239
+ "chroma_luminance_preservation": 1.0,
240
+ "black_noise_floor": 0.25,
241
+ "black_suppression_luminance": 0.03,
242
+ "black_suppression_softness": 0.08
243
+ },
219
244
  "black_level": [0.003, 0.0035, 0.003],
220
245
  "white_level": [1.0, 0.995, 1.0],
221
246
  "adc_bit_depth": 12,
@@ -272,11 +297,32 @@ Supported stage types:
272
297
  | Stage | Main effects |
273
298
  |---|---|
274
299
  | `optics` | Defocus/MTF blur, motion blur, bloom, veiling glare, vignetting, chromatic aberration, lens distortion, windshield haze, optional droplets. |
275
- | `sensor` | Exposure, white balance, camera matrix, Bayer mosaic, shot/read noise, fixed-pattern noise, row/column banding, hot/dead pixels, bilinear demosaic. |
300
+ | `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. |
276
301
  | `isp` | Denoising, color correction, tone mapping, sRGB/gamma, local contrast, sharpening halos, saturation shifts. |
277
302
  | `transport` | Crop/resize, bit-depth quantization, JPEG round-trip compression. |
278
303
  | `exposure` | Lightweight standalone exposure and white-balance stage for simple custom chains. |
279
304
 
305
+ Set `sensor.auto_exposure.enabled` to meter the rendered image before raw sensor
306
+ sampling. `target_luminance`, `metering`, `highlight_*`, and gain bounds choose
307
+ the exposure; `resolve_iso` can raise ISO from the metering pressure, dark pixel
308
+ fraction, and fog opacity. When auto exposure is enabled, `exposure_gain` still
309
+ applies as scenario-specific exposure compensation.
310
+
311
+ Set `sensor.shadow_recovery_noise.enabled` to add extra post-demosaic luma and
312
+ chroma corruption only where the pre-exposure rendered luminance was low. This
313
+ is useful for reducing broad global grain while keeping lifted shadows visibly
314
+ noisy. For dark high-ISO scenes, keep `luma_sigma` much lower than
315
+ `chroma_sigma`, use `chroma_mode: "balanced"`, and leave
316
+ `chroma_luminance_preservation` near `1.0`; this makes the local corruption read
317
+ as color noise instead of black luma speckles. `red_chroma_gain`,
318
+ `blue_chroma_gain`, and `chroma_axis_correlation` can match camera-specific
319
+ color noise, including blue/purple-biased high-ISO noise. Keep
320
+ `chroma_spatial_sigma` near `0` and avoid chroma subsampling when the target
321
+ noise is fine-grained rather than blocky. `black_noise_floor` with
322
+ `black_suppression_luminance`/`black_suppression_softness` reduces the extra
323
+ noise in near-clipped black regions, so the strongest visible noise sits in
324
+ dim-but-readable shadows.
325
+
280
326
  Any stage can define `condition_profiles` to sample coherent per-image settings
281
327
  before the stage runs. This is useful for exposure states where ISO, exposure
282
328
  gain, read noise, banding, and dark/fog noise modulation should move together:
@@ -291,6 +337,51 @@ gain, read noise, banding, and dark/fog noise modulation should move together:
291
337
  }
292
338
  ```
293
339
 
340
+ Top-level `scenario_profiles` sample one latent scene/camera condition before
341
+ rendering. The selected scenario is merged over the root config, so it can drive
342
+ fog density, atmospheric light, camera profile, capture-stage overrides, ISP, and
343
+ compression together:
344
+
345
+ ```json
346
+ "scenario_profiles": [
347
+ {
348
+ "name": "clean_low_noise_haze",
349
+ "weight": 0.22,
350
+ "model": "heterogeneous_k_ls",
351
+ "airlight_method": "dcp_heuristic",
352
+ "models": {
353
+ "heterogeneous_k_ls": {
354
+ "visibility_m": {"dist": "uniform", "min": 60.0, "max": 130.0}
355
+ }
356
+ },
357
+ "capture_overrides": {
358
+ "sensor": {"condition_profile": "clean_daylight"},
359
+ "isp": {"denoise_sigma": {"dist": "uniform", "min": 0.08, "max": 0.32}}
360
+ }
361
+ },
362
+ {
363
+ "name": "underexposed_dense_gloom",
364
+ "weight": 0.25,
365
+ "model": "heterogeneous_k_ls",
366
+ "airlight_method": "dcp_heuristic",
367
+ "models": {
368
+ "heterogeneous_k_ls": {
369
+ "visibility_m": {"dist": "uniform", "min": 18.0, "max": 55.0}
370
+ }
371
+ },
372
+ "capture_overrides": {
373
+ "sensor": {"condition_profile": "underexposed_noisy"},
374
+ "transport": {"jpeg": {"quality": {"dist": "uniform", "min": 54, "max": 78}}}
375
+ }
376
+ }
377
+ ]
378
+ ```
379
+
380
+ `capture_overrides` is merged after camera-profile and stage settings. Use
381
+ `condition_profile` to force one named profile from a stage's
382
+ `condition_profiles`; if omitted, the stage continues sampling its own profile
383
+ weights locally.
384
+
294
385
  ### Fog Model
295
386
 
296
387
  The core equation is the **Koschmieder model** (atmospheric scattering):
@@ -156,6 +156,7 @@ Controls the fog simulation.
156
156
  | `capture` / `capture_artifacts` | Optional post-fog camera artifact pipeline. Omit it or set `{"stages": []}` for the legacy no-op path. Set `true`, `{"preset": "camera"}`, or a custom `stages` list to enable optics, raw sensor, ISP, and compression artifacts. |
157
157
  | `camera_profile` | Optional named or inline camera profile whose stage defaults are merged into the capture stack before per-stage overrides. Built-ins are `"default"`, `"generic"`, `"dashcam"`, and `"low_light_fog"`. |
158
158
  | `camera_profiles` | Optional map of project-specific named profiles. Use this for calibrated lens, sensor, ISP, and transport settings. |
159
+ | `scenario_profiles` | Optional top-level correlated condition sampler. Each sampled scenario can choose the fog model, override model parameters, select the airlight method, switch camera profile/settings, and force named capture-stage condition profiles. |
159
160
  | `augmentations` | Optional stepped augmentation set. When present, every input sample produces every configured augmentation and uses the file-id hierarchy output layout described below. |
160
161
 
161
162
  ### Processing Pipeline
@@ -200,8 +201,32 @@ For tighter control, provide explicit stages in camera order:
200
201
  "bayer_pattern": "RGGB",
201
202
  "iso": {"dist": "choice", "values": [200, 400, 800]},
202
203
  "base_iso": 100,
204
+ "auto_exposure": {
205
+ "enabled": true,
206
+ "metering": "center_weighted",
207
+ "target_luminance": {"dist": "uniform", "min": 0.16, "max": 0.24},
208
+ "highlight_protection": 0.7,
209
+ "resolve_iso": true,
210
+ "max_iso": 1600
211
+ },
203
212
  "full_well_electrons": [14000, 12000, 13000],
204
213
  "read_noise_electrons": {"dist": "uniform", "min": 2.0, "max": 6.0},
214
+ "shadow_recovery_noise": {
215
+ "enabled": true,
216
+ "luminance_threshold": {"dist": "uniform", "min": 0.16, "max": 0.24},
217
+ "luma_sigma": {"dist": "uniform", "min": 0.0, "max": 0.001},
218
+ "chroma_sigma": {"dist": "uniform", "min": 0.012, "max": 0.028},
219
+ "chroma_mode": "balanced",
220
+ "red_chroma_gain": {"dist": "uniform", "min": 0.7, "max": 1.0},
221
+ "blue_chroma_gain": {"dist": "uniform", "min": 1.7, "max": 2.7},
222
+ "chroma_axis_correlation": {"dist": "uniform", "min": 0.05, "max": 0.25},
223
+ "chroma_spatial_sigma": 0.0,
224
+ "chroma_fine_fraction": 1.0,
225
+ "chroma_luminance_preservation": 1.0,
226
+ "black_noise_floor": 0.25,
227
+ "black_suppression_luminance": 0.03,
228
+ "black_suppression_softness": 0.08
229
+ },
205
230
  "black_level": [0.003, 0.0035, 0.003],
206
231
  "white_level": [1.0, 0.995, 1.0],
207
232
  "adc_bit_depth": 12,
@@ -258,11 +283,32 @@ Supported stage types:
258
283
  | Stage | Main effects |
259
284
  |---|---|
260
285
  | `optics` | Defocus/MTF blur, motion blur, bloom, veiling glare, vignetting, chromatic aberration, lens distortion, windshield haze, optional droplets. |
261
- | `sensor` | Exposure, white balance, camera matrix, Bayer mosaic, shot/read noise, fixed-pattern noise, row/column banding, hot/dead pixels, bilinear demosaic. |
286
+ | `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. |
262
287
  | `isp` | Denoising, color correction, tone mapping, sRGB/gamma, local contrast, sharpening halos, saturation shifts. |
263
288
  | `transport` | Crop/resize, bit-depth quantization, JPEG round-trip compression. |
264
289
  | `exposure` | Lightweight standalone exposure and white-balance stage for simple custom chains. |
265
290
 
291
+ Set `sensor.auto_exposure.enabled` to meter the rendered image before raw sensor
292
+ sampling. `target_luminance`, `metering`, `highlight_*`, and gain bounds choose
293
+ the exposure; `resolve_iso` can raise ISO from the metering pressure, dark pixel
294
+ fraction, and fog opacity. When auto exposure is enabled, `exposure_gain` still
295
+ applies as scenario-specific exposure compensation.
296
+
297
+ Set `sensor.shadow_recovery_noise.enabled` to add extra post-demosaic luma and
298
+ chroma corruption only where the pre-exposure rendered luminance was low. This
299
+ is useful for reducing broad global grain while keeping lifted shadows visibly
300
+ noisy. For dark high-ISO scenes, keep `luma_sigma` much lower than
301
+ `chroma_sigma`, use `chroma_mode: "balanced"`, and leave
302
+ `chroma_luminance_preservation` near `1.0`; this makes the local corruption read
303
+ as color noise instead of black luma speckles. `red_chroma_gain`,
304
+ `blue_chroma_gain`, and `chroma_axis_correlation` can match camera-specific
305
+ color noise, including blue/purple-biased high-ISO noise. Keep
306
+ `chroma_spatial_sigma` near `0` and avoid chroma subsampling when the target
307
+ noise is fine-grained rather than blocky. `black_noise_floor` with
308
+ `black_suppression_luminance`/`black_suppression_softness` reduces the extra
309
+ noise in near-clipped black regions, so the strongest visible noise sits in
310
+ dim-but-readable shadows.
311
+
266
312
  Any stage can define `condition_profiles` to sample coherent per-image settings
267
313
  before the stage runs. This is useful for exposure states where ISO, exposure
268
314
  gain, read noise, banding, and dark/fog noise modulation should move together:
@@ -277,6 +323,51 @@ gain, read noise, banding, and dark/fog noise modulation should move together:
277
323
  }
278
324
  ```
279
325
 
326
+ Top-level `scenario_profiles` sample one latent scene/camera condition before
327
+ rendering. The selected scenario is merged over the root config, so it can drive
328
+ fog density, atmospheric light, camera profile, capture-stage overrides, ISP, and
329
+ compression together:
330
+
331
+ ```json
332
+ "scenario_profiles": [
333
+ {
334
+ "name": "clean_low_noise_haze",
335
+ "weight": 0.22,
336
+ "model": "heterogeneous_k_ls",
337
+ "airlight_method": "dcp_heuristic",
338
+ "models": {
339
+ "heterogeneous_k_ls": {
340
+ "visibility_m": {"dist": "uniform", "min": 60.0, "max": 130.0}
341
+ }
342
+ },
343
+ "capture_overrides": {
344
+ "sensor": {"condition_profile": "clean_daylight"},
345
+ "isp": {"denoise_sigma": {"dist": "uniform", "min": 0.08, "max": 0.32}}
346
+ }
347
+ },
348
+ {
349
+ "name": "underexposed_dense_gloom",
350
+ "weight": 0.25,
351
+ "model": "heterogeneous_k_ls",
352
+ "airlight_method": "dcp_heuristic",
353
+ "models": {
354
+ "heterogeneous_k_ls": {
355
+ "visibility_m": {"dist": "uniform", "min": 18.0, "max": 55.0}
356
+ }
357
+ },
358
+ "capture_overrides": {
359
+ "sensor": {"condition_profile": "underexposed_noisy"},
360
+ "transport": {"jpeg": {"quality": {"dist": "uniform", "min": 54, "max": 78}}}
361
+ }
362
+ }
363
+ ]
364
+ ```
365
+
366
+ `capture_overrides` is merged after camera-profile and stage settings. Use
367
+ `condition_profile` to force one named profile from a stage's
368
+ `condition_profiles`; if omitted, the stage continues sampling its own profile
369
+ weights locally.
370
+
280
371
  ### Fog Model
281
372
 
282
373
  The core equation is the **Koschmieder model** (atmospheric scattering):
@@ -0,0 +1,11 @@
1
+ from euler_preprocess.fog.inference import (
2
+ FogInferenceResult,
3
+ render_fog_image,
4
+ render_fog_sample,
5
+ )
6
+
7
+ __all__ = [
8
+ "FogInferenceResult",
9
+ "render_fog_image",
10
+ "render_fog_sample",
11
+ ]
@@ -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
- if self.method == "from_sky":
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(self.method)
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 '{self.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 self.method == "dcp":
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(