Tuto Python & Pillow : traitement d'images

Table des matières

Introduction

  1. Quelques bibliothèques Python pour le traitement d’images

1.1. Pillow/PIL (Python Imaging Library)

1.2. NumPy

1.3. Scikit-image

1.4. OpenCV (Open Source Computer Vision Library)

1.5. OpenFace

1.6. SimpleITK

  1. Traitement d’images avec Pillow : Vos premiers pas !

2.1. Importer une image

2.2. Créer une image

2.3. Manipuler une image

  1. 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 présenter quelques bibliothèques Python les plus utilisées pour le traitement d’images. L'objectif n'est pas d'apprendre ces bibliothèques en détail, mais d'en apprendre suffisamment pour que vous soyez capables de choisir la bonne bibliothèque pour résoudre le projet qui vous intéresse. Ensuite, nous nous concentrerons sur Pillow, une bibliothèque qui est puissante, qui fournit plusieurs fonctionnalités de traitement d'images et qui est simple à utiliser.

Pillow est une bibliothèque basée sur la Python Imaging Library (PIL). PIL est une bibliothèque qui offre plusieurs procédures standards pour la manipulation d'images. C'est une bibliothèque puissante, mais elle n'a pas été mise à jour depuis 2011 et ne supporte pas Python 3. Pillow s'appuie sur cette bibliothèque, en ajoutant des fonctionnalités supplémentaires et en prenant en charge Python 3. Nous verrons comment effectuer diverses opérations sur les images telles que le recadrage, le redimensionnement, l'ajout de texte aux images, la rotation, les filtres…, en utilisant cette bibliothèque.

Maintenant vous pouvez commencer! Allons-y. 

1. Quelques bibliothèques Python pour le traitement d’images

Dans cette partie, nous présenterons quelques bibliothèques Python pour le traitement d'images. Ces bibliothèques fournissent un moyen facile et intelligent de transformer les images et de traiter leurs données. Aujourd'hui, les images contiennent une quantité importante de données. Cependant, elles doivent être traitées afin d'obtenir les informations nécessaires.

1.1. Pillow/PIL (Python Imaging Library)

Pillow est une bibliothèque open-source qui supporte de nombreuses fonctionnalités comme l'ouverture, la manipulation et la sauvegarde d'images avec différents formats.

1.2. NumPy 

NumPy est une bibliothèque fondamentale pour le calcul scientifique avec Python. Elle contient un puissant objet de type tableau (NumPy array), avec lequel vous pouvez stocker les pixels (valeurs) de l’image pour la traiter numériquement.

1.3. Scikit-image 

Scikit Image est une bibliothèque assez simple, destinée à la recherche, à l'éducation et aux applications industrielles. Elle dispose de nombreux algorithmes de segmentation, de manipulation des couleurs, de filtrage …. Pour en savoir plus, consultez le guide d'utilisation officiel de Scikit image.

1.4. OpenCV (Open Source Computer Vision Library) 

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 faire beaucoup de choses comme détecter et reconnaître des visages, identifier des objets et bien d'autres.

1.5. OpenFace

OpenFace est une bibliothèque open source qui vous permet de reconnaître les visages en utilisant des réseaux de neurones profonds.

1.6. SimpleITK

SimpleITK est une bibliothèque open source orientée en sciences biomédicales et qui dispose d'une large gamme d'outils permettant d'effectuer des analyses d'images.

Ces bibliothèques sont les meilleures bibliothèques de traitement d'images en Python. Vous pouvez facilement les importer et les utiliser dans vos propres projets. Dans la suite de ce tutoriel, nous présenterons la bibliothèque Pillow.

2. Traitement d’images avec Pillow : Vos premiers pas !

Passons à la pratique ! L'objectif est d'apprendre à manipuler les images en utilisant la bibliothèque Pillow et de comprendre les notions basiques avec un exemple concret. Avant de commencer, il faut choisir une image de test. Dans ce tutoriel, nous utiliserons une image téléchargée à partir de pixabay.

Une classe cruciale de la bibliothèque Pillow est la classe Image. Elle fournit une image PIL sur laquelle des opérations de manipulation peuvent être effectuées. Une instance de cette classe peut être créée de plusieurs façons : en chargeant des images à partir d'un fichier, en créant des images de toutes pièces ou en manipulant d'autres images. Nous verrons tout cela à l'usage dans le reste de ce tutoriel.

2.1. Importer une image

Tout d'abord, Importez la bibliothèque PIL et vérifiez sa version en utilisant l'attribut version :

Syntaxe:

import PIL
PIL.__version__

Ici, nous avons utilisé la version 7.0.0. Vous utilisez peut-être une version différente car nous l'avons mise à jour.

Remarque : Python fournit quelques fonctions intégrées pour nous aider à comprendre les fonctions et les objets qui sont disponibles dans les bibliothèques. Par exemple, la fonction d'aide help(), lorsqu'elle est appelée sur un objet, renvoie la documentation intégrée. Il y’a aussi la fonction appelée dir(), qui permet de lister le contenu d'un objet. Ces astuces de documentation permettent de fouiller facilement dans une bibliothèque inexplorée.

L'étape indispensable à tout traitement est le chargement de l'image. La commande Image.open() charge une image à partir d'un fichier, et renvoie une instance de la classe Image. Nous appellerons Image.open() et nous lui passerons le chemin vers le fichier image test.jpg. Cela devrait nous renvoyer un objet Image, que nous allons placer dans la variable image. En affichant l’image avec print(), vous aurez des informations sur l’image importée comme son mode (RGB) et sa taille :

Syntaxe:

from PIL import Image
from PIL import Image
# Importer l’image
image = Image.open( "/path/test.jpg" )
print(image)

Résultat d’exécution:

Vous pouvez accéder à ces informations (attributs de l’objet image) avec : image.format qui renvoie le format de l’image, image.mode qui renvoie le mode de l’image, image.size qui renvoie la taille etimage.width (resp. image.height) qui renvoie la largeur (resp. la hauteur) de l’image :

Syntaxe:

print( "Le format de l'image :", image.format )
print( "Le mode de l'image:", image.mode )
print( "La taille de l'image:", image.size )
print( "Largeur : {} pixel, hauteur : {} pixel".format( image.width, image.height) )

Résultat d’exécution:

Ensuite, créez une copie de votre image (image) pour la manipuler (modifier) sans changer l’originale en utilisant la fonction copy() de PIL et changer la taille de cette copie avec la fonction resize() :

Syntaxe:

# Créer une copie de l’image
img = image.copy()
# Changer la taille (largeur, hauteur)
size = (500, 500)
img = img.resize( size )

Remarque : La méthode resize() renvoie une image dont la largeur et la hauteur correspondent exactement aux valeurs choisies. Cela peut être ce que vous voulez, mais parfois vous pouvez trouver que les images retournées par cette fonction ne sont pas idéales (étirée ou écrasée). Cela est principalement dû au fait que la fonction ne tient pas compte du ratio d'aspect de l'image. Si vous souhaitez redimensionner les images et conserver leurs ratio d'aspect, vous devez utiliser la fonction thumbnail(). Cette fonction prend également un argument tuple à deux entiers représentant la largeur maximale et la hauteur maximale de l’image.

Vous avez importé une image, affiché ses caractéristiques et créé une copie de taille différente, mais vous vous demandez sûrement comment afficher l’image : Tapez le nom de l’image (vous pouvez aussi utiliser la fonction show()) :

Syntaxe:

Img

Résultat d’exécution:

2.2. Créer une image

Avec Pillow, vous pouvez également créer une nouvelle image vierge, dont vous pourriez avoir besoin à diverses fins, en utilisant Image.new() pour générer une image entièrement nouvelle :

Syntaxe:

# Créer une nouvelle image: Image.new( mode, taille, coleur )
new_image = Image.new('RGB', (500, 500), 'red')
new_image

Résultat d’exécution:

Vous pouvez également dessiner sur une image en utilisant le module ImageDraw. Vous pouvez dessiner des lignes, des points, des ellipses, des rectangles, des formes et du texte …. Pour dessiner un rectangle utilisez la fonction rectange() qui prend en paramètres 4 valeurs (x1 , y1, x2, y2) qui représentent la position du rectangle sur l’image. Pour se positionner, il faut savoir la taille de l’image (l’image que nous avons créée est de taille (500 pixels, 500 pixels)). C’est comme un repère (x, y) avec le point (0, 0) qui se situe en haut à gauche de l’image. Les deux premiers paramètres (x1, y1) représentent les coordonnées du point supérieur gauche du rectangle et les deux suivantes (x2, y2) représentent les coordonnées du point en bas à droite. Dessinons quelques rectangles sur l’image que nous avons créée :

Syntaxe:

from PIL import ImageDraw
# Créer un objet dessin
drawing_object = ImageDraw.Draw( new_image )
# Dessiner un rectangle plein
drawing_object.rectangle( (100, 150, 300, 350), fill = 'green', outline = None )
# Dessiner un rectangle vide
drawing_object.rectangle( (80, 130, 320, 370), fill = None , outline = 'black' )
new_image

Résultat d’exécution:

Nous pouvons également utiliser le module ImageDraw pour afficher du texte sur une image :

Syntaxe:

drawing_object = ImageDraw.Draw( new_image )
drawing_object.text( (110, 170), "Traitement d'image avec Pillow", fill = 'black')
new_image

Résultat d’exécution:

Vous pouvez aussi insérez une image dans une autre image avec la fonction paste() (il faut juste faire attention à la taille de l’image que vous souhaitez insérer) :

Syntaxe:

image_1 = Image.new('RGB', (500, 500),'gray')
image_2 = Image.new('L', (100,100))
# paste( image, position )
image_1.paste( image_2, (0,0) )
image_1.paste( image_2, (100,100) )
image_1.paste( image_2, (200,200) )
image_1.paste( image_2, (300,300) )
image_1.paste( image_2, (400,400) )
image_1

Résultat d’exécution:

2.3. Manipuler une image

Utilisons la copie (img) que nous avons créée de l’image importée, pour faire quelques manipulations.

2.3.1. Pivoter une image 

Vous pouvez faire pivoter les images avec Pillow en utilisant la méthode rotate(). Celle-ci prend un paramètre représentant le degré de rotation et renvoie un nouvel objet Image de l'image pivotée. Tournez l’image de 180 degrés et affichez le résultat :

Syntaxe:

img_180 = img.rotate(180)
img_180

Résultat d’exécution:

2.3.2. Recadrer une image

Pillow peut également être utilisé pour effectuer un recadrage de l'image avec la fonction crop(), c'est-à-dire que vous pouvez extraire un rectangle (nouvelle image) d'une image donnée en spécifiant ses coordonnées. Pour obtenir une paire de coordonnées, vous pouvez utiliser le module ImageDraw pour dessiner d’abord des rectangles et ainsi vérifier la zone.

Nous souhaitons extraire une image contenant que les lunettes de notre image test. Commençons d’abord par dessiner un rectangle autour des lunettes pour vérifier la zone :

Syntaxe:

# from PIL import ImageDraw
drawing_object = ImageDraw.Draw(img)
drawing_object.rectangle( (100, 150, 300, 350), fill = None, outline = 'red' )
img

Résultat d’exécution:

Ensuite, nous utilisons la fonction crop() qui permet de créer une image ne contenant que les pixels sélectionnés. À partir de notre copie de l’image test (img), nous allons générer une autre image (img_lunette) qui ne contient que les lunettes :

Syntaxe:

img_lunette = img.crop( (101, 151, 300, 350) )
img_lunette

Résultat d’exécution:

Affichons quelques informations sur l’image que nous avons générée.

Syntaxe:

print( "Le mode de l'image :", img_lunette.mode )
print( "La taille de l'image :", img_lunette.size )
print( "Largeur : {} pixel, hauteur : {} pixel".format( img_lunette.width, img_lunette.height) )

Résultat d’exécution:

2.3.3. Changer le Mode d’une image

Nous pouvons changer le mode de l’image avec la fonction convert(). Pour convertir l’image (img_lunette) en blanc et noir (mode L) nous utilisons le script suivant :

Syntaxe:

img_lunette_gray = img_lunette.convert('L')
img_lunette_gray

Résultat d’exécution:

2.3.4. Améliorer une image 

Nous pouvons ajuster la couleur (Color), la luminosité (Brightness), le contraste (Contrast) et la netteté (Sharpness) de notre image avec le module ImageEnhance. Nous allons créer plusieurs images avec différents contrastes à partir de img_lunette_gray:

Syntaxe:

from PIL import ImageEnhance
# Créer un objet enhance
enh = ImageEnhance.Contrast(img_lunette_gray)
# Générer une liste d’images
Images = []
for i in range(1,7):
images.append(enh.enhance(i))
# Afficher une seule image
images[2]

Résultat d’exécution:

Nous pouvons créer une image (tableau de plusieurs images) qui contient toutes les images générées à partir du script précédant :

Syntaxe:

image1=images[0]
# Créer une image avec la méthode new()
images_sheet = Image.new( 'L', (image1.width*3 , image1.height*2) )
x = 0
y = 0
# Parcourir les images dans la liste
for img in images:
# Ajouter chaque image avec la méthde past()
images_sheet.paste( img, (x, y) )
if x + image1.width == images_sheet.width:
x=0
y = y + image1.height
else:
x = x + image1.width
images_sheet

Résultat d’exécution:

2.3.5. Appliquer des filtres à une image

Le filtrage constitue un volet important en traitement d'images. Nous pouvons générer des filtres en utilisant le module ImageFilter pour créer des images filtrées. Nous utilisons la fonction filter() qui prend en paramètre le nom du filtre que nous souhaitons utiliser. Vous pouvez affichez tous les filtres avec la commande dir( ImageFilter). Pour générer une image avec le filtre CONTOUR à partir de l’image images_sheet, exécutez le script suivant :

Syntaxe:

from PIL import ImageFilter
images_sheet_CONTOUR = images_sheet.filter( ImageFilter.CONTOUR )
images_sheet_CONTOUR

 Résultat d’exécution:

2.3.6. Changer le format d’une image

Lorsque vous avez fini de traiter une image, vous pouvez l'enregistrer dans un fichier avec la méthode save() en passant le nom qui sera utilisé pour étiqueter le fichier. Lorsque vous enregistrez une image, vous pouvez spécifier une extension différente de celle de l'original et l'image enregistrée sera convertie dans le format spécifié. Sauvegardons notre image images_sheet_CONTOUR dans le format png :

Syntaxe:

images_sheet_CONTOUR.save( "/tmp/Contour_lunettes.png" )

3. Exercices

3.1. Exercice 1

Créer deux images avec la bibliothèque PIL pour représenter les drapeaux de l’Italie et la Russie (les drapeaux doivent avoir un contour noir).

3.2. Exercice 2

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

Syntaxe :

from skimage import data
import numpy as np
image = data.astronaut()
PIL_image = Image.fromarray( np.uint8(image) )
PIL_image

Résultat d’exécution:

Question 1: Dessiner un rectangle autour du visage et afficher l’image.

Question 2: Créer une nouvelle image contenant que le visage, appliquer le filtre FIND_EDGES et afficher le résultat.

4. Solution des exercices:

4.1. Exercice 1

Syntaxe :

from PIL import Image
from PIL import ImageDraw
darp_italie = Image.new('RGB', (300, 300), 'white')
drawing_object = ImageDraw.Draw( darp_italie )
drawing_object.rectangle( (0, 0, 100, 300), fill = 'green', outline = None )
drawing_object.rectangle( (200, 0, 300, 300), fill = 'red' , outline = None )
drawing_object.rectangle( (0, 0, 299, 299), fill = None , outline = 'black' )
darp_italie

Résultat d’exécution:

Syntaxe :

darp_russie = Image.new('RGB', (300, 300), 'white')
drawing_object = ImageDraw.Draw( darp_russie )
drawing_object.rectangle( (0, 100, 300, 200), fill = 'blue', outline = None )
drawing_object.rectangle( (0, 200, 300, 300), fill = 'red' , outline = None )
drawing_object.rectangle( (0, 0, 299, 299), fill = None , outline = 'black' )
darp_russie

Résultat d’exécution:

4.2. Exercice 2

Question 1:

Syntaxe :

from PIL import ImageDraw
drawing_object = ImageDraw.Draw(PIL_image)
drawing_object.rectangle((170,50,280,180), fill = None, outline ='red')
PIL_image

Résultat d’exécution:

Question 2:

Syntaxe :

from PIL import ImageFilter
img = PIL_image.crop((171,51,280,180))
img_FINDEDGES = img.filter( ImageFilter.FIND_EDGES )
img_FINDEDGES

Résultat d’exécution:

Conclusion

Nous sommes arrivés à la fin de ce tutoriel. J’espère qu’il vous sera utile à choisir la bonne bibliothèque python de traitement d’images qui s’adaptera à vos besoins et à vous initier au traitement d’images avec Pillow. Mais ne vous arrêtez pas là, il y a encore énormément de notions à apprendre au sujet de traitement d’images.

Merci d’avoir suivi le tutoriel et bon courage pour la suite.

Article publié le 21 Novembre 2020par Sami Nadif