YOLO (Partie 2) Utilisez YOLO avec OpenCV 6   Mise à jour récente !


Dans l’article La détection d’objets avec YOLO v4 nous avons vu l’implémentation de YOLO avec Darknet. L’idée de cet article est de nous simplifier les choses en utilisant l’implémentation de YOLO qui est fournie dans l’une des plus célèbre bibliothèque de vision par ordinateur: à savoir OpenCV. Et oui, au lieu de télécharger darknet comme nous l’avons fait, ou alors pourquoi pas implémenter YOLO « from scratch » il est possible d’utliser cette librairie pour directement utiliser YOLO … super n’est-ce pas ? alors allons-y …

Pré-requis

Dans cet article j’utiliserai Python dans Google Colab.

Vous devrez avoir néanmoins télécharger les 3 fichiers indispensables à YOLO:

  • La configuration (fichier cfg)
  • Les poids (weights)
  • La liste des labels (coco.names ici)

Je vous invite à vous référer à mon article La détection d’objets avec YOLO v4 où je vous explique comment récupérer et copier ces fichiers dans Google colab.

Une fois le notebook créé dans colab, importez les librairies comme suit:

Python
import numpy as np
import cv2
from google.colab.patches import cv2_imshow

Remarquez qu’on va devoir utiliser la fonction cv2_imshow à la place de cv2.imshow() car cette dernière n’est pas supportée dans colab.

Préparations

Nous allons maintenant définir les variables globales (permettant notament le paramétrage):

Python
ROOT_COLAB = '/content/drive/MyDrive/Colab Notebooks'
YOLO_CONFIG = ROOT_COLAB + '/YOLO/oc_data/'
COCO_LABELS_FILE = YOLO_CONFIG + 'coco.names'
YOLO_CONFIG_FILE = YOLO_CONFIG + 'yolov3.cfg'
YOLO_WEIGHTS_FILE = YOLO_CONFIG + 'yolov3.weights'
IMAGE_FILE = 'yoloimg.jpg'
IMAGE = cv2.imread(ROOT_COLAB + '/' + IMAGE_FILE)
CONFIDENCE_MIN = 0.5

La variable ROOT_COLAB est la racine de mes répertoires colab dans Google Drive, il vous faudra certainement l’adapter à votre environnement. J’ai bien sur téléchargé les fichiers YOLO et je les aie placé dans le répertoire /YOLO/oc_data/.

Afin d’afficher correctement – sans prendre tout votre écran – en retaillant et conservant les proportions de votre image, voici une petite fonction pratique :

Python
# Little function to resize in keeping the format ratio
# Cf. https://stackoverflow.com/questions/35180764/opencv-python-image-too-big-to-display
def ResizeWithAspectRatio(image, width=None, height=None, inter=cv2.INTER_AREA):
    dim = None
    image = image.copy()
    (h, w) = image.shape[:2]
    if width is None and height is None:
        return image
    if width is None:
        r = height / float(h)
        dim = (int(w * r), height)
    else:
        r = width / float(w)
        dim = (width, int(h * r))
    return cv2.resize(image, dim, interpolation=inter)
cv2_imshow(ResizeWithAspectRatio(IMAGE, width=700)) 

Nous allons maintenant lire la liste des libellés (classes) du fichier coco.names :

Python
with open(COCO_LABELS_FILE, 'rt') as f:
    labels = f.read().rstrip('\n').split('\n')

Puis nous allons créer un jeu de couleur aléatoire pour chaque classe/libellé :

Python
np.random.seed(45)
BOX_COLORS = np.random.randint(0, 255, size=(len(labels), 3), dtype="uint8")

Cela nous permettra, pour chaque classe d’avoir un cadre de couleur différente (pratique si nous avons beaucoup de détections dans la même image).

Initialisation de YOLO via OpenCV

Nous y voilà, nous allons maintenant initialiser le réseau YOLO (implémenté dans OpenCV) via la fonction readNetFromDarknet() :

Python
yolo = cv2.dnn.readNetFromDarknet(YOLO_CONFIG_FILE, YOLO_WEIGHTS_FILE)

On récupère ensuite la dernière couche (output) de sortie :

Python
yololayers = [yolo.getLayerNames()[i[0] - 1] for i in yolo.getUnconnectedOutLayers()]

Nous allons maintenant donner notre image au réseau de neurones, mais pour cela il nous faut la convertir en blob:

Python
blobimage = cv2.dnn.blobFromImage(IMAGE, 1 / 255.0, (416, 416),	swapRB=True, crop=False)
yolo.setInput(blobimage)

Détection des objets

Pour lancer le traitement, rien de plus simple:

Python
layerOutputs = yolo.forward(yololayers)

