Partie P5 · exercices 24 à 34

Filtrer les photos avec Python

Lire chaque pixel, calculer une nouvelle couleur, écrire le résultat

Un filtre est un petit calcul appliqué à chaque pixel d'une photo. Les applications de retouche font la même chose, en beaucoup plus perfectionné. Tu vas écrire tes propres filtres.

CoursLire, calculer, écrire

Tous les filtres de ce chapitre suivent le même modèle :

filtre.py
from PIL import Image

photo = Image.open("Ardeche.jpeg").convert("RGB")     # la photo de départ
largeur, hauteur = photo.size                         # sa définition
image = Image.new("RGB", (largeur, hauteur))          # une image vide, de même taille

for x in range(largeur):
    for y in range(hauteur):
        R1, V1, B1 = photo.getpixel((x, y))           # 1. lire le pixel d'origine
        R2 = V1                                       # 2. calculer la nouvelle couleur
        V2 = B1
        B2 = R1
        image.putpixel((x, y), (R2, V2, B2))          # 3. écrire le pixel dans la nouvelle image

image.show()
image.save("Ardeche_filtre.jpg")
LigneEffet
photo = Image.open("Ardeche.jpeg")ouvre la photo, qui doit être dans le même dossier que le programme
largeur, hauteur = photo.sizerange la définition de la photo dans deux variables
R1, V1, B1 = photo.getpixel((x, y))lit les trois valeurs du pixel \((x\,;y)\)
image.putpixel((x, y), (R2, V2, B2))écrit le pixel modifié à la même place dans la nouvelle image

Vigilance

Le nom du fichier doit être exact, extension comprise : ici Ardeche.jpeg (avec deux e), pas .jpg.

.convert("RGB") garantit trois valeurs par pixel. Sans lui, certaines images (PNG avec transparence, images en niveaux de gris) renvoient 4 valeurs ou une seule, et R1, V1, B1 = ... provoque une erreur.

Les valeurs écrites doivent être des entiers entre 0 et 255. Pour un résultat décimal, utilise // ou int(...). Pour un résultat qui peut dépasser, utilise min(255, ...) ou max(0, ...).

Le programme parcourt environ 700 000 pixels : compte quelques secondes d'exécution.

À retenir

Un filtre = une double boucle sur tous les pixels + un calcul de \((R_2\,;V_2\,;B_2)\) à partir de \((R_1\,;V_1\,;B_1)\).

Écrire dans une nouvelle image évite de mélanger anciennes et nouvelles valeurs.

ExercicesDes filtres à écrire

EX 24Je m'entraîne 🌶️prise en main

Exercice 24 — Ouvrir une photo

Copie photo.py, télécharge Ardeche.jpeg dans le même dossier et exécute :

photo.py
from PIL import Image

photo = Image.open("Ardeche.jpeg").convert("RGB")
largeur, hauteur = photo.size
print(largeur, hauteur)
photo.show()
  1. Quelle est la définition de la photo ?
  2. Combien de pixels contient-elle ? Combien de mégapixels ?
Indice

Relis le calcul de l'exercice sur les mégapixels.

💾 NOM_Prenom_ex24.py

EX 25Je m'entraîne 🌶️🌶️application

Exercice 25 — Permuter les couleurs

Teste filtre.py (vu dans le cours), qui applique \(R_2 = V_1\), \(V_2 = B_1\) et \(B_2 = R_1\).

  1. À quoi sert ce programme ? Décris l'effet sur le ciel de la photo.
  2. Modifie-le pour échanger seulement le rouge et le bleu.
  3. Combien y a-t-il de façons de ranger les trois canaux R, V, B dans un ordre ? Comment s'appelle chacune ?
  4. Quelles couleurs ne sont pas modifiées par le programme du cours ? Que peut-on en déduire ?
Indice 1

Question 3 : trois choix pour le premier canal, puis deux pour le deuxième, puis un.

Indice 2

Question 4 : cherche les pixels pour lesquels \(R_1 = V_1 = B_1\).

💾 NOM_Prenom_ex25.py

EX 26Je m'entraîne 🌶️🌶️application

Exercice 26 — Le négatif

