- Découpage du monolithe process_lidar.py (~2750 lignes) en package lidar_pipeline/ avec 9 modules (gpu, dtm, visualizations, ign, rendering, pipeline, cli, __init__, __main__) - Logging configurable: -v (verbose avec timestamps) et --debug (détails internes fichier:ligne) - Option --force pour régénérer tous les fichiers (par défaut skip les WebP existants) - Option --file NOM pour traiter un seul fichier LAZ (tests rapides) - ProcessPoolExecutor avec répertoires temporaires uniques par worker - Suppression du code mort (geomorphons, hillshade_ne, nodata_mask) - Aucun fichier TIFF résiduel après conversion WebP - setup.py pour installation pip, stub process_lidar.py compatible Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
155 lines
4.8 KiB
Python
155 lines
4.8 KiB
Python
"""Command-line interface for the LiDAR archaeological pipeline.
|
|
|
|
Handles argument parsing, logging configuration, and entry point.
|
|
"""
|
|
|
|
import argparse
|
|
import logging
|
|
import sys
|
|
|
|
from .pipeline import LidarArchaeoPipeline
|
|
from .gpu import log_gpu_status
|
|
|
|
logger = logging.getLogger("lidar")
|
|
|
|
|
|
def setup_logging(verbose=False, debug=False):
|
|
"""Configure the 'lidar' logger.
|
|
|
|
Args:
|
|
verbose: If True, include timestamps and level names.
|
|
debug: If True, set level to DEBUG and add file:line info.
|
|
"""
|
|
if debug:
|
|
level = logging.DEBUG
|
|
fmt = "%(asctime)s.%(msecs)03d %(levelname)-5s [%(filename)s:%(lineno)d] %(message)s"
|
|
elif verbose:
|
|
level = logging.INFO
|
|
fmt = "%(asctime)s %(levelname)-5s %(message)s"
|
|
else:
|
|
level = logging.INFO
|
|
fmt = "%(message)s"
|
|
|
|
handler = logging.StreamHandler(sys.stdout)
|
|
handler.setFormatter(logging.Formatter(fmt, datefmt="%H:%M:%S"))
|
|
logger.setLevel(level)
|
|
logger.handlers.clear()
|
|
logger.addHandler(handler)
|
|
return logger
|
|
|
|
|
|
def main():
|
|
"""Entry point for the LiDAR archaeological pipeline."""
|
|
parser = argparse.ArgumentParser(
|
|
description="Pipeline LiDAR pour détection archéologique",
|
|
formatter_class=argparse.RawDescriptionHelpFormatter,
|
|
epilog="""\
|
|
Exemples:
|
|
Traitement standard:
|
|
python -m lidar_pipeline /data/input -o /data/output
|
|
|
|
Haute résolution avec accélération GPU:
|
|
python -m lidar_pipeline /data/input -o /data/output -r 0.2 -g
|
|
|
|
Mode verbeux (timestamps):
|
|
python -m lidar_pipeline /data/input -o /data/output -v
|
|
|
|
Mode debug (détails internes):
|
|
python -m lidar_pipeline /data/input -o /data/output --debug
|
|
|
|
Forcer la régénération de tous les fichiers:
|
|
python -m lidar_pipeline /data/input -o /data/output --force
|
|
|
|
Traiter un seul fichier (pour tests):
|
|
python -m lidar_pipeline /data/input -o /data/output --file LHD_FXX_1000_6881_PTS_LAMB93_IGN69.copc.laz
|
|
|
|
Traitement parallèle (4 workers):
|
|
python -m lidar_pipeline /data/input -o /data/output -w 4
|
|
"""
|
|
)
|
|
parser.add_argument(
|
|
"input",
|
|
help="Dossier contenant les fichiers LAZ/LAS"
|
|
)
|
|
parser.add_argument(
|
|
"-o", "--output",
|
|
default="/data/output",
|
|
help="Dossier de sortie (défaut: /data/output)"
|
|
)
|
|
parser.add_argument(
|
|
"-r", "--resolution",
|
|
type=float,
|
|
default=0.5,
|
|
help="Résolution en mètres par pixel (défaut: 0.5)"
|
|
)
|
|
parser.add_argument(
|
|
"-w", "--workers",
|
|
type=int,
|
|
default=1,
|
|
help="Nombre de workers pour traitement parallèle (défaut: 1)"
|
|
)
|
|
parser.add_argument(
|
|
"-f", "--force",
|
|
action="store_true",
|
|
help="Régénérer tous les fichiers même si les WebP existent déjà"
|
|
)
|
|
parser.add_argument(
|
|
"--file",
|
|
type=str,
|
|
default=None,
|
|
help="Traiter un seul fichier LAZ/LAS (pour tests, par nom partiel ou complet)"
|
|
)
|
|
parser.add_argument(
|
|
"-v", "--verbose",
|
|
action="store_true",
|
|
help="Mode verbeux : affiche les timestamps et niveaux"
|
|
)
|
|
parser.add_argument(
|
|
"--debug",
|
|
action="store_true",
|
|
help="Mode debug : affiche les détails internes (fichier:ligne)"
|
|
)
|
|
|
|
args = parser.parse_args()
|
|
|
|
# Configure logging before any other output
|
|
setup_logging(verbose=args.verbose, debug=args.debug)
|
|
|
|
logger.info("=" * 60)
|
|
logger.info("Pipeline LiDAR Archéologique")
|
|
logger.info("=" * 60)
|
|
|
|
log_gpu_status()
|
|
|
|
try:
|
|
pipeline = LidarArchaeoPipeline(
|
|
input_dir=args.input,
|
|
output_dir=args.output,
|
|
resolution=args.resolution,
|
|
workers=args.workers,
|
|
force=args.force
|
|
)
|
|
|
|
# If --file is specified, process only that single file
|
|
if args.file:
|
|
from pathlib import Path
|
|
input_dir = Path(args.input)
|
|
# Find matching file
|
|
matches = list(input_dir.glob(f"*{args.file}*")) + list(input_dir.glob(f"*{args.file}*.laz")) + list(input_dir.glob(f"*{args.file}*.las"))
|
|
# Remove duplicates
|
|
matches = list(dict.fromkeys(matches))
|
|
if not matches:
|
|
logger.error(f"Aucun fichier trouvé pour: {args.file}")
|
|
sys.exit(1)
|
|
if len(matches) > 1:
|
|
logger.info(f"Plusieurs fichiers correspondent, utilisation du premier:")
|
|
for m in matches:
|
|
logger.info(f" {m.name}")
|
|
laz_file = matches[0]
|
|
logger.info(f"Traitement du fichier: {laz_file.name}")
|
|
pipeline.process_file(laz_file)
|
|
else:
|
|
pipeline.process_all()
|
|
except Exception as e:
|
|
logger.error(f"Erreur fatale: {e}", exc_info=True)
|
|
sys.exit(1) |