Travaux Pratiques N°4 : Traitement d’image
Hanen Jemal Abid
Objectifs :
o Connaître la représentation en mémoire d’une image
numérique
o Savoir ouvrir, retailler, afficher et sauvegarder une image
o Savoir manipuler une image au niveau du pixel
o Approfondir la notion de programmation
orienté objet
o Apprendre à documenter son code
C’est une introduction au traitement d’images pour présenter la notion d’image et effectuer des
opérations simples d’analyse.
L’image
Une image est une matrice MxN de valeurs entières prises sur un intervalle borné [0,Ng] où Ng
est la valeur maximale de couleur.
p(i,j) est le niveau de nuance de couleur du pixel de coordonnées ligne i et colonne j dans
l'image. p(i,j) ∈[0, Ng]. Les valeurs sont des entiers.
1
Image binaire
Une image binaire est une image MxN où chaque point peut prendre uniquement la valeur 0 ou
1. Les pixels sont noirs (0) ou blancs (1). Dans ce cas, Ng = 2 et la relation sur les niveaux de
gris devient: p(i,j) = 0 ou p(i,j) = 1.
Image en niveau de gris
Une image en niveaux de gris autorise un dégradé de gris entre le noir et le blanc. En général,
on code le niveau de gris sur un octet (8 bits) soit 256 nuances de dégradé. L'expression de la
valeur du niveau de gris avec Ng = 256 devient: p(i,j) ∈[0, 255].
Image couleur
Une image couleur est la composition de trois images en niveaux de gris sur trois composantes.
On définit donc trois plans de niveaux de gris, un rouge, un vert et un bleu. La couleur finale
est obtenue par synthèse additive des ces trois (ou plus) composantes.
On a les relations sur les niveaux de gris: p_R(i,j) ∈[0, 255], p_V(i,j) ∈[0, 255], p_B(i,j) ∈[0,
255]. On voit bien sur la figure qu'une image couleur est en fait l'association de trois plans de
niveau de gris, chacun d'eux étant une couleur de base.
Le traitement d’image
Le traitement d’image consiste donc à analyser et à manipuler une image numérique
principalement dans le but d’en améliorer la qualité ou d’en extraire des informations qui
pourraient ensuite être utilisées.
Les tâches courantes du traitement d’image incluent l’affichage des images, les manipulations
basiques comme le recadrage, le retournement, la rotation, ou encore la segmentation, la
classification et les extractions de caractéristiques, la restauration et la reconnaissance
d’images.
Voici quelques-unes des bibliothèques Python les plus couramment utilisées pour les tâches
de manipulation d’images.
Scikit Image, OpenCV-Python, Pgmagick, Mahotas….
2
Dans ce Tp on va utiliser Python pour le traitement d’images et nous utiliserons le
module PIL dont la documentation se trouve à l’adresse suivante :
https://pillow.readthedocs.io/en/stable/
Python Imaging Library (PIL)
Pillow est une bibliothèque de traitement d’image, qui est un fork et successeur du
projet PIL (Python Imaging Library). Elle est conçue de manière à offrir un accès rapide aux
données contenues dans une image, et offre un support pour différents formats de fichiers tels
que PPM, PNG, JPEG, GIF, TIFF et BMP.
Pillow dispose de capacités de traitement d’images relativement puissantes, et a pour but
d’offrir une solide base à toute application générale de traitement d’images.
Connaissances et savoir-faire à maîtriser
1) Encodage d’une image
Une image numérique est encodé sous la forme d’un tableau à deux dimensions dans
lequel chaque élément est appelé un pixel (pour PICture ELement). On accède à un pixel
de cette matrice par ses coordonnées définies dans un repère dont l’origine est le coin
supérieur gauche de l’image.
3
2) Espace colorimétrique d’une image
Une image possède un attribut appelé mode qui définit son espace colorimétrique (on
parle aussi de profondeur de couleur), autrement dit, la façon dont la couleur est
encodée au niveau de chaque pixel. On crée une nouvelle image via l’instruction :
a. Le mode 1 noir et blanc
Dans ce mode la couleur de chaque pixel est codé sur 1 bit (8 bits = 1 octet) qui vaut
soit 0 (noir), soit 1 (blanc). On affecte une couleur au pixel avec la méthode putpixel
où (x, y) représente les coordonnées du pixel à coloriser
b. Le mode L niveaux de gris
La couleur prend toutes les valeurs entières qu’il est possible d’encoder sur un octet, soit
256 valeurs qui vont du noir "total" (0) au blanc "pur" (255) en passant par tous les
niveaux de gris :
Remarque 1 : il existe par conséquent 256 niveaux de gris différents.
c. Le mode couleur RGB
La couleur est ici encodée sur trois octets soit 3 × 8 = 24 bits (un octet par couleur, on
peut dire aussi par canal). Chaque "teinte" de rouge, de vert ou de bleu prend une valeur
entière entre 0 et 255. La couleur finale est la synthèse additive des trois canaux.
Remarque 2 : on peut ainsi encoder plus de 16 millions de couleurs (2563 = 16 777 216).
Remarque 3 : il existe d’autres modes comme par exemple le mode RGBA dans lequel un
pixel est encodé avec un octet supplémentaire, ce qui permet de gérer un effet de
transparence ( A pour canal alpha).
4
d. En résumé
Mode Pixel sur . . . Couleurs disponibles Poids en mémoire en octets
1 1 bit 2 (noir ou blanc) N /8
L 8 bits = 1 octet 256 niveaux de gris N
(N = nombre de pixels de l’image)
Lecture, affichage d’une image
5
Ouvrir, manipuler et sauver une image
Les manipulations au niveau du pixel se font à travers les deux méthodes d’images
getpixel et putpixel. La première permet de récupérer le code RGB d’un pixel en
indiquant ses coordonnées en paramètre sous forme d’un 2-tuple (x, y). La deuxième
modifie la couleur d’un pixel, elle prend les coordonnées (x, y) du pixel à modifier et
le code (r, g, b) à lui attribuer. Dans l’exemple suivant, on modifie le pixel en ne
gardant que sa composante de rouge, on dit qu’on filtre le rouge.
from PIL import Image # importation du module
img = Image.open("fleur.jpg") # ouvre une image
(r, g, b) = img.getpixel((20, 35)) # récupère le code RGB du pixel dans
un tuple
img.putpixel((20, 35), (r, 0, 0)) # change la couleur du pixel
img.show() # affiche l'image sans l'enregistrer dans l'editeur par défaut
Retailler une image
La méthode d’image resize permet de changer les dimensions d’une image. Il
faudra toutefois veiller à conserver ses proportions (on parle de ratio) afin d’éviter toute
déformation :
img.resize((int(img.width / 2), int(img.height / 2))) # échelle 1/2
img.save("perroquet.png", "PNG") # enregistre l'image au format PNG
La classe Image en détail
On a déjà vu dans un précédent TP qu’un langage comme Python manipule les données
sous forme d’objets. Une chaîne de caractère par exemple est un objet de type string qui
possède des méthodes permettant d’agir sur celui-ci. Un objet possède aussi des attributs
: ce sont des propriétés qui décrivent l’état de cet objet. Deux objets Image différents vont
avoir des propriétés différentes mais vont être "construits" suivant le même modèle. Ce
modèle qui sert à créer un objet s’appelle une classe d’objet. Ci-dessous une brève
description de la classe Image :
6
+ +
| Classe Image |
+ +
| ATTRIBUTS : |
| |-- filename : nom de l'image |
| |-- format : son encodage (jpg, png, ...) |
| |-- mode : mode couleur (1, L, RGB, ...) |
| |-- width : largeur de l'image |
| |-- height : hauteur de l'image |
+ +
| METHODES : |
| |-- open(...) |
| |-- new(...) |
| |-- save(...) |
| |-- getpixel(...) |
| |-- putpixel(...) |
| |-- show(...) |
| |-- resize(...) |
+ +
On retiendra à ce stade que la classe est une notion abstraite qui décrit un objet et que
l’objet en lui-même est un exemplaire de cette classe, on dit que l’objet est une instance
de classe.
Parcourir les pixels d’une image
Pour parcourir l’ensemble des pixels d’une image, on utilise la technique de la double
boucle FOR imbriquée :
for y in range(img.height): # parcours ligne par ligne
for x in range(img.width): # et colonne par colonne
img.putpixel((x, y), (127, 127, 127)) # tout en gris
Améliorer contraste
Comentez le programme :
from PIL import Image, ImageFilter
im = Image.open( 'fleurc.jpg' )
im.show()
from PIL import ImageEnhance
enh = ImageEnhance.Contrast(im)
enh.enhance(1.8).show("30% more contrast")
7
Exercice1
• Tester le code suivant : la photo doit s’afficher à l’écran
Remarque : on pourrait écrire directement : im=Image.open(‘totem512.jpg’)
En complétant le script contenu dans le fichier p a r les lignes suivantes, Vous pouvez
maintenant obtenir des informations sur cette photo :
Quelles sont les données qui s’affichent à l’écran ?
............................................................................................
Vous pouvez enregistrer une photo sous un autre format :
im.save ("totem512.png", "png") En quel format la photo est –elle changée ?
.......................................................................................
……………………………………………………… Compléter la ligne du script ci-contre pour faire afficher
…………………..
notre image.
A retenir :
8
Exercice 2
But : on va fabriquer une image exp1.jpg en couleur avec Python :
from PIL import Image
exp1=Image.new("RGB",(8,8),"white") #On définit une image de taille 8x8, en mode
RGB de fond blanc
rouge=(255,0,0) #On définit la couleur rouge
bleu=(0,0,255) #On définit la couleur bleu
noir=(0,0,0) #On définit la couleur noir
rose=(255,153,204) #On définit la couleur rose
exp1.putpixel((2,0),rouge) #On met le pixel de coordonnées (2;0) à la couleur rouge
exp1.putpixel((3,0),rouge)
exp1.putpixel((4,0),rouge)
exp1.putpixel((6,4),rouge)
exp1.putpixel((4,3),noir) #On met le pixel de coordonnées (4;3) à la couleur noir
exp1.putpixel((5,3),noir)
exp1.putpixel((2,7),noir)
exp1.putpixel((5,7),noir)
exp1.putpixel((3,2),bleu)
exp1.putpixel((5,2),bleu)
exp1.putpixel((2,4),bleu)
9
exp1.putpixel((4,5),bleu)
for i in range(2,6): #Pour écrire moins de ligne, on utilise une double boucle for
pour mettre plusieurs pixels de l'image en bleu
for j in range(4,7):
exp1.putpixel((i,j),bleu)
exp1.putpixel((3,4),rouge)
exp1.putpixel((4,4),rouge)
exp1.save("exp1.png","png") #On enregistre l'image obtenue sous le nom de exp1 au
format png
exp1.save("exp1.jpeg","jpeg") #On enregistre l'image obtenue sous le nom de exp1 au
format jpeg
exp1.show() #On montre l'image contenue dans la variable exp1
Exercice 3
Soit le code suivant :
1) Quelle est la taille de l'image chargée dans le code précédent ?
La taille de l'image est……………….
2) Quelle est la couleur du pixel de coordonnées (45 ; 40) ?
La composante (R,G,B) du pixel de coordonnées (45,40) est :……………
3) Ecrire le programme qui permet de transformer l’image fleurc.jpg en niveau de
gris ?
from PIL import Image
im=Image.open("fleurc.jpg")
Taille=im.size
for i in range(Taille[0]): #On parcourt l'image selon la largeur
10
for j in range(Taille[1]): #On parcourt l'image selon la hauteur
R,G,B=im.getpixel((i,j))
gris=(R+G+B)//3 #On fait la moyenne des trois composantes. le // permet d'avoir
le quotient entier dans la division par trois
im.putpixel((i,j),(gris,gris,gris)) #On met le pixel a la couleur gris
im.show()
im.save("fleucgris.jpeg", "jpeg")
4) Créer un programme permettant de retirer la couleur rouge de l'image fleurc.jpg ?
from PIL import Image
MonImage=Image.open("fleurc.jpg")
Taille=MonImage.size
for i in range(Taille[0]): #On parcourt l'image selon la largeur
for j in range(Taille[1]): #On parcourt l'image selon la hauteur
R,G,B=MonImage.getpixel((i,j))
MonImage.putpixel((i,j),(R,0,B)) #eleminer la couleur vert au pixel de coordonnés (i;j)
MonImage.show()
5) Ecrire le programme qui permet de de créer le négatif de l’image fleur.jpg ?
Négatif : pour chaque pixel de l'image, on en prend le négatif : pour chaque
couleur, on garde donc le complément à 255.
Exp : Que deviendrait un pixel de composante RGB (10,160,204) lors de la
transformation négatif d’image?
Ce pixel sera transformé en (255-10,255-160,255-204) = (245,95,51)
from PIL import Image
MonImage=Image.open("fleur.jpg")
Taille=MonImage.size
for i in range(Taille[0]): #On parcourt l'image selon la largeur
for j in range(Taille[1]): #On parcourt l'image selon la hauteur
R,G,B=MonImage.getpixel((i,j))
MonImage.putpixel((i,j),(255-R,255-G,255-B))
MonImage.show()
11