Commit Graph

35 Commits

Author SHA1 Message Date
Flag
9e9eff6cc1 fix: bouton PLY — viewer-btn JS handler au lieu HTMX (evite JSON dans la page) 2026-04-23 20:52:25 +00:00
Flag
7f8c1d0d07 fix: segment_label end-time recalculé depuis video_duration_s (exiftool=0 à lingest) 2026-04-23 20:42:56 +00:00
Flag
71d82018b1 feat: dashboard — plongée label, serial visible, GLB link, is_file thumbnail fix 2026-04-23 20:33:46 +00:00
Flag
1b467c5f03 fix: cleanup stale demo.py + resolve_worker_video_source frames_dir + SSH keys repaired 2026-04-23 19:53:03 +00:00
Flag
194c94b872 dashboard — thumb 48x27, step live, spin busy, live thumbnail refresh
- 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
2026-04-22 23:00:11 +00:00
Flag
f13d27b997 dashboard — vrai tableau + probe viser_url alive + CSS propre
1. _jobs_table.html: remplace la liste <li>flex par un vrai <table> avec
   colonnes explicites: status · preview · #id · AUV+GP · label · duree
   video · frames · hors-eau · progression · temps · actions. Stitches
   restent en <ul> compacte.

2. main.py _build_acquisitions: probe TCP le viser_url avec cache 8s
   avant de le passer au template. Si port mort -> d[viser_url]=None ->
   pas de bouton affiche. Fini les liens qui mennent a rien.

3. style.css: purge des regles flex conflictuelles, rewrite propre pour
   table.jobs-table, badges, prog-bar, btn-viser direct link.
2026-04-22 22:43:14 +00:00
Flag
960ebc0393 dashboard — preview thumbnail par job
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.
2026-04-22 22:06:43 +00:00
Flag
e90d775dfd dispatcher — load balance 2 GPU (lower-load d abord) + fps=2 + debug pick_worker
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.
2026-04-22 22:02:20 +00:00
Flag
033abc41c5 dispatcher — window_size adaptatif + skip video courtes <8min
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.
2026-04-22 21:34:51 +00:00
Flag
9dd6a82d08 dashboard + dispatcher — UX props, trim head+tail, cols, link direct
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)
2026-04-22 21:28:06 +00:00
Flag
311824f036 dispatcher — trim auto prefix hors-eau avant reconstruct
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).
2026-04-22 20:31:30 +00:00
Flag
e8955aa406 dispatcher — status=skipped exclus du stitch per_auv
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.
2026-04-22 20:19:46 +00:00
Flag
fa996f62d6 dashboard — bouton live (viser natif demo.py) + PLY (fallback viser_ply)
live button:
- POST /jobs/{id}/live
- Probe le port natif (worker viser_port_base + job_id) via nc
- 200 + viser_url si demo.py encore alive (necessite le patch keep-alive)
- 410 + fallback message si ferme

PLY button:
- POST /jobs/{id}/view (existant)
- Lance viser_ply.py standalone sur port VIEWER_PORT_BASE+id

Permet de choisir entre viser natif (PointCloudViewer de lingbot-map avec
camera frustums, filtre confiance interactif, animation) et viewer basic
XYZ+RGB uniquement.
2026-04-22 19:48:43 +00:00
Flag
2bc24b2cc4 dispatcher — keep demo.py alive apres PLY + auto-clear error
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.
2026-04-22 19:41:00 +00:00
Flag
cfbb542992 dispatcher — clean frames avant extract + budget RAM 0.55 -> 0.45
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.
2026-04-22 19:25:56 +00:00
Flag
7630e72dcb api /view — utiliser worker lingbot_path au lieu de hardcode
Le wrapper viser cd /home/floppyrj45/ai-video/lingbot-map quel que soit le
worker. Sur .84 (user root) le path est /root/ai-video/lingbot-map, donc le
cd echouait + venv absent + open3d pas importe = viser ne demarrait jamais.

