Suppression éclairage solaire, GPU accéléré, --file multi, tests unitaires
- Suppression de generate_solar (éclairage solaire) des visualisations - Accélération GPU de hillshade, slope, aspect, curvature, depressions, anomalies, roughness, texture GLCM, flow (sink filling) - Nettoyage mémoire GPU entre visualisations (gpu_cleanup) - Correction OOM texture GLCM: calcul entropie bin par bin au lieu d'un tableau 3D massif sur GPU - Correction bug: xp_minimum_filter manquant dans imports visualizations - Option --file accepte plusieurs noms complets sans extension - run.sh affiche l'aide si appelé sans arguments - Option --test pour exécuter les tests unitaires dans Docker - Filtre ReturnNumber>=1 intégré dans le pipeline PDAL (plus d'erreur SMRF) - 60 tests unitaires: GPU, visualisations, rendering, DTM, pipeline, CLI - Ajout pytest au Dockerfile Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
98
lidar_pipeline/tests/test_rendering.py
Normal file
98
lidar_pipeline/tests/test_rendering.py
Normal file
@ -0,0 +1,98 @@
|
||||
"""Tests for rendering module (colormaps, tif_to_png)."""
|
||||
|
||||
import numpy as np
|
||||
import rasterio
|
||||
from rasterio.transform import from_bounds
|
||||
import pytest
|
||||
from pathlib import Path
|
||||
|
||||
|
||||
def _make_test_tif(tmp_path, data=None, size=50):
|
||||
"""Create a small test GeoTIFF and return its path."""
|
||||
if data is None:
|
||||
rng = np.random.default_rng(42)
|
||||
data = rng.normal(0, 1, (size, size)).astype(np.float32)
|
||||
|
||||
transform = from_bounds(660000, 6700000, 661000, 6701000, size, size)
|
||||
tif_file = tmp_path / "test_vis.tif"
|
||||
with rasterio.open(
|
||||
tif_file, 'w', driver='GTiff', height=size, width=size,
|
||||
count=1, dtype='float32', crs='EPSG:2154', transform=transform,
|
||||
compress='lzw'
|
||||
) as dst:
|
||||
dst.write(data, 1)
|
||||
return tif_file
|
||||
|
||||
|
||||
class TestColormaps:
|
||||
def test_colormaps_dict_exists(self):
|
||||
from lidar_pipeline.rendering import COLORMAPS
|
||||
assert isinstance(COLORMAPS, dict)
|
||||
|
||||
def test_all_viz_steps_have_colormaps(self):
|
||||
"""Every VIZ_STEPS entry should have a corresponding COLORMAPS entry or render correctly."""
|
||||
from lidar_pipeline.pipeline import VIZ_STEPS
|
||||
from lidar_pipeline.rendering import COLORMAPS
|
||||
# Some viz names differ from colormap keys
|
||||
name_map = {
|
||||
'pos_open': 'positive_openness',
|
||||
'neg_open': 'negative_openness',
|
||||
}
|
||||
# IGN overlays (ortho, topo) are RGB images — no colormap needed
|
||||
skip = {'ortho', 'topo'}
|
||||
for name, _ in VIZ_STEPS:
|
||||
if name in skip:
|
||||
continue
|
||||
cmap_key = name_map.get(name, name)
|
||||
assert cmap_key in COLORMAPS, f"Missing colormap for: {name} (looked as {cmap_key})"
|
||||
|
||||
def test_colormap_has_required_keys(self):
|
||||
"""Each colormap entry must have cmap, title, legend, description."""
|
||||
from lidar_pipeline.rendering import COLORMAPS
|
||||
required = {'cmap', 'title', 'legend', 'description'}
|
||||
for name, entry in COLORMAPS.items():
|
||||
missing = required - set(entry.keys())
|
||||
assert not missing, f"Colormap '{name}' missing keys: {missing}"
|
||||
|
||||
|
||||
class TestTifToPng:
|
||||
def test_converts_tif_to_webp(self, tmp_path):
|
||||
from lidar_pipeline.rendering import tif_to_png
|
||||
tif_file = _make_test_tif(tmp_path)
|
||||
result = tif_to_png(tif_file, tmp_path, 5.0)
|
||||
assert result is not None
|
||||
assert result.exists()
|
||||
assert result.suffix == '.webp'
|
||||
|
||||
def test_removes_source_tif(self, tmp_path):
|
||||
from lidar_pipeline.rendering import tif_to_png
|
||||
tif_file = _make_test_tif(tmp_path)
|
||||
assert tif_file.exists()
|
||||
tif_to_png(tif_file, tmp_path, 5.0)
|
||||
assert not tif_file.exists(), "Source TIF should be deleted after conversion"
|
||||
|
||||
def test_webp_has_content(self, tmp_path):
|
||||
from lidar_pipeline.rendering import tif_to_png
|
||||
tif_file = _make_test_tif(tmp_path)
|
||||
result = tif_to_png(tif_file, tmp_path, 5.0)
|
||||
assert result.stat().st_size > 1000 # Must be a real image
|
||||
|
||||
|
||||
class TestApplyColormap:
|
||||
def test_symmetric_mode(self, tmp_path):
|
||||
from lidar_pipeline.rendering import COLORMAPS, tif_to_png
|
||||
# LRM uses symmetric mode
|
||||
data = np.random.default_rng(42).normal(0, 0.5, (50, 50)).astype(np.float32)
|
||||
tif_file = _make_test_tif(tmp_path, data)
|
||||
result = tif_to_png(tif_file, tmp_path, 5.0)
|
||||
assert result is not None
|
||||
assert result.exists()
|
||||
|
||||
def test_percentile_mode(self, tmp_path):
|
||||
from lidar_pipeline.rendering import tif_to_png
|
||||
# Most visualizations use percentile mode
|
||||
data = np.random.default_rng(42).normal(50, 10, (50, 50)).astype(np.float32)
|
||||
tif_file = _make_test_tif(tmp_path, data)
|
||||
result = tif_to_png(tif_file, tmp_path, 5.0)
|
||||
assert result is not None
|
||||
assert result.exists()
|
||||
Reference in New Issue
Block a user