Détection d'Objets Multispectrale avec YOLOv11 : Guide de l'Initialisation des Données au Déploiement

La détection d'objets multispectrale joue un rôle pivot dans les applications modernes de vision par ordinateur, notamment dans des domaines spécialisés comme le monitoring agricole, la surveillance nocturne ou la télédétection environnementale. Le framework Ultralytics YOLOv11, une référence en détection d'objets, offre un support robuste pour le traitement des images multispectrales. Cependant, les développeurs qui intègrent YOLOv11 avec des données multispectrales rencontrent fréquemment des défis tels que des incompatibilités de canaux, des formats de données incorrects ou des erreurs de configuration du modèle. Ce guide propose une solution complète pour maîtriser les aspects fondamentaux de la détection d'objets multispectrale avec YOLOv11.

L'importance de la Détection Multispectrale

Les images multispectrales contiennent des informations spectrales bien plus riches que les données RVB traditionnelles à trois canaux. Cette abondance d'informations permet aux modèles de fonctionner sous diverses conditions d'éclairage, de pénétrer le brouillard ou la fumée, et même d'identifier des objets difficiles à distinguer avec des images RVB classiques. Grâce à son architecture flexible, YOLOv11 est capable de traiter efficacement des entrées multi-canaux, ouvrant de nouvelles opportunités pour la détection d'objets dans des scénarios complexes.

Préparation des Données : De l'RVB au Multispectral

Comprendre les exigences du format de données multispectrales

YOLOv11 requiert un format spécifique pour les données multispectrales. Contrairement aux images RVB conventionnelles, les images multispectrales comportent souvent plus de trois canaux. Dans le framwork Ultralytics, la logique de traitement des données multispectrales est principalement gérée dans ultralytics/utils/plotting.py, où une ligne spécifique (autour de la ligne 742) indique comment les images avec un nombre de canaux supérieur à trois sont gérées pour la visualisation :

elif c > 3:
    images_display = images[:, :3]  # Tronque les images multispectrales aux 3 premiers canaux pour affichage

Ce fragment de code montre que YOLOv11 utilise par défaut uniquement les trois premiers canaux pour la visualisation, mais le modèle peut effectivement traiter l'ensemble des canaux durant les phases d'entraînement et d'inférence.

Utilisation de l'outil de conversion officiel

Ultralytics fournit un outil de conversion dédié aux données multispectrales, situé dans ultralytics/data/converter.py. La fonction principale de cet outil est de transformer des images RVB standards en un format multispectral :

from ultralytics.data.converter import convert_to_multispectral as convertisseur_ms

# Conversion d'une image unique
chemin_image_rvb = "chemin/vers/image_originale.jpg"
convertisseur_ms(chemin_image_rvb, n_channels=9) # Exemple avec 9 canaux

# Traitement d'un ensemble de données complet
nom_du_dataset = "mon_jeu_de_donnees_test"
convertisseur_ms(nom_du_dataset, n_channels=9, replace=True)

Cette fonction utilise un algorithme d'interpolation de longueur d'onde pour générer le nombre de canaux spécifié dans la plage du spectre visible (450-700nm). La structure de l'ensemble de données après conversion se présente comme suit :

mon_jeu_de_donnees_test-multispectral/
├── images/
│   ├── train/          # Répertoire des images d'entraînement
│   └── val/            # Répertoire des images de validation
└── mon_jeu_de_donnees_test-multispectral.yaml  # Fichier de configuration du dataset multispectral

Analyse Approfondie du Fichier de Configuration : Prévenir les Incompatibilités de Canaux

Modèle de configuraton pour dataset multispectral

Une configuration correcte du fichier YAML est essentielle pour entraîner un modèle multispectral avec succès. En se basant sur un modèle comme coco8-multispectral.yaml, les paramètres clés incluent :

# Chemin du répertoire racine du dataset
path: mon_jeu_de_donnees_test-multispectral

# Chemins des images pour l'entraînement et la validation
train: images/train
val: images/val

