Tuto Python & OpenCV : traitement d'images

Table des matières

Introduction

  1. Manipuler une image avec OpenCV

1.1. Importer une image

1.2. Histogramme des pixels

1.3. Image en nuance de gris

1.4. Détection des bords

1.5. Segmentation

  1. Détection d’objets
  2. Exercices

3.1. Exercice 1

3.2. Exercice 2

  1. Solution des exercices

4.1. Exercice 1

4.2. Exercice 2

Conclusion

Introduction

Dans ce tutoriel, nous allons vous initier au traitement d’images et à la détection des objets en utilisant la bibliothèque OpenCV. L'objectif n'est pas d'apprendre la bibliothèque en détail, mais d'en apprendre suffisamment pour que vous soyez capable de réaliser quelques manipulations de bases.

OpenCV est l’une des bibliothèques les plus utilisées pour les applications de vision par ordinateur (Computer Vision, en anglais). Cette bibliothèque est principalement utilisée pour construire des applications de vision par ordinateur et d'apprentissage automatique. Elle possède plusieurs algorithmes optimisés. Ces algorithmes peuvent réaliser beaucoup de tâches telles que la détection et la reconnaissance des visages, l’identification des objets et bien d'autres. Vous pouvez aussi l’utiliser pour manipuler une image dans le but d’extraire des informations.

Maintenant vous pouvez commencer! Allons-y. 

1. Manipuler une image avec OpenCV

Commençons par importer les bibliothèques avec lesquelles nous allons travailler. Importez OpenCV cv2matplotlib.pyplot (pour afficher les images) et la bibliothèque numpy. Vous pouvez consulter le tutoriel « Les bases de traitement d'images en Python : Bibliothèque NumPy », pour vous initier au traitement d’images avec NumPy.

Syntaxe:

import cv2
import matplotlib.pyplot as plt
import numpy as np

Avant de commencer, il faut choisir une image de test.

1.1.  Importer une image

Chargeons l’image à partir d’un fichier avec la fonction imread() de OpenCV (cv2). Nous appellerons cv2.imread() et nous lui passerons le chemin vers le fichier image test.jpg :

Syntaxe:

image = cv2.imread('/path/test.jpg')
# informations
print( 'classe :', type(image) )
print( 'type :', image.dtype )
print( 'taille :', image.shape )

Résultat d’exécution:

Remarquez que la fonction imread() génère un vecteur de classe numpy.ndarray contenant des valeurs de type uint8 (entier) de 3 dimensions.

Essayons maintenant d’afficher l’image avec matplotlib (vous pouvez afficher l’image avec la commande cv2.imshow(), mais dans ce tutoriel nous préférons utiliser la bibliothèque matplotlib pour afficher les images) :

Syntaxe:

plt.imshow(image)
plt.show()

Résultat d’exécution:

Remarquez que les couleurs de cette image sont complétement différentes de l’image originale, pourquoi ?

La commande plt.imshow() affiche une image en mode RGB (rouge, vert, bleu), par contre la fonction imread() renvoie une image en mode BGR (bleu, vert, rouge). Il faut donc réorganiser les canaux de l’image pour pouvoir l’afficher correctement. Nous utilisons la fonction split() de OpenCV qui renvoie un vecteur numpy de 2 dimensions pour chaque canal et la fonction merge() pour les réassembler dans le bon ordre :

Syntaxe:

b, g, r = cv2.split(image)
image = cv2.merge([r, g, b])
plt.imshow(image)
plt.show()

Résultat d’exécution:

Finalement, vous pouvez enregistrer le vecteur numpy sous forme de fichier image avec la méthode imwrite() de cv2. Le format de l’image sauvegardé est automatiquement déterminé à partir de l'extension :

cv2.imwrite('/path/visages.png', image)

Passons à la pratique ! L'objectif est d'apprendre à manipuler les images en utilisant la bibliothèque OpenCV et de comprendre des notions basiques avec un exemple concret.

1.2. Histogramme des pixels

Nous affichons l'histogramme de notre image qui représente la répartition des pixels selon leur intensité. Nous utiliserons la fonction calcHist() de cv2 qui prend l'image originale, canal (RGB), le masque (None), le nombre d’intervalles (bins = 256), et l’intervalle de pixel en paramètres. Le script est le suivant :

Syntaxe:

hist_image = cv2.calcHist([image],[1],None,[256],[0,256])
plt.plot(hist_image)
plt.show()

Résultat d’exécution:

1.3.  Image en nuance de gris

Pour convertir une image couleur en une image en nuance de gris, utilisez la fonction cvtColor() de cv2 qui prend l'image originale et l'attribut COLOR_RGB2GRAY en paramètres. Le script est le suivant :

Syntaxe:

grey_image = cv2.cvtColor(image, cv2.COLOR_RGB2GRAY)
plt.imshow(grey_image, cmap = 'Greys_r')
plt.show()
print("taille de l'image en nuance de gris :", grey_image.shape)

 Résultat d’exécution:

1.4.  Détection des bords

Pour détecter les bords dans une image, vous pouvez utiliser la fonction Canny() de cv2 qui implémente le détecteur de bords Canny qui prend l'image originale et deux autres valeurs (val1, val2) en paramètres. Le script suivant implémente le détecteur de bords Canny avec différentes valeurs et affiche le résultat :

Syntaxe:

fig, axs = plt.subplots(nrows=3, ncols=3, figsize=(15,8))
# Liste des valeurs
Val1 = [50, 100, 400]
Val2 = [50, 100, 400]
for i, val1 in enumerate(Val1):
for j, val2 in enumerate(Val2):
edge_image = cv2.Canny(image, val1 , val2)
axs[i][j].imshow(edge_image, cmap = 'Greys_r')
axs[i][j].set_title((val1,val2))
axs[i][j].set_axis_off()

Résultat d’exécution:

Le traitement d’images joue un rôle essentiel dans la détection des objets. Comme vous pouvez le voir, la partie de l'image qui contient des objets, qui, dans ce cas représente deux visages, a été pointillée/séparée grâce à la détection des bords.

1.5.  Segmentation

Le but de la segmentation des images est d'étiqueter chaque pixel d'une image avec une classe correspondante. Il y a plusieurs façons de faire cela, la plus simple consiste à segmenter l’image en deux régions (image binaire). Cela revient à convertir l'image en nuance de gris, et fixer un seuil. Les pixels dont la valeur est supérieure au seuil sont traités comme appartenant à la première région, et les autres pixels (inférieur au seuil) à la deuxième région. Pour réaliser une segmentation binaire avec OpenCV nous utilisons la fonction threshold() qui prend l'image originale, le seuil, la valeur maximale des pixels et l'attribut THRESH_BINARY en paramètres. Dans le script suivant, nous utilisons trois seuils 50, 100 et 150 :

Syntaxe:

fig, axs = plt.subplots(nrows=1, ncols=3, figsize=(15,8))
# Transformer l’image en nuance de gris
grey_image = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
# Les seuils de la segmentation
seuils = [50, 100, 150]
for i, seuil in enumerate(seuils):
val , seg_image = cv2.threshold(grey_image, seuil, 255, cv2.THRESH_BINARY)
axs[i].imshow(seg_image, cmap = 'Greys_r')
axs[i].set_title(seuil)
axs[i].set_axis_off()

 Résultat d’exécution:

Comme vous pouvez le voir, dans les images résultantes, deux régions ont été établies : la région noire (valeur de pixel 0) et la région blanche (valeur de pixel 255).

2. Détection d’objets

Un algorithme de reconnaissance d'images (appelé classificateur d'images) prend une image (ou un morceau d'image) en entrée et essaye de prédire ce que l'image contient. En d'autres termes, la sortie est une étiquette de classe (par exemple "visage", "chat", "table", etc.). OpenCV contient des mécanismes permettant de faire de la détection d’objets. La technique utilisée est basée sur Haar cascade, qui est une approche d'apprentissage par machine. Vous pouvez créer votre propre classificateur HaaR pour n'importe quel objet, tout ce dont vous auriez besoin est de plusieurs images pour lancer un cycle d’apprentissage et ainsi générer votre propre classificateur HaaR. Pour ce tutoriel, nous allons traiter OpenCV comme une boîte noire et nous allons utiliser un classificateur disponible publiquement sur OpenCV spécialisé dans la détection des visages dans une image.

La première étape consiste à charger le classificateur de détection de visages :

Syntaxe:

haar_file = 'haarcascade_frontalface_default.xml'
face_cascade = cv2.CascadeClassifier( cv2.data.haarcascades + haar_file )

Nous voulons maintenant essayer de détecter les deux visages dans l’image. Pour effectuer cette tache, faut d’abord convertir l’image test en nuance de gris, puis utiliser le classificateur que nous avons importé. Plus précisément, nous allons utiliser la fonction . Cette fonction renvoie un vecteur NumPy qui contient pour chaque visage détecté une liste contenant 4 paramètres : (x, y, w, h) où x et y désignent le point supérieur gauche de l'image et w la largeur et h la hauteur. Le script est le suivant :

Syntaxe:

# Image en nuance de gris
grey_image = cv2.cvtColor(image, cv2.COLOR_RGB2GRAY)
faces = face_cascade.detectMultiScale(grey_image)
print("Nombre de visages détecté dans l’image: {0}".format(len(faces)))

Résultat d’exécution:

Vous pouvez afficher les paramètres du premier visage détecté. Le script suivant renvoie la liste suivante [236, 278, 615, 615].

Syntaxe:

position_list = faces.tolist()
print( position_list[0] )

Maintenant, nous souhaitons dessiner des rectangles autour des visages détectés. Pour réaliser cette tâche, vous pouvez utiliser la fonction rectangle() de cv2 :

Syntaxe:

for (x, y, w, h) in position_list:
cv2.rectangle(image, (x, y), (x + w, y + h), color = 255)
plt.imshow(image)
plt.show()

 Résultat d’exécution:

Finalement, enregistrez l’image avec OpenCV :

Syntaxe:

cv2.imwrite('/path/visages.jpg', img)

3. Exercices

Pour les exercices, nous allons utiliser une image générée à partir de la bibliothèque Scikit Image. Le script est le suivant:

Syntaxe:

from skimage import data
import matplotlib.pyplot as plt
import numpy as np
image = data.astronaut()
plt.imshow(image)
plt.show()

Résultat d’exécution:

3.1. Exercice 1 :

Question : Générer une image pour chaque canal (rouge, vert, bleu), puis effectuer des segmentations avec les trois seuils suivant : [100, 150, 200]. Afficher le résultat.

3.2. Exercice 2 :

Question : Détecter les yeux dans l’image avec OpenCV et dessiner un contour autour. Vous pouvez utiliser le classificateur de détection des yeux suivant : 'haarcascade_eye_tree_eyeglasses.xml'.

4. Solution des exercices

4.1. Exercice 1 :

Syntaxe:

Import cv2
Import matplotlib.pyplot as plt
fig, axs = plt.subplots(nrows=3, ncols=3, figsize=(14,8))
# Une image pour chaque canal
list_RGB = cv2.split(image)
# Les seuils de la segmentation
seuils = [100, 150, 200]
for i, seuil in enumerate(seuils):
for j, img in enumerate(list_RGB):
val , seg_image = cv2.threshold(img, seuil, 255, cv2.THRESH_BINARY)
axs[j][i].imshow(seg_image, cmap = 'Greys_r')
axs[j][i].set_title( seuil )
axs[j][i].set_axis_off()

Résultat d’exécution:

4.2. Exercice 2 :

Syntaxe:

import cv2
# Classificateur des yeux
haar_file = 'haarcascade_eye_tree_eyeglasses.xml'
eye_cascade = cv2.CascadeClassifier( cv2.data.haarcascades + haar_file )
# image en nuance de gris
img_gray = cv2.cvtColor( image, cv2.COLOR_RGB2GRAY)
eyes = eye_cascade.detectMultiScale( img_gray )
print("Nombre de yeux détecté dans l’image: {0}".format(len(eyes)))
position_list = eyes.tolist()
for (x, y, w, h) in position_list:
cv2.rectangle(image, (x, y), (x + w, y + h), color = 255)
plt.imshow(image)
plt.show()

Résultat d’exécution:

Conclusion

Nous sommes arrivés à la fin de ce tutoriel. Nous espérons qu’il vous sera utile pour vous initier au traitement d’images et à la détection d’objet avec la bibliothèque OpenCV. Cependant, ce tutoriel ne représente qu’une surface de ce qu’on peut faire avec OpenCV, il existe encore énormément de notions à apprendre. Vous serez surpris de ce que vous pouvez faire avec Jupyter notebook, la bibliothèque OpenCV et une simple webcam.

Merci d’avoir lu et bon courage pour la suite.

Article publié le 21 Novembre 2020par Sami Nadif