Intro (préparation du poste)° Ouvrir votre dossier/répertoire personnel et réaliser ces actions :
Ouvrir Thonny. Réduire la fenêtre de Thonny et de votre navigateur Web pour que chaque fenêtre occupe la moitié de l'écran.
Ouvrir votre dossier/répertoire personnel. Créer dans le dossier SNT un nouveau sous-dossier qu'on nommera photo1.
Sur Windows : vérifier via le menu affichage de votre explorateur de dossiers que vous avez bien coché la case "Afficher les extensions".
INSTALLATION DE PILLOW
✔ 01-A° Pour manipuler les images avec Python et Thonny, il faut disposer d'un module Python qui gère les images Pillow, issu d'un ancien projet nommé PIL. Commençons par voir s'il est installé sur votre poste.
Ouvrir Thonny.
Dans le menu Affichage ou View, sélectionner variables.
Tapez ceci dans la console pour voir si cela déclenche une erreur.
>>> from PIL import Image
Si vous avez une erreur, vérifier d'abord que vous avez bien géré les majuscules / minuscules puis si on vous signale qu'il manque le module, lire et utiliser la note ci-dessous.
EN CAS D'ERREUR D'IMPORTATION : Installer un module avec Thonny
Si votre poste n'est pas muni de Pillow, on vous propose deux méthodes d'installation : commencez par la première et passez à la seconde si la première ne fonctionne pas :
1er méthode
Ouvrir le menu Tool/Outils en haut vers la droite dans Thonny
Sélectionner Manage Packages/Gérer les Paquets
Faire une recherche sur Pillow puis installer la bibliothèque. Ca peut être un peu long.
2e méthode
Ouvrir le menu Tool/Outils en haut vers la droite dans Thonny
Sélectionner Ouvrir la console du système : vous allez voir apparaître un terminal.
Dans le terminal, tapez ceci :
pip install Pillow
Le téléchargement devrait se lancer, en espérant qu'il aboutisse...
✔ 01-B° Réaliser les actions suivantes :
Sur Windows : si vous n'êtes pas certain que ce soit le cas, vérifiez que la gestion des extensions est bien activée.
Avec un clic-droit sur l'image et "Enregistrer l'image sous", enregistrez ces deux images dans votre répertoire photo1 en gardant leurs noms et leurs extensions.
Image de base libre de droit : le Palais des congrès de Montréal
Logo Pillow Python avec transparence
✔ 01-C° Enregistrer ce programme dans photo1 sous le nom photopython.py. Vous devriez donc avoir 3 fichiers dans ce répertoire.
📁 SNT
📁 photo1
📄 photopython.py
📄 photo-montreal-palais-des-congres.jpeg
📄 pillow.png
Lancer et vérifier que Pillow fonctionne et interagisse bien avec votre système :
123456789
fromPILimportImagenom_image="photo-montreal-palais-des-congres.jpeg"img=Image.open(nom_image)print("Image importée dans Python")img.show()print("Image affichée sur l'écran")
✔ 01-D° Répondre à ces questions :
Comment se nomme la fonction qui permet d'ouvrir et stocker une image dans une variable ?
Comme se nomme la fonction qui permet d'afficher une image à l'écran ?
Qu'est-ce que ces fonctions ont de particulier au niveau de la syntaxe ?
...CORRECTION...
Comment se nomme la fonction qui permet d'ouvrir et stockée une image dans une variable ?
C'est la fonction open() à qui on doit transmettre le nom du fichier image sur le disque dur.
123456789
fromPILimportImagenom_image="photo-montreal-palais-des-congres.jpeg"img=Image.open(nom_image)print("Image importée dans Python")img.show()print("Image affichée sur l'écran")
Comme se nomme la fonction qui permet d'afficher une image à l'écran ?
C'est la fonction show() à qui on ne transmet rien visiblement.
Qu'est-ce que ces fonctions ont de particulier au niveau de la syntaxe ?
Ce ne sont pas des fonctions normales : on place l'objet sur lequel elles doivent agir devant la fonction. On parle de syntaxe pointée car on utilise un point entre les deux.
img.show()
Traduction : agit sur img en l'affichant.
Révisions : variables, fonctions d'entrée/sortie et if
⚙ 02° Complétez le programme pour qu'il fasse le travail suivant, puis lancez pour vérifier le résultat :
L06-L09 : on doit demander à l'utilisateur à l'aide de int(input()) lequel des deux fichiers il veut ouvrir ;
L11-L14 : on mémorise le nom du fichier voulu dans la variable nom_image ;
L16 : on mémorise les données de l'image dans Python dans la variable data_image en ouvrant l'image avec la fonction open() ;
L18-L23 : on affiche quelques informations sur l'image.
L25-L28 : On demande à l'utilisateur s'il veut afficher l'image à l'écran. S'il dit O pour oui, on l'affiche avec la fonction show().
fromPILimportImagenom1="photo-montreal-palais-des-congres.jpeg"nom2="pillow.png"print("Tapez :")print("1 pour ouvrir ",nom1)print("2 pour ouvrir ",nom2)n=...# ATTENTION, on veut récupérer un int, pas un strifn==1:nom_image=nom1elifn==...:...=...img=Image.open(nom_image)nb_col=img.width# width : largeur en anglaisnb_lig=img.height# height : hauteur en anglaisprint("Informations sur le fichier ",nom_image)print("Pixels en largeur : ",nb_col)print("Pixels en hauteur : ",nb_lig)print("Voulez-vous afficher l'image ? O/N ")rep=...# on veut récupérer un strif...=="O":...
fromPILimportImagenom1="photo-montreal-palais-des-congres.jpeg"nom2="pillow.png"print("Tapez :")print("1 pour ouvrir ",nom1)print("2 pour ouvrir ",nom2)n=int(input())ifn==1:nom_image=nom1elifn==2:nom_image=nom2img=Image.open(nom_image)nb_col=img.width# width : largeur en anglaisnb_lig=img.height# height : hauteur en anglaisprint("Informations sur le fichier ",nom_image)print("Pixels en largeur : ",nb_col)print("Pixels en hauteur : ",nb_lig)print("Voulez-vous afficher l'image ? O/N ")rep=input()ifrep=="O":img.show()
Révisions : while, boucle Tant Que
⚙ 03-A° Pour le moment, l'utilisateur peut encore faire planter le programme : on lui demande de taper 1 ou 2 mais il peut taper n'importe quoi. Lancer le programme et lorsqu'on vous demande votre choix, tapez ce que vous voulez SAUF 1 ou 2. Le programme va s'arrêter sur une erreur.
Questions
En regardant le message rouge, quelle ligne provoque l'erreur ?
Expliquer ce qui provoque cette erreur.
...CORRECTION...
>>> %Run tp_photo.pyTapez :1 pour ouvrir photo-montreal-palais-des-congres.jpeg2 pour ouvrir pillow.png5555Traceback (most recent call last): "/home/rv/Documents/SNT/photo1/tp_photo.py", line 21, in <module>
print("Informations sur le fichier ", nom_image)NameError: name 'nom_image' is not defined
L'erreur est provoquée par la ligne 21 comme l'indique l'interpréteur : 16 print("Informations sur le fichier ", nom_image)
Un NameError signifie qu'on tente d'utiliser une variable qui n'a pas été initialisée, c'est donc une variable inconnue pour Python. Pourquoi ?
On a tapé 5555 sur l'exemple : on ne réalise donc aucun des blocs du IF : ni la ligne 11, ni la ligne 13 n'est validée à True.
On arrive donc en ligne 21 sans avoir rencontré la ligne 12 ou la ligne 14 qui crée la variable nommée nom_image.
⚙ 03-B° Pour sécuriser la demande à l'utilisateur nous allons réaliser une boucle TANT QUE :
Seule l'étape 1 va donc changer un peu.
Question
Compléter ce nouveau qui devra intégrer le filtrage de la demande de l'utilisateur.
L8 : on crée une fausse valeur dans n contenant 0 par exemple.
L9 : tant que n n'est pas dans l'intervalle [0, 1]
L10-12 : on affiche la question à l'utilisateur.
L13 : on récupère sa réponse dans n (sous forme d'int, attention).
Une fois arrivé ici, on reprend le cours du programme précédent mais nous sommes certains
fromPILimportImagenom1="photo-montreal-palais-des-congres.jpeg"nom2="pillow.png"# Début du rajout ---------------n=...# Fausse réponse pour rentrer dans la boucle au départwhilennot in[...,...]:print("Tapez :")print("1 pour ouvrir ",nom1)print("2 pour ouvrir ",nom2)...# Demande d'un entier à l'utilisateur#Fin du rajout ------------------# Arrivé ici, c'est que n=1 ou n=2ifn==1:nom_image=nom1elifn==2:nom_image=nom2img=Image.open(nom_image)nb_col=img.width# width : largeur en anglaisnb_lig=img.height# height : hauteur en anglaisprint("Informations sur le fichier ",nom_image)print("Pixels en largeur : ",nb_col)print("Pixels en hauteur : ",nb_lig)print("Voulez-vous afficher l'image ? O/N ")rep=input()ifrep=="O":img.show()
fromPILimportImagenom1="photo-montreal-palais-des-congres.jpeg"nom2="pillow.png"# Début du rajout ---------------n=0# Fausse réponse pour rentrer dans la boucle au départwhilennot in[1,2]:print("Tapez :")print("1 pour ouvrir ",nom1)print("2 pour ouvrir ",nom2)n=int(input())#Fin du rajout ------------------# Arrivé ici, c'est que n=1 ou n=2ifn==1:nom_image=nom1elifn==2:nom_image=nom2img=Image.open(nom_image)nb_col=img.width# width : largeur en anglaisnb_lig=img.height# height : hauteur en anglaisprint("Informations sur le fichier ",nom_image)print("Pixels en largeur : ",nb_col)print("Pixels en hauteur : ",nb_lig)print("Voulez-vous afficher l'image ? O/N ")rep=input()ifrep=="O":img.show()
Révisions : fonctions
⚙ 04-A° Le programme commence à être long et semble compliqué.
Nous allons le rendre plus court en plaçant chaque tâche ou presque dans une fonction.
Questions
Lancer le programme ci-dessous pour vérifier qu'il agit de la même façon que le précédent.
Chaque tâche un peu complexe est maintenant réalisée par une fonction : le programme principal en lignes 49-52 ne comporte que 4 lignes alors qu'il y avait 5 tâches différentes dans le programme précédent. Quelle est la fonction qui réalise 2 tâches ?
Si les noms des fonctions sont bien choisis, a-t-on vraiment besoin d'aller lire le code interne des fonctions pour comprendre ce que réalise le programme ?
fromPILimportImage# Définitions des fonctionsdefchoisir_un_fichier():"""Renvoie le nom du fichier choisi par l'utilisateur"""nom1="photo-montreal-palais-des-congres.jpeg"nom2="pillow.png"n=0# Fausse réponse pour rentrer dans la boucle au départwhilennot in[1,2]:print("Tapez :")print("1 pour ouvrir ",nom1)print("2 pour ouvrir ",nom2)n=int(input())ifn==1:nom_image=nom1elifn==2:nom_image=nom2returnnom_imagedefobtenir_caracteristiques(img,nom_image):"""Affiche certaines caractéristiques de l'image, aucun renvoi"""nb_col=img.width# width : largeur en anglaisnb_lig=img.height# height : hauteur en anglaisprint("Informations sur le fichier ",nom_image)print("Pixels en largeur : ",nb_col)print("Pixels en hauteur : ",nb_lig)defdemander_affichage(img):"""Affiche l'image si l'utilisateur le veut, aucun renvoi"""print("Voulez-vous afficher l'image ? O/N ")rep=input()ifrep=="O":img.show()# Programme principalnom_image=choisir_un_fichier()# Appel à la fonction choisir_un_fichier img=Image.open(nom_image)obtenir_caracteristiques(img,nom_image)demander_affichage(img)
...CORRECTION...
-
La fonction qui réalise 2 tâches est choisir_image() : elle demande et filtre la demande de l'utilisateur puis renvoie le nom du bon fichier.
Non : en lisant uniquement les 4 lignes du programme principal, il est possible de comprendre ce qu'il réalise. C'est tout l'intérêt des fonctions : on peut "cacher" la façon de réaliser des tâches complexes et juste y faire appel.
⚙ 04-B° Regardons maintenant uniquement les appels de fonction et la première ligne des définitions.
Ne modifiez pas votre programme dans Thonny, c'est juste un affichage pour cette question.
6 72327283940474849505152
# Définitions des fonctionsdefchoisir_un_fichier():"""Renvoie le nom du fichier choisi par l'utilisateur"""returnnom_imagedefobtenir_caracteristiques(img,nom_image):"""Affiche certaines caractéristiques de l'image, aucun renvoi"""defdemander_affichage(img):"""Affiche l'image si l'utilisateur le veut, aucun renvoi"""# Programme principalnom_image=choisir_un_fichier()# Appel à la fonction choisir_un_fichier img=Image.open(nom_image)obtenir_caracteristiques(img,nom_image)demander_affichage(img)
Questions
Sur la ligne 49 , on voit qu'on lance un appel à la fonction choisir_un_fichier() sans lui envoyer quoi que ce soit. Où voit-on qu'on n'a rien à lui envoyer ? Où voit-on qu'elle va renvoyer une réponse qu'il faudra stocker ?
Sur la ligne 51, on voit qu'on envoie deux entrées à la fonction. Comment sait-on qu'il faut les fournir dans ce sens précis ?
Sur la ligne 51, on ne stocke pas la réponse de la fonction. Pourquoi ?
...CORRECTION...
Il faut comparer l'appel de la ligne 49 et la définition de la ligne 6 : on voit bien que la fonction n'attend rien en entrée. Ligne 23, on voit que la fonction va renvoyer une réponse. Il faudra donc la stocker.
Il suffit de comparer l'appel de la ligne 51 et la définition de la ligne 27.
On ne stocke pas la réponse de la fonction car elle ne renvoie rien : il n'y a aucun return. Elle agit puis elle rend la main.
⚙ 04-C° Quelqu'un décide de modifier les noms des variables du programme principal : ce nom programme va-t-il encore fonctionner ? Est-ce plus clair ou moins clair ?
6 72327283940474849505152
# Définitions des fonctionsdefchoisir_un_fichier():"""Renvoie le nom du fichier choisi par l'utilisateur"""returnnom_imagedefobtenir_caracteristiques(img,nom_image):"""Affiche certaines caractéristiques de l'image, aucun renvoi"""defdemander_affichage(img):"""Affiche l'image si l'utilisateur le veut, aucun renvoi"""# Programme principala=choisir_un_fichier()# Appel à la fonction choisir_un_fichier b=Image.open(a)obtenir_caracteristiques(b,a)demander_affichage(b)
...CORRECTION...
Oui, rien n'oblige les variables du programme et des fonctions à porter le même nom.
Par contre, puisque le nom d'une variable doit nous aider à comprendre ce qu'elle contient, ce n'est pas très malin de leur donner des noms différents et encore moins d'utiliser des noms d'une lettre qui n'ont aucune signification.
Conclusion : ce programme fonctionne mais est plus compliqué à suivre pour un humain.
Afficher des informations sur une image
1 Les trois types d'images : 1 couche, 3 couches ou 4 couches
1 couche L si image en nuance de gris : chaque pixel n'est défini que par sa luminosité L comprise entre (0 et 255) (ou 00 et FF)
3 couches RGB si image en couleur : chaque pixel est défini par 3 valeurs R G B comprises entre (0 et 255) ou (00 et FF).
4 couches RGBA si image avec transparence : chaque pixel est défini par 4 valeurs. En plus des valeurs RGB, on trouve une valeur A dite Alpha qui caractérise la transparence du pixel : doit-il être entiérement transparent (valeur 0/00) ou est-il entièrement opaque (valeur 255/FF). Sa valeur réelle peut donc être entre les deux.
⚙ 05° Nous allons rajouter des fonctionnalités à notre programme.
Questions
Remplacer votre ancien programme par celui proposé ci-dessous.
fromPILimportImage# Définitions des fonctionsdefchoisir_un_fichier():"""Renvoie le nom du fichier choisi par l'utilisateur"""nom1="photo-montreal-palais-des-congres.jpeg"nom2="pillow.png"n=0# Fausse réponse pour rentrer dans la boucle au départwhilennot in[1,2]:print("Tapez :")print("1 pour ouvrir ",nom1)print("2 pour ouvrir ",nom2)n=int(input())ifn==1:nom_image=nom1elifn==2:nom_image=nom2returnnom_imagedefobtenir_caracteristiques(img,nom_image):"""Affiche certaines caractéristiques de l'image, aucun renvoi"""nb_col=img.width# width : largeur en anglaisnb_lig=img.height# height : hauteur en anglaiscouches=img.getbands()# Conteneur contenant le nom des bandesnb_cou=...(couches)# Le nombre de bandesprint("Informations sur le fichier ",nom_image)print("Pixels en largeur : ",nb_col)print("Pixels en hauteur : ",nb_lig)print("Nombre de pixels : ",...*...)print("Couche(s) : ",couches)print("Nombre de couches : ",...)returnnb_coudefdemander_affichage(img):"""Affiche l'image si l'utilisateur le veut, aucun renvoi"""print("Voulez-vous afficher l'image ? O/N ")rep=input()ifrep=="O":img.show()# Programme principalnom_image=choisir_un_fichier()# Appel à la fonction choisir_un_fichier img=Image.open(nom_image)nb_couches=obtenir_caracteristiques(img,nom_image)demander_affichage(img)
Compléter la fonction obtenir_caracteristiques() pour qu'elle fonctionne comme on le veut maintenant :
Elle doit afficher le nombre total de pixels dans l'image en multipliant le nombre de pixels en largeur et en hauteur.
Elle doit afficher les couches de l'image avec img.getbands()
Elle doit afficher le nombre de couches en appliquant len() sur le résultat précédent
Elle doit renvoyer le nombre de couches (elle possède donc un return)
Utiliser le programme pour obtenir le nombre de couches des deux images. Laquelle possède une transparence ?
...CORRECTION...
27282930313233343536373839404142
defobtenir_caracteristiques(img,nom_image):"""Affiche certaines caractéristiques de l'image, aucun renvoi"""nb_col=img.width# width : largeur en anglaisnb_lig=img.height# height : hauteur en anglaiscouches=img.getbands()# Conteneur contenant le nom des bandesnb_cou=len(couches)# Le nombre de bandesprint("Informations sur le fichier ",nom_image)print("Pixels en largeur : ",nb_col)print("Pixels en hauteur : ",nb_lig)print("Nombre de pixels : ",nb_col*nb_lig)print("Couche(s) : ",couches)print("Nombre de couches : ",nb_cou)returnnb_cou
Pour le fichier du palais des congrès :
Tapez :
1 pour ouvrir photo-montreal-palais-des-congres.jpeg
2 pour ouvrir pillow.png
1
Informations sur le fichier photo-montreal-palais-des-congres.jpeg
Pixels en largeur : 1024
Pixels en hauteur : 771
Nombre de pixels : 789504
Couche(s) : ('R', 'G', 'B')
Nombre de couches : 3
Voulez-vous afficher l'image ? O/N
Pour le fichier du logo Pillow :
Tapez :
1 pour ouvrir photo-montreal-palais-des-congres.jpeg
2 pour ouvrir pillow.png
2
Informations sur le fichier pillow.png
Pixels en largeur : 248
Pixels en hauteur : 250
Nombre de pixels : 62000
Couche(s) : ('R', 'G', 'B', 'A')
Nombre de couches : 4
Voulez-vous afficher l'image ? O/N
Nous allons maintenant changer les valeurs RGB des différents pixels.
Si vous n'avez pas fait la première partie, il faut impérativement récupérer les images et faire quelques réglages. Voir ci-dessous.
A vérifier (préparation du poste)° Ouvrir votre dossier/répertoire personnel et réaliser ces actions :
Ouvrir Thonny. Réduire la fenêtre de Thonny et de votre navigateur Web pour que chaque fenêtre occupe la moitié de l'écran.
Ouvrir votre dossier/répertoire personnel. Créer dans le dossier SNT un nouveau sous-dossier qu'on nommera photo1.
Sur Windows : vérifier via le menu affichage de votre explorateur de dossiers que vous avez bien coché la case "Afficher les extensions".
Téléchargez les deux images suivantes (clic droit, télécharger sous) et placez là dans le répertoire photo1 :
Image de base libre de droit : le Palais des congrès de Montréal
Logo Pillow Python avec transparence
OBTENIR LES COULEURS RGB D'UN PIXEL
✔ 06° Utiliser la syntaxe img.getpixel((x,y)) en notation pointée sur l'image stockée dans img permet d'obtenir les 3 ou 4 valeurs qui définissent un pixel sur cette image.
Lancer ce programme pour demander les couleurs du pixel de coordonnées 50, 100 puis celui de coordonnées 200, 30 par exemple. Lisez bien ce qu'on vous indique sur la console pour savoir quoi répondre.
1 2 3 4 5 6 7 8 9101112131415161718192021
fromPILimportImagenom1="photo-montreal-palais-des-congres.jpeg"nom2="pillow.png"nom_image=nom1# Choisissez directement l'image, pas de input().img=Image.open(nom_image)# Ouverture de l'image dans Pythonnb_couches=len(img.getbands())# Recherche du nombre de couchesprint("Ouverture dans Python des données de ",nom_image)whileTrue:print("\nDonner les coordonnées x puis ENTREE puis y puis ENTREE")x=int(input())y=int(input())ifnb_couches==3:# RGBr,g,b=img.getpixel((x,y))print((r,g,b))elifnb_couches==4:r,g,b,a=img.getpixel((x,y))print((r,g,b,a))
4 choses à noter :
Ligne 12 : nous créons une boucle infinie avec while True qui revient à dire "Tant que vrai est vrai, réalise un tour de boucle".
Ligne 5 : on choisit le fichier voulu directement dans le code, plus d'interrogation de l'utilisateur.
Lignes 17 et 20 : on récupère les valeurs d'un pixel avec la syntaxe pointée img.getpixel((x,y))
Lignes 17 et 20 : il faut mettre à gauche le bon nombre de variables pour récupérer les 3 ou 4 valeurs du pixel, d'où la nécessité de connaître le nombre de couches.
MODIFIER LES COULEURS RGB D'UN PIXEL
✔ 07° La syntaxe img.putpixel((x,x),(0,255,0,255)) en notation pointée sur l'image voulue permet de modifier les 4 valeurs RGBA du pixel de coordonnées (x, y). Ici, on applique la couleur (0, 255, 0) équivalente à #00FF00. Il s'agit donc de vert vif. La valeur A vaut 255, ce qui indique que le pixel est totalement opaque.
A la fin du programme, l'affichage de l'image vous permettra de voir une droite verte sur l'image : explication lors de la question suivante.
1 2 3 4 5 6 7 8 91011121314151617181920
fromPILimportImagenom1="photo-montreal-palais-des-congres.jpeg"nom2="pillow.png"nom_image=nom2# Choississez directement l'image au démarrage.img=Image.open(nom_image)# Ouverture de l'image dans Pythonnb_couches=len(img.getbands())# Recherche du nombre de couchesprint("Ouverture dans Python des données de ",nom_image)ifnb_couches==3:# RGBforkinrange(200):img.putpixel((k,k),(0,255,0))elifnb_couches==4:forkinrange(200):img.putpixel((k,k),(0,255,0,255))# A sur 255 : opaqueimg.show()
L'image n'est pas modifiée sur le disque dur pour le moment : seule les données de l'image dans Python (en RAM) sont modifiées.
✔ 08° Comprendre ce qui suit, c'est important pour la suite. Si ce n'est pas clair, demandez de l'aide.
On obtient une droite car on demande de modifier les couleurs de quelques pixels : (0,0) puis (1, 1) puis (2, 2) puis (3, 3)...
Cette boucle bornée for k in range(200) fournit les valeurs de k de 0 à 199, une par une à chaque tour de boucle. On impose donc que le pixel de coordonnées (k,k) soit un pixel vert, pour toutes les valeurs que prendra k.
⚙ 09° Associer mentalement chacun des effets A à E à l'instruction Python 1 à 5 qui correspond.
Effets
Permet de modifier un pixel RGBA.
Permet de récupérer le nombre de couches.
Permet de récupérer la largeur en pixels.
Permet de récupérer la hauteur en pixels.
Permet de récupérer les 3 ou 4 valeurs RGB(A).
Instructions
len( image.getbands() )
img.width
img.height
img.getpixel( (x,y) )
img.putpixel( (x,y), (r,g,b,a) )
...CORRECTION...
Effets
[5] Permet de modifier les 3 ou 4 valeurs RGB(A) : image.putpixel( (x,y), (r,g,b,a) )
[1] Permet de récupérer le nombre de couches de l'image : len( img.getbands() )
[2] Permet de récupérer la largeur en pixels de l'image : img.width
[3] Permet de récupérer la hauteur en pixels de l'image : img.height
[4] Permet de récupérer les 3 ou 4 valeurs RGB(A) : img.getpixel( (x,y) )
⚙ 10° Compléter ce programme qui doit permettre d'afficher un par un tous les pixels de l'image :
L06 : il faudra chercher toutes les valeurs possibles y de lignes.
L07 : il faudra chercher toutes les valeurs possibles x de colonnes.
1 2 3 4 5 6 7 8 910
fromPILimportImagenom_image="photo-montreal-palais-des-congres.jpeg"img=Image.open(nom_image)foryinrange(...):# Pour chaque ligne y de l'imagefor...inrange(...):# et chaque colonne x de cette ligner,g,b=img.getpixel((x,y))# On récupère les 3 valeursprint((x,y)," : ",(r,g,b))
Lancer le programme complété : il va afficher un par un toutes les valeurs de pixels. Vous pouvez stopper avant la fin, ça va être long sinon.
...CORRECTION...
1 2 3 4 5 6 7 8 910
fromPILimportImagenom_image="photo-montreal-palais-des-congres.jpeg"img=Image.open(nom_image)foryinrange(img.height):# Pour chaque ligne y de l'imageforxinrange(img.width):# et chaque colonne x de cette ligner,g,b=img.getpixel((x,y))# On récupère les 3 valeursprint((x,y)," : ",(r,g,b))
✔ 11° Lancer ce nouveau programme : cette fois, on récupère les valeurs de chaque pixel mais on les modifie : sur la ligne 17 ou 21, on voit qu'on intervertit la valeur rouge et la valeur bleue lors du putpixel().
1 2 3 4 5 6 7 8 910111213141516171819202122232425
fromPILimportImage# Programme principalnom1="photo-montreal-palais-des-congres.jpeg"nom2="pillow.png"nom_sauvegarde="modification.png"nom_image=nom2# Choississez directement l'image au démarrage.img=Image.open(nom_image)foryinrange(img.height):# Pour chaque lignee y de cette image forxinrange(img.width):# Pour chaque colonne x de cette ligneiflen(img.getbands() )==3:r,g,b=img.getpixel((x,y))img.putpixel((x,y),(b,g,r))eliflen(img.getbands() )==4:r,g,b,a=img.getpixel((x,y))img.putpixel((x,y),(b,g,r,a))img.show()# On la montreimg.save(nom_sauvegarde)# On l'enregistreimg.close()# On ferme proprement le lien entre Python et le fichier
Nouveautés
Ligne 24 : on sauvegarde l'image sur le disque dur, avec un autre nom pour éviter d'écraser l'image de base.
Ligne 25 : on ferme la liaison entre Python et le fichier sur disque dur, pour éviter les conflits d'écriture.
Exemple avec notre image contenant beaucoup de rouge et d'orange
Image de base libre de droit : le Palais des congrès de Montréal
Et qui devient une image contenant beaucoup de bleu et de cyan :
Même image en intervertissant Bleu et Rouge
⚙ 12° Cela fonctionne. Mais on voit que nous sommes obligés de taper deux fois l'inversion du rouge et du bleu. En cas de modification, on peut oublier l'une des deux versions. Autant passer par une fonction qui réalisera cette modification.
Nous décidons donc de créer une fonction filtrer() :
qui reçoit en entrée 3 valeurs rgb dans old_r, old_g, old_b ;
qui renvoie en sortie 3 valeurs rgb new_r, new_g, new_b.
Question de compréhension
Expliquer pourquoi la fonction filtrer() intervertit bien rouge et bleu alors qu'elle renvoie new_r, new_g, new_b en ligne 8.
01
02
03
04
05
06
07
08
09
10
11
deffiltrer(old_r,old_g,old_b):"""Fonction qui renvoie des valeurs RGB après les avoir modifiées"""new_r=old_bnew_g=old_gnew_b=old_rreturn(new_r,new_g,new_b)print( filtrer(1, 2, 3) )
print( filtrer(50, 10, 200) )
...CORRECTION...
Il suffit de suivre les affectations des valeurs pour le rouge et le bleu, sans faire attention aux noms des variables.
Vous verrez qu'on reçoit les valeurs dans l'ordre RGB mais qu'on renvoie en réalité les valeurs dans l'ordre BGR.
01
02
03
04
05
06
07
08
deffiltrer(old_r,old_g,old_b):"""Fonction qui renvoie des valeurs RGB après les avoir modifiées"""new_r=old_b# On place la valeur bleue à la place de la rougenew_g=old_g# On laisse la valeur g inchangéenew_b=old_r# On place la valeur rouge à la place de la bleuereturn(new_r,new_g,new_b)
Sur ce pixel, les intensités du bleu et du rouge sont donc inversées.
✔ 13° Utiliser notre version finale du programme qui modifie les valeurs des pixels en utilisant la fonction filtrer() à la fois pour les images 3 couches et les images 4 couches.
L'intérêt ? : si on veut modifier la façon de filtrer, il suffit de modifier la fonction et la modification aura lieu pour les deux types d'images.
Pour alléger le programme, on décide de se passe des variables nb_col nb_lig nb_couches en allant directement chercher ces valeurs dans l'image.
fromPILimportImagedeffiltrer(old_r,old_g,old_b):"""Fonction qui renvoie des valeurs RGB après les avoir modifiées"""new_r=old_bnew_g=old_gnew_b=old_rreturn(new_r,new_g,new_b)# Programme principalnom1="photo-montreal-palais-des-congres.jpeg"nom2="pillow.png"nom_sauvegarde="modification.png"nom_image=nom1# Choississez directement l'image au démarrage.img=Image.open(nom_image)foryinrange(img.height):# Pour chaque ligne y de cette imageforxinrange(img.width):# Pour chaque colonne x de cette ligneiflen(img.getbands())==3:r,g,b=img.getpixel((x,y))r,g,b=filtrer(r,g,b)img.putpixel((x,y),(r,g,b))eliflen(img.getbands())==4:r,g,b,a=img.getpixel((x,y))r,g,b=filtrer(r,g,b)img.putpixel((x,y),(r,g,b,a))img.show()# On la montreimg.save(nom_sauvegarde)# On l'enregistreimg.close()# On ferme proprement le lien entre Python et le fichier
Vérifiez que vous obtenez bien le même résultat qu'avant mais avec un seul endroit pour comprendre comment on modifie les pixels : les lignes 4 à 11 de la fonction filtrer().
Filtrer les couleurs veut dire en laisser passer certaines et pas d'autres.
Pour l'instant, la fonction filtrer() ne provoque qu'une interversion de couleurs. La modifier va vous permettre de créer beaucoup d'effets différents.
⚙ 14° Modifiez uniquement le code de la fonction filtrer() pour créer un filtre rouge : on ne veut garder que la valeur rouge du pixel, les autres valeurs sont placées à 0.
...CORRECTION...
04
05
06
07
08
09
10
11
deffiltrer(old_r,old_g,old_b):"""Fonction qui renvoie des valeurs RGB après les avoir modifiées"""new_r=old_r# On laisse le rougenew_g=0# On supprime le vertnew_b=0# On supprime le bleureturn(new_r,new_g,new_b)
⚙ 15° Modifiez uniquement le code de la fonction filtrer() pour créer un filtre rouge plus léger : on veut garder la valeur rouge du pixel, les autres valeurs sont divisées par 3.
...CORRECTION...
04
05
06
07
08
09
10
11
deffiltrer(old_r,old_g,old_b):"""Fonction qui renvoie des valeurs RGB après les avoir modifiées"""new_r=old_r# On laisse le rougenew_g=old_g//3# On diminue le vertnew_b=old_b//3# On diminue le bleureturn(new_r,new_g,new_b)
⚙ 16° Créer un filtre bleu pour qu'il ne laisse passer que le bleu.
...CORRECTION...
04
05
06
07
08
09
10
11
deffiltrer(old_r,old_g,old_b):"""Fonction qui renvoie des valeurs RGB après les avoir modifiées"""new_r=0# On supprime le rougenew_g=0# On supprime le vertnew_b=old_b# On laisse le bleureturn(new_r,new_g,new_b)
⚙ 17° Créer un filtre jaune pour qu'il ne laisse passer que le jaune : donc le vert et le rouge. Il ne bloque que le bleu
...CORRECTION...
04
05
06
07
08
09
10
11
deffiltrer(old_r,old_g,old_b):"""Fonction qui renvoie des valeurs RGB après les avoir modifiées"""new_r=old_r# On laisse le rougenew_g=old_g# On laisse le vertnew_b=0# On supprime le bleureturn(new_r,new_g,new_b)
⚙ 18° On voit que notre filtre "jaune" fournit une image un peu plus foncée. C'est normal : on supprime l'intensité bleue donc on reçoit moins de lumière.
Nous allons l'améliorer : on supprime toujours le bleu mais on rajoute la moitié de l'intensité bleu au rouge et l'autre moitié au vert.
Bien entendu, il faudra veiller à ce que les intensités ne dépassent pas 255. Si c'est le cas, on limite à 255.
Compléter la fonction pour qu'elle fasse son travail correctement.
1 2 3 4 5 6 7 8 9101112
deffiltrer(old_r,old_g,old_b):"""Fonction qui renvoie des valeurs RGB après les avoir modifiées"""new_r=old_r+...# On augmente le rougeif...>255:...=...new_g=old_g+old_b# On augmente le vertif...>255:...=...new_b=0# On supprime le bleureturn(new_r,new_g,new_b)
...CORRECTION...
1 2 3 4 5 6 7 8 9101112
deffiltrer(old_r,old_g,old_b):"""Fonction qui renvoie des valeurs RGB après les avoir modifiées"""new_r=old_r+old_b//2# On augmente le rougeifnew_r>255:new_r=255new_g=old_g+old_b//2# On augmente le vertifnew_g>255:new_g=255new_b=0# On supprime le bleureturn(new_r,new_g,new_b)
⚙ 19° IMAGE GRISE : Remplacer les trois intensités R, G et B par la moyenne des trois. On pourra utiliser la division euclidienne en Python en utilisant // plutôt que simplement /.
04
05
06
07
08
09
10
11
12
deffiltrer(old_r,old_g,old_b):"""Fonction qui renvoie des valeurs RGB après les avoir modifiées"""moyenne==(old_r+old_g+old_b)//3new_r=moyennenew_g=moyennenew_b=moyennereturn(new_r,new_g,new_b)
Puisque les trois intensités RGB ont la même valeur, votre oeil percevra du gris.
Si on veut transformer une image en couleur en réelle perception grisée pour un oeil humain, il faut utiliser des formules particulières pour avoir un rendu correct car nos yeux ne sont pas sensibles de la même manière à la lumière rouge, verte ou bleue. ils sont beaucoup plus sensibles à la lumière verte qu'aux lumières bleues et rouges.
⚙ 20° IMAGE GRISE HUMAINE : Remplacer les trois intensités R, G et B par :
moyenne = (21*old_r + 71*old_g + 8*old_b) // 100
La nouvelle version grise à gauche, la moyenne pure à droite :
...CORRECTION...
04
05
06
07
08
09
10
11
12
deffiltrer(old_r,old_g,old_b):"""Fonction qui renvoie des valeurs RGB après les avoir modifiées"""moyenne=(21*old_r + 71*old_g + 8*old_b)//100new_g=moyennenew_b=moyennereturn(new_r,new_g,new_b)