Ejemplos de ImageMagick -- Transformadas de Fourier
- Las imágenes son ondas
- Imágenes de solo magnitud o solo fase
- Imagen del espectro FFT
- Imágenes FFT en HDRI
- Efectos del color DC
- Espectro de una imagen de onda sinusoidal
- Generar imágenes FFT directamente
- Espectro de una imagen de patrón rectangular
- Espectro de una imagen de patrón circular plano
- Espectro de una imagen de patrón circular gaussiano
-
Cambiar el contraste de una imagen - Radicación de coeficientes
- Desenfocar una imagen - Filtrado de paso bajo
- Detectar bordes en una imagen - Filtrado de paso alto
- Afilar una imagen - Filtrado de realce alto
-
Eliminación de ruido - Filtrado de muesca
Multiplicación y división FFT (ejemplos de bajo nivel - subpágina)
Introducción
Uno de los conceptos más difíciles de comprender en el procesamiento de imágenes son las transformadas de Fourier. Hay dos razones para ello. Primero, es matemáticamente avanzado y, segundo, las imágenes resultantes, que no se parecen a la imagen original, son difíciles de interpretar. No obstante, aprovechar las transformadas de Fourier puede ofrecer nuevas formas de realizar procesamientos familiares como mejorar el brillo y el contraste, desenfocar, afilar y eliminar ruido. Pero también puede aportar nuevas capacidades que no se pueden lograr en el dominio de la imagen normal. Entre ellas se incluyen la deconvolución (también conocida como eliminación de desenfoque) de distorsiones de cámara típicas como el desenfoque de movimiento y la desfocalización del objetivo, así como la coincidencia de imágenes mediante correlación cruzada normalizada. El objetivo de esta página es tratar de explicar los fundamentos y las matemáticas simplificadas de la transformada de Fourier y dar ejemplos del procesamiento que se puede hacer usando la transformada de Fourier. Si esto te resulta excesivo, puedes saltártelo y centrarte simplemente en las propiedades y los ejemplos, comenzando por FFT/IFT en ImageMagick. Para quienes estén interesados, otra exposición sencilla y agradable, que incluye analogías con la óptica, se encuentra en An Intuitive Explanation of Fourier Theory. Los apuntes de clase de la Escuela de Ingeniería de la Universidad de Vanderbilt también son muy informativos para quienes tengan una inclinación más matemática: 1 & 2 Dimensional Fourier Transforms y Frequency Filtering. Otras referencias matemáticas incluyen las páginas de Wikipedia sobre Fourier Transform, Discrete Fourier Transform y Fast Fourier Transform, así como Complex Numbers. Mi agradecimiento a Sean Burke por programar la demo original y al creador de ImageMagick por integrarla en ImageMagick. Ambos fueron esfuerzos heroicos. Muchos de los ejemplos usan una versión HDRI de ImageMagick que es necesaria para preservar la precisión de las imágenes transformadas. Se recomienda compilar una versión HDRI personal si quieres sacar el máximo partido a estas técnicas.
The Fourier Transform
Normalmente una imagen consiste en una matriz de «píxeles», cada uno de los cuales se define mediante un conjunto de valores: rojo, verde, azul y, a veces, también transparencia. Pero para nuestros propósitos aquí ignoraremos la transparencia. Así, cada uno de los «canales» rojo, verde y azul contiene un conjunto de valores de «intensidad» o «escala de grises». Esto se conoce como una imagen ráster «en el dominio espacial». No es más que una forma rebuscada de decir que la imagen se define por los «valores de intensidad» que tiene en cada «ubicación» o «posición en el espacio». Pero una imagen también puede representarse de otra manera, conocida como el «dominio de la frecuencia» de la imagen. En este dominio, cada canal de la imagen se representa en términos de ondas sinusoidales. En ese «dominio de la frecuencia», cada canal tiene valores de «amplitud» que se almacenan en ubicaciones basadas no en coordenadas «espaciales» X,Y, sino en «frecuencias» X,Y. Como se trata de una representación digital, las frecuencias son múltiplos de una frecuencia «mínima» o unitaria, y las coordenadas de los píxeles representan los índices o múltiplos enteros de esta frecuencia unitaria. Esto se deriva del principio de que «cualquier función que se comporte adecuadamente puede representarse mediante una superposición (combinación o suma) de ondas sinusoidales». En otras palabras, la representación en el «dominio de la frecuencia» no es más que otra forma de almacenar y reproducir la imagen del «dominio espacial». Pero ¿cómo puede representarse una imagen como una «onda»?
Las imágenes son ondas
Pues bien, si tomamos una sola fila o columna de píxeles de cualquier imagen y la representamos en una gráfica (generada con "gnuplot" usando el script "[im_profile](../static/img/scripts/im_profile)"), verás que se parece bastante a una onda.
magick holocaust_tn.gif -colorspace gray miff:- |\
im_profile -s - image_profile.gif
Si las fluctuaciones tuvieran un espaciado y una amplitud más regulares, obtendrías algo más parecido a un patrón de onda, como...
magick -size 20x150 gradient: -rotate 90 \
-function sinusoid 3.5,0,.4 wave.gif
im_profile -s wave.gif wave_profile.gif
Sin embargo, aunque este patrón de onda regular se parece vagamente al perfil de imagen mostrado arriba, es demasiado regular. No obstante, si sumaras más ondas entre sí, podrías crear un patrón aún más cercano al obtenido de la imagen.
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
Véase también Adding Biased Gradients para un ejemplo alternativo al anterior. Esta «superposición de ondas» (suma de ondas) se acerca mucho más, pero aún no coincide exactamente con el patrón de la imagen. No obstante, puedes continuar de esta manera, añadiendo más ondas y ajustándolas, de modo que la onda compuesta resultante se acerque cada vez más al perfil real de la imagen original. Finalmente, sumando suficientes ondas, podrás reproducir exactamente el perfil original de la imagen. Este fue el descubrimiento que hizo el matemático Joseph Fourier. Una interpretación moderna del mismo afirma que «cualquier función que se comporte adecuadamente puede representarse mediante una superposición de ondas sinusoidales». En otras palabras, sumando un número suficiente de ondas sinusoidales con la frecuencia y la amplitud justas, puedes reproducir cualquier patrón fluctuante. Por lo tanto, la representación en el «dominio de la frecuencia» no es más que otra forma de almacenar y reproducir la imagen del «dominio espacial». La «transformada de Fourier» es entonces el proceso de averiguar qué «ondas» componen una imagen, tal como se hizo en el ejemplo anterior.
Ondas bidimensionales en las imágenes
Lo anterior muestra un ejemplo de cómo se puede aproximar el perfil de una sola fila de una imagen con varias ondas sinusoidales. Sin embargo, las imágenes son bidimensionales y, por ello, las ondas usadas para representar una imagen en el «dominio de la frecuencia» también deben ser bidimensionales. A continuación se muestra un ejemplo de una de esas ondas bidimensionales. La onda tiene varios componentes. Ejemplo de imagen
Uso de FFT/IFT en ImageMagick
Notas de implementación
ImageMagick makes use of the FFTW, Discrete Fourier Transform Library which requires images to be converted to and from floating point values (complex numbers), and was first implemented in IM version 6.5.4-3. To make it work as people generally expect for images, any non-square image or one with an odd dimension will be padded (using Virtual Pixels to be square of the maximum width or height of the image. To allow for proper centering of the 'FFT origin' in the center of the image, it is also forced to have an even (multiple of 2) dimensions. The consequence of this is that after applying the Inverse Fourier Transform, the image will need to be cropped back to its original dimensions to remove the padding. As the Fourier Transform is composed of "Complex Numbers", the result of the transform cannot be visualized directly. Therefore, the complex transform is separated into two component images in one of two forms. ![[Diagram]](../static/img/img_diagrams/complex_number.jpg)
Número complejo
Real/Imaginario
Parte real e imaginaria
The normal mathematical and numerical representation of the "Complex Numbers", is a pair of floating point values consisting of 'Real' (a) and 'Imaginary' (b) components. Unfortunately these two numbers may contain negative values and thus do not form viewable images. As such this representation can not be using in a normal version of ImageMagick, which would clip such images (see example below of the resulting effects. However when using a HDRI version of ImageMagick you can still generate, use, and even save this representation of a Fourier Transformed image. They may not be useful or even viewable as images in their own right, but you can still apply many mathematical operations to them. To generate this representation we use the 'plus' form of the operators, "[+fft](https://usage.imagemagick.org/fourier/option_link.cgi#fft)" and "[+ift](https://usage.imagemagick.org/fourier/option_link.cgi#ift)", and will be looked at in detail below in FFT as Real-Imaginary Components. ![[Diagram]](../static/img/img_diagrams/polar_number.jpg)
Forma polar compleja
Magnitud/Fase
Magnitud y fase
Direct numerical representation of the "Complex Numbers" is not very useful for image work. But by plotting the values onto a 2-dimensional plane, you can then magick the value into a Polar Representation consisting of 'Magnitude' (r) and 'Phase' (θ) components. This form is very useful in image processing, especially the magnitude component, which essentially specifies all the frequencies that go to make up the image. The 'Magnitude' component only contains positive values, and is just directly mapped into image values. It has no fixed range of values, though except for the DC or zero frequency color, the values will generally be quite small. As a consequence of this the magnitude image generally will appear to be very dark (practically black). By scaling the magnitude and applying a log transform of its intensity values usually will be needed to bring out any visual detail. The resulting 'log-transformed' magnitude image is known as the image's 'spectrum'. However remember that it is the 'magnitude' image, and not the 'spectrum' image, that should be used for the inverse transform. The DC (Short for "Direct Current") or "Zero Frequency", color that appears at the central 'origin' of the image, will be the average color value for the whole image. Also as input images not contain 'imaginary' components, the DC phase value will also always have zero phase, producing a pure-gray color. The 'Phase' component however ranges from -π to +π. This is first biased into a 0 to 2π range, then scaled into actual image values ranging from 0 to QuantumRange (as determined by the Compile Time Memory Quality). As a consequence of this, a zero phase will have a pure-gray value (as appropriate for each channel), while a negated phase will be a pure-black ('0') value. Note that a pure-white ('_QuantumRange_') is almost but not quite the same thing. A Magnitude and Phase FFT representation of an image is generated using the normal FFT operators, "[+fft](https://usage.imagemagick.org/fourier/option_link.cgi#fft)" and "[+ift](https://usage.imagemagick.org/fourier/option_link.cgi#ift)". This will be looked at first in Generating FFT Images and its Inverse.
Generar imágenes FFT y su inversa
(Magnitud y fase)
Ahora, probemos simplemente un viaje de ida y vuelta de la transformada de Fourier en la imagen de Lena. Es decir, hacemos la transformada directa e inmediatamente aplicamos la transformada inversa para recuperar la imagen original. Luego compararemos los resultados para ver el nivel de calidad obtenido.
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:
El programa "[compare](basics.html#compare)" anterior devuelve una medida de cuánto difieren las dos imágenes. En este caso puedes ver que la diferencia general es muy pequeña, de aproximadamente 0.22%. Con una diferencia de valor pico en al menos un píxel de aproximadamente ("PAE", error absoluto pico) de apenas 1%. Puedes mejorar esto usando una versión HDRI de ImageMagick. (Véase FFT con HDRI más abajo). Echemos un vistazo más de cerca a las imágenes FFT que se generaron en el viaje de ida y vuelta anterior.
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)
Magnitud | ![[IM Output]](../static/img/fourier/lena_fft_1.png)
Fase
---|---|---|---
Como dijo John M. Brayer sobre las transformadas de Fourier... Generalmente no mostramos imágenes de FASE porque la mayoría de la gente que las ve sucumbe poco después a los alucinógenos o acaba en un monasterio tibetano. Observa que el operador "[-fft](https://usage.imagemagick.org/fourier/option_link.cgi#fft)" generó dos imágenes: la primera es el componente «magnitud» (sí, es casi todo negro con un único punto de color en el medio), mientras que la segunda, de aspecto casi aleatorio, contiene el componente «fase». Las imágenes PNG solo pueden almacenar una imagen por archivo, así que en realidad no hacían falta ni la "[+adjoin](https://usage.imagemagick.org/fourier/option_link.cgi#adjoin)" ni el '%d' del nombre de archivo de salida, ya que IM se encargaría de ello. No obstante, incluyo las opciones arriba por completitud, para dejar claro que estoy generando dos archivos de imagen separados, no uno. Véase Writing a Multi-Image Sequence para más detalles. Como se generan dos imágenes, la imagen de magnitud (la primera o imagen cero) se guarda en "lena_fft_0.png" y la imagen de fase (segunda imagen) en "lena_fft_1.png". | _Para evitar cualquier posibilidad de distorsiones al guardar imágenes FFT, lo mejor es no guardarlas en disco en absoluto, sino mantenerlas en memoria mientras procesas la imagen.
Si debes guardarlas, lo mejor es usar el formato de archivo de Magick "[MIFF](https://usage.imagemagick.org/files/#miff")" para preservar la imagen con su máxima calidad (profundidad de bits). Este formato también puede guardar varias imágenes en un mismo archivo. Para trabajos con scripts también puedes usar el detallado formato de píxeles enumerados "[TXT](files.html#txt)".
NO las guardes usando los formatos de imagen "[JPEG](formats.html#jpg)" ni "[GIF](formats.html#gif)".
Si debes guardar estas imágenes en archivos para verlas realmente, por ejemplo en un navegador web, usa el formato de imagen "[PNG](formats.html#png)" con un "[+depth](https://usage.imagemagick.org/fourier/option_link.cgi#depth)" restablecido al valor interno por defecto (como hacemos en estos ejemplos). Sin embargo, solo puede almacenar una imagen por archivo.
El formato de archivo "[TIFF](formats.html#tiff)" también puede usarse, aunque no es tan aceptable para los navegadores web, si bien permite varias imágenes por archivo.
_
---|---
La mejor manera de guardar imágenes intermedias en un solo archivo es usar el formato de archivo "[MIFF](https://usage.imagemagick.org/files/#miff")"...
magick lena.png -fft +depth lena_fft.miff
O puedes guardarlas en nombres de archivo completamente separados usando "[-write](https://usage.imagemagick.org/fourier/option_link.cgi#write)" (véase Writing Images)...
magick lena.png -fft +depth \
\( -clone 0 -write lena_magnitude.png +delete \) \
\( -clone 1 -write lena_phase.png +delete \) \
null:
Observa que arriba usé el formato de imagen especial "[NULL:](files.html#null)" para descartar las dos imágenes, que de todos modos siguen conservadas en memoria para su procesamiento posterior. Y finalmente leemos de nuevo las dos imágenes para volver a convertirlas en una imagen «espacial» normal...
magick lena_magnitude.png lena_phase.png -ift lena_restored.png
Ambas imágenes generadas por el proceso FFT son muy sensibles a las modificaciones, donde incluso cambios pequeños pueden producir resultados muy distorsionados. Por ello es importante no guardarlas nunca en ningún formato de imagen que pudiera distorsionar esos valores. Es importante recordar que ambas imágenes son necesarias al recuperar la imagen desde el dominio de la frecuencia. Así que de nada sirve guardar una imagen y descartar la otra si piensas usarlas para reconstruir la imagen.
Imágenes de solo magnitud o solo fase
Por último, intentemos reconstruir una imagen a partir de solo su componente de magnitud o solo su componente de fase.
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)
Solo magnitud | ![[IM Output]](../static/img/fourier/lena_phase_only.png)
Solo fase
---|---
De esto notarás que es la imagen de fase la que en realidad contiene la mayor parte de la información de posición de la imagen, mientras que la magnitud retiene en realidad gran parte de la información de color. Esto no es exacto, ya que hay cierto solapamiento en la información, pero generalmente es así. La imagen de «solo magnitud» siempre tendrá las esquinas blancas, porque se usó una imagen de fase constante al 50%. Puedes eliminar esos parches blancos usando una imagen de fase aleatorizada. No obstante, asegúrate de que la fase del píxel central sea un gris perfecto al 50%, o toda la imagen se oscurecerá. La imagen de «solo fase» usó una imagen de magnitud de gris constante al 1% (casi negro puro) para la conversión. Incluso con esta magnitud constante, sigue produciendo parches de píxeles muy intensos, especialmente a lo largo de los bordes. Solo necesitas recordar que ambas imágenes son necesarias para reconstruir la imagen original.
Imagen del espectro de frecuencia
Habrás notado que la imagen de magnitud (la primera o imagen cero) parece casi totalmente negra. En realidad no lo es, pero para nuestros ojos todos los valores son muy muy pequeños. Una imagen así no resulta muy interesante de observar para estudiarla, así que realcemos el resultado con una transformación logarítmica para producir una imagen de «espectro de frecuencia». Esto se hace aplicando una fuerte Evaluate Log Transform a una imagen de «magnitud» normalizada.
magick lena_fft_0.png -auto-level -evaluate log 10000 \
lena_spectrum.png
Ahora podemos ver el detalle en la versión de espectro de la imagen de magnitud. Puede que incluso veas algunos colores específicos en la imagen del espectro, pero generalmente esos colores carecen de importancia en una imagen de espectro. Lo que importa mucho más es la intensidad global de cada frecuencia y los patrones que producen. Por ello quizá también prefieras convertir la imagen del espectro a escala de grises tras el realce. Cuánto realce logarítmico necesitas usar depende de la imagen, así que deberías ajustarlo hasta obtener la cantidad de detalle que necesites para ver con claridad el patrón del espectro de frecuencia de la imagen. Como alternativa, puedes usar el siguiente pequeño script de shell para calcular un factor de escala logarítmico a usar para la imagen de magnitud concreta. |
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)
Sin embargo, recuerda que no puedes usar una imagen de espectro para la transformada inversa "[-ift](https://usage.imagemagick.org/fourier/option_link.cgi#ift)", ya que producirá una imagen demasiado brillante. |
magick lena_spectrum.png lena_fft_1.png -ift lena_roundtrip_fail.png
Básicamente, al haber realzado la imagen de «magnitud», también has realzado la imagen resultante de la misma manera, produciendo el resultado muy «recortado» que se muestra. ![[IM Output]](../static/img/fourier/lena_roundtrip_fail.png)
Imágenes FFT en HDRI
Cuando asignamos los resultados de la transformada de Fourier a una representación de imagen, escalamos y convertimos los valores de «números complejos» de punto flotante a valores enteros de imagen. Esto produjo de forma natural Rounding Errors, and other "Quantum" Effects, especialmente en las magnitudes más pequeñas de baja frecuencia. Si la precisión es importante en tu procesamiento de imágenes, entonces necesitarás usar una Bit Quality (como las versiones de ImageMagick de Q32 o Q64 bits) o, mejor aún, usar una versión HDRI de ImageMagick para que los valores se almacenen como números de punto flotante. Al usar una versión HDRI de IM con una representación de magnitud y fase de la transformada de Fourier, el componente de magnitud seguirá teniendo todos valores positivos y, por tanto, aún puede usarse como se mostró arriba, solo que con mucha más exactitud. El componente de fase, sin embargo, seguirá estando sesgado y escalado, como se mostró anteriormente. En otras palabras, la representación de magnitud y fase en HDRI es exactamente la misma, solo que mucho más precisa.
Por ejemplo, aquí uso una versión HDRI de ImageMagick para generar otra conversión de «ida y vuelta» de una imagen. |
# 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 comparas los resultados anteriores con la comparación previa sin HDRI...
| Verás que la versión HDRI de IM produjo un resultado mucho más preciso, aproximadamente a la misma velocidad que antes (la velocidad puede variar según tu ordenador). Aunque habrá requerido mucha más memoria que un IM Q16 normal (véase Compile-Time Quality). No obstante, tales imágenes, aunque representan con mayor exactitud los componentes de frecuencia de la FFT de la imagen, pueden contener valores negativos y fraccionarios y solo pueden guardarse usando HDRI capable File Formats especiales que admitan valores de punto flotante. | Entre los formatos de archivo compatibles con punto flotante están "[MIFF](https://usage.imagemagick.org/files/#miff")", "[TIFF](formats.html#tiff)", "[PFM](formats.html#netpbm)" y el formato de archivo «EXR» específico de HDRI. Sin embargo, puede que necesites establecer "-define quantum:format=floating-point" para que funcione. |
|---|---|
| En ejemplos posteriores, procesar la FFT de una imagen necesitará esa precisión para producir buenos resultados. Por ello, a medida que avancemos en el uso de las transformadas rápidas de Fourier, una versión HDRI de ImageMagick se convertirá en un requisito. |
FFT como componentes real-imaginario
Hasta ahora solo hemos visto una representación de «magnitud» y «fase» de las imágenes transformadas por Fourier. Pero si has compilado una versión HDRI de IM, también puedes procesar imágenes usando componentes «real» e «imaginario» de punto flotante. Esto se hace usando las versiones «plus» de las opciones "[+fft](https://usage.imagemagick.org/fourier/option_link.cgi#fft)" y "[+ift](https://usage.imagemagick.org/fourier/option_link.cgi#ift)". Por ejemplo, aquí usé una versión HDRI de IM para realizar también una FFT de «ida y vuelta» de una imagen, pero esta vez generando imágenes real/imaginario. |
# 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:
Debes usar una versión HDRI cuando uses las formas «plus» para generar imágenes FFT real/imaginario. Si no lo haces, aproximadamente la mitad de los valores serán cero, lo que da una imagen de aspecto «sucio». Por ejemplo... |
# 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)
La otra cosa que hay que recordar es que cualquiera que sea la forma de imágenes FFT que generes también afectará a TODAS las operaciones de procesamiento de imágenes que quieras aplicar a las imágenes FFT. Son imágenes muy diferentes y, por ello, deben procesarse de formas muy distintas, con operaciones matemáticas diferentes. Además, como antes, debes tener ambas imágenes de componentes real e imaginario para restaurar la imagen final. Por ejemplo, esto es lo que ocurre si sustituimos uno de los componentes por una imagen «negra».
# 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)
Solo real | ![[IM Output]](../static/img/fourier/lena_imaginary_only.png)
Solo imaginario
---|---
De esto puedes ver que ambas imágenes FFT real/imaginario contienen información vital sobre la imagen original de forma bastante equitativa. La mayor diferencia entre ambas es que el DC especial o «color promedio» no tiene componente imaginario y, por ello, solo está presente en la imagen de magnitud. El efecto de espejo diagonal (en realidad una rotación de 180 grados) que ves en ambas imágenes se debe a la pérdida de la información de «signo» contenida en el otro componente. Sin el otro componente, podría pensarse que la onda está 180 grados desfasada, generando este aspecto extraño. Esta pérdida de información es igual entre los dos tipos de imagen.
Propiedades de la transformada de Fourier
FFT de una imagen constante
Demostremos algunas de estas propiedades. Primero, tomemos simplemente una imagen de color constante y obtengamos su magnitud.
magick -size 128x128 xc:gold constant.png
magick constant.png -fft +delete constant_magnitude.png
Observa que la imagen de magnitud en este caso realmente es negra pura, salvo por un único píxel de color en el centro mismo de la imagen, en la ubicación de píxel ancho/2, alto/2. Este píxel es el valor de frecuencia cero o DC («corriente continua») de la imagen, y es el único píxel que no representa una onda sinusoidal. En otras palabras, ¡este valor es simplemente el componente constante de la FFT! Para ver este único píxel con más claridad, ampliemos también esa zona de la imagen... |
magick constant_magnitude.png -gravity center -extent 5x5 \
-scale 2000% constant_dc_zoom.gif
![[IM Output]](../static/img/fourier/constant_dc_zoom.gif)
Observa que el color del punto DC es el mismo que el de la imagen original. En realidad, conviene recordar que lo que estás viendo son tres valores. Es decir, la imagen generada son en realidad tres transformadas rápidas de Fourier separadas. Una FFT para cada uno de los tres canales rojo, verde y azul de la imagen. La FFT en sí no tiene un conocimiento real sobre los colores, solo sobre los valores de color o «niveles de gris». De hecho, una transformada FFT podría aplicarse a prácticamente cualquier espacio de color, porque en realidad... ¡le da igual! Para una transformada de Fourier, una imagen no es más que una matriz de valores, y eso es todo. | Aunque la «fase» del valor DC no es importante, debería ser siempre un ángulo «cero» (un valor de color de fase de gris al 50%). Si no se fija en gris al 50%, el valor DC tendrá un componente «irreal» y su valor quedará modulado por el ángulo dado.
---|---
Efectos del color DC
En una imagen no constante más típica, el valor DC es el color promedio de la imagen. El color que generalmente obtendrías si hubieras desenfocado, promediado o redimensionado por completo la imagen hasta reducirla a un solo píxel o color. Por ejemplo, extraigamos el píxel DC de la FFT de la imagen «Lena».
magick lena.png -fft +delete lena_magnitude.png
magick lena_magnitude.png -gravity center -extent 1x1 \
-scale 60x60 lena_dc_zoom.gif
Como puedes ver, el color promedio de la imagen es una especie de «rosa oscuro». Otra forma de pensar en este píxel especial es que representa el nivel de «sesgo» central, alrededor del cual todas las demás ondas sinusoidales modifican los colores de la imagen. Por ejemplo, reemplacemos ese píxel DC «rosa oscuro» por algún otro color, como el más anaranjado «tomato»... |
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)
Lo que realmente ocurre es que, al cambiar el valor DC en las imágenes FFT, estás cambiando toda la imagen de esa misma manera. De hecho, cualquier cambio en el valor DC (la diferencia) se sumará (o restará) a todos y cada uno de los píxeles de la imagen resultante. Esto es igual que si realmente estuviéramos sumando alguna constante a cada píxel de la imagen original. Por ello, los colores finales de los píxeles en la imagen reconstruida también podrían quedar recortados por los límites máximo (blanco) o mínimo (negro). Por tanto, este no es un método recomendado para teñir de color una imagen. Es más sencillo de aplicar que modificar cada píxel de toda la imagen, aunque el viaje de ida y vuelta de la FFT lo convierte, en general, en una técnica de tintado de color mucho más lenta.
Espectro de una imagen de onda sinusoidal
A continuación, echemos un vistazo al espectro de una imagen de una sola onda sinusoidal (o cosenoidal) con 4 ciclos a lo ancho de la imagen
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 inusual creación de la imagen de degradado anterior es necesaria para asegurar que la imagen de onda sinusoidal resultante se repita en mosaico perfectamente a lo ancho de la imagen.
Una imagen "[gradient:](canvas.html#gradient)" normal no se repite perfectamente en mosaico, así que tampoco lo hace una onda sinusoidal generada a partir de ella. Una transformada FFT de un mosaico tan imperfecto dará lugar a una serie de armónicos no deseados, en lugar de «puntos» individuales en el espectro de la transformada de Fourier.
Véase Generating the Perfect Gradient para más detalles sobre este problema.
_
---|---
En la imagen del espectro (imagen de magnitud realzada) de arriba, podemos ver que tiene 3 puntos. El punto central es, como antes, el valor DC promedio. Los otros dos puntos representan la onda sinusoidal perfecta que el operador de Fourier encontró en la imagen. Como la frecuencia a lo ancho de la imagen es exactamente de 4 ciclos, en consecuencia dos píxeles de frecuencia están exactamente a 4 píxeles del valor DC central. Pero ¿por qué dos píxeles? Pues porque una sola onda sinusoidal puede describirse de dos formas completamente distintas (una con dirección y fase negativas). Ambas descripciones son matemáticamente correctas, y una transformada de Fourier no distingue entre ellas. Si repetimos esto con una onda sinusoidal de 16 ciclos, volvemos a ver que tiene 3 puntos, pero los puntos están más separados. En este caso, los puntos laterales están separados 16 píxeles a la izquierda y a la derecha del punto 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
De esto puedes ver que las ondas sinusoidales perfectas se representarán simplemente con dos puntos en la posición adecuada. La distancia a la que esta posición está del valor DC central determina la frecuencia de la onda sinusoidal. Cuanto menor es la longitud de onda, mayor es la frecuencia, así que más lejos estarán los puntos del valor DC. De hecho, dividir el tamaño de la imagen por la frecuencia (distancia de los puntos al centro) te da la longitud de onda (distancia entre picos) de la onda. En el caso anterior: 128 píxeles divididos entre 16 ciclos da una longitud de onda de 8 píxeles entre cada «banda». Esta es una de las características distintivas más importantes de la transformación FFT. Un patrón de detalles pequeños en la imagen original requiere longitudes de onda pequeñas y, por tanto, frecuencias grandes. Eso da lugar a efectos a gran escala en el dominio de la frecuencia. De forma similar, los detalles grandes usan frecuencias más pequeñas, por lo que generan patrones a pequeña escala, especialmente cerca del centro. En una transformada de Fourier...
Lo pequeño se vuelve grande y lo grande se vuelve pequeño.
Este es uno de los aspectos más esenciales que hay que recordar al trabajar con transformadas de Fourier, ya que es la clave para eliminar el ruido (detalles pequeños) de una imagen, preservando al mismo tiempo los aspectos más grandes y generales de la imagen. Echemos un vistazo más de cerca a estas tres «frecuencias» representando sus magnitudes originales (no el espectro logarítmico). |
magick sine16.png -fft -delete 1 miff:- |\
im_profile - sine16_magnitude_pf.png
![[IM Output]](../static/img/fourier/sine16_magnitude_pf.png)
Observa que el valor DC (promedio o sesgo de la imagen) tiene un valor de 1/2, lo cual es de esperar (el valor promedio de la imagen es un gris perfecto al 50%), pero que la magnitud real de las dos ondas sinusoidales de 16 ciclos que encontró la transformada de Fourier es solo 1/4 del valor máximo. La magnitud de la onda sinusoidal original es en realidad 1/2, pero la transformada de Fourier dividió esa magnitud en dos, repartiendo los resultados entre ambas ondas de frecuencia representadas, de modo que cada uno de los dos componentes solo tiene una magnitud de 1/4. Eso es algo normal de las transformadas de Fourier.
Esta dualidad de frecuencias positivas y negativas en las imágenes FFT explica por qué el espectro de toda imagen FFT (como el espectro de Lena repetido a la izquierda) es siempre simétrico respecto al centro. Por cada punto en un lado de la imagen, siempre obtendrás un «punto» similar reflejado rotacionalmente al otro lado del centro de la imagen. Lo mismo ocurre con el componente «fase» del par de imágenes FFT, pero con un desplazamiento de 180 grados (una fase negativa) en el valor. Eso significa que la mitad de cada imagen es en realidad un duplicado de la otra mitad, pero necesitas AMBAS imágenes para recrear la imagen original. En otras palabras, las dos imágenes siguen conteniendo exactamente la misma cantidad de información, la mitad en una imagen y la mitad en la otra. Juntas forman un todo. | _Durante la generación, el algoritmo FFT solo genera la mitad izquierda de las imágenes. La otra mitad se genera mediante rotaciones y duplicación de los datos generados.
Al convertir imágenes del dominio de la frecuencia de nuevo a una imagen del dominio espacial, el algoritmo solo observa de nuevo la mitad izquierda de la imagen. La mitad derecha se ignora por completo, ya que solo es un duplicado.
Por ello, cuando (en ejemplos posteriores) apliques un «filtro de muesca» a una imagen de magnitud FFT, en realidad solo necesitas filtrar la mitad izquierda de la imagen de magnitud. Puedes ahorrarte algo de trabajo ignorando también la mitad derecha. No obstante, por claridad, aplicaré la «muesca» a ambas mitades.
Generar imágenes FFT directamente
Ahora podemos usar la información anterior para generar realmente una imagen de una onda sinusoidal. Todo lo que necesitas hacer es crear un par de imágenes, una negra y otra de gris al 50%, y añadir «puntos» con la magnitud y la fase adecuadas. Por ejemplo...
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
| Y, como por arte de magia, una onda sinusoidal perfecta, en ángulo (y repetible en mosaico). Por supuesto, solo puedes generar ondas sinusoidales perfectas en frecuencias concretas, y solo son repetibles en mosaico en imágenes cuadradas (a menos que se redimensionen después). Por desgracia, todas las frecuencias serán además una potencia de dos en cualquier dirección horizontal o vertical, y esa es la principal limitación de esta técnica. | En realidad, solo se necesitaba el primer punto «gray25» (el más a la izquierda) para generar la onda sinusoidal, ya que la transformada IFT ignora por completo la mitad derecha de la imagen, pues debería ser simplemente un espejo rotacional de la mitad izquierda. |
|---|---|
| La fase del valor DC debe tener un «ángulo cero» (color gris al 50%). Si no te aseguras de que así sea, el valor de color DC quedará modulado por su fase distinta de cero, produciendo una imagen más oscura y posiblemente «recortada». | |
| --- | --- |
| _Los demás píxeles de la fase pueden tener cualquier nivel de gris que prefieras, y «desplazarán» efectivamente la onda sinusoidal a lo ancho de la imagen. De nuevo, solo importa realmente la fase del punto más a la izquierda. El lado derecho se ignora por completo. Solo asegúrate de que el píxel de fase DC central siga siendo gris al 50%. |
_
---|---
FUTURE: Perlin Noise Generator using FFT
Espectro de una línea vertical
Mostrar el espectro FFT de una línea fina y una gruesa Demostrar cómo los detalles pequeños se vuelven «grandes» y los grandes se vuelven «pequeños» en la FFT de la imagen. Relacionarlo con la onda sinusoidal, que podría considerarse una «línea» con un único armónico. Rotar la línea
Espectro de una imagen de patrón rectangular
A continuación, veamos el espectro de un rectángulo blanco de ancho 8 y alto 16 dentro de un fondo negro.
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
Como puedes ver, la imagen resultante tiene un patrón muy particular, con muchas frecuencias armónicas. También puedes ver que el rectángulo parecía estar rotado 90 grados. Eso es incorrecto: lo que estás viendo es esa misma regla que mencionamos antes... los detalles grandes se vuelven pequeños y los detalles pequeños se vuelven grandes. Por ello, la dimensión más pequeña del rectángulo se volvió más grande y la dimensión más grande se volvió más pequeña. Ahora, rotemos el rectángulo 45 grados. Encontramos que el espectro también está rotado en la misma dirección 45 grados.
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
Como puedes ver, la misma rotación en el dominio de la frecuencia. Es decir, el efecto de algún objeto rotado también estará rotado en su transformada de Fourier. Sin embargo, si ahora movemos el rectángulo...
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
El patrón de frecuencia no se movió. Esto se debe a que toda la información de posicionamiento está contenida en la imagen de fase. El patrón de frecuencia (la magnitud o su espectro) no cambia por el hecho de que se moviera. Esta separación de la posición es una de las características clave de la transformada de Fourier que la hace tan importante. Te permitirá buscar un patrón de imagen concreto dentro de una imagen más grande, independientemente de la ubicación del objeto que produjo ese patrón de espectro de Fourier.
Espectro de una imagen de patrón circular plano
A continuación, veamos el espectro de una imagen con un patrón circular plano blanco, en un caso con diámetro de 12 (radio 6) y en otro caso con diámetro de 24 (radio 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
Observa que la primera imagen es muy similar a lo que generamos para el ejemplo de jinc más arriba. No obstante, está un poco fragmentada. Estos artefactos se producen por el pequeño tamaño del círculo. Como se representa digitalmente, su perímetro no es perfectamente circular. De nuevo vemos que los detalles pequeños se vuelven grandes en el espacio de frecuencia transformado. La transformada del círculo más grande es mejor, ya que su perímetro es una aproximación más cercana a un círculo verdadero. Concluimos, por tanto, que efectivamente la transformada de la forma circular plana es una función jinc, y que la imagen que contiene el círculo de menor diámetro produce características de transformada más extendidas y anchas. Según las propiedades matemáticas de una transformada de Fourier, la distancia desde el centro hasta el medio del primer anillo oscuro del espectro será 1.22N/d. Cuando el diámetro del círculo es d=12, obtenemos una distancia de 1.22128/12=13. De igual modo, cuando el diámetro del círculo es d=24, obtenemos una distancia de 1.22*128/24=6.5.
Espectro de una imagen de patrón gaussiano
A continuación, veamos el espectro de dos imágenes, cada una con un patrón circular gaussiano blanco con sigmas de 8 y 16, respectivamente
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
Aparte del ruido producido por la disposición rectangular del patrón, el resultado fue que un patrón gaussiano produjo un patrón de frecuencia gaussiano casi idéntico. Más importante aún, ese patrón tenía un aspecto bastante limpio. Por supuesto, hay una diferencia de tamaño, siguiendo de nuevo esa misma regla: lo grande se vuelve pequeño y lo pequeño se vuelve grande. A partir de las propiedades matemáticas, el sigma en el espectro será simplemente N/(2*sigma), donde sigma es el de la imagen original. Así, para una imagen de tamaño N=128 y sigma=8, el sigma en el espectro será 128/16=8. De igual modo, si el sigma de la imagen es 16, entonces el sigma en el espectro será 128/32=4. Esta es la relación matemática de la regla «lo grande se vuelve pequeño y viceversa», y puede ser útil conocerla.
Espectro de una imagen de patrón de rejilla
A continuación, transformemos una imagen que contiene solo un conjunto de líneas de rejilla separadas 16x8 píxeles.
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
El espectro resultante es simplemente una serie de puntos donde las líneas de rejilla más juntas producen puntos más separados y viceversa. Según las propiedades anteriores, como las líneas de rejilla están separadas 16x8 píxeles, los puntos deberían estar separados N/a=128/16=8 y M/b=128/8=16, que es justo lo que se mide en esta imagen. Este patrón es de particular importancia, ya que te dará a conocer la relación de la transformada de Fourier con un patrón de mosaico regular en una imagen. Tales patrones de mosaico producen patrones de rejilla no centrales muy intensos en su transformada de Fourier. El punto clave aquí es que la información de forma está en el centro, pero la información de mosaico está en una disposición tipo rejilla alejada del centro de su transformada de Fourier.
Más información sobre el espectro
Aquí tienes algunos enlaces por si quieres saber más sobre las imágenes de espectro y sus propiedades.
- Wikipedia: Fourier Transform
- Fred Weinhaus, Properities of a Fourier Transform
- Wolfram MathWorld: Fourier Transform
Aplicaciones prácticas
Bien, ahora que hemos cubierto los fundamentos, ¿cuáles son las aplicaciones prácticas del uso de la transformada de Fourier? Algunas de las cosas que se pueden hacer incluyen: 1) aumentar o disminuir el contraste de una imagen, 2) desenfocar, 3) afilar, 4) detección de bordes y 5) eliminación de ruido.
Cambiar el contraste de una imagen - Radicación de coeficientes
Se puede ajustar el contraste de una imagen realizando la transformada directa de Fourier, elevando la imagen de magnitud a una potencia y usando luego eso junto con la fase en la transformada inversa de Fourier. Para aumentar el contraste se usa un exponente ligeramente menor que uno, y para disminuir el contraste se usa un exponente ligeramente mayor que uno. Así que primero aumentemos el contraste de la imagen de Lena usando un exponente de 0.9 y luego disminuyamos el contraste usando un exponente 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
No obstante, hacer esto a la imagen original también tendría el mismo efecto que hacer esto a la imagen original. Es decir, una modificación global de las magnitudes tiene el mismo efecto que si hicieras una modificación global de la imagen original.
Desenfocar una imagen - Filtrado de paso bajo
Una de las propiedades más importantes de las transformadas de Fourier es que la convolución en el dominio espacial equivale a una simple multiplicación en el dominio de la frecuencia. En el dominio espacial se usan filtros de convolución (núcleos) pequeños, de forma cuadrada y simples para desenfocar una imagen con la opción -convole. Esto se denomina filtro de paso bajo. El filtro más simple no es más que una matriz cuadrada con pesos iguales. Es decir, todos los valores son unos, que se normalizan dividiendo por su suma antes de aplicar la convolución. Esto equivale a un promedio local o de vecindad. Otro filtro de paso bajo es el filtro de forma circular con pesos gaussianos que proporcionan -gaussian-blur o -blur. En el dominio de la frecuencia, un tipo de filtro de desenfoque de paso bajo es simplemente un círculo blanco de intensidad constante rodeado de negro. Esto sería similar a un filtro de convolución de promediado de forma circular en el dominio espacial. Sin embargo, dado que la convolución en el dominio espacial equivale a la multiplicación en el dominio de la frecuencia, todo lo que necesitamos hacer es realizar una transformada directa de Fourier, luego multiplicar el filtro por la imagen de magnitud y finalmente realizar la transformada inversa de Fourier. Observamos que un filtro de convolución de tamaño pequeño corresponderá a un círculo grande en el dominio de la frecuencia. La multiplicación se lleva a cabo mediante -composite con un ajuste de multiplicación de -compose. Así que probemos a hacer esto con dos tamaños de filtros circulares, uno de diámetro 40 (radio 20) y el otro de diámetro 28 (radio 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
Así vemos que la imagen que usó el filtro de menor diámetro produjo más desenfoque. También observamos el efecto de «campaneo» u «ondulación» cerca de los bordes en las imágenes resultantes. Esto ocurre porque la transformada de Fourier de un círculo, como vimos antes, es una función jinc, que tiene oscilaciones decrecientes a medida que avanza hacia afuera desde el centro. Aquí, sin embargo, la función jinc y las oscilaciones están en el dominio espacial y no en el dominio de la frecuencia, como habíamos demostrado más arriba. Entonces, ¿qué podemos hacer al respecto? Lo más sencillo es suavizar los bordes de los círculos usando diversas Windowing Functions. Como alternativa, se puede usar un filtro como una forma gaussiana que, por definición, ya está suavizada. Así que hagamos esto último y usemos dos círculos desenfocados gaussianamente para eliminar la mayoría de los severos efectos de «campaneo».
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
Esto, por supuesto, es mucho mejor. El filtro de paso bajo ideal no consiste en desenfocar círculos en absoluto, sino en usar realmente una curva gaussiana adecuada de sigma en lugar de un radio. ¡Por supuesto, en este ejemplo acabamos haciendo un desenfoque para hacer un desenfoque! No obstante, el patrón de desenfoque que se multiplica por la imagen de magnitud FFT es fijo y, de hecho, podría recuperarse de una caché pregenerada. Además, la imagen multiplicadora no necesita tener el tamaño completo de la imagen original; puedes usar una imagen más pequeña. Por ello, lo anterior puede ser mucho más rápido para imágenes grandes y en el caso de manejar muchas imágenes. El punto más importante es que, para desenfoques grandes e intensos, la imagen del dominio de la frecuencia es pequeña y solo realiza una única multiplicación, en lugar de tener que promediar muchos píxeles para todos y cada uno de los píxeles de la imagen original. Para desenfoques de tamaño pequeño, quizá te convenga más el desenfoque por convolución más directo.
Detectar bordes en una imagen - Filtrado de paso alto
En el dominio espacial, los filtros de paso alto que extraen bordes de una imagen a menudo se implementan como convoluciones con pesos positivos y negativos de modo que sumen cero. Las cosas son mucho más simples en el dominio de la frecuencia. Aquí, un filtro de paso alto no es más que la versión negada del filtro de paso bajo. Es decir, donde el filtro de paso bajo es brillante, el filtro de paso alto es oscuro y viceversa. Así que en ImageMagick todo lo que necesitamos hacer es -negate la imagen del filtro de paso bajo. Apliquemos entonces filtros de paso alto a la imagen de Lena usando una imagen de círculo. Y luego de nuevo usando una curva puramente gaussiana.
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
Examinando con cuidado estos dos resultados, vemos que el círculo simple no es tan bueno como el gaussiano, ya que tiene artefactos de «campaneo» y no es tan nítido.
Afilar una imagen - Filtrado de realce alto
La forma más sencilla de afilar una imagen es aplicarle un filtro de paso alto (sin el estiramiento de normalización) y luego mezclarla con la imagen original.
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
Aquí se aplica un filtro de paso alto en el dominio de la frecuencia y el resultado se transforma de nuevo al dominio espacial, donde se mezcla con la imagen original para realzar los bordes de la imagen.
Eliminación de ruido - Filtrado de muesca
Muchas imágenes ruidosas contienen algún tipo de ruido con patrón. Este tipo de ruido es fácil de eliminar en el dominio de la frecuencia, ya que los patrones aparecen como un patrón de unos pocos puntos o líneas. Recuerda que una simple onda sinusoidal es un patrón repetido y aparece como solo 3 puntos en el espectro. Para eliminar este ruido, uno simplemente, pero por desgracia, tiene que enmascarar (o «amuescar») manualmente los puntos o líneas en la imagen de magnitud. Hacemos esto transformando al dominio de la frecuencia, creando una versión en escala de grises del espectro, enmascarando los puntos o líneas, aplicándole un umbral, multiplicando la imagen de máscara binaria por la imagen de magnitud y luego transformando de nuevo al dominio espacial. Probemos esto con la clown image, que contiene un patrón tipo tramado con rayas diagonales. Primero transformamos la imagen del payaso para crear sus imágenes de magnitud y fase.
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)
Espectro | ![[IM Output]](../static/img/fourier/clown_phase.png)
Fase
---|---|---|---
Vemos que el espectro contiene cuatro puntos brillantes en forma de estrella, uno en cada cuadrante. Estos puntos inusuales representan el patrón de la imagen del que queremos deshacernos. El punto y las líneas brillantes en el medio de la imagen no son motivo de preocupación, ya que representan el DC (color promedio de la imagen) y los efectos de los bordes de la imagen, y no deben modificarse. Observa que, al generar la imagen del espectro, forcé la imagen resultante a ser una imagen puramente en escala de grises. Esto es para poder ahora cargar la imagen en un editor y, usando cualquier color no gris (como el rojo), enmascarar la zona de esos 4 patrones en forma de estrella. Cuando termine de editar, puedo extraer las zonas que coloreé extrayendo una imagen de diferencia frente a la versión sin editar. Así...
magick clown_spectrum_edited.png clown_spectrum.png \
-compose difference -composite \
-threshold 0 -negate clown_spectrum_mask.png
Ahora simplemente multiplicamos la máscara por la magnitud y usamos el resultado junto con la imagen de fase original para transformar de nuevo al dominio espacial. Mostramos la imagen original al lado para comparar
magick clown_magnitude.png clown_spectrum_mask.png \
-compose multiply -composite \
clown_phase.png -ift clown_filtered.png
Un resultado muy bueno. Pero podemos hacerlo aún mejor. Como viste en los ejemplos anteriores, los simples «círculos» no son especialmente amigables con una imagen FFT, así que desenfoquemos un poco la máscara... |
magick clown_spectrum_mask.png \
-blur 0x5 -level 50x100% clown_mask_blurred.png
![[IM Output]](../static/img/fourier/clown_mask_blurred.png)
Y filtremos el payaso, esta vez regenerando las imágenes FFT en memoria. |
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 resultado sencillamente asombroso! Y uno que posiblemente podría mejorarse aún más ajustando esa máscara para que se adapte mejor a las formas de «estrella». Incluso podemos tomar la diferencia entre el original y el resultado para crear una imagen de las zonas donde se eliminó el ruido. |
magick clown_orig.jpg clown_filtered_2.png -compose difference \
-composite -normalize clown_noise.png
![[IM Output]](../static/img/fourier/clown_noise.png)
Probemos esto con otro ejemplo. Esta vez con una imagen de «Twigs» (ramitas) que se encuentra en el sitio web RoboRealm, que contiene un patrón irregular de rayas horizontales y verticales. De nuevo extraemos una imagen de espectro en escala de grises, igual que hicimos antes.
magick twigs.jpg -fft +delete -colorspace gray \
-auto-level -evaluate log 100000 twigs_spectrum.png
En este caso, como el ruido de la imagen está orientado horizontal y verticalmente, esto aparece como bandas horizontales y verticales gruesas a lo largo de las líneas centrales, pero no en el centro real de la imagen. De nuevo enmascaramos las partes usando un editor de imágenes, esta vez usando un color «azul» (en realidad no importa qué color se use)...
magick twigs_spectrum_edited.png twigs_spectrum.png \
-compose difference -composite \
-threshold 0 -negate twigs_spectrum_mask.png
Ahora multiplicamos de nuevo la máscara por la imagen de magnitud FFT y reconstruimos la imagen.
magick twigs.jpg -fft \
\( -clone 0 twigs_spectrum_mask.png -compose multiply -composite \) \
-swap 0 +delete -ift twigs_filtered.png
Y podemos tomar la diferencia entre el original y el resultado para crear una imagen de las zonas donde se eliminó el ruido.
magick twigs.jpg twigs_filtered.png -compose difference -composite \
-normalize twigs_noise.png
Añadir un poco de desenfoque a la máscara podría, de nuevo, mejorar todavía más los resultados. Como ejercicio, intenta eliminar el hilo de la imagen. Como pista, recuerda cómo el efecto de una línea en una imagen real se rota 90 grados en la FFT. Si te equivocas en esto, probablemente eliminarás la ramita en su lugar.
Aplicaciones avanzadas
Algunas de las otras aplicaciones más avanzadas del uso de la transformada de Fourier incluyen: 1) la deconvolución (eliminación de desenfoque) de imágenes con desenfoque de movimiento y desenfocadas y 2) la correlación cruzada normalizada para encontrar dónde una imagen pequeña coincide mejor dentro de una imagen más grande. Los ejemplos de multiplicación y división FFT (deconvolución) se trasladaron a un sub-directory, ya que están a la espera de operadores de procesamiento de imágenes definidos de manera más formal.
![[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)