"""GPU acceleration helpers for LiDAR pipeline. Provides CuPy/numpy abstraction layer. If CuPy is available and a CUDA GPU is detected, array operations are accelerated on the GPU. Otherwise, all operations fall back to numpy/scipy on CPU. """ import logging import numpy as np from scipy import ndimage logger = logging.getLogger("lidar") # GPU detection - must happen at import time HAS_GPU = False _gpu_name = None _gpu_mem_gb = 0 _xp = np # Default: CPU try: import cupy as cp import cupyx.scipy.ndimage as cp_ndimage _gpu_info = cp.cuda.runtime.getDeviceProperties(0) _gpu_name = _gpu_info['name'].decode() if isinstance(_gpu_info['name'], bytes) else str(_gpu_info['name']) _gpu_mem_gb = _gpu_info['totalGlobalMem'] // (1024 ** 3) HAS_GPU = True _xp = cp except (ImportError, Exception): pass def log_gpu_status(): """Log GPU detection result. Called after logging is configured.""" if HAS_GPU: logger.info(f"GPU détectée: {_gpu_name} ({_gpu_mem_gb} Go VRAM)") else: logger.info("Pas de GPU — mode CPU uniquement") def to_gpu(arr): """Send array to GPU if available, otherwise return as float64 numpy.""" if HAS_GPU: return cp.asarray(arr.astype(np.float64)) return arr.astype(np.float64) def to_cpu(arr): """Bring array back to CPU (numpy). No-op if already on CPU.""" if HAS_GPU and isinstance(arr, cp.ndarray): return cp.asnumpy(arr) return arr def xp_gaussian_filter(arr, sigma): """Gaussian filter — uses GPU if array is on GPU, CPU otherwise.""" if HAS_GPU and isinstance(arr, cp.ndarray): return cp_ndimage.gaussian_filter(arr, sigma) return ndimage.gaussian_filter(arr, sigma) def xp_uniform_filter(arr, size): """Uniform filter — uses GPU if array is on GPU, CPU otherwise.""" if HAS_GPU and isinstance(arr, cp.ndarray): return cp_ndimage.uniform_filter(arr, size) return ndimage.uniform_filter(arr, size) def xp_minimum_filter(arr, footprint=None, size=None): """Minimum filter — uses GPU if array is on GPU, CPU otherwise.""" if HAS_GPU and isinstance(arr, cp.ndarray): return cp_ndimage.minimum_filter(arr, footprint=footprint, size=size) return ndimage.minimum_filter(arr, footprint=footprint, size=size)