Fix GPU bugs, restore aspect viz, fix anomaly mask, revert flow_acc to vectorized D8
GPU: num_gpus() returns real count via _gpu_candidates (was always 0/1), available_gpu_ids() added. Pipeline: round-robin on real GPU host indices instead of file enumerate index. _process_file_standalone signature simplified. Restore generate_aspect using SharedDEM gradient (dy, dx). Colormap twilight 0-360 fixed range. VIZ_STEPS back to 16. Flow accumulation: revert to vectorized numpy D8 direction + module-level numba accumulator (cached, top-down sort) with Python fallback. Priority-flood NaN-aware. Log1p transform. Anomaly mask: replace RMS+fixed 2sigma threshold (was blank) with weighted sum of |z-score| + adaptive percentile threshold. Absolute z-score captures both positive and negative deviations. 6% signal detected vs 0% before.
This commit is contained in:
@ -57,14 +57,15 @@ _file_filter = FilePrefixFilter()
|
||||
from .dtm import classify_ground, create_dtm_fast
|
||||
from .visualizations import (
|
||||
SharedDEM,
|
||||
generate_hillshade, generate_slope,
|
||||
generate_hillshade, generate_slope, generate_aspect,
|
||||
generate_openness,
|
||||
generate_mslrm, generate_sailore,
|
||||
generate_roughness, generate_wavelet,
|
||||
generate_svf, generate_aniso_open,
|
||||
generate_flow_accumulation,
|
||||
generate_anomaly_mask,
|
||||
)
|
||||
generate_solar,
|
||||
generate_svf, generate_aniso_open,
|
||||
generate_flow_accumulation,
|
||||
generate_anomaly_mask,
|
||||
)
|
||||
from .gpu import gpu_cleanup, num_gpus, restrict_gpus, safe_gpu_call
|
||||
from .ign import generate_ign_overlay
|
||||
from .rendering import tif_to_png
|
||||
@ -76,6 +77,7 @@ from .rendering import tif_to_png
|
||||
VIZ_STEPS = [
|
||||
('hillshade', generate_hillshade),
|
||||
('slope', generate_slope),
|
||||
('aspect', generate_aspect),
|
||||
('mslrm', generate_mslrm),
|
||||
('sailore', generate_sailore),
|
||||
('pos_open', lambda d, b, v, r, shared=None: generate_openness(d, b, v, r, positive=True, shared=shared)),
|
||||
@ -85,6 +87,7 @@ VIZ_STEPS = [
|
||||
('roughness', generate_roughness),
|
||||
('wavelet', generate_wavelet),
|
||||
('flow_acc', generate_flow_accumulation),
|
||||
('solar', generate_solar),
|
||||
('anomaly', generate_anomaly_mask),
|
||||
('ortho', lambda d, b, v, r: generate_ign_overlay(
|
||||
d, b, v, r,
|
||||
@ -463,11 +466,12 @@ class LidarArchaeoPipeline:
|
||||
logger.info(f"Fichiers: {len(files)}")
|
||||
|
||||
with ProcessPoolExecutor(max_workers=self.workers) as executor:
|
||||
# Pass resolutions as comma-separated string for multiprocessing serialization
|
||||
# Round-robin assign each file to a real GPU host index
|
||||
active_ids = self.gpu_ids if self.gpu_ids else _gpu_mod.available_gpu_ids()
|
||||
resolutions_str = ','.join(str(r) for r in self.resolutions)
|
||||
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, gpu_ids=self.gpu_ids): laz_file
|
||||
for gpu_id, laz_file in enumerate(files)
|
||||
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, active_ids[file_idx % len(active_ids)] if active_ids else None): laz_file
|
||||
for file_idx, laz_file in enumerate(files)
|
||||
}
|
||||
done = 0
|
||||
try:
|
||||
@ -535,18 +539,13 @@ class LidarArchaeoPipeline:
|
||||
logger.warning(f" Note: Impossible de supprimer les fichiers temporaires: {e}")
|
||||
|
||||
|
||||
def _process_file_standalone(laz_file_str, input_dir, output_dir, resolution, force=False, ground_method='auto', force_classify=False, keep_tif=False, quality=98, only_viz=None, skip_viz=None, output_format='avif', gpu_id=None, gpu_ids=None):
|
||||
def _process_file_standalone(laz_file_str, input_dir, output_dir, resolution, force=False, ground_method='auto', force_classify=False, keep_tif=False, quality=98, only_viz=None, skip_viz=None, output_format='avif', gpu_id=None):
|
||||
"""Standalone function for multiprocessing — creates its own pipeline instance.
|
||||
|
||||
Each worker gets its own temp directory to avoid file conflicts.
|
||||
When multiple GPUs are available, each worker is assigned a GPU via
|
||||
CUDA_VISIBLE_DEVICES to balance load across GPUs.
|
||||
"""
|
||||
# Restrict visible GPUs first, then pick one for this worker
|
||||
if gpu_ids is not None:
|
||||
from .gpu import restrict_gpus
|
||||
restrict_gpus(gpu_ids)
|
||||
|
||||
if gpu_id is not None and gpu_id >= 0:
|
||||
from .gpu import set_active_gpu
|
||||
set_active_gpu(gpu_id)
|
||||
|
||||
Reference in New Issue
Block a user