euler-preprocess 3.17.0__tar.gz → 3.19.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.19.0/CHANGELOG.md +44 -0
- {euler_preprocess-3.17.0/euler_preprocess.egg-info → euler_preprocess-3.19.0}/PKG-INFO +144 -3
- {euler_preprocess-3.17.0 → euler_preprocess-3.19.0}/README.md +141 -0
- euler_preprocess-3.19.0/docs/plans/coherent-cropping.md +521 -0
- {euler_preprocess-3.17.0 → euler_preprocess-3.19.0}/euler_preprocess/cli.py +55 -1
- {euler_preprocess-3.17.0 → euler_preprocess-3.19.0}/euler_preprocess/common/intrinsics.py +26 -6
- {euler_preprocess-3.17.0 → euler_preprocess-3.19.0}/euler_preprocess/common/output.py +162 -0
- euler_preprocess-3.19.0/euler_preprocess/crop/__init__.py +1 -0
- euler_preprocess-3.19.0/euler_preprocess/crop/calibration.py +103 -0
- euler_preprocess-3.19.0/euler_preprocess/crop/files.py +84 -0
- euler_preprocess-3.19.0/euler_preprocess/crop/roles.py +169 -0
- euler_preprocess-3.19.0/euler_preprocess/crop/run.py +770 -0
- euler_preprocess-3.19.0/euler_preprocess/crop/spec.py +444 -0
- euler_preprocess-3.19.0/euler_preprocess/crop/verify.py +109 -0
- euler_preprocess-3.19.0/euler_preprocess/lidar_motion/compensation.py +78 -0
- euler_preprocess-3.19.0/euler_preprocess/lidar_motion/transform.py +162 -0
- euler_preprocess-3.19.0/euler_preprocess/sky_depth/__init__.py +0 -0
- {euler_preprocess-3.17.0 → euler_preprocess-3.19.0/euler_preprocess.egg-info}/PKG-INFO +144 -3
- {euler_preprocess-3.17.0 → euler_preprocess-3.19.0}/euler_preprocess.egg-info/SOURCES.txt +16 -0
- {euler_preprocess-3.17.0 → euler_preprocess-3.19.0}/euler_preprocess.egg-info/requires.txt +2 -2
- {euler_preprocess-3.17.0 → euler_preprocess-3.19.0}/pyproject.toml +3 -3
- euler_preprocess-3.19.0/tests/test_crop.py +423 -0
- euler_preprocess-3.19.0/tests/test_crop_roles.py +84 -0
- euler_preprocess-3.19.0/tests/test_crop_spec.py +195 -0
- euler_preprocess-3.19.0/tests/test_intrinsics.py +30 -0
- euler_preprocess-3.19.0/tests/test_lidar_motion.py +227 -0
- euler_preprocess-3.17.0/CHANGELOG.md +0 -5
- {euler_preprocess-3.17.0 → euler_preprocess-3.19.0}/LICENSE +0 -0
- {euler_preprocess-3.17.0 → euler_preprocess-3.19.0}/MANIFEST.in +0 -0
- {euler_preprocess-3.17.0 → euler_preprocess-3.19.0}/docs/spatial.md +0 -0
- {euler_preprocess-3.17.0 → euler_preprocess-3.19.0}/euler_preprocess/__init__.py +0 -0
- {euler_preprocess-3.17.0 → euler_preprocess-3.19.0}/euler_preprocess/common/__init__.py +0 -0
- {euler_preprocess-3.17.0 → euler_preprocess-3.19.0}/euler_preprocess/common/color.py +0 -0
- {euler_preprocess-3.17.0 → euler_preprocess-3.19.0}/euler_preprocess/common/dataset.py +0 -0
- {euler_preprocess-3.17.0 → euler_preprocess-3.19.0}/euler_preprocess/common/device.py +0 -0
- {euler_preprocess-3.17.0 → euler_preprocess-3.19.0}/euler_preprocess/common/io.py +0 -0
- {euler_preprocess-3.17.0 → euler_preprocess-3.19.0}/euler_preprocess/common/logging.py +0 -0
- {euler_preprocess-3.17.0 → euler_preprocess-3.19.0}/euler_preprocess/common/noise.py +0 -0
- {euler_preprocess-3.17.0 → euler_preprocess-3.19.0}/euler_preprocess/common/normalize.py +0 -0
- {euler_preprocess-3.17.0 → euler_preprocess-3.19.0}/euler_preprocess/common/sampling.py +0 -0
- {euler_preprocess-3.17.0 → euler_preprocess-3.19.0}/euler_preprocess/common/transform.py +0 -0
- {euler_preprocess-3.17.0 → euler_preprocess-3.19.0}/euler_preprocess/fog/__init__.py +0 -0
- {euler_preprocess-3.17.0 → euler_preprocess-3.19.0}/euler_preprocess/fog/airlight_from_sky.py +0 -0
- {euler_preprocess-3.17.0 → euler_preprocess-3.19.0}/euler_preprocess/fog/atmospheric_light.py +0 -0
- {euler_preprocess-3.17.0 → euler_preprocess-3.19.0}/euler_preprocess/fog/augmentations.py +0 -0
- {euler_preprocess-3.17.0 → euler_preprocess-3.19.0}/euler_preprocess/fog/capture.py +0 -0
- {euler_preprocess-3.17.0 → euler_preprocess-3.19.0}/euler_preprocess/fog/dcp_airlight.py +0 -0
- {euler_preprocess-3.17.0 → euler_preprocess-3.19.0}/euler_preprocess/fog/dcp_airlight_torch.py +0 -0
- {euler_preprocess-3.17.0 → euler_preprocess-3.19.0}/euler_preprocess/fog/dcp_heuristic_airlight.py +0 -0
- {euler_preprocess-3.17.0 → euler_preprocess-3.19.0}/euler_preprocess/fog/dcp_heuristic_airlight_torch.py +0 -0
- {euler_preprocess-3.17.0 → euler_preprocess-3.19.0}/euler_preprocess/fog/inference.py +0 -0
- {euler_preprocess-3.17.0 → euler_preprocess-3.19.0}/euler_preprocess/fog/logging.py +0 -0
- {euler_preprocess-3.17.0 → euler_preprocess-3.19.0}/euler_preprocess/fog/models.py +0 -0
- {euler_preprocess-3.17.0 → euler_preprocess-3.19.0}/euler_preprocess/fog/pipeline.py +0 -0
- {euler_preprocess-3.17.0 → euler_preprocess-3.19.0}/euler_preprocess/fog/transform.py +0 -0
- {euler_preprocess-3.17.0/euler_preprocess/radial → euler_preprocess-3.19.0/euler_preprocess/lidar_motion}/__init__.py +0 -0
- {euler_preprocess-3.17.0/euler_preprocess/sky_depth → euler_preprocess-3.19.0/euler_preprocess/radial}/__init__.py +0 -0
- {euler_preprocess-3.17.0 → euler_preprocess-3.19.0}/euler_preprocess/radial/transform.py +0 -0
- {euler_preprocess-3.17.0 → euler_preprocess-3.19.0}/euler_preprocess/sky_depth/transform.py +0 -0
- {euler_preprocess-3.17.0 → euler_preprocess-3.19.0}/euler_preprocess/spatial.py +0 -0
- {euler_preprocess-3.17.0 → euler_preprocess-3.19.0}/euler_preprocess.egg-info/dependency_links.txt +0 -0
- {euler_preprocess-3.17.0 → euler_preprocess-3.19.0}/euler_preprocess.egg-info/entry_points.txt +0 -0
- {euler_preprocess-3.17.0 → euler_preprocess-3.19.0}/euler_preprocess.egg-info/top_level.txt +0 -0
- {euler_preprocess-3.17.0 → euler_preprocess-3.19.0}/examples/spatial-five-field.json +0 -0
- {euler_preprocess-3.17.0 → euler_preprocess-3.19.0}/setup.cfg +0 -0
- {euler_preprocess-3.17.0 → euler_preprocess-3.19.0}/tests/test_airlight_fallback.py +0 -0
- {euler_preprocess-3.17.0 → euler_preprocess-3.19.0}/tests/test_cli_sample_selection.py +0 -0
- {euler_preprocess-3.17.0 → euler_preprocess-3.19.0}/tests/test_dcp_heuristic_airlight.py +0 -0
- {euler_preprocess-3.17.0 → euler_preprocess-3.19.0}/tests/test_dense_gloomy_daylight_config.py +0 -0
- {euler_preprocess-3.17.0 → euler_preprocess-3.19.0}/tests/test_fog_aux_outputs.py +0 -0
- {euler_preprocess-3.17.0 → euler_preprocess-3.19.0}/tests/test_fog_aware_auto_exposure.py +0 -0
- {euler_preprocess-3.17.0 → euler_preprocess-3.19.0}/tests/test_fog_cpu_gpu_parity.py +0 -0
- {euler_preprocess-3.17.0 → euler_preprocess-3.19.0}/tests/test_fog_input_normalization.py +0 -0
- {euler_preprocess-3.17.0 → euler_preprocess-3.19.0}/tests/test_fog_integration.py +0 -0
- {euler_preprocess-3.17.0 → euler_preprocess-3.19.0}/tests/test_modality_roles.py +0 -0
- {euler_preprocess-3.17.0 → euler_preprocess-3.19.0}/tests/test_radial.py +0 -0
- {euler_preprocess-3.17.0 → euler_preprocess-3.19.0}/tests/test_real_drive_sim_scenario_profiles.py +0 -0
- {euler_preprocess-3.17.0 → euler_preprocess-3.19.0}/tests/test_scene_illumination.py +0 -0
- {euler_preprocess-3.17.0 → euler_preprocess-3.19.0}/tests/test_sensor_identity.py +0 -0
- {euler_preprocess-3.17.0 → euler_preprocess-3.19.0}/tests/test_sky_depth.py +0 -0
- {euler_preprocess-3.17.0 → euler_preprocess-3.19.0}/tests/test_source_backed_output.py +0 -0
- {euler_preprocess-3.17.0 → euler_preprocess-3.19.0}/tests/test_spatial.py +0 -0
- {euler_preprocess-3.17.0 → euler_preprocess-3.19.0}/tests/test_tone_map_lut.py +0 -0
- {euler_preprocess-3.17.0 → euler_preprocess-3.19.0}/tests/test_zip_output.py +0 -0
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
## Unreleased
|
|
4
|
+
|
|
5
|
+
- Add `euler-preprocess lidar-motion`, which compensates scanning-lidar points for
|
|
6
|
+
the vehicle's motion during the sweep. It follows the MUSES SDK, moving each
|
|
7
|
+
point to the frame camera's mid-exposure using the `gnss`, `sensor_timing`
|
|
8
|
+
and `lidar_to_gnss` modalities.
|
|
9
|
+
- Require euler-loading 2.32.0, the first release with `muses.gnss`,
|
|
10
|
+
`muses.sensor_timing`, `muses.read_lidar_to_gnss` and `muses.write_sparse_depth`.
|
|
11
|
+
- Add `euler-preprocess crop`, which crops RGB, depth, intrinsics and
|
|
12
|
+
extrinsics coherently. The box is given as fractions or pixels, either removed
|
|
13
|
+
per edge or kept with an anchor, relative to a reference raster. Rasters are
|
|
14
|
+
sliced, pinhole intrinsics get `cx -= left`, `cy -= top` merged into a copy of
|
|
15
|
+
their file, and lidar point clouds and extrinsics are copied unchanged. Each
|
|
16
|
+
modality's treatment comes from its loader annotation, not its name. Shared
|
|
17
|
+
calibration files are written once, and samples with different crops cannot
|
|
18
|
+
share one. The outputs are reopened and checked against the crop.
|
|
19
|
+
`configs/crop_stf_valid_region_config.json` removes SeeingThroughFog's black
|
|
20
|
+
rectification border.
|
|
21
|
+
- `crop` phase 3:
|
|
22
|
+
- `point_clouds: "prune_to_crop"` keeps only the lidar points that project
|
|
23
|
+
into the output image (`z > 0`, `rint(K'·T·X)` inside), the projection
|
|
24
|
+
consumers such as dmd3c-euler use;
|
|
25
|
+
- `resize` (`size` or `scale`) after the crop, with `K' = S·C·K`; colour
|
|
26
|
+
images are interpolated bilinearly, all other rasters take the nearest
|
|
27
|
+
pixel;
|
|
28
|
+
- `"output_layout": "scoped"` writes one root (directory or zip) with one
|
|
29
|
+
metadata scope per modality, as dmd3c-euler's `--dataset-root` reads;
|
|
30
|
+
- every encoded raster or pruned cloud is decoded again and must reproduce
|
|
31
|
+
the values;
|
|
32
|
+
- `euler-preprocess crop-box` prints the pixel box and the matching
|
|
33
|
+
`spatial` preprocessing block.
|
|
34
|
+
- `crop` uses the new public euler-loading accessors (`get_loader_meta`,
|
|
35
|
+
`get_hierarchical_files`) and ds-crawler's `set_modality_meta` / `path_prefix`.
|
|
36
|
+
Requires euler-loading 2.33.0 and ds-crawler 2.14.0.
|
|
37
|
+
- `extract_intrinsics` (radial, fog) also uses a hierarchical intrinsics file
|
|
38
|
+
that is not named `intrinsics` when it is the only one, such as SeeingThroughFog's
|
|
39
|
+
`calib_cam_stereo_left`. Several matching files now raise instead of being
|
|
40
|
+
ignored.
|
|
41
|
+
|
|
42
|
+
## 3.14.0 (unreleased)
|
|
43
|
+
|
|
44
|
+
Consume shared preprocessing config and add the opt-in spatial producer, explicit variants/mappings and captured source-backed outputs. Preserve ordinary pipeline APIs.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: euler-preprocess
|
|
3
|
-
Version: 3.
|
|
3
|
+
Version: 3.19.0
|
|
4
4
|
Summary: Physics-based preprocessing (fog, etc.) for RGB+depth datasets
|
|
5
5
|
Author-email: Daniel Rothenpieler <rothenpielerdaniel@gmail.com>
|
|
6
6
|
License-Expression: MIT
|
|
@@ -9,8 +9,8 @@ Description-Content-Type: text/markdown
|
|
|
9
9
|
License-File: LICENSE
|
|
10
10
|
Requires-Dist: numpy
|
|
11
11
|
Requires-Dist: Pillow
|
|
12
|
-
Requires-Dist: euler-loading>=2.
|
|
13
|
-
Requires-Dist: ds-crawler>=2.
|
|
12
|
+
Requires-Dist: euler-loading>=2.33.0
|
|
13
|
+
Requires-Dist: ds-crawler>=2.14.0
|
|
14
14
|
Requires-Dist: euler-dataset-contract>=0.5.0
|
|
15
15
|
Provides-Extra: gpu
|
|
16
16
|
Requires-Dist: torch; extra == "gpu"
|
|
@@ -64,6 +64,9 @@ qualitative review.
|
|
|
64
64
|
| `euler-preprocess fog` | Synthetic fog via the Koschmieder atmospheric scattering model |
|
|
65
65
|
| `euler-preprocess sky-depth` | Override depth values in sky regions with a constant |
|
|
66
66
|
| `euler-preprocess radial` | Convert planar (z-buffer) depth to radial (Euclidean) depth |
|
|
67
|
+
| `euler-preprocess lidar-motion` | Compensate scanning-lidar points for the vehicle's motion during a sweep |
|
|
68
|
+
| `euler-preprocess crop` | Crop RGB, depth, intrinsics and extrinsics coherently |
|
|
69
|
+
| `euler-preprocess crop-box` | Print the pixel box a crop config gives for an image size |
|
|
67
70
|
|
|
68
71
|
## Installation
|
|
69
72
|
|
|
@@ -77,6 +80,8 @@ uv pip install "euler-preprocess[gpu,progress]"
|
|
|
77
80
|
euler-preprocess fog -c configs/example_dataset_config.json
|
|
78
81
|
euler-preprocess sky-depth -c configs/sky_depth_dataset_config.json
|
|
79
82
|
euler-preprocess radial -c configs/radial_dataset_config.json
|
|
83
|
+
euler-preprocess lidar-motion -c configs/lidar_motion_dataset_config.json
|
|
84
|
+
euler-preprocess crop -c configs/crop_stf_dataset_config.json
|
|
80
85
|
```
|
|
81
86
|
|
|
82
87
|
## Dataset Config
|
|
@@ -118,6 +123,7 @@ loader.
|
|
|
118
123
|
| `fog` | `rgb`, `depth`, `semantic_segmentation` | `intrinsics`; used for radial depth conversion and camera-profile optics |
|
|
119
124
|
| `sky-depth` | `depth`, `semantic_segmentation` | — |
|
|
120
125
|
| `radial` | `depth`, `intrinsics` | — |
|
|
126
|
+
| `lidar-motion` | `sparse_depth`, `gnss`, `sensor_timing`, `lidar_to_gnss` | — |
|
|
121
127
|
|
|
122
128
|
`fog` and `sky-depth` need `semantic_segmentation` as a boolean sky mask. They load
|
|
123
129
|
it with the `sky_mask` function of the euler-loading module that the index's
|
|
@@ -703,6 +709,141 @@ Standalone `RadialTransform(...)` usage keeps the legacy `.npy` output behaviour
|
|
|
703
709
|
|
|
704
710
|
---
|
|
705
711
|
|
|
712
|
+
## LiDAR Motion Compensation
|
|
713
|
+
|
|
714
|
+
A scanning lidar measures its points one after another while the vehicle moves.
|
|
715
|
+
The MUSES lidar sweeps its field of view top to bottom in about 85 ms, so
|
|
716
|
+
uncorrected points projected into the frame camera match tall objects at their
|
|
717
|
+
base and drift away towards the top. Across MUSES, 92 % of frames are taken
|
|
718
|
+
above 10 km/h. `lidar-motion` moves every point to where it was at the
|
|
719
|
+
frame camera's mid-exposure. It follows the official MUSES SDK
|
|
720
|
+
(`motion_compensate_pcd`): constant ground speed along the GNSS frame's `x`,
|
|
721
|
+
constant roll, pitch and heading rates.
|
|
722
|
+
|
|
723
|
+
| Modality | Content | MUSES loader (euler-loading) |
|
|
724
|
+
|---|---|---|
|
|
725
|
+
| `sparse_depth` | `(N, C)` points with each point's time in seconds | `muses.sparse_depth` |
|
|
726
|
+
| `gnss` | The frame's GNSS/IMU record | `muses.gnss` |
|
|
727
|
+
| `sensor_timing` | `camera_exposure_start_us` / `camera_exposure_end_us` | `muses.sensor_timing` (`meta.json`) |
|
|
728
|
+
| `lidar_to_gnss` | `(4, 4)` lidar → GNSS/IMU transform | `muses.read_lidar_to_gnss` (`calib.json`) |
|
|
729
|
+
|
|
730
|
+
Index `gnss/` like the `lidar` folder, so samples join it by ID. Index
|
|
731
|
+
`meta.json` and `calib.json` as root-level files; the euler-loading docs
|
|
732
|
+
describe the MUSES ego-motion inputs. The transform config takes one optional
|
|
733
|
+
key, `timestamp_column` (default `5`, MUSES' `timestamp` column).
|
|
734
|
+
|
|
735
|
+
The output is a `sparse_depth` dataset mirroring the input's paths, file names
|
|
736
|
+
and `.bin` format. Only `x, y, z` change. The head records
|
|
737
|
+
`meta.motion_compensated`, and each file's `attributes.motion_compensation`
|
|
738
|
+
holds the target time and the speed and rates used. Point
|
|
739
|
+
`dmd3c-euler`, `euler-eval` or a crop at it in place of the raw lidar.
|
|
740
|
+
|
|
741
|
+
---
|
|
742
|
+
|
|
743
|
+
## Coherent Crop
|
|
744
|
+
|
|
745
|
+
`crop` cuts the same box out of every image-shaped modality and corrects
|
|
746
|
+
everything that refers to the pixel grid, so a cropped bundle projects exactly
|
|
747
|
+
like the original:
|
|
748
|
+
|
|
749
|
+
| Modality kind | Crop |
|
|
750
|
+
|---|---|
|
|
751
|
+
| Rasters: RGB, dense depth, masks | Sliced; no resampling, so values are unchanged |
|
|
752
|
+
| Pinhole intrinsics `K` (or `P` for rectified images) | `cx -= left`, `cy -= top`; `fx`, `fy`, skew unchanged |
|
|
753
|
+
| Lidar point clouds | Copied byte for byte, or pruned to the points that project into the crop |
|
|
754
|
+
| Extrinsics | Copied byte for byte |
|
|
755
|
+
|
|
756
|
+
The extrinsics need no change, because `C·K·[R|t]·X = K'·[R|t]·X` with
|
|
757
|
+
`C = [[1,0,-left],[0,1,-top],[0,0,1]]`: the whole correction lives in the
|
|
758
|
+
camera matrix, which is also what the official SeeingThroughFog tools do. An
|
|
759
|
+
optional resize afterwards adds `S` (an `align_corners=False` rescale), so
|
|
760
|
+
`K' = S·C·K`, still with untouched extrinsics.
|
|
761
|
+
Which kind a modality is comes from its euler-loading loader annotation
|
|
762
|
+
(`shape`, `type`), never from its name.
|
|
763
|
+
|
|
764
|
+
The dataset config lists every modality to crop; whether a modality is
|
|
765
|
+
per-sample or hierarchical (shared calibration) is inferred from its index:
|
|
766
|
+
|
|
767
|
+
```json
|
|
768
|
+
{
|
|
769
|
+
"transform_config_path": "crop_stf_valid_region_config.json",
|
|
770
|
+
"output_path": "/data/stf_crop",
|
|
771
|
+
"modalities": {
|
|
772
|
+
"rgb": "/data/stf/cam_stereo_left_lut",
|
|
773
|
+
"sparse_depth": "/data/stf/lidar_hdl64_strongest",
|
|
774
|
+
"intrinsics": "/data/stf/calib_cam_stereo_left",
|
|
775
|
+
"camera_extrinsics": "/data/stf/calib_tf_tree"
|
|
776
|
+
},
|
|
777
|
+
"samples": {"start": 0, "count": 100}
|
|
778
|
+
}
|
|
779
|
+
```
|
|
780
|
+
|
|
781
|
+
Each modality is written to `<output_path>/<name>`, or to its
|
|
782
|
+
`pipeline.output_targets[]` entry (`slot` = the modality name; directory or zip).
|
|
783
|
+
With pipeline targets, every modality needs one and the outputs manifest lists
|
|
784
|
+
them all. `"output_layout": "scoped"` writes one root at `output_path` instead
|
|
785
|
+
(a directory or `.zip`): one ds-crawler metadata scope per modality, named after
|
|
786
|
+
it, with its files below `<name>/`. That is the layout dmd3c-euler's
|
|
787
|
+
`--dataset-root` reads. The dataset config must not contain `preprocessing`.
|
|
788
|
+
|
|
789
|
+
The crop config (`configs/crop_config.json`) describes the box relative to the
|
|
790
|
+
`reference` raster:
|
|
791
|
+
|
|
792
|
+
```json
|
|
793
|
+
{
|
|
794
|
+
"reference": "rgb",
|
|
795
|
+
"crop": {"remove": {"top": 0.20, "bottom": 0.05, "left": 0.15, "right": 0.19}},
|
|
796
|
+
"point_clouds": "keep",
|
|
797
|
+
"verify": "all"
|
|
798
|
+
}
|
|
799
|
+
```
|
|
800
|
+
|
|
801
|
+
| Key | Meaning |
|
|
802
|
+
|---|---|
|
|
803
|
+
| `crop.remove` | What to cut off each edge (`top`, `bottom`, `left`, `right`) |
|
|
804
|
+
| `crop.keep` + `crop.anchor` | The kept `height` / `width`, placed at `center` (default), `top`, `bottom`, `left`, `right`, `top_left`, `top_right`, `bottom_left` or `bottom_right` |
|
|
805
|
+
| `crop.unit` | `fraction` (default) or `pixel`. Fractions lie in `[0, 1)` (`keep`: `(0, 1]`) and round half up per edge: `floor(f · size + 0.5)` |
|
|
806
|
+
| `crop.multiple_of` | Trim the kept size down to a multiple, e.g. a network stride; the extra rows and columns come off both sides evenly |
|
|
807
|
+
| `resize` | Optional, after the crop: `{"size": [h, w]}` or `{"scale": 0.5}` (or `[sy, sx]`); `interpolation` maps modality names to `nearest`, `bilinear` or `bicubic` |
|
|
808
|
+
| `point_clouds` | `keep` (default): copy every point, consumers clip to the image. `prune_to_crop`: keep only the points with `z > 0` whose pixel `rint(K'·T·X)` lies in the output image, as consumers such as dmd3c-euler project them |
|
|
809
|
+
| `projection` | For `prune_to_crop`: the `intrinsics` and `extrinsics` modalities to project with, when the dataset has more than one candidate |
|
|
810
|
+
| `verify` | `all` (default), `none`, or a sample count: reopen the outputs and check every file against the crop (rasters and pruned clouds as encoded, intrinsics to 1e-3 px, copies byte-identical) |
|
|
811
|
+
| `roles` | Override a modality's kind with `raster` or `copy`, for loaders whose annotation the crop does not know |
|
|
812
|
+
|
|
813
|
+
Pixel units reproduce the official boxes exactly, e.g. `RGB2Gatedv2` is
|
|
814
|
+
`{"remove": {"top": 202, "bottom": 54, "left": 280, "right": 360}, "unit": "pixel"}`.
|
|
815
|
+
`configs/crop_stf_valid_region_config.json` removes the black border the
|
|
816
|
+
SeeingThroughFog rectification leaves on `_lut` frames (top 34, bottom 46,
|
|
817
|
+
left 16 px), keeping a fully valid 1904×944 image.
|
|
818
|
+
|
|
819
|
+
Rasters are re-encoded with the dataset's euler-loading writer, so a raster
|
|
820
|
+
modality needs one (SeeingThroughFog's raw `rccb` TIFFs have none). Each
|
|
821
|
+
encoded file is decoded again right away and must give back the cropped values:
|
|
822
|
+
exactly, or for an interpolated resize within half an 8-bit step. A resize
|
|
823
|
+
interpolates colour images (`rgb`, `rccb`) bilinearly and takes the nearest
|
|
824
|
+
pixel for everything else, so depth and label values stay real values;
|
|
825
|
+
sparse-depth rasters are not resized. Pruned point clouds need a writer too
|
|
826
|
+
(`princeton_dense.write_sparse_depth`, `muses.write_sparse_depth`). Intrinsics
|
|
827
|
+
are written by copying the source file and letting its writer move the matrices
|
|
828
|
+
onto the new grid: for a ROS CameraInfo file such as
|
|
829
|
+
`calib_cam_stereo_left.json`, `K` and `P` shift by exact integers, `width` and
|
|
830
|
+
`height` become the crop size, and `D`, `R`, `roi` and the header are kept. A
|
|
831
|
+
calibration file that records another image size than the reference rasters
|
|
832
|
+
is refused. A hierarchical file is written once, so every sample sharing it
|
|
833
|
+
must get the same box; mixed image sizes under one calibration file are an
|
|
834
|
+
error. Every output head records `meta.crop` (reference, spec and boxes), and
|
|
835
|
+
every file its own `attributes.crop` box; pruned clouds also record
|
|
836
|
+
`attributes.point_clouds` (source and kept point counts).
|
|
837
|
+
|
|
838
|
+
To reproduce a crop with `euler-preprocess spatial`, print its pixel box and
|
|
839
|
+
the matching `preprocessing` block for a reference size:
|
|
840
|
+
|
|
841
|
+
```bash
|
|
842
|
+
euler-preprocess crop-box -c configs/crop_config.json --size 1024 1920
|
|
843
|
+
```
|
|
844
|
+
|
|
845
|
+
---
|
|
846
|
+
|
|
706
847
|
## License
|
|
707
848
|
|
|
708
849
|
[MIT](LICENSE) © Daniel Rothenpieler
|
|
@@ -44,6 +44,9 @@ qualitative review.
|
|
|
44
44
|
| `euler-preprocess fog` | Synthetic fog via the Koschmieder atmospheric scattering model |
|
|
45
45
|
| `euler-preprocess sky-depth` | Override depth values in sky regions with a constant |
|
|
46
46
|
| `euler-preprocess radial` | Convert planar (z-buffer) depth to radial (Euclidean) depth |
|
|
47
|
+
| `euler-preprocess lidar-motion` | Compensate scanning-lidar points for the vehicle's motion during a sweep |
|
|
48
|
+
| `euler-preprocess crop` | Crop RGB, depth, intrinsics and extrinsics coherently |
|
|
49
|
+
| `euler-preprocess crop-box` | Print the pixel box a crop config gives for an image size |
|
|
47
50
|
|
|
48
51
|
## Installation
|
|
49
52
|
|
|
@@ -57,6 +60,8 @@ uv pip install "euler-preprocess[gpu,progress]"
|
|
|
57
60
|
euler-preprocess fog -c configs/example_dataset_config.json
|
|
58
61
|
euler-preprocess sky-depth -c configs/sky_depth_dataset_config.json
|
|
59
62
|
euler-preprocess radial -c configs/radial_dataset_config.json
|
|
63
|
+
euler-preprocess lidar-motion -c configs/lidar_motion_dataset_config.json
|
|
64
|
+
euler-preprocess crop -c configs/crop_stf_dataset_config.json
|
|
60
65
|
```
|
|
61
66
|
|
|
62
67
|
## Dataset Config
|
|
@@ -98,6 +103,7 @@ loader.
|
|
|
98
103
|
| `fog` | `rgb`, `depth`, `semantic_segmentation` | `intrinsics`; used for radial depth conversion and camera-profile optics |
|
|
99
104
|
| `sky-depth` | `depth`, `semantic_segmentation` | — |
|
|
100
105
|
| `radial` | `depth`, `intrinsics` | — |
|
|
106
|
+
| `lidar-motion` | `sparse_depth`, `gnss`, `sensor_timing`, `lidar_to_gnss` | — |
|
|
101
107
|
|
|
102
108
|
`fog` and `sky-depth` need `semantic_segmentation` as a boolean sky mask. They load
|
|
103
109
|
it with the `sky_mask` function of the euler-loading module that the index's
|
|
@@ -683,6 +689,141 @@ Standalone `RadialTransform(...)` usage keeps the legacy `.npy` output behaviour
|
|
|
683
689
|
|
|
684
690
|
---
|
|
685
691
|
|
|
692
|
+
## LiDAR Motion Compensation
|
|
693
|
+
|
|
694
|
+
A scanning lidar measures its points one after another while the vehicle moves.
|
|
695
|
+
The MUSES lidar sweeps its field of view top to bottom in about 85 ms, so
|
|
696
|
+
uncorrected points projected into the frame camera match tall objects at their
|
|
697
|
+
base and drift away towards the top. Across MUSES, 92 % of frames are taken
|
|
698
|
+
above 10 km/h. `lidar-motion` moves every point to where it was at the
|
|
699
|
+
frame camera's mid-exposure. It follows the official MUSES SDK
|
|
700
|
+
(`motion_compensate_pcd`): constant ground speed along the GNSS frame's `x`,
|
|
701
|
+
constant roll, pitch and heading rates.
|
|
702
|
+
|
|
703
|
+
| Modality | Content | MUSES loader (euler-loading) |
|
|
704
|
+
|---|---|---|
|
|
705
|
+
| `sparse_depth` | `(N, C)` points with each point's time in seconds | `muses.sparse_depth` |
|
|
706
|
+
| `gnss` | The frame's GNSS/IMU record | `muses.gnss` |
|
|
707
|
+
| `sensor_timing` | `camera_exposure_start_us` / `camera_exposure_end_us` | `muses.sensor_timing` (`meta.json`) |
|
|
708
|
+
| `lidar_to_gnss` | `(4, 4)` lidar → GNSS/IMU transform | `muses.read_lidar_to_gnss` (`calib.json`) |
|
|
709
|
+
|
|
710
|
+
Index `gnss/` like the `lidar` folder, so samples join it by ID. Index
|
|
711
|
+
`meta.json` and `calib.json` as root-level files; the euler-loading docs
|
|
712
|
+
describe the MUSES ego-motion inputs. The transform config takes one optional
|
|
713
|
+
key, `timestamp_column` (default `5`, MUSES' `timestamp` column).
|
|
714
|
+
|
|
715
|
+
The output is a `sparse_depth` dataset mirroring the input's paths, file names
|
|
716
|
+
and `.bin` format. Only `x, y, z` change. The head records
|
|
717
|
+
`meta.motion_compensated`, and each file's `attributes.motion_compensation`
|
|
718
|
+
holds the target time and the speed and rates used. Point
|
|
719
|
+
`dmd3c-euler`, `euler-eval` or a crop at it in place of the raw lidar.
|
|
720
|
+
|
|
721
|
+
---
|
|
722
|
+
|
|
723
|
+
## Coherent Crop
|
|
724
|
+
|
|
725
|
+
`crop` cuts the same box out of every image-shaped modality and corrects
|
|
726
|
+
everything that refers to the pixel grid, so a cropped bundle projects exactly
|
|
727
|
+
like the original:
|
|
728
|
+
|
|
729
|
+
| Modality kind | Crop |
|
|
730
|
+
|---|---|
|
|
731
|
+
| Rasters: RGB, dense depth, masks | Sliced; no resampling, so values are unchanged |
|
|
732
|
+
| Pinhole intrinsics `K` (or `P` for rectified images) | `cx -= left`, `cy -= top`; `fx`, `fy`, skew unchanged |
|
|
733
|
+
| Lidar point clouds | Copied byte for byte, or pruned to the points that project into the crop |
|
|
734
|
+
| Extrinsics | Copied byte for byte |
|
|
735
|
+
|
|
736
|
+
The extrinsics need no change, because `C·K·[R|t]·X = K'·[R|t]·X` with
|
|
737
|
+
`C = [[1,0,-left],[0,1,-top],[0,0,1]]`: the whole correction lives in the
|
|
738
|
+
camera matrix, which is also what the official SeeingThroughFog tools do. An
|
|
739
|
+
optional resize afterwards adds `S` (an `align_corners=False` rescale), so
|
|
740
|
+
`K' = S·C·K`, still with untouched extrinsics.
|
|
741
|
+
Which kind a modality is comes from its euler-loading loader annotation
|
|
742
|
+
(`shape`, `type`), never from its name.
|
|
743
|
+
|
|
744
|
+
The dataset config lists every modality to crop; whether a modality is
|
|
745
|
+
per-sample or hierarchical (shared calibration) is inferred from its index:
|
|
746
|
+
|
|
747
|
+
```json
|
|
748
|
+
{
|
|
749
|
+
"transform_config_path": "crop_stf_valid_region_config.json",
|
|
750
|
+
"output_path": "/data/stf_crop",
|
|
751
|
+
"modalities": {
|
|
752
|
+
"rgb": "/data/stf/cam_stereo_left_lut",
|
|
753
|
+
"sparse_depth": "/data/stf/lidar_hdl64_strongest",
|
|
754
|
+
"intrinsics": "/data/stf/calib_cam_stereo_left",
|
|
755
|
+
"camera_extrinsics": "/data/stf/calib_tf_tree"
|
|
756
|
+
},
|
|
757
|
+
"samples": {"start": 0, "count": 100}
|
|
758
|
+
}
|
|
759
|
+
```
|
|
760
|
+
|
|
761
|
+
Each modality is written to `<output_path>/<name>`, or to its
|
|
762
|
+
`pipeline.output_targets[]` entry (`slot` = the modality name; directory or zip).
|
|
763
|
+
With pipeline targets, every modality needs one and the outputs manifest lists
|
|
764
|
+
them all. `"output_layout": "scoped"` writes one root at `output_path` instead
|
|
765
|
+
(a directory or `.zip`): one ds-crawler metadata scope per modality, named after
|
|
766
|
+
it, with its files below `<name>/`. That is the layout dmd3c-euler's
|
|
767
|
+
`--dataset-root` reads. The dataset config must not contain `preprocessing`.
|
|
768
|
+
|
|
769
|
+
The crop config (`configs/crop_config.json`) describes the box relative to the
|
|
770
|
+
`reference` raster:
|
|
771
|
+
|
|
772
|
+
```json
|
|
773
|
+
{
|
|
774
|
+
"reference": "rgb",
|
|
775
|
+
"crop": {"remove": {"top": 0.20, "bottom": 0.05, "left": 0.15, "right": 0.19}},
|
|
776
|
+
"point_clouds": "keep",
|
|
777
|
+
"verify": "all"
|
|
778
|
+
}
|
|
779
|
+
```
|
|
780
|
+
|
|
781
|
+
| Key | Meaning |
|
|
782
|
+
|---|---|
|
|
783
|
+
| `crop.remove` | What to cut off each edge (`top`, `bottom`, `left`, `right`) |
|
|
784
|
+
| `crop.keep` + `crop.anchor` | The kept `height` / `width`, placed at `center` (default), `top`, `bottom`, `left`, `right`, `top_left`, `top_right`, `bottom_left` or `bottom_right` |
|
|
785
|
+
| `crop.unit` | `fraction` (default) or `pixel`. Fractions lie in `[0, 1)` (`keep`: `(0, 1]`) and round half up per edge: `floor(f · size + 0.5)` |
|
|
786
|
+
| `crop.multiple_of` | Trim the kept size down to a multiple, e.g. a network stride; the extra rows and columns come off both sides evenly |
|
|
787
|
+
| `resize` | Optional, after the crop: `{"size": [h, w]}` or `{"scale": 0.5}` (or `[sy, sx]`); `interpolation` maps modality names to `nearest`, `bilinear` or `bicubic` |
|
|
788
|
+
| `point_clouds` | `keep` (default): copy every point, consumers clip to the image. `prune_to_crop`: keep only the points with `z > 0` whose pixel `rint(K'·T·X)` lies in the output image, as consumers such as dmd3c-euler project them |
|
|
789
|
+
| `projection` | For `prune_to_crop`: the `intrinsics` and `extrinsics` modalities to project with, when the dataset has more than one candidate |
|
|
790
|
+
| `verify` | `all` (default), `none`, or a sample count: reopen the outputs and check every file against the crop (rasters and pruned clouds as encoded, intrinsics to 1e-3 px, copies byte-identical) |
|
|
791
|
+
| `roles` | Override a modality's kind with `raster` or `copy`, for loaders whose annotation the crop does not know |
|
|
792
|
+
|
|
793
|
+
Pixel units reproduce the official boxes exactly, e.g. `RGB2Gatedv2` is
|
|
794
|
+
`{"remove": {"top": 202, "bottom": 54, "left": 280, "right": 360}, "unit": "pixel"}`.
|
|
795
|
+
`configs/crop_stf_valid_region_config.json` removes the black border the
|
|
796
|
+
SeeingThroughFog rectification leaves on `_lut` frames (top 34, bottom 46,
|
|
797
|
+
left 16 px), keeping a fully valid 1904×944 image.
|
|
798
|
+
|
|
799
|
+
Rasters are re-encoded with the dataset's euler-loading writer, so a raster
|
|
800
|
+
modality needs one (SeeingThroughFog's raw `rccb` TIFFs have none). Each
|
|
801
|
+
encoded file is decoded again right away and must give back the cropped values:
|
|
802
|
+
exactly, or for an interpolated resize within half an 8-bit step. A resize
|
|
803
|
+
interpolates colour images (`rgb`, `rccb`) bilinearly and takes the nearest
|
|
804
|
+
pixel for everything else, so depth and label values stay real values;
|
|
805
|
+
sparse-depth rasters are not resized. Pruned point clouds need a writer too
|
|
806
|
+
(`princeton_dense.write_sparse_depth`, `muses.write_sparse_depth`). Intrinsics
|
|
807
|
+
are written by copying the source file and letting its writer move the matrices
|
|
808
|
+
onto the new grid: for a ROS CameraInfo file such as
|
|
809
|
+
`calib_cam_stereo_left.json`, `K` and `P` shift by exact integers, `width` and
|
|
810
|
+
`height` become the crop size, and `D`, `R`, `roi` and the header are kept. A
|
|
811
|
+
calibration file that records another image size than the reference rasters
|
|
812
|
+
is refused. A hierarchical file is written once, so every sample sharing it
|
|
813
|
+
must get the same box; mixed image sizes under one calibration file are an
|
|
814
|
+
error. Every output head records `meta.crop` (reference, spec and boxes), and
|
|
815
|
+
every file its own `attributes.crop` box; pruned clouds also record
|
|
816
|
+
`attributes.point_clouds` (source and kept point counts).
|
|
817
|
+
|
|
818
|
+
To reproduce a crop with `euler-preprocess spatial`, print its pixel box and
|
|
819
|
+
the matching `preprocessing` block for a reference size:
|
|
820
|
+
|
|
821
|
+
```bash
|
|
822
|
+
euler-preprocess crop-box -c configs/crop_config.json --size 1024 1920
|
|
823
|
+
```
|
|
824
|
+
|
|
825
|
+
---
|
|
826
|
+
|
|
686
827
|
## License
|
|
687
828
|
|
|
688
829
|
[MIT](LICENSE) © Daniel Rothenpieler
|