Adloun

Matrices de pixels et images

Cours · informatique (tronc commun des prépas scientifiques), chapitre 8 · prépas scientifiques, tronc commun · le début du chapitre, la suite sur Adloun

Une image numérique est une matrice de pixels : chaque case porte une valeur de niveau de gris.

8.1 Introduction et motivation

Une photographie numérique n'est rien d'autre qu'un immense tableau de nombres : chaque case — un pixel — contient l'intensité lumineuse d'un point de l'image. Faire pivoter la photo, l'éclaircir, la flouter, détecter les contours d'un visage : toutes ces opérations, qui semblent relever de la magie des logiciels de retouche, sont des parcours de tableaux à deux dimensions — des doubles boucles du chapitre 3, appliquées à un objet qu'on peut voir.

C'est ce qui fait des images le terrain d'entraînement parfait pour les tableaux bidimensionnels, dont la manipulation est l'objectif technique du chapitre : création (et son piège fameux), parcours par lignes et par colonnes, transformations d'indices (rotations, symétries), changements d'échelle, et l'opération reine du traitement d'image — la convolution, où chaque pixel est recalculé à partir de ses voisins. Le bénéfice pédagogique est immédiat : une erreur d'indice, invisible dans un tableau de nombres, saute aux yeux sur une image — l'image penche, se déchire ou se brouille. Le débogage devient littéralement visuel.

8.2 Tableaux à deux dimensions

8.2.1 Listes de listes

Définition 8.1Tableau bidimensionnel

En Python, un tableau à deux dimensions de lignes et colonnes se représente par une liste de listes de longueur : M[i] est la ligne , et M[i][j] l'élément en ligne , colonne — avec et .


M = [[1, 2, 3],
     [4, 5, 6]]          # n = 2 lignes, p = 3 colonnes
M[1][2]                  # 6 : ligne 1, colonne 2
len(M)                   # n = 2 (nombre de lignes)
len(M[0])                # p = 3 (nombre de colonnes)

Convention à fixer une fois pour toutes : premier indice ligne, second colonne — l'ordre des mathématiques (matrices), qui n'est pas celui des coordonnées géométriques : la ligne croît vers le bas.

AttentionLe piège de la création

Pour créer une matrice nulle de , l'écriture suivante est un bogue célèbre :


M = [[0] * p] * n        # FAUX : n fois LA MÊME ligne !
M[0][0] = 1
# M == [[1, 0, 0], [1, 0, 0]] : TOUTES les lignes ont changé

[ligne] * n ne copie pas la ligne : il répète fois la même liste — modifier une case les modifie « toutes » (il n'y en a qu'une). La forme correcte crée une ligne neuve à chaque tour :


M = [[0] * p for _ in range(n)]      # n lignes indépendantes

([0] * p reste licite : les entiers ne sont pas modifiables, partager des est sans danger — c'est le partage de listes qui mord.)

8.2.2 Les deux parcours

Méthode : Parcours ligne par ligne, colonne par colonne


for i in range(n):              # ligne par ligne (l'ordre naturel)
    for j in range(p):
        ... M[i][j] ...

for j in range(p):              # colonne par colonne
    for i in range(n):
        ... M[i][j] ...

Les deux visitent les cases — coût , le « linéaire » des images (on dit aussi : linéaire en le nombre de pixels). Le choix de l'ordre suit le problème : sommes par ligne premier parcours ; sommes par colonne second.

Exemple 8.2Premières fonctions sur matrices

def somme_matrice(M: list) -> float:
    s = 0
    for ligne in M:
        for x in ligne:
            s += x
    return s

def maximum_matrice(M: list) -> float:
    """Précondition : M non vide."""
    m = M[0][0]
    for ligne in M:
        for x in ligne:
            if x > m:
                m = x
    return m

Quand les indices ne servent pas, le parcours par valeurs à deux étages est le plus lisible — exactement la règle du chapitre 2, doublée.

Exercices du chapitre

La suite du cours · Tous les exercices du chapitre