Dans ce TP nous aurons besoin de la bibliothèque Pillow. C’est une bibliothèque permettant de travailler sur les images.
Cette bibliothèque est déjà installée avec EduPython.
Maintenant créez un dossier TP_image. Nous travaillerons dans ce dossier. Dans cette activité, nous allons travailler avec l’image ci-dessous « IMAGE.jpg ».
Une image est constituée de pixels (points), la définition d’une image L x H (par exemple 330 x 330 pour l’image considérée correspond au nombre de pixels de l’image (L : la largeur de l’image en pixel, H : la hauteur de l’image en pixel).
Chaque pixel est constitué de 3 éléments (chacun est appelé aussi canal) : un rouge, un vertet un bleu(RVB). C’est la somme de ces 3 couleurs qui permet d’obtenir un grand nombre de couleurs (synthèse additive).
Classiquement, à chaque canal, on associe un nombre binaire codé sur 8 bits (soit donc 24 bits par pixel).
Pour chaque pixel, on aura donc une valeur pour le rouge (comprise entre 0 et 255 puisque codé sur 8 bits), une valeur pour le vert (comprise entre 0 et 255) et une valeur pour le bleu (comprise entre 0 et 255). Quelques exemples : (0,0,0) => noir ; (255,0,0) => rouge ; (255,255,255) => blanc ; (0,255,0) => vert…
Remarque : Il peut exister un 4e canal en plus du canal rouge, canal vert et canal bleu, le canal « alpha » qui permet de gérer la transparence du pixel (par exemple, les images au format .png gèrent la transparence). Chaque pixel est donc codé sur 32 bits (4 x 8).
Partant du principe que l’image que vous avez copiée possède une définition de 508*512, que chaque pixel est constitué de 3 éléments (RVB) chacun codé sur 8 bits = 1 octet, calculez la taille (ou poids) de cette image en kilo octet (ko). Rappel : 1 ko = 1024 octets ; 1 octet = 8 bits Si vous regardez la taille réelle de l’image en ko, vous allez sans doute trouver une valeur inférieure à celle que vous venez de calculer. Ceci est tout à fait normal, car le .jpeg est un format compressé (le taux de compression est compris entre 10 : 1 et 25 : 1). Vous pouvez convertir cette image en .PNG avec « paint » ou « photofiltre » et comparer sa nouvelle taille à la taille précédente.
Fonctions de base
1. Voici le code minimal vous permettant d’ouvrir une image et de l’afficher :
from PIL import Image
image = Image.open("IMAGE.jpg")
image.show()
Attention ! Pour que cela fonctionne votre fichier .py doit se trouver dans le même dossier que votre image ! La première ligne permet d’indiquer que nous utiliserons les outils de la bibliothèque Pillow. La deuxième ligne permet d’ouvrir l’image et de l’enregistrer dans variable (pour simplifier) “image” La troisième ligne permet d’afficher l’image à l’écran.
2. Plutôt que d’afficher l’image on peut plutôt en enregistrer une copie:
from PIL import Image
image = Image.open("IMAGE.jpg")
image.save("resultat.jpg", "JPEG")
La ligne “image.save(“resultat.jpg”,”JPEG”)” permet de sauvegarder une image. L’image sera sauvegardée dans le dossier courant (le même dossier que “IMAGE.jpg”) et aura pour nom “resultat.jpg”.
3. La méthode getpixel((x,y)) renvoie un tuple (un tuple ressemble beaucoup à une liste, à l’exception près, qu’un tuple, une fois créé, n’est pas modifiable.) contenant les trois composantes : rouge, verteet bleuedu pixel situé aux coordonnées x, y (sachant que le pixel de coordonnées 0, 0 se situe en haut à gauche de l’image).
Pixel de coordonnées (3; 1)
Saisissez, analysez et testez ce code :
from PIL import Image
image = Image.open("IMAGE.jpg")
(r, v, b) = image.getpixel((100, 100))
print(r, v, b)
Si tout se passe correctement, vous devriez avoir à l’écran (dans la console) : (175, 68, 76). Nous pouvons donc affirmer que le pixel de l’image “IMAGE.jpg”, ayant pour coordonnées (100, 100), a pour composantes : Rouge=>175 ; Vert=>68 ; Bleu=>76. Encore une chose importante sur les tuples : si vous voulez récupérer une valeur du tuple, cela se passe exactement comme avec les listes. Si vous reprenez l’exemple ci-dessus, tu [0] est égal à 175 et tu [2] est égal à 68. Vous remarquerez que la méthode getpixel prend en paramètre un tuple : (100,100).
4. Autre fonction très utile : putpixel. Cette méthode permet de modifier les canaux Rouge, Vert et Bleu d’un pixel. La méthode putpixel prend deux paramètres (2 tuples) : le premier paramètre correspond aux coordonnées du pixel que nous voulons modifier (tuple avec 2 éléments : (x, y)). Le second paramètre correspond aux valeurs à attribuer aux canaux R, V, B (tuple avec 3 éléments : (255,255,255), ici un pixel blanc).Saisissez, analysez et testez ce code :
Si vous le souhaitez vous pouvez redimensionner l’image avec :
image = image.resize((300, 200))
Exercices
Exercice :
Modifiez le programme pour obtenir le drapeau allemand.
Exercice :
Une fois ce drapeau réalisé essayez de réaliser un dessin de votre choix. (une lettre, un smiley, etc )
Exercice supplémentaire(Exercice un peu difficile, vous pouvez le passer si vous êtes en seconde) :
Ecrivez une procédure qui permet de colorier un rectangle d’une certaine longueur, d’une certaine largeur et d’une certaine couleur. Compléter :
from PIL import Image
def rectangle(origine, image, longueur, largeur, couleur):
# A vous de complèter ici
# test de la procédure :
image = Image.new('RGB', (50, 50))
rectangle((2,2), image, 20, 30, (0, 0, 250))
Le programme précédent devrait dessiner un rectangle bleu de 20 pixels sur 30 pixels. Le coin en haut à gauche de ce rectangle se situe sur le pixel de coordonnées (2;2)
Utilisez la fonction précédente pour faire un drapeau français de 500 pixels de côté.
Modification d’une photo
Très souvent il est nécessaire de parcourir les pixels d’une image. Voyons un exemple permettant de transformer tous les pixels en bleu :
from PIL import Image
image = Image.open("IMAGE.jpg")
(L, H)= image.size
# On parcours les lignes de pixels de l'image
for i in range(H):
# Pour chaque ligne on parcours les pixels de la ligne i
for j in range(L):
image.putpixel((j, i), (0, 0, 255))
image.save("resultat.jpg", "JPEG")
image.show()
Le résultat n’est pas très intéressant… Avant d’aller plus loin assure toi d’avoir bien compris ce petit programme.
Regardons des manipulations plus intéressantes :
Les corrigés sont disponibles pour les étudiants connectés seulement.
(Avant de consulter les corrigés tu peux essayer un peu quand même… Et si tu es amené à consulter le corriger assure toi de bien le comprendre)
Assombrir une image
Écrire et commenter le programme permettant de d’assombrir l’image. (soustraire 50 à chaque canal, par exemple) Indication : Il faut penser à utilise get.pixel pour récupérer les couleurs de ce pixel.
Corrigé (visible si connecté)
Négatif d'une image
Écrire et commenter un programme en Python qui permettra de transformer une image couleur en son négatif.
Corrigé
from PIL import Image
image = Image.open("IMAGE.jpg")
(L, H) = image.size
for i in range(H):
for j in range(L):
(r, v, b) = image.getpixel((j, i))
image.putpixel((j, i), (255 - r, 255 - v, 255 - b))
image.save("resultat.jpg", "JPEG")
Image en niveau de gris
Écrire et commenter un programme en Python qui permettra de transformer une image couleur en une image en niveau de gris (souvent improprement appelée “noir et blanc”, car une image “noir et blanc” est uniquement composée de pixel noir et de pixel blanc). Une des façons d’obtenir du gris est de prendre les fractions suivantes pour les composantes RVB : (rouge=0.299 ; verte=0.587 ; bleue=0.144), la somme faisant 1.
Remarque : en Python, int(x) permet de prendre la valeur entière de x.
Corrigé
from PIL import Image
image = Image.open("IMAGE.jpg")
(L, H) = image.size
for i in range(H):
for j in range(L):
(r, v, b) = image.getpixel((j, i))
image.putpixel((j, i), (int(0.299 * r), int(0.587 * v), int(0.144 * b)))
image.save("resultat.jpg", "JPEG")
Symétrique d'une image
Transformer une image en sa symétrique par rapport à l’axe de symétrie « vertical » (principal) de la fenêtre graphique.
Corrigé
from PIL import Image
image = Image.open("IMAGE.jpg")
(L, H) = image.size
# On utilise une nouvelle image
nouvelle_image = Image.new("RGB", (L, H))
for i in range(H):
for j in range(L):
(r, v, b) = image.getpixel((j, i))
nouvelle_image.putpixel((j, -i+H-1), (r, v, b))
nouvelle_image.save("resultat.jpg", "JPEG")
Rotation d'une image
(Attention, cette question est optionnelle en seconde) Faite tourner l’image d’un quart de tour dans le sens des aiguilles d’une montre. (Et sans dupliquer l’image si vous souhaitez faire la question en mode Iron Man)
Pour la future application FaceSturm, pourriez vous écrire un programme qui change la couleur du rouge à lèvre de cette dame.
Faite la même chose pour la deuxième photo mais en faisant attention de ne pas modifier la couleur du fond !
Variation autour d'une pomme
Pour commencer, télécharger cette image de pomme :
Maintenant testez le code qui permet de coller l’image de pomme dans une image de fond :
from PIL import Image
# On ouvre l'image de pomme
image = Image.open("pomme.jpg")
# On réccupère la définition de l'image de pomme
(L, H) = image.size
# Je crée une nouvelle image pouvant contenir 4 fois l'image de pomme
fond = Image.new("RGB", (L * 2, H * 2))
# Je colle l'image en (0, 0) (coin en haut à gauche
fond.paste(image, (0, 0))
fond.save("resultatf.jpg", "JPEG")
fond.show()
Maintenant modifiez ce code pour obtenir l’image suivante :
from PIL import Image
# On ouvre l'image de pomme
image = Image.open("pomme.jpg")
# On réccupère la définition de l'image de pomme
(L, H) = image.size
# Je crée une nouvelle image pouvant contenir 4 fois l'image de pomme
fond = Image.new("RGB", (L * 2, H * 2))
# Je colle l'image en (0, 0) (coin en haut à gauche
fond.paste(image, (0, 0))
fond.paste(image, (L, 0))
fond.paste(image, (0, H))
fond.paste(image, (L, H))
fond.save("resultatf.jpg", "JPEG")
fond.show()