Rewrite README and docs in English for GitHub, add MIT license, remove internal files

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Antoine Jacquin
2026-09-27 22:31:54 +02:00
parent cef070ad77
commit d3704d6714
11 changed files with 810 additions and 923 deletions

512
README.md
View File

@ -1,318 +1,256 @@
# Pipeline LiDAR Archéologique
<div align="center">
Workflow automatisé pour générer des visualisations exploitables à partir de données LiDAR HD (IGN) pour la détection de structures archéologiques. Tourne en Docker avec accélération GPU optionnelle (NVIDIA/CuPy).
# lidar_rendu
## Visualisations (18 par fichier)
**See through the forest.** Turn France's open LiDAR HD point clouds into seamless,
archaeology-grade relief maps — served as a fast slippy map, reusable XYZ tiles
and print-ready PDF field sheets.
### Relief orienté (couche par défaut)
Une seule image fusionne le micro-relief et l'orientation des pentes : la **clarté** porte le relief local (openness positive sur MNT détendancé, rayons 5–20 m, plus un léger ombrage), la **teinte** porte l'orientation (aspect, cercle CIELAB à clarté constante : aucune couleur ne crée de faux relief). Échelle fixe : les dalles voisines se raccordent sans couture.
[![License: MIT](https://img.shields.io/badge/license-MIT-2ea44f.svg)](LICENSE)
![Docker](https://img.shields.io/badge/run%20with-Docker%20Compose-2496ED?logo=docker&logoColor=white)
![Python](https://img.shields.io/badge/python-3.10+-3776AB?logo=python&logoColor=white)
![GPU](https://img.shields.io/badge/GPU-optional%20(CUDA%20%2F%20CuPy)-76B900?logo=nvidia&logoColor=white)
![Data](https://img.shields.io/badge/data-IGN%20LiDAR%20HD-0b6fb7)
![Tiles](https://img.shields.io/badge/tiles-XYZ%20%C2%B7%20TileJSON%20%C2%B7%20WMTS-7952b3)
C'est la **seule couche produite et affichée par défaut** (`PANEL_VIZ` dans `index.py`) : un traitement sans `--only`, la génération lancée depuis la carte et la carte elle-même (panneau, tuiles XYZ, TileJSON, WMTS) se limitent au relief orienté. Les visualisations ci-dessous restent calculables explicitement (`--only slope aspect ...`).
<img src="docs/img/neuf-brisach.png" alt="Neuf-Brisach fortress in oriented relief" width="900">
### Visualisations principales
| # | Visualisation | Utilité archéologique |
|---|--------------|----------------------|
| 1 | **Hillshade multidirectionnel** | Murs, terrasses, structures linéaires, routes |
| 2 | **Pente (Slope)** | Murs de soutènement, talus, changements brusques |
| 3 | **Aspect (Orientation)** | Direction des pentes, exposition |
| 4 | **Courbure (Curvature)** | Fossés, terrasses, talus, concavité/convexité |
| 5 | **Sky-View Factor** | Structures, tumulus, fondations (ray-tracing 16 azimuts) |
| 6 | **Local Relief Model** | Micro-reliefs, fossés, levées de terrain |
| 7 | **Positive Openness** | Élévations, tumulus, bâtiments (ray-tracing 8 directions) |
| 8 | **Negative Openness** | Cavités, fossés, souterrains (ray-tracing 8 directions) |
<sub>Neuf-Brisach (Haut-Rhin), Vauban's star fortress (UNESCO World Heritage), rendered from one 1 km LiDAR HD tile.
Every bastion, moat and ravelin reads at a glance; buildings are black (no ground points).</sub>
### Visualisations avancées
| # | Visualisation | Description | Détection |
|---|--------------|-------------|-----------|
| 9 | **MSRM** | Multi-Scale Relief Model (sigma 5/10/25/50/100m) | Tumulus, fossés, murs à toutes les échelles |
| 10 | **TPI multi-échelle** | Topographic Position Index (5m + 100m) | Crêtes, vallées, plateformes |
| 11 | **Dépressions** | Remplissage cuvettes + différence | Dolines, sinkholes, zones inondables |
| 12 | **SAILORE** | LRM adaptatif (noyau = f(pente)) | Terrain hétérogène, tout relief |
| 13 | **Rugosité** | Écart-type de l'élévation | Surfaces anthropiques vs naturelles |
| 14 | **Anomalies statistiques** | Z-score + Local Moran's I | Anomalies topographiques significatives |
| 15 | **Ondelette Mexican Hat** | CWT 2D multi-échelle | Tumulus, fossés circulaires |
| 16 | **Accumulation de flux** | Algorithme D8 hydrologique | Fossés d'enceinte, routes antiques |
</div>
### Cartes de référence IGN
| # | Visualisation | Source |
|---|--------------|--------|
| 17 | **Photographie aérienne IGN** | Orthophotographie WMTS |
| 18 | **Carte topographique IGN** | Plan IGN V2 WMTS |
---
## Classification du sol
## Why
Le pipeline classifie automatiquement les points sol à partir du nuage de points bruit. Le pré-traitement suit le workflow recommandé par PDAL :
The French mapping agency (IGN) publishes **LiDAR HD**: a nationwide airborne laser
scan, ~10 points/m², free under the *Licence Ouverte 2.0*. Hidden under forest
canopy and fields are hollow ways, trenches, burial mounds, field systems and
forgotten walls — but raw point clouds are hard to read, and naïve DEM renderings
show seams at every tile edge, stripes from the flight lines and colour blotches
that look like relief but aren't.
1. **Filtre ReturnNumber** — élimine les points avec numéros de retour invalides
2. **Réinitialisation Classification** — remet toutes les classifications à 0
3. **ELM** (Extended Local Minimum) — marque les points bas aberrants comme bruit (Classification=7)
4. **Outlier statistique** — supprime les points isolés (mean_k=8, multiplier=3.0)
5. **Classification sol** — SMRF, PMF ou CSF
6. **Extraction** — ne conserve que les points Classification=2
`lidar_rendu` is an end-to-end pipeline that solves exactly that:
### Méthodes de classification
- **One command** from an IGN tile ID to a browsable map.
- **One carefully designed layer** — *oriented relief* — instead of fifteen
colormaps to flip through.
- **Seamless at any scale**: fixed scales, no per-tile statistics, 100 m
overlap borrowed from the eight neighbouring tiles.
- **Measurement-aware**: a companion *precision* layer shows where the terrain is
measured and where it is interpolated, so you never mistake a gap for a feature.
| Méthode | Mode | Usage | Vitesse |
|---------|------|-------|---------|
| **SMRF** | Auto (défaut) | Terrain naturel, forêt, rocaille | Rapide |
| **PMF** | Auto (si urbain) | Zones urbaines, bâtiments, routes | Rapide |
| **CSF** | Manuel uniquement | Terrain très complexe, falaises | Lent |
## Gallery
L'auto-détection analyse le ratio de retours uniques du nuage de points : ratio > 0.6 = milieu urbain → PMF, sinon → SMRF.
<table>
<tr>
<td width="50%"><img src="docs/img/hartmannswillerkopf.png" alt="Hartmannswillerkopf battlefield"></td>
<td width="50%"><img src="docs/img/neuf-brisach-a4.png" alt="A4 PDF field sheet of Neuf-Brisach"></td>
</tr>
<tr>
<td><b>Hartmannswillerkopf</b> (tile 1011_6760) — the WWI battlefield of 1915 under dense
forest: roads, trench lines and shell craters appear on the slope.</td>
<td><b>PDF field sheet</b> exported from the map (A4 landscape, 1:5,000): Lambert 93 grid,
true-north arrow, scale bar, legend and data-quality inset.</td>
</tr>
</table>
### Pré-traitement ELM (terrain calcaire)
## Highlights
Les paramètres ELM sont adaptés au terrain calcaire rocailleux avec végétation basse :
- `cell=5.0m` — résolution fine pour capturer le relief rocheux
- `threshold=2.0m` — seuil élevé pour ne pas marquer les affleurements comme bruit
| | |
|---|---|
| **Oriented relief** | A single RGB image: CIELAB *lightness* carries local openness (bumps light, hollows dark), *hue* carries slope orientation at constant lightness — so colour never fakes relief. |
| **Flight-strip calibration** | Each tile mixes several passes, sometimes offset by a few cm. The pipeline estimates per-strip offsets, per-scan-line shift and roll, and a per-beam cross-track profile, then removes them before rasterising. No more stripes. |
| **Honest gap filling** | At 0.2 m, ~80 % of pixels contain no point. Gaps are closed morphologically within the point envelope, with a radius that follows local point spacing — nothing is extrapolated outward. |
| **Seamless tiles** | DTMs are built on the nominal 1 km tile plus a 100 m buffer taken from the neighbours (downloaded automatically), then cropped back exactly. |
| **Fast** | GPU (CuPy) when available, numba otherwise. Ground extraction straight from IGN classes with laspy (~5 s per tile), relief kernel ~5 s on a 12-core CPU, AVIF encoding in 0.6 s. |
| **Slippy map** | Built-in Leaflet UI: tile selection with IGN metadata, relief / precision / side-by-side compare, adjustable intensity, light & dark themes, mobile bottom sheet, shareable links. |
| **Standard tiles** | `/tiles/{layer}/{z}/{x}/{y}.png` in the OpenStreetMap scheme, plus TileJSON, WMTS and a JOSM imagery file — drop it into QGIS, JOSM, iD, uMap or MapLibre. |
| **Generate from the map** | Draw a rectangle: the tiles are downloaded from IGN, processed, and appear on the map row by row as they finish. |
| **Print** | Vector PDF sheets (A4/A3, 1:1,000 – 1:10,000) with grid, WGS84 corners, meridian convergence, legend and point-density inset. |
| **Runs on a Raspberry Pi** | A lightweight image (no PDAL, no GPU) serves the map and delegates generation to a worker machine; it keeps working offline. |
## Architecture modulaire
## Quick start
Requirements: Docker with Compose ≥ 2.24. An NVIDIA GPU is optional.
```bash
git clone https://github.com/<you>/lidar_rendu.git && cd lidar_rendu
./start.sh # build the image, start the map on http://localhost:8973/
./start.sh process --fetch-tiles 1037,6779 # download one IGN tile (Neuf-Brisach) and process it
./start.sh logs # follow the server log
./start.sh stop # stop everything
```
`start.sh` creates `input/` and `output/` owned by you and checks whether Docker
can reach a GPU; if not, it adds `docker-compose.cpu.yml` and everything runs on
the CPU (slower, same output).
Tile IDs are the IGN grid coordinates in km (Lambert 93): `1037,6779` is the tile
whose top-left corner is at X = 1,037 km, Y = 6,779 km. You can also skip the
command line entirely: open the map, go to the **Génération** tab and draw a
zone.
> The user interface and log messages are in French; code identifiers are in English.
## How it works
```mermaid
flowchart LR
A[IGN LiDAR HD<br/>COPC .laz tile] --> B[Ground points<br/>IGN classes, laspy]
N[8 neighbour tiles<br/>100 m buffer] --> B
B --> C[Strip & scan-line<br/>calibration]
C --> D[DTM 0.2 m<br/>+ bounded gap fill]
D --> E[Oriented relief<br/>+ precision layer]
E --> F[AVIF tiles<br/>cropped to 1 km]
F --> G[Inventory +<br/>XYZ pyramid]
G --> H[Map · XYZ/WMTS · PDF]
```
1. **Download** — the COPC tile and, if missing, its eight neighbours from the IGN
Géoplateforme. Tiles are processed north to south, so the map fills top down.
2. **Ground** — IGN's ground class (2) extracted directly with laspy; PDAL (SMRF /
CSF) remains available. See [ground classification](docs/GROUND_CLASSIFICATION.md).
3. **Calibration** — robust per-strip vertical offsets, then a joint least-squares
adjustment of every scan line (offset + roll) against the other strips, plus a
per-beam angular profile. Results are written to a JSON sidecar next to the DTM.
4. **DTM** — rasterised at 0.2 m on the tile + buffer; small gaps closed within the
point envelope; ground density saved alongside.
5. **Render** — oriented relief (openness at 5 / 10 / 20 m on a detrended DTM,
16 directions, plus 35 % hillshade) and precision (16 log-scale density levels).
Fixed scales everywhere: identical terrain gives identical colour on every tile.
6. **Publish** — AVIF quadrants encoded once from the source raster, inventory
refreshed after every tile, XYZ pyramid pre-generated in the background.
## The map
- **Click a tile** to select it: extent, IGN acquisition date, sensors, point count,
download link, and the calibration applied to each flight strip.
- **Three view modes** — relief, precision, or *compare* with a draggable split bar.
- **Relief intensity** slider (0.5×–2×), remembered per browser and carried in the link.
- **Share** — the URL encodes position, mode, comparison and intensity.
- **Use in JOSM / QGIS** button — copies the tile URL for the current layer.
- Keyboard: `1`–`5` tabs, `P` next view mode, `Esc` deselect / collapse.
- Works on phones: the panel becomes a three-height bottom sheet; GPS centring over HTTPS.
Full reference: [docs/MAPS.md](docs/MAPS.md).
## Use the tiles anywhere
| Client | URL |
|---|---|
| QGIS, iD, uMap, Leaflet, MapLibre | `http://<host>:8973/tiles/relief_oriente/{z}/{x}/{y}.png` |
| JOSM (TMS) | `http://<host>:8973/tiles/relief_oriente/{zoom}/{x}/{y}.png` |
| JOSM (all layers at once) | `http://<host>:8973/tiles/josm.imagery.xml` |
| TileJSON 3.0 | `http://<host>:8973/tiles/relief_oriente.json` |
| WMTS 1.0 | `http://<host>:8973/tiles/wmts.xml` |
256 px PNG, EPSG:3857, transparent outside coverage, CORS enabled, zoom 5–19
(19 ≈ 0.2 m/px, one screen pixel per LiDAR pixel).
## Command line
The processing pipeline is `python3 -m lidar_pipeline`; with Docker Compose,
`./start.sh process [options]` runs it on `input/` → `output/`.
| Option | Default | Purpose |
|---|---|---|
| `--fetch-tiles COL,ROW …` | — | Download these LiDAR HD tiles from IGN first |
| `--file NAME …` | all of `input/` | Process only these tiles (name without `.laz`) |
| `-r RES` | `0.2` | Resolution in m/px (comma-separated for several) |
| `-g [GPU]` | CPU | GPU(s): `-g`, `-g 0`, `-g 0,2`, `-g all` |
| `-w N` | `auto` | Parallel workers (bounded by free VRAM per GPU) |
| `--only VIZ …` / `--skip VIZ …` | map layers | Choose visualisations |
| `--edge-buffer M` | `100` | Buffer borrowed from neighbour tiles (0 = off) |
| `--ground-classification` | `ign` | `ign`, `auto`, `smrf`, `csf` |
| `--no-strip-align` | on | Disable flight-strip calibration |
| `--format`, `--quality` | `avif`, `60` | Output encoding |
| `-f`, `--force` | off | Regenerate even if outputs exist |
| `--rebuild-index` | — | Rebuild the tile inventory only |
| `-v`, `--debug` | — | Verbose / debug logging |
By default only the map layers are produced (`relief_oriente`, `densite_sol`).
Classic visualisations remain one flag away, e.g.
`./start.sh process --only hillshade slope svf pos_open`:
| Key | Visualisation | | Key | Visualisation |
|---|---|---|---|---|
| `hillshade` | Multi-directional hillshade | | `svf` | Sky-View Factor |
| `slope` | Slope | | `roughness` | Roughness |
| `aspect` | Aspect | | `wavelet` | Mexican-hat wavelet (multi-scale) |
| `mslrm` | Multi-scale relief model | | `flow_acc` | D8 flow accumulation |
| `sailore` | Slope-adaptive local relief | | `solar` | Solar illumination |
| `pos_open` / `neg_open` | Positive / negative openness | | `anomaly` | Statistical anomalies |
| `ortho` / `topo` | IGN orthophoto / topographic map | | | |
## Deployment
| Setup | Command | Port |
|---|---|---|
| All-in-one (map + generation) | `docker compose up -d --build serve` (or `./start.sh`) | 8973 |
| Processing worker for remote maps | `docker compose -f docker-compose.worker.yml up -d --build` | 8973 |
| Lightweight map (Raspberry Pi, no PDAL/GPU) | `docker compose -f docker-compose.maps.yml up -d --build` | 8975 |
The lightweight map mirrors the worker's tiles, delegates generation to it, and
keeps serving from disk when the worker is off. Tokens (`LIDAR_API_TOKEN`) and a
network allow-list (`LIDAR_REGEN_CIDR`, private ranges by default) protect the
generation API. Step-by-step guide: [docs/DEPLOY_WEBAPP.md](docs/DEPLOY_WEBAPP.md).
Always pass `--build`: the code is baked into the image, and the build is
near-instant thanks to the layer cache.
## Project layout
```
lidar_pipeline/
├── __init__.py # Exports publics
├── __main__.py # Point d'entrée: python -m lidar_pipeline
├── cli.py # argparse + logging + main()
├── gpu.py # CuPy/numpy abstraction (HAS_GPU, to_gpu, to_cpu, xp_*)
├── dtm.py # Classification PDAL (SMRF/PMF/CSF + auto) + génération DTM
├── visualizations.py # Fonctions generate_* (19 visualisations)
├── ign.py # Téléchargement tuiles IGN + overlay
├── rendering.py # Colormaps, tif_to_png, rapport PDF
├── pipeline.py # LidarArchaeoPipeline (orchestration + registry)
└── tests/ # Tests unitaires (pytest)
├── cli.py command line
├── pipeline.py orchestration, worker pool, VRAM-aware GPU slots
├── fetch_ign.py IGN catalogue and downloads
├── dtm.py ground extraction, strip calibration, DTM, gap filling
├── visualizations.py generate_* functions (relief, openness, SVF, …)
├── rendering.py colormaps, GeoTIFF → AVIF, 1 km crop
├── index.py catalogue, thumbnails, sub-tiles, inventory, layer registry
├── quality.py per-tile quality sidecar (density, acquisition dates)
├── tiles.py XYZ pyramid: reprojection, cache, background maintenance
├── mapserve.py FastAPI server: map, tiles, TileJSON/WMTS, generation API
├── export_pdf.py PDF field sheets (Pillow + pyproj + reportlab)
├── gpu.py CuPy / NumPy abstraction with CPU fallback
├── web/ map UI (HTML, CSS, JS)
└── tests/ ~400 pytest tests
```
Ajouter une visualisation = 1 fonction + 1 entrée dans `VIZ_STEPS` + 1 entrée dans `COLORMAPS`.
## Exemples
Relief orienté sur deux dalles LiDAR HD, rendues par ce pipeline (`./start.sh process --fetch-tiles 1037,6779 1011,6760 ...`).
**Neuf-Brisach** (dalle 1037_6779) — l'enceinte bastionnée de Vauban, ses fossés et ses demi-lunes ; en noir, les bâtiments (aucun point sol).
![Neuf-Brisach, relief orienté](docs/img/neuf-brisach.png)
**Hartmannswillerkopf** (dalle 1011_6760) — versant forestier du champ de bataille de 1915 : chemins, tranchées et trous d'obus sous le couvert.
![Hartmannswillerkopf, relief orienté](docs/img/hartmannswillerkopf.png)
**Planche PDF** exportée depuis la carte (onglet PDF, A4 paysage, 1:5 000) : quadrillage Lambert 93, légende, encart qualité des données.
![Planche PDF de Neuf-Brisach](docs/img/neuf-brisach-a4.png)
## Démarrage rapide
## Development
```bash
git clone <ce dépôt> && cd lidar_rendu
./start.sh # construit l'image, démarre la carte sur http://localhost:8973/
./start.sh process --fetch-tiles 1037,6779 --file LHD_FXX_1037_6779_PTS_LAMB93_IGN69.copc.laz
# télécharge une dalle IGN et la traite
./start.sh logs # journal du serveur
./start.sh stop # arrêt
./run.sh --test # rebuild the image and run the full test suite
docker run --rm lidar-lidar python3 -m pytest -v --pyargs lidar_pipeline.tests.test_tiles
```
`start.sh` crée `input/` et `output/` à votre nom et détecte le GPU : sans GPU
NVIDIA utilisable par Docker, il ajoute `docker-compose.cpu.yml` et le
traitement tourne sur le CPU (plus lent). Docker Compose ≥ 2.24 requis.
Adding a visualisation takes four edits: a `generate_X()` in `visualizations.py`,
an entry in `VIZ_STEPS` (`pipeline.py`), a colormap in `rendering.py`, and a legend
in `VIZ_LEGENDS` (`index.py`). Design decisions and conventions are documented in
[AGENTS.md](AGENTS.md) (French).
## Installation Docker
Contributions are welcome: open an issue or a pull request.
```bash
cd /votre/dossier/lidar
mkdir -p input
## Documentation
# Copiez vos fichiers .laz dans input/
cp /chemin/vos/fichiers/*.laz input/
- [docs/MAPS.md](docs/MAPS.md) — map UI, tile contract, cache, PDF export, environment variables
- [docs/DEPLOY_WEBAPP.md](docs/DEPLOY_WEBAPP.md) — two-machine deployment (Raspberry Pi + worker)
- [docs/GROUND_CLASSIFICATION.md](docs/GROUND_CLASSIFICATION.md) — ground filters benchmark and literature
# Build l'image Docker
docker build -t lidar-lidar .
```
## Data and licences
## Utilisation
- **Code**: [MIT](LICENSE).
- **LiDAR HD, orthophotos, maps**: © IGN, [Licence Ouverte 2.0](https://www.etalab.gouv.fr/licence-ouverte-open-licence/).
Attribution is required and is shown on every map, tile endpoint and PDF.
- **Base map**: © [OpenStreetMap](https://www.openstreetmap.org/copyright) contributors.
Before using these renderings as a tracing source in OpenStreetMap, check the
community's position on the source.
### Traitement complet avec GPU (recommandé)
```bash
./run.sh -g
```
### Traitement standard (CPU seul)
```bash
./run.sh
```
### Options du script run.sh
```
./run.sh [options]
-r RESOLUTION Résolution en m/px (défaut: 0.5)
-w WORKERS Nombre de workers parallèles (défaut: 1)
-g Activer l'accélération GPU NVIDIA
-v Mode verbeux (timestamps + niveaux)
--debug Mode debug (détails internes fichier:ligne)
-f / --force Régénérer tous les fichiers même si les WebP existent
--force-classification Reclassifier le sol même si le fichier .las existe déjà
--ground-classification Méthode de classification: ign, auto, smrf, csf (défaut: ign — imposé par la carte)
--edge-buffer M Raccord des bords avec les dalles voisines (défaut: 100 m — imposé par la carte)
--file NOM... Traiter un ou plusieurs fichiers LAZ spécifiques
--test Exécuter les tests unitaires
-h Afficher l'aide
```
### Exemples
```bash
# Traitement standard avec GPU
./run.sh -g
# GPU + mode verbeux
./run.sh -g -v
# GPU + 4 workers parallèles
./run.sh -g -w 4
# Haute résolution (0.2m/px)
./run.sh -g -r 0.2
# Forcer la régénération de tous les fichiers
./run.sh -g --force
# Reclassifier le sol seulement (sans régénérer les visualisations)
./run.sh -g --force-classification
# Forcer la classification PMF au lieu de l'auto-détection
./run.sh -g --ground-classification pmf
# Forcer la classification CSF (lent mais robuste sur terrain complexe)
./run.sh -g --ground-classification csf
# Traiter un fichier spécifique (test rapide)
./run.sh -g --file LHD_FXX_1000_6882_PTS_LAMB93_IGN69.copc
# Traiter deux fichiers spécifiques
./run.sh -g --file LHD_FXX_1000_6881_PTS_LAMB93_IGN69.copc LHD_FXX_1000_6882_PTS_LAMB93_IGN69.copc
# Exécuter les tests unitaires
./run.sh --test
```
### Utilisation directe Docker
```bash
# Traitement standard
docker run --rm -v $(pwd)/input:/data/input:ro -v $(pwd)/output:/data/output lidar-lidar
# Avec GPU + classification forcée
docker run --rm --gpus all -v $(pwd)/input:/data/input:ro -v $(pwd)/output:/data/output \
lidar-lidar python3 -m lidar_pipeline /data/input -o /data/output \
--ground-classification pmf
# Forcer la reclassification du sol
docker run --rm --gpus all -v $(pwd)/input:/data/input:ro -v $(pwd)/output:/data/output \
lidar-lidar python3 -m lidar_pipeline /data/input -o /data/output \
--force-classification
# Mode verbeux
docker run --rm --gpus all -v $(pwd)/input:/data/input:ro -v $(pwd)/output:/data/output \
lidar-lidar python3 -m lidar_pipeline /data/input -o /data/output -v
```
## Structure des dossiers
```
.
├── input/ # Fichiers .laz (monté en read-only dans Docker)
├── output/ # Résultats générés
│ ├── DTM/ # Modèles numériques de terrain (GeoTIFF)
│ ├── temp/ # Fichiers temporaires (classification .las)
│ ├── visualisations/ # Images WebP par fichier LAZ
│ │ ├── fichier_6881/ # Un sous-dossier par fichier LAZ
│ │ │ ├── ..._hillshade_multi.webp
│ │ │ ├── ..._svf.webp
│ │ │ ├── ..._mslrm.webp
│ │ │ └── ... (19 visualisations)
│ │ └── fichier_6882/
│ │ └── ...
│ └── rapports/ # Rapports PDF A3 par fichier
│ ├── fichier_6881_rapport.pdf
│ └── fichier_6882_rapport.pdf
├── lidar_pipeline/ # Package Python modulaire
│ ├── cli.py # Arguments CLI + logging
│ ├── gpu.py # Abstraction CuPy/numpy
│ ├── dtm.py # Classification sol + DTM
│ ├── visualizations.py # 19 fonctions generate_*
│ ├── ign.py # Tuiles IGN
│ ├── rendering.py # Colormaps, WebP, PDF
│ ├── pipeline.py # Orchestration
│ └── tests/ # Tests unitaires
├── process_lidar.py # Point d'entrée compatible
├── Dockerfile
├── run.sh
└── README.md
```
## Paramètres
| Paramètre | Option | Défaut | Description |
|-----------|--------|--------|-------------|
| Résolution | `-r` | 0.5 | Résolution en mètres par pixel |
| Workers | `-w` | 1 | Nombre de CPU pour traitement parallèle |
| GPU | `-g` | off | Activer l'accélération NVIDIA GPU |
| Classification sol | `--ground-classification` | ign | Méthode : ign, auto, smrf, csf (la carte impose ign) |
| Forcer classification | `--force-classification` | off | Reclassifier le sol même si .las existe |
| Output | `-o` | /data/output | Dossier de sortie |
| Force | `-f/--force` | off | Régénérer même si les WebP existent |
| File | `--file` | tous | Traiter un ou plusieurs fichiers LAZ |
| Verbose | `-v` | off | Mode verbeux (timestamps + niveaux) |
| Debug | `--debug` | off | Mode debug (détails internes) |
### Résolution recommandée
- `0.2` — Très fine, bâtiments individuels (lent)
- `0.5` — Recommandée archéologie (équilibre vitesse/détail)
- `1.0` — Rapide, grandes structures uniquement
## Interprétation archéologique
### Pour détecter les cavités et souterrains
1. **Negative Openness** — Zones sombres = creux profonds
2. **Dépressions** — Carte spécifique des dolines et sinkholes
3. **Local Relief Model** — Zones bleues = dépressions
4. **Hillshade** — Ombres inhabituelles en forme de trous
### Pour détecter structures et bâtiments anciens
1. **MSRM** — Détection multi-échelle de tous les reliefs
2. **Sky-View Factor** — Structures géométriques claires
3. **SAILORE** — LRM adaptatif pour terrain hétérogène
4. **Anomalies statistiques** — Anomalies topographiques significatives
### Pour hydrologie et fossés
1. **Accumulation de flux** — Fossés d'enceinte, routes antiques
2. **Dépressions** — Zones de collecte d'eau, dolines
3. **Negative Openness** — Fossés et tranchées
## Tests
```bash
./run.sh --test
```
Les tests tournent dans le conteneur Docker et couvrent la classification du sol (SMRF/PMF/CSF), l'auto-détection, le rendu, et les visualisations.
## Dépannage
```bash
# Vérifier Docker
docker --version
# Shell dans le conteneur
docker run --rm -it -v $(pwd)/input:/data/input -v $(pwd)/output:/data/output \
--entrypoint bash lidar-lidar
# Reconstruire l'image
docker build --no-cache -t lidar-lidar .
# Nettoyer
docker system prune -a
```
### Erreur mémoire
Augmenter la mémoire Docker à 16Go+ pour les gros fichiers LiDAR HD.
### Données LiDAR HD (IGN)
Les fichiers COPC (.laz) de l'IGN sont supportés directement. Le pipeline détecte automatiquement la méthode de classification du sol (SMRF/PMF) en analysant le ratio de retours uniques du nuage de points.
Built on [PDAL](https://pdal.io), [laspy](https://laspy.readthedocs.io),
[rasterio](https://rasterio.readthedocs.io), [CuPy](https://cupy.dev),
[numba](https://numba.pydata.org), [FastAPI](https://fastapi.tiangolo.com),
[Leaflet](https://leafletjs.com) and [reportlab](https://www.reportlab.com).