- dispatcher: col step ajoutee (migration). set a chaque phase:
scp N/M, ffmpeg N/M, trimming hors-eau, reconstruct demo.py
- _refresh_thumbnail() scp la DERNIERE frame extraite toutes les ~15s
pendant ffmpeg pour que le preview colle a la progression live
- template: cache-bust thumbnail via ?t=mtime, step affiche sous
progress bar, thumb revenue a 48x27
- CSS: .badge.busy -> animation spin (rotation infinie) au lieu de
juste une couleur, .step-text italique mute
dispatcher scp frame_*.jpg (premiere apres trim head) vers
/var/lib/cosma-qc/thumbnails/job_N.jpg a la fin de do_extract.
Endpoint GET /jobs/{id}/thumbnail serve via FileResponse. Template:
<img class=thumb src=jobs/N/thumbnail> si has_thumbnail. 48x27 px,
object-fit cover.
Backfill manuel des jobs deja done (9, 12, 13, 16, 19) via scp direct.
pick_worker trie les candidats par:
1. nombre de jobs deja assignes sur le worker (moins d abord)
2. VRAM free (plus d abord)
3. hostname (tiebreaker sans comparer les dicts)
Avant: le worker avec le plus de VRAM gagnait toujours (ex: .84 24GB vs
.87 12GB) donc tous les jobs empilaient sur .84 pendant que .87 idle.
fps=3 -> fps=2 via COSMA_QC_FPS dans dispatcher.env (cf user: 1 kt +
reconstruction SfM -> 2 fps pas stride 6).
Logs pick_worker ajoutes pour debug quand no candidate.
window_size selon len effectif (frame_count / stride):
<= 320 -> 16/2 (petit overhead)
<= 3000 -> 32/8
> 3000 -> 64/16 (moins de windows sur longues sequences)
Skip auto si video_duration_s < COSMA_QC_MIN_VIDEO_S (default 480s = 8min):
segments trop courts ne contiennent pas de plongee exploitable.
Reco user: videos < 8 min sont inutiles pour la reconstruction.
dashboard:
- job_id, AUV GP1/GP2 (serial en tooltip), segment_label, duree reelle,
nb frames, nb hors-eau trimes
- lien viser plain <a href> (plus de POST ni popup). Affiche uniquement
si job.done ET viser_url persistee (demo.py kept alive)
- CSS minimal: flex row, separateurs, skipped en italic mute
dispatcher:
- trim head ET tail (AUV hors-eau en debut + fin de session)
- migration DB: trimmed_head, trimmed_tail, video_duration_s
- do_extract persiste total_duration_s + trimmed counts via set_status
- run_one: RuntimeError(skipped_short) preserve le status=skipped
- min_frames underwater pour skip les segments trop courts
- ram_budget 0.45 -> 0.35 (OOM rc=137 avec 8237 frames sur 62GB RAM)
Les sessions record commencent systematiquement avec l AUV sur le pont
ou en surface. Les frames hors-eau polluent le model et bloquent l alignment
ICP du stitch.
trim_above_water_prefix detecte underwater par absorption du canal rouge
(mean_R < mean_G-5 ET mean_R < mean_B-5) et exige un streak consecutif
de 10 frames underwater pour lock le start. Tout ce qui precede est
supprime avant demo.py.
Opencv charge les frames en REDUCED_COLOR_4 pour acceleration.
Execute dans le venv lingbot-map cote worker (cv2 dispo).
Les segments auto-ingested peuvent contenir des vidéos non-exploitables
(GoPro sur le pont, hors-eau, bruyantes). Marquer le job skipped empeche
linclusion dans le stitch per_auv: total/done comptent hors-skipped, et
input_job_ids filtre status=done seulement.
Exemple: job 10 AUV209/10:58-11:05 = pull marron + gilet sauvetage sur
le pont pendant preparation, reconstruction 3D inutile pour le puzzle.
1. Ne plus kill demo.py apres PLY saved: son viser/PointCloudViewer natif
(camera frustums, per-frame confidence filtering, animation) donne une
visu bcp plus propre que viser_ply.py standalone (XYZ+RGB seul).
Cout: ~6GB VRAM par job done garde alive jusquau prochain pick_worker
qui peut kill si besoin.
2. set_status clear auto le champ error quand status transitionne vers
extracting/running/done/queued: sinon les dashboards montrent les
erreurs historiques sur les jobs en cours de retry.
Bugs decouverts en live:
1. Les retries/restarts ne cleanaient pas frames_dir -> ffmpeg re-extrayait
par dessus les anciennes -> frame_count inflate (ex: 21991 au lieu de
11000) -> budget stride fausse -> OOM.
2. Budget 0.55*RAM laissait pas assez de headroom (OS + CUDA pinned buffers
+ autres processes) -> kill -9 a la limite. 0.45 plus conservateur.
Le cache src_*.MP4 sur les workers s empile: 12 fichiers pour 82 GB au pire.
Le thin pool LVM sur le host Proxmox est trop petit (810 GB pour 1144 GB
thick-provisionned) et se remplit a 100% en quelques heures de pipeline
-> I/O errors -> VMs auto-paused -> tout casse.
Fix: delete src_*.MP4 immediatement apres count_frames (les frames sont
deja extraites), puis fstrim en fin de job pour que le thin pool reclaim
les blocks immediatement via DISCARD/UNMAP.
Le setsid bash lance ffmpeg puis ecrit le code de retour:
echo $? > exit_file
Avec test -f on matchait le fichier pendant que le shell le creait vide
(write() du "> exit_file" cree le fichier avant fwrite). Resultat:
code_str="" -> isdigit()=False -> rc=1 -> ffmpeg failed false positive.
Fix: test -s (existe ET non-vide) pour attendre que echo ait termine.
- estimate_vram_mib: 3500+13*frames (absurde 110GB pour 8k frames) ? 6000 MiB fixe
(windowed+offload_to_cpu plafonne la VRAM indépendamment du total frames)
- do_reconstruct: stride adaptatif basé sur RAM worker (23GB .87, 62GB .84)
load_fn.py stack le tensor complet en CPU RAM (~3.15 MB/frame)
budget 55% RAM ? stride 2-3 pour jobs >4k frames
Debloque tous les jobs avant aucun ne pouvait passer pick_worker car
estimate dépassait la VRAM totale des GPUs.
- threading: main loop spawns run_one in a thread per queued job; up to len(WORKERS) concurrent.
- pick_worker: thread-safe VRAM reservation to avoid two threads picking the same GPU.
- pop_queued/pop_queued_stitch: atomic SELECT+UPDATE sous BEGIN IMMEDIATE (status claimed).
- Heartbeat daemon: thread qui ecrit dispatcher.heartbeat toutes les 5s (fini le faux dead pendant les jobs longs).
- run_one: libere la reservation VRAM sur finally (error/done/queued).
- dispatcher.py : ffmpeg lancé en background (setsid), polling du
nombre de frames toutes les 5s → mise à jour du champ `progress`
en DB. ffprobe estime le total avant lancement pour calculer le %.
- _jobs_table.html : barre de progression visible sur les jobs
en status extracting/running
- style.css : styles .prog-wrap/.prog-fill/.prog-text
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- ingest.py : --remote-host <alias> pour scanner/exiftool via SSH, stocke
les chemins avec préfixe "alias:" pour que le worker sache puller direct
- dispatcher.py : scp_to_worker détecte "host:path" et fait pull remote
(worker → source host) au lieu du double hop via dispatcher
- _path_basename gère les paths préfixés pour ffmpeg
Permet d'ingester les vidéos depuis n'importe quelle machine accessible
en SSH sans passer 145GB par le conteneur FastAPI.
Quand un seul PLY est disponible (1 segment, 1 caméra), aucun alignement
n'est nécessaire. On copie directement le PLY en output et on marque la
stitch done plutôt qu'error. Cross-AUV est quand même tenté si conditions remplies.
- dispatcher: scp du MP4 source vers le worker avant ffmpeg (les chemins .82 ne sont pas accessibles côté .87)
- dispatcher: wrapper shell autour de demo.py pour killer viser dès que le PLY est écrit (setsid + pkill -f frames_dir)
- dispatcher: PLY_ok fallback — accepte rc!=0 si le PLY existe et a une taille > 0
- dispatcher: fallback frame_count abaissé à 150 pour l'estimation VRAM
- ingest: strip du suffixe timezone (+00:00) des timestamps exiftool QuickTimeUTC=1
Testé bout-en-bout sur GX010001.MP4 (70 frames, 10.6M pts PLY, VRAM peak 9.4 GB, kill viser OK).
- nvidia-smi : +temperature.gpu + power.draw
- UI : tags °C / W / espace disque libre
- Dispatcher heartbeat toutes les 4s → point vert/rouge en haut du monitor
- Fix Docker SSH : copie + chmod 600 au démarrage (Bad owner)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Table stitches (per_auv + cross_auv) avec cancel/retry API
- Dispatcher : PLY export auto (--save_ply), trigger stitch en cascade
quand tous les jobs d'un AUV sont done
- UI : section stitch live depuis DB avec statuts/durées/boutons
- Fix : <base href="/cosma-qc/"> + chemins relatifs pour Caddy subpath
- open3d 0.19.0 installé sur gpu (.87)
- SSH key .82→.87 configurée, alias gpu ajouté sur .82
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>