Correction orientation nord + filtrage ReturnNumber=0 pour fichiers corrompus
- Inversion axe Y du DTM pour orientation nord correcte - Fallback filters.range pour fichiers LAZ avec ReturnNumber=0 - Flèche nord vectorielle noire au-dessus de la légende - 9/9 fichiers traités avec succès Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
116
process_lidar.py
116
process_lidar.py
@ -109,53 +109,7 @@ class LidarArchaeoPipeline:
|
||||
print(f" ✓ Classification déjà effectuée")
|
||||
return output_las
|
||||
|
||||
# First, try to fix ReturnNumber values if needed
|
||||
fixed_las = self.temp_dir / f"{laz_file.stem}_fixed.las"
|
||||
|
||||
# Create preprocessing pipeline to fix ReturnNumber=0 issue using expression
|
||||
fix_pipeline = [
|
||||
{
|
||||
"type": "readers.las",
|
||||
"filename": str(laz_file)
|
||||
},
|
||||
{
|
||||
"type": "filters.expression",
|
||||
"expression": "ReturnNumber = MAX(ReturnNumber, 1)"
|
||||
},
|
||||
{
|
||||
"type": "filters.expression",
|
||||
"expression": "NumberOfReturns = MAX(NumberOfReturns, 1)"
|
||||
},
|
||||
{
|
||||
"type": "writers.las",
|
||||
"filename": str(fixed_las),
|
||||
"extra_dims": "all"
|
||||
}
|
||||
]
|
||||
|
||||
try:
|
||||
# Try with fixed data first
|
||||
fix_json = json.dumps(fix_pipeline)
|
||||
fix_pipe_file = self.temp_dir / "fix_pipeline.json"
|
||||
with open(fix_pipe_file, 'w') as f:
|
||||
f.write(fix_json)
|
||||
|
||||
import subprocess
|
||||
result = subprocess.run(['pdal', 'pipeline', str(fix_pipe_file)],
|
||||
capture_output=True, text=True)
|
||||
if result.returncode == 0:
|
||||
print(f" → Correction ReturnNumber effectuée")
|
||||
input_for_smrf = fixed_las
|
||||
else:
|
||||
print(f" → Erreur correction, utilisation fichier original")
|
||||
input_for_smrf = laz_file
|
||||
if fixed_las.exists():
|
||||
fixed_las.unlink()
|
||||
except Exception as e:
|
||||
print(f" → Erreur prétraitement: {e}")
|
||||
input_for_smrf = laz_file
|
||||
|
||||
pipeline_json = self.create_pipeline_json(input_for_smrf, output_las)
|
||||
pipeline_json = self.create_pipeline_json(laz_file, output_las)
|
||||
pipeline_file = self.temp_dir / "pipeline.json"
|
||||
|
||||
with open(pipeline_file, 'w') as f:
|
||||
@ -169,6 +123,71 @@ class LidarArchaeoPipeline:
|
||||
)
|
||||
print(f" ✓ Classification sol terminée")
|
||||
return output_las
|
||||
except subprocess.CalledProcessError as e:
|
||||
error_msg = e.stderr.decode()
|
||||
print(f" ✗ Erreur PDAL: {error_msg}")
|
||||
|
||||
# If error is about ReturnNumber=0, try filtering those points
|
||||
if "ReturnNumber" in error_msg and "NumberOfReturns" in error_msg:
|
||||
print(f" → Tentative de filtrage des points ReturnNumber=0...")
|
||||
|
||||
# Use filters.range to keep only valid points (ReturnNumber >= 1)
|
||||
filtered_pipeline = [
|
||||
{
|
||||
"type": "readers.las",
|
||||
"filename": str(laz_file)
|
||||
},
|
||||
{
|
||||
"type": "filters.range",
|
||||
"limits": "ReturnNumber[1:],NumberOfReturns[1:]"
|
||||
},
|
||||
{
|
||||
"type": "filters.smrf",
|
||||
"scalar": 1.25
|
||||
},
|
||||
{
|
||||
"type": "filters.range",
|
||||
"limits": "Classification[2:2]"
|
||||
},
|
||||
{
|
||||
"type": "writers.las",
|
||||
"filename": str(output_las),
|
||||
"extra_dims": "all"
|
||||
}
|
||||
]
|
||||
|
||||
filtered_json = json.dumps(filtered_pipeline)
|
||||
with open(pipeline_file, 'w') as f:
|
||||
f.write(filtered_json)
|
||||
|
||||
try:
|
||||
subprocess.run(
|
||||
["pdal", "pipeline", str(pipeline_file)],
|
||||
capture_output=True,
|
||||
check=True
|
||||
)
|
||||
print(f" ✓ Classification sol terminée (points filtrés)")
|
||||
return output_las
|
||||
except subprocess.CalledProcessError as e2:
|
||||
print(f" ✗ Erreur même avec filtrage: {e2.stderr.decode()}")
|
||||
return None
|
||||
|
||||
return None
|
||||
|
||||
def run_pdal_pipeline(self, pipeline_json, output_file):
|
||||
"""Exécute un pipeline PDAL"""
|
||||
pipeline_file = self.temp_dir / "temp_pipeline.json"
|
||||
with open(pipeline_file, 'w') as f:
|
||||
f.write(pipeline_json)
|
||||
|
||||
try:
|
||||
subprocess.run(
|
||||
["pdal", "pipeline", str(pipeline_file)],
|
||||
capture_output=True,
|
||||
check=True
|
||||
)
|
||||
print(f" ✓ Pipeline terminé")
|
||||
return output_file
|
||||
except subprocess.CalledProcessError as e:
|
||||
print(f" ✗ Erreur PDAL: {e.stderr.decode()}")
|
||||
return None
|
||||
@ -209,6 +228,9 @@ class LidarArchaeoPipeline:
|
||||
|
||||
dtm = stat.statistic.T
|
||||
|
||||
# Flip Y axis so north is at the top (Y decreases from top to bottom in image)
|
||||
dtm = dtm[::-1, :]
|
||||
|
||||
# Fill gaps using interpolation
|
||||
print(f" Remplissage des zones vides...")
|
||||
from scipy.ndimage import distance_transform_edt
|
||||
|
||||
Reference in New Issue
Block a user