Skip to main content Informatique en prépa - Lycée Malherbe 

10- Matrices de pixels et images

Une image numérique est un tableau de pixels (picture elements). C'est un bon support pour manipuler des tableaux à deux dimensions, c'est-à-dire des listes de listes.

I. Représentation d'une image

a) Images en niveaux de gris

Dans une image en niveaux de gris, chaque pixel est un entier entre 0 (noir) et 255 (blanc). Une image de hauteur et de largeur est représentée par une liste de lignes, chacune étant une liste de pixels.

Python

Loading editor...

# une petite image de 4 lignes et 5 colonnes : un carré blanc sur fond noir
petite = [[0,   0,   0,   0, 0],
          [0, 255, 255, 255, 0],
          [0, 255, 255, 255, 0],
          [0,   0,   0,   0, 0]]

print("hauteur :", len(petite))
print("largeur :", len(petite[0]))
print("pixel ligne 1, colonne 2 :", petite[1][2])

On accède à un pixel par image[y][x] : d'abord la ligne (l'ordonnée , comptée depuis le haut), puis la colonne (l'abscisse ).

b) Afficher une image

On utilise les bibliothèques PIL (pour lire le fichier) et matplotlib (pour afficher l'image) :

Python
code masqué : pas besoin de le lire pour suivre, exécutez-le simplement

c) Créer et parcourir une image

On crée une image vide (noire) de taille avec une liste de listes en compréhension :

Python

Loading editor...

def image_noire(h, w):
    return [[0 for x in range(w)] for y in range(h)]

degrade = image_noire(100, 256)
for y in range(100):
    for x in range(256):
        degrade[y][x] = x
imshow(degrade)

Ne pas écrire [[0] * w] * h : on obtiendrait fois la même ligne, et modifier un pixel modifierait toute la colonne !

On ne modifie en général pas l'image de départ : on en construit une nouvelle. Pour copier une image :

Python

Loading editor...

def copie(image):
    return [[pixel for pixel in ligne] for ligne in image]

II. Transformations géométriques

a) Symétrie et rotation

Pour une symétrie gauche-droite, le pixel en colonne de la nouvelle image est celui de la colonne de l'image de départ :

Python

Loading editor...

def symetrie(image):
    h, w = len(image), len(image[0])
    return [[image[y][w - 1 - x] for x in range(w)] for y in range(h)]

imshow(symetrie(guepiers))

Pour une rotation d'un quart de tour dans le sens horaire, l'image obtenue a pour hauteur la largeur de l'image de départ (et inversement). La ligne de la nouvelle image est la colonne de l'image de départ, lue de bas en haut :

Python

Loading editor...

def rotation(image):
    h, w = len(image), len(image[0])
    return [[image[h - 1 - x][y] for x in range(h)] for y in range(w)]

imshow(rotation(guepiers))

Vérifiez la formule de rotation sur la petite image de 2 lignes et 3 colonnes [[1, 2, 3], [4, 5, 6]].

Python

Loading editor...

rotation([[1, 2, 3], [4, 5, 6]])

b) Réduction et agrandissement

Pour réduire une image d'un facteur 2, on peut garder un pixel sur deux dans chaque direction, ou (mieux) remplacer chaque carré de pixels par leur moyenne :

Python

Loading editor...

def reduction(image):
    h, w = len(image), len(image[0])
    return [[(image[2*y][2*x] + image[2*y][2*x+1] + image[2*y+1][2*x] + image[2*y+1][2*x+1]) // 4
             for x in range(w // 2)] for y in range(h // 2)]

petite_image = reduction(reduction(reduction(guepiers)))
print(len(petite_image), "x", len(petite_image[0]))
imshow(petite_image)

Pour agrandir une image d'un facteur 2, le plus simple est de dupliquer chaque pixel en un carré de :

Python

Loading editor...

def agrandissement(image):
    h, w = len(image), len(image[0])
    return [[image[y // 2][x // 2] for x in range(2 * w)] for y in range(2 * h)]

imshow(agrandissement(petite_image))

L'image agrandie est "pixelisée" : on ne peut pas retrouver l'information perdue lors de la réduction.

III. Convolution

Beaucoup de traitements d'images (flou, détection de contours, netteté...) sont des convolutions : chaque pixel de l'image obtenue est une combinaison des pixels voisins dans l'image de départ.

Définition : convolution par un filtre

Soit une matrice (le filtre, ou noyau). La convolution d'une image par est l'image telle que, pour chaque pixel qui n'est pas sur le bord :

Voici le calcul, pixel par pixel, sur une petite image (choisissez le filtre) :

Image
20
20
20
20
20
20
20
20
200
200
200
20
20
20
20
200
200
200
20
20
20
20
200
200
200
20
20
20
20
20
20
20
250
20
20
20
20
20
20
20
250
20
20
20
20
20
20
20
20
Filtre
1/9
1/9
1/9
1/9
1/9
1/9
1/9
1/9
1/9
Résultat
1 / 26

Chaque pixel de sortie est la somme des 9 pixels autour, multipliés par les coefficients du filtre.

Python

Loading editor...

def convolution(image, filtre):
    h, w = len(image), len(image[0])
    resultat = image_noire(h, w)
    for y in range(1, h - 1):
        for x in range(1, w - 1):
            s = 0
            for i in range(3):
                for j in range(3):
                    s += image[y - 1 + i][x - 1 + j] * filtre[i][j]
            # on ramène la valeur entre 0 et 255
            resultat[y][x] = min(255, max(0, int(s)))
    return resultat

flou = [[1/9, 1/9, 1/9], [1/9, 1/9, 1/9], [1/9, 1/9, 1/9]]
contours = [[-1, -1, -1], [-1, 8, -1], [-1, -1, -1]]
imshow(convolution(petite_image, contours))

La complexité d'une convolution est en : pour chacun des pixels, on fait 9 multiplications.

Exercices

Exercice 1

Écrire une fonction negatif(image) qui renvoie le négatif de l'image : chaque pixel devient .

Python

Loading editor...

def negatif(image):
    return [[255 - p for p in ligne] for ligne in image]
Exercice 2

Écrire une fonction symetrie_verticale(image) qui renvoie l'image retournée "la tête en bas" (symétrie par rapport à l'axe horizontal).

Python

Loading editor...

def symetrie_verticale(image):
    h = len(image)
    return [image[h - 1 - y][:] for y in range(h)]
Exercice 3

Écrire une fonction seuillage(image, s) qui renvoie une image en noir et blanc : les pixels de valeur au moins s deviennent blancs (255), les autres noirs (0).

Python

Loading editor...

def seuillage(image, s):
    return [[255 if p >= s else 0 for p in ligne] for ligne in image]