Traitement
d'Images
Guide de revision complet — theorie, syntaxe Python, et corrections commentees pour chaque exercice.
Passage en niveaux de gris
Transformer une image couleur (triplets RGB) en matrice d'entiers [0..255]
Une image couleur est une matrice de triplets [R, G, B] où chaque composante est dans [0..255]. Convertir en niveaux de gris consiste à remplacer chaque triplet par un seul entier représentant la luminosité.
- Q1 — Moyenne simple : les 3 canaux ont le même poids →
g = (R + G + B) // 3 - Q2 — Luminance pondérée : l'œil humain est bien plus sensible au vert qu'au bleu →
g = int(0.2126·R + 0.7152·G + 0.0722·B)
La formule Q2 donne un résultat visuellement plus fidèle à ce que l'œil perçoit réellement comme "luminosité".
for (r, g, b) in row: ...
[[expr for pix in row] for row in img]
(r + g + b) // 3
int(0.2126*r + 0.7152*g + 0.0722*b)
def to_grayscale(img): """ Convertit une image couleur en niveaux de gris (moyenne simple). Paramètre : img — matrice de triplets [R, G, B] Retour : matrice d'entiers dans [0..255] """ # Pour chaque ligne, pour chaque pixel (r,g,b), calculer la moyenne return [[(r + g + b) // 3 for (r, g, b) in row] for row in img] # ── Test ────────────────────────────────────── img = [[[120, 60, 30], [255, 255, 255]]] print(to_grayscale(img)) # [[70, 255]]
def to_grayscale2(img): """ Convertit une image couleur en niveaux de gris (luminance pondérée). Formule : g = int(0.2126*R + 0.7152*G + 0.0722*B) Paramètre : img — matrice de triplets [R, G, B] Retour : matrice d'entiers dans [0..255] """ # Coefficients ITU-R BT.709 — standard de la télévision haute définition return [[int(0.2126*r + 0.7152*g + 0.0722*b) for (r, g, b) in row] for row in img]
Les fonctions attendent une liste de listes Python. Une image réelle se charge via matplotlib ou PIL — il faut la convertir en liste, appliquer la fonction, puis afficher le résultat.
Le pipeline complet est le suivant :
- Charger l'image → obtenir un tableau NumPy de forme
(h, w, 3) - Convertir en liste Python avec
.tolist() - Appliquer
to_grayscale()outo_grayscale2() - Afficher le résultat avec
imshow(..., cmap='gray')
import matplotlib.pyplot as plt import matplotlib.image as mpimg # ── 1. Charger l'image (tableau NumPy de forme h×w×3) ────────────── img_np = mpimg.imread('mon_image.jpg') # ── 2. Convertir en liste Python (liste de listes de triplets) ────── img = img_np.tolist() # ── 3. Appliquer les deux fonctions ──────────────────────────────── gray1 = to_grayscale(img) # moyenne simple gray2 = to_grayscale2(img) # luminance pondérée # ── 4. Afficher les trois images côte à côte ─────────────────────── fig, axes = plt.subplots(1, 3, figsize=(15, 5)) axes[0].imshow(img_np) axes[0].set_title('Originale (RGB)') axes[0].axis('off') axes[1].imshow(gray1, cmap='gray') axes[1].set_title('Moyenne simple') axes[1].axis('off') axes[2].imshow(gray2, cmap='gray') axes[2].set_title('Luminance (Q2)') axes[2].axis('off') plt.tight_layout() plt.show()
[0..255]. Si c'est un PNG, elles peuvent être des flottants dans [0.0..1.0] — multiplier par 255 dans ce cas : int(val * 255) dans les fonctions, ou faire (img_np * 255).astype(int).tolist() avant de convertir.
from PIL import Image import numpy as np import matplotlib.pyplot as plt # Charger et convertir en liste Python img = list([list(row) for row in np.array(Image.open('mon_image.jpg'))]) # Appliquer et afficher gray = to_grayscale2(img) plt.imshow(gray, cmap='gray') plt.axis('off') plt.show()
Filtrage des couleurs
Isoler un seul canal RGB, mettre les deux autres à zéro
Une image RGB stocke 3 canaux indépendants par pixel. Filtrer une couleur signifie conserver uniquement un canal et forcer les deux autres à 0.
- Filtre rouge :
(120, 80, 200) → (120, 0, 0) - Filtre vert :
(120, 80, 200) → (0, 80, 0) - Filtre bleu :
(120, 80, 200) → (0, 0, 200)
La version générique utilise un index canal ∈ {0, 1, 2} pour sélectionner dynamiquement quel canal conserver.
[r, 0, 0] # rouge [0, g, 0] # vert [0, 0, b] # bleu
[p[canal] if i==canal else 0 for i in range(3)]
def rouge(img): """Filtre uniquement le canal rouge. G=0, B=0.""" return [[[r, 0, 0] for [r, g, b] in row] for row in img] def vert(img): """Filtre uniquement le canal vert. R=0, B=0.""" return [[[0, g, 0] for [r, g, b] in row] for row in img] def bleu(img): """Filtre uniquement le canal bleu. R=0, G=0.""" return [[[0, 0, b] for [r, g, b] in row] for row in img] # Test : rouge([[255, 100, 50]]) → [[255, 0, 0]]
def filtre(img, canal): """ Filtre générique : conserve le canal d'index `canal`. canal : 0 = rouge, 1 = vert, 2 = bleu """ # Pour chaque pixel p, garder p[canal] à sa position, 0 ailleurs return [[[p[canal] if i == canal else 0 for i in range(3)] for p in row] for row in img] # rouge = filtre(img, 0) | vert = filtre(img, 1) | bleu = filtre(img, 2)
Réorganisation des pixels
Une fonction générique pilote toutes les transformations géométriques
Toutes les transformations géométriques partagent la même idée fondamentale : d'où vient le pixel (i, j) du résultat dans l'image source ?
- Symétrie horizontale (flip vertical) : ligne i ← ligne
n-1-i, colonnes inchangées - Symétrie verticale (flip horizontal) : colonne j ← colonne
m-1-j, lignes inchangées - Rotation 90° horaire : résultat de dimensions m×n. Pixel (i,j) du résultat ← pixel
(n-1-j, i)de la source - Rognage : pixel (i,j) du résultat ← pixel
(i0+i, j0+j)de la source
cree_image(img, n, m, f) abstrait tout cela : on passe une fonction de correspondance f(i,j) → (i', j') et elle construit la nouvelle image.
f = lambda i, j: (n-1-i, j)
img[f(i,j)[0]][f(i,j)[1]]
raise ValueError("message")
n = len(img) m = len(img[0])
def cree_image(img, n, m, f): """ Crée une nouvelle image n×m en réorganisant les pixels de img. f : (i,j) → (i',j') indique d'où vient chaque pixel du résultat. """ return [[img[f(i,j)[0]][f(i,j)[1]] for j in range(m)] for i in range(n)]
def symetrie_horizontale(img): """Retourne l'image verticalement (haut ↔ bas).""" n, m = len(img), len(img[0]) # Ligne i du résultat ← ligne n-1-i de la source return cree_image(img, n, m, lambda i, j: (n-1-i, j))
def symetrie_verticale(img): """Retourne l'image horizontalement (gauche ↔ droite).""" n, m = len(img), len(img[0]) # Colonne j du résultat ← colonne m-1-j de la source return cree_image(img, n, m, lambda i, j: (i, m-1-j))
def rotation(img): """Rotation 90° sens horaire. Dimensions résultat : m×n.""" n, m = len(img), len(img[0]) # Résultat : m lignes × n colonnes (dimensions inversées !) # Pixel (i,j) du résultat ← pixel (n-1-j, i) de la source return cree_image(img, m, n, lambda i, j: (n-1-j, i))
m, n (et non n, m) à cree_image — c'est l'erreur classique sur cette question.
def rognage(img, i0, j0, nbl, nbc): """ Extrait une sous-image de dimensions nbl×nbc. Hypothèses : i0 >= 0, j0 >= 0, nbl > 0, nbc > 0 i0 + nbl <= len(img), j0 + nbc <= len(img[0]) """ # Vérification des hypothèses — lever une exception si violation if i0 < 0 or j0 < 0 or nbl <= 0 or nbc <= 0: raise ValueError("Paramètres invalides") if i0 + nbl > len(img) or j0 + nbc > len(img[0]): raise ValueError("Dépassement des bords de l'image") # Pixel (i,j) du résultat ← pixel (i0+i, j0+j) de la source return cree_image(img, nbl, nbc, lambda i, j: (i0+i, j0+j))
Transformation du photomaton
Disperse les pixels en 4 copies réduites arrangées en grille 2×2
Le photomaton prend une image h×l (h et l pairs) et produit une image de même taille contenant 4 copies réduites de moitié, disposées en grille 2×2.
Le principe : les pixels aux positions paires/impaires (lignes et colonnes) sont séparés dans les 4 quadrants.
- Source
(2i, 2j)→ résultat haut-gauche(i, j) - Source
(2i, 2j+1)→ résultat haut-droite(i, j + l//2) - Source
(2i+1, 2j)→ résultat bas-gauche(i + h//2, j) - Source
(2i+1, 2j+1)→ résultat bas-droite(i + h//2, j + l//2)
Appliquée de façon répétée, la transformation finit par revenir à l'image originale — c'est une permutation réversible de pixels, ce qui la rapproche conceptuellement d'un chiffrement par transposition.
result = [[None] * l for _ in range(h)]
h % 2 == 0 # True si pair
h // 2 # nb de lignes/quadrant l // 2 # nb de colonnes/quadrant
for i in range(h//2): for j in range(l//2): ...
def photomaton(img): """ Calcule la transformation du photomaton de img. Hypothèse : len(img) et len(img[0]) doivent être pairs. Retour : nouvelle matrice de même taille que img. """ h = len(img) l = len(img[0]) # Vérifier que les dimensions sont bien paires if h % 2 != 0 or l % 2 != 0: raise ValueError("Les dimensions doivent être paires") # Créer une matrice résultat vide de même taille result = [[None] * l for _ in range(h)] # Remplir les 4 quadrants simultanément for i in range(h // 2): for j in range(l // 2): result[i][j] = img[2*i ][2*j ] # haut-gauche result[i][j + l//2] = img[2*i ][2*j+1] # haut-droite result[i + h//2][j] = img[2*i+1][2*j ] # bas-gauche result[i + h//2][j + l//2] = img[2*i+1][2*j+1] # bas-droite return result # Test minimal (image 4×4 pour voir l'effet) # Appliquer photomaton() plusieurs fois ramène à l'image originale.