Exemples ImageMagick -- Transformées de Fourier
- Les images sont des ondes
- Images de magnitude ou de phase seule
- Image de spectre FFT
- Images FFT HDRI
- Effets de la couleur DC
- Spectre d'une image d'onde sinusoïdale
- Générer directement des images FFT
- Spectre d'une image de motif rectangulaire
- Spectre d'une image de motif circulaire plat
- Spectre d'une image de motif circulaire gaussien
-
Modifier le contraste d'une image - extraction de racine des coefficients
- Flouter une image - filtrage passe-bas
- Détecter les contours d'une image - filtrage passe-haut
- Accentuer une image - filtrage à renforcement des hautes fréquences
-
Suppression du bruit - filtrage coupe-bande
Multiplication et division FFT (exemples bas niveau - sous-page)
Introduction
L'un des concepts les plus difficiles à appréhender en traitement d'image est la transformée de Fourier. Il y a deux raisons à cela. D'une part, elle est mathématiquement avancée ; d'autre part, les images obtenues, qui ne ressemblent pas à l'image d'origine, sont difficiles à interpréter. La transformée de Fourier permet néanmoins d'aborder autrement des traitements familiers comme le rehaussement de la luminosité et du contraste, le flou, l'accentuation et la suppression du bruit. Mais elle offre aussi des possibilités inaccessibles dans le domaine image habituel : par exemple la déconvolution (aussi appelée « défloutage ») des distorsions typiques d'un appareil photo, comme le flou de bougé et la mise au point défectueuse, ou encore la mise en correspondance d'images par corrélation croisée normalisée. Cette page a pour but d'expliquer le contexte et les mathématiques simplifiées de la transformée de Fourier, et de donner des exemples de traitements réalisables grâce à elle. Si cela vous paraît trop ardu, vous pouvez sauter cette partie et vous concentrer simplement sur les propriétés et les exemples, en commençant par FFT/IFT dans ImageMagick. Pour ceux que cela intéresse, on trouvera une autre présentation simple et agréable, incluant des analogies avec l'optique, dans An Intuitive Explanation of Fourier Theory. Les notes de cours de la Vanderbilt University School Of Engineering sont également très instructives pour les esprits plus portés sur les mathématiques : 1 & 2 Dimensional Fourier Transforms et Frequency Filtering. Parmi les autres références mathématiques figurent les pages Wikipedia sur Fourier Transform, Discrete Fourier Transform et Fast Fourier Transform, ainsi que Complex Numbers. Je remercie Sean Burke pour le codage de la démo originale, et le créateur d'ImageMagick pour l'avoir intégrée à ImageMagick. Ces deux contributions furent héroïques. De nombreux exemples utilisent une version HDRI d'ImageMagick, nécessaire pour préserver la précision des images transformées. Il est recommandé de compiler votre propre version HDRI si vous souhaitez tirer le meilleur parti de ces techniques.
La transformée de Fourier
En temps normal, une image est constituée d'un tableau de « pixels », chacun défini par un ensemble de valeurs : rouge, vert, bleu, et parfois aussi la transparence. Mais pour ce qui nous intéresse ici, nous ignorerons la transparence. Chacun des canaux rouge, vert et bleu contient donc un ensemble de valeurs d'« intensité » ou de « niveaux de gris ». C'est ce que l'on appelle une image matricielle « dans le domaine spatial ». C'est simplement une façon savante de dire que l'image est définie par les « valeurs d'intensité » qu'elle possède à chaque « emplacement » ou « position dans l'espace ». Mais une image peut aussi être représentée d'une autre manière, appelée « domaine fréquentiel » de l'image. Dans ce domaine, chaque canal de l'image est représenté par des ondes sinusoïdales. Dans un tel « domaine fréquentiel », chaque canal possède des valeurs d'« amplitude » stockées à des emplacements fondés non pas sur des coordonnées « spatiales » X,Y, mais sur des « fréquences » X,Y. Comme il s'agit d'une représentation numérique, les fréquences sont des multiples d'une fréquence « minimale » ou unitaire, et les coordonnées des pixels représentent les indices, c'est-à-dire les multiples entiers de cette fréquence unitaire. Cela découle du principe selon lequel « toute fonction se comportant convenablement peut être représentée par une superposition (combinaison ou somme) d'ondes sinusoïdales ». Autrement dit, la représentation dans le « domaine fréquentiel » n'est qu'une autre façon de stocker et de reproduire l'image du « domaine spatial ». Mais comment une image peut-elle être représentée sous forme d'« onde » ?
Les images sont des ondes
Eh bien, si l'on prend une seule ligne ou colonne de pixels de n'importe quelle image et qu'on la trace (au moyen de « gnuplot » et du script « [im_profile](../static/img/scripts/im_profile) »), on constate qu'elle ressemble assez à une onde.
magick holocaust_tn.gif -colorspace gray miff:- |\
im_profile -s - image_profile.gif
Si les fluctuations étaient plus régulières en espacement et en amplitude, on obtiendrait quelque chose de plus proche d'un motif d'onde, comme...
magick -size 20x150 gradient: -rotate 90 \
-function sinusoid 3.5,0,.4 wave.gif
im_profile -s wave.gif wave_profile.gif
Cependant, si ce motif d'onde régulier est vaguement semblable au profil d'image montré ci-dessus, il est trop régulier. En additionnant davantage d'ondes, on peut toutefois créer un motif encore plus proche de celui issu de l'image.
magick -size 1x150 gradient: -rotate 90 \
-function sinusoid 3.5,0,.25,.25 wave_1.png
magick -size 1x150 gradient: -rotate 90 \
-function sinusoid 1.5,-90,.13,.15 wave_2.png
magick -size 1x150 gradient: -rotate 90 \
-function sinusoid 0.6,-90,.07,.1 wave_3.png
magick wave_1.png wave_2.png wave_3.png \
-evaluate-sequence add added_waves.png
Voir aussi Adding Biased Gradients pour un exemple alternatif à ce qui précède. Cette « superposition d'ondes » (addition d'ondes) est bien plus proche, mais ne correspond toujours pas exactement au motif de l'image. On peut néanmoins continuer ainsi, en ajoutant et en ajustant d'autres ondes, de sorte que l'onde composite obtenue se rapproche de plus en plus du profil réel de l'image d'origine. En additionnant finalement suffisamment d'ondes, on peut reproduire exactement le profil d'origine de l'image. Telle fut la découverte du mathématicien Joseph Fourier. Une interprétation moderne en énonce que « toute fonction se comportant convenablement peut être représentée par une superposition d'ondes sinusoïdales ». Autrement dit, en additionnant un nombre suffisant d'ondes sinusoïdales ayant exactement la bonne fréquence et la bonne amplitude, on peut reproduire n'importe quel motif fluctuant. Par conséquent, la représentation dans le « domaine fréquentiel » n'est qu'une autre façon de stocker et de reproduire l'image du « domaine spatial ». La « transformée de Fourier » est alors le procédé consistant à déterminer quelles « ondes » composent une image, exactement comme dans l'exemple ci-dessus.
Ondes bidimensionnelles dans les images
Ce qui précède montre un exemple de la façon dont on peut approcher le profil d'une seule ligne d'une image à l'aide de plusieurs ondes sinusoïdales. Les images sont cependant bidimensionnelles ; les ondes utilisées pour représenter une image dans le « domaine fréquentiel » doivent donc l'être aussi. Voici un exemple d'une telle onde bidimensionnelle. Cette onde comporte plusieurs composantes. Exemple d'image
Utiliser la FFT/IFT dans ImageMagick
Notes d'implémentation
ImageMagick utilise la FFTW, bibliothèque de transformée de Fourier discrète, qui nécessite de convertir les images en valeurs à virgule flottante (nombres complexes) et inversement ; elle a été implémentée pour la première fois dans la version 6.5.4-3 d'IM. Pour qu'elle se comporte comme on l'attend généralement pour des images, toute image non carrée ou de dimension impaire est complétée (à l'aide des pixels virtuels) afin de devenir carrée, à la plus grande des deux dimensions (largeur ou hauteur) de l'image. Afin de permettre un centrage correct de l'« origine de la FFT » au centre de l'image, les dimensions sont aussi forcées à être paires (multiples de 2). La conséquence en est qu'après application de la transformée de Fourier inverse, l'image devra être rognée à ses dimensions d'origine pour retirer le remplissage. Comme la transformée de Fourier est composée de « nombres complexes », le résultat de la transformée ne peut pas être visualisé directement. La transformée complexe est donc séparée en deux images de composantes, sous l'une de deux formes possibles. ![[Diagram]](../static/img/img_diagrams/complex_number.jpg)
Nombre complexe
Partie réelle/imaginaire
Partie réelle et partie imaginaire
La représentation mathématique et numérique habituelle des « nombres complexes » est un couple de valeurs à virgule flottante composé d'une partie « réelle » (a) et d'une partie « imaginaire » (b). Malheureusement, ces deux nombres peuvent contenir des valeurs négatives et ne forment donc pas des images affichables. Cette représentation ne peut par conséquent pas être utilisée dans une version normale d'ImageMagick, qui écrêterait de telles images (voir plus bas l'exemple des effets obtenus). En revanche, avec une version HDRI d'ImageMagick, vous pouvez tout de même générer, utiliser et même enregistrer cette représentation d'une image transformée par Fourier. Ces images ne sont peut-être ni utiles ni même affichables en tant que telles, mais vous pouvez néanmoins leur appliquer de nombreuses opérations mathématiques. Pour générer cette représentation, on utilise la forme « plus » des opérateurs, « [+fft](https://usage.imagemagick.org/fourier/option_link.cgi#fft) » et « [+ift](https://usage.imagemagick.org/fourier/option_link.cgi#ift) », que nous examinerons en détail plus bas dans La FFT en composantes réelle et imaginaire. ![[Diagram]](../static/img/img_diagrams/polar_number.jpg)
Forme polaire complexe
Magnitude/Phase
Magnitude et phase
La représentation numérique directe des « nombres complexes » n'est pas très utile pour le travail sur les images. Mais en reportant les valeurs sur un plan à deux dimensions, on peut convertir la valeur en une représentation polaire composée d'une « magnitude » (r) et d'une « phase » (θ). Cette forme est très utile en traitement d'image, en particulier la composante de magnitude, qui spécifie essentiellement toutes les fréquences qui composent l'image. La composante « magnitude » ne contient que des valeurs positives et est directement transposée en valeurs d'image. Elle n'a pas de plage de valeurs fixe, mais, hormis la couleur de la fréquence continue (DC) ou nulle, les valeurs sont en général assez faibles. En conséquence, l'image de magnitude paraît généralement très sombre (pratiquement noire). Pour faire ressortir le moindre détail visuel, il faut habituellement mettre la magnitude à l'échelle et appliquer une transformation logarithmique à ses valeurs d'intensité. L'image de magnitude ainsi « transformée en logarithme » est appelée le « spectre » de l'image. N'oubliez toutefois pas que c'est l'image de « magnitude », et non l'image de « spectre », qui doit être utilisée pour la transformée inverse. La couleur DC (abréviation de « Direct Current », courant continu) ou « fréquence nulle », qui apparaît à l'« origine » centrale de l'image, correspond à la couleur moyenne de l'ensemble de l'image. De plus, comme les images d'entrée ne contiennent pas de composante « imaginaire », la phase DC aura elle aussi toujours une valeur nulle, produisant une couleur d'un gris pur. La composante « phase », en revanche, s'étend de -π à +π. Elle est d'abord décalée dans une plage de 0 à 2π, puis mise à l'échelle en valeurs d'image effectives allant de 0 à QuantumRange (déterminé par la qualité mémoire fixée à la compilation). En conséquence, une phase nulle correspondra à une valeur de gris pur (appropriée à chaque canal), tandis qu'une phase négative correspondra à un noir pur (« 0 »). Notez qu'un blanc pur (« _QuantumRange_ ») en est presque, mais pas tout à fait, l'équivalent. Une représentation FFT en magnitude et phase d'une image est générée à l'aide des opérateurs FFT normaux, « [+fft](https://usage.imagemagick.org/fourier/option_link.cgi#fft) » et « [+ift](https://usage.imagemagick.org/fourier/option_link.cgi#ift) ». Nous l'examinerons d'abord dans Générer les images FFT et leur inverse.
Générer les images FFT et leur inverse
(Magnitude et phase)
À présent, essayons simplement un aller-retour de transformée de Fourier sur l'image Lena. Autrement dit, nous effectuons la transformée directe puis appliquons immédiatement la transformée inverse pour retrouver l'image d'origine. Nous comparerons ensuite les résultats afin d'évaluer le niveau de qualité obtenu.
time magick lena.png -fft -ift lena_roundtrip.png
echo -n "RMSE = "
magick compare -metric RMSE lena.png lena_roundtrip.png null:
echo -n "PAE = "
magick compare -metric PAE lena.png lena_roundtrip.png null:
Le programme « [compare](basics.html#compare) » ci-dessus renvoie une mesure de la différence entre les deux images. Dans ce cas, on constate que la différence générale est très faible, d'environ 0.22%, avec une différence de valeur maximale sur au moins un pixel (« PAE », Peak Absolute Error) d'à peine 1% environ. Vous pouvez améliorer cela en utilisant une version HDRI d'ImageMagick (voir FFT avec HDRI plus bas). Examinons de plus près les images FFT générées lors de l'aller-retour ci-dessus.
magick lena.png -fft +depth +adjoin lena_fft_%d.png
![[IM Output]](../static/img/img_photos/lena.png)
Original | | ![[IM Output]](../static/img/fourier/lena_fft_0.png)
Magnitude | ![[IM Output]](../static/img/fourier/lena_fft_1.png)
Phase
---|---|---|---
Comme l'a dit John M. Brayer à propos des transformées de Fourier... Nous n'affichons généralement pas les images de PHASE, car la plupart des gens qui les voient sombrent peu après dans les hallucinogènes ou finissent dans un monastère tibétain. Notez que l'opérateur « [-fft](https://usage.imagemagick.org/fourier/option_link.cgi#fft) » a généré deux images : la première est la composante de « magnitude » (oui, elle est presque entièrement noire avec un unique point coloré au milieu), tandis que la seconde, d'apparence presque aléatoire, contient la composante de « phase ». Les images PNG ne pouvant stocker qu'une seule image par fichier, ni « [+adjoin](https://usage.imagemagick.org/fourier/option_link.cgi#adjoin) » ni le « %d » du nom de fichier de sortie n'étaient réellement nécessaires, car IM s'en serait chargé. J'inclus toutefois ces options ci-dessus par souci d'exhaustivité, afin qu'il soit clair que je génère deux fichiers image distincts, et non un seul. Voir Écrire une séquence de plusieurs images pour plus de détails. Comme deux images sont générées, l'image de magnitude (la première, dite d'indice zéro) est enregistrée dans « lena_fft_0.png » et l'image de phase (la seconde) dans « lena_fft_1.png ». | _Pour écarter tout risque de distorsion lié à l'enregistrement des images FFT, le mieux est de ne pas les enregistrer du tout sur disque, mais de les conserver en mémoire pendant que vous traitez l'image.
S'il faut absolument les enregistrer, le mieux est d'utiliser le format de fichier Magick « [MIFF](https://usage.imagemagick.org/files/#miff") » afin de préserver l'image à sa plus haute qualité (profondeur de bits). Ce format peut aussi enregistrer plusieurs images dans un même fichier. Pour le travail par script, vous pouvez également recourir au format de pixels énumérés détaillé « [TXT](files.html#txt) ».
N'ENREGISTREZ PAS ces images aux formats d'image « [JPEG](formats.html#jpg) » ou « [GIF](formats.html#gif) ».
Si vous devez enregistrer ces images dans des fichiers pour un affichage réel, par exemple pour un navigateur web, utilisez le format d'image « [PNG](formats.html#png) » avec un « [+depth](https://usage.imagemagick.org/fourier/option_link.cgi#depth) » réinitialisé à la valeur interne par défaut (comme nous le faisons dans ces exemples). Il ne peut toutefois stocker qu'une seule image par fichier.
Le format de fichier « [TIFF](formats.html#tiff) » peut également être utilisé, bien qu'il soit moins adapté aux navigateurs web ; il permet en revanche de stocker plusieurs images par fichier.
_
---|---
La meilleure façon d'enregistrer des images intermédiaires dans un seul fichier est d'utiliser le format de fichier « [MIFF](https://usage.imagemagick.org/files/#miff") »...
magick lena.png -fft +depth lena_fft.miff
Vous pouvez aussi les enregistrer sous des noms de fichiers entièrement distincts à l'aide de « [-write](https://usage.imagemagick.org/fourier/option_link.cgi#write) » (voir Écrire des images)...
magick lena.png -fft +depth \
\( -clone 0 -write lena_magnitude.png +delete \) \
\( -clone 1 -write lena_phase.png +delete \) \
null:
Notez que, ci-dessus, j'ai utilisé le format d'image spécial « [NULL:](files.html#null) » pour jeter les deux images, qui restent néanmoins conservées en mémoire pour un traitement ultérieur. Et pour finir, nous relisons les deux images afin de les reconvertir en une image « spatiale » normale...
magick lena_magnitude.png lena_phase.png -ift lena_restored.png
Les deux images produites par le processus FFT sont très sensibles aux modifications : même de faibles changements peuvent entraîner des résultats fortement déformés. Il est donc important de ne jamais les enregistrer dans un format d'image susceptible d'altérer ces valeurs. Il importe aussi de se rappeler que les deux images sont nécessaires pour récupérer l'image depuis le domaine fréquentiel. Il ne sert donc à rien d'enregistrer une image et de jeter l'autre si vous comptez les utiliser pour reconstruire l'image.
Images de magnitude ou de phase seule
Enfin, essayons de reconstruire une image à partir de sa seule composante de magnitude, ou de sa seule composante de phase.
magick lena_fft_0.png -size 128x128 xc:'gray(50%)' \
-ift lena_magitude_only.png
magick -size 128x128 xc:gray1 lena_fft_1.png -ift lena_phase_only.png
![[IM Output]](../static/img/fourier/lena_magitude_only.png)
Magnitude seule | ![[IM Output]](../static/img/fourier/lena_phase_only.png)
Phase seule
---|---
Vous remarquerez que c'est l'image de phase qui contient en réalité l'essentiel de l'information de position de l'image, tandis que la magnitude retient une grande partie de l'information de couleur. Ce n'est pas exact, car il y a un certain recouvrement de l'information, mais c'est généralement le cas. L'image « magnitude seule » présentera toujours des coins blancs, car une image de phase constante à 50 % a été utilisée. Vous pouvez supprimer ces taches blanches en utilisant une image de phase aléatoire. Veillez cependant à ce que la phase du pixel central soit un gris à 50 % parfait, faute de quoi toute l'image sera assombrie. L'image « phase seule » a utilisé, pour la conversion, une image de magnitude d'un gris constant à 1 % (presque noir pur). Même avec cette magnitude constante, elle produit tout de même des taches de pixels très intenses, en particulier le long des contours. Il faut simplement se rappeler que les deux images sont nécessaires pour reconstruire l'image d'origine.
Image de spectre de fréquences
Vous aurez remarqué que l'image de magnitude (la première, dite d'indice zéro) paraît presque entièrement noire. Elle ne l'est pas vraiment, mais à nos yeux toutes les valeurs sont très, très faibles. Une telle image n'est pas très intéressante à étudier ; rehaussons donc le résultat par une transformation logarithmique afin de produire une image de « spectre de fréquences ». On procède en appliquant une forte transformation logarithmique Evaluate à une image de « magnitude » normalisée.
magick lena_fft_0.png -auto-level -evaluate log 10000 \
lena_spectrum.png
Nous pouvons maintenant voir le détail dans la version spectre de l'image de magnitude. Vous y verrez peut-être même certaines couleurs particulières, mais en général ces couleurs n'ont pas d'importance dans une image de spectre. C'est l'intensité globale de chaque fréquence, et les motifs qu'elles produisent, qui importent bien davantage. Vous pourrez donc aussi vouloir convertir l'image de spectre en niveaux de gris après le rehaussement. L'ampleur du rehaussement logarithmique nécessaire dépend de l'image : ajustez-la jusqu'à obtenir le niveau de détail voulu pour distinguer clairement le motif du spectre de fréquences de l'image. Vous pouvez aussi utiliser le petit script shell suivant pour calculer un facteur d'échelle logarithmique adapté à l'image de magnitude considérée. |
scale=`magick lena_fft_0.png -auto-level \
-format "%[fx:exp(log(mean)/log(0.5))]" info:`
magick lena_fft_0.png -auto-level \
-evaluate log $scale lena_spectrum_auto.png
![[IM Output]](../static/img/fourier/lena_spectrum_auto.png)
N'oubliez cependant pas que vous ne pouvez pas utiliser une image de spectre pour la transformée inverse « [-ift](https://usage.imagemagick.org/fourier/option_link.cgi#ift) », car elle produirait une image excessivement claire. |
magick lena_spectrum.png lena_fft_1.png -ift lena_roundtrip_fail.png
Fondamentalement, en rehaussant l'image de « magnitude », vous avez rehaussé de la même manière l'image obtenue, produisant le résultat fortement « écrêté » montré ici. ![[IM Output]](../static/img/fourier/lena_roundtrip_fail.png)
Images FFT HDRI
Lorsque nous avons transposé les résultats de la transformée de Fourier en une représentation d'image, nous avons mis à l'échelle et converti les valeurs des « nombres complexes » à virgule flottante en valeurs d'image entières. Cela a naturellement produit des erreurs d'arrondi et d'autres effets « quantiques », en particulier pour les magnitudes des plus basses fréquences. Si la précision compte dans votre traitement d'image, il vous faudra soit une qualité de bits plus élevée (par exemple les versions Q32 ou Q64 d'ImageMagick), soit, mieux encore, une version HDRI d'ImageMagick afin que les valeurs soient stockées sous forme de nombres à virgule flottante. Lorsqu'on utilise une version HDRI d'IM avec une représentation magnitude et phase de la transformée de Fourier, la composante de magnitude reste entièrement positive et peut donc s'utiliser comme montré plus haut, mais de façon bien plus exacte. La composante de phase reste néanmoins décalée et mise à l'échelle, comme indiqué précédemment. Autrement dit, la représentation magnitude et phase en HDRI est exactement la même, simplement bien plus précise.
Par exemple, j'utilise ici une version HDRI d'ImageMagick pour générer un autre aller-retour de conversion d'une image. |
# HDRI version of IM used
time magick lena.png -fft -ift lena_roundtrip_hdri.png
echo -n "RMSE = "
magick compare -metric RMSE lena.png lena_roundtrip_hdri.png null:
echo -n "PAE = "
magick compare -metric PAE lena.png lena_roundtrip_hdri.png null:
Si l'on compare les résultats ci-dessus avec la précédente comparaison non-HDRI...
| Vous constaterez que la version HDRI d'IM a produit un résultat bien plus précis, à une vitesse à peu près identique à la précédente (la vitesse peut varier selon votre ordinateur). Elle aura toutefois nécessité bien plus de mémoire qu'un IM Q16 normal (voir Qualité fixée à la compilation). Cependant, ces images, tout en représentant plus exactement les composantes fréquentielles de la FFT de l'image, peuvent contenir des valeurs négatives et fractionnaires et ne peuvent être enregistrées qu'à l'aide de formats de fichier compatibles HDRI capables de gérer les valeurs à virgule flottante. | Parmi les formats de fichier compatibles avec la virgule flottante figurent « [MIFF](https://usage.imagemagick.org/files/#miff") », « [TIFF](formats.html#tiff) », « [PFM](formats.html#netpbm) » et le format « EXR » propre au HDRI. Il se peut toutefois que vous deviez définir « -define quantum:format=floating-point » pour que cela fonctionne. |
|---|---|
| Dans les exemples ultérieurs, le traitement de la FFT d'une image nécessitera une telle précision pour donner de bons résultats. Aussi, à mesure que nous avancerons dans l'utilisation des transformées de Fourier rapides, une version HDRI d'ImageMagick deviendra une nécessité. |
La FFT en composantes réelle et imaginaire
Jusqu'à présent, nous n'avons examiné que les représentations en « magnitude » et en « phase » des images transformées par Fourier. Mais si vous avez compilé une version HDRI d'IM, vous pouvez aussi traiter les images à l'aide des composantes « réelle » et « imaginaire » à virgule flottante. Cela se fait au moyen des versions « plus » des options « [+fft](https://usage.imagemagick.org/fourier/option_link.cgi#fft) » et « [+ift](https://usage.imagemagick.org/fourier/option_link.cgi#ift) ». Par exemple, j'utilise ici une version HDRI d'IM pour effectuer également un aller-retour FFT d'une image, mais en générant cette fois des images réelle/imaginaire. |
# HDRI version of IM used
time magick lena.png +fft +ift lena_roundtrip_ri.png
echo -n "RMSE = "
magick compare -metric RMSE lena.png lena_roundtrip_ri.png null:
echo -n "PAE = "
magick compare -metric PAE lena.png lena_roundtrip_ri.png null:
Vous devez utiliser une version HDRI lorsque vous employez les formes « plus » pour générer des images FFT réelle/imaginaire. Sinon, environ la moitié des valeurs seront nulles, produisant une image d'aspect « sale ». Par exemple... |
# non-HDRI Q16 version of IM used -- THIS IS BAD
magick lena.png +fft +ift lena_roundtrip_ri_bad.png
![[IM Output]](../static/img/fourier/lena_roundtrip_ri_bad.png)
L'autre chose à retenir, c'est que la forme des images FFT que vous générez conditionnera aussi TOUTES les opérations de traitement d'image que vous voudrez leur appliquer. Ce sont des images très différentes ; elles doivent donc être traitées de manières très différentes, par des opérations mathématiques différentes. Et, comme précédemment, il vous faut à la fois les images de composante réelle et imaginaire pour restaurer l'image finale. Par exemple, voici ce qui se produit si l'on remplace l'une des composantes par une image « noire ».
# HDRI version of IM used
magick lena.png +fft -delete 1 \
-size 128x128 xc:black +ift lena_real_only.png
magick lena.png +fft -delete 0 \
-size 128x128 xc:black +ift lena_imaginary_only.png
![[IM Output]](../static/img/fourier/lena_real_only.png)
Réelle seule | ![[IM Output]](../static/img/fourier/lena_imaginary_only.png)
Imaginaire seule
---|---
On voit ainsi que les deux images FFT réelle/imaginaire contiennent, de façon assez équilibrée, des informations essentielles sur l'image d'origine. La plus grande différence entre les deux est que le DC particulier, ou « couleur moyenne », n'a pas de composante imaginaire et n'est donc présent que dans l'image de magnitude. L'effet de miroir diagonal (en réalité une rotation de 180°) que l'on observe dans les deux images est dû à la perte de l'information de « signe » contenue dans l'autre composante. En l'absence de l'autre composante, l'onde pourrait être considérée comme déphasée de 180 degrés, d'où cet aspect étrange. Cette perte d'information est équivalente entre les deux types d'image.
Propriétés de la transformée de Fourier
FFT d'une image constante
Illustrons certaines de ces propriétés. Prenons d'abord simplement une image de couleur constante et obtenons sa magnitude.
magick -size 128x128 xc:gold constant.png
magick constant.png -fft +delete constant_magnitude.png
Notez que, dans ce cas, l'image de magnitude est réellement d'un noir pur, hormis un unique pixel coloré situé exactement au centre de l'image, à la position width/2, height/2. Ce pixel est la fréquence nulle, ou valeur DC (« Direct Current », courant continu), de l'image, et c'est le seul pixel qui ne représente pas une onde sinusoïdale. Autrement dit, cette valeur n'est autre que la composante constante de la FFT ! Pour mieux voir ce pixel unique, agrandissons également cette zone de l'image... |
magick constant_magnitude.png -gravity center -extent 5x5 \
-scale 2000% constant_dc_zoom.gif
![[IM Output]](../static/img/fourier/constant_dc_zoom.gif)
Notez que la couleur du point DC est identique à celle de l'image d'origine. Il est d'ailleurs bon de se rappeler que ce que vous voyez correspond à trois valeurs. En effet, l'image générée est en réalité constituée de trois transformées de Fourier rapides distinctes : une FFT pour chacun des canaux rouge, vert et bleu de l'image. La FFT elle-même n'a aucune connaissance réelle des couleurs, seulement des valeurs de couleur ou « niveaux de gris ». En fait, une transformée FFT pourrait s'appliquer à peu près à n'importe quel espace colorimétrique, car en réalité... cela lui est indifférent ! Pour une transformée de Fourier, une image n'est qu'un tableau de valeurs, rien de plus. | Bien que la « phase » de la valeur DC n'ait pas d'importance, elle devrait toujours correspondre à un angle « nul » (une valeur de couleur de phase d'un gris à 50 %). Si elle n'est pas réglée sur un gris à 50 %, la valeur DC aura une composante « irréelle » et sa valeur sera modulée par l'angle indiqué.
---|---
Effets de la couleur DC
Dans une image non constante plus typique, la valeur DC est la couleur moyenne de l'image. C'est la couleur que vous obtiendriez généralement en floutant complètement, en moyennant ou en réduisant l'image à un seul pixel ou à une seule couleur. Par exemple, extrayons le pixel DC de la FFT de l'image « Lena ».
magick lena.png -fft +delete lena_magnitude.png
magick lena_magnitude.png -gravity center -extent 1x1 \
-scale 60x60 lena_dc_zoom.gif
Comme vous pouvez le constater, la couleur moyenne de l'image est une sorte de « rose sombre ». Une autre façon de concevoir ce pixel particulier est qu'il représente le niveau de « biais » central autour duquel toutes les autres ondes sinusoïdales modifient les couleurs de l'image. Par exemple, remplaçons ce pixel DC « rose sombre » par une autre couleur, comme le « tomato » plus orangé... |
magick lena.png -fft \
\( -clone 0 -draw "fill tomato color 64,64 point" \) \
-swap 0 +delete -ift lena_dc_replace.png
![[IM Output]](../static/img/fourier/lena_dc_replace.png)
Ce qui se passe réellement, c'est qu'en modifiant la valeur DC dans les images FFT, vous modifiez de la même manière l'ensemble de l'image. En fait, tout changement de la valeur DC (l'écart) sera ajouté (ou soustrait) à chacun des pixels de l'image obtenue. C'est exactement comme si l'on ajoutait réellement une constante à chaque pixel de l'image d'origine. Les couleurs finales des pixels de l'image reconstruite pourraient donc aussi être écrêtées par les limites maximale (blanc) ou minimale (noir). Ce n'est pour cette raison pas une méthode recommandée pour teinter une image en couleur. Elle est plus simple à appliquer que la modification de chaque pixel de l'image entière, mais l'aller-retour FFT en fait globalement une technique de teinte bien plus lente.
Spectre d'une image d'onde sinusoïdale
Examinons ensuite le spectre d'une image d'onde sinusoïdale (ou cosinusoïdale) unique comportant 4 cycles sur la largeur de l'image.
magick -size 128x129 gradient: -chop 0x1 -rotate 90 -evaluate sine 4 \
sine4.png
magick sine4.png -fft +delete \
-auto-level -evaluate log 100 sine4_spectrum.png
| _La création inhabituelle de l'image de dégradé ci-dessus est nécessaire pour garantir que l'image d'onde sinusoïdale obtenue se juxtapose parfaitement en mosaïque sur toute l'image.
Une image « [gradient:](canvas.html#gradient) » normale ne se juxtapose pas parfaitement en mosaïque ; il en va donc de même pour une onde sinusoïdale qui en est issue. La transformée FFT d'une telle mosaïque imparfaite produira un ensemble d'harmoniques indésirables, plutôt que des « points » uniques dans le spectre de la transformée de Fourier.
Voir Générer le dégradé parfait pour plus de détails sur ce problème.
_
---|---
Dans l'image de spectre (image de magnitude rehaussée) ci-dessus, on voit qu'elle comporte 3 points. Le point central est, comme précédemment, la valeur DC moyenne. Les deux autres points représentent l'onde sinusoïdale parfaite que l'opérateur de Fourier a trouvée dans l'image. Comme la fréquence sur la largeur de l'image est d'exactement 4 cycles, deux pixels de fréquence se trouvent en conséquence à exactement 4 pixels du point DC central. Mais pourquoi deux pixels ? Parce qu'une onde sinusoïdale unique peut être décrite de deux façons complètement différentes (l'une avec une direction et une phase négatives). Ces deux descriptions sont mathématiquement correctes, et une transformée de Fourier ne les distingue pas. Si l'on recommence avec une onde sinusoïdale de 16 cycles, on voit à nouveau 3 points, mais plus éloignés les uns des autres. Dans ce cas, les points latéraux sont situés à 16 pixels à gauche et à droite du point central.
magick -size 128x129 gradient: -chop 0x1 -rotate 90 -evaluate sine 16 \
-write sine16.png -fft -delete 1 \
-auto-level -evaluate log 100 sine16_spectrum.png
On voit ainsi que les ondes sinusoïdales parfaites seront simplement représentées par deux points à la position appropriée. La distance de cette position au point DC central détermine la fréquence de l'onde sinusoïdale. Plus la longueur d'onde est courte, plus la fréquence est élevée, et donc plus les points seront éloignés de la valeur DC. En fait, en divisant la taille de l'image par la fréquence (la distance des points au centre), on obtient la longueur d'onde (la distance entre les crêtes) de l'onde. Dans le cas ci-dessus : 128 pixels divisés par 16 cycles donnent une longueur d'onde de 8 pixels entre chaque « bande ». C'est l'une des caractéristiques distinctives les plus importantes de la transformation FFT. Un motif de petits détails sur l'image d'origine requiert de petites longueurs d'onde, donc de grandes fréquences. Cela se traduit par des effets à grande échelle dans le domaine fréquentiel. De même, les grands détails utilisent de plus petites fréquences et engendrent donc des motifs à petite échelle, en particulier près du centre. Dans une transformée de Fourier...
Le petit devient grand et le grand devient petit.
C'est l'un des aspects les plus essentiels à retenir lorsqu'on manipule des transformées de Fourier, car c'est la clé pour retirer le bruit (les petits détails) d'une image tout en préservant ses aspects globaux plus grands. Examinons de plus près ces trois « fréquences » en traçant leurs magnitudes d'origine (et non le spectre logarithmique). |
magick sine16.png -fft -delete 1 miff:- |\
im_profile - sine16_magnitude_pf.png
![[IM Output]](../static/img/fourier/sine16_magnitude_pf.png)
Notez que la valeur DC (moyenne ou biais de l'image) a une valeur de 1/2, ce qui est attendu (la valeur moyenne de l'image est un gris parfait à 50 %), mais que la magnitude réelle des deux ondes sinusoïdales de 16 cycles trouvées par la transformée de Fourier n'est que de 1/4 de la valeur maximale. La magnitude de l'onde sinusoïdale d'origine est en réalité de 1/2, mais la transformée de Fourier a divisé cette magnitude en deux, répartissant le résultat entre les deux ondes de fréquence tracées, de sorte que chacune des deux composantes n'a qu'une magnitude de 1/4. C'est une part normale des transformées de Fourier.
Cette dualité des fréquences positives et négatives dans les images FFT explique pourquoi tous les spectres d'images FFT (comme le spectre de Lena repris à gauche) sont toujours symétriques par rapport au centre. Pour chaque point d'un côté de l'image, on obtiendra toujours un « point » similaire en miroir par rotation autour du centre de l'image. La même chose se produit pour la composante « phase » de la paire d'images FFT, mais avec en plus un décalage de 180 degrés (une phase négative). Cela signifie que la moitié de chaque image est en réalité un double de l'autre moitié, mais que vous avez besoin des DEUX images pour recréer l'image d'origine. Autrement dit, les deux images contiennent toujours exactement la même quantité d'information : une moitié dans l'une, une moitié dans l'autre. Ensemble, elles forment un tout. | _Lors de la génération, l'algorithme FFT ne génère que la moitié gauche des images. L'autre moitié est produite par rotation et duplication des données générées.
Lors de la reconversion des images du domaine fréquentiel en une image du domaine spatial, l'algorithme ne regarde là encore que la moitié gauche de l'image. La moitié droite est complètement ignorée, car ce n'est qu'un double.
Ainsi, lorsque (dans les exemples ultérieurs) vous appliquerez un « filtre coupe-bande » à une image de magnitude FFT, il vous suffira en réalité de filtrer la moitié gauche de l'image de magnitude. Vous pouvez vous épargner du travail en ignorant également la moitié droite. Par souci de clarté, je « filtrerai » toutefois les deux moitiés.
Générer directement des images FFT
Nous pouvons maintenant utiliser les informations ci-dessus pour générer réellement une image d'onde sinusoïdale. Il suffit de créer une paire d'images — une noire et une grise à 50 % — et d'y ajouter des « points » ayant la magnitude et la phase appropriées. Par exemple...
magick -size 128x128 xc:black \
-draw 'fill gray(50%) color 64,64 point' \
-draw 'fill gray(50%) color 50,68 point' \
-draw 'fill gray(25%) color 78,60 point' \
generated_magnitude.png
magick generated_magnitude.png \
-auto-level -evaluate log 3 generated_spectrum.png
magick -size 128x128 xc:gray50 generated_phase.png
magick generated_magnitude.png generated_phase.png \
-ift generated_wave.png
| Et voilà, une onde sinusoïdale parfaite, inclinée (et juxtaposable en mosaïque). Bien sûr, vous ne pouvez générer des ondes sinusoïdales parfaites qu'à certaines fréquences, et elles ne sont juxtaposables que dans des images carrées (à moins de les redimensionner ensuite). Malheureusement, toutes les fréquences seront aussi des puissances de deux dans chaque direction horizontale ou verticale, ce qui constitue la principale limitation de cette technique. | En réalité, seul le premier point « gray25 » (le plus à gauche) était nécessaire pour générer l'onde sinusoïdale, car la transformée IFT ignore complètement la moitié droite de l'image, qui devrait simplement être le miroir par rotation de la moitié gauche. |
|---|---|
| La phase de la valeur DC doit avoir un « angle nul » (couleur d'un gris à 50 %). Si vous ne le garantissez pas, la valeur de couleur DC sera modulée par sa phase non nulle, produisant une image plus sombre, voire « écrêtée ». | |
| --- | --- |
| _Les autres pixels de la phase peuvent avoir n'importe quel niveau de gris de votre choix, ce qui aura pour effet de « faire rouler » l'onde sinusoïdale sur l'image. Là encore, seule la phase du point le plus à gauche a réellement de l'importance. La moitié droite est complètement ignorée. Assurez-vous simplement que le pixel de phase DC central reste à un gris à 50 %. |
_
---|---
FUTURE: Perlin Noise Generator using FFT
Spectre d'une ligne verticale
Montrer le spectre FFT d'une ligne fine et d'une ligne épaisse Démontrer comment les petits détails deviennent « grands » et les grands détails « petits » dans la FFT de l'image. Relier cela à l'onde sinusoïdale, que l'on pourrait considérer comme une « ligne » à une seule harmonique. Faire pivoter la ligne
Spectre d'une image de motif rectangulaire
Examinons ensuite le spectre d'un rectangle blanc de largeur 8 et de hauteur 16 sur un fond noir.
magick -size 8x16 xc:white -gravity center \
-gravity center -background black -extent 128x128 rectangle.png
magick rectangle.png -fft +delete \
-auto-level -evaluate log 100 rect_spectrum.png
Comme vous pouvez le voir, l'image obtenue présente un motif très particulier, avec de nombreuses fréquences harmoniques. On remarque aussi que le rectangle semble avoir pivoté de 90 degrés. C'est faux : ce que vous observez, c'est la même règle que celle mentionnée précédemment... les grands détails deviennent petits et les petits détails deviennent grands. Ainsi, la plus petite dimension du rectangle est devenue plus grande, et la plus grande dimension est devenue plus petite. Faisons maintenant pivoter le rectangle de 45 degrés. On constate que le spectre pivote lui aussi de 45 degrés dans la même direction.
magick rectangle.png -rotate 45 -gravity center -extent 128x128 \
-write rect_rot45.png -fft -delete 1 \
-auto-level -evaluate log 100 rect_rot45_spectrum.png
Comme vous pouvez le voir, la même rotation se produit dans le domaine fréquentiel. Autrement dit, l'effet d'un objet ayant pivoté pivote lui aussi dans sa transformée de Fourier. Mais si nous déplaçons maintenant le rectangle...
magick rectangle.png -rotate 45 -geometry +30+20 -extent 128x128 \
-write rect_rot45off.png -fft -delete 1 \
-auto-level -evaluate log 100 rect_rot45off_spectrum.png
Le motif fréquentiel ne s'est pas déplacé. C'est parce que toute l'information de position est contenue dans l'image de phase. Le motif fréquentiel (la magnitude ou son spectre) ne change pas parce que l'objet s'est déplacé. Cette séparation de la position est l'une des caractéristiques clés de la transformée de Fourier qui la rendent si importante. Elle vous permet de rechercher un motif d'image particulier au sein d'une image plus grande, indépendamment de l'emplacement de l'objet ayant produit ce motif de spectre de Fourier.
Spectre d'une image de motif circulaire plat
Examinons ensuite le spectre d'une image comportant un motif circulaire plat et blanc : dans un cas d'un diamètre de 12 (rayon 6), et dans l'autre d'un diamètre de 24 (rayon 12).
magick -size 128x128 xc:black -fill white \
-draw "circle 64,64 64,70" -write circle6.png -fft -delete 1 \
-auto-level -evaluate log 100 circle6_spectrum.png
magick -size 128x128 xc:black -fill white \
-draw "circle 64,64 64,76" -write circle12.png -fft -delete 1 \
-auto-level -evaluate log 100 circle12_spectrum.png
Notez que la première image est très proche de ce que nous avons généré pour l'exemple de la fonction jinc plus haut. Elle est cependant un peu morcelée. Ces artefacts apparaissent en raison de la petite taille du cercle. Comme il est représenté numériquement, son périmètre n'est pas parfaitement circulaire. Là encore, on voit que les petits détails deviennent grands dans l'espace fréquentiel transformé. La transformée du cercle plus grand est meilleure, car son périmètre est une approximation plus proche d'un vrai cercle. Nous en concluons donc que la transformée d'une forme circulaire plate est bien une fonction jinc, et que l'image contenant le cercle de plus petit diamètre produit des caractéristiques de transformée plus étalées et plus larges. D'après les propriétés mathématiques d'une transformée de Fourier, la distance du centre au milieu du premier anneau sombre du spectre vaut 1.22N/d. Lorsque le diamètre du cercle est d=12, on obtient une distance de 1.22128/12=13. De même, lorsque le diamètre du cercle est d=24, on obtient une distance de 1.22*128/24=6.5.
Spectre d'une image de motif gaussien
Examinons ensuite le spectre de deux images, comportant chacune un motif circulaire gaussien blanc, de sigma respectivement égal à 8 et à 16.
magick -size 128x128 xc:black -fill white \
-draw "point 64,64" -gaussian-blur 0x8 -auto-level \
-write gaus8.png -fft -delete 1 \
-auto-level -evaluate log 1000 gaus8_spectrum.png
im_profile -s gaus8.png gaus8_pf.gif
im_profile -s gaus8_spectrum.png gaus8_spectrum_pf.gif
magick -size 128x128 xc:black -fill white \
-draw "point 64,64" -gaussian-blur 0x16 -auto-level \
-write gaus16.png -fft -delete 1 \
-auto-level -evaluate log 1000 gaus16_spectrum.png
im_profile -s gaus16.png gaus16_pf.gif
im_profile -s gaus16_spectrum.png gaus16_spectrum_pf.gif
Hormis le bruit produit par la disposition rectangulaire du motif, le résultat est qu'un motif gaussien a produit un motif fréquentiel gaussien presque identique. Plus important encore, ce motif était d'aspect assez net. Il y a bien sûr une différence de taille, suivant là encore la même règle : le grand devient petit et le petit devient grand. D'après les propriétés mathématiques, le sigma dans le spectre vaut simplement N/(2*sigma), où sigma est celui de l'image d'origine. Ainsi, pour une image de taille N=128 et de sigma=8, le sigma dans le spectre vaut 128/16=8. De même, si le sigma de l'image est 16, le sigma dans le spectre vaut 128/32=4. C'est la relation mathématique traduisant la règle « le grand devient petit et vice versa », et il peut être utile de la connaître.
Spectre d'une image de motif en grille
Transformons ensuite une image ne contenant qu'un ensemble de lignes de grille espacées de 16x8 pixels.
magick -size 16x8 xc:white -fill black \
-draw "line 0,0 15,0" -draw "line 0,0 0,7" \
-write mpr:tile +delete \
-size 128x128 tile:mpr:tile \
-write grid16x8.png -fft -delete 1 \
-auto-level -evaluate log 100000 grid16x8_spectrum.png
Le spectre obtenu n'est qu'un tableau de points, où les lignes de grille les plus rapprochées produisent des points plus éloignés, et inversement. D'après les propriétés ci-dessus, puisque les lignes de grille sont espacées de 16x8 pixels, les points devraient être espacés de N/a=128/16=8 et M/b=128/8=16, ce qui correspond exactement à ce que l'on mesure sur cette image. Ce motif revêt une importance particulière, car il vous renseigne sur la relation entre la transformée de Fourier et un motif régulier de pavage dans une image. Un tel motif de pavage produit des motifs en grille non centraux très marqués dans sa transformée de Fourier. Le point essentiel ici est que l'information de forme se trouve au centre, tandis que l'information de pavage se trouve dans un tableau en grille, à l'écart du centre de la transformée de Fourier.
Plus d'informations sur les spectres
Voici quelques liens si vous souhaitez en savoir plus sur les images de spectre et leurs propriétés.
- Wikipedia: Fourier Transform
- Fred Weinhaus, Properities of a Fourier Transform
- Wolfram MathWorld: Fourier Transform
Applications pratiques
Bien, maintenant que nous avons couvert les bases, quelles sont les applications pratiques de la transformée de Fourier ? Parmi les traitements réalisables figurent : 1) augmenter ou diminuer le contraste d'une image, 2) le flou, 3) l'accentuation, 4) la détection de contours et 5) la suppression du bruit.
Modifier le contraste d'une image - extraction de racine des coefficients
On peut ajuster le contraste d'une image en effectuant la transformée de Fourier directe, en élevant l'image de magnitude à une puissance, puis en l'utilisant avec la phase dans la transformée de Fourier inverse. Pour augmenter le contraste, on utilise un exposant légèrement inférieur à un ; pour le diminuer, un exposant légèrement supérieur à un. Commençons donc par augmenter le contraste de l'image Lena avec un exposant de 0.9, puis diminuons-le avec un exposant de 1.1.
magick lena.png -fft \
\( -clone 0 -evaluate pow 0.9 \) -delete 0 \
+swap -ift lena_plus_contrast.png
magick lena.png -fft \
\( -clone 0 -evaluate pow 1.1 \) -delete 0 \
+swap -ift lena_minus_contrast.png
Cependant, faire cela reviendrait au même que d'appliquer ce traitement à l'image d'origine. Autrement dit, une modification globale des magnitudes a le même effet qu'une modification globale de l'image d'origine.
Flouter une image - filtrage passe-bas
L'une des propriétés les plus importantes des transformées de Fourier est que la convolution dans le domaine spatial équivaut à une simple multiplication dans le domaine fréquentiel. Dans le domaine spatial, on utilise de petits filtres de convolution (noyaux) simples et carrés pour flouter une image avec l'option -convole. C'est ce qu'on appelle un filtre passe-bas. Le filtre le plus simple n'est autre qu'un tableau carré à pondération uniforme. Autrement dit, toutes les valeurs valent un, et sont normalisées en les divisant par leur somme avant d'appliquer la convolution. Cela équivaut à une moyenne locale ou de voisinage. Un autre filtre passe-bas est le filtre de forme circulaire à pondération gaussienne fourni par -gaussian-blur ou -blur. Dans le domaine fréquentiel, un type de filtre de flou passe-bas n'est autre qu'un cercle blanc d'intensité constante entouré de noir. Cela s'apparenterait à un filtre de convolution moyenneur de forme circulaire dans le domaine spatial. Cependant, comme la convolution dans le domaine spatial équivaut à la multiplication dans le domaine fréquentiel, il nous suffit d'effectuer une transformée de Fourier directe, de multiplier le filtre par l'image de magnitude, puis d'effectuer la transformée de Fourier inverse. Notons qu'un filtre de convolution de petite taille correspondra à un grand cercle dans le domaine fréquentiel. La multiplication s'effectue via -composite avec un réglage -compose multiply. Essayons donc avec deux tailles de filtres circulaires, l'un de diamètre 40 (rayon 20) et l'autre de diamètre 28 (rayon 14).
magick -size 128x128 xc:black -fill white \
-draw "circle 64,64 44,64" circle_r20.png
magick lena.png -fft \
\( -clone 0 circle_r20.png -compose multiply -composite \) \
\( +clone -evaluate log 10000 -write lena_blur_r20_spec.png +delete \) \
-swap 0 +delete -ift lena_blur_r20.png
magick -size 128x128 xc:black -fill white \
-draw "circle 64,64 50,64" circle_r14.png
magick lena.png -fft \
\( -clone 0 circle_r14.png -compose multiply -composite \) \
\( +clone -evaluate log 10000 -write lena_blur_r14_spec.png +delete \) \
-swap 0 +delete -ift lena_blur_r14.png
On voit donc que l'image ayant utilisé le filtre de plus petit diamètre a produit davantage de flou. On remarque aussi l'effet de « ringing » (rebond) ou d'« ondulation » près des contours dans les images obtenues. Cela se produit parce que la transformée de Fourier d'un cercle est, comme nous l'avons vu plus haut, une fonction jinc, dont les oscillations décroissent à mesure qu'elle progresse vers l'extérieur depuis le centre. Ici toutefois, la fonction jinc et les oscillations se trouvent dans le domaine spatial plutôt que dans le domaine fréquentiel, comme nous l'avions montré plus haut. Que peut-on donc y faire ? Le plus simple est d'adoucir les bords des cercles à l'aide de diverses fonctions de fenêtrage. On peut aussi utiliser un filtre tel qu'une forme gaussienne, déjà adoucie par définition. Faisons donc ce dernier choix et utilisons deux cercles floutés par une gaussienne pour supprimer la majeure partie des sévères effets de « ringing ».
magick circle_r20.png -blur 0x4 -auto-level gaussian_r20.png
magick lena.png -fft \
\( -clone 0 gaussian_r20.png -compose multiply -composite \) \
\( +clone -evaluate log 10000 -write lena_gblur_r20_spec.png +delete \) \
-swap 0 +delete -ift lena_gblur_r20.png
magick circle_r14.png -blur 0x4 -auto-level gaussian_r14.png
magick lena.png -fft \
\( -clone 0 gaussian_r14.png -compose multiply -composite \) \
\( +clone -evaluate log 10000 -write lena_gblur_r14_spec.png +delete \) \
-swap 0 +delete -ift lena_gblur_r14.png
C'est bien sûr bien mieux. Le filtre passe-bas idéal consiste non pas à flouter des cercles, mais à utiliser une véritable courbe gaussienne définie par un sigma plutôt que par un rayon. Bien sûr, dans cet exemple, nous avons fini par faire un flou pour faire un flou ! Mais le motif de flou multiplié à l'image de magnitude FFT est fixe et pourrait en fait être récupéré depuis un cache pré-généré. De plus, l'image multiplicatrice n'a pas besoin d'avoir la taille complète de l'image d'origine ; vous pouvez utiliser une image plus petite. Ce qui précède peut donc être bien plus rapide pour de grandes images, et lorsqu'on traite de nombreuses images. Le point le plus important est que, pour de grands flous intenses, l'image du domaine fréquentiel est petite et n'effectue qu'une seule multiplication, au lieu de devoir moyenner de nombreux pixels pour chacun des pixels de l'image d'origine. Pour des flous de petite taille, la convolution directe vous conviendra peut-être mieux.
Détecter les contours d'une image - filtrage passe-haut
Dans le domaine spatial, les filtres passe-haut qui extraient les contours d'une image sont souvent implémentés sous forme de convolutions à poids positifs et négatifs dont la somme est nulle. Les choses sont bien plus simples dans le domaine fréquentiel. Ici, un filtre passe-haut n'est autre que la version inversée du filtre passe-bas. Autrement dit, là où le filtre passe-bas est clair, le filtre passe-haut est sombre, et inversement. Dans ImageMagick, il nous suffit donc d'appliquer -negate à l'image du filtre passe-bas. Appliquons donc des filtres passe-haut à l'image Lena à l'aide d'une image de cercle. Puis à nouveau à l'aide d'une courbe purement gaussienne.
magick circle_r14.png -negate circle_r14i.png
magick lena.png -fft \
\( -clone 0 circle_r14i.png -compose multiply -composite \) \
\( +clone -evaluate log 10000 -write lena_edge_r14_spec.png +delete \) \
-delete 0 +swap -ift -normalize lena_edge_r14.png
magick -size 128x128 xc: -draw "point 64,64" -blur 0x14 \
-auto-level gaussian_s14i.png
magick lena.png -fft \
\( -clone 0 gaussian_s14i.png -compose multiply -composite \) \
\( +clone -evaluate log 10000 -write lena_edge_s14_spec.png +delete \) \
-delete 0 +swap -ift -normalize lena_edge_s14.png
En examinant attentivement ces deux résultats, on constate que le simple cercle n'est pas tout à fait aussi bon que la gaussienne, car il présente des artefacts de « ringing » et n'est pas tout à fait aussi net.
Accentuer une image - filtrage à renforcement des hautes fréquences
La façon la plus simple d'accentuer une image est de lui appliquer un filtre passe-haut (sans l'étirement de normalisation), puis de la fondre avec l'image d'origine.
magick lena.png -fft \
\( -size 128x128 xc: -draw "point 64,64" -blur 0x14 -auto-level \
-clone 0 -compose multiply -composite \) \
-delete 0 +swap -ift \
lena.png -compose blend -set option:compose:args 100x100 -composite \
lena_sharp14.png
Ici, un filtre passe-haut est appliqué dans le domaine fréquentiel, et le résultat est retransformé vers le domaine spatial où il est fondu avec l'image d'origine, afin d'en renforcer les contours.
Suppression du bruit - filtrage coupe-bande
De nombreuses images bruitées contiennent une forme de bruit à motif. Ce type de bruit est facile à supprimer dans le domaine fréquentiel, car les motifs y apparaissent soit comme un motif de quelques points, soit comme des lignes. Rappelez-vous qu'une simple onde sinusoïdale est un motif répété et n'apparaît que sous la forme de 3 points dans le spectre. Pour supprimer ce bruit, il faut malheureusement masquer (ou « notcher ») à la main les points ou les lignes dans l'image de magnitude. On procède en transformant vers le domaine fréquentiel, en créant une version en niveaux de gris du spectre, en masquant les points ou les lignes, en appliquant un seuil, en multipliant l'image de masque binaire par l'image de magnitude, puis en retransformant vers le domaine spatial. Essayons sur l'image du clown, qui contient un motif diagonal rayé rappelant un tramage. Transformons d'abord l'image du clown pour créer ses images de magnitude et de phase.
magick clown_orig.jpg -fft \
\( +clone -write clown_phase.png +delete \) +delete \
-write clown_magnitude.png -colorspace gray \
-auto-level -evaluate log 100000 clown_spectrum.png
![[IM Output]](../static/img/fourier/clown_orig.jpg)
Original | | ![[IM Output]](../static/img/fourier/clown_spectrum.png)
Spectre | ![[IM Output]](../static/img/fourier/clown_phase.png)
Phase
---|---|---|---
On voit que le spectre contient quatre points brillants en forme d'étoile, un dans chaque quadrant. Ces points inhabituels représentent le motif de l'image dont nous voulons nous débarrasser. Le point et les lignes brillants au milieu de l'image ne posent aucun problème, car ils représentent le DC (la couleur moyenne de l'image) et les effets des bords de l'image, et ne doivent pas être modifiés. Notez que, lors de la génération de l'image de spectre, j'ai forcé l'image obtenue à être une image purement en niveaux de gris. Ainsi, je peux maintenant charger l'image dans un éditeur et, à l'aide d'une couleur non grise quelconque (comme le rouge), masquer la zone de ces 4 motifs en forme d'étoile. Une fois l'édition terminée, je peux extraire les zones que j'ai colorées en calculant une image de différence par rapport à la version non éditée. Comme ceci...
magick clown_spectrum_edited.png clown_spectrum.png \
-compose difference -composite \
-threshold 0 -negate clown_spectrum_mask.png
Il ne reste plus qu'à multiplier le masque par la magnitude et à utiliser le résultat avec l'image de phase d'origine pour retransformer vers le domaine spatial. Nous affichons l'image d'origine à côté pour comparaison.
magick clown_magnitude.png clown_spectrum_mask.png \
-compose multiply -composite \
clown_phase.png -ift clown_filtered.png
Un très bon résultat. Mais nous pouvons faire encore mieux. Comme vous l'avez vu dans les exemples précédents, les simples « cercles » ne sont pas particulièrement adaptés à une image FFT ; floutons donc légèrement le masque... |
magick clown_spectrum_mask.png \
-blur 0x5 -level 50x100% clown_mask_blurred.png
![[IM Output]](../static/img/fourier/clown_mask_blurred.png)
Puis filtrons le clown, en régénérant cette fois les images FFT en mémoire. |
magick clown_orig.jpg -fft \
\( -clone 0 clown_mask_blurred.png -compose multiply -composite \) \
-swap 0 +delete -ift clown_filtered_2.png
![[IM Output]](../static/img/fourier/clown_filtered_2.png)
Un résultat tout simplement stupéfiant ! Et que l'on pourrait sans doute améliorer encore en ajustant ce masque pour mieux épouser les formes en « étoile ». Nous pouvons même prendre la différence entre l'original et le résultat afin de créer une image des zones où le bruit a été supprimé. |
magick clown_orig.jpg clown_filtered_2.png -compose difference \
-composite -normalize clown_noise.png
![[IM Output]](../static/img/fourier/clown_noise.png)
Essayons sur un autre exemple. Cette fois sur une image « Twigs » trouvée sur le site RoboRealm, qui contient un motif irrégulier de rayures horizontales et verticales. Là encore, nous extrayons une image de spectre en niveaux de gris, exactement comme précédemment.
magick twigs.jpg -fft +delete -colorspace gray \
-auto-level -evaluate log 100000 twigs_spectrum.png
Dans ce cas, comme le bruit de l'image est orienté horizontalement et verticalement, il apparaît sous forme de bandes horizontales et verticales épaisses le long des lignes centrales, mais pas au centre même de l'image. Là encore, nous masquons ces parties à l'aide d'un éditeur d'images, cette fois avec une couleur « bleue » (peu importe en réalité la couleur utilisée)...
magick twigs_spectrum_edited.png twigs_spectrum.png \
-compose difference -composite \
-threshold 0 -negate twigs_spectrum_mask.png
Nous multiplions ici de nouveau le masque par l'image de magnitude FFT, puis reconstruisons l'image.
magick twigs.jpg -fft \
\( -clone 0 twigs_spectrum_mask.png -compose multiply -composite \) \
-swap 0 +delete -ift twigs_filtered.png
Et nous pouvons prendre la différence entre l'original et le résultat afin de créer une image des zones où le bruit a été supprimé.
magick twigs.jpg twigs_filtered.png -compose difference -composite \
-normalize twigs_noise.png
Ajouter un léger flou au masque pourrait encore améliorer les résultats. À titre d'exercice, essayez de retirer la ficelle de l'image. Comme indice, rappelez-vous que l'effet d'une ligne dans une image réelle est pivoté de 90 degrés dans la FFT. Si vous vous trompez, vous retirerez probablement la brindille à la place.
Applications avancées
Parmi les autres applications plus avancées de la transformée de Fourier figurent : 1) la déconvolution (défloutage) des images à flou de bougé et défocalisées, et 2) la corrélation croisée normalisée pour trouver l'endroit où une petite image correspond le mieux au sein d'une image plus grande. Les exemples de multiplication et de division FFT (déconvolution) ont été déplacés vers un sous-répertoire, car ils attendent des opérateurs de traitement d'image définis plus formellement.
![[IM Output]](../static/img/img_photos/holocaust_tn.gif)
![[IM Output]](../static/img/fourier/image_profile.gif)
![[IM Output]](../static/img/fourier/wave.gif)
![[IM Output]](../static/img/fourier/wave_profile.gif)
![[IM Output]](../static/img/fourier/wave_1_pf.gif)
![[IM Output]](../static/img/fourier/wave_2_pf.gif)
![[IM Output]](../static/img/fourier/wave_3_pf.gif)
![[IM Output]](../static/img/fourier/added_waves_pf.gif)
![[IM Output]](../static/img/fourier/lena_roundtrip.png)
![[IM Text]](../static/img/fourier/lena_roundtrip_cmp.txt.gif)
![[IM Output]](../static/img/fourier/lena_magnitude.png)
![[IM Output]](../static/img/fourier/lena_phase.png)
![[IM Output]](../static/img/fourier/lena_restored.png)
![[IM Output]](../static/img/fourier/lena_roundtrip_hdri.png)
![[IM Text]](../static/img/fourier/lena_roundtrip_hdri_cmp.txt.gif)
![[IM Output]](../static/img/fourier/lena_roundtrip_ri.png)
![[IM Text]](../static/img/fourier/lena_roundtrip_ri_cmp.txt.gif)
![[IM Output]](../static/img/fourier/constant.png)
![[IM Output]](../static/img/fourier/constant_magnitude.png)
![[IM Output]](../static/img/fourier/lena_dc_zoom.gif)
![[IM Output]](../static/img/fourier/sine4.png)
![[IM Output]](../static/img/fourier/sine4_spectrum.png)
![[IM Output]](../static/img/fourier/sine16.png)
![[IM Output]](../static/img/fourier/sine16_spectrum.png)
![[IM Output]](../static/img/fourier/generated_spectrum.png)
![[IM Output]](../static/img/fourier/generated_phase.png)
![[IM Output]](../static/img/fourier/generated_wave.png)
![[IM Output]](../static/img/fourier/rectangle.png)
![[IM Output]](../static/img/fourier/rect_spectrum.png)
![[IM Output]](../static/img/fourier/rect_rot45.png)
![[IM Output]](../static/img/fourier/rect_rot45_spectrum.png)
![[IM Output]](../static/img/fourier/rect_rot45off.png)
![[IM Output]](../static/img/fourier/rect_rot45off_spectrum.png)
![[IM Output]](../static/img/fourier/circle6.png)
![[IM Output]](../static/img/fourier/circle6_spectrum.png)
![[IM Output]](../static/img/fourier/circle12.png)
![[IM Output]](../static/img/fourier/circle12_spectrum.png)
![[IM Output]](../static/img/fourier/gaus8.png)
![[IM Output]](../static/img/fourier/gaus8_spectrum.png)
![[IM Output]](../static/img/fourier/gaus8_pf.gif)
![[IM Output]](../static/img/fourier/gaus8_spectrum_pf.gif)
![[IM Output]](../static/img/fourier/gaus16.png)
![[IM Output]](../static/img/fourier/gaus16_spectrum.png)
![[IM Output]](../static/img/fourier/gaus16_pf.gif)
![[IM Output]](../static/img/fourier/gaus16_spectrum_pf.gif)
![[IM Output]](../static/img/fourier/grid16x8.png)
![[IM Output]](../static/img/fourier/grid16x8_spectrum.png)
![[IM Output]](../static/img/fourier/lena_plus_contrast.png)
![[IM Output]](../static/img/fourier/lena_minus_contrast.png)
![[IM Output]](../static/img/fourier/circle_r20.png)
![[IM Output]](../static/img/fourier/lena_blur_r20_spec.png)
![[IM Output]](../static/img/fourier/lena_blur_r20.png)
![[IM Output]](../static/img/fourier/circle_r14.png)
![[IM Output]](../static/img/fourier/lena_blur_r14_spec.png)
![[IM Output]](../static/img/fourier/lena_blur_r14.png)
![[IM Output]](../static/img/fourier/gaussian_r20.png)
![[IM Output]](../static/img/fourier/lena_gblur_r20_spec.png)
![[IM Output]](../static/img/fourier/lena_gblur_r20.png)
![[IM Output]](../static/img/fourier/gaussian_r14.png)
![[IM Output]](../static/img/fourier/lena_gblur_r14_spec.png)
![[IM Output]](../static/img/fourier/lena_gblur_r14.png)
![[IM Output]](../static/img/fourier/circle_r14i.png)
![[IM Output]](../static/img/fourier/lena_edge_r14_spec.png)
![[IM Output]](../static/img/fourier/lena_edge_r14.png)
![[IM Output]](../static/img/fourier/gaussian_s14i.png)
![[IM Output]](../static/img/fourier/lena_edge_s14_spec.png)
![[IM Output]](../static/img/fourier/lena_edge_s14.png)
![[IM Output]](../static/img/fourier/lena_sharp14.png)
![[IM Output]](../static/img/fourier/clown_spectrum_edited.png)
![[IM Output]](../static/img/img_photos/clown_spectrum_mask.png)
![[IM Output]](../static/img/fourier/clown_filtered.png)
![[IM Output]](../static/img/fourier/twigs.jpg)
![[IM Output]](../static/img/fourier/twigs_spectrum.png)
![[IM Output]](../static/img/fourier/twigs_spectrum_edited.png)
![[IM Output]](../static/img/img_photos/twigs_spectrum_mask.png)
![[IM Output]](../static/img/fourier/twigs_filtered.png)
![[IM Output]](../static/img/fourier/twigs_noise.png)