diff --git a/lidar_pipeline/pipeline.py b/lidar_pipeline/pipeline.py index 8068d0f..ea10369 100644 --- a/lidar_pipeline/pipeline.py +++ b/lidar_pipeline/pipeline.py @@ -447,17 +447,18 @@ class LidarArchaeoPipeline: t_pipeline_start = time.time() if self.workers > 1 and len(files) > 1: - n_gpus = num_gpus() + n_gpus = num_gpus() or 1 if n_gpus > 1: logger.info(f"Traitement parallèle avec {self.workers} workers sur {n_gpus} GPUs...") else: logger.info(f"Traitement parallèle avec {self.workers} workers...") + if n_gpus > 1 and self.workers == 1: + logger.info(f"Conseil: utilisez -w {n_gpus} pour exploiter tous les GPUs") logger.info(f"Fichiers: {len(files)}") with ProcessPoolExecutor(max_workers=self.workers) as executor: # Pass resolutions as comma-separated string for multiprocessing serialization resolutions_str = ','.join(str(r) for r in self.resolutions) - n_gpus = num_gpus() or 1 future_to_file = { executor.submit(_process_file_standalone, str(laz_file), str(self.input_dir), str(self.output_dir), resolutions_str, self.force, self.ground_method, self.force_classify, self.keep_tif, self.quality, self.only_viz, self.skip_viz, self.output_format, gpu_id % n_gpus): laz_file for gpu_id, laz_file in enumerate(files) diff --git a/lidar_pipeline/rendering.py b/lidar_pipeline/rendering.py index cf31b82..f490345 100644 --- a/lidar_pipeline/rendering.py +++ b/lidar_pipeline/rendering.py @@ -743,21 +743,22 @@ def tif_to_png(tif_file, vis_dir, resolution, keep_tif=False, source_info=None, fig.patch.set_facecolor('white') - # Save as PNG then convert to final format — fixed layout, no bbox_inches='tight' + # Save figure to in-memory buffer (avoids disk I/O of temp PNG) save_dpi = 200 if width > 3000 else 150 - png_temp = vis_dir / f"{tif_file.stem}_temp.png" + from io import BytesIO + buf = BytesIO() try: - plt.savefig(png_temp, dpi=save_dpi, facecolor='white', format='png') + plt.savefig(buf, dpi=save_dpi, facecolor='white', format='png') finally: plt.close() + buf.seek(0) - img = PILImage.open(str(png_temp)) + img = PILImage.open(buf) pil_format = 'AVIF' if output_format == 'avif' else 'WEBP' if quality >= 100: img.save(str(output_file), format=pil_format, lossless=True) else: img.save(str(output_file), format=pil_format, quality=quality) - png_temp.unlink(missing_ok=True) # Delete source TIFF (unless --keep-tif) if not keep_tif: diff --git a/lidar_pipeline/visualizations.py b/lidar_pipeline/visualizations.py index f694cd6..f325736 100644 --- a/lidar_pipeline/visualizations.py +++ b/lidar_pipeline/visualizations.py @@ -480,7 +480,9 @@ def generate_svf(dem_file, basename, vis_dir, resolution, shared=None): angles = np.linspace(0, 2 * np.pi, n_dirs, endpoint=False) dx_dir = np.cos(angles) dy_dir = np.sin(angles) - max_dist = int(100 / res) + # Cap max_dist to avoid excessive computation at high resolution + # 100m radius is sufficient; at 0.2m that's 500 steps which is very slow + max_dist = min(int(100 / res), 300) padded = xp.pad(dem, max_dist, mode='constant', constant_values=xp.nan) svf = xp.zeros_like(dem) @@ -556,7 +558,7 @@ def generate_openness(dem_file, basename, vis_dir, resolution, positive=True, sh angles = np.linspace(0, 2 * np.pi, n_dirs, endpoint=False) dx_dir = np.cos(angles) dy_dir = np.sin(angles) - max_dist = int(100 / res) + max_dist = min(int(100 / res), 300) padded = xp.pad(dem, max_dist, mode='constant', constant_values=xp.nan) openness_sum = xp.zeros_like(dem) @@ -1320,7 +1322,7 @@ def generate_svf(dem_file, basename, vis_dir, resolution, shared=None): angles = np.linspace(0, 2 * np.pi, n_dirs, endpoint=False) dx_dir = np.cos(angles) dy_dir = np.sin(angles) - max_dist = int(100 / res) + max_dist = min(int(100 / res), 300) padded = xp.pad(dem, max_dist, mode='constant', constant_values=xp.nan) svf_sum = xp.zeros_like(dem) @@ -1405,7 +1407,7 @@ def generate_aniso_open(dem_file, basename, vis_dir, resolution, shared=None): # aligned with Roman and medieval settlement patterns in France weights = np.array([1.0, 1.5, 1.0, 1.5, 1.0, 1.5, 1.0, 1.5]) - max_dist = int(100 / res) + max_dist = min(int(100 / res), 300) padded = xp.pad(dem, max_dist, mode='constant', constant_values=xp.nan) pos_sum = xp.zeros_like(dem)