Aller au contenu

TP - Le module PIL

Ce TP a pour objectif de vous faire découvrir les instructions usuelles de manipulation d'image proposées par le module PIL.

Enregistrez les fichiers correspondant à chaque partie dans le dossier [B03_Images] préalablement créé dans votre répertoire personnel.

Présentation

Le module PIL de Python permet de manipuler un grand nombre de formats d'image. Ce module n'est pas inclus d'office dans Python, il faut le télécharger puis l'installer « à la main ».

Il est installé par défaut sur les ordinateurs du Lycée.
Pour l'installer chez vous (si ce n'est pas déjà fait), vous pouvez suivre le mode d'emploi ci-dessous.

Installer PIL sur sa machine

Voici comment installer ce module à partir d'un environnement de travail en Python (comme Pyzo qui sert d'exemple) :

  1. Lancer Pyzo Installer PIL

  2. Dans la console, entrer l'instruction :

    >>> pip install pillow
    

  3. pip recherche les bibliothèques nécessaires : Installer PIL

  4. Le module est téléchargé et prêt à l'emploi.

Pour plus d'informations sur les possibilités de ce module, on pourra consulter le site de référence.

L'image ci-dessous va servir de support dans les trois premières parties de ce TP. Vous pourrez ensuite tester vos fonctions à l'aide des images que vous choisirez vous-même.

Commencez par faire un clic droit sur l'image puis enregistrez-la dans le répertoire [B03_Images] sous le nom pommeRouge.png.

Pomme rouge

Rappel : manipuler des fichiers externes avec Python

Tout est rappelé dans ce chapitre. Le langage Python permet de créer, d'ouvrir et de manipuler des fichiers.

Raccourcis

Certains éditeurs Python, en particulier Pyzo, peuvent renvoyer une erreur s'ils sont mal paramétrés.

Dans le cas de Pyzo, il ne faut pas oublier de cocher la case Changer le répertoire lors de l'exécution d'un fichier, présente dans le menu [Exécuter].

Autre possibilité avec l'environnement Pyzo

Pour travailler sur des fichiers avec Pyzo, le plus simple est de déplacer le shell dans le répertoire courant du programme. Pour cela :

  1. Dans l'interface, repérer, en bas à droite, le gestionnaire d'arborescence :
    Fichiers

  2. En cliquant sur , on remonte d'un dossier. En cliquant sur un dossier, on l'ouvre. Ci-dessous, je suis remonté à la racine de mes disques durs :
    Fichiers

  3. Une fois dans le dossier désiré, cliquer sur l'étoile :
    Fichiers

  4. L'étoile devient jaune, avec une petite flèche en bas à droite. Cliquer sur cette petite flèche :
    Fichiers

  5. Dans la liste qui apparaît, sélectionner [Aller dans ce dossier (shell courant)] afin de changer de répertoire par défaut. Dans la console, apparaît alors le chemin absolu de ce répertoire :
    Fichiers

Cas général : Utilisation du module os

La manipulation précédente cache en fait l'utilisation du module os qui comporte de nombreuses commandes permettant d'interagir avec le système d'exploitation.
Voici les instructions à placer en début de programme qui permettent la réalisation automatique de la manipulation proposée plus haut :

import os           # Importation du module
rep = os.getcwd()    # Obtenir le chemin du répertoire courant, c'est-à-dire
                   # celui qui contient le fichier .py du programme
os.chdir(rep)        # Changer de répertoire courant

