Académique Documents
Professionnel Documents
Culture Documents
2023-24
Chapitre 1
Introduction aux traitements d'images
I. Lire une image avec Python
Indépendament du traitement à appliquer à une image, il est souvent nécessaire de lire et charger
l’image sur l’espace de travail, afin de pouvoir travailler dessus. C’est pourquoi, nous allons
apprendre, avec un exemple de code et une base d’images, comment lire une image avec python à
l’aide de la bibliothèque 𝑃𝑖𝑙𝑙𝑜𝑤.
La bibliothèque Pillow
𝑃𝑖𝑙𝑙𝑜𝑤 est le successeur de 𝑃𝐼𝐿 (Python Imaging Library) et a pour principale vocation le
traitement d’images avec python.
Installation de Pillow
L’installation est assez simple et pratiquement la même dans les différents systèmes
d’exploitations: '𝑝𝑖𝑝 𝑖𝑛𝑠𝑡𝑎𝑙𝑙 𝑝𝑖𝑙𝑙𝑜𝑤'. Pour plus de détails, nous vous invitons à consulter la page
officielle ( https://pillow.readthedocs.io/en/stable/installation.html).
1
Plus de 30 formats d’images peuvent être lues, selon la documentation officielle de Pillow
(https://pillow.readthedocs.io/en/stable/handbook/image-file-formats.html).
2 Image à prendre de la base de données https://www.kaggle.com/nitishabharathi/scene-classification , dont le but est la
Le code précèdent nous a permis de lire une image dans un emplacement précis, c’est à dire que
le chemin de l’image est bien précisé. Maintenant comment faire pour lire une image à partir de
n’importe quel emplacement?
Pour notre cas, nous proposons la boite de dialogue de la bibliothèque d’interfaces graphiques
𝑇𝐾𝑖𝑛𝑡𝑒𝑟 (https://docs.python.org/fr/3/library/tkinter.html), qui a la particularité d’être livré avec
l’installation de python, et donc aucune installation de plus ! Néanmoins, si ce n’est pas le cas un
simple 𝑝𝑖𝑝 𝑖𝑛𝑠𝑡𝑎𝑙𝑙 𝑡𝑘𝑖𝑛𝑡𝑒𝑟 fait l’affaire généralement !
Passons maintenant au code, là où il faut faire appel au module d’ouverture de boite de dialogue,
en l’important via 𝑡𝑒𝑟. 𝑓𝑖𝑙𝑒𝑑𝑖𝑎𝑙𝑜𝑔 𝑖𝑚𝑝𝑜𝑟𝑡 𝑎𝑠𝑘𝑜𝑝𝑒𝑛𝑓𝑖𝑙𝑒𝑛𝑎𝑚𝑒, le code python complet devient
alors :
• Cette fiche est dans un but pédagogique, et en python il y a beaucoup de partisans de minimum
de lignes la lecture de l’image avec la boite de dialogue peut se faire directement:
Formats d’images
𝑃𝑖𝑙𝑙𝑜𝑤 supporte plusieurs formats d’images : https://pillow.readthedocs.io/en/stable/handbook/image-file-
formats.html. Quand nous voulons enregistrer une image, il faut faire appel à la fonction 𝑠𝑎𝑣𝑒 de
l’image à enregistrer et spécifier le format voulu :
𝑖𝑚𝑎𝑔𝑒_𝑎_𝑒𝑛𝑟𝑒𝑔𝑖𝑠𝑡𝑟𝑒𝑟. 𝑠𝑎𝑣𝑒(′𝑛𝑜𝑚_𝑖𝑚𝑎𝑔𝑒. 𝑓𝑜𝑟𝑚𝑎𝑡′, ′𝑓𝑜𝑟𝑚𝑎𝑡′)
NB: si le format n’est pas spécifié, alors il va être déterminé du fichier de l’image, quand cela est
possible.
Une image lourde c’est une image qui prend beaucoup de places en mémoire, et ça a pour
conséquences un ralentissement dans les traitements, dans le chargement de site/blog et bien
d’autres … La solution est de réduire sa taille.
Réduire la taille de l’image avec python est parmi les opérations de bases de toutes les librairies
de traitement d’images, tel que : 𝑜𝑝𝑒𝑛𝑐𝑣 − 𝑝𝑦𝑡ℎ𝑜𝑛, 𝑠𝑖𝑐𝑘𝑖𝑡 − 𝑖𝑚𝑎𝑔𝑒, 𝑝𝑖𝑙𝑙𝑜𝑤 et bien d’autres. Dans
la suite, nous allons continuer avec 𝑃𝑖𝑙𝑙𝑜𝑤, pour montrer comment, avec le langage python,
compresser une image.
Nous voyons la réduction de la taille de l’image de deux manières:
1. Passer un format connu pour son pouvoir de compression et par conséquent conserver les
dimensions initiales de l’image
2. Ou réduire les dimensions de l’image: diviser l’image en 2, en 3 …
Utiliser un format de compression
Le JPEG est un format d’images, qui par définition cible la compression d’images. Réduire la taille
en sauvegardant l’image sous forme JPEG peut se relever efficace. Il faudra appeler la fonction 𝑠𝑎𝑣𝑒
et spécifier le format 𝐽𝑃𝐸𝐺, le code python sera alors:
#importer le package Image de la bibliothèque Pillow
from PIL import Image
#lire l'image
imageLue = Image.open("motif.bmp")
#sauvegarder l'image au format JPEG
imageLue.save("motif.jpg")
Après l’exécution du code, nous obtenons une nouvelle image JPEG de
37 ko de taille. C’est 62 fois plus inférieur à la taille de l’image originale
et la qualité à vue d’œil est restée la même.
JPEG et alpha
Sur certains formats d’images, nous trouvons le composant alpha (la transparence). Cette
dernière ne peut être transformée directement par le format JPEG et il faut absolument convertir au
système classique RGB, avant de sauvegarder au format JPEG. Le code suivant est la mise à jour du
précèdent, en s’assurant une conversion au format RGB avant la sauvegarde au JPEG.
#importer le package Image de la bibliothèque Pillow
from PIL import Image
#lire l'image
imageLue = Image.open("motif.bmp")
#convertir au format RGB
imageRGB = imageLue.convert("RGB")
#sauvegarder l'image au format JPEG
imageRGB.save("motif.jpg")
Réduire les dimensions de l’image
La fonction 𝑟𝑒𝑠𝑖𝑧𝑒 du module Image de la librairie 𝑃𝑖𝑙𝑙𝑜𝑤, va nous permettre de réduire la
dimension de l’image. Notez toutes fois que 𝑟𝑒𝑠𝑖𝑧𝑒 sert à redimensionner l’image dans les deux sens
: réduire ou augmenter selon la dimension choisit par rapport à la dimension d’origine. La fonction
𝑟𝑒𝑠𝑖𝑧𝑒 attend comme argument un tuple contenant la taille souhaitée (largeur x hauteur). Ici nous
voulons diviser la taille de l’image en 2, donc, nous allons commencer par récupérer la dimension
d’origine et la diviser en 2 dans la dimension compressée, et cela nous donne le code suivant:
#importer le package Image de la bibliothèque Pillow
from PIL import Image
imageLue = Image.open("motif.bmp")
#taille de l'image
dim=imageLue.size
#redimensionner
imageComp=imageLue.resize((int(dim[0]/2.),int(dim[1]/2.)))
#sauvegarder l'image réduite
MP-ISI2 FSM-Monastir A.U. 2023-24
imageComp.save("motif2.bmp")
#récupérer la dimension de l'image compressée
dimComp=imageComp.size
#Affichage des dimensions
print("Dimension d'origine: ",dim,"\nDimension compressée:",dimComp)
V. Manipulations d'histogrammes
Nous abordons ici les techniques de traitement d'images basées sur la modification
d'histogrammes. Ces méthodes font partie de la classe des traitements dits ponctuels : la valeur de
chaque pixel est corrigée, et ce indépendamment des autres pixels.
Qu'est-ce qu'un histogramme dans le domaine de l'imagerie numérique ?
L'histogramme d'une image numérique est une courbe statistique représentant la répartition de
ses pixels selon leur intensité. Pour une image en noir et blanc, il indique en abscisse le niveau de gris
(entier entre 0 et 255) et en ordonnée, le nombre de pixels ayant cette valeur.
Lorsque l'histogramme est normalisé, il indique en ordonnée la probabilité pi de trouver un pixel
de niveau de gris i dans l'image :
𝑛𝑜𝑚𝑏𝑟𝑒 𝑑𝑒 𝑝𝑖𝑥𝑒𝑙𝑠 𝑑′ 𝑖𝑛𝑡𝑒𝑛𝑠𝑖𝑡é 𝑖
∀𝑖 ∈ {0, ⋯ ,255}, 𝑝𝑖 =
𝑛𝑜𝑚𝑏𝑟𝑒 𝑡𝑜𝑡𝑎𝑙 𝑑𝑒 𝑝𝑖𝑥𝑒𝑙𝑠
L'intensité d'un pixel est alors vue comme une variable aléatoire discrète.
Un histogramme cumulé normalisé calcule le pourcentage de pixels ayant une valeur inférieure à
un niveau de gris donné :
𝑖
∀𝑖 ∈ {0, ⋯ ,255}, 𝑃𝑖 = ∑ 𝑝𝑘
𝑘=0
L'histogramme normalisé peut être interprété comme une densité de probabilité, et l'histogramme
cumulé normalisé comme la fonction de répartition.
La génération d'histogrammes se fait aisément en Python avec la fonction ℎ𝑖𝑠𝑡 de
𝑚𝑎𝑡𝑝𝑙𝑜𝑡𝑙𝑖𝑏. 𝑝𝑦𝑝𝑙𝑜𝑡 :
import matplotlib.pyplot as plt
import numpy as np
from PIL import Image
Nombre de pixels
Niveau de gris
Figure 1 : Image test et son histogramme
MP-ISI2 FSM-Monastir A.U. 2023-24
Il s'agit d'un outil très important en traitement d'images, car sa modification permet d'ajuster la
dynamique des niveaux de gris ou des couleurs dans une image afin de la rendre plus agréable
visuellement. Grossièrement, à gauche se situent les pixels noirs, à droite les pixels blancs, et au milieu,
toutes les nuances de gris.
Nous pouvons constater que l'image de la Figure 1 est trop sombre, ou sous-exposée, la majorité
des pixels se situent dans la partie gauche de l'histogramme, vers les valeurs de niveaux de gris faibles.
Inversement, les pixels de l'image trop claire, ou sur-exposée, se concentrent dans la partie droite
de l'histogramme.
Étirement d’histogrammes
Une première application consiste à corriger la luminosité, ou exposition, de l'image. Analysons la
forme des histogrammes pour des images dont l'exposition est mauvaise. Ainsi, pour corriger les
défauts liés à l’exposition d'une image, il suffit simplement d'étirer son histogramme : l'objectif est
d'étendre les valeurs des niveaux de gris de l'image mal exposée, majoritairement répartis dans un
sous intervalle [Imin,Imax]⊂[0,255] , à tout l'intervalle disponible. Cette transformation se fait
simplement à l’aide de la règle de trois : la valeur de chaque pixel est remplacée par le résultat de la
formule ci-dessous.
255 × (𝐼(𝑥, 𝑦) − 𝐼𝑚𝑖𝑛 )
𝐼 ′ (𝑥, 𝑦) =
𝐼𝑚𝑎𝑥 − 𝐼𝑚𝑖𝑛
où 𝐼(𝑥, 𝑦) et 𝐼 ′ (𝑥, 𝑦) désignent les intensités du pixel de coordonnées (x,y) respectivement dans
l'image mal exposée et la nouvelle image.
Concrètement, cette formule mathématique signifie qu'on déplace les niveaux de gris de
[𝐼𝑚𝑖𝑛 , 𝐼𝑚𝑎𝑥 ] vers [0, 𝐼𝑚𝑎𝑥 − 𝐼𝑚𝑖𝑛 ], avant de les répartir dans [0 , 255].
En pratique, 𝐼𝑚𝑖𝑛 et 𝐼𝑚𝑎𝑥 sont souvent déterminées comme les niveaux de gris minimum et
maximum de l'image après avoir éliminé 1% des valeurs extrêmes.
L'étirement d'histogramme se fait donc avec la fonction 𝒂𝒖𝒕𝒐𝒄𝒐𝒏𝒕𝒓𝒂𝒔𝒕 du module 𝑰𝒎𝒂𝒈𝒆𝑶𝒑𝒔
de 𝑷𝒊𝒍𝒍𝒐𝒘. On l'applique sur l'image sous-exposée de la Figure 1 (voir Figure 2).
Égalisation d’histogrammes
La deuxième application courante concerne l'amélioration du contraste de l'image. Le contraste
caractérise la répartition de lumière dans une image : plus une image est contrastée, plus la différence
de luminosité entre ses zones claires et sombres est importante. En général, une image peu contrastée
est terne, tandis qu'une image trop contrastée est visuellement "agressive". Dans les deux cas, l'image
manque de clarté car certains de ses détails seront peu, voire pas du tout, visibles.
L'égalisation d'histogrammes est une technique simple permettant de réajuster le contraste d'une
image et ainsi de lui redonner du dynamisme ou de l'adoucir. Pour comprendre de manière intuitive
le fonctionnement de ce traitement, étudions l'allure de l'histogramme pour des images peu
contrastées (voir Figure 1).
Comme vous pouvez le constater, les pixels des images dont le contraste est mauvais se répartissent
dans tout l'intervalle disponible – donc un étirement d'histogramme n'améliore pas le contraste de
manière équitable (voir Figure 2).
L'objectif est donc d'harmoniser la distribution des niveaux de gris de l'image, de sorte que chaque
niveau de l'histogramme contienne idéalement le même nombre de pixels. Concrètement, on essaye
d'aplatir au maximum l'histogramme original.
Pour cela, nous calculons d'abord l'histogramme cumulé normalisé de l'image, puis nous ajustons
la valeur de chaque pixel en utilisant la formule mathématique suivante :
𝐼(𝑥,𝑦)
𝐼 ′ (𝑥, 𝑦) = 255. ∑ 𝑝𝑖
𝑖=0
où 𝑝𝑖 désigne la probabilité qu'un pixel de l'image initiale soit d'intensité 𝑖.
Pour mieux comprendre cette formule, rappelez-vous que le but est de répartir les niveaux de gris
de l'image le plus équitablement possible sur [0,255]. Autrement dit, nous voulons imposer la loi de
probabilité 𝐼′(𝑥, 𝑦) comme étant uniforme discrète sur cet intervalle. Nous exploitons alors la loi de
𝐼(𝑥,𝑦 )
𝐼(𝑥, 𝑦) : sa fonction de répartition, donnée par ∑𝑖=0 𝑝𝑖 , est multipliée par 255. Si l’on considère
𝐼(𝑥, 𝑦) et 𝐼′(𝑥, 𝑦) comme des variables aléatoires continues, respectivement 𝐼𝑐(𝑥, 𝑦) et 𝐼′𝑐(𝑥, 𝑦), il
est prouvé mathématiquement que 𝐼′𝑐(𝑥, 𝑦) est uniformément distribuée sur [0,255].
L'égalisation d'histogramme correspond à la fonction 𝑃𝐼𝐿. 𝐼𝑚𝑎𝑔𝑒𝑂𝑝𝑠. 𝑒𝑞𝑢𝑎𝑙𝑖𝑧𝑒. L'application de
ce traitement sur l'image peu contrastée nous donne :
Néanmoins, si l'histogramme de l'image corrigée n'est visiblement pas plat, l'histogramme cumulé
normalisé montre que la fonction de répartition semble être linéaire comme celle de la loi uniforme :
Bien vu ! La transformation inverse nous assure une exploration de tous les pixels de l'image
transformée, mais il s'agit à nouveau d'une application qui va de ℕ2 dans ℝ2 . Le problème n'est donc
pas tout à fait résolu, puisque les coordonnées (𝑥′′, 𝑦′′) ne sont pas nécessairement entières !
Dans ce cas, comment déterminer l'intensité associée à un point de
coordonnées réelles dans l'image originale ?
En fait, il faut coupler la transformation inverse avec une méthode d'interpolation, qui va associer
à chaque couple de coordonnées dans l'image originale une valeur déterminée à partir des intensités
des pixels voisins. Il existe deux modes d'interpolation principaux :
L'interpolation au plus proche voisin : la nouvelle valeur est déterminée comme
l'intensité du pixel le plus proche. Il s'agit de la technique d'interpolation la plus simple et
la plus rapide, mais en contrepartie, elle offre une moins bonne qualité visuelle
L'interpolation bilinéaire : la nouvelle valeur est calculée à partir des intensités des
quatre pixels voisins
Le module 𝑃𝐼𝐿. 𝐼𝑚𝑎𝑔𝑒 contient les méthodes permettant d'appliquer les
transformations géométriques présentées. Leur paramètre d'entrée 𝑟𝑒𝑠𝑎𝑚𝑝𝑙𝑒
donne la possibilité de choisir le mode d'interpolation, et 𝑒𝑥𝑝𝑎𝑛𝑑 d'agrandir l'image
au cas où des pixels se trouvent en dehors des bornes après transformation.
Comparons les effets des interpolations au plus proche voisin et bilinéaire avec une rotation
(𝑃𝐼𝐿. 𝐼𝑚𝑎𝑔𝑒. 𝑟𝑜𝑡𝑎𝑡𝑒 , voir Figure 5) :
Figure 5 : Image test après rotation avec une interpolation au plus proche voisin (à
gauche) et une interpolation bilinéaire (à droite)
L'interpolation bilinéaire donne une image de bien meilleure qualité que l'interpolation au plus
proche voisin.
Figure 6 : Image test (à gauche) et Image altérée par un bruit gaussien (à droite)
Pour se débarrasse du bruit, plusieurs techniques de débruitage (ou lissage) ont été développées
afin d'atténuer le bruit dans une image. Le lissage par moyennage désigne la solution la plus intuitive.
Dans une image de bonne qualité, un pixel a généralement une intensité relativement similaire à
celle de ses voisins. Le lissage par moyennage consiste alors à remplacer la valeur de chaque pixel par
l'intensité moyenne de son voisinage. Le voisinage est défini par une fenêtre carrée de taille impaire,
centrée en le pixel à corriger. Le principe de cette méthode est schématisé ci-dessous :
MP-ISI2 FSM-Monastir A.U. 2023-24