Pipeline cosma-qc
=================

Vue d'ensemble
--------------

Le pipeline cosma-qc traite les vidéos GoPro brutes acquises par les drones sous-marins
AUV COSMA pour produire des nuages de points 3D denses (PLY) et des exports web (GLB).

Flux de données global
----------------------

.. code-block:: text

    z620 (/mnt/portablessd)
    ├── GX*.MP4  (brut)
         │
         ▼  [1. Ingest]
    SQLite DB (jobs)
         │
         ▼  [2. Extraction ffmpeg]
    GPU worker  /cosma-qc-frames/job_{id}/frame_%06d.jpg
         │
         ▼  [3. Reconstruction lingbot-map]
    GPU worker  /cosma-qc-frames/job_{id}/reconstruction.ply
         │
         ▼  [4. Stitch per_auv]
    GPU worker  /cosma-qc-frames/stitch_{N}.ply  (par AUV)
         │
         ▼  [5. Stitch cross_auv]
    GPU worker  /cosma-qc-frames/stitch_global.ply
         │
         ▼  [6. Export GLB  (à la demande)]
    GPU worker  /cosma-qc-frames/job_{id}/reconstruction.glb


Étape 1 — Ingest
-----------------

**Script :** scripts/ingest.py

**Rôle :** Scanner le SSD de z620, regrouper les MP4 GoPro par AUV/GoPro/segment
temporel et écrire les jobs dans la base SQLite.

Logique de regroupement :

- Les fichiers sont groupés par numéro de caméra GoPro (GXXX).
- Un nouveau segment est créé si l'écart entre deux fichiers dépasse **5 minutes**.
- Chaque segment → un job en base avec statut pending.

Structure d'un job en base :

.. code-block:: sql

    CREATE TABLE jobs (
        id INTEGER PRIMARY KEY,
        auv TEXT,
        gopro TEXT,
        segment INTEGER,
        files TEXT,          -- JSON list of z620:/path/GX*.MP4
        status TEXT,         -- pending / running / done / failed
        worker TEXT,
        created_at TIMESTAMP,
        updated_at TIMESTAMP
    );

Les chemins vidéo sont stockés sous la forme z620:/chemin/absolu/GX*.MP4
pour indiquer que la source est sur z620, pas sur le worker GPU.


Étape 2 — Extraction des frames
---------------------------------

**Fonction :** do_extract dans le dispatcher (core .82)

**Outil :** ffmpeg

Paramètres d'extraction :

.. code-block:: bash

    ffmpeg -i input.mp4         -vf fps=2,scale=1920:1080         -q:v 2         {frames_dir}/job_{id}/frame_%06d.jpg

**Particularité z620 :** lorsque la source est z620:/..., ffmpeg s'exécute
**directement sur z620** via SSH — seuls les JPEG voyagent sur le réseau.
Les MP4 bruts ne quittent jamais z620.

Filtre hors-eau (heuristique de luminosité) :

- Les frames dont la luminosité moyenne est au-dessus d'un seuil sont supprimées
  (ciel, surface, hors-eau).
- Ce filtre réduit le bruit dans la reconstruction.

Reprise sur crash :

- Un fichier marqueur .video_N.done est créé après chaque MP4 traité.
- En cas de redémarrage, les vidéos déjà traitées sont ignorées.


Étape 3 — Reconstruction 3D
-----------------------------

**Fonction :** do_reconstruct

**Outil :** lingbot-map (demo.py) sur le worker GPU

**Entrée :** répertoire de frames {worker_frames_dir}/job_{id}/

**Sortie :** {worker_frames_dir}/job_{id}/reconstruction.ply
(nuage de points dense, jusqu'à **150 millions de points**)

Un viewer Viser est automatiquement démarré sur le port 8100 + job_id
pendant la reconstruction pour visualisation en temps réel.

.. code-block:: bash

    # Visualiser pendant reconstruction
    # naviguer vers http://{worker_ip}:{8100+job_id}


Étape 4 — Stitch par AUV
--------------------------

**Fonction :** do_stitch_per_auv

**Déclenchement :** automatique quand **tous les jobs d'un AUV** sont en statut done.

**Outil :** cosma-stitch.py

**Opération :** alignement et fusion des PLY de tous les segments d'un même AUV.

**Sortie :** {worker_frames_dir}/stitch_{N}.ply

Ce stitch s'exécute sur le worker qui détient les PLY (pas de copie inter-workers).


Étape 5 — Stitch cross-AUV
----------------------------

**Fonction :** do_stitch_cross_auv

**Déclenchement :** automatique quand **tous les stitches per_auv** sont validés.

**Opération :** fusion de tous les PLY par AUV en un nuage de points global final.

**Sortie :** {worker_frames_dir}/stitch_global.ply


Étape 6 — Export GLB (à la demande)
-------------------------------------

**Outil :** trimesh (conversion PLY → GLB)

**Sous-échantillonnage :** 5 millions de points (adapté web/navigateur)

**Sortie :** {worker_frames_dir}/job_{id}/reconstruction.glb

Un serveur HTTP minimal est lancé sur le worker (port **8300**) pour le téléchargement :

.. code-block:: bash

    # Sur le worker
    python3 -m http.server 8300 --directory {worker_frames_dir}

    # Télécharger depuis un PC
    wget http://{worker_ip}:8300/job_{id}/reconstruction.glb


Statuts de jobs
---------------

.. list-table::
   :header-rows: 1
   :widths: 20 80

   * - Statut
     - Description
   * - pending
     - Job créé, en attente de dispatch
   * - running
     - En cours de traitement (extraction ou reconstruction)
   * - done
     - Reconstruction terminée, PLY disponible
   * - failed
     - Erreur — voir logs du dispatcher