Le négatif d'une photo s'obtient en remplaçant chaque intensité par son complément à 255 : \(R_2 = 255 - R_1\), et de même pour V et B.

  1. Écris le programme qui produit le négatif de la photo « Ardèche ».
  2. Quel est le négatif du noir ? du blanc ? du rouge pur (255, 0, 0) ?
  3. Que se passe-t-il si on applique le filtre deux fois de suite ? Justifie par le calcul.
Indice

Reprends filtre.py et change seulement les trois lignes de calcul.

💾 NOM_Prenom_ex26.py

EX 27Je m'entraîne 🌶️🌶️application

Exercice 27 — Passer en niveaux de gris

  1. Écris un programme qui donne à \(R_2\), \(V_2\) et \(B_2\) la moyenne des trois intensités du pixel d'origine.
  2. Écris une deuxième version avec la moyenne pondérée \(0{,}299\,R_1 + 0{,}587\,V_1 + 0{,}114\,B_1\) (revois l'exercice sur le gris). Compare les deux images.
Indice 1

Range le calcul dans une variable g, puis écris image.putpixel((x, y), (g, g, g)).

Indice 2

Pour la moyenne, utilise // : le résultat doit être un entier. Pour la pondérée, entoure le calcul de int(...).

💾 NOM_Prenom_ex27.py

EX 28Je m'entraîne 🌶️🌶️🌶️plusieurs étapes

Exercice 28 — Noir et blanc pur

Un pixel devient blanc si son niveau de gris dépasse un seuil, et noir sinon.

  1. Écris ce filtre avec un seuil de 128 (calcule d'abord le niveau de gris comme à l'exercice précédent).
  2. Essaie les seuils 60 puis 200. Comment change l'image ?
  3. Pourquoi cette image est-elle une image « à deux couleurs » ? Combien de couleurs différentes contient-elle ?
Indice

Un if g > 128: qui écrit (255, 255, 255), un else: qui écrit (0, 0, 0).

💾 NOM_Prenom_ex28.py

EX 29Je m'entraîne 🌶️🌶️🌶️plusieurs étapes

Exercice 29 — L'image dans un miroir

Télécharge Gargouille1.jpeg, puis remplace la dernière ligne de la boucle de filtre.py par :

programme.py
image.putpixel((largeur - 1 - x, y), (R1, V1, B1))
  1. À quoi sert ce programme ?
  2. Modifie-le pour retourner la photo de haut en bas.
  3. Que se passerait-il si on écrivait largeur - x au lieu de largeur - 1 - x ?
  4. Comment obtenir une rotation de 180° ?
Indice 1

Le pixel de la colonne \(x\) est recopié dans la colonne symétrique.

Indice 2

Question 3 : quelle est la plus grande colonne autorisée ? Que vaut largeur - x pour \(x = 0\) ?

💾 NOM_Prenom_ex29.py

EX 30Je m'entraîne 🌶️🌶️🌶️plusieurs étapes

Exercice 30 — Éclaircir et assombrir

  1. Écris un filtre qui éclaircit la photo en ajoutant 60 à chaque intensité.
  2. Écris un filtre qui l'assombrit en retranchant 60.
  3. Que vaut le pixel (230, 100, 20) après éclaircissement ? Pourquoi faut-il écrire min(255, ...) ?
Indice 1

Une intensité ne peut pas dépasser 255, ni descendre sous 0.

Indice 2

min(255, R1 + 60) renvoie la plus petite des deux valeurs.

💾 NOM_Prenom_ex30.py

EX 31Je m'entraîne 🌶️🌶️🌶️plusieurs étapes

Exercice 31 — Compter les pixels sombres et clairs

Pour savoir si une photo est plutôt sombre ou claire, on compte les pixels par tranche de luminosité. On calcule le niveau de gris \(g\) de chaque pixel (moyenne) :

  • sombre si \(g < 85\) ;
  • moyen si \(85 \leqslant g < 170\) ;
  • clair si \(g \geqslant 170\).
  1. Écris un programme qui compte les pixels de chaque tranche pour la photo « Ardèche » et affiche les trois pourcentages, arrondis au dixième.
  2. Que peut-on dire de cette photo ?
  3. Fais de même avec Eiffel.jpeg et compare.
Indice 1

Trois compteurs qui démarrent à 0, un if / elif / else dans la double boucle.

Indice 2

Le total des pixels est largeur * hauteur. Pourcentage : 100 * sombres / total.

💾 NOM_Prenom_ex31.py

EX 32Je m'entraîne 🌶️🌶️🌶️plusieurs étapes

Exercice 32 — Fusionner une photo et un drapeau

Recopie et teste ce programme avec Eiffel.jpeg :

fusion.py
from PIL import Image

photo = Image.open("Eiffel.jpeg").convert("RGB")
largeur, hauteur = photo.size
image = Image.new("RGB", (largeur, hauteur))

L = largeur // 3                          # largeur d'une bande du drapeau
for x in range(largeur):
    for y in range(hauteur):
        if x < L:
            image.putpixel((x, y), (0, 0, 255))
        elif x < 2 * L:
            image.putpixel((x, y), (255, 255, 255))
        else:
            image.putpixel((x, y), (255, 0, 0))

for x in range(largeur):
    for y in range(hauteur):
        R1, V1, B1 = photo.getpixel((x, y))
        r, v, b = image.getpixel((x, y))
        image.putpixel((x, y), ((R1 + r) // 2, (V1 + v) // 2, (B1 + b) // 2))

image.show()
image.save("Eiffel_fusion.jpg")
  1. À quoi sert ce programme ? Que représentent \((R_1\,;V_1\,;B_1)\) et \((r\,;v\,;b)\) ?
  2. Pourquoi divise-t-on par 2 ? Que se passerait-il en divisant par 3 ?
  3. Modifie-le pour fusionner la photo avec le drapeau de l'Ukraine : deux bandes horizontales, bleu (0, 87, 183) en haut et jaune (255, 215, 0) en bas.
  4. Fusionne-la ensuite avec un drapeau de ton choix.
Indice 1

Question 2 : quelle est la moyenne de deux nombres ?

Indice 2

Question 3 : la couleur du drapeau dépend de y (haut ou bas) : y < hauteur // 2.

💾 NOM_Prenom_ex32.py

EX 33Je m'entraîne 🌶️🌶️🌶️plusieurs étapes

Exercice 33 — Pixelliser une photo

Pixelliser, c'est remplacer chaque petit carré de la photo par un carré de couleur unique. Pour un carré de côté taille :

  • on parcourt la photo de taille en taille : for x in range(0, largeur, taille) ;
  • on lit la couleur du pixel \((x\,;y)\), coin du carré ;
  • on colorie tous les pixels du carré avec cette couleur.
  1. Écris ce programme avec taille = 10.
  2. Essaie taille = 3, puis taille = 30. Que constates-tu ?
  3. Pourquoi écrit-on min(x + taille, largeur) pour arrêter les boucles internes ?
Indice 1

Quatre boucles : deux pour les carrés, deux (à l'intérieur) pour les pixels d'un carré.

Indice 2

Les pixels du carré sont range(x, x + taille) pour la colonne et range(y, y + taille) pour la ligne.

💾 NOM_Prenom_ex33.py

EX 34Défi 🌶️🌶️🌶️🌶️défi

Exercice 34 — Flouter une photo

Pour lisser une photo, on remplace chaque pixel par la moyenne des 9 pixels du carré 3 × 3 qui l'entoure (lui compris).

  1. Écris ce filtre. Pour éviter de sortir de la photo, on ne traite que les pixels de la colonne 1 à la colonne largeur - 2, et de la ligne 1 à hauteur - 2.
  2. Pourquoi ne peut-on pas traiter les pixels du bord de la même façon ?
  3. Applique le filtre plusieurs fois de suite (en rouvrant l'image obtenue). Que se passe-t-il ?
Indice 1

Pour chaque pixel \((x\,;y)\), deux boucles de \(-1\) à \(+1\) lisent les voisins \((x + i\,;y + j)\).

Indice 2

Trois accumulateurs r, v, b démarrent à 0 pour chaque pixel ; on les divise par 9 à la fin.

💾 NOM_Prenom_ex34.py

À retenirSynthèse

Ce que je sais faire

  • Écrire un filtre : lire un pixel, calculer, l'écrire dans une nouvelle image.
  • Modifier les couleurs (permutation, négatif, gris, seuillage, éclairage) et déplacer les pixels (miroir).
  • Fusionner deux images par une moyenne.
  • Compter des pixels pour analyser une photo.