SB
articles
Article · Vision 2026-05 · 12 min de lecture

Visual SLAM ↔ Python : un pont propre via sous-processus

Comment intégrer un moteur Visual SLAM C++ open-source dans un back-end FastAPI sans bindings exotiques, et comment aligner ensuite le résultat sur un plan d'étage avec deux contraintes GPS. Retour d'expérience sur un back-office de cartographie indoor.

Contexte

Du flot de pixels à un plan navigable

Le projet : transformer des vidéos 360° en plans indoor navigables. La pièce centrale du pipeline, c'est le Visual SLAM — Simultaneous Localization And Mapping — qui reconstruit en parallèle la trajectoire 3D de la caméra et un nuage de points 3D de l'environnement.

On a choisi un moteur SLAM open-source en C++, construit sur OpenCV, g2o et Eigen. Notre back-end est en Python (FastAPI). La question : comment les faire dialoguer proprement ?

Le choix

Sous-processus, pas binding

Trois options techniques sur la table :

Bindings pybind11

Tentant sur le papier. Mais le moteur SLAM expose une API stateful complexe (config YAML, observers, callbacks). Maintenir un binding qui couvre tout ça revient à dupliquer leur surface publique en C++ avec un coût de maintenance énorme à chaque release upstream.

Service réseau séparé (gRPC)

Propre, scalable. Mais surdimensionné : on a un seul job SLAM à la fois par site, déclenché à la demande. Ajouter un service à packager, déployer et monitorer pour ça serait du complexity tax.

Sous-processus + format de sérialisation neutre

On exécute le binaire SLAM en sous-processus depuis FastAPI, on lui passe la vidéo et la config YAML, on récupère un map.msg (msgpack) qu'on parse côté Python. Simple, observable, isolé du runtime web.

On a pris la troisième. Le binaire SLAM crashe ? Le sous-processus meurt, on capture le code de retour, on logue, on remonte une erreur propre. Aucun risque que ça emporte FastAPI avec lui.

Pipeline

De la vidéo au map.msg

  1. 1

    La vidéo 360° est uploadée vers Cloud Storage via signed URL — pas de proxy backend, le client uploade directement.

  2. 2

    À la demande, FastAPI extrait les frames clés (OpenCV) à une cadence configurable. Pas tout d'un coup : pipeline streamé pour limiter l'IO disque.

  3. 3

    FastAPI invoque le moteur SLAM en sous-processus avec la config (camera intrinsics, scale factor, vocabulary FBoW) et le dossier de frames.

  4. 4

    Le binaire écrit un fichier map.msg contenant keyframes (poses + descripteurs) et map points (3D + observations).

  5. 5

    Côté Python on parse le map.msg via la lib msgpack, on materialise des modèles SQLAlchemy, et on stocke en PostgreSQL.

Le bridge

Trois propriétés du sous-processus

Côté FastAPI, le pattern est court : on lance le binaire SLAM via asyncio.create_subprocess_exec, on attend le returncode, on parse le map.msg produit avec la lib msgpack. Pas de binding, pas de FFI, pas de serveur intermédiaire — juste un process Unix et un fichier de sortie. Ce qu'on gagne :

  • Isolation runtime : le crash d'un job SLAM ne tue jamais l'API.
  • Cancellation propre : tuer le sous-processus revient à l'annuler, sans state corrupted côté Python.
  • Observable : stdout/stderr sont capturés et loggés, on debug en lisant les logs comme n'importe quel process Unix.
Géoréférencement

Aligner la trajectoire sur un plan

Le moteur nous donne la trajectoire dans un repère SLAM arbitraire (origine à la première frame, échelle métrique relative). Pour que ce soit utile dans le back-office, il faut l'aligner sur le plan d'étage en coordonnées WGS84 (lon, lat).

L'opérateur clique sur quelques keyframes et indique « ici dans le SLAM = ici sur le plan ». Avec assez de contraintes, on calcule la transformation rigide qui aligne tout.

1 contrainte

Translation seule : on déplace le repère SLAM pour que le point coïncide. L'orientation et l'échelle restent celles du SLAM.

≥2 contraintes

Sim(2) via l'algorithme d'Umeyama : on résout simultanément rotation 2D, translation et échelle qui minimisent l'erreur quadratique sur les correspondances. Closed-form, robuste, déterministe.

Auto-fit

Si on n'a qu'une contrainte mais que l'opérateur a posé les bornes du plan, on peut auto-scaler la trajectoire pour qu'elle remplisse le plan — sanity check rapide.

Lessons

Ce qu’on retient

  • Pour intégrer une lib C++ avec une API stateful, les sous-processus sont sous-estimés. Ils tracent une frontière nette : un crash là-bas n'est pas un crash chez vous.
  • msgpack est un excellent format d'IPC : binaire, typé, multi-langage, sans la surface d'attaque de pickle.
  • Pour la perf 3D côté navigateur (milliers de frustums), on a utilisé Three.js directement plutôt que react-three-fiber — le coût React par frame est non négligeable à ces volumes.
  • Le géoréférencement Sim(2) Umeyama est en 30 lignes de NumPy. Ne pas se laisser intimider par la littérature SLAM : la plupart des briques utiles ont des solutions closed-form.
  • Toujours tester avec une vidéo « cassée » (frame manquante, codec exotique, taille zéro). Le sous-processus doit échouer proprement, pas planter le worker.