# Nombre de classes
nc: 5 # Exemple avec 5 classes

# Nombre de canaux multispectraux (paramètre obligatoire)
channels: 9

# Liste des noms de classes
names:
  0: objet_A
  1: objet_B
  2: objet_C
  # ... autres classes

️ Important : Le paramètre channels doit impérativement correspondre au nombre réel de canaux dans vos données. Oublier de définir ce paramètre entraînera une configuration par défaut à 3 canaux et des erreurs de dimension lors de l'entraînement.

Stratégie de sélection et de configuration du modèle

YOLOv11 propose différentes tailles de modèles. Pour l'entraînement multispectral, une approche progressive est recommandée :

from ultralytics import YOLO

# 1. Utiliser une version 'nano' pour une validation rapide
modele_nano_ms = YOLO("yolo11n.pt")
modele_nano_ms.train(data="mon_jeu_de_donnees_test-multispectral.yaml", epochs=30, imgsz=640)

# 2. Une fois validé, passer à un modèle plus grand
modele_large_ms = YOLO("yolo11l.pt")
modele_large_ms.train(data="mon_jeu_de_donnees_test-multispectral.yaml", epochs=80, imgsz=640, batch=6)

Cas Pratiques : Résoudre les Problèmes Courants d'Entraînement

Cas 1 : Gestion des erreurs de dimension des canaux

Symptôme : Erreur ValueError: Expected input to be 3 channels, got 9 instead au démarrage de l'entraînement.

Cause principale : Le chargeur de données s'attend à une entrée à 3 canaux, mais les données réelles en possèdent 9 (dans notre exemple).

Solution :

import cv2
import numpy as np
import yaml # Pour charger le fichier YAML

# Charger une image multispectrale et vérifier sa dimension
image_ms_test = cv2.imread("chemin/vers/image_multispectrale_test.tif", cv2.IMREAD_UNCHANGED)
print(f"Forme de l'image: {image_ms_test.shape}")  # Devrait afficher (hauteur, largeur, 9)

# Vérifier la configuration YAML
with open("mon_jeu_de_donnees_test-multispectral.yaml", "r") as f_yaml:
    config_dataset = yaml.safe_load(f_yaml)
    assert config_dataset.get("channels") == image_ms_test.shape[2], "Le nombre de canaux dans le YAML ne correspond pas aux données !"

Cas 2 : Stratégies d'optimisation de la mémoire GPU

Scénario : Erreur de mémoire insuffisante lors de l'entraînement avec des images 640×640 à 9 canaux.

Optimisation :

# Ajuster les paramètres d'entraînement pour optimiser l'utilisation de la mémoire
modele_large_ms.train(
    data="mon_jeu_de_donnees_test-multispectrale.yaml",
    epochs=80,
    imgsz=640,
    batch=3,           # Réduire la taille du lot
    accumulate=5,      # Accumulation de gradient
    amp=True,          # Entraînement en précision mixte
    workers=1,         # Réduire le nombre de threads pour le chargement des données
    device=0,          # Utiliser un seul GPU
)

Cas 3 : Diagnostic d'indicateurs anormaux sur l'ensemble de validation

Problème : L'entraînement se déroule normalement, mais le mAP de validation reste constamment à 0.

Étapes de diagnostic :

  1. Vérifier que le format des données de validation est cohérent avec l'ensemble d'entraînement.
  2. S'assurer que le nombre correct de canaux est spécifié lors de la validation.
  3. Confirmer que les augmentations de données n'affectent pas négativement les caractéristiques multispectrales.

Code de correction :

# Spécifier explicitement le nombre de canaux lors de la validation
resultats_validation = modele_large_ms.val(
    data="mon_jeu_de_donnees_test-multispectral.yaml",
    channels=9,      # Spécifier clairement le nombre de canaux
    split="val",
    save_json=True    # Sauvegarder les résultats détaillés
)

Cas 4 : Problème de visualisation des résultats de prédiction

Symptôme : L'entraînement est correct, mais les images de prédiction affichent des anomalies.

