195 views
--- title: INFO911 (TP3) Détection d'objets par réseau de neurones convolutionnels (YOLO) --- INFO911 (TP3) Détection d'objets par réseau de neurones convolutionnels (YOLO) === [TOC] > [name=Jacques-Olivier Lachaud][time=Novembre 2023][color=#907bf7] ###### tags: `info911` `tp` Retour à [INFO911 (Main) Traitement et analyse d'image](https://codimd.math.cnrs.fr/s/UE_B59gMy) [TOC] Ce TP vous fait découvrir une méthode moderne (2018) de détection d'objets dans une image, qui utilise l'apprentissage profond et les réseaux de neurones convolutionnels. Par détection d'objets, on effectue deux tâches: localiser les objets et les reconnaître. YOLO est l'acronyme de You Look Only Once. C'est une référence à d'autres méthodes de segmentation/détection d'objets qui demandent plusieurs réseaux ou plusieurs itérations pour résoudre le problème. Un exemple est Mask-R-CNN, qui demande beaucoup plus de calculs pour des résultats assez similaires (voir [Segmentation d’image et classification d'objets par réseau de neurones convolutionnels (Mask-R-CNN)](https://codimd.math.cnrs.fr/s/0fihmHphz)). Il s'agit de vous faire mettre en place un réseau de neurones existant, et de le tester. Vous n'aurez pas vraiment de code à écrire proprement dit. Les références suivantes peuvent être utiles: - [YOLO Object detection official openCV tutorial](https://opencv-tutorial.readthedocs.io/en/latest/yolo/yolo.html) - [YOLO Object detection unofficial tutorial, more explanations](https://pyimagesearch.com/2018/11/12/yolo-object-detection-with-opencv/) ## 1. Fichiers à récupérer - La description du réseau de neurones et des paramètres d'apprentissage : [yolov3.cfg](https://opencv-tutorial.readthedocs.io/en/latest/_downloads/10e685aad953495a95c17bfecd1649e5/yolov3.cfg) - L'ensemble de tous les paramètres du réseau une fois optimisé : [yolov3.weights](https://pjreddie.com/media/files/yolov3.weights) - les noms des objets/classes reconnus [coco.names](https://opencv-tutorial.readthedocs.io/en/latest/_downloads/a9fb13cbea0745f3d11da9017d1b8467/coco.names) Quelques images de tests: | | | | | | --- | ---- | ---- | ---- | | ![horse](https://codimd.math.cnrs.fr/uploads/upload_e31c4ad67f707ef72c91e845c4b8638b.jpg =x100) | ![city-1](https://codimd.math.cnrs.fr/uploads/upload_6a85161f30eaf76c3a5d8d53fb67c6d1.jpg =x100) | ![musician](https://codimd.math.cnrs.fr/uploads/upload_bb0924fcf2aa0403179012e789768ef4.jpg =x100) | ![chat-2](https://codimd.math.cnrs.fr/uploads/upload_742a46190c653f1cd1b408dc3d5a5e23.jpg =x100) | ## 2. Installation d'opencv pour python :::warning Vérifiez d'abord si vous avez OpenCV déjà installé en tapant `python3`, puis le code suivant. ```python= import cv2 as cv ``` S'il ne se plaint pas, c'est qu'OpenCV est déjà installé. ::: Normalement, la commande suivante suffit pour installer opencv. La théorie dit qu'il vaut mieux créer un environnement virtuel avant. ```shell pip install opencv-python ``` ## 3. Utilisation du réseau YOLO On commence par importer les modules nécessaires ```python import cv2 as cv import numpy as np import time import sys ``` Puis on charge tous les fichiers nécessaires ```python # charge l'image 'horse' ou une image donnée sur la ligne de commande filename = 'horse.jpg' if ( len( sys.argv ) > 1 ): filename = sys.argv[ 1 ] img = cv.imread( filename ) cv.imshow('window', img) cv.waitKey(1) # Load names of classes and get random colors classes = open('coco.names').read().strip().split('\n') np.random.seed(42) colors = np.random.randint(0, 255, size=(len(classes), 3), dtype='uint8') # Give the configuration and weight files for the model and load the network. net = cv.dnn.readNetFromDarknet('yolov3.cfg', 'yolov3.weights') net.setPreferableBackend(cv.dnn.DNN_BACKEND_OPENCV) ``` Il faut ensuite déterminer dans le réseau les couches de sorties. On voit que le réseau possède plus de 200 composants, mais seulement 3 en sortie. ```python # determine the output layer ln = net.getLayerNames() print(ln) Out = [i for i in net.getUnconnectedOutLayers()] ln_out = [] for j in range( len( Out ) ): print( Out[ j ] - 1, ' ', ln[ Out[ j ] - 1 ] ) ln_out.append( ln[ Out[ j ] - 1 ] ); print( ln_out ) ``` On transforme l'image en blob (un tableau adapté à la taille de l'entrée attendue par le réseau, ici image carré 416x416 avec valeurs entre 0 et 1). ```python # construct a blob from the image blob = cv.dnn.blobFromImage(img, 1/255.0, (416, 416), swapRB=True, crop=False) r = blob[0, 0, :, :] # si vous voulez l'afficher. cv.imshow('blob', r ) cv.waitKey(1) ``` Le réseau est juste une fonction de calcul. On place le blob en entrée, et on lance le calcul pour avoir la sortie. ```python # Analyse l'image en la mettant en entrée dans le réseau. net.setInput(blob) t0 = time.time() outputs = net.forward(ln_out) t = time.time() print('time=', t-t0) print(len(outputs)) for out in outputs: print(out.shape) ``` Tout le reste ne sert qu'à afficher de façon joli les résultats, qui sont organisés de manière précise (voir la doc donnée en lien au début). ```python def trackbar2(x): confidence = x/100 r = r0.copy() for output in np.vstack(outputs): if output[4] > confidence: x, y, w, h = output[:4] p0 = int((x-w/2)*416), int((y-h/2)*416) p1 = int((x+w/2)*416), int((y+h/2)*416) cv.rectangle(r, p0, p1, 1, 1) rdisplay = r.copy() text = f'Bbox confidence={confidence}' displayText( rdisplay, text, (20, 20), scale=1.0 ) cv.imshow('blob', rdisplay) # cv.displayOverlay('blob', text) r0 = blob[0, 0, :, :] r = r0.copy() cv.imshow('blob', r) cv.createTrackbar('confidence', 'blob', 50, 101, trackbar2) trackbar2(50) boxes = [] confidences = [] classIDs = [] h, w = img.shape[:2] for output in outputs: for detection in output: scores = detection[5:] classID = np.argmax(scores) confidence = scores[classID] if confidence > 0.5: box = detection[:4] * np.array([w, h, w, h]) (centerX, centerY, width, height) = box.astype("int") x = int(centerX - (width / 2)) y = int(centerY - (height / 2)) box = [x, y, int(width), int(height)] boxes.append(box) confidences.append(float(confidence)) classIDs.append(classID) indices = cv.dnn.NMSBoxes(boxes, confidences, 0.5, 0.4) if len(indices) > 0: for i in indices.flatten(): (x, y) = (boxes[i][0], boxes[i][1]) (w, h) = (boxes[i][2], boxes[i][3]) color = [int(c) for c in colors[classIDs[i]]] cv.rectangle(img, (x, y), (x + w, y + h), color, 2) text = "{}: {:.4f}".format(classes[classIDs[i]], confidences[i]) cv.putText(img, text, (x, y - 5), cv.FONT_HERSHEY_SIMPLEX, 0.5, color, 1) cv.imshow('window', img) cv.imwrite('output.jpg', img) cv.waitKey(0) cv.destroyAllWindows() ``` Voilà un exemple de résultat: ![output-city-1](https://codimd.math.cnrs.fr/uploads/upload_427ee46abf4536621bfebf2f4f2b7e85.jpg)