Caler les faisceaux de vol, décimer l'openness et cibler les couches affichées

Trois chantiers liés à la qualité et au coût des rendus :

- Calage vertical des faisceaux : les passes d'une tuile peuvent être biaisées
  de quelques cm (±2,5 cm mesurés sur 1000_6882), créant des marches aux
  recouvrements. Les offsets par PointSourceId sont mesurés sur les points
  sol de la tuile (réf. médiane itérée) et retranchés ≥ 0,5 cm avant
  rastérisation, avec sidecar de cache et application au plancher bare-earth.
- Openness décimée ×2 : lancé de rayons sur grille par blocs (max/min) puis
  rééchantillonnage bilinéaire — 532 s → 40 s par tuile à 0,2 m sur CPU,
  signal archéologique préservé. Réglable --openness-downsample.
- Génération webapp concentrée sur les couches affichées : les défauts
  /api/generate, /api/preview et le sélecteur génèrent le panneau complet
  (slope, aspect, pos_open) au lieu d'aspect seul.
This commit is contained in:
Antoine Jacquin
2026-09-19 00:53:22 +02:00
parent 796e68c870
commit 41206f65de
11 changed files with 455 additions and 25 deletions

View File

@ -463,6 +463,20 @@ def _fallback_viz_step_names():
return [KEYWORD_TO_STEP.get(k, k) for k in VIZ_LABELS]
def _panel_viz_steps():
"""Couches réellement affichées par la webapp, en noms d'étapes --only.
Source unique : PANEL_VIZ (index.py) — le panneau de couches et le
sélecteur de génération sont pilotés par ce registre, les valeurs par
défaut de /api/preview et /api/generate doivent donc l'être aussi, sans
quoi une tuile générée « par défaut » serait incomplète pour le panneau.
"""
from .index import PANEL_VIZ, KEYWORD_TO_STEP
if not PANEL_VIZ:
return ["aspect"]
return [KEYWORD_TO_STEP.get(v, v) for v in PANEL_VIZ]
def _viz_step_labels():
"""Libellés français des étapes de visualisation (clé = nom d'étape).
@ -480,7 +494,8 @@ class PreviewRequest(BaseModel):
regenerate: bool = Field(False, description="Inclure les tuiles déjà générées")
viz: Optional[list] = Field(None,
description="Visualisations demandées, noms d'étapes du "
"pipeline (ex: aspect, wavelet) ; défaut : aspect. "
"pipeline (ex: aspect, wavelet) ; défaut : les "
"couches affichées dans le panneau. "
"Une tuile existante est considérée faite "
"seulement si elle les possède toutes")
all_missing: bool = Field(False,
@ -517,7 +532,7 @@ class GenerateRequest(BaseModel):
viz: list = Field(None,
description="Visualisations à générer, noms d'étapes du "
"pipeline (ex: aspect, wavelet, slope) ; "
"défaut : aspect")
"défaut : les couches affichées dans le panneau")
all_missing: bool = Field(False,
description="Ignorer la liste de tuiles : traiter toutes "
"les dalles LHD présentes dans input/ qui "
@ -1152,7 +1167,7 @@ def preview(req: PreviewRequest):
# Webapp légère : le distant connaît input/ et output/ complets
return _proxy_api("POST", "/api/preview", json.loads(req.json()))
names = _viz_step_names()
viz = [v for v in (req.viz or []) if v] or ["aspect"]
viz = [v for v in (req.viz or []) if v] or _panel_viz_steps()
invalid = [v for v in viz if v not in names]
if invalid:
raise HTTPException(
@ -1217,7 +1232,7 @@ def _build_command(tiles, regenerate=False, ground_class="ign", bare_earth=False
cmd = [sys.executable, "-u", "-m", "lidar_pipeline", str(INPUT_DIR),
"-o", str(OUTPUT_DIR),
"-r", ",".join(str(r) for r in GENERATE_RESOLUTIONS),
"--only", *(viz or ["aspect"]),
"--only", *(viz or _panel_viz_steps()),
"--ground-classification", ground_class,
"--ign-classes", ign_classes]
if bare_earth:
@ -1246,7 +1261,7 @@ def _resolve_request(req):
input/ peuvent avoir changé entre la mise en file et le départ du run.
"""
names = _viz_step_names()
viz = [v for v in (req.viz or []) if v] or ["aspect"]
viz = [v for v in (req.viz or []) if v] or _panel_viz_steps()
invalid = [v for v in viz if v not in names]
if invalid:
raise HTTPException(