Refactor pipeline en modules + logging verbose/debug + options CLI
- 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>
This commit is contained in:
155
lidar_pipeline/cli.py
Normal file
155
lidar_pipeline/cli.py
Normal file
@ -0,0 +1,155 @@
|
||||
"""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)
|
||||
Reference in New Issue
Block a user