Python Imaging Library (PIL)

Pillow logo

Par Quentin Vaucher. 1

Introduction

Pillow est une bibliothèque de traitement d’image, qui est un fork et successeur du projet PIL (Python Imaging Library). Elle est conçue de manière à offrir un accès rapide aux données contenues dans une image, et offre un support pour différents formats de fichiers tels que PPM, PNG, JPEG, GIF, TIFF et BMP.

Pillow dispose de capacités de traitement d’images relativement puissantes, et a pour but d’offrir une solide base à toute application générale de traitement d’images.

Domaines d’utilisations

La bibliothèque de fonctions peut être utilisée pour différents types d’activités, telles que:

Archivage d’images:

Création de miniatures, conversion d’images d’un format de fichier à un autre, …

Affichage d’images:

Création et affichage d’images via le module PIL.ImageTk, ou PIL.ImageWin sous Windows. Ouverture d’une image dans un utilitaire externe via la méthode PIL.Image.Image.show().

Traitement d’images:

Offre un support pour quelques fonctions de bases telles que le filtrage, la convolution ou encore la conversion d’espaces couleurs. Il est également possible de redimensionner et d’appliquer des transformations géométriques à l’image (rotation, …).

Concepts

La bibliothèque utilise le principe d’images matricielles (par opposition aux images vectorielles), c’est-à-dire que chaque élément de la matrice représente un point avec une couleur associée (= un pixel). Pillow utilise également les concepts des bandes et de modes décrits ci-dessous:

Bandes:

Les images sont constituées de bandes de données (une ou plusieurs, pour autant que celles-ci aient toute les mêmes dimensions et profondeurs). Un exemple commun de bandes est celles sous la forme RGBA, qui sépare les informations sur le rouge, le vert, le bleu et la transparence. Il est ainsi possible de réaliser différentes actions qui agissent que sur une seule bande. Finalement, du point de vue des pixels, on peut dire qu’ils disposent tous d’une valeur par bande.

Modes:

Ils définissent le type et la profondeur des pixels d’une image. Parmi les modes les plus connus, on peut notamment citer RGB et RGBA, qui représentent les pixels sur respectivement 3x8 bits et 4x8 bits.

Exemples

Le premier exemple ci-dessous permet dans un premier temps de se familiariser avec le fonctionnement de base de Pillow, puis une fois les bases acquises, un deuxième exemple plus technique met en évidence la puissance de la bibliothèque.

Exemple basique

L’exemple suivant aborde de manière simple quelques notions de bases de Pillow. Une image en couleur au format .png est récupérée et convertie en nuances de gris. Le résultat s’affiche puis est sauvegardé au format .jpeg.

1
2
3
4
5
6
7
8
9
"""Exemple simple de manipulation d'images."""

from PIL import Image

image = Image.open("../../_static/pillow.png")
image = image.convert('L')

image.show()
image.save('gray-pillow.jpeg', 'jpeg')

Le résultat obtenu est le suivant:

Logo de Pillow en nuances de gris

Exemple technique

Dans cet exemple, le logo de la bibliothèque Pillow subit diverses modifications afin de mettre en pratique quelques fonctions de la bibliothèque. Le logo est d’abord flouté à l’aide d’un filtre, puis transposé afin d’inverser la position de chaque python. On parcourt ensuite tous les pixels, puis on colorie l’arrière-plan en étudiant les attributs de chacun d’eux (couleurs et position).

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
"""Filtrage, transformation, puis coloration."""

from PIL import Image, ImageFilter

img = Image.open("../../_static/pillow.png")

img = img.filter(ImageFilter.BLUR) \
         .transpose(Image.FLIP_TOP_BOTTOM) \
         .transpose(Image.FLIP_LEFT_RIGHT)

width, height = img.size

WHITE_THRESHOLD = 250
PURPLE = (155, 89, 182)
BLUE = (52, 152, 219)

for x in range(width):
    for y in range(height):
        pixel = img.getpixel((x, y))
        # On vérifie que le pixel est blanc
        if all(channel > WHITE_THRESHOLD for channel in pixel):
            if x + y <= width:
                img.putpixel((x, y), PURPLE)
            else:
                img.putpixel((x, y), BLUE)

img.show()

Le résultat obtenu est le suivant:

Cercle Trigonométrique

Manipulation des bandes

Pillow utilise le concept des bandes de données, qu’il est possible de traiter séparément. L’exemple suivant illustre cette notion, en inversant l’ordre des bandes R, G et B.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
"""Inversion des bandes (r, b, g) d'une image."""

from PIL import Image

image = Image.open("../../_static/pillow.png")

r, g, b, a = image.split()

image = Image.merge("RGBA", (b, r, g, a))

image.show()

Le résultat obtenu est le suivant:

Suppression du rouge dans le logo de Pillow

Méthodes de dessin