Analyse des causes : Comme mentionné, dans ultralytics/utils/plotting.py, les images multispectrales sont tronquées aux trois premiers canaux pour la visualisation :

images_display = images[:, :3]  # Tronque les images multispectrales aux 3 premiers canaux pour affichage

Solution :

import cv2
from ultralytics.utils.plotting import Annotator as YOLOAnnotator

def predire_et_visualiser_ms(modele, chemin_image, num_canaux=9):
    """Fonction de prédiction pour images multispectrales avec visualisation"""
    
    # Charger l'image multispectrale originale
    image_entree = cv2.imread(chemin_image, cv2.IMREAD_UNCHANGED)
    
    # Utiliser tous les canaux pour la prédiction
    resultats_pred = modele(image_entree, channels=num_canaux)
    
    # Préparer l'image RVB pour la visualisation (utiliser les 3 premiers canaux si plus nombreux)
    image_rvb_pour_affichage = image_entree[:, :, :3] if image_entree.shape[2] > 3 else image_entree
    
    # Annoter l'image RVB avec les résultats
    image_annotee = resultats_pred[0].plot(img=image_rvb_pour_affichage)
    
    return image_annotee

# Exemple d'utilisation
# image_visualisee = predire_et_visualiser_ms(modele_large_ms, "chemin/vers/image_a_predire.tif", num_canaux=9)
# cv2.imwrite("prediction_multispectrale.jpg", image_visualisee)

Cas 5 : Problèmes de compatibilité avec les augmentations de données

Message d'erreur : AttributeError: 'NoneType' object has no attribute 'shape'

Méthode de résolution : Ajuster la stratégie d'augmentation des données pour éviter les opérations incompatibles ou préjudiciables aux caractéristiques spectrales :

modele_large_ms.train(
    data="mon_jeu_de_donnees_test-multispectral.yaml",
    epochs=80,
    imgsz=640,
    mosaic=0.3,        # Réduire la probabilité de mosaic
    mixup=0.0,         # Désactiver mixup (peut altérer les caractéristiques spectrales)
    hsv_h=0.01,        # Réduire l'amplitude des ajustements de teinte
    hsv_s=0.6,
    hsv_v=0.3,
    degrees=0.0,       # Désactiver les rotations (pour maintenir la cohérence spectrale)
    translate=0.05,
    scale=0.4,
    shear=0.0,
    perspective=0.0,
    flipud=0.0,
    fliplr=0.4,
)

Déploiement du Modèle et Optimisation pour la Production

Stratégie d'exportation de modèle multispectral

Lors de l'exportation d'un modèle multispectral, une attention particulière doit être portée à la configuration des canaux d'entrée :

# Exportation via l'interface en ligne de commande (CLI)
yolo export model=meilleur_modele.pt format=onnx dynamic=True channels=9

# Exportation via l'API Python
from ultralytics import YOLO

modele_final = YOLO("meilleur_modele.pt")
modele_final.export(format="onnx", dynamic=True, channels=9)

Bonnes pratiques pour le code d'inférence

En environnement de production, il est crucial de s'assurer que le format des données d'entrée est correct pour le modèle multispectral :

import cv2
import numpy as np
from ultralytics import YOLO

