Introduction à la bibilothèque PIL


Le module PIL de python permet de manipuler des images.

Installation

Avec python 3 on doit installer le package Pillow (il est installé par défaut avec Anaconda)

In [ ]:
pip install Pillow

Chargement et création d'images

Pour charger une image on procède ainsi :

In [ ]:
from PIL import Image

img = Image.open('logo.png')

On peut alors visualiser l'image en utilsant show() :

In [ ]:
img.show() # Utilise le logiciel associé du système d'exploitation, peut ne pas s'effectuer selon les droits de l'utilisateur
img # plus direct dans la console, on utilise cette technique dans la suite de la présentation

On peut accéder aux dimensions d'une image à l'aide de size :

In [ ]:
largeur, hauteur = img.size

print(largeur)
print(hauteur)

Pour créer une image on procède ainsi :

In [ ]:
new_img = Image.new("RGB", (400, 300), "red")

new_img

L'instruction précédente crée ainsi une image :

  • au format RGB (Red, Green, Blue)
  • de 800 px de large sur 500 px de haut
  • initialement remplie de rouge

Pour sauvegarder une image on utilise save('nom_du_fichier.format') :

In [ ]:
new_img.save('nouvelle_image.png')

Modification d'images

Une fois une image ouverte, il est possible de la modifier rapidement. Chacune des commandes ci-dessous renvoie une image. On peut donc créer une nouvelle variable afin de la stocker en faisant par exemple img_rotation = img.rotate(45)

  • Pour la redimensionner :
In [ ]:
img = img.resize((largeur//4,hauteur//4))
img
  • Pour la tourner :
In [ ]:
img.rotate(45)
  • Pour lui faire subir une réflexion (différentes options possibles) :
    • FLIP_LEFT_RIGHT
    • FLIP_TOP_BOTTOM
In [ ]:
img.transpose(Image.FLIP_LEFT_RIGHT)

Manipuler les pixels

Une image matricielle a pour structure un tableau de pixels (en mathématiques, une matrice est un tableau de nombres).

Chaque pixel a une position précise et une couleur précise.

Les positions sont repérées à l'aide de deux coordonnées x et y dont l'origine se situe en haut à gauche de l'image.

title

Accéder aux couleurs d'un pixel

Avant de travailler sur les couleurs des pixels de l'image, il faut générer un tableau contenant l'ensemble des couleurs.

Ce tableau contient une case par pixel. Il a autant de lignes que l'image est haute et autant de colonnes qu'elle est large.

Pour ce faire on utilise la fonction load() de PIL. Elle renvoie le tableau de valeurs que l'on stocke dans une variable.

In [ ]:
tableau = img.load()

# On "affiche" l'objet
print(tableau)

#On affiche les couleurs des 10 premiers pixels de la cinquième ligne (indice 4) de l'image :
# Avec logo.png, ils sont tous noirs (0,0,0) et transparents (le 4ème 0)
for i in range(10) :
    print(tableau[4,i])

Les couleurs d'un pixel de coordonnées (x,y) sont accessibles à l'aide de tableau[x,y].

In [ ]:
# Les dimensions de l'image
largeur, hauteur = img.size

# Les couleurs du pixels du centre de l'image (le // est la division entière)
tableau[largeur//2, hauteur//2]

Comme on peut le voir on obtient un quadruplet en résultat :

  • la valeur du canal rouge
  • la valeur du canal vert
  • la valeur du canal bleu
  • la valeur du canal alpha (transparence)

Ces différentes valeurs varient entre $0$ et $255$. Il y en a donc $2^8$ ce qui indique que chaque canal ets codé sur un octet.

Le canal alpha ne se trouve pas sur toutes les images. Il n'apparaît jamais sur les images jpg.

Il est ainsi possible de récupérer les valeurs de chaque canal d'un pixel dans des variables et de faire des calculs dessus :

In [ ]:
rouge, vert, bleu, alpha = tableau[100,100]

print("Le rouge vaut :",rouge,". Le vert :", vert, sep="") # le sep='' ôte les espaces entre les chaînes

rouge = rouge // 2 # on divise le rouge par 2, le double // permet d'arrondir à l'entier par troncature
vert = min (255, vert * 2) # on double le vert et l'on ne garde que la valeur minimal entre le résultat et 255

print("Le rouge vaut :",rouge,". Le vert :", vert, sep="")

Modifier les couleurs d'un pixel

On peut modifier les couleurs d'un pixel de coordonnées en modifiant directement la valeur de la case (i,j) du tableau.

La nouvalle valeur doit être un triplet (rouge, vert, bleu) ou un quadruplet (rouge, vert, bleu, transparence) selon le type d'image.

In [ ]:
tableau[0,0] = (255,0,0,255)
img

Notez bien l'unique pixel rouge opaque en haut à gauche !

On peut dès lors modifier l'image dans son ensemble :

In [ ]:
largeur, hauteur = img.size # on met à jour ces valeurs car on a redimensionné l'image

for i in range(largeur) :
    tableau[i,50] = (255, 0, 0, 255)

img

On a parcouru la ligne 50 en entier et passé tous les pixels en vert opaque (255, 0, 0, 255)

Un dernier exemple : on joue avec les couleurs !

In [ ]:
for i in range(largeur) :
    for j in range(hauteur) :
        rouge, vert, bleu, alpha = img.getpixel((i,j))
        rouge = min(255, rouge * 2)
        vert = min(255, vert * 3)
        bleu = min(255, bleu * 4)
        tableau[i,j] = (rouge, vert, bleu, alpha)

img

Remarque :

Deux autres fonctions de PIL permettent d'accéder et de modifier les couleurs des pixels sans passer par le tableau :

  • getpixel((i,j)) : renvoie la couleur du pixel de coordonnées (i,j). Attention à la double parenthèse
  • putpixel((i,j), (r,g,b,a) : fixe la couleur du pixel (i,j) à (r,g,b,a) (utiliser (r,g,b) s'il n'y pas de transparence)

Plus immédiates, ces fonctions sont toutefois plus lentes à utiliser... On leur préfèrera donc les méthodes décrites ci-dessus.

Exercices :

  1. Saisir le code ouvrant l'image "logo.png", et la redimensionnant en 300 * 220
In [ ]:
# Saisir le code ici
  1. Afficher dans la console (avec un print()) la valeur du canal rouge du pixel du centre de l'image
In [ ]:
# Saisir le code ici
  1. Passer le pixel du centre de l'image en cyan (vert + bleu)
In [ ]:
# Saisir le code ici