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:
@ -44,7 +44,7 @@ from .visualizations import (
|
||||
generate_hillshade, generate_slope, generate_aspect, generate_curvature,
|
||||
generate_lrm, generate_svf, generate_openness,
|
||||
generate_mslrm, generate_tpi, generate_depressions, generate_sailore,
|
||||
generate_roughness, generate_anomalies, generate_wavelet, generate_texture,
|
||||
generate_roughness, generate_anomalies, generate_wavelet,
|
||||
generate_flow,
|
||||
)
|
||||
from .gpu import gpu_cleanup
|
||||
@ -71,7 +71,6 @@ VIZ_STEPS = [
|
||||
('roughness', generate_roughness),
|
||||
('anomalies', generate_anomalies),
|
||||
('wavelet', generate_wavelet),
|
||||
('texture', generate_texture),
|
||||
('flow', generate_flow),
|
||||
('ortho', lambda d, b, v, r: generate_ign_overlay(
|
||||
d, b, v, r,
|
||||
|
||||
@ -156,13 +156,6 @@ COLORMAPS = {
|
||||
'description': 'Transformée en ondelette 2D — excellente pour détecter structures circulaires',
|
||||
'vmin_mode': 'symmetric', 'sym_pct': (2, 98),
|
||||
},
|
||||
'texture': {
|
||||
'cmap': 'inferno',
|
||||
'title': 'Texture GLCM (Contraste + Entropie - Homogénéité)',
|
||||
'legend': 'Analyse de la texture du relief (fenêtre 5m)\nClair = Texture hétérogène (labour, ruines, sol perturbé)\nSombre = Texture homogène (sol nu, route, zone plate)\n\nCombine contraste, entropie et homogénéité',
|
||||
'description': 'Distingue surfaces anthropiques (labour, chemins) des naturelles',
|
||||
'vmin_mode': 'symmetric', 'sym_pct': (2, 98),
|
||||
},
|
||||
'flow': {
|
||||
'cmap': 'Blues',
|
||||
'title': 'Accumulation de Flux Hydrologique (D8)',
|
||||
@ -525,7 +518,7 @@ def generate_pdf_report(basename, vis_dir, pdf_dir, resolution):
|
||||
order = ['mslrm', 'svf', 'negative_openness',
|
||||
'positive_openness', 'sailore', 'depressions', 'hillshade_multi',
|
||||
'lrm', 'tpi', 'slope', 'curvature', 'aspect',
|
||||
'roughness', 'anomalies', 'wavelet', 'texture', 'flow']
|
||||
'roughness', 'anomalies', 'wavelet', 'flow']
|
||||
|
||||
def sort_key(f):
|
||||
name = f.stem.lower()
|
||||
|
||||
@ -190,14 +190,6 @@ class TestWavelet:
|
||||
assert result.exists()
|
||||
|
||||
|
||||
class TestTexture:
|
||||
def test_generates_tif(self, synthetic_dem, tmp_output_dir):
|
||||
from lidar_pipeline.visualizations import generate_texture
|
||||
result = generate_texture(synthetic_dem, "test", tmp_output_dir, 5.0)
|
||||
assert result is not None
|
||||
assert result.exists()
|
||||
|
||||
|
||||
class TestFlow:
|
||||
def test_generates_tif(self, synthetic_dem, tmp_output_dir):
|
||||
from lidar_pipeline.visualizations import generate_flow
|
||||
|
||||
@ -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