Skip to content

Commit ffc3977

Browse files
M11.1 v1.9.2 Keep extra-dimensions Distance and Normal (#80)
* Do not clean out dims Normal and Distance * Keep extra_dims only if they exist in Cleaner. * Add test for Cleaning with a non-existing dim * Flake8 fix * Fix Cleaner test for edge case of empty str for extra-dims
1 parent cd03108 commit ffc3977

File tree

4 files changed

+31
-15
lines changed

4 files changed

+31
-15
lines changed

configs/data_format/default.yaml

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22
las_dimensions:
33
# input
44
classification: Classification # las format
5+
terrascan_normal: Normal
6+
terrascan_distance: Distance
57

68
# Extra dims
79
# ATTENTION: If extra dimensions are added, you may want to add them in cleaning.in parameter as well.
@@ -57,12 +59,14 @@ codes:
5759

5860

5961
cleaning:
60-
# Extra dims that are kept when application starts. Others are removed to lighten the LAS.
62+
# Extra dims that are kept when application starts. Others are removed to lighten the LAS.
6163
input_building:
6264
_target_: lidar_prod.tasks.cleaning.Cleaner
6365
extra_dims:
6466
- "${data_format.las_dimensions.ai_building_proba}=float"
6567
- "${data_format.las_dimensions.entropy}=float"
68+
- "${data_format.las_dimensions.terrascan_normal}=uint"
69+
- "${data_format.las_dimensions.terrascan_distance}=uint"
6670
# - "${data_format.las_dimensions.ai_vegetation_proba}=float"
6771
# - "${data_format.las_dimensions.ai_unclassified_proba}=float"
6872
output_building:
@@ -74,9 +78,11 @@ cleaning:
7478
# - "${data_format.las_dimensions.ai_vegetation_proba}=float"
7579
- "${data_format.las_dimensions.entropy}=float"
7680
- "${data_format.las_dimensions.ai_building_identified}=uint"
81+
- "${data_format.las_dimensions.terrascan_normal}=uint"
82+
- "${data_format.las_dimensions.terrascan_distance}=uint"
7783
# - "${data_format.las_dimensions.ai_vegetation_unclassified_groups}=uint"
7884
input_vegetation_unclassified:
79-
# Extra dims added for storing the result of the vegetation/unclassified detection.
85+
# Extra dims added for storing the result of the vegetation/unclassified detection.
8086
_target_: lidar_prod.tasks.cleaning.Cleaner
8187
extra_dims:
8288
- "${data_format.las_dimensions.ai_vegetation_unclassified_groups}=uint32"

lidar_prod/tasks/cleaning.py

Lines changed: 14 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
import laspy
77
import pdal
88

9-
from lidar_prod.tasks.utils import get_pdal_reader, get_pdal_writer
9+
from lidar_prod.tasks.utils import get_pdal_writer, pdal_read_las_array
1010

1111
log = logging.getLogger(__name__)
1212

@@ -27,16 +27,21 @@ def __init__(self, extra_dims: Optional[Union[Iterable[str], str]]):
2727

2828
# creating a dict where key = dimension's name and value = diemnsion's type
2929
# if no "=type" in extra_dims then value = None
30+
# This is for easier manipulation
3031
self.extra_dims_as_dict = dict()
3132
for extra_dim in self.extra_dims:
3233
if len(extra_dim.split("=")) == 2:
3334
self.extra_dims_as_dict[extra_dim.split("=")[0]] = extra_dim.split("=")[1]
34-
else:
35+
elif extra_dim:
3536
self.extra_dims_as_dict[extra_dim] = None
37+
else:
38+
# empty string
39+
pass
3640

3741
def get_extra_dims_as_str(self):
3842
"""'stringify' the extra_dims list and return it, or an empty list if there is no extra dims"""
39-
return_str = ",".join(self.extra_dims)
43+
self.extra_dims_as_dict
44+
return_str = ",".join([f"{k}={v}" for k, v in self.extra_dims_as_dict.items()])
4045
return return_str if return_str else []
4146

4247
def run(self, src_las_path: str, target_las_path: str):
@@ -46,9 +51,12 @@ def run(self, src_las_path: str, target_las_path: str):
4651
src_las_path (str): input LAS path
4752
target_las_path (str): output LAS path, with specified extra dims.
4853
"""
49-
pipeline = pdal.Pipeline()
50-
pipeline |= get_pdal_reader(src_las_path)
51-
pipeline |= get_pdal_writer(target_las_path, extra_dims=self.get_extra_dims_as_str())
54+
points = pdal_read_las_array(src_las_path)
55+
# Check input dims to see what we can keep.
56+
input_dims = points.dtype.fields.keys()
57+
self.extra_dims_as_dict = {k: v for k, v in self.extra_dims_as_dict.items() if k in input_dims}
58+
59+
pipeline = pdal.Pipeline(arrays=[points]) | get_pdal_writer(target_las_path, extra_dims=self.get_extra_dims_as_str())
5260
os.makedirs(osp.dirname(target_las_path), exist_ok=True)
5361
pipeline.execute()
5462
log.info(f"Saved to {target_las_path}")

package_metadata.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
__version__: "M11.1V1.9.1"
1+
__version__: "M11.1V1.9.2"
22
__name__: "lidar_prod"
33
__url__: "https://github.com/IGNF/lidar-prod-quality-control"
44
__description__: "A 3D semantic segmentation production tool to augment rule- based Lidar classification with AI and databases."

tests/lidar_prod/tasks/test_cleaning.py

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -38,10 +38,11 @@ def test_cleaning_float_extra_dim():
3838
assert "building" not in las_dimensions
3939

4040

41-
def test_cleaning_two_float_extra_dims():
41+
def test_cleaning_two_float_extra_dims_and_one_fantasy_dim():
4242
d1 = "entropy"
4343
d2 = "building"
44-
extra_dims = [f"{d1}=float", f"{d2}=float"]
44+
d3 = "i_do_not_exist_but_no_error_incurs"
45+
extra_dims = [f"{d1}=float", f"{d2}=float", f"{d3}=float"]
4546
cl = Cleaner(extra_dims=extra_dims)
4647
with tempfile.TemporaryDirectory() as td:
4748
clean_las_path = osp.join(td, "float_extra_dim.las")
@@ -50,10 +51,11 @@ def test_cleaning_two_float_extra_dims():
5051
out_a = pdal_read_las_array(clean_las_path)
5152
assert d1 in out_a.dtype.fields.keys()
5253
assert d2 in out_a.dtype.fields.keys()
54+
assert d3 not in out_a.dtype.fields.keys()
5355

5456

5557
@pytest.mark.parametrize("extra_dims", ("", "entropy=float", "building=float"))
56-
def test_cleaning_format(extra_dims):
58+
def test_pdal_cleaning_format(extra_dims):
5759
cl = Cleaner(extra_dims=extra_dims)
5860
with tempfile.TemporaryDirectory() as td:
5961
clean_las_path = osp.join(td, "float_extra_dim.las")
@@ -69,7 +71,7 @@ def test_cleaning_format(extra_dims):
6971
(["entropy=float", "building=float"], "entropy=float,building=float"),
7072
],
7173
)
72-
def test_cleaning_get_extra_dims_as_str(extra_dims, expected):
74+
def test_pdal_cleaning_get_extra_dims_as_str(extra_dims, expected):
7375
cleaner = Cleaner(extra_dims=extra_dims)
7476
assert cleaner.get_extra_dims_as_str() == expected
7577

@@ -144,7 +146,7 @@ def test_cleaning_get_extra_dims_as_str(extra_dims, expected):
144146
),
145147
],
146148
)
147-
def test_cleaning_remove_dimensions(extra_dims, expected):
149+
def test_laspy_cleaning_remove_dimensions(extra_dims, expected):
148150
las_data = get_las_data_from_las(LAS_SUBSET_FILE_VEGETATION)
149151
cleaner = Cleaner(extra_dims=extra_dims)
150152
cleaner.remove_dimensions(las_data)
@@ -215,7 +217,7 @@ def test_cleaning_remove_dimensions(extra_dims, expected):
215217
),
216218
],
217219
)
218-
def test_cleaning_add_dimensions(extra_dims, expected):
220+
def test_laspy_cleaning_add_dimensions(extra_dims, expected):
219221
las_data = get_las_data_from_las(LAS_SUBSET_FILE_VEGETATION)
220222
cleaner = Cleaner(extra_dims=extra_dims)
221223
cleaner.add_dimensions(las_data)

0 commit comments

Comments
 (0)