Exemplos do ImageMagick -- Transformadas de Fourier
- As imagens são ondas
- Imagens apenas de magnitude ou apenas de fase
- Imagem de espectro da FFT
- Imagens FFT em HDRI
- Efeitos da cor DC
- Espectro de uma imagem de onda senoidal
- Gerando imagens FFT diretamente
- Espectro de uma imagem com padrão retangular
- Espectro de uma imagem com padrão circular plano
- Espectro de uma imagem com padrão circular gaussiano
-
Alterando o contraste de uma imagem - radiciação de coeficientes
- Borrando uma imagem - filtragem passa-baixa
- Detectando bordas em uma imagem - filtragem passa-alta
- Aguçando uma imagem - filtragem high boost
-
Remoção de ruído - filtragem notch
Multiplicação e divisão de FFT (exemplos de baixo nível - subpágina)
Introdução
Um dos conceitos mais difíceis de compreender no processamento de imagens é a Transformada de Fourier. Há duas razões para isso. Primeiro, é matematicamente avançada e, segundo, as imagens resultantes, que não se parecem com a imagem original, são difíceis de interpretar. Ainda assim, o uso da Transformada de Fourier pode oferecer novas maneiras de realizar processamentos conhecidos, como realçar brilho e contraste, borrar, aguçar e remover ruído. Mas também pode oferecer novos recursos que não é possível realizar no domínio normal da imagem. Entre eles estão a deconvolução (também conhecida como remoção de borrão) de distorções típicas de câmera, como borrão de movimento e desfoque de lente, e a correspondência de imagens usando correlação cruzada normalizada. O objetivo desta página é tentar explicar o contexto e a matemática simplificada da Transformada de Fourier e dar exemplos do processamento que se pode fazer usando a Transformada de Fourier. Se você achar isto demasiado, pode pular e simplesmente concentrar-se nas propriedades e nos exemplos, começando por FFT/IFT no ImageMagick. Para os interessados, outra discussão simples e agradável, incluindo analogias com a óptica, pode ser encontrada em An Intuitive Explanation of Fourier Theory. As notas de aula da Vanderbilt University School Of Engineering também são muito informativas para os mais inclinados à matemática: 1 & 2 Dimensional Fourier Transforms e Frequency Filtering. Outras referências matemáticas incluem as páginas da Wikipedia sobre Fourier Transform, Discrete Fourier Transform e Fast Fourier Transform, bem como Complex Numbers. Meus agradecimentos a Sean Burke por ter codificado a demonstração original e ao criador do ImageMagick por tê-la integrado ao ImageMagick. Ambos foram esforços heroicos. Muitos dos exemplos usam uma versão HDRI do ImageMagick, necessária para preservar a precisão das imagens transformadas. Recomenda-se compilar uma versão HDRI pessoal caso você queira tirar o máximo proveito dessas técnicas.
A Transformada de Fourier
Normalmente, uma imagem consiste em um arranjo de 'pixels', cada um definido por um conjunto de valores: vermelho, verde, azul e, às vezes, também transparência. Mas, para nossos propósitos aqui, ignoraremos a transparência. Assim, cada um dos canais vermelho, verde e azul contém um conjunto de valores de 'intensidade' ou de 'tons de cinza'. Isso é conhecido como uma imagem rasterizada 'no domínio espacial'. Essa é apenas uma forma sofisticada de dizer que a imagem é definida pelos 'valores de intensidade' que ela possui em cada 'local' ou 'posição no espaço'. Mas uma imagem também pode ser representada de outra maneira, conhecida como o 'domínio da frequência' da imagem. Nesse domínio, cada canal da imagem é representado em termos de ondas senoidais. Nesse 'domínio da frequência', cada canal tem valores de 'amplitude' armazenados em posições baseadas não em coordenadas 'espaciais' X,Y, mas em 'frequências' X,Y. Como essa é uma representação digital, as frequências são múltiplos de uma frequência 'mínima' ou unitária, e as coordenadas dos pixels representam os índices ou múltiplos inteiros dessa frequência unitária. Isso decorre do princípio de que "qualquer função bem-comportada pode ser representada por uma superposição (combinação ou soma) de ondas senoidais". Em outras palavras, a representação no 'domínio da frequência' é apenas outra forma de armazenar e reproduzir a imagem do 'domínio espacial'. Mas como uma imagem pode ser representada como uma 'onda'?
As imagens são ondas
Pois bem, se pegarmos uma única linha ou coluna de pixels de qualquer imagem e a representarmos em um gráfico (gerado com "gnuplot" usando o script "[im_profile](../static/img/scripts/im_profile)"), você verá que ele se parece bastante com uma onda.
magick holocaust_tn.gif -colorspace gray miff:- |\
im_profile -s - image_profile.gif
Se as flutuações fossem mais regulares em espaçamento e amplitude, você obteria algo mais parecido com um padrão 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
No entanto, embora esse padrão de onda regular seja vagamente semelhante ao perfil da imagem mostrado acima, ele é regular demais. Contudo, se você somasse mais ondas, poderia criar um padrão ainda mais próximo daquele obtido da imagem.
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
Veja também Adição de gradientes enviesados para um exemplo alternativo ao acima. Essa 'superposição de ondas' (soma de ondas) está muito mais próxima, mas ainda não corresponde exatamente ao padrão da imagem. No entanto, você pode continuar dessa maneira, somando mais ondas e ajustando-as, de modo que a onda composta resultante se aproxime cada vez mais do perfil real da imagem original. Por fim, somando ondas suficientes, você pode reproduzir exatamente o perfil original da imagem. Essa foi a descoberta feita pelo matemático Joseph Fourier. Uma interpretação moderna dela afirma que "qualquer função bem-comportada pode ser representada por uma superposição de ondas senoidais". Em outras palavras, somando um número suficiente de ondas senoidais com a frequência e a amplitude exatas, você pode reproduzir qualquer padrão flutuante. Portanto, a representação no 'domínio da frequência' é apenas outra forma de armazenar e reproduzir a imagem do 'domínio espacial'. A 'Transformada de Fourier' é então o processo de descobrir quais 'ondas' compõem uma imagem, tal como foi feito no exemplo acima.
Ondas bidimensionais em imagens
O exemplo acima mostra uma forma de aproximar o perfil de uma única linha de uma imagem com múltiplas ondas senoidais. Contudo, as imagens são bidimensionais e, por isso, as ondas usadas para representar uma imagem no 'domínio da frequência' também precisam ser bidimensionais. Eis um exemplo de uma dessas ondas bidimensionais. A onda tem vários componentes. Exemplo de imagem
Usando FFT/IFT no ImageMagick
Notas de implementação
O ImageMagick faz uso da FFTW, biblioteca de Transformada Discreta de Fourier, que exige a conversão das imagens de e para valores de ponto flutuante (números complexos), e foi implementada pela primeira vez na versão IM 6.5.4-3. Para que funcione como as pessoas geralmente esperam para imagens, qualquer imagem não quadrada ou com uma dimensão ímpar será preenchida (usando Pixels Virtuais) até ficar quadrada com o maior valor entre a largura e a altura da imagem. Para permitir a centralização correta da 'origem da FFT' no centro da imagem, ela também é forçada a ter dimensões pares (múltiplas de 2). A consequência disso é que, após aplicar a Transformada Inversa de Fourier, a imagem precisará ser recortada de volta às suas dimensões originais para remover o preenchimento. Como a Transformada de Fourier é composta por "Números Complexos", o resultado da transformada não pode ser visualizado diretamente. Portanto, a transformada complexa é separada em duas imagens de componentes em uma de duas formas. ![[Diagram]](../static/img/img_diagrams/complex_number.jpg)
Número Complexo
Real/Imaginário
Real e imaginário
A representação matemática e numérica usual dos "Números Complexos" é um par de valores de ponto flutuante formado por um componente 'Real' (a) e um 'Imaginário' (b). Infelizmente, esses dois números podem conter valores negativos e, portanto, não formam imagens visualizáveis. Assim, essa representação não pode ser usada em uma versão normal do ImageMagick, que recortaria (clip) tais imagens (veja abaixo o exemplo dos efeitos resultantes). No entanto, ao usar uma versão HDRI do ImageMagick, você ainda pode gerar, usar e até salvar essa representação de uma imagem transformada por Fourier. Elas podem não ser úteis nem sequer visualizáveis como imagens por si só, mas você ainda pode aplicar-lhes muitas operações matemáticas. Para gerar essa representação, usamos a forma 'plus' dos operadores, "[+fft](https://usage.imagemagick.org/fourier/option_link.cgi#fft)" e "[+ift](https://usage.imagemagick.org/fourier/option_link.cgi#ift)", que serão vistos em detalhe abaixo em FFT como componentes real-imaginário. ![[Diagram]](../static/img/img_diagrams/polar_number.jpg)
Complexo polar
Magnitude/Fase
Magnitude e fase
A representação numérica direta dos "Números Complexos" não é muito útil para o trabalho com imagens. Mas, ao plotar os valores em um plano bidimensional, você pode então converter o valor para uma Representação Polar formada por um componente de 'Magnitude' (r) e um de 'Fase' (θ). Essa forma é muito útil no processamento de imagens, especialmente o componente de magnitude, que essencialmente especifica todas as frequências que compõem a imagem. O componente de 'Magnitude' contém apenas valores positivos e é mapeado diretamente para valores de imagem. Ele não tem um intervalo fixo de valores, embora, exceto pela cor DC ou de frequência zero, os valores sejam geralmente bastante pequenos. Como consequência disso, a imagem de magnitude geralmente parecerá muito escura (praticamente preta). Normalmente será preciso escalar a magnitude e aplicar uma transformação logarítmica aos seus valores de intensidade para revelar qualquer detalhe visual. A imagem de magnitude 'transformada por logaritmo' resultante é conhecida como o 'espectro' da imagem. Contudo, lembre-se de que é a imagem de 'magnitude', e não a imagem de 'espectro', que deve ser usada para a transformada inversa. A cor DC (abreviação de "Corrente Contínua", em inglês "Direct Current") ou "Frequência Zero", que aparece na 'origem' central da imagem, será o valor médio de cor de toda a imagem. Além disso, como as imagens de entrada não contêm componentes 'imaginários', o valor de fase DC também terá sempre fase zero, produzindo uma cor cinza puro. O componente de 'Fase', por sua vez, varia de -π a +π. Ele é primeiro enviesado para um intervalo de 0 a 2π e depois escalado para valores de imagem reais que vão de 0 a QuantumRange (conforme determinado pela Qualidade de Memória em Tempo de Compilação). Como consequência disso, uma fase zero terá um valor cinza puro (conforme apropriado para cada canal), enquanto uma fase negada será um valor preto puro ('0'). Observe que um branco puro ('_QuantumRange_') é quase, mas não exatamente, a mesma coisa. Uma representação FFT de magnitude e fase de uma imagem é gerada usando os operadores FFT normais, "[+fft](https://usage.imagemagick.org/fourier/option_link.cgi#fft)" e "[+ift](https://usage.imagemagick.org/fourier/option_link.cgi#ift)". Isso será visto primeiro em Gerando imagens FFT e sua inversa.
Gerando imagens FFT e sua inversa
(Magnitude e fase)
Agora, vamos simplesmente tentar uma ida e volta de Transformada de Fourier na imagem Lena. Ou seja, fazemos apenas a transformada direta e imediatamente aplicamos a transformada inversa para recuperar a imagem original. Depois compararemos os resultados para ver o nível de qualidade produzido.
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:
O programa "[compare](basics.html#compare)" acima retorna uma medida de quão diferentes as duas imagens são. Neste caso, você pode ver que a diferença geral é muito pequena, cerca de 0.22%. Com uma diferença de valor de pico em pelo menos um pixel de cerca de ("PAE", Peak Absolute Error) de apenas cerca de 1%. Você pode melhorar isso usando uma versão HDRI do ImageMagick. (Veja FFT com HDRI abaixo). Vamos dar uma olhada mais de perto nas imagens FFT que foram geradas na ida e volta acima.
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)
Fase
---|---|---|---
Como disse John M. Brayer sobre as Transformadas de Fourier... Geralmente não exibimos imagens de FASE porque a maioria das pessoas que as veem logo em seguida sucumbe aos alucinógenos ou acaba num mosteiro tibetano. Observe que o operador "[-fft](https://usage.imagemagick.org/fourier/option_link.cgi#fft)" gerou duas imagens: a primeira é o componente de 'magnitude' (sim, é quase toda preta, com um único ponto colorido no meio), enquanto a segunda, de aparência quase aleatória, contém o componente de 'fase'. As imagens PNG só podem armazenar uma imagem por arquivo, de modo que nem a "[+adjoin](https://usage.imagemagick.org/fourier/option_link.cgi#adjoin)" nem o '%d' no nome do arquivo de saída eram realmente necessários, pois o IM cuidaria disso. Contudo, incluo as opções acima para fins de completude, deixando claro que estou gerando dois arquivos de imagem separados, não um só. Veja Escrevendo uma sequência de múltiplas imagens para mais detalhes. Como duas imagens são geradas, a imagem de magnitude (a primeira ou de índice zero) é salva em "lena_fft_0.png" e a imagem de fase (a segunda imagem) em "lena_fft_1.png". | _Para evitar qualquer chance de distorções resultantes de salvar imagens FFT, o melhor é não salvá-las em disco de forma alguma, mas mantê-las em memória enquanto você processa a imagem.
Se você precisar salvar, o melhor é usar o Formato de Arquivo Magick "[MIFF](https://usage.imagemagick.org/files/#miff")", de modo a preservar a imagem em sua mais alta qualidade (profundidade de bits). Esse formato também pode salvar múltiplas imagens em um único arquivo. Para trabalho com scripts, você também pode usar o verboso Formato de Pixel Enumerado "[TXT](files.html#txt)".
NÃO as salve usando os formatos de imagem "[JPEG](formats.html#jpg)" ou "[GIF](formats.html#gif)".
Se você precisar salvar essas imagens em arquivos para visualização real, como em um navegador web, use o formato de imagem "[PNG](formats.html#png)" com uma "[+depth](https://usage.imagemagick.org/fourier/option_link.cgi#depth)" redefinida ao padrão interno (como fazemos nesses exemplos). No entanto, ele só pode armazenar uma imagem por arquivo.
O formato de arquivo "[TIFF](formats.html#tiff)" também pode ser usado, embora não seja tão aceitável para navegadores web, mas permite múltiplas imagens por arquivo.
_
---|---
A melhor maneira de salvar imagens intermediárias em um único arquivo é usar o formato de arquivo "[MIFF](https://usage.imagemagick.org/files/#miff")"...
magick lena.png -fft +depth lena_fft.miff
Ou você pode salvá-las em nomes de arquivo completamente separados usando "[-write](https://usage.imagemagick.org/fourier/option_link.cgi#write)" (veja Escrevendo imagens)...
magick lena.png -fft +depth \
\( -clone 0 -write lena_magnitude.png +delete \) \
\( -clone 1 -write lena_phase.png +delete \) \
null:
Observe que, no exemplo acima, usei o formato de imagem especial "[NULL:](files.html#null)" para descartar as duas imagens, que ainda são preservadas em memória para processamento adicional. E, por fim, lemos novamente as duas imagens para reconvertê-las em uma imagem 'espacial' normal...
magick lena_magnitude.png lena_phase.png -ift lena_restored.png
Ambas as imagens geradas pelo processo FFT são muito sensíveis a modificações, onde até pequenas alterações podem resultar em resultados muito distorcidos. Por isso, é importante nunca salvá-las em qualquer formato de imagem que possa distorcer esses valores. É importante lembrar que ambas as imagens são necessárias para recuperar a imagem a partir do domínio da frequência. Portanto, não adianta salvar uma imagem e descartar a outra, se você pretende usá-las para a reconstrução da imagem.
Imagens apenas de magnitude ou apenas de fase
Por fim, vamos tentar reconstruir uma imagem apenas a partir do seu componente de magnitude ou apenas a partir do seu 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)
Apenas magnitude | ![[IM Output]](../static/img/fourier/lena_phase_only.png)
Apenas fase
---|---
Você notará por isso que é a imagem de fase que realmente contém a maior parte da informação de posição da imagem, enquanto a magnitude, na verdade, guarda grande parte da informação de cor. Isso não é exato, pois há alguma sobreposição na informação, mas geralmente é o caso. A imagem 'Apenas magnitude' sempre terá cantos brancos, pois foi usada uma imagem de fase constante de 50%. Você pode remover essas manchas brancas usando uma imagem de fase aleatorizada. No entanto, certifique-se de que a fase do pixel central seja um cinza 50% perfeito, ou a imagem inteira ficará escurecida. A imagem 'Apenas fase' usou uma imagem de magnitude cinza constante de 1% (quase preto puro) para a conversão. Mesmo com essa magnitude constante, ela ainda produz manchas de pixels muito intensos, especialmente ao longo das bordas. Você só precisa lembrar que ambas as imagens são necessárias para reconstruir a imagem original.
Imagem de espectro de frequência
Você terá notado que a imagem de magnitude (a primeira ou de índice zero) parece ser quase totalmente preta. Ela não é realmente, mas, para os nossos olhos, todos os valores são muito, muito pequenos. Uma imagem dessas não é muito interessante de se observar para estudo, então vamos realçar o resultado com uma transformação logarítmica para produzir uma imagem de 'espectro de frequência'. Isso é feito aplicando uma forte Transformação Logarítmica com Evaluate a uma imagem de 'magnitude' Normalizada.
magick lena_fft_0.png -auto-level -evaluate log 10000 \
lena_spectrum.png
Agora podemos ver os detalhes na versão de espectro da imagem de magnitude. Você pode até ver algumas cores específicas na imagem de espectro, mas geralmente essas cores não são importantes em uma imagem de espectro. É a intensidade geral de cada frequência, e os padrões que elas produzem, que é muito mais importante. Por isso, você também pode preferir converter a imagem de espectro para tons de cinza após o realce. O quanto de realce logarítmico você precisa usar depende da imagem, então você deve ajustá-lo até obter a quantidade de detalhe necessária para ver claramente o padrão do espectro de frequência das imagens. Como alternativa, você pode usar o seguinte pequeno script de shell para calcular um fator de escala logarítmico a usar para a imagem de magnitude específica. |
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)
No entanto, lembre-se de que você não pode usar uma imagem de espectro para a transformada inversa "[-ift](https://usage.imagemagick.org/fourier/option_link.cgi#ift)", pois ela produzirá uma imagem excessivamente clara. |
magick lena_spectrum.png lena_fft_1.png -ift lena_roundtrip_fail.png
Basicamente, como você realçou a imagem de 'magnitude', também realçou da mesma forma a imagem resultante, produzindo o resultado severamente 'recortado' (clipped) mostrado. ![[IM Output]](../static/img/fourier/lena_roundtrip_fail.png)
Imagens FFT em HDRI
Quando mapeamos os resultados da Transformada de Fourier em uma representação de imagem, escalamos e convertemos os valores de "Números Complexos" de ponto flutuante em valores de imagem inteiros. Isso naturalmente produziu Erros de Arredondamento e outros efeitos "Quânticos", especialmente nas magnitudes menores de baixa frequência. Se a precisão é importante no seu processamento de imagens, você precisará usar uma Qualidade de Bits (como as versões Q32 ou Q64 bits do ImageMagick) ou, melhor ainda, usar uma versão HDRI do ImageMagick para que os valores sejam armazenados como números de ponto flutuante. Ao usar uma versão HDRI do IM com uma representação de Magnitude e Fase da transformada de Fourier, o componente de magnitude ainda será todo composto por valores positivos e, portanto, ainda pode ser usado como mostrado acima, apenas de forma muito mais exata. O componente de fase, no entanto, ainda será enviesado e escalado, como mostrado anteriormente. Em outras palavras, a representação de magnitude e fase em HDRI é exatamente a mesma, apenas muito mais precisa.
Por exemplo, aqui uso uma versão HDRI do ImageMagick para gerar outra conversão de 'ida e volta' de uma imagem. |
# 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:
Se você comparar os resultados acima com a comparação não-HDRI anterior...
| Você verá que a versão HDRI do IM produziu um resultado muito mais preciso, aproximadamente na mesma velocidade de antes (a velocidade pode variar dependendo do seu computador). Embora tenha exigido muito mais memória do que um IM Q16 normal (veja Qualidade em Tempo de Compilação). No entanto, tais imagens, embora representem de forma mais exata os componentes de frequência da FFT da imagem, podem conter valores negativos e fracionários, que só podem ser salvos usando Formatos de Arquivo com suporte a HDRI capazes de lidar com valores de ponto flutuante. | Formatos de arquivo compatíveis com ponto flutuante incluem "[MIFF](https://usage.imagemagick.org/files/#miff")", "[TIFF](formats.html#tiff)", "[PFM](formats.html#netpbm)" e o formato de arquivo "EXR" específico de HDRI. No entanto, você pode precisar definir "-define quantum:format=floating-point" para que funcione. |
|---|---|
| Em exemplos posteriores, o processamento de uma FFT de uma imagem precisará dessa precisão para produzir bons resultados. Por isso, à medida que avançamos no uso de Transformadas Rápidas de Fourier, uma versão HDRI do ImageMagick se tornará um requisito. |
FFT como componentes real-imaginário
Até agora, só olhamos a representação de 'Magnitude' e 'Fase' de imagens transformadas por Fourier. Mas, se você compilou uma versão HDRI do IM, também pode processar imagens usando componentes 'Real' e 'Imaginário' de ponto flutuante. Isso é feito usando as versões 'plus' das opções "[+fft](https://usage.imagemagick.org/fourier/option_link.cgi#fft)" e "[+ift](https://usage.imagemagick.org/fourier/option_link.cgi#ift)". Por exemplo, aqui usei uma versão HDRI do IM para também realizar uma 'ida e volta' de FFT de uma imagem, mas desta vez gerando imagens Real/Imaginário. |
# 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:
Você deve usar uma versão HDRI quando usar as formas 'plus' para gerar imagens FFT Real/Imaginário. Se não o fizer, cerca de metade dos valores será zero, resultando em uma imagem de aparência 'suja'. Por exemplo... |
# 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)
A outra coisa a lembrar é que a forma de imagens FFT que você gerar também afetará TODAS as operações de processamento de imagem que você quiser aplicar às imagens FFT. Elas são imagens muito diferentes e, por isso, devem ser processadas de maneiras muito diferentes, com operações matemáticas distintas. Além disso, como antes, você deve ter ambas as imagens de componentes Real e Imaginário para restaurar a imagem final. Por exemplo, eis o que acontece se substituirmos uma imagem 'preta' por um dos componentes.
# 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)
Apenas real | ![[IM Output]](../static/img/fourier/lena_imaginary_only.png)
Apenas imaginário
---|---
Você pode ver por isso que ambas as imagens FFT Real/Imaginário contêm informações vitais sobre a imagem original de forma razoavelmente equilibrada. A maior diferença entre as duas é que a cor DC especial, ou 'cor média', não tem componente imaginário e, por isso, está presente apenas na imagem de magnitude. O efeito de espelhamento diagonal (na verdade, uma rotação de 180 graus) que você vê em ambas as imagens é causado pela perda da informação de 'sinal' contida no outro componente. Sem o outro componente, a onda poderia ser interpretada como estando 180 graus fora de fase, gerando essa aparência estranha. Essa perda de informação é igual entre os dois tipos de imagem.
Propriedades da Transformada de Fourier
FFT de uma imagem constante
Vamos demonstrar algumas dessas propriedades. Primeiro, vamos simplesmente pegar uma imagem de cor constante e obter sua magnitude.
magick -size 128x128 xc:gold constant.png
magick constant.png -fft +delete constant_magnitude.png
Observe que, neste caso, a imagem de magnitude é realmente preto puro, exceto por um único pixel colorido bem no centro da imagem, na posição de pixel largura/2, altura/2. Esse pixel é o valor de frequência zero ou DC ('Corrente Contínua') da imagem, e é o único pixel que não representa uma onda senoidal. Em outras palavras, esse valor é simplesmente o componente constante da FFT! Para ver esse único pixel com mais clareza, vamos também ampliar essa área da imagem... |
magick constant_magnitude.png -gravity center -extent 5x5 \
-scale 2000% constant_dc_zoom.gif
![[IM Output]](../static/img/fourier/constant_dc_zoom.gif)
Observe que a cor do ponto DC é a mesma da imagem original. Na verdade, é bom lembrar que o que você está vendo são três valores. Ou seja, a imagem gerada é, na verdade, três Transformadas Rápidas de Fourier separadas. Uma FFT para cada um dos três canais de imagem vermelho, verde e azul. A própria FFT não tem conhecimento real sobre cores, apenas sobre os valores de cor ou 'níveis de cinza'. De fato, uma transformada FFT poderia ser aplicada a praticamente qualquer espaço de cor, pois realmente... ela não se importa! Para uma transformada de Fourier, uma imagem é apenas um arranjo de valores, e é só isso. | Embora a 'fase' do valor DC não seja importante, ela deve ser sempre um ângulo 'zero' (um valor de cor de fase cinza 50%). Se não estiver definida como cinza 50%, o valor DC terá um componente 'irreal', e seu valor será modulado pelo ângulo dado.
---|---
Efeitos da cor DC
Em uma imagem não-constante mais típica, o valor DC é a cor média da imagem. A cor que você geralmente obteria se tivesse borrado, calculado a média ou redimensionado completamente a imagem para um único pixel ou cor. Por exemplo, vamos extrair o pixel DC da FFT da imagem "Lena".
magick lena.png -fft +delete lena_magnitude.png
magick lena_magnitude.png -gravity center -extent 1x1 \
-scale 60x60 lena_dc_zoom.gif
Como você pode ver, a cor média da imagem é uma espécie de 'rosa escuro'. Outra maneira de pensar nesse pixel especial é que ele representa o nível de 'viés' central em torno do qual todas as outras ondas senoidais modificam as cores da imagem. Por exemplo, vamos substituir esse pixel DC 'rosa escuro' por alguma outra cor, como a cor mais alaranjada '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)
O que está realmente acontecendo é que, ao alterar o valor DC nas imagens FFT, você está alterando toda a imagem da mesma forma. Na verdade, qualquer alteração no valor DC (a diferença) será somada (ou subtraída) de todo e cada pixel da imagem resultante. Isso é como se estivéssemos realmente somando alguma constante a cada pixel da imagem original. Assim, as cores finais dos pixels na imagem reconstruída também poderiam ser recortadas (clipped) pelos limites máximo (branco) ou mínimo (preto). Por isso, este não é um método recomendado para tingir a cor de uma imagem. Ele é mais simples de aplicar do que modificar cada pixel de toda a imagem, embora a ida e volta da FFT o torne, no geral, uma técnica de tingimento de cor muito mais lenta.
Espectro de uma imagem de onda senoidal
Em seguida, vamos observar o espectro de uma única imagem de onda senoidal (ou cossenoidal) com 4 ciclos ao longo da imagem
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
| _A criação incomum da imagem de gradiente acima é necessária para garantir que a imagem de onda senoidal resultante se replique (tile) perfeitamente ao longo da imagem.
Uma imagem "[gradient:](canvas.html#gradient)" normal não se replica perfeitamente, então uma onda senoidal gerada a partir dela também não. Uma transformada FFT de um ladrilho tão imperfeito resultará em um arranjo de harmônicos indesejados, em vez de 'pontos' únicos no espectro da Transformada de Fourier.
Veja Gerando o gradiente perfeito para mais detalhes sobre esse problema.
_
---|---
Na imagem de espectro (imagem de magnitude realçada) acima, podemos ver que ela tem 3 pontos. O ponto central é, como antes, o valor médio DC. Os outros dois pontos representam a onda senoidal perfeita que o Operador de Fourier encontrou na imagem. Como a frequência ao longo da largura da imagem é de exatamente 4 ciclos, dois pixels de frequência ficam a exatamente 4 pixels de distância do valor DC central. Mas por que dois pixels? Bem, isso ocorre porque uma única onda senoidal pode ser descrita de duas maneiras completamente diferentes (uma com direção e fase negativas). Ambas as descrições estão matematicamente corretas, e uma transformada de Fourier não distingue entre elas. Se repetirmos isso com uma onda senoidal de 16 ciclos, veremos novamente que ela tem 3 pontos, mas os pontos estão mais afastados. Nesse caso, os pontos laterais estão espaçados 16 pixels à esquerda e à direita do ponto 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
A partir disso, você pode ver que ondas senoidais perfeitas serão representadas simplesmente por dois pontos na posição apropriada. A distância dessa posição em relação ao valor DC central determina a frequência da onda senoidal. Quanto menor o comprimento de onda, maior a frequência, então mais distantes os pontos estarão do valor DC. Na verdade, dividindo o tamanho da imagem pela frequência (distância dos pontos ao centro), você obtém o comprimento de onda (distância entre os picos) da onda. No caso acima: 128 pixels divididos por 16 ciclos resultam em um comprimento de onda de 8 pixels entre cada 'banda'. Esta é uma das características distintivas mais importantes da transformação FFT. Um padrão de pequenas características na imagem original requer comprimentos de onda pequenos e, portanto, grandes frequências. Isso resulta em efeitos de grande escala no domínio da frequência. De maneira semelhante, características grandes usam frequências menores, gerando padrões de pequena escala, especialmente próximos ao centro. Em uma transformada de Fourier...
O pequeno se torna grande e o grande se torna pequeno.
Este é um dos aspectos mais vitais a lembrar ao lidar com Transformadas de Fourier, pois é a chave para remover ruído (pequenas características) de uma imagem, preservando os aspectos maiores gerais da imagem. Vamos dar uma olhada mais de perto nessas três 'frequências' plotando suas magnitudes originais (não o 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)
Observe que o valor DC (média ou viés da imagem) tem um valor de 1/2, o que é esperado (o valor médio da imagem é um cinza 50% perfeito), mas que a magnitude real das duas ondas senoidais de 16 ciclos que a transformada de Fourier encontrou é de apenas 1/4 do valor máximo. A magnitude da onda senoidal original é realmente 1/2, mas a transformada de Fourier dividiu essa magnitude em duas, distribuindo os resultados entre ambas as ondas de frequência plotadas, de modo que cada um dos dois componentes tem magnitude de apenas 1/4. Isso é uma parte normal das transformadas de Fourier.
Essa dualidade de frequências positivas e negativas nas imagens FFT explica por que o espectro de toda imagem FFT (como o espectro de Lena repetido à esquerda) é sempre simétrico em relação ao centro. Para cada ponto de um lado da imagem, você sempre obterá um 'ponto' semelhante espelhado por rotação através do centro da imagem. O mesmo acontece com o componente de 'fase' do par de imagens FFT, mas com um deslocamento de 180 graus (uma fase negativa) no valor também. Isso significa que metade de cada imagem é, na verdade, uma duplicata da outra metade, mas você precisa de AMBAS as imagens para recriar a imagem original. Em outras palavras, as duas imagens ainda contêm exatamente a mesma quantidade de informação, metade em uma imagem e metade na outra. Juntas, elas produzem um todo. | _Durante a geração, o algoritmo FFT gera apenas a metade esquerda das imagens. A outra metade é gerada por rotações e duplicação dos dados gerados.
Ao converter imagens do Domínio da Frequência de volta para uma Imagem do Domínio Espacial, o algoritmo novamente olha apenas para a metade esquerda da imagem. A metade direita é completamente ignorada, pois é apenas uma duplicata.
Por isso, quando (em exemplos posteriores) você aplicar uma 'filtragem notch' a uma imagem de magnitude FFT, só precisará realmente filtrar o lado esquerdo da imagem de magnitude. Você pode poupar algum trabalho ignorando também a metade direita. No entanto, para maior clareza, aplicarei o 'notch' em ambas as metades.
Gerando imagens FFT diretamente
Agora podemos usar as informações acima para efetivamente gerar uma imagem de uma onda senoidal. Tudo o que você precisa fazer é criar um par de imagens, uma preta e uma cinza 50%, e adicionar 'pontos' com a magnitude e a fase apropriadas. Por exemplo...
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
| E pronto, uma onda senoidal perfeitamente angulada (e replicável). É claro que você só pode gerar ondas senoidais perfeitas em frequências específicas, e elas só são replicáveis em imagens quadradas (a menos que sejam redimensionadas depois). Infelizmente, todas as frequências também serão uma potência de dois em qualquer direção horizontal ou vertical, e essa é a principal limitação dessa técnica. | Na verdade, apenas o primeiro ponto 'gray25' (mais à esquerda) era necessário para gerar a onda senoidal, pois a transformada IFT ignora completamente a metade direita da imagem, já que ela deve ser simplesmente um espelho por rotação da metade esquerda. |
|---|---|
| A fase do valor DC deve ter um 'ângulo zero' (cor cinza 50%). Se você não garantir que seja esse o caso, o valor de cor DC será modulado por sua fase não-zero, produzindo uma imagem mais escura, possivelmente 'recortada' (clipped). | |
| --- | --- |
| _Os demais pixels na fase podem ter qualquer nível de cinza que você quiser, e efetivamente 'rolarão' a onda senoidal ao longo da imagem. Novamente, apenas a fase do ponto mais à esquerda realmente importa. O lado direito é completamente ignorado. Apenas garanta que o pixel de fase DC central permaneça cinza 50%. |
_
---|---
FUTURE: Perlin Noise Generator using FFT
Espectro de uma linha vertical
Mostrar o espectro FFT de uma linha fina e de uma linha grossa Demonstrar como pequenas características se tornam 'grandes' e grandes características se tornam 'pequenas' na FFT da imagem. Relacionar isso de volta à onda senoidal, que poderia ser considerada uma 'linha' com um único harmônico. Rotacionar a linha
Espectro de uma imagem com padrão retangular
Em seguida, vamos observar o espectro de um retângulo branco de largura 8 e altura 16 dentro de um fundo preto.
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 você pode ver, a imagem resultante tem um padrão muito particular, com muitas frequências harmônicas. Você também pode ver que o retângulo parece ter sido rotacionado 90 graus. Isso está incorreto; o que você está vendo é aquela mesma regra que mencionamos antes... grandes características se tornam pequenas e pequenas características se tornam grandes. Assim, a menor dimensão do retângulo tornou-se maior e a maior dimensão tornou-se menor. Agora, vamos rotacionar o retângulo em 45 graus. Descobrimos que o espectro também é rotacionado na mesma direção em 45 graus.
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 você pode ver, ocorre a mesma rotação no domínio da frequência. Ou seja, o efeito de algum objeto rotacionado também será rotacionado em sua Transformada de Fourier. No entanto, se agora movermos o retâ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
O padrão de frequência não se moveu. Isso acontece porque toda a informação de posicionamento está contida na imagem de fase. O padrão de frequência (magnitude ou seu espectro) não muda por ter sido movido. Essa separação de posição é uma das características-chave da Transformada de Fourier que a torna tão importante. Ela permitirá que você procure por um padrão de imagem específico dentro de uma imagem maior, independentemente da localização do objeto que produziu esse padrão de espectro de Fourier.
Espectro de uma imagem com padrão circular plano
Em seguida, vamos observar o espectro de uma imagem com um padrão circular plano e branco, em um caso com diâmetro de 12 (raio 6) e em outro com diâmetro de 24 (raio 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
Observe que a primeira imagem é muito próxima do que geramos para o exemplo do jinc mais acima. Ela está, no entanto, um pouco fragmentada. Esses artefatos ocorrem devido ao pequeno tamanho do círculo. Como ele é representado digitalmente, seu perímetro não é perfeitamente circular. Novamente, vemos que os pequenos detalhes se tornam grandes no espaço de frequência transformado. A transformada do círculo maior é melhor, pois seu perímetro é uma aproximação mais próxima de um círculo verdadeiro. Concluímos, portanto, que a transformada da forma circular plana é de fato uma função jinc e que a imagem contendo o círculo de menor diâmetro produz características de transformada mais espalhadas e mais largas. De acordo com as propriedades matemáticas de uma Transformada de Fourier, a distância do centro até o meio do primeiro anel escuro no espectro será 1.22N/d. Quando o diâmetro do círculo é d=12, obtemos uma distância de 1.22128/12=13. Da mesma forma, quando o diâmetro do círculo é d=24, obtemos uma distância de 1.22*128/24=6.5.
Espectro de uma imagem com padrão gaussiano
Em seguida, vamos observar o espectro de duas imagens, cada uma com um padrão circular gaussiano branco com sigmas de 8 e 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
Além do ruído produzido pelo arranjo retangular do padrão, o resultado foi que um padrão gaussiano produziu um padrão de frequência gaussiano quase idêntico. Mais importante, esse padrão tinha uma aparência bastante limpa. É claro que há uma diferença de tamanho, seguindo novamente aquela mesma regra: o grande se torna pequeno e o pequeno se torna grande. A partir das propriedades matemáticas, o sigma no espectro será simplesmente N/(2*sigma), onde sigma é o da imagem original. Assim, para uma imagem de tamanho N=128 e sigma=8, o sigma no espectro será 128/16=8. De maneira semelhante, se o sigma da imagem for 16, então o sigma no espectro será 128/32=4. Esta é a relação matemática da regra "o grande se torna pequeno e vice-versa", e pode ser útil conhecê-la.
Espectro de uma imagem com padrão de grade
Em seguida, vamos transformar uma imagem contendo apenas um conjunto de linhas de grade espaçadas 16x8 pixels entre si.
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
O espectro resultante é apenas um arranjo de pontos, onde as linhas de grade mais próximas entre si produzem pontos mais afastados e vice-versa. De acordo com as propriedades acima, como as linhas de grade estão espaçadas 16x8 pixels entre si, então os pontos devem estar espaçados N/a=128/16=8 e M/b=128/8=16, que é justamente o que se mede nesta imagem. Este padrão é de particular importância, pois lhe dará a conhecer a relação da Transformada de Fourier com um padrão de ladrilhamento regular em uma imagem. Tais padrões de ladrilhamento produzem padrões de grade não-centrais muito fortes em sua Transformada de Fourier. O ponto-chave aqui é que a informação de forma está no centro, mas a informação de ladrilhamento está em um arranjo em forma de grade afastado do centro de sua transformada de Fourier.
Mais informações sobre o espectro
Aqui estão alguns links caso você queira saber mais sobre imagens de espectro e suas propriedades.
- Wikipedia: Fourier Transform
- Fred Weinhaus, Properities of a Fourier Transform
- Wolfram MathWorld: Fourier Transform
Aplicações práticas
OK, agora que cobrimos o básico, quais são as aplicações práticas do uso da Transformada de Fourier? Algumas das coisas que se pode fazer incluem: 1) aumentar ou diminuir o contraste de uma imagem, 2) borrar, 3) aguçar, 4) detecção de bordas e 5) remoção de ruído.
Alterando o contraste de uma imagem - radiciação de coeficientes
É possível ajustar o contraste em uma imagem realizando a transformada direta de Fourier, elevando a imagem de magnitude a uma potência e depois usando isso com a fase na transformada inversa de Fourier. Para aumentar o contraste, usa-se um expoente ligeiramente menor que um e, para diminuir o contraste, usa-se um expoente ligeiramente maior que um. Então, vamos primeiro aumentar o contraste na imagem Lena usando um expoente de 0.9 e depois diminuir o contraste usando um expoente 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 entanto, fazer isso teria o mesmo efeito que fazê-lo na imagem original. Ou seja, uma modificação global das magnitudes tem o mesmo efeito que uma modificação global da imagem original.
Borrando uma imagem - filtragem passa-baixa
Uma das propriedades mais importantes das Transformadas de Fourier é que a convolução no domínio espacial equivale a uma simples multiplicação no domínio da frequência. No domínio espacial, usam-se filtros de convolução (núcleos) pequenos, quadrados e simples para borrar uma imagem com a opção -convole. Isso é chamado de filtro passa-baixa. O filtro mais simples é apenas um arranjo quadrado com pesos iguais. Ou seja, todos os valores são uns, que são normalizados dividindo-os por sua soma antes de aplicar a convolução. Isso equivale a uma média local ou de vizinhança. Outro filtro passa-baixa é o filtro de formato circular com peso gaussiano fornecido por -gaussian-blur ou -blur. No domínio da frequência, um tipo de filtro de borramento passa-baixa é apenas um círculo branco de intensidade constante rodeado de preto. Isso seria semelhante a um filtro de convolução de média de formato circular no domínio espacial. No entanto, como a convolução no domínio espacial equivale à multiplicação no domínio da frequência, tudo o que precisamos fazer é realizar uma transformada direta de Fourier, depois multiplicar o filtro pela imagem de magnitude e, por fim, realizar a transformada inversa de Fourier. Observamos que um filtro de convolução de pequeno tamanho corresponderá a um grande círculo no domínio da frequência. A multiplicação é realizada via -composite com uma configuração multiply de -compose. Então, vamos tentar fazer isso com dois tamanhos de filtros circulares, um de diâmetro 40 (raio 20) e o outro de diâmetro 28 (raio 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
Assim, vemos que a imagem que usou o filtro de menor diâmetro produziu mais borramento. Também observamos o efeito de 'ringing' ou 'ondulação' (ripple) próximo às bordas nas imagens resultantes. Isso ocorre porque a Transformada de Fourier de um círculo, como vimos anteriormente, é uma função jinc, que tem oscilações decrescentes à medida que avança para fora do centro. Aqui, no entanto, a função jinc e as oscilações estão no domínio espacial, e não no domínio da frequência, como havíamos demonstrado antes. Então, o que podemos fazer a respeito disso? A coisa mais simples é suavizar as bordas dos círculos usando diversas Funções de Janelamento. Como alternativa, pode-se usar um filtro como um formato gaussiano, que por definição já é suavizado. Então, vamos fazer o último e usar dois círculos borrados por gaussiana para remover a maior parte dos severos efeitos 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
Isso, é claro, é muito melhor. O filtro passa-baixa ideal não é borrar círculos, mas realmente usar uma curva gaussiana adequada de sigma em vez de um raio. É claro que, neste exemplo, acabamos fazendo um borramento para fazer um borramento! No entanto, o padrão de borramento que é multiplicado pela imagem de magnitude FFT usada é fixo e poderia, de fato, ser recuperado de um cache pré-gerado. Além disso, a imagem multiplicadora não precisa ter o tamanho completo da imagem original; você pode usar uma imagem menor. Por isso, o procedimento acima pode ser muito mais rápido para imagens grandes e no caso de lidar com muitas imagens. O ponto mais importante é que, para borramentos grandes e fortes, a imagem no domínio da frequência é pequena e faz apenas uma única multiplicação, em vez de ter de calcular a média de muitos pixels, para todo e cada pixel da imagem original. Para borramentos de pequeno tamanho, talvez seja melhor usar o borramento por convolução mais direto.
Detectando bordas em uma imagem - filtragem passa-alta
No domínio espacial, os filtros passa-alta que extraem bordas de uma imagem são frequentemente implementados como convoluções com pesos positivos e negativos, de modo que somem zero. As coisas são muito mais simples no domínio da frequência. Aqui, um filtro passa-alta é apenas a versão negada do filtro passa-baixa. Ou seja, onde o filtro passa-baixa é claro, o filtro passa-alta é escuro e vice-versa. Então, no ImageMagick, tudo o que precisamos fazer é -negate a imagem do filtro passa-baixa. Então, vamos aplicar filtros passa-alta à imagem Lena usando uma imagem de círculo. E depois novamente usando uma 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 cuidadosamente esses dois resultados, vemos que o círculo simples não é tão bom quanto o gaussiano, pois tem artefatos de 'ringing' e não é tão nítido.
Aguçando uma imagem - filtragem high boost
A maneira mais simples de aguçar uma imagem é aplicar-lhe um filtro passa-alta (sem o esticamento de normalização) e depois mesclá-la com a imagem 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
Aqui, um filtro passa-alta é aplicado no domínio da frequência e o resultado é transformado de volta para o domínio espacial, onde é mesclado com a imagem original, para realçar as bordas da imagem.
Remoção de ruído - filtragem notch
Muitas imagens ruidosas contêm algum tipo de ruído padronizado. Esse tipo de ruído é fácil de remover no domínio da frequência, pois os padrões aparecem como um padrão de alguns poucos pontos ou linhas. Lembre-se de que uma simples onda senoidal é um padrão repetido e aparece como apenas 3 pontos no espectro. Para remover esse ruído, basta, mas infelizmente, mascarar (ou aplicar um notch) manualmente os pontos ou linhas na imagem de magnitude. Fazemos isso transformando para o domínio da frequência, criando uma versão em tons de cinza do espectro, mascarando os pontos ou linhas, aplicando um limiar, multiplicando a imagem de máscara binária pela imagem de magnitude e depois transformando de volta para o domínio espacial. Vamos tentar isso na imagem do palhaço, que contém um padrão diagonal listrado semelhante a um dither. Primeiro, transformamos a imagem do palhaço para criar suas imagens de magnitude e 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 o espectro contém quatro pontos brilhantes em forma de estrela, um em cada quadrante. Esses pontos incomuns representam o padrão na imagem do qual queremos nos livrar. O ponto e as linhas brilhantes no meio da imagem não são motivo de preocupação, pois representam o DC (cor média da imagem) e efeitos das bordas da imagem, e não devem ser modificados. Observe que, ao gerar a imagem de espectro, forcei a imagem resultante a ser uma imagem em tons de cinza puros. Isso é para que eu possa agora carregar a imagem em um editor e, usando qualquer cor não-cinza (como o vermelho), mascarar a área daqueles 4 padrões em forma de estrela. Quando terminar a edição, posso extrair as áreas que colori, extraindo uma imagem de diferença em relação à versão não editada. Assim...
magick clown_spectrum_edited.png clown_spectrum.png \
-compose difference -composite \
-threshold 0 -negate clown_spectrum_mask.png
Agora simplesmente multiplicamos a máscara pela magnitude e usamos o resultado com a imagem de fase original para transformar de volta ao domínio espacial. Exibimos a imagem original ao lado dela para comparação
magick clown_magnitude.png clown_spectrum_mask.png \
-compose multiply -composite \
clown_phase.png -ift clown_filtered.png
Um resultado muito bom. Mas podemos fazer ainda melhor. Como você viu nos exemplos anteriores, simples 'círculos' não são particularmente amigáveis a uma imagem FFT, então vamos borrar levemente a máscara... |
magick clown_spectrum_mask.png \
-blur 0x5 -level 50x100% clown_mask_blurred.png
![[IM Output]](../static/img/fourier/clown_mask_blurred.png)
E filtrar o palhaço, desta vez regerando as imagens FFT em memória. |
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)
Um resultado simplesmente incrível! E que poderia possivelmente ser melhorado ainda mais ajustando aquela máscara para se ajustar melhor aos formatos de 'estrela'. Podemos até tomar a diferença entre o original e o resultado para criar uma imagem das áreas onde o ruído foi removido. |
magick clown_orig.jpg clown_filtered_2.png -compose difference \
-composite -normalize clown_noise.png
![[IM Output]](../static/img/fourier/clown_noise.png)
Vamos tentar isso em outro exemplo. Desta vez, em uma imagem "Twigs" encontrada no site RoboRealm, que contém um padrão irregular de listras horizontais e verticais. Novamente, extraímos uma imagem de espectro em tons de cinza, exatamente como fizemos antes.
magick twigs.jpg -fft +delete -colorspace gray \
-auto-level -evaluate log 100000 twigs_spectrum.png
Neste caso, como o ruído na imagem é orientado horizontal e verticalmente, ele aparece como faixas horizontais e verticais espessas ao longo das linhas centrais, mas não no centro real da imagem. Novamente, mascaramos as partes usando um editor de imagem, desta vez usando a cor 'azul' (realmente não importa qual cor seja usada)...
magick twigs_spectrum_edited.png twigs_spectrum.png \
-compose difference -composite \
-threshold 0 -negate twigs_spectrum_mask.png
Agora novamente multiplicamos a máscara pela imagem de magnitude FFT e reconstruímos a imagem.
magick twigs.jpg -fft \
\( -clone 0 twigs_spectrum_mask.png -compose multiply -composite \) \
-swap 0 +delete -ift twigs_filtered.png
E podemos tomar a diferença entre o original e o resultado para criar uma imagem das áreas onde o ruído foi removido.
magick twigs.jpg twigs_filtered.png -compose difference -composite \
-normalize twigs_noise.png
Adicionar um pouco de borramento à máscara poderia, novamente, melhorar ainda mais os resultados. Como exercício, tente remover o barbante da imagem. Como dica, lembre-se de como o efeito de uma linha em uma imagem real é rotacionado 90 graus na FFT. Se você errar isso, provavelmente removerá o galho em vez do barbante.
Aplicações avançadas
Algumas das outras aplicações mais avançadas do uso da Transformada de Fourier incluem: 1) a deconvolução (remoção de borrão) de imagens com borrão de movimento e desfocadas e 2) a correlação cruzada normalizada para encontrar onde uma imagem pequena melhor corresponde dentro de uma imagem maior. Os exemplos de Multiplicação e Divisão de FFT (deconvolução) foram movidos para um subdiretório, pois estão aguardando operadores de processamento de imagem mais formalmente definidos.
![[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)