class MoteurInferenceMultispectral:
    def __init__(self, chemin_modele, num_canaux=9):
        self.modele = YOLO(chemin_modele)
        self.canaux_attendus = num_canaux
        
    def pretraiter_image(self, chemin_image_input):
        """Prétraitement d'une image multispectrale."""
        # Lecture de tous les canaux
        image_lue = cv2.imread(chemin_image_input, cv2.IMREAD_UNCHANGED)
        
        # Vérification du nombre de canaux
        if image_lue.shape[2] != self.canaux_attendus:
            raise ValueError(f"Attendu {self.canaux_attendus} canaux, reçu {image_lue.shape[2]} canaux pour {chemin_image_input}")
            
        # Normalisation
        image_lue = image_lue.astype(np.float32) / 255.0
        return image_lue
    
    def executer_prediction(self, chemin_image_input):
        """Exécute la prédiction sur une image donnée."""
        # Prétraitement
        image_preparee = self.pretraiter_image(chemin_image_input)
        
        # Inférence
        resultats_inf = self.modele(image_preparee, channels=self.canaux_attendus)
        
        # Post-traitement des résultats
        predictions_formatees = []
        for resultat_unique in resultats_inf:
            boites_detectees = resultat_unique.boxes.xyxy.cpu().numpy()
            confiances = resultat_unique.boxes.conf.cpu().numpy()
            ids_classes = resultat_unique.boxes.cls.cpu().numpy()
            
            predictions_formatees.append({
                "boites": boites_detectees,
                "confiances": confiances,
                "classes_ids": ids_classes
            })
            
        return predictions_formatees

# Exemple d'utilisation
moteur_inference = MoteurInferenceMultispectral("modele_multispectral_deploye.pt", num_canaux=9)
detections = moteur_inference.executer_prediction("image_a_tester_deployement.tif")
# print(detections)

Optimisation des Performances et Techniques Avancées

Intégration d'un mécanisme d'attention canal

Pour améliorer la performance des modèles multispectraux, l'intégration de mécanismes d'attention canal peut être bénéfique. Ceci se fait généralement en modifiant le fichier de configuration du modèle :

backbone:
  # [from, repeats, module, args]
  [[-1, 1, Conv, [64, 3, 2]],  # 0-P1/2
   [-1, 1, Conv, [128, 3, 2]],  # 1-P2/4
   [-1, 3, C2f, [128, True]],
   [-1, 1, Conv, [256, 3, 2]],  # 3-P3/8
   [-1, 6, C2f, [256, True]],
   [-1, 1, Conv, [512, 3, 2]],  # 5-P4/16
   [-1, 6, C2f, [512, True]],
   [-1, 1, Conv, [1024, 3, 2]], # 7-P5/32
   [-1, 3, C2f, [1024, True]],
   [-1, 1, SPPF, [1024, 5]],    # 9
   [-1, 1, CBAM, [1024]],        # Ajout du module d'attention CBAM (Convolutional Block Attention Module)
  ]

Visualisation et analyse des caractéristiques spectrales

Utilisez TensorBoard pour surveiller les activations des caractéristiques à travers les différents canaux spectraux :

import torch
from torch.utils.tensorboard import SummaryWriter

def enregistrer_activations_canaux(modele_entraine, chargeur_donnees, enregistreur_tb, etape_epoch):
    """Enregistre les statistiques d'activation par canal dans TensorBoard."""
    modele_entraine.eval()
    with torch.no_grad():
        for indice_lot, (images_batch, _cibles_batch) in enumerate(chargeur_donnees):
            # Accéder aux caractéristiques intermédiaires (par exemple, la 5ème couche du modèle)
            caracteristiques = modele_entraine.model[5](images_batch) 
            # Enregistrer l'histogramme d'activation pour chaque canal
            for idx_canal in range(caracteristiques.shape[1]):
                enregistreur_tb.add_histogram(
                    f"activations_canal_{idx_canal}",
                    caracteristiques[:, idx_canal, :, :],
                    etape_epoch * len(chargeur_donnees) + indice_lot
                )
            break  # Ne traiter que le premier lot pour l'exemple

# Exemple d'utilisation
# enregistreur_tensorboard = SummaryWriter("runs/experience_multispectrale")
# # Assurez-vous d'avoir un DataLoader 'chargeur_val' et un modèle 'modele_entraine'
# enregistrer_activations_canaux(modele_entraine, chargeur_val, enregistreur_tensorboard, epoch_actuelle)

Guide Avancé : Hyperspectral et Applications Personnalisées

Extension du multispectral à l'hyperspectral

