---
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:
| | | | |
| --- | ---- | ---- | ---- |
|  |  |  |  |
## 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:
