Notebook pour le projet : Débruitage d’Images avec Ondelettes et Opérateurs Proximaux
I. Introduction¶
Le débruitage d’images est une problématique fondamentale en traitement du signal et des images. L’objectif est de restaurer une image corrompue par du bruit tout en préservant ses détails et ses structures. Dans ce projet, nous utilisons une approche basée sur les ondelettes et les opérateurs proximaux pour résoudre un problème d’optimisation. Nous intégrons des concepts théoriques tels que les transformées en ondelettes, la régularisation ( $L_1$ ) et ( $L_2$), ainsi que les propriétés des opérateurs proximaux pour proposer une méthode efficace de débruitage.
Définition des concepts clés¶
Avant d'aborder les détails de ce projet, il est essentiel de définir deux concepts fondamentaux : les ondelettes et les opérateurs proximaux. Ces outils, au cœur des méthodes utilisées dans ce projet, sont étroitement liés aux théories du traitement du signal et de l'optimisation convexe.
1. Qu’est-ce qu’une ondelette ?¶
Une ondelette est une fonction oscillante de durée finie, conçue pour analyser un signal ou une image à différentes échelles. Contrairement à la transformée de Fourier, qui fournit une analyse purement fréquentielle, la transformée en ondelettes conserve des informations temporelles ou spatiales grâce à son approche multi-résolution.
Transformée en ondelettes discrète (DWT) :
La DWT permet de décomposer un signal (ou une image) en un ensemble de coefficients distincts :- Les basses fréquences ($cA$), qui représentent les structures globales de l'image, ou approximations.
- Les hautes fréquences ($cH$, $cV$, $cD$), qui contiennent les détails locaux dans les directions horizontale, verticale et diagonale.
Propriétés clés des ondelettes :
- Elles offrent une localisation spatiale et fréquentielle, permettant une analyse précise des variations locales.
- Leur capacité à représenter les données à plusieurs échelles est particulièrement utile dans le débruitage et la compression des images.
Dans le contexte de ce projet, les ondelettes sont utilisées pour séparer les composantes d’une image bruitée en basses et hautes fréquences. Cela facilite l’application d’une régularisation ciblée sur les hautes fréquences.
2. Qu’est-ce qu’un opérateur proximal ?¶
L'opérateur proximal est un outil issu de l'optimisation convexe, utilisé pour résoudre des problèmes impliquant des fonctions non différentiables. Il est particulièrement adapté aux algorithmes d’optimisation pour le débruitage, notamment lorsque des régularisations telles que $L_1$ (parcimonie) ou $L_2$ (lissage) sont appliquées.
Définition mathématique :¶
L’opérateur proximal d’une fonction convexe propre semi-continue inférieurement $f \in \Gamma_0(H)$, où $H$ est un espace de Hilbert, est défini comme suit :
$$ \text{prox}_f(y) = \arg \min_{x \in H} \left( f(x) + \frac{1}{2} \|x - y\|_2^2 \right). $$
Propriétés :¶
- La proposition associée indique que $\text{prox}_f(y)$ est toujours bien défini pour $f \in \Gamma_0(H)$.
- Interprétation :
- $\text{prox}_f(y)$ calcule un point $x$ proche de $y$ tout en régularisant cette solution via $f$.
- La régularisation via $f$ contrôle la nature de la solution : parcimonieuse ($L_1$) ou lissée ($L_2$).
Dans le contexte du projet :¶
- Régularisation $L_1$ (norme $L_1$) :
$$
f(x) = \lambda \|x\|_1 \quad \Rightarrow \quad \text{prox}_f(y) = \text{soft-thresholding}(y, \lambda).
$$
- Cet opérateur est particulièrement utile pour supprimer les petits coefficients (effet de parcimonie).
- Régularisation $L_2$ (norme $L_2$) :
$$
f(x) = \frac{\lambda}{2} \|x\|_2^2 \quad \Rightarrow \quad \text{prox}_f(y) = \frac{y}{1 + \lambda}.
$$
- Cet opérateur réduit tous les coefficients de manière homogène (effet de lissage).
Lien avec le débruitage¶
Dans ce projet, les ondelettes sont utilisées pour analyser l’image bruitée en séparant ses composantes fréquentielles. Les opérateurs proximaux permettent ensuite d’appliquer une régularisation adaptée (parcimonieuse ou lissée) sur les coefficients de hautes fréquences. Cette approche combine la flexibilité des ondelettes avec la rigueur mathématique des opérateurs proximaux pour un débruitage efficace et contrôlé.
Résolution des questions théoriques¶
1 : Montrons que résoudre (1) revient à calculer un opérateur proximal**¶
L'équation donnée est :
$$ \hat{x} \in \underset{x \in \mathbb{R}^N}{\text{argmin}} \; \frac{1}{2} \|x - y\|_2^2 + f(Lx), $$
où :
- $y \in \mathbb{R}^N$ est l'image bruitée,
- $L : \mathbb{R}^N \to \mathbb{R}^M$ est un opérateur linéaire,
- $f : \mathbb{R}^M \to \mathbb{R} \cup \{+\infty\}$ est une fonction convexe propre semi-continue inférieurement ($f \in \Gamma_0(\mathbb{R}^M)$).
Lien avec l’opérateur proximal :¶
D’après la définition de l’opérateur proximal, pour une fonction $g \in \Gamma_0(\mathbb{R}^N)$, on a :
$$ \text{prox}_g(y) = \underset{x \in \mathbb{R}^N}{\arg \min} \; g(x) + \frac{1}{2} \|x - y\|_2^2. $$
En posant $g(x) = f(Lx)$, notre problème devient exactement celui de la définition de $\text{prox}_{f \circ L}$ :
$$ \hat{x} = \text{prox}_{f \circ L}(y). $$
Alors résoudre (1) revient à calculer l’opérateur proximal $\text{prox}_{f \circ L}$ appliqué à $y$.
2. Montrons que le calcul de cet opérateur est équivalent au calcul de $L^{-1} \text{prox}_f(Ly)$**¶
On sait que:
- $L$ est un opérateur linéaire injectif, et $L^* : \mathbb{R}^M \to \mathbb{R}^N$ désigne son adjoint.
- $L^* L$ est inversible, ce qui permet de définir $L^{-1} = (L^* L)^{-1} L^*$.
Calcul de l’opérateur proximal $\text{prox}_{f \circ L}(y)$ : Le problème de minimisation s’écrit :
$$ \text{prox}_{f \circ L}(y) = \underset{x \in \mathbb{R}^N}{\arg \min} \; \frac{1}{2} \|x - y\|_2^2 + f(Lx). $$
En introduisant une variable intermédiaire $u = Lx$, on a :
- $x = L^{-1} u$ par la propriété de $L^{-1}$,
- Le problème se reformule en termes de $u$ comme suit :
$$ \text{prox}_{f \circ L}(y) = L^{-1} \underset{u \in \mathbb{R}^M}{\arg \min} \; \frac{1}{2} \|L^{-1} u - y\|_2^2 + f(u). $$
Simplification grâce aux propriétés de $L$ :
- Propriété de conservation de la norme : Si $L$ est un opérateur orthogonal, alors $\|L^{-1} u - y\|_2 = \|u - Ly\|_2$. En appliquant cette propriété :
$$ \text{prox}_{f \circ L}(y) = L^{-1} \underset{u \in \mathbb{R}^M}{\arg \min} \; \frac{1}{2} \|u - Ly\|_2^2 + f(u). $$
- Reconnaissance de l’opérateur proximal : Le terme
$$ \underset{u \in \mathbb{R}^M}{\arg \min} \; \frac{1}{2} \|u - Ly\|_2^2 + f(u) $$
est par définition l’opérateur proximal de $f$ appliqué à $Ly$, soit :
$$ \text{prox}_f(Ly). $$
En combinant ces résultats, on obtient :
$$ \text{prox}_{f \circ L}(y) = L^{-1} \text{prox}_f(Ly). $$
Enconclusion, nous avons démontré que le calcul de $\text{prox}_{f \circ L}(y)$ dans le domaine original $\mathbb{R}^N$ est équivalent au calcul de $\text{prox}_f(Ly)$ dans l’espace transformé $\mathbb{R}^M$, suivi de l’application de l’opérateur inverse $L^{-1}$.
Cette simplification est essentielle dans les applications pratiques, car elle permet de résoudre le problème dans un espace de moindre dimension (celui de $Lx$) tout en utilisant des outils bien définis pour $\text{prox}_f$. Cela est particulièrement utile dans le contexte des ondelettes, où $L$ est une transformée en ondelettes, et $L^{-1}$ correspond à la reconstruction.
II. Analyse multi-résolution¶
Dans cette partie, nous allons réaliser une analyse multi-résolution à l’aide de la décomposition en ondelettes 2D. Cette approche permet de visualiser l’image initiale à plusieurs échelles et de séparer les composantes basse fréquence (approximations) et haute fréquence (détails directionnels)
# Importation des bibliothèques nécessaires
import cv2
import numpy as np
import pywt
import matplotlib.pyplot as plt
from skimage import io
print("L'environnement 'debruitage' est configuré avec succès et toutes les bibliothèques fonctionnent !")
L'environnement 'debruitage' est configuré avec succès et toutes les bibliothèques fonctionnent !
1. Décomposition de l'image¶
# Chargement de l'image
in_img = cv2.imread('cameraman.jpg', cv2.IMREAD_GRAYSCALE)
# Paramètres de décomposition
wavelet_type = 'db2' # Type d'ondelettes
nb_level = 1 # Nombre d'échelles
# Calcul de la transformée en ondelettes
decomp = pywt.wavedec2(in_img, wavelet_type, mode='periodization', level=nb_level)
# Renormalisation pour un affichage correct
decomp[0] /= np.abs(decomp[0]).max() # Normalisation des basses fréquences
for detail_level in range(nb_level):
decomp[detail_level + 1] = [d / np.abs(d).max() for d in decomp[detail_level + 1]]
# Agrégation des coefficients en un tableau unique
arr, slices = pywt.coeffs_to_array(decomp)
# Affichage de la décomposition de l'image
plt.figure(figsize=(10, 10))
plt.imshow(arr, cmap='gray')
plt.title("Décomposition en ondelettes (multi-résolution)")
#plt.colorbar()
plt.show()
Interprétation des résultats
Il ressort après visualisation que la décomposition en ondelettes produit :
- Coin supérieur gauche ($cA$) : Représente les basses fréquences, contenant les structures globales de l'image (les détails principaux et lisses).
- Les coins ($cH$, $cV$, $cD$) : Contiennent les hautes fréquences :
- $cH$ (haut droit) : Détails horizontaux (bordures verticales).
- $cV$ (bas gauche) : Détails verticaux (bordures horizontales).
- $cD$ (bas droit) : Détails diagonaux (transitions obliques). en conclusion, la décomposition en ondelettes permet de séparer les informations globales (basses fréquences) des variations locales (hautes fréquences).
2. Expérimentation avec les paramètres de la transformée en ondelettes¶
Dans cette section, nous explorons les effets des paramètres de la décomposition en ondelettes, notamment le type d'ondelette (wavelet_type) et le nombre de niveaux de décomposition (nb_level). Ces expériences permettent de se familiariser avec la transformée en ondelettes et d'identifier les configurations adaptées aux besoins spécifiques de l'analyse.
Description des paramètres¶
wavelet_type(type d’ondelette) : Ce paramètre détermine la forme des filtres utilisés pour la décomposition. Différents types d’ondelettes, commehaar,db(Daubechies),coif(Coiflets) ousym(Symlets), influencent la manière dont les détails sont capturés. Par exemple,haarest simple et rapide, mais moins lisse, tandis quedb2oucoif1offrent une meilleure conservation des structures fines.nb_level(nombre de niveaux) : Ce paramètre contrôle la profondeur de la décomposition. Chaque niveau divise les informations en basses et hautes fréquences à une résolution plus fine. Un faible nombre de niveaux conserve davantage de détails globaux, tandis qu'un plus grand nombre de niveaux permet d'analyser les variations locales à plusieurs échelles.
# Paramètres à tester
wavelet_types = ['haar', 'db2', 'coif1', 'sym3'] # Différents types d'ondelettes
nb_levels = [1, 2, 3] # Différents niveaux de décomposition
# Préparation de la grille pour afficher les résultats
fig, axes = plt.subplots(len(wavelet_types), len(nb_levels), figsize=(15, 15))
fig.suptitle("Comparaison des décompositions en ondelettes", fontsize=16)
# Boucle pour tester les paramètres
for i, wavelet_type in enumerate(wavelet_types):
for j, nb_level in enumerate(nb_levels):
# Calcul de la transformée en ondelettes
decomp = pywt.wavedec2(in_img, wavelet_type, mode='periodization', level=nb_level)
# Renormalisation pour un affichage correct
decomp[0] /= np.abs(decomp[0]).max()
for detail_level in range(nb_level):
decomp[detail_level + 1] = [d / np.abs(d).max() for d in decomp[detail_level + 1]]
# Agrégation des coefficients en un tableau unique
arr, slices = pywt.coeffs_to_array(decomp)
# Affichage sur la grille
ax = axes[i, j]
ax.imshow(arr, cmap='gray')
ax.set_title(f"Wavelet: {wavelet_type}, Level: {nb_level}")
ax.axis('off')
# Ajustement de l'espacement entre les sous-graphiques
plt.tight_layout(rect=[0, 0, 1, 0.95])
plt.show()
Le graphique montre les décompositions en ondelettes pour différents types d’ondelettes (haar, db2, coif1, sym3) et niveaux de décomposition (1, 2, 3) :
Effet du niveau de décomposition (
nb_level) :- Niveau 1 : L’image reste proche de l’originale, seules les premières approximations et détails directionnels sont visibles.
- Niveau 2 et 3 : Les approximations ($cA$) deviennent de plus en plus lissées, et les détails ($cH$, $cV$, $cD$) deviennent plus spécifiques aux résolutions supérieures.
Effet du type d’ondelette (
wavelet_type) :haar: Produit des transitions nettes, mais simplifie les variations fines.db2etsym3: Capturent mieux les variations locales, produisant des résultats plus lissés.coif1: Offre un compromis, capturant à la fois les structures principales et des détails précis.
En conclusion, les résultats illustrent comment le type d’ondelette et le niveau de décomposition influencent la séparation des informations globales et des variations locales, posant une base pour une application ciblée du débruitage.
3. Transformée en ondelettes et reconstruction inverse¶
Dans cette section, nous expliquons comment extraire les coefficients d’une transformée en ondelettes 2D (DWT) et comment reconstruire l’image initiale à partir de ces coefficients en utilisant la transformée inverse (IDWT).
a. Extraction des coefficients de la DWT¶
La transformée discrète en ondelettes 2D décompose l’image en coefficients correspondant à différentes fréquences spatiales :
- $cA$ : Coefficients d’approximation (basses fréquences).
- $cH$, $cV$, $cD$ : Coefficients de détails, représentant respectivement les hautes fréquences horizontales, verticales et diagonales. Le code suivant extrait ces coefficients pour notre image donnée :
# Calcul de la transformée en ondelettes (1 niveau)
wavelet_type = 'db1' # Choix de l'ondelette
cA, (cH, cV, cD) = pywt.dwt2(in_img, wavelet_type)
# Affichage des coefficients sous forme de vecteurs
print("Approximation coefficients (cA):", cA)
print("Horizontal detail coefficients (cH):", cH)
print("Vertical detail coefficients (cV):", cV)
print("Diagonal detail coefficients (cD):", cD)
# Visualisation de ces
plt.figure(figsize=(10, 10))
titles = ['Approximation (cA)', 'Horizontal (cH)', 'Vertical (cV)', 'Diagonal (cD)']
coeffs = [cA, cH, cV, cD]
for i, coeff in enumerate(coeffs):
plt.subplot(2, 2, i + 1)
plt.imshow(coeff, cmap='gray')
plt.title(titles[i])
plt.axis('off')
plt.tight_layout()
plt.show()
Approximation coefficients (cA): [[315. 317.5 316.5 ... 301. 304. 303. ] [314.5 312.5 313. ... 304.5 305. 303. ] [314.5 316. 313. ... 302. 305.5 305. ] ... [241. 252.5 236.5 ... 266.5 261. 228. ] [240. 259.5 269.5 ... 271.5 252.5 223. ] [244.5 251. 284. ... 263.5 250.5 225. ]] Horizontal detail coefficients (cH): [[ 3.00000000e+00 -5.00000000e-01 -1.50000000e+00 ... 7.25535486e-16 1.00000000e+00 2.00000000e+00] [-2.50000000e+00 2.50000000e+00 3.00000000e+00 ... -1.50000000e+00 -1.00000000e+00 -2.00000000e+00] [ 2.50000000e+00 -3.00000000e+00 -3.00000000e+00 ... 2.00000000e+00 1.50000000e+00 1.00000000e+00] ... [ 5.00000000e+00 -2.50000000e+00 2.50000000e+00 ... -5.00000000e-01 -1.00000000e+00 4.00000000e+00] [-1.00000000e+00 3.50000000e+00 -1.05000000e+01 ... 5.50000000e+00 -3.50000000e+00 1.00000000e+00] [-3.50000000e+00 -3.00000000e+00 -1.00000000e+00 ... -6.50000000e+00 1.05000000e+01 -4.00000000e+00]] Vertical detail coefficients (cV): [[-1.00000000e+00 -5.00000000e-01 1.50000000e+00 ... -8.55246698e-15 -1.00000000e+00 1.00000000e+00] [ 5.00000000e-01 5.00000000e-01 -1.00000000e+00 ... 5.00000000e-01 -1.23612750e-14 1.00000000e+00] [-5.00000000e-01 -7.76260955e-15 1.00000000e+00 ... -4.48037312e-15 -5.00000000e-01 -1.23612750e-14] ... [-6.00000000e+00 2.50000000e+00 4.50000000e+00 ... -1.50000000e+00 7.00000000e+00 7.00000000e+00] [-4.00000000e+00 5.00000000e-01 -1.25000000e+01 ... -1.50000000e+00 1.05000000e+01 3.00000000e+00] [ 3.50000000e+00 3.02552964e-15 -2.40000000e+01 ... -4.50000000e+00 9.50000000e+00 2.00000000e+00]] Diagonal detail coefficients (cD): [[ 1.00000000e+00 1.50000000e+00 -5.00000000e-01 ... -1.00000000e+00 -6.02113109e-15 -4.03287619e-15] [-5.00000000e-01 -1.50000000e+00 1.00000000e+00 ... 5.00000000e-01 -8.90187929e-18 -3.88453593e-15] [ 1.50000000e+00 1.00000000e+00 -1.00000000e+00 ... 4.93642070e-17 -5.00000000e-01 2.37248724e-17] ... [ 2.00000000e+00 -5.00000000e-01 5.00000000e-01 ... 3.50000000e+00 -5.00000000e+00 3.00000000e+00] [-3.00000000e+00 5.00000000e-01 7.50000000e+00 ... -1.50000000e+00 4.50000000e+00 -7.00000000e+00] [-5.00000000e-01 2.39032262e-17 -1.00000000e+00 ... 1.50000000e+00 -6.50000000e+00 1.10000000e+01]]
Interprétation¶
Visualisation des coefficients :
- $cA$ (approximation) : Représente les basses fréquences, contenant les structures globales et les zones lisses de l'image.
- $cH$ (détails horizontaux) : Met en évidence les variations horizontales (bordures verticales).
- $cV$ (détails verticaux) : Révèle les variations verticales (bordures horizontales).
- $cD$ (détails diagonaux) : Capture les transitions obliques et les détails fins.
Coefficients numériques :
- $cA$ contient les grandes structures avec des valeurs élevées.
- $cH$, $cV$, $cD$ reflètent les variations locales avec des valeurs plus faibles.
En conclusion, la DWT sépare efficacement les informations globales ($cA$) des variations directionnelles ($cH$, $cV$, $cD$), fournissant une base solide pour des applications comme le débruitage.
b. Reconstruction de l’image avec l’IDWT¶
La reconstruction de l’image originale à partir des coefficients s’effectue avec la transformée en ondelettes inverse. Cette opération est l’application de l’opérateur $L^{-1}$, qui recompose l’image à partir de $cA$, $cH$, $cV$ et $cD$.
Le code suivant montre comment effectuer cette reconstruction :
# Reconstruction de l'image
decomp = (cA, (cH, cV, cD)) # Regroupement des coefficients
reconstruct_sig = pywt.idwt2(decomp, wavelet_type) # Transformée inverse
# Affichage des images originale, décomposée et reconstruite côte à côte
plt.figure(figsize=(18, 6))
# Image originale
plt.subplot(1, 3, 1)
plt.imshow(in_img, cmap='gray')
plt.title("Image originale")
plt.axis('off')
# Image décomposée (approximation et détails)
plt.subplot(1, 3, 2)
coeffs_image = np.vstack([
np.hstack([cA, cH]),
np.hstack([cV, cD])
]) # Organisation des coefficients pour affichage
plt.imshow(coeffs_image, cmap='gray')
plt.title("Décomposition en ondelettes")
plt.axis('off')
# Image reconstruite
plt.subplot(1, 3, 3)
plt.imshow(reconstruct_sig, cmap='gray')
plt.title("Image reconstruite")
plt.axis('off')
plt.tight_layout()
plt.show()
L'image originale (à gauche) et l'image reconstruite (à droite) sont visuellement identiques. Cela montre que l'opération de décomposition en ondelettes (DWT) suivie de la reconstruction inverse (IDWT) conserve parfaitement l'information de l'image d'origine.
Conclusion : Cette parfaite correspondance valide la fidélité de l'opérateur de reconstruction $L^{-1}$ et confirme que la transformée en ondelettes est une méthode réversible lorsqu'aucune modification n'est apportée aux coefficients.
III. Débruitage¶
Pour tester notre méthode de débruitage, nous commençons par créer une version bruitée de l’image originale du caméraman. Le bruit ajouté sera un bruit gaussien centré avec un écart type $\sigma = 30$. Cela simule un cas pratique où l’image est corrompue par un bruit aléatoire.
# Paramètres du bruit gaussien
sigma = 30 # Écart type
mean = 0 # Moyenne
# Génération du bruit gaussien
gaussian_noise = np.random.normal(mean, sigma, in_img.shape)
# Ajout du bruit à l'image originale
noisy_img = in_img + gaussian_noise
# Normalisation pour que les valeurs restent dans la plage [0, 255]
noisy_img = np.clip(noisy_img, 0, 255).astype(np.uint8)
# Affichage des images
plt.figure(figsize=(12, 6))
# Image originale
plt.subplot(1, 2, 1)
plt.imshow(in_img, cmap='gray')
plt.title("Image originale")
plt.axis('off')
# Image bruitée
plt.subplot(1, 2, 2)
plt.imshow(noisy_img, cmap='gray')
plt.title("Image bruitée (y)")
plt.axis('off')
plt.tight_layout()
plt.show()
L'image originale (à gauche) est propre et sans bruit, tandis que l'image bruitée (à droite) montre des grains aléatoires dus au bruit gaussien ($\sigma = 30$). Les structures principales restent visibles, mais la qualité globale est dégradée. Cette image bruitée sera utilisée pour tester notre méthode de débruitage.
1. Décomposition en ondelettes de l’image bruitée¶
L’objectif ici est d’appliquer la décomposition en ondelettes à l’image bruitée ( y ) afin d’analyser comment le bruit affecte les différentes composantes fréquentielles (approximations et détails) à plusieurs échelles.
Choix de l’affichage séparé¶
Dans cette représentation, chaque composante fréquentielle ($cA$, $cH$, $cV$, $cD$) est affichée séparément. Ce choix permet une analyse détaillée de l'impact du bruit sur chaque type de coefficient, en distinguant clairement les approximations (basses fréquences) des détails directionnels (hautes fréquences).
Contrairement à une mosaïque unifiée, cet affichage offre l’avantage de mieux isoler les effets spécifiques du bruit sur chaque composante, facilitant ainsi l’identification des zones où la régularisation sera la plus efficace.
# Paramètres de la décomposition
wavelet_type = 'db2' # Type d'ondelette
nb_levels = 2 # Nombre de niveaux d'échelles
# Décomposition de l'image bruitée
decomp_noisy = pywt.wavedec2(noisy_img, wavelet_type, mode='periodization', level=nb_levels)
# Affichage des coefficients pour chaque niveau
fig, axes = plt.subplots(nb_levels + 1, 4, figsize=(16, 8))
titles = ['Approximation (cA)', 'Horizontal (cH)', 'Vertical (cV)', 'Diagonal (cD)']
for level in range(nb_levels + 1):
if level == 0:
# Affichage des basses fréquences
axes[level, 0].imshow(decomp_noisy[0], cmap='gray')
axes[level, 0].set_title("Approximation Niveau 0")
axes[level, 1].axis('off')
axes[level, 2].axis('off')
axes[level, 3].axis('off')
else:
# Affichage des détails (cH, cV, cD)
cH, cV, cD = decomp_noisy[level]
axes[level, 0].axis('off') # Pas d'approximation à ce niveau
for i, coeff in enumerate([cH, cV, cD]):
axes[level, i + 1].imshow(coeff, cmap='gray')
axes[level, i + 1].set_title(f"{titles[i + 1]} (Niveau {level})")
# Ajustement de l'espacement
plt.tight_layout()
plt.show()
Dans cette décomposition en ondelettes, nous observons que :
- Approximation (Niveau 0) : Les grandes structures de l'image sont bien conservées, mais le bruit reste visible.
- Détails directionnels (Niveau 1) : Le bruit perturbe les variations horizontales, verticales et diagonales, rendant les contours moins nets.
- Détails directionnels (Niveau 2) : Les détails plus fins amplifient davantage le bruit, masquant les structures réelles.
En conslusion, le bruit affecte surtout les coefficients de détails, en particulier aux niveaux supérieurs. Nous concentrerons nos efforts de débruitage sur ces composantes pour réduire les perturbations tout en préservant les structures importantes.
Calcul du minimiseur $\hat{x}$¶
Dans cette partie, nous développons un algorithme pour résoudre le problème d'optimisation donné, en utilisant l'opérateur de décomposition en ondelettes $L$ présenté précédemment. L'objectif est de réduire le bruit en appliquant une régularisation spécifique aux hautes fréquences tout en préservant les détails essentiels de l'image. La qualité du débruitage sera mesurée à l'aide de l'erreur quadratique normalisée.
Deux outils mathématiques jouent un rôle central dans cette approche. Tout d'abord, une fonction séparable est utilisée, définie par $f(x) = \sum_{n=1}^N f_n(x_n)$, où chaque $f_n(x_n)$ agit indépendamment sur les coefficients d'ondelettes. Cette propriété permet de cibler les coefficients correspondant aux hautes fréquences ($cH$, $cV$, $cD$) pour y appliquer une régularisation adaptée. Ensuite, la performance du débruitage est évaluée par l'erreur quadratique normalisée, donnée par :
$$ E(\hat{x}, \bar{x}) = \frac{\|\hat{x} - \bar{x}\|_2^2}{\|\bar{x}\|_2^2}, $$
où $\hat{x}$ représente l'image débruitée et $\bar{x}$ l'image originale non bruitée.
2. Développement des algorithmes pour résoudre cette partie¶
Pour résoudre le problème d’optimisation et calculer le minimiseur $\hat{x}$, nous devons implémenter différentes méthodes, chacune adaptée à une fonction $f(x)$ donnée. Voici les étapes et les algorithmes nécessaires pour cette résolution.
Étape 1 : Définition de la fonction de régularisation ($f(x)$)¶
La régularisation est définie comme suit :
- $f(x) = \sum_{n=1}^N f_n(x_n),$
- $f_n(x_n) = \begin{cases} 0, & \text{si } n \notin I, \\ \lambda |x_n|^q, & \text{si } n \in I, \end{cases}$
où :
- $I$ est l’ensemble des indices des coefficients dans les composantes hautes fréquences ($cH$, $cV$, $cD$),
- $\lambda > 0$ est un paramètre de régularisation,
- $q \in \{1, 2\}$ détermine la régularisation ($L_1$ pour $q=1$, $L_2$ pour $q=2$).
Étape 2 : Décomposition et reconstruction avec $L$ et $L^{-1}$¶
Décomposition ($L$) :
On décompose l’image bruitée ($y$) en coefficients ($cA$, $cH$, $cV$, $cD$) à l’aide de la transformée en ondelettes.Régularisation ($\text{prox}_f$) :
La régularisation est appliquée uniquement sur les coefficients hautes fréquences ($cH$, $cV$, $cD$) :- Pour $q = 1$, nous utilisons le soft-thresholding :
$$ \text{prox}_{\lambda |.|}(x) = \text{sign}(x) \cdot \max(|x| - \lambda, 0). $$
- Pour $q = 2$, nous appliquons une réduction quadratique :
$$ \text{prox}_{\lambda |.|^2}(x) = \frac{x}{1 + \lambda}. $$
- Reconstruction ($L^{-1}$) :
Les coefficients modifiés sont reconstitués dans le domaine d’origine pour obtenir l’image débruitée ($\hat{x}$).
3. Implémentation des algorithmes¶
a. Algorithme pour régularisation $L_1$ (soft-thresholding)¶
def soft_thresholding(x, threshold):
"""Application du soft-thresholding sur un coefficient."""
return np.sign(x) * np.maximum(np.abs(x) - threshold, 0)
# Fonction de débruitage avec L1
def denoise_l1(noisy_img, wavelet_type, nb_levels, lambda_param):
"""Débruitage avec régularisation L1 (soft-thresholding)."""
# Décomposition en ondelettes
decomp_noisy = pywt.wavedec2(noisy_img, wavelet_type, mode='periodization', level=nb_levels)
# Application de la régularisation L1
decomp_denoised = [decomp_noisy[0]] # Garder cA inchangé
for details in decomp_noisy[1:]:
cH, cV, cD = details
cH_denoised = soft_thresholding(cH, lambda_param)
cV_denoised = soft_thresholding(cV, lambda_param)
cD_denoised = soft_thresholding(cD, lambda_param)
decomp_denoised.append((cH_denoised, cV_denoised, cD_denoised))
# Reconstruction de l'image
denoised_img = pywt.waverec2(decomp_denoised, wavelet_type, mode='periodization')
return denoised_img
b. Algorithme pour régularisation $L_2$ (Régularisation quadratique)¶
def quadratic_regularization(x, lambda_param):
"""Appliquons la régularisation quadratique."""
return x / (1 + 2*lambda_param)
# Fonction de débruitage avec L2
def denoise_l2(noisy_img, wavelet_type, nb_levels, lambda_param):
"""Débruitage avec régularisation L2 (réduction quadratique)."""
# Décomposition en ondelettes
decomp_noisy = pywt.wavedec2(noisy_img, wavelet_type, mode='periodization', level=nb_levels)
# Application de la régularisation L2
decomp_denoised = [decomp_noisy[0]] # Garder cA inchangé
for details in decomp_noisy[1:]:
cH, cV, cD = details
cH_denoised = quadratic_thresholding(cH, lambda_param)
cV_denoised = quadratic_thresholding(cV, lambda_param)
cD_denoised = quadratic_thresholding(cD, lambda_param)
decomp_denoised.append((cH_denoised, cV_denoised, cD_denoised))
# Reconstruction de l'image
denoised_img = pywt.waverec2(decomp_denoised, wavelet_type, mode='periodization')
return denoised_img
Application des algorithmes et visualisation¶
# Paramètres
lambda_param = 30 # Régularisation
wavelet_type = 'db2'
nb_levels = 2
# Débruitage avec L1
denoised_img_l1 = denoise_l1(noisy_img, wavelet_type, nb_levels, lambda_param)
# Débruitage avec L2
denoised_img_l2 = denoise_l2(noisy_img, wavelet_type, nb_levels, lambda_param)
# Calcul des pourcentages d'erreur
error_noisy = compute_error(noisy_img, in_img, norm_type='L2') * 100 # Pourcentage pour image bruitée
error_l1 = compute_error(denoised_img_l1, in_img, norm_type='L1') * 100 # Pourcentage pour débruitage L1
error_l2 = compute_error(denoised_img_l2, in_img, norm_type='L2') * 100 # Pourcentage pour débruitage L2
# Affichage des résultats
plt.figure(figsize=(15, 5))
# Image bruitée
plt.subplot(1, 3, 1)
plt.imshow(noisy_img, cmap='gray')
plt.title(f"Image bruitée\nErreur: {error_noisy:.2f}%")
plt.axis('off')
# Image débruitée (L1)
plt.subplot(1, 3, 2)
plt.imshow(denoised_img_l1, cmap='gray')
plt.title(f"Débruitée (L1)\nErreur: {error_l1:.2f}%")
plt.axis('off')
# Image débruitée (L2)
plt.subplot(1, 3, 3)
plt.imshow(denoised_img_l2, cmap='gray')
plt.title(f"Débruitée (L2)\nErreur: {error_l2:.2f}%")
plt.axis('off')
plt.tight_layout()
plt.show()
Analyse¶
Le résultat montre une nette amélioration après l'application des méthodes de débruitage $L_1$ et $L_2$, avec une réduction significative du pourcentage d'erreur par rapport à l'image bruitée initiale. L'image bruitée présente une erreur de $157.11\%$, reflétant un niveau élevé de dégradation causé par le bruit ajouté. Après le débruitage avec la régularisation $L_1$, l'erreur diminue à $9.09\%$, indiquant une bonne capacité de cette méthode à réduire le bruit tout en préservant les détails de l'image. Cependant, la méthode $L_2$ montre une performance supérieure avec une erreur de seulement $1.03\%$, démontrant une meilleure reconstruction de l'image originale en minimisant davantage les artefacts. Cette différence de performance souligne l'efficacité accrue de la régularisation $L_2$ pour ce type de tâche, particulièrement adaptée pour réduire les variations quadratiques tout en conservant les structures importantes de l'image.
c. Calcul de l’erreur quadratique normalisée¶
# Vérifier les dimensions et ajuster si nécessaire
if denoised_img_l1.shape != in_img.shape:
denoised_img_l1 = cv2.resize(denoised_img_l1, (in_img.shape[1], in_img.shape[0]))
if denoised_img_l2.shape != in_img.shape:
denoised_img_l2 = cv2.resize(denoised_img_l2, (in_img.shape[1], in_img.shape[0]))
# Calcul des erreurs
error_noisy_l1 = compute_error(noisy_img, in_img, norm_type='L1') # Erreur entre image bruitée et originale (L1)
error_noisy_l2 = compute_error(noisy_img, in_img, norm_type='L2') # Erreur entre image bruitée et originale (L2)
error_l1 = compute_error(denoised_img_l1, in_img, norm_type='L1') # Erreur pour L1
error_l2 = compute_error(denoised_img_l2, in_img, norm_type='L2') # Erreur pour L2
# Affichage des erreurs
print(f"Erreur (L1) entre l'image bruitée et l'originale : {error_noisy_l1:.4f}")
print(f"Erreur (L2) entre l'image bruitée et l'originale : {error_noisy_l2:.4f}")
print(f"Erreur (L1) avec débruitage L1 : {error_l1:.4f}")
print(f"Erreur (L2) avec débruitage L2 : {error_l2:.4f}")
# Tracé des erreurs (barres côte à côte)
labels = ['Bruitée', 'Débruitée (L1)', 'Débruitée (L2)']
errors_l1 = [error_noisy_l1, error_l1, np.nan] # Ajout de NaN pour équilibrer
errors_l2 = [error_noisy_l2, np.nan, error_l2]
x = np.arange(len(labels)) # Position des groupes
width = 0.4 # Largeur des barres
plt.figure(figsize=(10, 6))
plt.bar(x - width / 2, errors_l1, width, label="Norme L1", color='blue', alpha=0.7)
plt.bar(x + width / 2, errors_l2, width, label="Norme L2", color='green', alpha=0.7)
# Ajout des annotations
for i, val in enumerate(errors_l1):
if not np.isnan(val):
plt.text(i - width / 2, val + 0.005, f"{val:.2f}", ha='center', va='bottom')
for i, val in enumerate(errors_l2):
if not np.isnan(val):
plt.text(i + width / 2, val + 0.005, f"{val:.2f}", ha='center', va='bottom')
plt.xticks(x, labels)
plt.ylabel("Erreur normalisée")
plt.title("Comparaison des erreurs avec Normes L1 et L2")
plt.legend()
plt.grid(axis='y', linestyle='--', alpha=0.7)
plt.show()
Erreur (L1) entre l'image bruitée et l'originale : 1.0887 Erreur (L2) entre l'image bruitée et l'originale : 1.5711 Erreur (L1) avec débruitage L1 : 0.0909 Erreur (L2) avec débruitage L2 : 0.0103
Les résultats obtenus montrent une amélioration significative après application des techniques de débruitage $L_1$ et $L_2$. L’image bruitée initiale présente des erreurs relativement élevées : $1.0887$ pour la norme $L_1$ et $1.5711$ pour la norme $L_2$, ce qui reflète un fort impact du bruit sur les données. Après débruitage, la méthode $L_1$ réduit l’erreur à $0.0909$, indiquant une performance notable dans l’élimination du bruit tout en préservant une partie des détails. Cependant, la méthode $L_2$ s’avère plus efficace, ramenant l’erreur à seulement $0.0103$, démontrant sa supériorité pour reconstruire l’image originale de manière plus précise. Ces résultats soulignent l’efficacité croissante de $L_2$ pour des tâches de débruitage nécessitant une minimisation des variations quadratiques, avec un niveau de précision supérieur à $L_1$.
Calcul de pourcentage de bruit¶
# Fonction pour calculer le pourcentage de bruit
def calculate_noise_percentage(original_img, noisy_or_denoised_img):
"""
Calcule le pourcentage de bruit entre deux images.
- original_img : Image de référence (par exemple, l'image originale).
- noisy_or_denoised_img : Image bruitée ou débruitée à comparer.
"""
noise_percentage = (np.linalg.norm(original_img - noisy_or_denoised_img) ** 2) / (np.linalg.norm(original_img) ** 2) * 100
return noise_percentage
# Calcul du pourcentage de bruit dans y (image bruitée)
noise_percentage_y = calculate_noise_percentage(in_img, noisy_img)
# Calcul du pourcentage de bruit restant après débruitage (L1)
noise_percentage_x_l1 = calculate_noise_percentage(in_img, denoised_img_l1)
# Calcul du pourcentage de bruit restant après débruitage (L2)
noise_percentage_x_l2 = calculate_noise_percentage(in_img, denoised_img_l2)
# Affichage des résultats
print(f"Pourcentage de bruit ajouté dans l'image bruitée (y) : {noise_percentage_y:.2f}%")
print(f"Pourcentage de bruit restant après débruitage (L1) : {noise_percentage_x_l1:.2f}%")
print(f"Pourcentage de bruit restant après débruitage (L2) : {noise_percentage_x_l2:.2f}%")
# Comparaison visuelle
plt.figure(figsize=(10, 6))
bars = ['Bruit dans $y$', 'Bruit restant ($L_1$)', 'Bruit restant ($L_2$)']
values = [noise_percentage_y, noise_percentage_x_l1, noise_percentage_x_l2]
colors = ['red', 'blue', 'green']
plt.bar(bars, values, color=colors, alpha=0.7)
plt.ylabel("Pourcentage de bruit (%)")
plt.title("Comparaison des pourcentages de bruit")
plt.grid(axis='y')
plt.ylim(0, max(values) * 1.1)
plt.show()
Pourcentage de bruit ajouté dans l'image bruitée (y) : 149.78% Pourcentage de bruit restant après débruitage (L1) : 1.12% Pourcentage de bruit restant après débruitage (L2) : 1.03%
Comparaison des pourcentages de bruit¶
Les résultats montrent une réduction drastique du bruit initial après débruitage avec les méthodes de régularisation $L_1$ et $L_2$. L'image bruitée ($y$) présente un pourcentage de bruit très élevé de $149.78\%$, reflétant l'importance du bruit ajouté. Après débruitage avec $L_1$, le bruit résiduel est ramené à seulement $1.12\%$, prouvant l'efficacité de cette méthode pour réduire les perturbations. Cependant, la régularisation $L_2$ démontre une performance légèrement supérieure, avec un bruit résiduel de $1.03\%$, indiquant sa capacité à mieux préserver les structures de l'image tout en supprimant les artefacts. Ces résultats soulignent l'efficacité des deux méthodes, avec un avantage notable pour $L_2$.
Conclusion¶
Ce TP a permis d’explorer les performances des régularisations $L_1$ et $L_2$ pour le débruitage d’images via la transformée en ondelettes. Les résultats obtenus montrent que les deux méthodes réduisent efficacement le bruit tout en préservant les détails de l’image. La régularisation $L_1$, grâce à son application du soft-thresholding, a montré une bonne capacité de débruitage, bien que légèrement inférieure à celle de $L_2$. La régularisation $L_2$, avec sa réduction quadratique, a démontré une meilleure performance globale, réduisant davantage le bruit tout en minimisant les artefacts, ce qui est confirmé par un pourcentage de bruit restant plus faible et des erreurs normalisées plus basses. Ces observations mettent en évidence l’importance du choix de la méthode de régularisation selon le contexte : $L_1$ pour une réduction efficace tout en maintenant des détails précis, et $L_2$ pour une reconstruction optimale dans des scénarios où une précision quadratique est cruciale
Aucun lien GitHub disponible pour ce projet.