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:
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))
Pingback: La détection d'objets avec YOLO v4 - datacorner par Benoit Cayla
Pingback: YOLO v4 & Non Maxima Suppression (NMS) - datacorner par Benoit Cayla
Pingback: YOLO (Partie 4) Réduire le scope de détection - datacorner par Benoit Cayla
Pingback: YOLO (Partie 5) Créer son modèle avec YOLO : Préparation des données - datacorner par Benoit Cayla
Pingback: YOLO (Partie 6) Créer son modèle avec YOLO : Préparer le modèle - datacorner par Benoit Cayla
Bonsoir, je souhaiterais vous demander quelques choses concernant opencv