Et voilà le réseau de neurones a fait son travail: voyons comment nous allons récupérer/traiter ses résultats. Pour celà nous allons avoir besoin de 3 listes :

  • Une pour les cadres qui ont détecté des objets
  • Une pour les scores de confiance de ces objets/détection
  • Une pour le type d’objet (classe/libellé) détecté

YOLO nous renvoit en effet tout cela en vrac et nous allons devoir choisir ce que nous allons faire de toutes ses informations, par exemple un objet détecté sur un confiance de moins de 10% est-il intéressant ?

Nous allons devoir parcourir tous ces objets détectés et filtrer/retravailler les informations selon notre besoin:

Python
boxes_detected = []
confidences_scores = []
labels_detected = []
# loop over each of the layer outputs
for output in layerOutputs:
  # loop over each of the detections
  for detection in output:
    # extract the class ID and confidence (i.e., probability) of the current object detection
    scores = detection[5:]
    classID = np.argmax(scores)
    confidence = scores[classID]
    # Take only predictions with confidence more than CONFIDENCE_MIN thresold
    if confidence > CONFIDENCE_MIN:
      # Bounding box
      box = detection[0:4] * np.array([W, H, W, H])
      (centerX, centerY, width, height) = box.astype("int")
      # Use the center (x, y)-coordinates to derive the top and left corner of the bounding box
      x = int(centerX - (width / 2))
      y = int(centerY - (height / 2))
      # update our result list (detection)
      boxes_detected.append([x, y, int(width), int(height)])
      confidences_scores.append(float(confidence))
      labels_detected.append(classID)

Ci-dessus nous avons par exemple filtré sur le score de confiance minimum de CONFIDENCE_MIN. Nous avons aussi retravaillé les coordonnées de cadres détectés afin de pouvoir les afficher ultérieurement avec la fonction cv2.Rectangle d’OpenCV.

Attention aussi car les classes (libellés) renvoyés par YOLO sont les ID et non les noms usuels. il faudra utiliser le tableau initial pour les afficher:

Python
label_names = [labels[i] for i in labels_detected]
label_names
['laptop', 'cell phone']

Affichage du résultat

Aller j’arrête la le suspense ! nous allons afficher le résultat dans l’image en créan les cadres colorés (selon les classes) directement dans l’image source. On y rajoutera bien sur le taux de confiance de la détection :

Python
image = IMAGE.copy()
if nb_results > 0:
  for i in range(nb_results):
    # extract the bounding box coordinates
    (x, y) = (boxes_detected[i][0], boxes_detected[i][1])
    (w, h) = (boxes_detected[i][2], boxes_detected[i][3])
    # draw a bounding box rectangle and label on the image
    color = [int(c) for c in BOX_COLORS[labels_detected[i]]]
    cv2.rectangle(image, (x, y), (x + w, y + h), color, 2)
    score = str(round(float(confidences_scores[i]) * 100, 1)) + "%"
    text = "{}: {}".format(labels[labels_detected[i]], score)
    cv2.putText(image, text, (x, y - 5), cv2.FONT_HERSHEY_SIMPLEX, 0.5, color, 2)
  
cv2_imshow(ResizeWithAspectRatio(image, width=700))
Rapide et efficace n’est-ce pas ? n’hésitez pas à jouer aussi avec le paramètre de seuil de confiance CONFIDENCE_MIN. Vous vous rendrez d’ailleurs compte que sur des scores assez faible on peut avoir quelques surprises …

Les sources (Jupyter notebook) de cet article ici.

Lire la suite

Dans l’article suivant nous verrons comment utiliser la technique de NMS pour supprimer les détections en double.


A propos de Benoit Cayla

Ingénieur en informatique avec plus de 20 ans d’expérience dans la gestion et l’utilisation de données, Benoit CAYLA a mis son expertise au profit de projets très variés tels que l’intégration, la gouvernance, l’analyse, l’IA, la mise en place de MDM ou de solution PIM pour le compte de diverses entreprises spécialisées dans la donnée (dont IBM, Informatica et Tableau). Ces riches expériences l’ont naturellement conduit à intervenir dans des projets de plus grande envergure autour de la gestion et de la valorisation des données, et ce principalement dans des secteurs d’activités tels que l’industrie, la grande distribution, l’assurance et la finance. Également, passionné d’IA (Machine Learning, NLP et Deep Learning), l’auteur a rejoint Blue Prism en 2019 et travaille aujourd’hui en tant qu’expert data/IA et processus. Son sens pédagogique ainsi que son expertise l’ont aussi amené à animer un blog en français (datacorner.fr) ayant pour but de montrer comment comprendre, analyser et utiliser ses données le plus simplement possible.

Laissez un commentaire

Votre adresse e-mail ne sera pas publiée. Les champs obligatoires sont indiqués avec *

6 commentaires sur “YOLO (Partie 2) Utilisez YOLO avec OpenCV