Skip to content

Transformer une image

Tu sais créer une image et écrire dans ses pixels. Pour transformer une photo, il manque le geste inverse : lire la couleur d'un pixel. Avec lire + calculer + écrire, on peut programmer des filtres, comme ceux de tes applications.

Cours ici, exercices sur Capytale

Cette page est le cours. Les exercices sont sur Capytale, où une photo de travail est fournie.

Ouvrir une photo, lire un pixel

from PIL import Image

photo = Image.open("photo.jpg")
print(photo.size)

Image.open ouvre un fichier image et le range dans une variable ; photo.size donne le couple (largeur, hauteur).

Pour lire un pixel, on demande sa couleur avec getpixel :

r, v, b = photo.getpixel((10, 20))

getpixel((10, 20)) renvoie le triplet du pixel (10, 20), et on range ses trois nombres dans trois variables d'un coup : r, v, b. On peut ensuite calculer avec.

Le motif du filtre

Tous les filtres qu'on va écrire ont la même forme. Pour chaque pixel de l'image : lire sa couleur, calculer une nouvelle couleur, la réécrire au même endroit. C'est nos boucles imbriquées, avec une lecture avant l'écriture.

Et comme c'est un traitement qu'on voudra refaire, on en fait une fonction, à qui on passe l'image en paramètre, exactement comme on passait la tortue :

def filtre(img):
    largeur, hauteur = img.size
    for y in range(0, hauteur):
        for x in range(0, largeur):
            r, v, b = img.getpixel((x, y))
            # ... calculer la nouvelle couleur ...
            img.putpixel((x, y), (r, v, b))

Il ne reste qu'à choisir le calcul au centre. Chaque calcul donne un filtre différent.

Filtre 1 : les niveaux de gris

Un pixel est gris quand ses trois composantes sont égales (regarde le tableau des couleurs de la première page : noir, gris moyen, blanc). Pour rendre une photo grise, on remplace donc les trois composantes d'un pixel par une même valeur : leur moyenne.

def niveaux_de_gris(img):
    largeur, hauteur = img.size
    for y in range(0, hauteur):
        for x in range(0, largeur):
            r, v, b = img.getpixel((x, y))
            m = (r + v + b) // 3
            img.putpixel((x, y), (m, m, m))
  • m = (r + v + b) // 3 calcule la moyenne des trois composantes. Le // est la division entière : elle laisse tomber les chiffres après la virgule, car une composante doit rester un nombre entier.
  • (m, m, m) : trois composantes égales, donc un gris. Un pixel clair (grosses valeurs) donne un gris clair, un pixel sombre un gris foncé : la photo garde ses lumières, elle perd ses couleurs.

Et pour l'utiliser, on appelle la fonction sur la photo :

niveaux_de_gris(photo)
photo

Lis avant d'exécuter

Le réflexe à prendre avec un filtre : lire son calcul central et prédire l'effet avant de l'exécuter. Ici, « les trois composantes deviennent leur moyenne » se lit avant de voir la photo grise. C'est ce qu'on te demandera sur Capytale.

Filtre 2 : le négatif

Le négatif inverse les lumières : ce qui était à fond passe à zéro, ce qui était éteint passe à fond. Pour une composante, l'inversion s'écrit 255 - composante : 0 devient 255, 255 devient 0, 100 devient 155.

Le filtre a exactement la même forme que les niveaux de gris ; seul le calcul central change :

r, v, b = img.getpixel((x, y))
img.putpixel((x, y), (255 - r, 255 - v, 255 - b))

Sur Capytale, c'est toi qui écriras la fonction complète, sur le modèle de niveaux_de_gris.

Garder l'originale

Un filtre modifie l'image qu'on lui passe. Pour comparer avant/après, on travaille sur une copie : essai = photo.copy(), puis negatif(essai). L'originale reste intacte dans photo.

Ce qu'on retient

  • Image.open("photo.jpg") ouvre une photo ; r, v, b = img.getpixel((x, y)) lit un pixel (trois variables remplies d'un coup).
  • Un filtre = une fonction qui reçoit l'image en paramètre et, pour chaque pixel : lire, calculer, réécrire.
  • Niveaux de gris : les trois composantes deviennent leur moyenne (r + v + b) // 3 (le // garde un entier). Négatif : chaque composante devient 255 - composante.
  • On prédit l'effet d'un filtre en lisant son calcul central, avant de l'exécuter. Et on travaille sur une copie pour garder l'originale.
  • Le cours est ici ; on s'entraîne sur Capytale.

Et ensuite

Nos filtres appliquent le même calcul à tous les pixels. La prochaine étape est plus fine : décider, pixel par pixel, de ce qu'on fait selon sa couleur. Il nous faut pour ça une nouvelle construction : le si… sinon.