Segmentation d’Instances avec Invite Utilisateur: YOLOE-26 sur Hailo-10H
Cet article présente la segmentation d’instances avec invite utilisateur via YOLOE-26 sur NPU Hailo-10H. Il détaille la modification du modèle, sa quantification INT8 et son optimisation jusqu’à 15,6 FPS (640px) et ~31 FPS (320px). La prise en charge du vocabulaire ouvert de YOLOE-26 permet aux opérateurs de saisir n’importe quel nom de classe pour une segmentation en temps réel sur le Hailo-10H.
Notre objectif était d’exécuter la segmentation d’instances en temps réel sur le Hailo-10H tout en préservant sa capacité de vocabulaire ouvert. Cet article retrace le parcours complet, du modèle PyTorch au pipeline final. Nous abordons l’adaptation au prompting, la compilation Hailo en deux résolutions (640px et 320px) et l’intégration Python. Enfin, nous détaillons l’optimisation des performances (de 6,3 à 15,6 FPS) et présentons un benchmark comparatif.
Pourquoi la segmentation avec vocabulaire ouvert
Nous remplaçons les réseaux à classes fixes et les cycles de réentraînement par un déploiement embarqué unique qui segmente n’importe quelle invite texte utilisateur. La segmentation d’instances sur l’embarqué signifie généralement un ensemble fixe et fermé de classes intégrées au modèle lors de l’entraînement. Si vous souhaitez segmenter un élément sur lequel le modèle n’a pas été entraîné, vous devez réentraîner et réexporter votre modèle. C’est coûteux et chronophage.
YOLOE-26 brise cette hypothèse : il s’agit d’un modèle de segmentation à vocabulaire ouvert (« prompt anything »). Vous fournissez des invites textuelles lors de l’exécution (ex. person, forklift, solar panel). Le modèle segmente ensuite ces classes sans aucun réentraînement. C’est idéal pour la manutention en entrepôt, l’agriculture ou l’inspection. Cela facilite aussi le prototypage rapide quand la liste des classes évolue fréquemment. La nouvelle vague de NPU discrets (DNPU) débloque ce type de traitement plus lourd qui offre des capacités plus flexibles aux applications de vision par ordinateur. Nous exploitons un Hailo-10H sur un Raspberry Pi AI HAT+ 2, combiné à un Raspberry Pi 5.
Hailo-10H sur un Raspberry Pi AI HAT+ 2
Le modèle : un vocabulaire ouvert
YOLOE (Real-Time Seeing Anything) est une nouvelle avancée dans les modèles YOLO zero-shot guidés par invites (promptable), conçus pour la détection et la segmentation à vocabulaire ouvert. Contrairement aux modèles YOLO précédents limités à des catégories fixes, YOLOE utilise des invites textuelles, visuelles ou de vocabulaire interne, permettant la détection en temps réel de n’importe quelle classe d’objets.
https://docs.ultralytics.com/models/yoloe
La tête de segmentation de YOLOE est contrastive : elle évalue chaque emplacement spatial par rapport à un ensemble d’embeddings textuels. Les scores de classe sont les produits scalaires des embeddings d’image du backbone et des embeddings de texte, mis à l’échelle et biaisés par niveau :
Cette structure est la clé pour exécuter une inférence à vocabulaire ouvert sur un accélérateur fixe. Les embeddings d’image ne dépendent pas de l’invite. Nous avons donc divisé le modèle :
NPU (graphe fixe) : le backbone génère des embeddings d’image, des boîtes brutes, des coefficients de masque et des prototypes par image.
Sur le CPU (runtime) : la liste d’invites actives est évaluée par rapport à ces embeddings d’image, puis décodée en boîtes et masques d’instances.
Grâce à cette séparation invariante aux invites, changer d’invites, même pour une classe jamais vue par la démo, ne coûte qu’une réévaluation du score par le CPU. Aucune recompilation HEF, aucun redémarrage de caméra. La séparation est mathématiquement exacte. Le calcul du score se divise entre le NPU (embeddings d’image) et le CPU (produit scalaire, échelle, biais), avec un écart maximal de 6×10⁻⁴ par rapport au modèle de référence.
Véritable saisie de texte libre : l’encodeur de texte sur l’appareil
La séparation invariante aux invites répond à la question de savoir où le calcul du score s’effectue, mais laisse une question ouverte : d’où proviennent les embeddings de texte ? Un cache précalculé (ex. 80 classes COCO) suffit pour une démo fixe. Cependant, il réintroduit le vocabulaire fermé que nous cherchions à éliminer. YOLOE-26s-seg a été entraîné sur plus de 1200 classes ; le limiter à un cache gaspille le potentiel du modèle. La version finale de ce pipeline encode du texte arbitraire sur l’appareil lui-même, lors de l’exécution.
Architecture de l’encodeur de texte sur l’appareil
Pour permettre la saisie de texte libre, nous avons déployé un encodeur MobileCLIP2-B + RepRTA sur l’appareil. L’opérateur peut ainsi saisir n’importe quel mot pour le segmenter en direct.
Pour ce faire, le système intègre l’ensemble du parcours textuel YOLOE sur l’appareil, décrit comme suit :
Architecture de l’encodeur de texte sur l’appareil
Tokenizer : un tokenizer CLIP BPE purement Python intégré — sans ultralytics, open_clip, clip ou onnxruntime sur le Pi.
Encodeur de texte : MobileCLIP2-B sous forme de module TorchScript (mobileclip2_b.ts, 243 Mo, ressource de publication Ultralytics figée et vérifiée par SHA256), s’exécutant sur le CPU du Pi via torch.
Adaptateur RepRTA : l’adaptateur côté texte de YOLOE, exporté en TorchScript (reprta.ts), suivi d’une normalisation L2.
Vérifié de bout en bout sur l’appareil, dans le même processus que HailoRT, sur un ensemble de mots incluant des termes hors COCO : diff. abs. max. 5,59×10⁻⁷. Peu importe ce que saisit l’opérateur, l’appareil Edge calcule le même embedding d’invite que le modèle PyTorch complet.
Lors de l’exécution, toute invite qui n’est pas déjà dans la banque est encodée sur le champ, ajoutée et mise en cache. L’encodage initial d’un mot prend ~0,5 s sur CPU, hors préchauffage TorchScript unique de 2 s. Les répétitions utilisent le cache et ne prennent que ~0,02 ms. Cela s’exécute en dehors de la boucle critique, préservant la vitesse du pipeline d’images tout en encodant de nouveaux mots en arrière-plan.
La démo combine trois étapes coopératives : l’encodage texte sur CPU (à la demande) et le backbone vision sur NPU (fixe). S’y ajoute le calcul du score contrastif avec décodage sur CPU (dépendant des invites).
Adaptation du modèle pour le NPU : la découpe de la tête
Le parseur Hailo rejette les opérations de post-traitement en fin de réseau comme TopK, GatherElements et les Gathers dynamiques sur NPU.
La solution est une découpe de la tête brute (raw-head cut) : le graphe ONNX est coupé avant la partie décodage/NMS, exposant les têtes brutes du backbone comme sorties. Tout ce qui suit la découpe (scores, décodage DFL, top-k, masques) bascule sur le CPU. Cela répond exactement aux besoins de la séparation invariante aux invites. L’accélérateur n’exécute que la partie à la fois indépendante de l’invite et compatible avec le NPU.
Un second problème, plus subtil : le graphe brut échouait toujours au niveau du parseur avec ValueError: channels is not in list sur les nœuds d’attention Split (formes dynamiques Shape/Slice/Gather). L’exécution d’onnxsim avec des formes d’entrée fixes (images=[1,3,H,W]) résout les formes dynamiques pour la compilation. Les formes d’entrée statiques sont obligatoires à la fois pour l’exportation ONNX et la compilation Hailo.
Quantification : INT8 via le Hailo Dataflow Compiler (niveau 4)
Le NPU fonctionne en INT8. Nous compilons en utilisant l’optimisation de niveau 4 du Hailo Dataflow Compiler avec 1 024 images de calibration de validation. La normalisation (/255) est intégrée dans le graphe via le script du modèle (.alls), ainsi le HEF accepte directement du uint8 NHWC et il n’y a aucune conversion float côté CPU dans la boucle critique.
Deux propriétés de compilation sont essentielles :
Le multi-contexte est requis. Une compilation mono-contexte échoue avec Resources presolve failed: lcus=(…/80) ; le compilateur partitionne le réseau en 6–7 contextes et réussit.
La normalisation de calibration doit correspondre exactement à la normalisation d’inférence. Un écart à ce niveau produit silencieusement des masques erronés. Les tenseurs de calibration sont adaptés au format letterbox et codés en uint8 de la même manière que le prétraitement à l’exécution pour chaque image.
HEF 640px
HEF 320px
Entrée
[1,3,640,640] uint8 NHWC
[1,3,320,320] uint8 NHWC
Contextes
7
6
Taille du HEF
14,2 Mo
13,9 Mo
Échelles d’embedding
80 / 40 / 20
40 / 20 / 10
Ancres
8400
2100
Prototypes
160×160×32
80×80×32
Calibration
1024 images, uint8 HWC
1024 images, uint8 HWC
Optimisation
L4 Adaround (~4 h)
L4 Adaround (~1 h 23 min)
Les deux variantes HEF compilées à partir du même modèle accepté.
Le pipeline : de la caméra à l’affichage
Notre pipeline purement Python utilise Picamera2/OpenCV pour la capture, les API HailoRT pour l’inférence, et OpenCV pour le rendu.
L’architecture du pipeline est :
Architecture du pipeline
L’accélérateur émet six tenseurs NHWC (boîtes, coefficients de masque, prototypes et trois échelles d’embedding d’image). Le CPU évalue les invites actives et décode uniquement les détections conservées. Il construit ensuite un masque d’instance par détection avant l’intégration en alpha-blending. Le parcours de l’invite (texte → embedding) est mis en cache en dehors de la boucle critique, de sorte que le coût en régime permanent est dominé par le NPU et le nombre d’invites actives.
Élément crucial, la boucle ne s’exécute pas de façon série. Une boucle série naïve paie capture + inference + post par image. Le pipeline exécute l’inférence Hailo sur un thread dédié en double-buffering asynchrone (3 inférences simultanées). Une image apparaît ainsi à chaque période limitée par le NPU, plutôt qu’à chaque aller-retour. C’est le levier de débit le plus important, voir les détails ci-dessous.
Parcours d’optimisation
La première pipeline fonctionnelle s’exécutait à environ ~5,5 FPS (640px), de façon strictement sérielle, le NPU et le CPU ne se chevauchant jamais. Les étapes clés :
Étape d’optimisation
Action réalisée
Résultat (640px)
Version de référence (Baseline)
Boucle sérielle, transpositions à chaque image
~5,5 FPS
Élimination des transpositions NHWC→BCHW
Consommation directe du format NHWC natif de Hailo ; transposition uniquement des lignes conservées ≤max_det
map_outputs 58 ms → ~0 ms
Précalcul des statiques + mise en cache des invites normalisées
Grilles d’ancres et cartographie des rôles résolues une fois au démarrage
déterministe, quelques ms
Pipeline multithreadé
Chevauchement de l’inférence Hailo avec le post-traitement CPU
6,45 → 8,82 FPS
Réutilisation des bindings + double-buffering asynchrone
Jusqu’à 3 inférences en cours de traitement
6,34 → 15,58 FPS
Décodage des ancres conservées uniquement
Décodage de 100 boîtes, au lieu de 8400 (exact au bit près)
plus propre, limité par le NPU
Étapes d’optimisation depuis la version de référence sérielle jusqu’au pipeline asynchrone limité par le NPU.
Éléments clés :
Le coût CPU le plus important n’était pas du calcul réel. Près de la moitié du temps CPU servait à transposer les sorties du format natif NHWC vers BCHW (~22 Mo de copies mémoire par image). Cela générait une pénalité de ~58 ms. Réécrire les fonctions consommatrices (calcul de score, décodage de boîtes, assemblage de masques) pour lire directement le NHWC, par ex. en modifiant l’einsum du calcul de score de bchw,bkc->bkhw vers bhwc,bkc->bkhw, est mathématiquement identique et a réduit l’étape de cartographie de ~58 ms à quasiment zéro. À retenir : adaptez-vous à la disposition de tenseurs native de l’accélérateur au lieu de la combattre.
La latence et le débit sont des valeurs différentes. Une inférence NPU unique représente un aller-retour de ~110 ms (640px), mais le Hailo-10H dispose d’un pipeline interne et gère jusqu’à ~10 images simultanément. Grâce au double-buffering asynchrone, une image sort toutes les ~64 ms du NPU. Pourtant, chaque image individuelle prend toujours ~110 ms de bout en bout. L’aller-retour de ~110 ms se décompose en ~64 ms de calcul NPU (le goulot d’étranglement) plus ~48 ms de temps DMA + pilote/ordonnanceur pouvant être chevauchés.
Après ces optimisations, la démo est limitée en débit par l’étape NPU de ~64 ms (640px).
Résultats et performances
Nous avons évalué deux modèles compilés aux résolutions 640px et 320px avec la même configuration :
640px vs 320px (banc d’essai synchrone par image)
Métrique
640px
320px
Δ (320 vs 640)
IoU de l’union des masques vs GT (moyenne)
0,7031
0,6079
−0,095 (−13,5%)
IoU des masques ONNX vs HEF (moyenne)
0,8713
0,7739
−0,097
Latence de l’inférence seule
108,3 ms
30,0 ms
3,6× plus rapide
FPS de l’inférence seule
9,23
33,29
3,6×
Latence de bout en bout
371,0 ms
118,7 ms
3,1× plus rapide
FPS de bout en bout
2,72
9,02
3,3×
640px vs 320px sur les 50 mêmes images de benchmark.
Débit de la démo en direct (pipeline asynchrone double-bufferisé, profondeur 3)
Configuration
Débit
Période
640px, peu d’invites (0-5)
~15,6 FPS
~64 ms
320px, peu d’invites (0-5)
~31 FPS
~32 ms
320px, 80 invites (COCO80 complet)
~12 FPS
~80 ms
Tests de débit asynchrone en direct.
À retenir :
La résolution est le levier de vitesse dominant. Diviser par deux l’entrée (640→320) offre une inférence NPU ~3,6× plus rapide. Le gain sur l’ensemble du pipeline est légèrement plus faible (~3,1×) car le coût fixe côté CPU (calcul de score, décodage, reconstruction du masque) ne diminue pas proportionnellement.
Le coût réside dans la précision du masque pour les petits objets. Le 320px perd ~0,095 d’IoU de masque, presque entièrement sur le rappel des petits objets que la grille plus grossière du 320 (2100 ancres, proto 80×80) résout moins que le 640 (8400 ancres, proto 160×160). La référence float ONNX en 320px chute déjà à 0,672 avant quantification, il s’agit du compromis de résolution inhérent au modèle et non d’un artéfact de quantification.
Le nombre d’invites actives est le second levier de vitesse. L’inférence NPU reste constante pour une résolution donnée. En revanche, le calcul CPU augmente de ~1,5 ms par invite active et varie selon le nombre de détections. Invites vides/peu nombreuses → limité par le NPU (~31 FPS en 320). 80 invites → limité par le CPU (~12 FPS en 320). Conservez un ensemble d’invites actives restreint pour la fréquence d’images la plus fluide possible.
Utilisez le 320px lorsque le débit, la latence et la consommation d’énergie comptent plus que la précision fine des masques. Utilisez le 640px lorsque la qualité du masque sur les petits objets est la priorité.
Principaux enseignements d’ingénierie
Déployer un modèle de segmentation à vocabulaire ouvert sur un NPU n’est pas une opération plug-and-play. Plusieurs leçons sont ressorties de ce projet :
Séparez le modèle le long de la frontière d’invariance à l’invite. Séparer le backbone (NPU) du calcul de score de l’invite (CPU) permet l’inférence à vocabulaire ouvert sur des accélérateurs fixes. N’importe quelle invite, aucune recompilation.
Adaptez-vous à la disposition de tenseurs native de l’accélérateur. Le plus grand gain CPU unique ne provient pas d’un calcul plus intelligent mais de la suppression des transpositions NHWC→BCHW par image en lisant directement la disposition native du NPU. ~58 ms → ~0 ms.
Latence ≠ débit sur un NPU en mode pipeline. Le double-buffering asynchrone a transformé une inférence à 110 ms de latence en une étape à débit de 64 ms en chevauchant les temps DMA/pilote entre les images.
La calibration doit refléter exactement l’inférence. La calibration et l’exécution partagent un format identique (letterbox, uint8, normalisation). Intégrer la normalisation dans le modèle élimine les bogues de prétraitement et évite une conversion float par image.
La résolution est un paramètre de déploiement de premier plan. Deux versions compilées à partir d’un seul modèle offrent un réglage net entre débit et qualité (vitesse ×3,6 pour ~0,095 d’IoU) sélectionnable au lancement. Aucun réentraînement.
Conclusion
Nous avons déployé une solution de segmentation en temps réel et à vocabulaire ouvert sur Hailo-10H avec YOLOE-26s-seg avec une séparation de modèle optimisée pour l’Edge. L’opérateur saisit une classe, le NPU la segmente, sans aucune reconstruction de modèle. Passer du modèle PyTorch à une démo fluide a nécessité une ingénierie rigoureuse. Nous avons appliqué une découpe de graphe et une quantification INT8 L4 adaptée. Enfin, l’alignement des tenseurs et le double-buffering asynchrone exploitent pleinement le débit du NPU.
La segmentation à vocabulaire ouvert sur du matériel à faible consommation s’applique partout où la liste de classes évolue plus vite qu’un cycle de réentraînement : la manutention d’objets en entrepôt et AMR, l’inspection agricole, le tri de défauts industriels et le prototypage rapide. La valeur ajoutée consiste à remplacer un réseau à classes fixes et sa boucle de réentraînement par un déploiement Edge unique piloté par du texte brut.
Le résultat est une pile embarquée complète et reproductible qui transforme un flux caméra unique en une vue de segmentation d’instances en direct et guidée par invites, à ~15,6 FPS (640px) ou ~31 FPS (320px), sur une plateforme à haute efficacité énergétique, la résolution et l’ensemble d’invites servant de réglages à l’exécution.
Estimation de profondeur monoculaire sur le Verdin i.MX95 Déploiement de l’estimation de profondeur monoculaire sur le Verdin i.MX95 pour l’IA embarquée : adaptation du modèle, quantification INT8 et une pile logicielle embarquée complète pour l’inférence de profondeur en temps réel à 30 FPS à partir d’une seule caméra RGB. L’estimation de profondeur monoculaire consiste […]
Démonstrations en direct au stand 4-642 (Hall 4) pour présenter des pipelines de vision par ordinateur avec l’IA en temps réel, optimisés pour les accélérateurs matériels embarqués et construits sur l’écosystème open source de l’IA. Nuremberg, Allemagne, le 6 mars 2026 – Savoir-faire Linux, une société leader dans le domaine de l’ingénierie logicielle open […]
Entraînement de YOLO pour la Détection Temps Réel sur Système LiDAR Il s’agit du deuxième volet d’une série en trois parties consacrée à la détection temps réel sur système LiDAR. Retrouvez ici l’article 1 sur la génération de jeux de données synthétiques de profondeur et NIR. Actuellement, de nouveaux LiDAR embarqués à basse résolution, tels […]
Permettre la Détection Temps Réel grâce à la Génération de Données LiDAR Synthétiques Cette série d’articles couvrira les éléments essentiels au déploiement d’un système de détection en temps réel avec un module dToF 3D LiDAR. Ce premier article est consacré à la génération de données LiDAR synthétiques. Génération de données LiDAR synthétiques pour la détection […]