Utiliser worker[lingbot_path] qui est deja configure dans WORKERS.
2026-04-22 18:18:10 +00:00
Flag
3eb568f14e dispatcher — rm worker_src apres extract + fstrim pour eviter thin pool full
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.
2026-04-22 15:39:56 +00:00
Flag
2599a376af dispatcher — fix race condition sur exit_file extract
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.
2026-04-22 09:35:48 +00:00
Flag
621f4e63e8 dispatcher — fix VRAM estimate + adaptive stride pour éviter OOM RAM
- 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.
2026-04-22 08:44:52 +00:00
Flag
d4158b24bc dispatcher — parallel workers (threading) + heartbeat daemon + atomic claim
- 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).
2026-04-21 23:15:24 +00:00
Flag
43e2e6836e workers — alias .84 ml-stack (cosma-vm inexistant) 2026-04-21 22:52:35 +00:00
Poulpe
f567d7e459 dashboard — barre de progression extraction par job
- 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>
2026-04-21 13:48:41 +00:00
Poulpe
192550b60b ingest+dispatcher — support acquisition depuis remote host via SSH
- 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.
2026-04-21 13:31:40 +00:00
Poulpe
468f9084ec viewer on-demand — relancer viser à la demande depuis le dashboard
Le viser de demo.py était tué dès que le PLY était écrit (pour libérer la VRAM),
donc les liens dans le dashboard menaient vers ERR_CONNECTION_REFUSED.

Ajout d'un viewer standalone indépendant :
- scripts/viser_ply.py : charge un PLY via open3d + sert via viser (sans GPU),
  subsample random à 2M pts max pour rester fluide
- app/main.py : routes POST /jobs/{id}/view et /stitches/{id}/view qui scp
  le script sur le worker et lancent un viser détaché (nohup+setsid+disown via
  wrapper shell déposé sur le worker)
- templates : remplace <a href> par <button class=viewer-btn> qui POST puis
  window.open de l'URL retournée
- Dockerfile : copie scripts/ dans l'image (nécessaire pour scp-er viser_ply.py)
2026-04-21 13:09:48 +00:00
Poulpe
6ac3a382c7 dispatcher — stitch passthrough pour acquisition 1 seul PLY
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.
2026-04-21 12:53:57 +00:00
Poulpe
69eb547463 dispatcher+ingest — fixes pour test end-to-end réel
- 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).
2026-04-21 12:49:09 +00:00
Poulpe
654bb47825 monitor : temp GPU, conso watts, espace disque, heartbeat dispatcher
- 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>
2026-04-21 10:42:44 +00:00
Poulpe
0e9a4c2684 Dockerfile : ajoute openssh-client pour worker monitor
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-21 10:38:08 +00:00
Poulpe
79b31e8839 dispatcher : fix spin loop quand pas de worker dispo (sleep 30s)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-21 10:36:14 +00:00
Poulpe
26e5bfc05b stitch pipeline câblé : DB + dispatcher + UI + fix subpath Caddy
- 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>
2026-04-21 10:32:05 +00:00
3b005a4994 dashboard — colonnes par acquisition, checklist verticale hierarchique 2026-04-21 10:08:38 +00:00
47b082fd9d add stitch.py (Open3D RANSAC+ICP multi-PLY merge) 2026-04-21 10:02:23 +00:00
857ebcf7df add Dockerfile + docker-compose (port 3849 on .82) 2026-04-21 09:57:42 +00:00
b7d957c806 scaffold — FastAPI + SQLite + HTMX dashboard, ingest + dispatcher
- app/main.py : dashboard /, partials /partials/{jobs,monitor} (htmx polling)
- app/templates/ : index, jobs table, monitor card par worker
- app/static/style.css : thème sombre cohérent
- scripts/ingest.py : scan SSD d'acquisition, EXIF CreateDate → segments
  continus par (AUV, GoPro serial) avec seuil configurable
- scripts/dispatcher.py : polling queue, pick worker selon VRAM free,
  extraction ffmpeg + lingbot-map windowed --offload_to_cpu, progression DB
- DB : SQLite (acquisitions + jobs), lifecycle queued→extracting→running→done
- Workers par défaut : .87 (3060 12GB) + .84 (3090 24GB)

Contexte : QC terrain le jour-même (avant photogrammétrie à 30 jours),
plusieurs heures × 2 GoPros × 2-3 AUVs d'enregistrement à traiter en parallèle.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-21 09:52:41 +00:00
17edbcbd8b Initial commit 2026-04-21 09:47:36 +00:00