Mesure de distance stéréo avec YOLOv9

Cet article décrit l'implémentation de YOLOv9 pour la mesure de distance basée sur la vision stéréo. L'objectif est de détecter des objets et calculer leur profondeur en utilisant une configuration de caméras stéréo.

Confiugration de l'environnement

Pour exécuter ce projet, utilisez Python 3.8 avec PyCharm sous Windows. Le code de YOLOv9 est similaire à celui de YOLOv5, ce qui facilite l'adaptation.

Processus et principes de mesure

Flux de mesure

Le processus général comprend la calibration stéréo, la rectification, l'appariement stéréo, l'intégration avec YOLOv9, et le calcul de la profondeur. Voici les étapes clés :

  • Identifier dans le code source de détection d'objets la partie qui produit les coordonnées des boîtes englobantes.
  • Repérer dans le code de mesure stéréo le segment qui calcule la profondeur des objets.
  • Fusionner ces deux sections pour obtenir la profondeur au centre des boîtes de détection.
  • Ajouter la valeur de profondeur à l'affichage des classes d'objets détectés.

Note : Cette implémentation est optimisée pour des distances inférieures à 20 mètres. La précision dépend de la qualité de la calibration et des conditions d'éclairage, ainsi que des caractéristiques de la caméra.

Principes théoriques

Pour une explication détaillée des principes de la mesure stéréo, veuillez consulter des ressources spécialisées en vision par ordinateur.

Analyse du code

Paramètres de la caméra stéréo (fichier de configuration)

Les paramètres de calibration stéréo doivent avoir une erreur minimale, idéalement inférieure à 0.2. Voici un exemple de configuration :

import numpy as np

class ParametresStereo:
    def __init__(self):
        self.matrice_intrinseque_gauche = np.array([[1101.89299, 0, 1119.89634],
                                                     [0, 1100.75252, 636.75282],
                                                     [0, 0, 1]])
        self.matrice_intrinseque_droite = np.array([[1091.11026, 0, 1117.16592],
                                                     [0, 1090.53772, 633.28256],
                                                     [0, 0, 1]])
        self.distorsion_gauche = np.array([[-0.08369, 0.05367, -0.00138, -0.0009, 0]])
        self.distorsion_droite = np.array([[-0.09585, 0.07391, -0.00065, -0.00083, 0]])
        self.rotation = np.array([[1.0000, -0.000603116945856524, 0.00377055351856816],
                                  [0.000608108737333211, 1.0000, -0.00132288199083992],
                                  [-0.00376975166958581, 0.00132516525298933, 1.0000]])
        self.translation = np.array([[-119.99423], [-0.22807], [0.18540]])
        self.ligne_base = 119.99423

Section de mesure de distance

Une approche multi-thread est utilisée pour accélérer le calcul de la profondeur au centre des boîtes de détection. Voici un extrait du code intégré :

# Initialisation des paramètres et rectification
parametres = ParametresStereo()
carte1x, carte1y, carte2x, carte2y, Q_matrice = obtenirTransformationRectification(720, 1280, parametres)

for chemin, image, images_originales, capacite_video, etat in jeu_donnees:
    # Prétraitement et inférence
    with dt[0]:
        image_tenseur = torch.from_numpy(image).to(modele.device)
        image_tenseur = image_tenseur.half() if modele.fp16 else image_tenseur.float()
        image_tenseur /= 255
        if len(image_tenseur.shape) == 3:
            image_tenseur = image_tenseur[None]

    with dt[1]:
        predictions = modele(image_tenseur, augment=augmenter, visualiser=visualiser)

    with dt[2]:
        predictions = suppression_non_maximale(predictions, seuil_confiance, seuil_iou, classes, nms_agnostique, max_detections=max_detect)

    # Traitement des prédictions
    for i, detections in enumerate(predictions):
        seen += 1
        if webcam:
            p, im0, frame = chemin[i], images_originales[i].copy(), jeu_donnees.count
        else:
            p, im0, frame = chemin, images_originales.copy(), getattr(jeu_donnees, 'frame', 0)
        fil = MonThread(threading_stereo, args=(parametres, im0, carte1x, carte1y, carte2x, carte2y, Q_matrice))
        fil.start()

        if len(detections):
            detections[:, :4] = echanger_boites(image_tenseur.shape[2:], detections[:, :4], im0.shape).round()

            for *xyxy, confiance, classe in reversed(detections):
                if 0 < xyxy[2] < 1280:
                    centre_x = (xyxy[0] + xyxy[2]) / 2
                    centre_y = (xyxy[1] + xyxy[3]) / 2
                    xi = int(centre_x)
                    yi = int(centre_y)
                    if 0 < xi < 1280:
                        fil.join()
                        points_3d = fil.resultat()
                        coord_x = points_3d[yi, xi, 0] / 1000
                        coord_y = points_3d[yi, xi, 1] / 1000
                        coord_z = points_3d[yi, xi, 2] / 1000
                        distance = ((coord_x ** 2 + coord_y ** 2 + coord_z ** 2) ** 0.5)

                        if distance != 0:
                            etiquette = f'{noms[int(classe)]} {confiance:.2f} '
                            annotateur.ajouter_boite(xyxy, etiquette, couleur=couleurs(classe, True))
                            texte_profondeur = f"dist:{distance:.2f}m"
                            cv2.putText(im0, texte_profondeur, (int(xyxy[2] + 5), int(xyxy[1] + 30)),
                                        cv2.FONT_ITALIC, 1.2, (0, 255, 255), 3)

Code principal

Le script principal charge le modèle YOLOv9, initialise les caméras stéréo, et lance le processus de détection et mesure. Il intègre la logique ci-dessus dans une boucle de traitement des images ou vidéos.

Résultats expérimentaux

Des tests ont été effectués pour évaluer la précision de la mesure de distance dans différentes conditions. Les performances dépendent de la qualité de la calibration et de l'environnement d'éclairage.

Étiquettes: YOLOv9 vision stéréo mesure de distance Python OpenCV

Publié le 11 juin à 00h56