Remove PMF, fix NaN in gradient visualizations, fix pos_open/neg_open shared param
- Remove PMF from ground classification options (PDAL recommends SMRF over PMF) - Auto-detection now uses CSF for urban/complex terrain instead of PMF - Add z_std > 30m heuristic to auto-select CSF for complex terrain - Fix pos_open/neg_open lambda missing 'shared' parameter (NameError in workers) - Fix NaN mask not restored in hillshade, slope, aspect, curvature (gradient-based products computed on filled DEM lost NaN transparency) - Add nan_mask parameter to _save_tif for centralized NaN restoration - DTM TIF kept by default (no longer deleted after WebP conversion) Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@ -1,6 +1,6 @@
|
||||
"""DTM generation from classified LiDAR point clouds.
|
||||
|
||||
Handles ground classification via PDAL/SMRF and DTM rasterisation
|
||||
Handles ground classification via PDAL (SMRF or CSF) and DTM rasterisation
|
||||
using scipy binned_statistic_2d. Zones without LiDAR data remain as NaN.
|
||||
"""
|
||||
|
||||
@ -27,13 +27,13 @@ def _create_ground_pipeline(input_laz, output_las, method):
|
||||
1. Reset Classification to 0
|
||||
2. ELM (Extended Local Minimum) — mark low outliers as noise (Classification=7)
|
||||
3. Statistical outlier removal
|
||||
4. Ground classification (SMRF/PMF/CSF)
|
||||
4. Ground classification (SMRF or CSF)
|
||||
5. Extract ground points (Classification=2)
|
||||
|
||||
Args:
|
||||
input_laz: Path to input LAZ/LAS file.
|
||||
output_las: Path to output classified LAS file.
|
||||
method: Ground classification method ('smrf', 'pmf', or 'csf').
|
||||
method: Ground classification method ('smrf' or 'csf').
|
||||
|
||||
Returns:
|
||||
JSON string of the PDAL pipeline.
|
||||
@ -84,15 +84,6 @@ def _create_ground_pipeline(input_laz, output_las, method):
|
||||
"threshold": 0.5,
|
||||
"scalar": 1.25
|
||||
}
|
||||
elif method == 'pmf':
|
||||
ground_step = {
|
||||
"type": "filters.pmf",
|
||||
"max_window": 33,
|
||||
"slope": 0.15,
|
||||
"max_distance": 2.5,
|
||||
"initial_distance": 0.15,
|
||||
"cell_size": 1.0
|
||||
}
|
||||
elif method == 'csf':
|
||||
ground_step = {
|
||||
"type": "filters.csf",
|
||||
@ -128,11 +119,6 @@ def create_smrf_pipeline(input_laz, output_las):
|
||||
return _create_ground_pipeline(input_laz, output_las, 'smrf')
|
||||
|
||||
|
||||
def create_pmf_pipeline(input_laz, output_las):
|
||||
"""Create a PDAL pipeline JSON for PMF ground classification."""
|
||||
return _create_ground_pipeline(input_laz, output_las, 'pmf')
|
||||
|
||||
|
||||
def create_csf_pipeline(input_laz, output_las):
|
||||
"""Create a PDAL pipeline JSON for CSF ground classification."""
|
||||
return _create_ground_pipeline(input_laz, output_las, 'csf')
|
||||
@ -141,9 +127,9 @@ def create_csf_pipeline(input_laz, output_las):
|
||||
def detect_ground_method(laz_file):
|
||||
"""Detect the best ground classification method based on point cloud statistics.
|
||||
|
||||
Auto-selects between SMRF (natural terrain) and PMF (urban) only.
|
||||
CSF is available only via --ground-classification csf (slow but robust
|
||||
on complex terrain).
|
||||
Auto-selects between SMRF and CSF:
|
||||
- SMRF: fast, robust for most natural terrain (PDAL recommended default)
|
||||
- CSF: cloth simulation, better for complex/urban terrain
|
||||
|
||||
Falls back to SMRF if the file cannot be read or attributes are missing.
|
||||
|
||||
@ -151,7 +137,7 @@ def detect_ground_method(laz_file):
|
||||
laz_file: Path to input LAZ/LAS file.
|
||||
|
||||
Returns:
|
||||
String: 'smrf', 'pmf', or 'csf'
|
||||
String: 'smrf' or 'csf'
|
||||
"""
|
||||
import laspy
|
||||
|
||||
@ -182,13 +168,16 @@ def detect_ground_method(laz_file):
|
||||
f"ratio_retours_uniques={single_return_ratio:.2f}, "
|
||||
f"écart_Z={z_std:.1f}m, amplitude_Z={z_range:.1f}m")
|
||||
|
||||
# Decision logic (auto selects between SMRF and PMF only):
|
||||
# - High single-return ratio (>0.6) → urban (buildings, roads) → PMF
|
||||
# Decision logic:
|
||||
# - High single-return ratio (>0.6) → urban (buildings, roads) → CSF (cloth simulation)
|
||||
# - High elevation variance (>30m) → complex/mountainous terrain → CSF
|
||||
# - Default → SMRF (fast, robust for most natural terrain)
|
||||
# Note: CSF is available only via --ground-classification csf (slow but robust on complex terrain)
|
||||
if single_return_ratio > 0.6:
|
||||
method = 'pmf'
|
||||
method = 'csf'
|
||||
reason = f"ratio retours uniques={single_return_ratio:.2f} > 0.6 → milieu urbain"
|
||||
elif z_std > 30:
|
||||
method = 'csf'
|
||||
reason = f"écart_Z={z_std:.1f}m > 30m → terrain complexe"
|
||||
else:
|
||||
method = 'smrf'
|
||||
reason = f"terrain naturel standard"
|
||||
@ -203,7 +192,7 @@ def classify_ground(laz_file, temp_dir, method='auto', force=False):
|
||||
Args:
|
||||
laz_file: Path to input LAZ/LAS file.
|
||||
temp_dir: Directory for temporary files (pipeline.json, ground.las).
|
||||
method: Ground classification method ('auto', 'smrf', 'pmf', or 'csf').
|
||||
method: Ground classification method ('auto', 'smrf', or 'csf').
|
||||
force: If True, reclassify even if output file already exists.
|
||||
|
||||
Returns:
|
||||
|
||||
Reference in New Issue
Block a user