Back to report index

Step 5 filteringintensity fec59aa: AI3D-379 Review fixes: accept null config sections, packaged JSON default_sigma key, README dep floors

Miroslav Simko <ms@iolabs.ch> 2026-09-02T08:56:49+02:00

Commit #26 · 9 snippets

 README.md                                          |  2 +-
 .../_config.py                                     | 13 +++++++++++
 .../bright_points.default.json                     |  1 +
 tests/test_bright_points_config.py                 | 26 ++++++++++++++++++++++
 4 files changed, 41 insertions(+), 1 deletion(-)
Importance #1: src/iolabs_point_cloud_filtering_intensity/_config.py @@ -4,8 +4,10 @@
4from collections.abc import Mapping4from collections.abc import Mapping
5from pathlib import Path5from pathlib import Path
6from typing import Any, Literal6from typing import Any, Literal
77
8import pydantic
9
8from iolabs.common import config_loader10from iolabs.common import config_loader
911
10logger = logging.getLogger(__name__)12logger = logging.getLogger(__name__)
1113
Importance #2: src/iolabs_point_cloud_filtering_intensity/_config.py @@ -88,8 +90,19 @@
88 laser_intensity_fitting: LaserIntensityFittingConfig = LaserIntensityFittingConfig()90 laser_intensity_fitting: LaserIntensityFittingConfig = LaserIntensityFittingConfig()
89 color_intensity_fitting: ColorIntensityFittingConfig = ColorIntensityFittingConfig()91 color_intensity_fitting: ColorIntensityFittingConfig = ColorIntensityFittingConfig()
90 file_naming: FileNamingConfig = FileNamingConfig()92 file_naming: FileNamingConfig = FileNamingConfig()
9193
94 @pydantic.field_validator(
95 "laser_intensity_fitting",
96 "color_intensity_fitting",
97 "file_naming",
98 mode="before",
99 )
100 @classmethod
101 def _null_section_means_defaults(cls, value: Any) -> Any:
102 """Treat an explicit JSON ``null`` section as "use the section defaults"."""
103 return {} if value is None else value
104
92105
93class BrightPointsConfigError(config_loader.ConfigError):106class BrightPointsConfigError(config_loader.ConfigError):
94 """Raised when bright-points config contains unsupported keys or values."""107 """Raised when bright-points config contains unsupported keys or values."""
95108
Importance #3: src/iolabs_point_cloud_filtering_intensity/bright_points.default.json @@ -24,8 +24,9 @@
24 "fit_width": 10000.0,24 "fit_width": 10000.0,
25 "n_sigma": 4.0,25 "n_sigma": 4.0,
26 "cutoff_n_sigma": 7.0,26 "cutoff_n_sigma": 7.0,
27 "max_sigma": 5000.0,27 "max_sigma": 5000.0,
28 "default_sigma": null,
28 "min_mu": 0.0,29 "min_mu": 0.0,
29 "max_mu": 68000.0,30 "max_mu": 68000.0,
30 "min_sigma": 200.0,31 "min_sigma": 200.0,
31 "angle_min": -80.0,32 "angle_min": -80.0,
Importance #4: tests/test_bright_points_config.py @@ -87,4 +87,30 @@
87 )87 )
8888
89 assert config["laser_intensity_fitting"]["default_sigma"] == 1234.089 assert config["laser_intensity_fitting"]["default_sigma"] == 1234.0
90 assert load_bright_points_config()["laser_intensity_fitting"]["default_sigma"] is None90 assert load_bright_points_config()["laser_intensity_fitting"]["default_sigma"] is None
91
92
93@pytest.mark.parametrize(
94 "section",
95 ["laser_intensity_fitting", "color_intensity_fitting", "file_naming"],
96)
97def test_normalize_bright_points_config_accepts_null_section(section):
98 config = normalize_bright_points_config({section: None})
99
100 assert config[section] == _config.BrightPointsConfig().model_dump()[section]
101
102
103def test_packaged_json_holds_every_model_key():
104 import json
105 from importlib import resources
106
107 path = resources.files("iolabs_point_cloud_filtering_intensity").joinpath(
108 "bright_points.default.json"
109 )
110 raw = json.loads(path.read_text(encoding="utf-8"))
111 model_dump = _config.BrightPointsConfig().model_dump()
112
113 assert sorted(raw) == sorted(model_dump)
114 for key, value in model_dump.items():
115 if isinstance(value, dict):
116 assert sorted(raw[key]) == sorted(value), key
Importance #5: README.md @@ -16,9 +16,9 @@
1616
17## Requirements17## Requirements
1818
19- Python ≥3.11, <3.1319- Python ≥3.11, <3.13
20- numpy, open3d, torch, scipy, matplotlib, laspy, pypdf, tqdm, pydantic, iolabs-common (≥0.8.0), iolabs-geometry-geometry, iolabs-geometry-visualization, iolabs-point-cloud-filtering-surface, iolabs-point-cloud-las-tools20- numpy, open3d, torch, scipy, matplotlib, laspy, pypdf, tqdm, pydantic (≥2.7), iolabs-common (≥0.9.0), iolabs-geometry-geometry, iolabs-geometry-visualization, iolabs-point-cloud-filtering-surface, iolabs-point-cloud-las-tools
2121
22## Usage22## Usage
2323
24Identifies bright lane markings using intensity filtering on top of surface detection. Feeds into cluster-based middle lane detection and trajectory segmentation.24Identifies bright lane markings using intensity filtering on top of surface detection. Feeds into cluster-based middle lane detection and trajectory segmentation.
Importance #6: src/iolabs_point_cloud_filtering_intensity/_config.py @@ -4,8 +4,10 @@
4from collections.abc import Mapping4from collections.abc import Mapping
5from pathlib import Path5from pathlib import Path
6from typing import Any, Literal6from typing import Any, Literal
77
8import pydantic
9
8from iolabs.common import config_loader10from iolabs.common import config_loader
911
10logger = logging.getLogger(__name__)12logger = logging.getLogger(__name__)
1113
Importance #7: src/iolabs_point_cloud_filtering_intensity/_config.py @@ -88,8 +90,19 @@
88 laser_intensity_fitting: LaserIntensityFittingConfig = LaserIntensityFittingConfig()90 laser_intensity_fitting: LaserIntensityFittingConfig = LaserIntensityFittingConfig()
89 color_intensity_fitting: ColorIntensityFittingConfig = ColorIntensityFittingConfig()91 color_intensity_fitting: ColorIntensityFittingConfig = ColorIntensityFittingConfig()
90 file_naming: FileNamingConfig = FileNamingConfig()92 file_naming: FileNamingConfig = FileNamingConfig()
9193
94 @pydantic.field_validator(
95 "laser_intensity_fitting",
96 "color_intensity_fitting",
97 "file_naming",
98 mode="before",
99 )
100 @classmethod
101 def _null_section_means_defaults(cls, value: Any) -> Any:
102 """Treat an explicit JSON ``null`` section as "use the section defaults"."""
103 return {} if value is None else value
104
92105
93class BrightPointsConfigError(config_loader.ConfigError):106class BrightPointsConfigError(config_loader.ConfigError):
94 """Raised when bright-points config contains unsupported keys or values."""107 """Raised when bright-points config contains unsupported keys or values."""
95108
Importance #8: src/iolabs_point_cloud_filtering_intensity/bright_points.default.json @@ -24,8 +24,9 @@
24 "fit_width": 10000.0,24 "fit_width": 10000.0,
25 "n_sigma": 4.0,25 "n_sigma": 4.0,
26 "cutoff_n_sigma": 7.0,26 "cutoff_n_sigma": 7.0,
27 "max_sigma": 5000.0,27 "max_sigma": 5000.0,
28 "default_sigma": null,
28 "min_mu": 0.0,29 "min_mu": 0.0,
29 "max_mu": 68000.0,30 "max_mu": 68000.0,
30 "min_sigma": 200.0,31 "min_sigma": 200.0,
31 "angle_min": -80.0,32 "angle_min": -80.0,
Importance #9: tests/test_bright_points_config.py @@ -87,4 +87,30 @@
87 )87 )
8888
89 assert config["laser_intensity_fitting"]["default_sigma"] == 1234.089 assert config["laser_intensity_fitting"]["default_sigma"] == 1234.0
90 assert load_bright_points_config()["laser_intensity_fitting"]["default_sigma"] is None90 assert load_bright_points_config()["laser_intensity_fitting"]["default_sigma"] is None
91
92
93@pytest.mark.parametrize(
94 "section",
95 ["laser_intensity_fitting", "color_intensity_fitting", "file_naming"],
96)
97def test_normalize_bright_points_config_accepts_null_section(section):
98 config = normalize_bright_points_config({section: None})
99
100 assert config[section] == _config.BrightPointsConfig().model_dump()[section]
101
102
103def test_packaged_json_holds_every_model_key():
104 import json
105 from importlib import resources
106
107 path = resources.files("iolabs_point_cloud_filtering_intensity").joinpath(
108 "bright_points.default.json"
109 )
110 raw = json.loads(path.read_text(encoding="utf-8"))
111 model_dump = _config.BrightPointsConfig().model_dump()
112
113 assert sorted(raw) == sorted(model_dump)
114 for key, value in model_dump.items():
115 if isinstance(value, dict):
116 assert sorted(raw[key]) == sorted(value), key