Pillow fournit également des outils de base pour le graphisme 2D. Toutes ces fonctions sont regroupées dans le module PIL.ImageDraw. Il est possible de dessiner diverses formes géométriques, ainsi que du texte, dans le but de créer ou retoucher des images. L’exemple suivant met en évidence quelques-unes des fonctionnalités disponibles.

  • PIL.Image.new() crée une image avec la taille et la couleur spécifiée;

  • PIL.ImageDraw.Draw crée un objet qui peut être utilisé pour dessiner sur l’image;

  • PIL.ImageDraw.Draw.line dessine une ligne entre les points donnés, avec la couleur choisie;

  • PIL.ImageDraw.Draw.ellipse dessine une ellipse à l’intérieur du rectangle donné;

  • PIL.ImageDraw.Draw.text dessine du texte à l’endroit choisi.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
"""Graphisme 2D avec Pillow."""

from PIL import Image, ImageDraw

width = 160
height = 160

WHITE = (255, 255, 255, 0)

image = Image.new('RGBA', (width, height), WHITE)

# Obtention du contexte graphique
draw = ImageDraw.Draw(image)

GREEN = (0, 255, 0, 0)
draw.line((0, height//2,
          width, height//2),
          fill=GREEN)
draw.line((width//2, 0,
          width//2, height),
          fill=GREEN)

BLUE = (0, 0, 255, 0)
draw.ellipse((width//4, height//4,
             3*width//4, 3*height//4),
             outline=BLUE)

BLACK = (0, 0, 0, 0)
draw.text((20, 20), "Cercle trigonométrique", fill=BLACK)

image.show()
image.save("cercleTrigo.png", "png")

Le résultat obtenu est le suivant:

Cercle Trigonométrique

Utilisation au sein du binding PyQt

Pillow fournit le module PIL.ImageQt afin de créer des Qimage directement utilisables par le binding PyQt. En effet, le module dispose d’une classe du même nom qui est une sous-classe de QtGui.QImage. Il est donc possible de passer l’objet directement à l’API PyQt. On peut donc utiliser la puissance de Pillow pour réaliser du traitement d’images au sein d’un logiciel graphique utilisant le Framework Qt.

Le code suivant illustre un telle utilisation, permettant à l’utilisateur de visualiser une image, de la tourner et de la flouter. Le traitement de l’image est réalisé à l’aide de Pillow, tandis que l’interface utilisateur utilise l’api PyQt. La fonction initUI() n’est pas présentée car elle ne sert qu’à mettre en place les différents composants visuels.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
"""Manipulation d'une image graphiquement à l'aide de PyQt."""

import sys

from PIL import Image, ImageFilter, ImageQt
from PyQt5.QtGui import QPixmap
from PyQt5.QtWidgets import (QApplication, QHBoxLayout, QLabel, QPushButton,
                             QVBoxLayout, QWidget)


class Viewer(QWidget):
    """Affiche une image avec trois boutons pour la pivoter/flouter."""

    def __init__(self):
        """Initialisation des composants, connexion signaux/slots."""
        super().__init__()

        self.blurButton = QPushButton("Blur")
        self.rotateLeftButton = QPushButton("Rotate Left")
        self.rotateRightButton = QPushButton("Rotate Right")

        self.label = QLabel()
        self.image = Image.open("../../_static/pillow.png")

        self.initUI()
        self.displayImage()

        self.blurButton.clicked.connect(self.blurImage)
        self.rotateLeftButton.clicked.connect(self.turnLeft)
        self.rotateRightButton.clicked.connect(self.turnRight)

    def displayImage(self):
        """PIL Image -> QImage -> QPixmap.

        Convertit une image de la bibliothèque PIL en QImage. Convertit
        l'objet QImage en QPixmap puis affiche le résultat dans un label.
        """
        self.label.setPixmap(QPixmap.fromImage(ImageQt.ImageQt(self.image)))

    def turnLeft(self):
        """Tourne l'image à gauche à l'aide de PIL puis affiche le résultat."""
        self.image = self.image.rotate(90)
        self.displayImage()

    def turnRight(self):
        """Tourne l'image à droite à l'aide de PIL puis affiche le résultat."""
        self.image = self.image.rotate(-90)
        self.displayImage()

    def blurImage(self):
        """Floute l'image à l'aide de PIL puis affiche le résultat."""
        self.image = self.image.filter(ImageFilter.BLUR)
        self.displayImage()

if __name__ == '__main__':
    app = QApplication(sys.argv)

    viewer = Viewer()
    viewer.resize(450, 400)
    viewer.setWindowTitle('Image Viewer')
    viewer.show()

    sys.exit(app.exec_())

Le résultat obtenu est le suivant:

Application PyQt avec traitement d'images réalisé par Pillow

Conclusion

Pillow est une bibliothèque relativement complète qui offre la possibilité de manipuler des images avec une grande simplicité. Elle dispose d’une large palette de fonctions qui touche à différents domaines allant du filtrage au graphisme, en passant par la manipulation de pixels.

La bibliothèque se positionne donc plutôt comme une bibliothèque à vocation généraliste dans le domaine du traitement d’images, et ne se démarque donc dans aucun domaine spécifique.

Pour conclure, les quelques exemples abordés dans cet article offrent un bon aperçu du fonctionnement de Pillow, mais ne couvrent en aucun cas toutes ses possibilités. Pour une liste plus exhaustive et plus détaillée, la documentation officielle semble être la candidate idéale.

1

<quentin.vaucher@he-arc.ch>