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 …
Index
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:
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):
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 :
# 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 :
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é :
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() :
yolo = cv2.dnn.readNetFromDarknet(YOLO_CONFIG_FILE, YOLO_WEIGHTS_FILE)
On récupère ensuite la dernière couche (output) de sortie :
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:
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:
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:
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:
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 :
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))

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.
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.
Bonsoir, je souhaiterais vous demander quelques choses concernant opencv