Borner le comblement du MNT à l'enveloppe des points, ajouter la couche précision et un affichage relief/précision, encoder les sous-tuiles une seule fois en q75

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Antoine Jacquin
2026-09-27 15:07:04 +02:00
parent c5097e21a0
commit 90dbe61f3b
17 changed files with 994 additions and 593 deletions

View File

@ -90,10 +90,11 @@ from .visualizations import (
generate_flow_accumulation,
generate_anomaly_mask,
generate_relief_oriente,
generate_densite_sol,
)
from .gpu import gpu_cleanup, num_gpus, available_gpu_ids, restrict_gpus, safe_gpu_call, gpu_worker_slots
from .ign import generate_ign_overlay
from .rendering import tif_to_crop
from .rendering import tif_to_crop, LOSSLESS_GRAY_KEYWORDS
# Ordered list of visualization steps.
@ -114,6 +115,7 @@ VIZ_STEPS = [
('solar', generate_solar),
('anomaly', generate_anomaly_mask),
('relief_oriente', generate_relief_oriente),
('densite_sol', generate_densite_sol),
('ortho', lambda d, b, v, r: generate_ign_overlay(
d, b, v, r,
layer='ORTHOIMAGERY.ORTHOPHOTOS',
@ -262,6 +264,8 @@ class LidarArchaeoPipeline:
def _expected_output_path(name, basename, file_vis_dir, output_format='avif'):
"""Return the expected output filename for a visualization step."""
ext = 'avif' if output_format == 'avif' else 'webp'
if name in LOSSLESS_GRAY_KEYWORDS:
ext = 'webp' # aplats de niveaux : WebP sans perte (rendering.tif_to_crop)
if name == 'pos_open':
return file_vis_dir / f"{basename}_positive_openness.{ext}"
elif name == 'neg_open':
@ -370,7 +374,7 @@ class LidarArchaeoPipeline:
logger.info(f" Conversion images {fmt_label}:")
for name, tif_file in vis_results.items():
if tif_file and isinstance(tif_file, Path) and tif_file.suffix == '.tif' and tif_file.exists():
img_file = tif_to_crop(tif_file, file_vis_dir, resolution, keep_tif=self.keep_tif, quality=self.quality, output_format=self.output_format)
img_file = tif_to_crop(tif_file, file_vis_dir, resolution, keep_tif=self.keep_tif, quality=self.quality, output_format=self.output_format, subtiles_dir=self.output_dir)
if img_file:
logger.info(f" ✓ {img_file.name}")
@ -468,6 +472,12 @@ class LidarArchaeoPipeline:
from .dtm import read_dtm_edge_buffer
return abs(read_dtm_edge_buffer(dtm_path) - self.edge_buffer) < 1e-6
def _gap_fill_matches(self, dtm_path):
"""True si le DTM a été comblé par la version courante (tag
LIDAR_GAP_FILL, dtm.py) ; absent = ancien comblement à distance fixe."""
from .dtm import read_dtm_gap_fill, GAP_FILL_VERSION
return read_dtm_gap_fill(dtm_path) == GAP_FILL_VERSION
def _fetch_edge_neighbors(self, files):
"""Télécharge les dalles LAZ voisines manquantes (raccord des bords).
@ -556,6 +566,10 @@ class LidarArchaeoPipeline:
if not self._strip_align_matches(basename, res_suffix):
logger.info(f" DTM{res_suffix} sans calage de faisceaux conforme — régénération (offsets verticaux mesurés et appliqués)")
dtm_path.unlink()
elif not self._gap_fill_matches(dtm_path):
logger.info(f" DTM{res_suffix} au comblement d'une version antérieure — régénération "
f"(vides comblés dans l'enveloppe des points, rayon selon la densité)")
dtm_path.unlink()
elif not self._edge_buffer_matches(dtm_path):
from .dtm import read_dtm_edge_buffer
recorded = read_dtm_edge_buffer(dtm_path)