Vous avez déjà remarqué que vos photos de nourriture sous l'éclairage d'un restaurant prennent une teinte jaunâtre, que vos paysages par temps nuageux semblent terne, ou que la couleur d'un vêtement en ligne apparaît différente à la réception ? Ces problèmes sont dus aux distorsions colorimétriques ! La restauration des couleurs vise à corriger ces déviations causées par l'équipement, l'éclairage et d'autres facteurs, pour restituer aux images leur apparence véritable. Que ce soit pour la photographie quotidienne, la restauration de vieilles photos, la conception graphique, ou la création artistique, cette technique est essentielle. Cet article explique les algorithmes principaux de restauration des couleurs avec des exemples de code Python directement exécutables.
Pourquoi les images subissent-elles des distorsions colorimétriques ?
Les distorsions colorimétriques ne signifient pas que votre équipement est "défectueux". Elles résultent de l'interaction de plusieurs facteurs lors du processus d'imagerie. Les quatre causes principales sont :
- L'influence de l'éclairage : Différentes sources lumineuses ont des températures de couleur différentes (par exemple, les lampes à incandescence sont jaunes, les lampes fluorescentes sont bleutées), ce qui fait dévier les couleurs des objets. Une chemise blanche photographiée sous la lumière d'une chambre prendra une teinte jaunâtre dans l'image.
- Les différences d'équipement : Les téléphones, appareils photo et moniteurs n'ont pas la même sensibilité colorimétrique, entraînant "des couleurs différentes pour le même objet". Comme lors de l'achat de vêtements où la couleur bleu ciel sur votre téléphone apparaît cyan sur votre ordinateur.
- Les conditions environnementales : La lumière diffuse par temps nuageux, les reflets sur les vitres, voire l'humidité de l'air, peuvent assombrir ou déplacer les couleurs. Un paysage photographié à travers une fenêtre semble souvent terne et sans vie.
- Les manipulations post-traitement : Les réglages de couleur excessifs, la compression d'images ou les conversions de format peuvent également provoquer des distorsions. Une image sauvegardée plusieurs fois verra sa qualité se dégrader progressivement.
L'objectif principal de la restauration colorimétrique est de corriger ces déviations par des techniques appropriées pour rapprocher les couleurs de l'image de ce que l'œil humain perçoit réellement.
Principes fondamentaux de la restauration des couleurs
La restauration des couleurs repose sur un principe simple : établir une relation de mappage entre l'image distordue et l'image réelle. En d'autres termes, il s'agit de trouver une "formule de correction" qui, une fois appliquée aux données colorimétriques distordues, permet de calculer les couleurs originales.
Ce processus peut être comparé à un "étalonnage colorimétrique" de l'image distordue, similaire à redresser un cadre de photo penché. En ajustant la luminosité et les proportions des canaux rouge (R), vert (G) et bleu (B), on fait revenir l'ensemble des couleurs à un état plus fidèle.
Les différentes causes de distorsion correspondent à différentes "formules de correction", ce qui constitue la différence fondamentale entre les algorithmes présentés ci-dessous.
Algorithmes principaux de restauration des couleurs (avec implémentations Python)
Selon la complexité et les cas d'usage, les algorithmes de restauration des couleurs peuvent être classés en trois catégories : "algorithmes statistiques pour débutants", "algorithmes physiques professionnels" et "algorithmes d'apprentissage profond avancés". Nous commencerons par les plus accessibles, en expliquant leur utilisation dans des scénarios quotidiens.
3.1 Algorithmes statistiques pour débutants (accessibles et rapides à mettre en œuvre)
Ces algorithmes ne nécessitent pas d'équipement complexe ou de connaissances spécialisées. Ils corrigent les distorsions en analysant les caractéristiques statistiques des couleurs de l'image, ce qui les rend idéaux pour traiter les photos de tous les jours avec des distorsions colorimétriques.
3.1.1 Algorithme du monde gris (pour corriger les "déséquilibres colorimétriques généraux")
- Principe fondamental : Dans les scènes naturelles, la diversité des couleurs fait que leur moyenne tend vers un gris neutre (par exemple, le mélange du ciel bleu, des arbres verts et de fleurs rouges donne des valeurs moyennes similaires pour les trois canaux RGB). L'algorithme ajuste la luminosité des trois canaux pour ramener la couleur moyenne de l'image au gris neutre, corrigeant ainsi le déséquilibre colorimétrique.
- Cas d'usage : Photos prises sous éclairage intérieur (teintes jaunes ou rouges), paysages par temps nuageux (teintes grises ou bleutées).
- Implémentation Python (OpenCV) :
import cv2
import numpy as np
def equilibre_mondial_couleurs(image):
"""
Algorithme du monde gris : corrige le déséquilibre colorimétrique global des photos
:param image: Image distordue en entrée (lue par OpenCV au format BGR par défaut)
:return: Image restaurée avec des couleurs corrigées
"""
# Conversion en flottants pour éviter les dépassements de couleur
img_float = image.astype(np.float32)
hauteur, largeur, canaux = img_float.shape
# Calcul des moyennes pour chaque canal de couleur
moyenne_r = np.mean(img_float[..., 2]) # Canal rouge
moyenne_v = np.mean(img_float[..., 1]) # Canal vert
moyenne_b = np.mean(img_float[..., 0]) # Canal bleu
# Cible de gris neutre (128 pour une image 8 bits, gris de luminosité moyenne)
cible_gris = 128.0
# Calcul des coefficients d'ajustement (éviter la division par zéro)
coeff_r = cible_gris / moyenne_r if moyenne_r != 0 else 1.0
coeff_v = cible_gris / moyenne_v if moyenne_v != 0 else 1.0
coeff_b = cible_gris / moyenne_b if moyenne_b != 0 else 1.0
# Application des coefficients avec limitation des valeurs entre 0-255
image_restauree = np.zeros_like(img_float)
image_restauree[..., 2] = np.clip(img_float[..., 2] * coeff_r, 0, 255)
image_restauree[..., 1] = np.clip(img_float[..., 1] * coeff_v, 0, 255)
image_restauree[..., 0] = np.clip(img_float[..., 0] * coeff_b, 0, 255)
return image_restauree.astype(np.uint8)
# Test : correction d'une photo de nourriture avec teinte jaune
if __name__ == "__main__":
# Charger une photo distordue (remplacer par votre propre chemin)
image_distordue = cv2.imread("nourriture_interieur.jpg")
# Appliquer la restauration des couleurs
image_restauree = equilibre_mondial_couleurs(image_distordue)
# Afficher les résultats
cv2.imshow("Photo originale avec teinte jaune", image_distordue)
cv2.imshow("Photo restaurée", image_restauree)
cv2.waitKey(0) # Appuyer sur une touche pour fermer
cv2.destroyAllWindows()
# Sauvegarder le résultat
cv2.imwrite("nouriture_restauree.jpg", image_restauree)
- Avantages et inconvénients :
- Avantages : Code simple et rapide, ne nécessite aucun outil spécialisé, fonctionne directement avec les photos de téléphone.
- Inconvénients : Moins efficace pour les images à couleurs unies (par exemple, une mer de fleurs rouges).
3.1.2 Hypothèse de réflexion parfaite (pour corriger "les blancs impurs")
- Principe fondamental : Dans les scènes naturelles, il existe généralement des objets proches du blanc pur (comme du papier blanc, des murs blancs ou des nuages). Dans des conditions idéales, ces objets devraient avoir des valeurs RGB de 255 (blanc pur). L'algorithme identifie les zones les plus lumineuses de l'image, les ajuste pour les rendre purement blanches, et ce processus entraîne la restauration des couleurs de l'ensemble de l'image.
- Cas d'usage : Photos contenant des objets blancs (scans de documents, produits avec fond blanc, paysages avec des nuages).
- Implémentation Python :
def correction_reflexion_parfaite(image, pourcentage_sommet=0.001):
"""
Algorithme de réflexion parfaite : corrige les zones blanches grises ou jaunâtres
:param image: Image distordie en format BGR
:param pourcentage_sommet: Pourcentage de pixels les plus lumineux à considérer (0.1% par défaut)
:return: Image restaurée avec des couleurs corrigées
"""
img_float = image.astype(np.float32)
hauteur, largeur, canaux = img_float.shape
total_pixels = hauteur * largeur
# Nombre de pixels les plus lumineux à considérer (0.1% par défaut)
pixels_sommet = int(total_pixels * pourcentage_sommet)
# Aplatir l'image et trier par luminosité (moyenne des trois canaux)
img_aplatie = img_float.reshape(-1, 3)
luminosite = np.mean(img_aplatie, axis=1)
indices_sommet = np.argsort(luminosite)[-pixels_sommet:] # Indices des pixels les plus lumineux
pixels_sommet_rgb = img_aplatie[indices_sommet] # Couleurs des zones les plus lumineuses
# Calculer la moyenne RGB des zones les plus lumineuses (blanc distordu)
max_r = np.mean(pixels_sommet_rgb[:, 2])
max_v = np.mean(pixels_sommet_rgb[:, 1])
max_b = np.mean(pixels_sommet_rgb[:, 0])
# Coefficients d'ajustement : mapper le blanc distordu vers le blanc pur (255,255,255)
coeff_r = 255.0 / max_r if max_r != 0 else 1.0
coeff_v = 255.0 / max_v if max_v != 0 else 1.0
coeff_b = 255.0 / max_b if max_b != 0 else 1.0
# Application des ajustements avec limitation
image_restauree = np.zeros_like(img_float)
image_restauree[..., 2] = np.clip(img_float[..., 2] * coeff_r, 0, 255)
image_restauree[..., 1] = np.clip(img_float[..., 1] * coeff_v, 0, 255)
image_restauree[..., 0] = np.clip(img_float[..., 0] * coeff_b, 0, 255)
return image_restauree.astype(np.uint8)
# Test : correction d'un document scanné avec teinte grise
if __name__ == "__main__":
image_distordue = cv2.imread("document_gris.jpg")
image_restauree = correction_reflexion_parfaite(image_distordue)
cv2.imshow("Document gris d'origine", image_distordue)
cv2.imshow("Document restauré", image_restauree)
cv2.waitKey(0)
cv2.destroyAllWindows()
cv2.imwrite("document_restaure.jpg", image_restauree)
- Avantages et inconvénients :
- Avantages : Meilleur résultat que l'algorithme du monde gris pour les scènes avec des objets blancs, comme les documents scannés qui deviennent plus nets.
- Inconvénients : Problèmes de surcorrection si l'image n'a pas de zones claires (par exemple, une scène nocturne entièrement sombre).
3.2 Algorithmes physiques professionnels (précision élevée pour les scénarios spécialisés)
Ces algorithmes sont basés sur les principes physiques de l'imagerie et nécessitent quelques informations a priori (comme une mire de couleur ou des paramètres d'appareil). Ils offrent une précision de restauration plus élevée, idéale pour les scénarios exigeants en termes de couleur (comme la conception graphique, la photographie professionnelle ou la création artistique).
3.2.1 Matrice de correction colorimétrique (CCC)
- Principe fondamental : Les distorsions colorimétriques des appareils photo, moniteurs, etc., sont essentiellement une transformation linéaire. C'est comme mesurer une longueur avec une défectueuse - on a besoin d'un "coefficient de correction" pour corriger la déviation. En utilisant une mire de couleur standard (comme une mire 24 couleurs), on établit un mappage "couleurs distordues → couleurs réelles" pour calculer une matrice de correction 3x3, qui est ensuite appliquée à l'ensemble de l'image.
- Cas d'usage : Post-traitement photographique professionnel, impression graphique (éviter les différences de couleur entre l'écran et l'objet réel), art de rue "effet tableau" (faire correspondre les couleurs du tableau au décor réel).
- Implémentation Python (avec résolution de la matrice de calibration) :
def calculer_matrice_ccm(couleurs_distordues, couleurs_reelles):
"""
Calcule la matrice de correction colorimétrique (CCC) à partir des données d'une mire
:param couleurs_distordues: Valeurs RGB de la mire dans la photo (matrice n×3)
:param couleurs_reelles: Valeurs RGB standard de la mire (disponibles sur le site du fabricant)
:return: Matrice de correction 3x3
"""
# Résolution par les moindres carrés (algèbre linéaire de base, pas besoin de détails)
transpose_distordues = couleurs_distordues.T
M = couleurs_reelles.T @ np.linalg.inv(transpose_distordues @ couleurs_distordues)
return M
def appliquer_correction_ccm(image, matrice_ccm):
"""
Restaure les couleurs en utilisant une matrice CCC
:param image: Image distordue en format BGR
:param matrice_ccm: Matrice de correction 3x3 précalculée
:return: Image restaurée avec des couleurs corrigées
"""
img_float = image.astype(np.float32)
hauteur, largeur, canaux = img_float.shape
# Conversion au format RGB (OpenCV lit par défaut en BGR)
img_rgb = cv2.cvtColor(img_float, cv2.COLOR_BGR2RGB)
plat_rgb = img_rgb.reshape(-1, 3) # Aplatir en tableau de pixels
# Application de la matrice de correction (étape cruciale)
plat_restaure = (matrice_ccm @ plat_rgb.T).T
plat_restaure = np.clip(plat_restaure, 0, 255) # Limiter les valeurs de couleur
# Remettre en format image et convertir en BGR
rgb_restaure = plat_restaure.reshape(hauteur, largeur, 3)
bgr_restaure = cv2.cvtColor(rgb_restaure.astype(np.uint8), cv2.COLOR_RGB2BGR)
return bgr_restaure
# Test (nécessite des données de mire préalables)
if __name__ == "__main__":
# Exemple : données simulées pour une mire 24 couleurs (en pratique, utiliser des valeurs extraites d'une photo de mire)
rgb_distordu = np.array([[100, 80, 70], [150, 120, 100], [200, 180, 160]] * 8) # 24×3
rgb_reel = np.array([[105, 85, 75], [155, 125, 105], [205, 185, 165]] * 8) # Valeurs standard de la mire
# Calcul de la matrice CCC
matrice_ccm = calculer_matrice_ccm(rgb_distordu, rgb_reel)
print("Matrice de correction colorimétrique :")
print(matrice_ccm)
# Restauration d'une photo professionnelle distordue
image_distordue = cv2.imread("photo_professionnelle.jpg")
image_restauree = appliquer_correction_ccm(image_distordue, matrice_ccm)
cv2.imshow("Photo distordue", image_distordue)
cv2.imshow("Photo restaurée (niveau professionnel)", image_restauree)
cv2.waitKey(0)
cv2.destroyAllWindows()
- Avantages et inconvénients :
- Avantages : Précision de restauration très élevée, résout le problème "l'écran est joli mais l'objet réel est décevant", comme les affiches dont les couleurs restent cohérentes après impression.
- Inconvénients : Nécessite une mire de couleur standard, adapté aux scénarios professionnels, pas nécessaire pour la photographie quotidienne.
3.2.2 Compensation de température de couleur (pour corriger "les déviations dues à l'éclairage")
- Principe fondamental : Différentes sources lumineuses ont des "températures de couleur" différentes (lumière chaude = jaune, lumière froide = bleue). L'algorithme estime d'abord la température de couleur actuelle de l'éclairage, puis ajuste les proportions des canaux RGB pour compenser la déviation causée par la lumière.
- Cas d'usage : Photos prises sous éclairage intérieur (chambre à coucher, restaurant, centre commercial).
- Implémentation Python simplifiée :
def estimer_temperature_couleur(image):
"""
Estime simplement la température de couleur de l'éclairage (chaude/neutre/froide)
:param image: Image en entrée au format BGR
:return: Température estimée (K)
"""
img_float = image.astype(np.float32)
moyenne_r = np.mean(img_float[..., 2])
moyenne_v = np.mean(img_float[..., 1])
moyenne_b = np.mean(img_float[..., 0])
# Utiliser le ratio RV pour déterminer la température (logique simplifiée, suffisante pour un usage quotidien)
ratio_rb = moyenne_r / moyenne_b
if ratio_rb < 0.9:
return 6500 # Lumière froide (lampe fluorescent, bleutée)
elif ratio_rb < 1.1:
return 5500 # Lumière neutre (lumière du jour, couleur normale)
else:
return 3000 # Lumière chaude (lampe à incandescence, jaunâtre)
def compenser_temperature_couleur(image, temperature_cible=5500):
"""
Compensation de température de couleur : corrige les distorsions dues à l'éclairage
:param image: Image distordie en entrée
:param temperature_cible: Température de couleur cible (5500K par défaut, simulant la lumière du jour)
:return: Image compensée
"""
temperature_actuelle = estimer_temperature_couleur(image)
img_float = image.astype(np.float32)
# Ajuster les canaux RGB selon la différence de température
if temperature_actuelle < temperature_cible:
# Lumière chaude vers neutre : augmenter le canal bleu, diminuer le rouge
coeff_r = 0.9 * (temperature_actuelle / temperature_cible)
coeff_b = 1.1 * (temperature_cible / temperature_actuelle)
else:
# Lumière froide vers neutre : augmenter le canal rouge, diminuer le bleu
coeff_r = 1.1 * (temperature_cible / temperature_actuelle)
coeff_b = 0.9 * (temperature_actuelle / temperature_cible)
coeff_v = 1.0 # Canal vert inchangé
# Application des ajustements
img_float[..., 2] = np.clip(img_float[..., 2] * coeff_r, 0, 255)
img_float[..., 1] = np.clip(img_float[..., 1] * coeff_v, 0, 255)
img_float[..., 0] = np.clip(img_float[..., 0] * coeff_b, 0, 255)
return img_float.astype(np.uint8)
# Test : correction d'une photo avec teinte jaune sous lampe à incandescence
if __name__ == "__main__":
image_distordue = cv2.imread("photo_lumiere_chaude.jpg")
image_restauree = compenser_temperature_couleur(image_distordue)
cv2.imshow("Photo originale jaunâtre", image_distordue)
cv2.imshow("Photo restaurée (lumière du jour)", image_restauree)
cv2.waitKey(0)
cv2.destroyAllWindows()
3.3 Algorithmes d'apprentissage profond avancés (restauration en un clic pour scénarios complexes)
Pour les distorsions complexes (comme la décoloration des vieilles photos, la photographie sous-marine, les prises de vue par temps de brouillard), les algorithmes traditionnels ont des limites. C'est là que les algorithmes d'apprentissage profond entrent en jeu.
- Principe fondamental : Au lieu de concevoir manuellement une "formule de correction", le réseau de neurones apprend à partir d'un grand nombre de paires "image distordue - image réelle" pour trouver automatiquement les règles de restauration. C'est comme si l'IA voyait des milliers de photos décolorées et leurs versions normales correspondantes, apprenant ainsi à les restaurer par elle-même.
- Cas d'usage : Restauration de vieilles photos, photographie sous-marine, scénaires de faible éclairage/brouillard, création artistique dans des conditions d'éclairage complexes.
- Outils recommandés : Pas besoin d'écrire des réseaux complexes soi-même, utilisez les outils existants :
- Convivial pour les débutants : Bibliothèque
OpenMMLaben Python (avec des modèles de restauration colorimétrique préexistants). - Rapidement efficace : Outils en ligne (comme la fonction de restauration de couleurs de Remove.bg, l'outil de restauration de photos anciennes de Meitu).
Guide de sélection d'algorithmes selon les scénarios
Pas besoin de mémoriser tout, choisissez simplement l'algorithme adapté à vos besoins :
- Photos quotidiennes avec déséquilibre colorimétrique (intérieur, temps nuageux) : Privilégiez l'algorithme du monde gris (simple et rapide).
- Contenant des objets blancs/scans de documents : Utilisez l'algorithme de réflexion parfaite (blancs plus purs).
- Photographie professionnelle/conception graphique : Utilisez la matrice CCC (couleurs précises et cohérentes).
- Déséquilibre jaune/bleu dû à l'éclairage : Utilisez la compensation de température de couleur (restauration de la lumière du jour).
- Vieilles photos/distorsions complexes : Utilisez des outils d'apprentissage profond (restauration en un clic).
Conseils pratiques pour une restauration colorimétrique plus précise
- Lors de la prise de photos, incluez si possible des objets de couleurs neutres (comme un papier blanc ou une veste grise) pour aider les algorithmes à identifier les couleurs de référence.
- Évitez de trop compresser les images (comme l'envoi en original via WeChat) car cela entraîne une perte d'informations colorimétriques, affectant la qualité de la restauration.
- En utilisant du code Python, ajustez les paramètres (comme
pourcentage_sommetdans l'algorithme de réflexion parfaite) et affinez selon les résultats obtenus. - Pour les scénarios professionnels, utilisez une mire de couleur standard pour l'étalonnage. Une fois calibré, la même matrice de correction peut être réutilisée pour les photos prises avec le même équipement.