Suppression de la visualisation Texture GLCM
- Suppression de generate_texture() de visualizations.py - Suppression de l'entrée 'texture' de VIZ_STEPS et COLORMAPS - Suppression du test TestTexture - Mise à jour README (19 → 18 visualisations) - Mise à jour CLAUDE.md (17 → 16 fonctions generate_*) Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@ -676,88 +676,6 @@ def generate_wavelet(dem_file, basename, vis_dir, resolution):
|
||||
return None
|
||||
|
||||
|
||||
# ============================================================
|
||||
# Texture GLCM
|
||||
# ============================================================
|
||||
|
||||
def generate_texture(dem_file, basename, vis_dir, resolution):
|
||||
"""GLCM-inspired texture analysis — contrast, entropy, homogeneity (GPU-accelerated)."""
|
||||
gpu_tag = " [GPU]" if HAS_GPU else ""
|
||||
logger.info(f" → Texture GLCM{gpu_tag}...")
|
||||
t0 = time.time()
|
||||
output = vis_dir / f"{basename}_texture.tif"
|
||||
|
||||
try:
|
||||
dem_np, transform, crs = _read_dem(dem_file)
|
||||
|
||||
# Hillshade — compute on CPU to avoid holding DEM on GPU during texture
|
||||
gy, gx = np.gradient(dem_np, resolution)
|
||||
slope = np.arctan(np.sqrt(gx**2 + gy**2))
|
||||
alt_rad = np.radians(45)
|
||||
az_rad = np.radians(315)
|
||||
aspect = np.arctan2(gy, gx)
|
||||
shading = (np.sin(alt_rad) * np.cos(slope) +
|
||||
np.cos(alt_rad) * np.sin(slope) *
|
||||
np.cos(az_rad - aspect))
|
||||
hillshade = np.clip(shading, 0, 1)
|
||||
|
||||
valid = np.asarray(hillshade[~np.isnan(hillshade)])
|
||||
if len(valid) == 0:
|
||||
raise ValueError("No valid data for texture analysis")
|
||||
lo, hi = np.percentile(valid, (1, 99))
|
||||
img = np.clip((hillshade - lo) / max(hi - lo, 0.001), 0, 1)
|
||||
del hillshade, shading, slope, aspect, gy, gx # free memory
|
||||
|
||||
window = int(5 / resolution)
|
||||
if window % 2 == 0:
|
||||
window += 1
|
||||
|
||||
# Contrast (variance) — GPU-accelerated
|
||||
img_gpu = to_gpu(img.astype(np.float32))
|
||||
local_mean = xp_uniform_filter(img_gpu, size=window)
|
||||
local_mean_sq = xp_uniform_filter(img_gpu * img_gpu, size=window)
|
||||
contrast = to_cpu(local_mean_sq - local_mean * local_mean).astype(np.float64)
|
||||
del img_gpu, local_mean, local_mean_sq # free GPU memory
|
||||
|
||||
# Entropy — compute bin-by-bin to avoid large 3D allocation
|
||||
n_bins = 16
|
||||
img_clean = np.nan_to_num(img, nan=0.0)
|
||||
img_uint8 = np.clip(img_clean * 255, 0, 255).astype(np.uint8)
|
||||
quantized = (img_uint8 // (256 // n_bins)).astype(np.int32)
|
||||
entropy = np.zeros_like(img, dtype=np.float64)
|
||||
win_area = max(window * window, 1)
|
||||
|
||||
for b in range(n_bins):
|
||||
plane = (quantized == b).astype(np.float32)
|
||||
plane_gpu = to_gpu(plane)
|
||||
prob_plane = to_cpu(xp_uniform_filter(plane_gpu, size=window))
|
||||
prob_val = prob_plane / win_area
|
||||
prob_val = np.clip(prob_val, 1e-10, None)
|
||||
entropy -= prob_val * np.log2(prob_val)
|
||||
del plane_gpu # free GPU memory per bin
|
||||
|
||||
del quantized, img_uint8 # free CPU memory
|
||||
|
||||
# Homogeneity — 1 / (1 + variance)
|
||||
homogeneity = 1.0 / (1.0 + contrast)
|
||||
|
||||
def norm(arr):
|
||||
valid_arr = arr[~np.isnan(arr)]
|
||||
if len(valid_arr) == 0:
|
||||
return arr
|
||||
std_val = max(np.std(valid_arr), 0.01)
|
||||
return (arr - np.mean(valid_arr)) / std_val
|
||||
|
||||
texture_combined = 0.4 * norm(contrast) + 0.4 * norm(entropy) - 0.2 * norm(homogeneity)
|
||||
|
||||
_save_tif(output, texture_combined, transform, crs)
|
||||
logger.info(f" ✓ Texture terminée ({time.time()-t0:.1f}s){gpu_tag}")
|
||||
return output
|
||||
except Exception as e:
|
||||
logger.error(f" ✗ Erreur texture GLCM: {e}", exc_info=True)
|
||||
return None
|
||||
|
||||
|
||||
# ============================================================
|
||||
# Flow accumulation
|
||||
# ============================================================
|
||||
|
||||
Reference in New Issue
Block a user