Les images hyperspectrales, comportant des centaines de canaux spectraux continus, nécessitent des méthodes de traitement spécifiques :

  1. Réduction de dimension : Utilisation de PCA ou d'auto-encodeurs pour réduire le volume de données.
  2. Sélection de bandes : Choix des bandes spectrales les plus informatives.
  3. Normalisation spectrale : Gestion des variations de réponse spectrale entre différents capteurs.

Pipeline de traitement spectral personnalisé

import numpy as np

class PipelineSpectralPersonnalise:
    def __init__(self, bandes_choisies=None):
        self.bandes_selectionnees = bandes_choisies or list(range(9)) # Par défaut 9 bandes
        
    def traiter_donnees(self, donnees_hyperspectrales):
        """Traite les données hyperspectrales."""
        # 1. Sélection des bandes
        if self.bandes_selectionnees:
            donnees_traitees = donnees_hyperspectrales[:, :, self.bandes_selectionnees]
        else:
            donnees_traitees = donnees_hyperspectrales
        
        # 2. Normalisation spectrale
        donnees_traitees = self._normalisation_spectrale(donnees_traitees)
        
        # 3. Augmentation spatiale (si applicable)
        donnees_traitees = self._augmentation_spatiale(donnees_traitees)
        
        return donnees_traitees
    
    def _normalisation_spectrale(self, data_input):
        """Applique une normalisation spectrale."""
        # Soustraire la moyenne, diviser par l'écart-type par canal
        moyenne_canaux = np.mean(data_input, axis=(0, 1), keepdims=True)
        std_canaux = np.std(data_input, axis=(0, 1), keepdims=True)
        return (data_input - moyenne_canaux) / (std_canaux + 1e-8)
    
    def _augmentation_spatiale(self, data_input):
        """Applique des augmentations spatiales (exemple : recadrage aléatoire, retournement)."""
        # Des logiques d'augmentation spatiales plus complexes seraient ici
        return data_input # Pour cet exemple, renvoie juste les données

Récapitulatif et Meilleures Pratiques

Points clés à retenir

  1. La préparation des données est fondamentale : Utilisez l'outil convert_to_multispectral pour assurer un format de données correct.
  2. Le fichier de configuration doit être complet : Le paramètre channels est obligatoire dans le fichier YAML.
  3. Les paramètres d'entraînement doivent être ajustés : Optimisez la taille des lots et les stratégies d'augmentation pour les données multispectrales.
  4. Validation et prédiction cohérentes : Assurez-vous que l'entraînement, la validation et la prédiction utilisent la même configuration de canaux.
  5. Déploiement avec attention : Spécifiez clairement le nombre de canaux d'entrée lors de l'exportation du modèle.

Liste de contrôle pour l'optimisation des performances

  • Optimisation de la mémoire : Réduire la taille des lots, activer l'accumulation de gradient, utiliser l'entraînement en précision mixte.
  • Augmentation des données : Choisir des méthodes d'augmentation respectueuses des caractéristiques spectrales.
  • Sélection du modèle : Commencer avec un petit modèle pour validation, puis passer à un modèle plus grand.
  • Suivi et analyse : Utiliser TensorBoard pour surveiller les activations des canaux et l'évolusion de la perte.
  • Déploiement en production : Tester la performance d'inférence sur différentes configurations matérielles, optimiser le pipeline de prétraitement.

Questions Fréquemment Posées

Q1: Est-il possible d'initialiser un modèle multispectral avec un modèle RVB pré-entraîné ?

R: Oui, mais il est nécessaire de remplacer la première couche de convolution pour l'adapter au nombre différent de canaux d'entrée :

import torch.nn as nn
from ultralytics import YOLO
import torch

# Charger un modèle pré-entraîné (par exemple, YOLOv11 nano)
modele_yolo_rgb = YOLO("yolo11n.pt")

# Accéder et modifier la première couche de convolution
premiere_couche_conv_orig = modele_yolo_rgb.model.model[0].conv
nouvelle_couche_conv = nn.Conv2d(
    in_channels=9,           # Nombre de canaux multispectraux souhaité
    out_channels=premiere_couche_conv_orig.out_channels,
    kernel_size=premiere_couche_conv_orig.kernel_size,
    stride=premiere_couche_conv_orig.stride,
    padding=premiere_couche_conv_orig.padding,
    bias=premiere_couche_conv_orig.bias is not None
)

