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:
@ -628,12 +628,24 @@ def generate_svf(dem_file, basename, vis_dir, resolution, shared=None):
|
||||
return None
|
||||
|
||||
|
||||
# Sous-échantillonnage du calcul d'openness : le lancé de rayons est l'étape
|
||||
# la plus coûteuse du pipeline (rayons de 100 m = 500 pas à 0,2 m/px). L'openness
|
||||
# étant un champ angulaire lisse (moyenne d'horizons jusqu'à 100 m), on la
|
||||
# calcule sur une grille décimée par blocs — max local pour l'openness
|
||||
# positive, min pour la négative, afin de préserver les reliefs qui bornent
|
||||
# l'angle d'horizon (talus, murs) — puis on la rééchantillonne à la
|
||||
# résolution demandée. Coût ÷ facteur³ (cellules ÷ facteur², rayons ÷ facteur)
|
||||
# ; facteur 2 ≈ ×8 plus rapide, rendu quasi identique. 1 = pleine résolution.
|
||||
OPENNESS_DOWNSAMPLE = 2
|
||||
|
||||
|
||||
def generate_openness(dem_file, basename, vis_dir, resolution, positive=True, shared=None):
|
||||
"""Positive/Negative Openness - multi-radius ray-tracing with std normalization.
|
||||
|
||||
Traces rays in 8 directions at 3 radii (25, 50, 100m).
|
||||
Results are combined with equal weight across radii, then normalized
|
||||
by standard deviation for cross-tile comparability.
|
||||
Traces rays in 8 directions at 3 radii (25, 50, 100m) on a block-decimated
|
||||
grid (cf. OPENNESS_DOWNSAMPLE), then bilinearly resamples the result back
|
||||
to the requested resolution. Results are combined with equal weight across
|
||||
radii, then normalized by standard deviation for cross-tile comparability.
|
||||
"""
|
||||
name = "positive_openness" if positive else "negative_openness"
|
||||
gpu_tag = " [GPU]" if _gpu_mod.HAS_GPU else ""
|
||||
@ -646,8 +658,21 @@ def generate_openness(dem_file, basename, vis_dir, resolution, positive=True, sh
|
||||
_prepare_dem_for_raycast(dem_file, shared, resolution)
|
||||
|
||||
radii_m = [25, 50, 100]
|
||||
max_dist = int(max(radii_m) / res) # rayon réel en pixels, non tronqué
|
||||
n_dirs = 8
|
||||
full_rows, full_cols = rows, cols
|
||||
|
||||
# Grille décimée par blocs (cf. OPENNESS_DOWNSAMPLE)
|
||||
factor = max(1, int(OPENNESS_DOWNSAMPLE))
|
||||
if factor > 1 and rows >= 2 * factor and cols >= 2 * factor:
|
||||
r2 = (rows // factor) * factor
|
||||
c2 = (cols // factor) * factor
|
||||
blocks = dem[:r2, :c2].reshape(r2 // factor, factor, c2 // factor, factor)
|
||||
dem = blocks.max(axis=(1, 3)) if positive else blocks.min(axis=(1, 3))
|
||||
rows, cols = dem.shape
|
||||
res = res * factor
|
||||
logger.info(f" Grille décimée ×{factor} ({rows}×{cols}) — rééchantillonnage final")
|
||||
|
||||
max_dist = int(max(radii_m) / res) # rayon réel en pixels, non tronqué
|
||||
|
||||
pos_angles, neg_angles = _ray_trace_horizons(dem, rows, cols, res, n_dirs, max_dist, radii_m)
|
||||
|
||||
@ -660,6 +685,18 @@ def generate_openness(dem_file, basename, vis_dir, resolution, positive=True, sh
|
||||
# Mean across directions and radii (equal weight) — on CPU now
|
||||
openness = np.mean(angles, axis=(0, 1))
|
||||
openness_result = np.degrees(openness).astype(np.float32)
|
||||
|
||||
# Retour à la grille pleine résolution (bilinéaire ; bord ajusté au plus
|
||||
# proche voisin si les dimensions n'étaient pas divisibles par le facteur)
|
||||
if (rows, cols) != (full_rows, full_cols):
|
||||
from scipy.ndimage import zoom
|
||||
zoomed = zoom(openness_result, factor, order=1)
|
||||
if zoomed.shape != (full_rows, full_cols):
|
||||
zoomed = np.pad(zoomed,
|
||||
((0, full_rows - zoomed.shape[0]),
|
||||
(0, full_cols - zoomed.shape[1])),
|
||||
mode='edge')
|
||||
openness_result = zoomed.astype(np.float32)
|
||||
openness_result[nan_mask] = np.nan
|
||||
|
||||
# Z-score (écarts locaux en sigmas) : unités comparables entre tuiles,
|
||||
|
||||
Reference in New Issue
Block a user