Cours:Elen4 TNS TP TraitImage : Différence entre versions
(→Bruitage) |
|||
| (5 révisions intermédiaires par le même utilisateur non affichées) | |||
| Ligne 10 : | Ligne 10 : | ||
* appliquer des filtres linéaires pour : débruiter des images ou faire apparaitre leurs contours; | * appliquer des filtres linéaires pour : débruiter des images ou faire apparaitre leurs contours; | ||
* détecter des motifs dans des images | * détecter des motifs dans des images | ||
| + | |||
| + | Nous aurons besoin de la librairie <code>skimage-image</code> : | ||
| + | <source> | ||
| + | pixi add scikit-image | ||
| + | </source> | ||
| + | |||
| + | et des images suivantes de test : | ||
| + | |||
| + | [[Fichier:cameraman.png|200px]] [[Fichier:coins.png|200px]] | ||
| + | [[Fichier:rice.png|200px]] [[Fichier:ngc6543a.jpg|200px]] | ||
| + | |||
== Représentation des images numériques == | == Représentation des images numériques == | ||
| Ligne 21 : | Ligne 32 : | ||
Si les domaines de variation des variables sont finis, alors le signal ''x(k,l)'' peut être représenté | Si les domaines de variation des variables sont finis, alors le signal ''x(k,l)'' peut être représenté | ||
par une tableau rectangulaire. | par une tableau rectangulaire. | ||
| + | |||
| + | |||
=== Chargement et visualisation === | === Chargement et visualisation === | ||
| − | + | En partant de ce code exemple pour charger une image et l'afficher avec une palette en niveaux de gris : | |
| − | <source lang= | + | <source lang=python> |
| − | + | def fastshow(img): | |
| + | plt.imshow(img, cmap='gray') | ||
| + | plt.colorbar() | ||
| + | plt.show() | ||
| + | |||
| + | img = skimage.io.imread(filename) | ||
| + | print(img.shape) | ||
| + | fastshow(img) | ||
</source> | </source> | ||
| − | + | {{Todos| Charger et afficher l’image <code>coins.png</code>}} | |
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | {{Todos| Charger et afficher l’image <code>coins.png</code> | ||
{{Todos| Observer comment sont stockées les valeurs des pixels dans le tableau <code>I</code>.}} | {{Todos| Observer comment sont stockées les valeurs des pixels dans le tableau <code>I</code>.}} | ||
| Ligne 47 : | Ligne 55 : | ||
=== Modification de niveaux de gris === | === Modification de niveaux de gris === | ||
| − | {{q| a)}} Puisque les valeur de luminance sont (pour l'image <code> | + | {{q| a)}} Puisque les valeur de luminance sont (pour l'image <code>coins</code>) codées sur 8 bits. Il est possible d'inverser la palette (en niv. de gris) par la transformation suivante |
<center> | <center> | ||
''Y(k,l) = 255 - X(k,l)'' | ''Y(k,l) = 255 - X(k,l)'' | ||
| Ligne 63 : | Ligne 71 : | ||
{{Todos| Choisir une valeur seuil pour permettre de binariser l'image de façon à afficher les pièces en blanc sur fond noir.}} | {{Todos| Choisir une valeur seuil pour permettre de binariser l'image de façon à afficher les pièces en blanc sur fond noir.}} | ||
| − | {{Todos| Essayer de faire de même avec l'image <code> | + | {{Todos| Essayer de faire de même avec l'image <code>rice.png</code>.}} |
=== Images couleurs === | === Images couleurs === | ||
| Ligne 76 : | Ligne 84 : | ||
On utilisera une image test de caméraman : | On utilisera une image test de caméraman : | ||
| − | <code>X = imread('cameraman. | + | <code>X = skimage.io.imread('cameraman.png')</code> |
{{q| a)}} Lire et afficher l'image <code>X</code>. | {{q| a)}} Lire et afficher l'image <code>X</code>. | ||
| − | {{q| b)}} Calculer ses coefficients de Fourier à l'aide de la fonction <code>fft2()</code> (FFT en deux dimensions). | + | {{q| b)}} Calculer ses coefficients de Fourier à l'aide de la fonction <code>numpy.fft.fft2()</code> (FFT en deux dimensions). |
| − | {{q| c)}} Calculer et afficher | + | {{q| c)}} Calculer et afficher les spectres d'amplitude et de phase. |
| − | * | + | * Pour centrer l'affichage des fréquences, il est commode d'utiliser <code>numpy.fft.fftshift(...)</code> |
| − | * Il sera probablement judicieux d'utiliser une échelle log pour les intensités des harmoniques : <code>log(1+...)</code> | + | * Il sera probablement judicieux d'utiliser une échelle log pour les intensités des harmoniques : <code>numpy.log(1++...)</code> |
{{q| d)}} Essayer d'interpréter le contenu fréquentiel à partir de l'image du spectre d'amplitude, et de l'image du spectre de phase. | {{q| d)}} Essayer d'interpréter le contenu fréquentiel à partir de l'image du spectre d'amplitude, et de l'image du spectre de phase. | ||
| Ligne 97 : | Ligne 105 : | ||
''X'' etant une image 'propre' et ''B'' une image de bruit. | ''X'' etant une image 'propre' et ''B'' une image de bruit. | ||
| − | Pour ''X'', on prendra l'image cameraman : | + | Pour ''X'', on prendra l'image cameraman convertie en float: |
| − | < | + | <source lang=python> |
| + | X = skimage.io.imread('cameraman.png') | ||
| + | X = skimage.img_as_float(X) | ||
| + | </source> | ||
=== Bruitage === | === Bruitage === | ||
| − | {{q| a)}} À l'aide de la fonction <code> | + | {{q| a)}} À l'aide de la fonction <code>skimage.util.random_noise</code>, créer : |
* une image <code>Xb1</code> en ajoutant à l'image <code>X</code> du bruit gaussien | * une image <code>Xb1</code> en ajoutant à l'image <code>X</code> du bruit gaussien | ||
* et une image <code>Xb2</code> en ajoutant à l'image <code>X</code> du bruit poivre et sel (''salt-and-pepper'') | * et une image <code>Xb2</code> en ajoutant à l'image <code>X</code> du bruit poivre et sel (''salt-and-pepper'') | ||
| Ligne 122 : | Ligne 133 : | ||
</center> | </center> | ||
| − | {{q| b)}} A l'aide de la fonction <code> | + | {{q| b)}} A l'aide de la fonction <code>scipy.ndimage.convolve</code>, appliquer le filtre moyenneur aux images <code>Xb1</code> et <code>Xb2</code>. |
| − | + | {{Todos| Estimer la qualité du débruitage linéaire sur chaque image.}} | |
| − | |||
| − | {{Todos| | ||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
=== Débruitage non-linéaire === | === Débruitage non-linéaire === | ||
| Ligne 137 : | Ligne 141 : | ||
{{q| c)}} Il est possible d'appliquer des filtres non-linéaires. Dans notre cas, vous pouvez tester l'effet d'un filtre non-linéaire de type médiane (à la place de la moyenne) : | {{q| c)}} Il est possible d'appliquer des filtres non-linéaires. Dans notre cas, vous pouvez tester l'effet d'un filtre non-linéaire de type médiane (à la place de la moyenne) : | ||
| − | < | + | <source lang=python> |
| − | + | Y2 = sp.ndimage.generic_filter(Xb2, | |
| − | + | np.median, | |
| − | + | size=3) | |
| − | </ | + | </source> |
{{Todos| Apprécier la qualité de ce débruitage non-linéaire sur chaque image.}} | {{Todos| Apprécier la qualité de ce débruitage non-linéaire sur chaque image.}} | ||
| Ligne 171 : | Ligne 175 : | ||
|} | |} | ||
| − | {{q| a)}} En travaillant avec l'image caméraman, obtenir <code>Gx</code> et <code>Gy</code>, | + | {{q| a)}} En travaillant avec l'image caméraman, obtenir <code>Gx</code> et <code>Gy</code>, résultats de l'application des filtres de réponse impulsionnelle <code>Dx</code> et <code>Dy</code> sur cette image (en utilisant <code>scipy.ndimage.convolve(...)</code>). |
{{Todos| Afficher et apprécier visuellement le résultat}} | {{Todos| Afficher et apprécier visuellement le résultat}} | ||
| Ligne 179 : | Ligne 183 : | ||
''||G|| = (Gx<sup>2</sup> + Gy<sup>2</sup>)<sup>1/2</sup> | ''||G|| = (Gx<sup>2</sup> + Gy<sup>2</sup>)<sup>1/2</sup> | ||
</center> | </center> | ||
| − | {{Todos| Calculer l'image <code>G</code> et l'afficher.}} | + | {{Todos| Calculer l'image <code>G</code> et l'afficher. }} |
{{q| c)}} Avec la même méthode, obtenir la norme du gradient de l'image caméraman bruitée par du bruit gaussien (<code>Xb1</code> dans la partie ''Débruitage'' précédente). | {{q| c)}} Avec la même méthode, obtenir la norme du gradient de l'image caméraman bruitée par du bruit gaussien (<code>Xb1</code> dans la partie ''Débruitage'' précédente). | ||
| Ligne 209 : | Ligne 213 : | ||
|} | |} | ||
| − | {{Todos| Filtrer l'image caméraman avec ces deux réponses impulsionnelles, calculer la norme du gradient, et comparer avec le résultat précédent.}} | + | {{Todos| Filtrer l'image caméraman avec ces deux réponses impulsionnelles, calculer la norme du gradient, et comparer avec le résultat précédent. En cas de soucis, utiliser l'implémentation de scikit-image : <code>skimage.filters.sobel</code>}} |
Version actuelle datée du 18 mai 2026 à 14:44
TP4 : Traitement d'images numériques
Le travail de ce TP va consister à
- observer et manipuler (simplement) la représentation spatiale d'images numériques;
- observer et interpréter le contenu fréquentiel des images;
- appliquer des filtres linéaires pour : débruiter des images ou faire apparaitre leurs contours;
- détecter des motifs dans des images
Nous aurons besoin de la librairie skimage-image :
pixi add scikit-imageet des images suivantes de test :
Sommaire
Représentation des images numériques
Une image numérique est un signal numérique bidimensionnel. C'est donc une fonction réelle ou complexe de deux variables indépendantes. De manière générale, une image est représentée par :
x(k,l) avec k,l ∈ ℤ
Si les domaines de variation des variables sont finis, alors le signal x(k,l) peut être représenté par une tableau rectangulaire.
Chargement et visualisation
En partant de ce code exemple pour charger une image et l'afficher avec une palette en niveaux de gris :
def fastshow(img):
plt.imshow(img, cmap='gray')
plt.colorbar()
plt.show()
img = skimage.io.imread(filename)
print(img.shape)
fastshow(img)
Charger et afficher l’image coins.png
Observer comment sont stockées les valeurs des pixels dans le tableau I.
Modification de niveaux de gris
a) Puisque les valeur de luminance sont (pour l'image coins) codées sur 8 bits. Il est possible d'inverser la palette (en niv. de gris) par la transformation suivante
Y(k,l) = 255 - X(k,l)
Inverser l'image coins et afficher le résultat.
b) Pour binariser une image, le plus simple est de choisir une valeur seuil :
| si X(k,l)>seuil | Y(k,l)=1 |
| sinon | Y(k,l) = 0 |
Choisir une valeur seuil pour permettre de binariser l'image de façon à afficher les pièces en blanc sur fond noir.
Essayer de faire de même avec l'image rice.png.
Images couleurs
a) Lire l'image ngc6543a.jpg.
b) Observer comment sont stockées les valeurs des pixels dans le tableau. Que représente la troisième dimension ?
c) Binariser l'image pour obtenir une image binaire faisant apparaitre le noyau de la galaxie seul.
Contenu fréquentiel des images
On utilisera une image test de caméraman :
X = skimage.io.imread('cameraman.png')
a) Lire et afficher l'image X.
b) Calculer ses coefficients de Fourier à l'aide de la fonction numpy.fft.fft2() (FFT en deux dimensions).
c) Calculer et afficher les spectres d'amplitude et de phase.
- Pour centrer l'affichage des fréquences, il est commode d'utiliser
numpy.fft.fftshift(...) - Il sera probablement judicieux d'utiliser une échelle log pour les intensités des harmoniques :
numpy.log(1++...)
d) Essayer d'interpréter le contenu fréquentiel à partir de l'image du spectre d'amplitude, et de l'image du spectre de phase.
Débruitage
De façon similaire au TP n°3, l'objectif est de filtrer une image
Xb(k,l) = X(k,l) + B(k,l)
X etant une image 'propre' et B une image de bruit.
Pour X, on prendra l'image cameraman convertie en float:
X = skimage.io.imread('cameraman.png')
X = skimage.img_as_float(X)
Bruitage
a) À l'aide de la fonction skimage.util.random_noise, créer :
- une image
Xb1en ajoutant à l'imageXdu bruit gaussien - et une image
Xb2en ajoutant à l'imageXdu bruit poivre et sel (salt-and-pepper)
Afficher les images Xb1 et Xb2 pour observer les deux types de bruit
Débruitage linéaire
Pour réduire le bruit, il est possible d'appliquer un filtre moyenneur dont voici la réponse impulsionnelle :
| 1/9 | 1/9 | 1/9 |
| 1/9 | 1/9 | 1/9 |
| 1/9 | 1/9 | 1/9 |
b) A l'aide de la fonction scipy.ndimage.convolve, appliquer le filtre moyenneur aux images Xb1 et Xb2.
Estimer la qualité du débruitage linéaire sur chaque image.
Débruitage non-linéaire
c) Il est possible d'appliquer des filtres non-linéaires. Dans notre cas, vous pouvez tester l'effet d'un filtre non-linéaire de type médiane (à la place de la moyenne) :
Y2 = sp.ndimage.generic_filter(Xb2,
np.median,
size=3)
Apprécier la qualité de ce débruitage non-linéaire sur chaque image.
Détection de contours
Les contours d'une image sont souvent obtenus à partir d'une dérivée de l'image (contour = position d'une variation de grande amplitude). Puisqu'une image est à deux dimensions, il est possible de calculer deux dérivées (une sur chaque dimension), ce qui conduit à l'application de deux filtres dont voici les réponses impusionnelles :
Dx =
| 0 | 0 | 0 |
| 1 | 0 | -1 |
| 0 | 0 | 0 |
et
Dy =
| 0 | 1 | 0 |
| 0 | 0 | 0 |
| 0 | -1 | 0 |
a) En travaillant avec l'image caméraman, obtenir Gx et Gy, résultats de l'application des filtres de réponse impulsionnelle Dx et Dy sur cette image (en utilisant scipy.ndimage.convolve(...)).
Afficher et apprécier visuellement le résultat
b) En pratique, Gx et Gy sont les deux composantes d'un gradient dont on peut calculer une norme :
||G|| = (Gx2 + Gy2)1/2
Calculer l'image G et l'afficher.
c) Avec la même méthode, obtenir la norme du gradient de l'image caméraman bruitée par du bruit gaussien (Xb1 dans la partie Débruitage précédente).
Observer le comportement du filtre. Quel est le problème ?
d) Pour y remédier, une méthode consiste à moyenner les pixels dans la direction orthogonale à celle où la dérivée est appliquée. Ce qui conduit aux deux filtres de Sobel, dont voici les réponses impulsionnelles :
Sx =
| 1 | 0 | -1 |
| 2 | 0 | -2 |
| 1 | 0 | -1 |
et
Sy =
| 1 | 2 | 1 |
| 0 | 0 | 0 |
| -1 | -2 | -1 |
Filtrer l'image caméraman avec ces deux réponses impulsionnelles, calculer la norme du gradient, et comparer avec le résultat précédent. En cas de soucis, utiliser l'implémentation de scikit-image : skimage.filters.sobel