# Copier les poids des 3 premiers canaux du modèle RVB vers la nouvelle couche
with torch.no_grad():
    nouvelle_couche_conv.weight[:, :3, :, :] = premiere_couche_conv_orig.weight
    # Initialiser les poids des canaux supplémentaires avec une petite valeur aléatoire
    if nouvelle_couche_conv.in_channels > 3:
        nn.init.kaiming_normal_(nouvelle_couche_conv.weight[:, 3:, :, :], mode='fan_out', nonlinearity='relu')
    if premiere_couche_conv_orig.bias is not None:
        nouvelle_couche_conv.bias = premiere_couche_conv_orig.bias

modele_yolo_rgb.model.model[0].conv = nouvelle_couche_conv
# Le modèle 'modele_yolo_rgb' est maintenant prêt pour l'entraînement multispectral

Q2: Comment évaluer l'importance des différents canaux spectraux ?

R: Une méthode consiste à utiliser l'analyse de l'importance des caractéristiques, par exemple via les gradients :

import torch
import numpy as np

def analyser_importance_canaux(modele_entraine, chargeur_donnees):
    """Analyse l'importance de chaque canal par rapport au résultat de prédiction."""
    liste_importances = []
    
    modele_entraine.eval()
    for images_input, cibles_input in chargeur_donnees:
        images_input.requires_grad = True # Nécessaire pour calculer les gradients
        
        # Effectuer une passe avant
        # Assurez-vous que l'inférence utilise le bon nombre de canaux
        resultats_model = modele_entraine(images_input, channels=images_input.shape[1]) 
        
        # Pour cet exemple, nous utilisons la confiance moyenne des boîtes comme métrique de perte
        # Un scénario réel nécessiterait une perte plus robuste ou une métrique spécifique
        if len(resultats_model[0].boxes.conf) > 0:
            perte_simulee = resultats_model[0].boxes.conf.mean()
        else:
            continue # Passer si aucune détection
        
        # Rétropropagation pour obtenir les gradients
        perte_simulee.backward(retain_graph=True) # retain_graph peut être nécessaire selon le graph
        
        # Calculer l'importance du canal comme la moyenne absolue des gradients
        gradients_canaux = images_input.grad.abs().mean(dim=(0, 2, 3))
        liste_importances.append(gradients_canaux.cpu().numpy())
        
        break  # Analyser un seul lot est souvent suffisant pour une estimation
    
    if liste_importances:
        return np.mean(liste_importances, axis=0)
    return np.zeros(images_input.shape[1])

# Exemple d'utilisation
# importances_calculees = analyser_importance_canaux(modele_entraine, chargeur_val)
# print(f"Importance moyenne des canaux : {importances_calculees}")

Q3: Stratégies de déploiement de modèles multispectraux sur des dispositifs périphériques ?

R: Les solutions optimisées pour les dispositifs à ressources limitées incluent :

  1. Quantification du modèle : Réduction de la taille et du temps d'inférence du modèle par quantification en INT8.
  2. Élagage des canaux (Channel Pruning) : Suppression des canaux spectraux jugés moins importants, basée sur une analyse de leur contribution.
  3. Accélération matérielle : Utilisation de frameworks comme TensorRT ou OpenVINO pour optimiser l'inférence.
  4. Résolution dynamique : Ajustement de la taille de l'image d'entrée en fonction des capacités du dispositif.
# Exemple de quantification du modèle pour l'exportation ONNX
modele_final.export(
    format="onnx",
    dynamic=True,
    channels=9,
    int8=True,           # Quantification en INT8
    simplify=True,       # Simplification du graphe du modèle
    opset=17             # Version d'ONNX Opsets
)

Étiquettes: YOLOv11 ObjectDetection MultispectralImaging ComputerVision DeepLearning

Publié le 26 août à 20h57