TPB03.11 - Obtenir des informations sur une image source

  1. Copiez/collez et exécutez le code ci-dessous dans un programme Python :

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    # Importation du module
    from PIL import Image
    
    # Importation/ouverture de l'image
    # Pour pouvoir être "manipulée", cette image est affectée à une variable intitulée source (dans cet exemple)
    source = Image.open('pommeRouge.png')
    
    # Récupération des dimensions
    largeur, hauteur = source.size
    print(f"Le nombre de colonnes est {largeur}, le nombre de lignes est {hauteur}.")
    
    # Récupération de l'encodage
    encodage = source.mode
    print(f"L'encodage des couleurs est {encodage}.")
    
    # récupération de la couleur du pixel supérieur gauche :
    couleur_pixel_haut_gauche = source.getpixel( (0, 0) )
    print(f"Le pixel en haut à gauche (colonne 0, ligne 0) a pour couleur {couleur_pixel_haut_gauche}.")
    
    # récupération de la couleur du pixel "central" :
    couleur_pixel_central = source.getpixel( (largeur//2, hauteur//2) )
    print(f"Le pixel central (colonne {largeur//21}, ligne {hauteur//2}) a pour couleur {couleur_pixel_central}.")
    
    Affichage obtenu
    Le nombre de colonnes est 640, le nombre de lignes est 423.
    L'encodage des couleurs est RGB.
    Le pixel en haut à gauche (colonne 0, ligne 0) a pour couleur (255, 255, 255).
    Le pixel central (colonne 30, ligne 211) a pour couleur (133, 24, 65).
    
  2. Identifiez les tuples utilisés par PIL ainsi que les instructions qui permettent de les obtenir, que ce soit pour l'image ou pour un pixel de l'image.

    Réponse
    • Pour l'image, le couple (largeur, hauteur) est renvoyé par l'instruction :

      source.size
      
      source est le nom de la variable qui fait référence à l'image source.

    • Pour chaque pixel de l'image, le couple de coordonnées (colonne, ligne) est utilisé par l'instruction :

      source.getpixel( (colonne, ligne) )
      
      Cette instruction renvoie le triplet de composante couleur dans le cas d'une image au format 'RGB' :
      couleur = source.getpixel( (colonne, ligne) )
      
      couleur est un triplet de trois entiers compris entre 0 et 255.

    Compléments
    1. Avec PIL, l'encodage des couleurs d'une image peut être :

      • 'RGBA' : couleurs avec gestion de la transparence ;

      • 'RGB' : couleurs sur trois composantes ;

      • 'L' : nuances de gris ;

      • '1' (le chiffre « 1 ») : noir & blanc.

    2. Dans le cas d'une image encodée en 'RGB', la variable couleur prend pour valeur le triplet d'entier (r, g, b) correspondant aux composantes couleurs du pixels. Par conséquent,

      • ou bien on récupère l'intégralité du triplet puis on extrait les composantes r, g, b une à une (quand/si on en a besoin) avec les instructions :

        couleur = source.getpixel( (x, y) )
        r = couleur[0]
        g = couleur[1]
        b = couleur[2]
        

      • ou bien on les récupère directement composante par composante avec l'instruction

        r, g, b = source.getpixel( (x, y) )
        

TPB03.12 - Modifier une image

Pomme verte

Dans cette partie, nous allons utiliser notre image de pomme rouge pour obtenir une belle pomme verte.

Le principe pour réaliser cette transformation :

  1. Créer une image « vide » de même dimensions et de même encodage que l'image initiale à l'aide de l'instruction Image.new( encodage, dimensions).

  2. Parcourir chaque pixel de l'image initiale.

  3. Pour chaque pixel parcouru, récupérer ses composantes couleur grâce à la méthode .getpixel().

  4. « Injecter » ces couleurs au même emplacement de l'image vide en ayant échangé les intensités de vert et de rouge grâce à la méthode
    .putpixel( (abscisse, ordonnee), composantes ).

  5. Sauvegarder ensuite l'image vide sous un nouveau nom puis l'afficher à l'écran.

Copier/coller/compléter le code ci-dessous afin de faire apparaître la pomme verte désirée.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
from PIL import Image

# ouverture de l'image :
source = Image.open('pommeRouge.png')

# récupération de ses dimensions :
largeur, hauteur = source.size

# création d'une image "vide" de mêmes caractéristiques :
result = Image.new(source.mode, source.size)


# parcours des pixels de l'image initiale :

for ...
    for ...
        ...
        ...

result.save('pommeVerte.png')
result.show()
Une solution
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
from PIL import Image

# ouverture de l'image :
source = Image.open('pommeRouge.png')

# récupération de ses dimensions :
largeur, hauteur = source.size

# création d'une image "vide" de mêmes caractéristiques :
result = Image.new(source.mode, source.size)


# parcours des pixels de l'image initiale :

for ligne in range(hauteur):
    for colonne in range(largeur):
        r, v, b = source.getpixel( (colonne, ligne) )
        result.putpixel( (colonne, ligne), (v, r, b) )

result.save('pommeVerte.png')
result.show()

Vous pouvez maintenant tenter de créer une pomme bleue facilement. Pomme bleue

Vous pouvez aussi tenter d'autres couleurs. Par exemple, remplacer le triplet (rouge, vert, bleu) par ((rouge+vert+bleu)//2, (rouge+vert+bleu)//2, 0), etc...

TPB03.13 - Modification plus importante

Pomme transposée Dans cette partie, nous allons voir comment obtenir la pomme ci-contre, transposée de l'image originale (ce n'est pas une rotation).

Les différentes étapes vont permettre de constituer un programme complet qui renverra la transposée de l'image d'origine, c'est-à-dire une image :

  • dont la hauteur et la largeur ont été échangées,
  • dont chaque pixel de coordonnées (x, y) dans l'image d'origine est associé à celui de coordonnées (y, x) dans la nouvelle image.

On rappelle que l'instruction Image.new( encodage, (largeur, hauteur) ) permet de définir une nouvelle image.

  1. Importez l'image d'origine puis mettez en mémoire ses caractéristiques.

  2. Définissez une nouvelle image de même encodage que l'ancienne, mais de largeur et de hauteur échangées.
    Ne pas oublier d'affecter cette nouvelle image à une variable...

  3. Parcourez chaque pixel de l'image d'origine.

  4. Récupérez les composantes couleur de ce pixel.
  5. Injectez ces composantes couleur dans le pixel correspondant de la nouvelle image.

  6. Sauvegardez la nouvelle image dans le répertoire courant avec un nouveau nom, sans oublier son format (.jpg, .gif, .bmp, etc...) ;

  7. Affichez cette image.

    Une piste : programme à compléter
     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    from PIL import Image
    
    # ouverture de l'image d'origine:
    source = ...
    
    # récupération des informations :
    encodage = ...
    largeur, hauteur = ...
    
    # Définition de la nouvelle image :
    resultat = ...
    
    # Parcours des pixels de l'image d'origine :
    for ...
        for ...
            couleur = ...
            resultat.putpixel( ..., ... )
    
    resultat.save( ... )
    ...
    
    Une solution
     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    from PIL import Image
    
    # ouverture de l'image d'origine:
    source = Image.open('pommeRouge.png')
    
    # récupération des informations :
    encodage = source.mode
    largeur, hauteur = source.size
    
    # Définition de la nouvelle image :
    resultat = Image.new(encodage, (hauteur, largeur))
    
    # Parcours des pixels de l'image d'origine :
    for y in range(hauteur):
        for x in range(largeur):
            couleur = source.getpixel( (x, y) )
            resultat.putpixel( (y, x), couleur )
    
    resultat.save('pommeRougeTranspose.png')
    resultat.show()
    

TPB03.14 - Une image à partir de rien

Grille Le module PIL permet de créer très facilement une image « ex-nihilo » en suivant ce protocole :

  1. Importer le module PIL.
  2. Définir les propriétés de l'image (largeur, hauteur et encodage de couleur).
  3. Créer et stocker la nouvelle image dans une variable.
  4. Parcourir chaque pixel de l'image pour le coloriser.
  5. Sauvegarder puis afficher l'image ainsi conçue.

Créez l'image ci-contre de dimensions 101 x 201 représentant une grille rouge sur fond gris clair dont les lignes horizontales et verticales sont espacées de 10 pixels.

Une piste : les composantes couleur
  • Le rouge « pur » correspond au triplet (255, 0, 0).
  • Le gris de cette image correspond au triplet (220, 220, 220).
Une autre piste : les étapes pas à pas
  1. Importer le module PIL.
  2. Créer une nouvelle image au formet 'RGB' et de dimensions 101 x 201.
  3. Parcourir chaque pixel de cette nouvelle image.
  4. Si l'abscisse ou l'ordonnée de ce pixel sont des multiples de 10, alors ce pixel est rouge.
  5. Sinon, il est noir.
  6. Enregistrer cette image sous le nom désiré.
  7. Afficher cette image si besoin.
Une dernière piste : un programme à compléter
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
from PIL import Image

# Variables et constantes :
largeur = ...
hauteur = ...
encodage = ...

# Définition de la nouvelle image :
resultat = Image.new(..., ...)

# Colorisation des pixels de l'image :
for ...
    for ...
        if ...
            resultat.putpixel(..., ...)
        else :
            resultat.putpixel(..., ...)

resultat.save(...)
...
Une solution
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
from PIL import Image

# Variables et constantes :
largeur = 101
hauteur = 201
encodage = 'RGB'

# Définition de la nouvelle image :
resultat = Image.new(encodage, (largeur, hauteur))

# Colorisation des pixels de l'image :
for x in range(largeur):
    for y in range(hauteur):
        if x%10 == 0 or y%10 == 0 :
            resultat.putpixel((x, y), (255, 0, 0))      # colorie en rouge les pixels pour
                                                        # lesquels x ou y sont des multiples de 10
        else :
            resultat.putpixel((x, y), (220, 220, 220))   # colorie en gris clair sinon

resultat.save('grille.png')
resultat.show()