Retoucher une photo avec Python
Manipule les pixels d'une image pour créer des effets : négatif, niveaux de gris, filtres de couleur, détection de contours.
Objectifs pédagogiques
- Comprendre la représentation des images numériques (pixels, RGB)
- Manipuler les pixels d'une image avec la bibliothèque PIL
- Créer des filtres simples (niveaux de gris, négatif, sépia)
- Modifier la luminosité et le contraste
- Appliquer des effets de flou et de détection de contours
Prérequis
- Python avec la bibliothèque Pillow (pip install Pillow)
- Une image à manipuler (photo personnelle ou image libre de droits)
- Connaissances de base en Python (boucles, fonctions)
Concepts clés
Modèle RGB
Chaque pixel est défini par 3 valeurs (0-255) : Rouge, Vert, Bleu. (255, 0, 0) = rouge pur, (0, 255, 0) = vert pur.
Niveaux de gris
On calcule la moyenne des 3 composantes : gris = (R + G + B) / 3. On obtient une valeur entre 0 (noir) et 255 (blanc).
Luminosité
On ajoute ou soustrait une valeur à chaque composante RGB pour éclaircir ou assombrir l'image.
Négatif
On inverse chaque composante : nouveau = 255 - ancien. Le noir devient blanc, le rouge devient cyan, etc.
Les étapes du projet
Charger et afficher une image
On utilise la bibliothèque PIL (Pillow) pour manipuler les images.
from PIL import Image
# Charger l'image
image = Image.open("photo.jpg")
# Afficher les informations
print(f"Taille : {image.size[0]} x {image.size[1]} pixels")
print(f"Mode : {image.mode}") # RGB, RGBA, L (gris)...
# Afficher l'image
image.show()
# Accéder aux pixels
pixels = image.load()
largeur, hauteur = image.size
# Lire un pixel (x=100, y=50)
r, g, b = pixels[100, 50]
print(f"Pixel (100, 50) : R={r}, G={g}, B={b}")Astuce : Tu peux utiliser n'importe quelle image JPG ou PNG. Pour ce projet, une photo de paysage ou un portrait fonctionne bien.
Convertir en niveaux de gris
Pour chaque pixel, on calcule la moyenne des composantes RGB.
def niveaux_de_gris(image):
"""Convertit une image en niveaux de gris"""
# Créer une copie pour ne pas modifier l'originale
gris = image.copy()
pixels = gris.load()
largeur, hauteur = gris.size
for x in range(largeur):
for y in range(hauteur):
r, g, b = pixels[x, y]
# Moyenne des composantes
moyenne = (r + g + b) // 3
# Appliquer le gris
pixels[x, y] = (moyenne, moyenne, moyenne)
return gris
# Appliquer le filtre
image_gris = niveaux_de_gris(image)
image_gris.save("photo_gris.jpg")
image_gris.show()Variante : Pour un gris plus naturel, utilise la formule de luminance :0.299*R + 0.587*G + 0.114*B
Créer le négatif
On inverse chaque composante de couleur (255 - valeur).
def negatif(image):
"""Crée le négatif d'une image"""
neg = image.copy()
pixels = neg.load()
largeur, hauteur = neg.size
for x in range(largeur):
for y in range(hauteur):
r, g, b = pixels[x, y]
# Inverser chaque composante
pixels[x, y] = (255 - r, 255 - g, 255 - b)
return neg
# Appliquer le filtre
image_neg = negatif(image)
image_neg.save("photo_negatif.jpg")
image_neg.show()Appliquer un filtre sépia
Le sépia donne un effet "photo ancienne" avec des tons bruns chauds.
def sepia(image):
"""Applique un filtre sépia à une image"""
sep = image.copy()
pixels = sep.load()
largeur, hauteur = sep.size
for x in range(largeur):
for y in range(hauteur):
r, g, b = pixels[x, y]
# Formules du filtre sépia
new_r = int(0.393 * r + 0.769 * g + 0.189 * b)
new_g = int(0.349 * r + 0.686 * g + 0.168 * b)
new_b = int(0.272 * r + 0.534 * g + 0.131 * b)
# Limiter à 255
new_r = min(255, new_r)
new_g = min(255, new_g)
new_b = min(255, new_b)
pixels[x, y] = (new_r, new_g, new_b)
return sep
# Appliquer le filtre
image_sepia = sepia(image)
image_sepia.save("photo_sepia.jpg")
image_sepia.show()Modifier la luminosité
On ajoute ou soustrait une valeur à chaque composante.
def luminosite(image, valeur):
"""
Modifie la luminosité d'une image
valeur > 0 : éclaircit
valeur < 0 : assombrit
"""
lum = image.copy()
pixels = lum.load()
largeur, hauteur = lum.size
for x in range(largeur):
for y in range(hauteur):
r, g, b = pixels[x, y]
# Ajouter la valeur et limiter entre 0 et 255
new_r = max(0, min(255, r + valeur))
new_g = max(0, min(255, g + valeur))
new_b = max(0, min(255, b + valeur))
pixels[x, y] = (new_r, new_g, new_b)
return lum
# Éclaircir de 50
image_claire = luminosite(image, 50)
image_claire.save("photo_claire.jpg")
# Assombrir de 50
image_sombre = luminosite(image, -50)
image_sombre.save("photo_sombre.jpg")Filtre de couleur unique
Garde uniquement une composante de couleur pour un effet artistique.
def filtre_couleur(image, couleur):
"""
Garde uniquement une couleur
couleur : 'rouge', 'vert' ou 'bleu'
"""
filtre = image.copy()
pixels = filtre.load()
largeur, hauteur = filtre.size
for x in range(largeur):
for y in range(hauteur):
r, g, b = pixels[x, y]
if couleur == 'rouge':
pixels[x, y] = (r, 0, 0)
elif couleur == 'vert':
pixels[x, y] = (0, g, 0)
elif couleur == 'bleu':
pixels[x, y] = (0, 0, b)
return filtre
# Créer les 3 versions
image_rouge = filtre_couleur(image, 'rouge')
image_vert = filtre_couleur(image, 'vert')
image_bleu = filtre_couleur(image, 'bleu')
image_rouge.save("photo_rouge.jpg")
image_vert.save("photo_vert.jpg")
image_bleu.save("photo_bleu.jpg")Créer une mosaïque de tous les effets
Combinons tous nos filtres dans une seule image.
def creer_mosaique(image):
"""Crée une mosaïque avec tous les effets"""
# Redimensionner pour que ça tienne
petite = image.resize((image.size[0] // 3, image.size[1] // 3))
# Appliquer les filtres
originale = petite.copy()
gris = niveaux_de_gris(petite)
neg = negatif(petite)
sep = sepia(petite)
claire = luminosite(petite, 50)
sombre = luminosite(petite, -50)
rouge = filtre_couleur(petite, 'rouge')
vert = filtre_couleur(petite, 'vert')
bleu = filtre_couleur(petite, 'bleu')
# Créer la mosaïque 3x3
largeur, hauteur = petite.size
mosaique = Image.new('RGB', (largeur * 3, hauteur * 3))
# Placer les images
images = [originale, gris, neg, sep, claire, sombre, rouge, vert, bleu]
for i, img in enumerate(images):
x = (i % 3) * largeur
y = (i // 3) * hauteur
mosaique.paste(img, (x, y))
return mosaique
# Créer et sauvegarder la mosaïque
mosaique = creer_mosaique(image)
mosaique.save("photo_mosaique.jpg")
mosaique.show()Critères d'évaluation
| Critère | Points |
|---|---|
| Chargement et sauvegarde d'images | 2 pts |
| Filtre niveaux de gris fonctionnel | 3 pts |
| Filtre négatif fonctionnel | 3 pts |
| Au moins 2 filtres supplémentaires | 4 pts |
| Code structuré en fonctions | 4 pts |
| Présentation des résultats (mosaïque ou comparaison) | 4 pts |
| Total | 20 pts |
Pour aller plus loin
Variante 1 : Détection de contours
Utilise le filtre de Sobel pour détecter les bords dans l'image.
Variante 2 : Flou gaussien
Implémente un effet de flou en moyennant les pixels voisins.
Variante 3 : Pixelisation
Crée un effet "pixel art" en réduisant la résolution.
Variante 4 : Incrustation
Remplace une couleur par une autre image (fond vert).
