euler-preprocess 3.18.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.18.0/euler_preprocess.egg-info → euler_preprocess-3.19.0}/PKG-INFO +110 -3
- {euler_preprocess-3.18.0 → euler_preprocess-3.19.0}/README.md +107 -0
- {euler_preprocess-3.18.0 → euler_preprocess-3.19.0}/docs/plans/coherent-cropping.md +112 -37
- {euler_preprocess-3.18.0 → euler_preprocess-3.19.0}/euler_preprocess/cli.py +44 -2
- {euler_preprocess-3.18.0 → euler_preprocess-3.19.0}/euler_preprocess/common/intrinsics.py +26 -6
- {euler_preprocess-3.18.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.18.0 → euler_preprocess-3.19.0/euler_preprocess.egg-info}/PKG-INFO +110 -3
- {euler_preprocess-3.18.0 → euler_preprocess-3.19.0}/euler_preprocess.egg-info/SOURCES.txt +11 -0
- {euler_preprocess-3.18.0 → euler_preprocess-3.19.0}/euler_preprocess.egg-info/requires.txt +2 -2
- {euler_preprocess-3.18.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.18.0/CHANGELOG.md +0 -14
- {euler_preprocess-3.18.0 → euler_preprocess-3.19.0}/LICENSE +0 -0
- {euler_preprocess-3.18.0 → euler_preprocess-3.19.0}/MANIFEST.in +0 -0
- {euler_preprocess-3.18.0 → euler_preprocess-3.19.0}/docs/spatial.md +0 -0
- {euler_preprocess-3.18.0 → euler_preprocess-3.19.0}/euler_preprocess/__init__.py +0 -0
- {euler_preprocess-3.18.0 → euler_preprocess-3.19.0}/euler_preprocess/common/__init__.py +0 -0
- {euler_preprocess-3.18.0 → euler_preprocess-3.19.0}/euler_preprocess/common/color.py +0 -0
- {euler_preprocess-3.18.0 → euler_preprocess-3.19.0}/euler_preprocess/common/dataset.py +0 -0
- {euler_preprocess-3.18.0 → euler_preprocess-3.19.0}/euler_preprocess/common/device.py +0 -0
- {euler_preprocess-3.18.0 → euler_preprocess-3.19.0}/euler_preprocess/common/io.py +0 -0
- {euler_preprocess-3.18.0 → euler_preprocess-3.19.0}/euler_preprocess/common/logging.py +0 -0
- {euler_preprocess-3.18.0 → euler_preprocess-3.19.0}/euler_preprocess/common/noise.py +0 -0
- {euler_preprocess-3.18.0 → euler_preprocess-3.19.0}/euler_preprocess/common/normalize.py +0 -0
- {euler_preprocess-3.18.0 → euler_preprocess-3.19.0}/euler_preprocess/common/sampling.py +0 -0
- {euler_preprocess-3.18.0 → euler_preprocess-3.19.0}/euler_preprocess/common/transform.py +0 -0
- {euler_preprocess-3.18.0 → euler_preprocess-3.19.0}/euler_preprocess/fog/__init__.py +0 -0
- {euler_preprocess-3.18.0 → euler_preprocess-3.19.0}/euler_preprocess/fog/airlight_from_sky.py +0 -0
- {euler_preprocess-3.18.0 → euler_preprocess-3.19.0}/euler_preprocess/fog/atmospheric_light.py +0 -0
- {euler_preprocess-3.18.0 → euler_preprocess-3.19.0}/euler_preprocess/fog/augmentations.py +0 -0
- {euler_preprocess-3.18.0 → euler_preprocess-3.19.0}/euler_preprocess/fog/capture.py +0 -0
- {euler_preprocess-3.18.0 → euler_preprocess-3.19.0}/euler_preprocess/fog/dcp_airlight.py +0 -0
- {euler_preprocess-3.18.0 → euler_preprocess-3.19.0}/euler_preprocess/fog/dcp_airlight_torch.py +0 -0
- {euler_preprocess-3.18.0 → euler_preprocess-3.19.0}/euler_preprocess/fog/dcp_heuristic_airlight.py +0 -0
- {euler_preprocess-3.18.0 → euler_preprocess-3.19.0}/euler_preprocess/fog/dcp_heuristic_airlight_torch.py +0 -0
- {euler_preprocess-3.18.0 → euler_preprocess-3.19.0}/euler_preprocess/fog/inference.py +0 -0
- {euler_preprocess-3.18.0 → euler_preprocess-3.19.0}/euler_preprocess/fog/logging.py +0 -0
- {euler_preprocess-3.18.0 → euler_preprocess-3.19.0}/euler_preprocess/fog/models.py +0 -0
- {euler_preprocess-3.18.0 → euler_preprocess-3.19.0}/euler_preprocess/fog/pipeline.py +0 -0
- {euler_preprocess-3.18.0 → euler_preprocess-3.19.0}/euler_preprocess/fog/transform.py +0 -0
- {euler_preprocess-3.18.0 → euler_preprocess-3.19.0}/euler_preprocess/lidar_motion/__init__.py +0 -0
- {euler_preprocess-3.18.0 → euler_preprocess-3.19.0}/euler_preprocess/lidar_motion/compensation.py +0 -0
- {euler_preprocess-3.18.0 → euler_preprocess-3.19.0}/euler_preprocess/lidar_motion/transform.py +0 -0
- {euler_preprocess-3.18.0 → euler_preprocess-3.19.0}/euler_preprocess/radial/__init__.py +0 -0
- {euler_preprocess-3.18.0 → euler_preprocess-3.19.0}/euler_preprocess/radial/transform.py +0 -0
- {euler_preprocess-3.18.0 → euler_preprocess-3.19.0}/euler_preprocess/sky_depth/__init__.py +0 -0
- {euler_preprocess-3.18.0 → euler_preprocess-3.19.0}/euler_preprocess/sky_depth/transform.py +0 -0
- {euler_preprocess-3.18.0 → euler_preprocess-3.19.0}/euler_preprocess/spatial.py +0 -0
- {euler_preprocess-3.18.0 → euler_preprocess-3.19.0}/euler_preprocess.egg-info/dependency_links.txt +0 -0
- {euler_preprocess-3.18.0 → euler_preprocess-3.19.0}/euler_preprocess.egg-info/entry_points.txt +0 -0
- {euler_preprocess-3.18.0 → euler_preprocess-3.19.0}/euler_preprocess.egg-info/top_level.txt +0 -0
- {euler_preprocess-3.18.0 → euler_preprocess-3.19.0}/examples/spatial-five-field.json +0 -0
- {euler_preprocess-3.18.0 → euler_preprocess-3.19.0}/setup.cfg +0 -0
- {euler_preprocess-3.18.0 → euler_preprocess-3.19.0}/tests/test_airlight_fallback.py +0 -0
- {euler_preprocess-3.18.0 → euler_preprocess-3.19.0}/tests/test_cli_sample_selection.py +0 -0
- {euler_preprocess-3.18.0 → euler_preprocess-3.19.0}/tests/test_dcp_heuristic_airlight.py +0 -0
- {euler_preprocess-3.18.0 → euler_preprocess-3.19.0}/tests/test_dense_gloomy_daylight_config.py +0 -0
- {euler_preprocess-3.18.0 → euler_preprocess-3.19.0}/tests/test_fog_aux_outputs.py +0 -0
- {euler_preprocess-3.18.0 → euler_preprocess-3.19.0}/tests/test_fog_aware_auto_exposure.py +0 -0
- {euler_preprocess-3.18.0 → euler_preprocess-3.19.0}/tests/test_fog_cpu_gpu_parity.py +0 -0
- {euler_preprocess-3.18.0 → euler_preprocess-3.19.0}/tests/test_fog_input_normalization.py +0 -0
- {euler_preprocess-3.18.0 → euler_preprocess-3.19.0}/tests/test_fog_integration.py +0 -0
- {euler_preprocess-3.18.0 → euler_preprocess-3.19.0}/tests/test_lidar_motion.py +0 -0
- {euler_preprocess-3.18.0 → euler_preprocess-3.19.0}/tests/test_modality_roles.py +0 -0
- {euler_preprocess-3.18.0 → euler_preprocess-3.19.0}/tests/test_radial.py +0 -0
- {euler_preprocess-3.18.0 → euler_preprocess-3.19.0}/tests/test_real_drive_sim_scenario_profiles.py +0 -0
- {euler_preprocess-3.18.0 → euler_preprocess-3.19.0}/tests/test_scene_illumination.py +0 -0
- {euler_preprocess-3.18.0 → euler_preprocess-3.19.0}/tests/test_sensor_identity.py +0 -0
- {euler_preprocess-3.18.0 → euler_preprocess-3.19.0}/tests/test_sky_depth.py +0 -0
- {euler_preprocess-3.18.0 → euler_preprocess-3.19.0}/tests/test_source_backed_output.py +0 -0
- {euler_preprocess-3.18.0 → euler_preprocess-3.19.0}/tests/test_spatial.py +0 -0
- {euler_preprocess-3.18.0 → euler_preprocess-3.19.0}/tests/test_tone_map_lut.py +0 -0
- {euler_preprocess-3.18.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"
|
|
@@ -65,6 +65,8 @@ qualitative review.
|
|
|
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
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 |
|
|
68
70
|
|
|
69
71
|
## Installation
|
|
70
72
|
|
|
@@ -79,6 +81,7 @@ euler-preprocess fog -c configs/example_dataset_config.json
|
|
|
79
81
|
euler-preprocess sky-depth -c configs/sky_depth_dataset_config.json
|
|
80
82
|
euler-preprocess radial -c configs/radial_dataset_config.json
|
|
81
83
|
euler-preprocess lidar-motion -c configs/lidar_motion_dataset_config.json
|
|
84
|
+
euler-preprocess crop -c configs/crop_stf_dataset_config.json
|
|
82
85
|
```
|
|
83
86
|
|
|
84
87
|
## Dataset Config
|
|
@@ -737,6 +740,110 @@ holds the target time and the speed and rates used. Point
|
|
|
737
740
|
|
|
738
741
|
---
|
|
739
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
|
+
|
|
740
847
|
## License
|
|
741
848
|
|
|
742
849
|
[MIT](LICENSE) © Daniel Rothenpieler
|
|
@@ -45,6 +45,8 @@ qualitative review.
|
|
|
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
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 |
|
|
48
50
|
|
|
49
51
|
## Installation
|
|
50
52
|
|
|
@@ -59,6 +61,7 @@ euler-preprocess fog -c configs/example_dataset_config.json
|
|
|
59
61
|
euler-preprocess sky-depth -c configs/sky_depth_dataset_config.json
|
|
60
62
|
euler-preprocess radial -c configs/radial_dataset_config.json
|
|
61
63
|
euler-preprocess lidar-motion -c configs/lidar_motion_dataset_config.json
|
|
64
|
+
euler-preprocess crop -c configs/crop_stf_dataset_config.json
|
|
62
65
|
```
|
|
63
66
|
|
|
64
67
|
## Dataset Config
|
|
@@ -717,6 +720,110 @@ holds the target time and the speed and rates used. Point
|
|
|
717
720
|
|
|
718
721
|
---
|
|
719
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
|
+
|
|
720
827
|
## License
|
|
721
828
|
|
|
722
829
|
[MIT](LICENSE) © Daniel Rothenpieler
|
|
@@ -1,8 +1,12 @@
|
|
|
1
1
|
# Coherent cropping plan
|
|
2
2
|
|
|
3
|
-
Status:
|
|
4
|
-
(euler-loading's `princeton_dense`
|
|
5
|
-
`intrinsics` and `camera_extrinsics
|
|
3
|
+
Status: **phases 1 and 3 implemented** (2026-11-07) as `euler-preprocess crop`
|
|
4
|
+
(plus `crop-box`), for **SeeingThroughFog** (euler-loading's `princeton_dense`
|
|
5
|
+
loader) with `rgb`, `sparse_depth`, `intrinsics` and `camera_extrinsics`; see §5
|
|
6
|
+
for results. Phase 2 (other datasets) is open. The euler-loading side shipped in
|
|
7
|
+
2.31.0 (STF fixes) and 2.32.0 (MUSES inputs); phase 3 uses euler-loading 2.33.0
|
|
8
|
+
and ds-crawler 2.14.0 (both released), which euler-preprocess now requires.
|
|
9
|
+
euler-view launches it with the `euler-preprocess-crop` addon. Written 2026-11-06 against
|
|
6
10
|
euler-preprocess 3.17.0 and euler-loading 2.30.0.
|
|
7
11
|
|
|
8
12
|
## 1. Is there a pathway today?
|
|
@@ -72,10 +76,10 @@ two different K. SeeingThroughFog keeps them in separate files.
|
|
|
72
76
|
| `intrinsics` | `calib_cam_stereo_left.json`, one file, matched hierarchically | `read_intrinsics` (3×3) | ROS CameraInfo: `K, D, R, P, width, height, roi, …` | intrinsics | Patched copy via `princeton_dense.write_intrinsics`: K and P `cx`/`cy`, `width`, `height` |
|
|
73
77
|
| `camera_extrinsics` | `calib_tf_tree_full.json`, one file | `read_extrinsics` (4×4, HDL64 → `cam_stereo_left_optical`) | ROS TF list | unchanged | Byte copy |
|
|
74
78
|
|
|
75
|
-
The byte copies need no writer. `princeton_dense.write_intrinsics`
|
|
76
|
-
|
|
79
|
+
The byte copies need no writer. `princeton_dense.write_intrinsics` (euler-loading
|
|
80
|
+
2.31.0, §3.2) covers the intrinsics patch.
|
|
77
81
|
|
|
78
|
-
### 3.2 Fixed in euler-loading
|
|
82
|
+
### 3.2 Fixed in euler-loading 2.31.0: `read_intrinsics` returned the wrong matrix for `_lut` images
|
|
79
83
|
|
|
80
84
|
- `princeton_dense.read_intrinsics` returns `K`
|
|
81
85
|
(`euler_loading/loaders/_princeton_dense.py:170`).
|
|
@@ -91,7 +95,7 @@ in the euler-loading working tree (§3.2), so the intrinsics patch is covered.
|
|
|
91
95
|
about ±55 px at the top and bottom.
|
|
92
96
|
- This is independent of cropping, but a crop corrects whichever matrix the
|
|
93
97
|
loader returns, so it had to be settled first.
|
|
94
|
-
-
|
|
98
|
+
- Released in euler-loading 2.31.0: `read_intrinsics` now returns the
|
|
95
99
|
rectified `P[:, :3]` by default (a behaviour change, in its CHANGELOG).
|
|
96
100
|
`intrinsics_matrix: "K"` (alias `"raw"`) in per-file attributes or head
|
|
97
101
|
meta selects the raw K.
|
|
@@ -123,8 +127,8 @@ in the euler-loading working tree (§3.2), so the intrinsics patch is covered.
|
|
|
123
127
|
- Raw TIFF datasets (`princeton_stf+rgb`):
|
|
124
128
|
- `princeton_dense.rgb` silently returns an all-white image for them: Pillow
|
|
125
129
|
clips 12-bit data. Their head must name `rccb`.
|
|
126
|
-
- `rccb` swapped red and blue; it now demosaics as GRBG (fixed in
|
|
127
|
-
euler-loading
|
|
130
|
+
- `rccb` swapped red and blue; it now demosaics as GRBG (fixed in
|
|
131
|
+
euler-loading 2.31.0).
|
|
128
132
|
- The raw frames are not pinhole images (k1 = −0.57), so use the `_lut`
|
|
129
133
|
images for depth evaluation.
|
|
130
134
|
- The official crops are pixel boxes, e.g. `RGB2Gatedv2` keeps rows 202–970 and
|
|
@@ -136,8 +140,10 @@ in the euler-loading working tree (§3.2), so the intrinsics patch is covered.
|
|
|
136
140
|
`remove: {top: 34, bottom: 46, left: 16, right: 0}` (pixel units) leaves a
|
|
137
141
|
fully valid 1904×944 image (91.4 % of the frame). That's a sensible default
|
|
138
142
|
crop for depth evaluation, or else mask those pixels. Computed from
|
|
139
|
-
`calib_cam_stereo_left.json` and the official conversion code
|
|
140
|
-
real
|
|
143
|
+
`calib_cam_stereo_left.json` and the official conversion code, and confirmed
|
|
144
|
+
on two real dense-fog frames: the black pixels reach 33 rows at the top, 45 at
|
|
145
|
+
the bottom and 15 columns on the left, and none remain inside the box. Shipped
|
|
146
|
+
as `configs/crop_stf_valid_region_config.json`.
|
|
141
147
|
- Resampling in the official rectification is mild but uneven: about 1.11 raw
|
|
142
148
|
px per output px at the centre, but 0.76–0.81 near the left and right edges
|
|
143
149
|
(≈1.3× upsampled, so softer there). The `_lut` tone mapping also applies
|
|
@@ -291,6 +297,9 @@ modality name, which is what causes the hazard in §1.
|
|
|
291
297
|
- `euler_preprocess/crop/calibration.py`: hierarchical writer (unique-box
|
|
292
298
|
check, copy then merge).
|
|
293
299
|
- `euler_preprocess/crop/verify.py`.
|
|
300
|
+
- `euler_preprocess/crop/files.py` (added while implementing): zip-aware reads
|
|
301
|
+
of source files, value fingerprints for the verify step, and the
|
|
302
|
+
recorded-image-size check for JSON calibration files.
|
|
294
303
|
- `euler_preprocess/common/output.py`: byte-copy registration helper, and a
|
|
295
304
|
source-backed backend factory for any modality with head-meta overrides.
|
|
296
305
|
- `euler_preprocess/common/intrinsics.py`: `single_calibration(value)`. A
|
|
@@ -300,17 +309,45 @@ modality name, which is what causes the hazard in §1.
|
|
|
300
309
|
- `euler_preprocess/cli.py`: a `crop` subcommand with its own `_cmd_crop`, like
|
|
301
310
|
`_cmd_spatial`, because the config drives the modality set.
|
|
302
311
|
- `configs/crop_config.json`, `configs/crop_stf_dataset_config.json`, a README
|
|
303
|
-
section and a CHANGELOG entry.
|
|
304
|
-
`
|
|
312
|
+
section and a CHANGELOG entry. The `euler-loading` pin (`>=2.32.0`, raised
|
|
313
|
+
for `lidar-motion`) already covers 2.31.0.
|
|
305
314
|
- Optional `tools/check_crop_projection.py`: overlay projected lidar on the
|
|
306
315
|
cropped RGB for a few frames, as a visual K-vs-P and crop check.
|
|
307
316
|
|
|
308
317
|
## 5. Work plan
|
|
309
318
|
|
|
310
|
-
### Phase 1: SeeingThroughFog end to end
|
|
319
|
+
### Phase 1: SeeingThroughFog end to end — implemented 2026-11-07
|
|
311
320
|
|
|
312
|
-
|
|
313
|
-
|
|
321
|
+
Results:
|
|
322
|
+
- **Tests:** `tests/test_crop.py` (STF-format fixture: directory and zip
|
|
323
|
+
outputs with the pipeline manifest, sample selection, the hierarchical
|
|
324
|
+
conflict, a calibration for another resolution, a raster without a writer,
|
|
325
|
+
unsafe configs), `test_crop_spec.py`, `test_crop_roles.py`,
|
|
326
|
+
`test_intrinsics.py`. Full suite 273 passed, 3 skipped. The opt-in real-data
|
|
327
|
+
test (`EULER_PREPROCESS_STF_ROOT`) passes on two real frames.
|
|
328
|
+
- **Real frames** (two dense-fog `_lut` frames, valid-region crop): every §3.4
|
|
329
|
+
criterion holds. RGB is the bit-exact slice, lidar and the TF tree are
|
|
330
|
+
byte-identical, and `K`/`P` shift by exact float64 integers with `D`, `R`,
|
|
331
|
+
`roi` and the header untouched. Projections with the cropped `P` agree with the
|
|
332
|
+
shifted originals to 1e-10 px.
|
|
333
|
+
- **dmd3c-euler:** its own `build_dataset` loads the cropped roots unchanged, and
|
|
334
|
+
its `project_sparse_depth` raster equals the crop of the uncropped projection
|
|
335
|
+
(0 differing pixels). DMD3C inference itself was not run (no checkpoint or GPU
|
|
336
|
+
available).
|
|
337
|
+
|
|
338
|
+
Notes from implementing:
|
|
339
|
+
- euler-loading's datasets resolve the GPU (torch, CHW) loader variant, so the
|
|
340
|
+
crop slices CHW tensors in practice; the role's layout comes from the
|
|
341
|
+
annotation of that loader.
|
|
342
|
+
- Each output head's `meta.crop` holds `reference`, `spec` and `boxes` (one
|
|
343
|
+
box per reference size), set at finalize. `meta.dimensions` follows the crop
|
|
344
|
+
when present.
|
|
345
|
+
- With `pipeline.output_targets`, every modality needs a target (slot = name,
|
|
346
|
+
`datasetType` must match the source); the manifest lists them all and is
|
|
347
|
+
written after verification passes.
|
|
348
|
+
- Outputs inside a source dataset are refused.
|
|
349
|
+
|
|
350
|
+
euler-loading (released in 2.31.0):
|
|
314
351
|
|
|
315
352
|
1. `princeton_dense.write_intrinsics` (CPU and GPU share one function) merges
|
|
316
353
|
into an existing ROS CameraInfo JSON, as described in §3.2.
|
|
@@ -369,9 +406,11 @@ Verified with the real side data:
|
|
|
369
406
|
- The GNSS timestamp precedes mid-exposure by up to 16.6 ms, so `meta.json`'s
|
|
370
407
|
exposure times are needed.
|
|
371
408
|
|
|
372
|
-
Implemented
|
|
373
|
-
|
|
374
|
-
|
|
409
|
+
Implemented 2026-11-07 and released (euler-loading 2.32.0; euler-preprocess
|
|
410
|
+
`lidar-motion`, plus the euler-view addon `euler-preprocess-lidar-motion`). The
|
|
411
|
+
side data became ordinary euler-loading modalities, consumed by
|
|
412
|
+
euler-preprocess. ds-crawler is unchanged: it only interprets paths, never file
|
|
413
|
+
contents.
|
|
375
414
|
|
|
376
415
|
| Modality | ds-crawler indexing | euler-loading |
|
|
377
416
|
|---|---|---|
|
|
@@ -389,26 +428,60 @@ Results:
|
|
|
389
428
|
- **Real data:** on the three real frames the CLI output reproduces the
|
|
390
429
|
verified pole straightening.
|
|
391
430
|
|
|
392
|
-
Registered in euler-dataset-contract
|
|
393
|
-
`timing.platform.acquisition` and `geometry.platform.extrinsics`.
|
|
394
|
-
|
|
395
|
-
|
|
431
|
+
Registered in euler-dataset-contract as `kinematics.platform.state`,
|
|
432
|
+
`timing.platform.acquisition` and `geometry.platform.extrinsics`.
|
|
433
|
+
euler-preprocess requires `euler-loading>=2.32.0`, the first release with these
|
|
434
|
+
loaders.
|
|
396
435
|
|
|
397
436
|
The side data stays with the local dataset: MUSES license §2.3 forbids
|
|
398
437
|
distributing it.
|
|
399
438
|
|
|
400
|
-
### Phase 3
|
|
439
|
+
### Phase 3 — implemented 2026-11-07
|
|
440
|
+
|
|
441
|
+
- `point_clouds: "prune_to_crop"`: keeps the points with `z > 0` whose pixel
|
|
442
|
+
`rint(K'·T·X)` lies in the output image, the projection dmd3c-euler's
|
|
443
|
+
`project_sparse_depth` uses, with a 1e-6 px margin so a rounding boundary
|
|
444
|
+
never drops a visible point. Intrinsics and extrinsics are found by role
|
|
445
|
+
(`projection` names them when ambiguous). Needed a SeeingThroughFog writer:
|
|
446
|
+
`princeton_dense.write_sparse_depth` (euler-loading). Per-file
|
|
447
|
+
`attributes.point_clouds` records the source and kept counts.
|
|
448
|
+
- A single scoped output root (`"output_layout": "scoped"`): one metadata scope
|
|
449
|
+
per modality, its files below `<name>/` (ds-crawler writer `path_prefix`).
|
|
450
|
+
Zip roots are staged as a directory and packed once, since one zip writer
|
|
451
|
+
owns an archive. dmd3c's `--dataset-root` reads it.
|
|
452
|
+
- Resize after the crop (`resize: {size | scale}`): `K' = S·C·K` through
|
|
453
|
+
euler-loading's `crop_intrinsics` and `resize_intrinsics`. Rasters go through
|
|
454
|
+
the new public `euler_loading.resize_raster`, the same code as the online
|
|
455
|
+
`Resize`: bilinear for `rgb`/`rccb`, nearest otherwise (depth, masks, labels).
|
|
456
|
+
Sparse-depth rasters are refused.
|
|
457
|
+
- `euler-preprocess crop-box -c <crop config> --size H W` prints the box and a
|
|
458
|
+
`spatial` `preprocessing` block (`crop` with `size`/`offset`, then `resize`).
|
|
459
|
+
- Every encoded raster or pruned cloud is decoded right away with the
|
|
460
|
+
modality's loader and must match (exactly, or within half an 8-bit step after
|
|
461
|
+
interpolation); the verify step then compares the reloaded datasets with those
|
|
462
|
+
decoded values.
|
|
463
|
+
|
|
464
|
+
The two private hooks of phase 1 became public APIs:
|
|
465
|
+
- euler-loading: `MultiModalDataset.get_loader`, `get_loader_meta` and
|
|
466
|
+
`get_hierarchical_files`, plus `get_modality_meta(loader)`;
|
|
467
|
+
- ds-crawler: `DatasetWriter.set_modality_meta` (and `path_prefix`).
|
|
468
|
+
euler-preprocess requires euler-loading 2.33.0 and ds-crawler 2.14.0.
|
|
469
|
+
|
|
470
|
+
Results:
|
|
471
|
+
- **Tests:** euler-preprocess 292 passed, 3 skipped; euler-loading 1022 passed;
|
|
472
|
+
ds-crawler's new writer tests pass.
|
|
473
|
+
- **Real STF frames** (valid-region crop):
|
|
474
|
+
- pruning keeps 7.4 % of the HDL64 points;
|
|
475
|
+
- dmd3c-euler's projection of the pruned clouds equals the projection of
|
|
476
|
+
all points through the output camera, for the crop alone and with a
|
|
477
|
+
0.5x resize (472×952).
|
|
478
|
+
|
|
479
|
+
An euler-view addon (`euler-preprocess-crop`) launches the crop for a dataset
|
|
480
|
+
family.
|
|
401
481
|
|
|
402
|
-
|
|
403
|
-
This needs sparse-depth writers, is lossy, and must use the same projection as
|
|
404
|
-
the consumers.
|
|
405
|
-
- A single scoped output root (one `metadata_scope` per modality), matching
|
|
406
|
-
dmd3c's `--dataset-root`.
|
|
407
|
-
- Resize, with the same maths: `K' = S·C·K`. The official `Sweden2PSMNet` mode
|
|
408
|
-
halves 1920×1024.
|
|
409
|
-
- A percentage helper for `spatial` that emits `crop: {size, offset}`.
|
|
482
|
+
## 6. Decisions
|
|
410
483
|
|
|
411
|
-
|
|
484
|
+
Phase 1 adopted every recommendation below.
|
|
412
485
|
|
|
413
486
|
| # | Decision | Recommendation |
|
|
414
487
|
|---|---|---|
|
|
@@ -426,7 +499,7 @@ distributing it.
|
|
|
426
499
|
euler-loading: crop or resize only values whose spatial size matches the
|
|
427
500
|
reference, or classify fields from the loader annotations.
|
|
428
501
|
2. `princeton_dense.read_intrinsics` returned raw `K` for rectified `_lut`
|
|
429
|
-
images (§3.2). Fixed in
|
|
502
|
+
images (§3.2). Fixed in euler-loading 2.31.0.
|
|
430
503
|
3. `generic_dense_depth` PNG depth with `scale_to_meters_override` doesn't
|
|
431
504
|
survive decode → write: 10.0 m reloads as 0.039 m. The loader applies the
|
|
432
505
|
scale (`generic_dense_depth.py:133`), `write_depth` (`:226`) writes
|
|
@@ -436,11 +509,13 @@ distributing it.
|
|
|
436
509
|
(`vkitti2.py:208`), so it raises on the documented header line
|
|
437
510
|
`frame cameraID r1,1 …`. This is also open question 4 in
|
|
438
511
|
`euler-dataset-contract/docs/ecosystem-streamlining-plan.md`.
|
|
439
|
-
5. `extract_intrinsics` only
|
|
440
|
-
`"intrinsics"`, so radial and fog
|
|
512
|
+
5. `extract_intrinsics` only resolved hierarchical intrinsics whose file ID is
|
|
513
|
+
`"intrinsics"`, so radial and fog missed STF's `calib_cam_stereo_left`.
|
|
514
|
+
Fixed with phase 1 (`single_calibration`): a single file is used whatever
|
|
515
|
+
its ID; several files raise.
|
|
441
516
|
6. RGB writers always emit PNG bytes. VKITTI2 `.jpg` sources become PNG data
|
|
442
517
|
under `.jpg` names, and Real Drive Sim RGBA sources lose their alpha channel.
|
|
443
518
|
7. `princeton_dense.rccb` demosaiced as GBRG, swapping red and blue. Fixed in
|
|
444
|
-
|
|
519
|
+
euler-loading 2.31.0 (GRBG).
|
|
445
520
|
8. `princeton_dense.rgb` on a 12-bit TIFF returns an all-white image without
|
|
446
521
|
an error. Not fixed: a guard that raises would be a small follow-up.
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
"""CLI entry point for euler-preprocess.
|
|
2
2
|
|
|
3
3
|
Used both by ``python main.py`` and by the installed ``euler-preprocess`` console
|
|
4
|
-
script. Supports subcommands: ``fog``, ``sky-depth``, ``radial``, ``lidar-motion
|
|
5
|
-
and ``spatial``.
|
|
4
|
+
script. Supports subcommands: ``fog``, ``sky-depth``, ``radial``, ``lidar-motion``,
|
|
5
|
+
``crop``, ``crop-box`` and ``spatial``.
|
|
6
6
|
"""
|
|
7
7
|
from __future__ import annotations
|
|
8
8
|
|
|
@@ -257,6 +257,29 @@ def _cmd_lidar_motion(args: argparse.Namespace) -> int:
|
|
|
257
257
|
return _run_transform(args, LidarMotionTransform)
|
|
258
258
|
|
|
259
259
|
|
|
260
|
+
def _cmd_crop(args: argparse.Namespace) -> int:
|
|
261
|
+
from euler_preprocess.crop.run import run_crop_command
|
|
262
|
+
|
|
263
|
+
config_path = Path(args.config).resolve()
|
|
264
|
+
config = json.loads(config_path.read_text(encoding="utf-8"))
|
|
265
|
+
get_logger().info("Config: %s", config_path)
|
|
266
|
+
run_crop_command(config, config_path.parent)
|
|
267
|
+
return 0
|
|
268
|
+
|
|
269
|
+
|
|
270
|
+
def _cmd_crop_box(args: argparse.Namespace) -> int:
|
|
271
|
+
from euler_preprocess.crop.spec import parse_crop_config, spatial_preprocessing
|
|
272
|
+
|
|
273
|
+
config = parse_crop_config(json.loads(Path(args.config).read_text(encoding="utf-8")))
|
|
274
|
+
height, width = args.size
|
|
275
|
+
box = config.geometry(height, width)
|
|
276
|
+
print(json.dumps(
|
|
277
|
+
{"box": box.to_dict(), "preprocessing": spatial_preprocessing(box, reference=config.reference)},
|
|
278
|
+
indent=2,
|
|
279
|
+
))
|
|
280
|
+
return 0
|
|
281
|
+
|
|
282
|
+
|
|
260
283
|
def _cmd_spatial(args: argparse.Namespace) -> int:
|
|
261
284
|
from euler_preprocess.spatial import export_spatial
|
|
262
285
|
|
|
@@ -317,6 +340,25 @@ def parse_args() -> argparse.Namespace:
|
|
|
317
340
|
)
|
|
318
341
|
lidar_motion_parser.set_defaults(func=_cmd_lidar_motion)
|
|
319
342
|
|
|
343
|
+
crop_parser = subparsers.add_parser(
|
|
344
|
+
"crop", parents=[common],
|
|
345
|
+
help="Crop RGB, depth and calibration coherently (K' = C K; lidar and extrinsics unchanged).",
|
|
346
|
+
)
|
|
347
|
+
crop_parser.set_defaults(func=_cmd_crop)
|
|
348
|
+
|
|
349
|
+
crop_box_parser = subparsers.add_parser(
|
|
350
|
+
"crop-box",
|
|
351
|
+
help="Print the pixel box a crop config resolves to, and the matching spatial preprocessing block.",
|
|
352
|
+
)
|
|
353
|
+
crop_box_parser.add_argument(
|
|
354
|
+
"--config", "-c", type=str, required=True, help="Path to the crop config JSON file.",
|
|
355
|
+
)
|
|
356
|
+
crop_box_parser.add_argument(
|
|
357
|
+
"--size", type=int, nargs=2, required=True, metavar=("HEIGHT", "WIDTH"),
|
|
358
|
+
help="Size of the reference raster in pixels.",
|
|
359
|
+
)
|
|
360
|
+
crop_box_parser.set_defaults(func=_cmd_crop_box)
|
|
361
|
+
|
|
320
362
|
spatial_parser = subparsers.add_parser(
|
|
321
363
|
"spatial", parents=[common], help="Export deterministic crop/resize with verified execution receipts.",
|
|
322
364
|
)
|