virtual-background-segmentation

Arrière-plan virtuel : segmentation — LGB-062, étape 0

Nature de ce document

Rapport d’investigation pour l’étape 0 de LGB-062 (voir docs/projet/03-BACKLOG-LEGBA.md), pas une suite U/M/I/C/E — même patron que webrtc-tauri.md (LGB-037). Distingue explicitement, pour chaque affirmation :

  • mesuré : exécuté réellement dans un navigateur réel depuis cette session, chiffres à l’appui.
  • vérifié : confirmé par une source primaire (registre npm, code source du SDK), pas une connaissance générale supposée à jour.
  • non fermé : question réelle qui reste ouverte, jamais présentée comme tranchée.

Le banc de mesure exécuté est examples/virtual-background-bench — reproductible, aucune dépendance à installer.


Bibliothèque retenue

@mediapipe/tasks-vision, ImageSegmentervérifié, pas supposé :

  • L’ancienne API « Solutions » (@mediapipe/selfie_segmentation) est dépréciée au profit de l’API unifiée « Tasks ». C’est cette dernière qui est activement maintenue.
  • Licence Apache-2.0 — compatible avec une dépendance dans un projet AGPL-3.0 (dépendance permissive dans une chaîne AGPL : la partie AGPL du projet reste sous AGPL, aucun conflit).
  • Version réelle vérifiée avant tout code : 1.0.1 sur le registre npm (via l’API jsDelivr), pas la série 0.10.x attendue au départ d’une connaissance générale — corrigée en consultant directement le registre plutôt que de deviner un numéro de version pour l’URL du CDN.
  • Poids réel des fichiers servis (pas une estimation) : runtime WASM + JS ~860 Ko, modèle selfie_segmenter (flottant 16 bits) ~447 Ko — soit ~1,3 Mo au total, chargés à la demande (jamais dans le bundle principal d’une application qui n’active pas la fonctionnalité).

Mesure de performance — mesuré

Banc exécuté dans un vrai navigateur (Chromium), chargement effectif du runtime WASM et du modèle depuis les URLs de production (jsDelivr, Google Cloud Storage) — 300 images, canevas synthétique 640×480, par délégué :

DéléguéChargement modèleMoyenne/image (régime établi)p50p95maxDébit soutenu estimé
GPU (WebGL)222 ms0,33 ms0,20 ms1,30 ms2,50 ms~3048 images/s
CPU151 ms9,19 ms8,00 ms9,60 ms51,50 ms~109 images/s

Lecture. Budget de trame à 30 images/s = 33 ms. Les deux délégués tiennent ce budget avec une marge très confortable sur la machine de mesure — y compris le repli CPU (9,19 ms de moyenne, 9,60 ms au 95ᵉ centile, contre un budget de 33 ms). Le délégué GPU a été vérifié réellement actif (contexte WebGL 2.0 créé, observé dans la console du navigateur), pas un repli CPU silencieux supposé fonctionner.

Piège d’API réel, trouvé en exécutant — absent de la documentation consultée. segmentForVideo() exige un horodatage strictement croissant, en entier (millisecondes). Passer performance.now() (valeur flottante, peut piétiner d’un appel à l’autre selon la précision du navigateur) déclenche une vraie erreur MediaPipe :

INVALID_ARGUMENT: Packet timestamp mismatch on a calculator receiving
from stream "norm_rect". Current minimum expected timestamp is X but
received Y.

Résolu avec un compteur dédié, incrémenté manuellement à chaque appel — à retenir tel quel pour l’implémentation réelle (LGB-062, scénarios 5-9).

Ce qui reste non fermé

  • La machine de mesure n’est pas un appareil bas de gamme représentatif. C’est une machine hôte de développement — les chiffres ci-dessus montrent que la bibliothèque elle-même est légère et rapide, mais ne closent PAS la question d’un vrai vieux Chromebook ou d’une tablette Android d’entrée de gamme. Cette mesure reste à faire pendant l’implémentation, contre un appareil réellement faible (LGB-054 donne déjà le patron de rapport chiffré à suivre).
  • La qualité du masque sur un vrai visage n’est pas mesurée ici. Le banc utilise un canevas synthétique (une forme géométrique simple, pas un visage) — aucun accès webcam dans cet environnement de recherche non interactif. Le débit d’inférence ne dépend pas du contenu de l’image (le modèle traite un tenseur de taille fixe quel que soit le contenu), donc ces chiffres de performance restent valides ; la qualité perçue du découpage personne/fond reste à vérifier contre une vraie caméra pendant l’implémentation.
  • Le plan de dégradation précis n’est pas encore écrit, seulement esquissé : mesurer p95 en direct au démarrage de l’effet (le banc le fait déjà) et désactiver proprement si ça dépasse le budget de trame sur une fenêtre glissante, plutôt qu’une liste figée d’appareils à exclure. Reste à implémenter et à vérifier contre un appareil faible réel.

Conclusion de l’étape 0

Rien dans cette recherche ne remet en cause le principe retenu (interception de piste, segmentation par image, composition canevas, republication). Le signal de performance est positif — largement, y compris en repli CPU — sur la machine testée. Le seul vrai doute qui subsiste porte sur les appareils bas de gamme, qu’aucune mesure sur machine de développement ne peut fermer. Recommandation : passer à l’implémentation (<legba-background-select> + createVirtualBackgroundProcessor, voir LGB-062), avec la mesure sur appareil faible comme condition de clôture du billet, pas comme préalable bloquant à l’écriture du code.