Accélérer la rugosité 2.5x via écarts-types par sommes intégrales
This commit is contained in:
@ -843,11 +843,55 @@ def generate_sailore(dem_file, basename, vis_dir, resolution, shared=None):
|
|||||||
# Roughness
|
# Roughness
|
||||||
# ============================================================
|
# ============================================================
|
||||||
|
|
||||||
|
def _integral_sums(x):
|
||||||
|
"""Sommes intégrales 2D : S[i,j] = somme de x[0:i, 0:j] (float64).
|
||||||
|
|
||||||
|
Ligne/colonne 0 remplies de zéros — permet la somme d'une fenêtre
|
||||||
|
quelconque par 4 coins, y compris contre le bord (indices 0).
|
||||||
|
"""
|
||||||
|
rows, cols = x.shape
|
||||||
|
S = xp.zeros((rows + 1, cols + 1), dtype=np.float64)
|
||||||
|
S[1:, 1:] = xp.cumsum(xp.cumsum(x.astype(np.float64), axis=0), axis=1)
|
||||||
|
return S
|
||||||
|
|
||||||
|
|
||||||
|
def _box_std_from_integral(Sx, Sx2, size):
|
||||||
|
"""Écart-type local sur fenêtre size×size via sommes intégrales.
|
||||||
|
|
||||||
|
Coût indépendant de la taille de fenêtre (4 accès par pixel) — contre un
|
||||||
|
uniform_filter dont le coût croît avec la fenêtre (75 px à 0,2 m pour
|
||||||
|
l'échelle large). Aux bords, la fenêtre est tronquée et normalisée par le
|
||||||
|
nombre réel d'éléments (les dalles se recouvrent, le bord est sans effet
|
||||||
|
visuel).
|
||||||
|
"""
|
||||||
|
rows = Sx.shape[0] - 1
|
||||||
|
cols = Sx.shape[1] - 1
|
||||||
|
r = size // 2
|
||||||
|
iy0 = xp.maximum(xp.arange(rows) - r, 0)
|
||||||
|
iy1 = xp.minimum(xp.arange(rows) + r + 1, rows)
|
||||||
|
ix0 = xp.maximum(xp.arange(cols) - r, 0)
|
||||||
|
ix1 = xp.minimum(xp.arange(cols) + r + 1, cols)
|
||||||
|
# Nombre d'éléments réels de la fenêtre (tronquée aux bords)
|
||||||
|
counts = ((iy1 - iy0)[:, None] * (ix1 - ix0)[None, :]).astype(np.float64)
|
||||||
|
|
||||||
|
def box_sum(S):
|
||||||
|
return (S[iy1][:, ix1] - S[iy0][:, ix1]
|
||||||
|
- S[iy1][:, ix0] + S[iy0][:, ix0])
|
||||||
|
|
||||||
|
mean = box_sum(Sx) / counts
|
||||||
|
mean_sq = box_sum(Sx2) / counts
|
||||||
|
return xp.sqrt(xp.maximum(mean_sq - mean * mean, 0))
|
||||||
|
|
||||||
|
|
||||||
def generate_roughness(dem_file, basename, vis_dir, resolution, shared=None):
|
def generate_roughness(dem_file, basename, vis_dir, resolution, shared=None):
|
||||||
"""Surface roughness - multi-scale standard deviation (GPU-accelerated).
|
"""Surface roughness - multi-scale standard deviation (GPU-accelerated).
|
||||||
|
|
||||||
Combines fine (3m) and broad (15m) roughness for better detection
|
Combines fine (3m) and broad (15m) roughness for better detection
|
||||||
of archaeological features at multiple scales.
|
of archaeological features at multiple scales. Les écarts-types locaux
|
||||||
|
sont calculés par sommes intégrales : deux cumsum partagés entre les
|
||||||
|
deux échelles, extraction par 4 coins — coût constant quelle que soit
|
||||||
|
la fenêtre (un uniform_filter coûte proportionnellement à sa taille,
|
||||||
|
75 px à 0,2 m pour l'échelle large).
|
||||||
"""
|
"""
|
||||||
gpu_tag = " [GPU]" if _gpu_mod.HAS_GPU else ""
|
gpu_tag = " [GPU]" if _gpu_mod.HAS_GPU else ""
|
||||||
logger.info(f" → Rugosité de surface{gpu_tag}...")
|
logger.info(f" → Rugosité de surface{gpu_tag}...")
|
||||||
@ -860,38 +904,33 @@ def generate_roughness(dem_file, basename, vis_dir, resolution, shared=None):
|
|||||||
crs = shared.crs
|
crs = shared.crs
|
||||||
dem_np = shared.dem_np
|
dem_np = shared.dem_np
|
||||||
nan_mask = shared.nan_mask
|
nan_mask = shared.nan_mask
|
||||||
|
if _gpu_mod.HAS_GPU:
|
||||||
|
filled = shared.filled_gpu
|
||||||
|
else:
|
||||||
|
filled = shared.filled
|
||||||
else:
|
else:
|
||||||
dem_np, transform, crs = _read_dem(dem_file)
|
dem_np, transform, crs = _read_dem(dem_file)
|
||||||
nan_mask = np.isnan(dem_np)
|
nan_mask = np.isnan(dem_np)
|
||||||
|
filled, _ = _fill_nans(dem_np)
|
||||||
|
if _gpu_mod.HAS_GPU:
|
||||||
|
filled = to_gpu(filled)
|
||||||
|
|
||||||
|
# Sommes intégrales partagées par les deux échelles (X et X²)
|
||||||
|
Sx = _integral_sums(filled)
|
||||||
|
Sx2 = _integral_sums(filled.astype(np.float64) ** 2)
|
||||||
|
|
||||||
# Fine roughness (3m window)
|
|
||||||
fine_size = max(3, int(3 / resolution))
|
fine_size = max(3, int(3 / resolution))
|
||||||
if fine_size % 2 == 0:
|
if fine_size % 2 == 0:
|
||||||
fine_size += 1
|
fine_size += 1
|
||||||
|
|
||||||
if shared:
|
|
||||||
fine_mean = _filter_nanaware_from_filled(shared, xp_uniform_filter, size=fine_size)
|
|
||||||
fine_mean_sq = _filter_nanaware(shared.filled.astype(np.float64)**2, xp_uniform_filter, size=fine_size)
|
|
||||||
fine_mean_sq[shared.nan_mask] = np.nan
|
|
||||||
else:
|
|
||||||
fine_mean = _filter_nanaware(dem_np.astype(np.float64), xp_uniform_filter, size=fine_size)
|
|
||||||
fine_mean_sq = _filter_nanaware(dem_np.astype(np.float64)**2, xp_uniform_filter, size=fine_size)
|
|
||||||
roughness_fine = np.sqrt(np.maximum(fine_mean_sq - fine_mean * fine_mean, 0))
|
|
||||||
roughness_fine[nan_mask] = np.nan
|
|
||||||
|
|
||||||
# Broad roughness (15m window)
|
|
||||||
broad_size = max(3, int(15 / resolution))
|
broad_size = max(3, int(15 / resolution))
|
||||||
if broad_size % 2 == 0:
|
if broad_size % 2 == 0:
|
||||||
broad_size += 1
|
broad_size += 1
|
||||||
|
|
||||||
if shared:
|
roughness_fine = to_cpu(_box_std_from_integral(Sx, Sx2, fine_size))
|
||||||
broad_mean = _filter_nanaware_from_filled(shared, xp_uniform_filter, size=broad_size)
|
roughness_broad = to_cpu(_box_std_from_integral(Sx, Sx2, broad_size))
|
||||||
broad_mean_sq = _filter_nanaware(shared.filled.astype(np.float64)**2, xp_uniform_filter, size=broad_size)
|
del Sx, Sx2
|
||||||
broad_mean_sq[shared.nan_mask] = np.nan
|
gpu_cleanup()
|
||||||
else:
|
roughness_fine[nan_mask] = np.nan
|
||||||
broad_mean = _filter_nanaware(dem_np.astype(np.float64), xp_uniform_filter, size=broad_size)
|
|
||||||
broad_mean_sq = _filter_nanaware(dem_np.astype(np.float64)**2, xp_uniform_filter, size=broad_size)
|
|
||||||
roughness_broad = np.sqrt(np.maximum(broad_mean_sq - broad_mean * broad_mean, 0))
|
|
||||||
roughness_broad[nan_mask] = np.nan
|
roughness_broad[nan_mask] = np.nan
|
||||||
|
|
||||||
# Std normalization per scale then weighted combination
|
# Std normalization per scale then weighted combination
|
||||||
|
|||||||
Reference in New Issue
Block a user