fbpx
Sigmoidal
  • Home
  • Pós-Graduação
  • Blog
  • Sobre Mim
  • Contato
Sem Resultado
Ver Todos Resultados
  • English
  • Home
  • Pós-Graduação
  • Blog
  • Sobre Mim
  • Contato
Sem Resultado
Ver Todos Resultados
Sigmoidal
Sem Resultado
Ver Todos Resultados

Detecção de Blur com FFT (Transformada de Fourier)

Carlos Melo por Carlos Melo
dezembro 29, 2025
em Artigos, Python, Tutoriais, Visão Computacional
0
10
VIEWS
Publicar no LinkedInCompartilhar no FacebookCompartilhar no Whatsapp

A qualidade da imagem de entrada é uma das variáveis mais negligenciadas em pipelines de visão computacional. Modelos treinados em datasets curados sofrem degradação significativa de performance quando recebem imagens borradas em produção.

O problema é antigo e bem estudado. Desde o trabalho de Liu et al. (CVPR 2008) sobre detecção de blur parcial, a análise espectral se consolidou como uma das abordagens mais elegantes para o problema.

A ideia central é que o desfoque atua como um filtro passa-baixas no domínio da frequência, atenuando bordas e detalhes finos. A Transformada de Fourier permite quantificar essa atenuação diretamente.

Neste artigo, vamos implementar um detector de blur baseado na FFT 2D com NumPy e OpenCV, validar seu comportamento em um experimento controlado de blur progressivo, e comparar com a variância do Laplaciano.

Resultado do detector de blur via FFT — imagem nítida classificada corretamente em verde, borrada em vermelho

💡 Clique neste link para acessar o Jupyter Notebook deste artigo.

 

Por que a FFT detecta blur?

A intuição é surpreendentemente simples. Pense em uma imagem nítida: ela tem bordas bem definidas, texturas detalhadas, transições abruptas entre regiões claras e escuras. Se você já trabalhou com equalização de histograma com OpenCV, sabe que imagens carregam muita informação na distribuição de intensidades. No domínio da frequência, esses detalhes se traduzem como altas frequências.

Agora pense em uma imagem borrada. As bordas ficam suaves, os detalhes somem, as transições viram gradientes lentos. Em termos de frequência, é como se alguém tivesse passado um filtro e removido justamente as altas frequências.

E é exatamente isso que acontece. Do ponto de vista matemático, o blur gaussiano é uma convolução com um kernel que atua como filtro passa-baixas:

    \[g(x, y) = f(x, y) * h(x, y)\]

No domínio da frequência, essa convolução se torna uma multiplicação:

    \[G(u, v) = F(u, v) \cdot H(u, v)\]

Se h é um kernel gaussiano, então H(u, v) preserva as baixas frequências e atenua as altas. O resultado é uma imagem com menos detalhes — ou seja, borrada.

A Transformada de Fourier nos permite enxergar essa diferença diretamente. Veja os espectros de magnitude de uma imagem nítida e uma borrada:

Espectro de magnitude da imagem nítida — energia espalhada por todas as frequências
Espectro de magnitude da imagem borrada — energia concentrada no centro

Na imagem nítida, a energia se espalha por todo o espectro. As linhas irradiando do centro representam bordas e texturas em várias direções. Na imagem borrada, quase toda a energia está concentrada no centro. As altas frequências praticamente desapareceram.

Como funciona a detecção de blur com FFT?

A ideia do detector é medir quanta energia resta nas altas frequências. Se houver pouca, a imagem provavelmente está borrada. O algoritmo segue cinco passos:

  1. Calcular a FFT 2D da imagem e centralizar o espectro com fftshift.
  2. Criar uma máscara circular e zerar a região central — removendo as baixas frequências.
  3. Aplicar a FFT inversa para reconstruir apenas o conteúdo de altas frequências.
  4. Calcular a média da magnitude desse sinal reconstruído.
  5. Comparar com um limiar: se a média estiver abaixo, a imagem é considerada borrada.

Vamos à implementação. Primeiro, carregamos as imagens e convertemos para tons de cinza:

import numpy as np
import cv2

# Carregar imagens e converter para tons de cinza
img_nitida = cv2.imread("imagem_nitida.jpg")
img_borrada = cv2.imread("imagem_borrada.jpg")

gray_nitida = cv2.cvtColor(img_nitida, cv2.COLOR_BGR2GRAY)
gray_borrada = cv2.cvtColor(img_borrada, cv2.COLOR_BGR2GRAY)

Agora, a função principal do detector:

def detect_blur_fft(image, size=60, thresh=5):
    """
    Detecta se uma imagem em tons de cinza está borrada usando FFT.
    Retorna (média da magnitude, True se borrada).
    """
    h, w = image.shape
    cy, cx = h // 2, w // 2

    # FFT 2D + centralizar componente DC
    fft = np.fft.fft2(image)
    fft_shift = np.fft.fftshift(fft)

    # Máscara circular: zerar baixas frequências (raio <= size)
    Y, X = np.ogrid[:h, :w]
    dist = np.sqrt((X - cx)**2 + (Y - cy)**2)
    fft_shift[dist <= size] = 0

    # FFT inversa: reconstruir apenas altas frequências
    recon = np.fft.ifft2(np.fft.ifftshift(fft_shift))

    # Métrica: média da magnitude (sempre >= 0)
    mean_mag = np.mean(np.abs(recon))
    return mean_mag, mean_mag <= thresh

Alguns detalhes importantes sobre a implementação:

  • A máscara circular é mais adequada do que uma quadrada porque queremos remover todas as frequências abaixo de um determinado módulo, independentemente da orientação, e a distância ao centro no espectro corresponde exatamente ao módulo da frequência espacial.
  • Usamos np.mean(np.abs(recon)) como métrica. Ela é sempre não-negativa e monotonicamente relacionada ao conteúdo de alta frequência: quanto maior o valor, mais energia de alta frequência resta na imagem. O valor absoluto depende da resolução e do conteúdo da cena, então o limiar deve ser calibrado por aplicação.
  • O parâmetro size controla o raio da máscara. Valores maiores removem mais frequências, tornando o detector mais seletivo. Na prática, size deveria ser proporcional à dimensão da imagem. Para imagens de resolução maior, um raio proporcionalmente maior pode ser necessário.

Aplicando nas duas imagens:

mean_nitida, blur_nitida = detect_blur_fft(gray_nitida, size=60, thresh=5)
mean_borrada, blur_borrada = detect_blur_fft(gray_borrada, size=60, thresh=5)

print(f"Nítida  — métrica: {mean_nitida:.2f} | {'BORRADA' if blur_nitida else 'NÍTIDA'}")
print(f"Borrada — métrica: {mean_borrada:.2f} | {'BORRADA' if blur_borrada else 'NÍTIDA'}")

O resultado:

Nítida  — métrica: 9.03 | NÍTIDA
Borrada — métrica: 2.30 | BORRADA

A imagem nítida retornou uma métrica de 9.03 (acima do limiar de 5), enquanto a borrada ficou em 2.30 (abaixo). O detector separou corretamente os dois casos.

Validando com blur progressivo

Para ganhar mais confiança no detector, vamos fazer um experimento: pegar a imagem nítida e aplicar borramentos gaussianos com kernels cada vez maiores, observando como a métrica decai.

kernels = list(range(1, 32, 2))  # 1, 3, 5, ..., 31

for k in kernels:
    img_blur = cv2.GaussianBlur(gray_nitida, (k, k), 0) if k > 1 else gray_nitida
    mean_val, is_blurry = detect_blur_fft(img_blur, size=60, thresh=5)
    print(f"Kernel {k:2d} — métrica: {mean_val:.2f} — {'BORRADA' if is_blurry else 'NÍTIDA'}")
Kernel  1 — métrica: 9.03 — NÍTIDA
Kernel  3 — métrica: 6.00 — NÍTIDA
Kernel  5 — métrica: 4.81 — BORRADA
Kernel  7 — métrica: 3.54 — BORRADA
...
Kernel 31 — métrica: 0.89 — BORRADA

A métrica decai monotonicamente, como esperado, cada nível de blur remove mais altas frequências. O gráfico abaixo mostra a curva completa:

Degradação da métrica FFT conforme o blur gaussiano aumenta

A linha tracejada vermelha marca o limiar. Os pontos acima são classificados como nítidos, os abaixo como borrados. A transição acontece por volta do kernel 5, o que é razoável. Com kernel 3 a imagem ainda é aceitável, mas a partir de 5 o desfoque já é perceptível.

O que acontece no espectro?

Para consolidar a intuição, vale a pena visualizar o espectro em diferentes níveis de blur. O painel abaixo mostra a imagem e seu espectro para kernels de 1 a 31:

Painel mostrando o efeito progressivo do blur no espectro de frequência

A progressão é clara: conforme o blur aumenta, o espectro se contrai em direção ao centro. Para kernel 31, praticamente toda a energia está concentrada num ponto. Essa é a assinatura visual de uma imagem severamente borrada.

FFT vs. variância do Laplaciano

O detector via FFT não é a única opção. O método mais popular e simples para detecção de blur é a variância do Laplaciano, proposta por Pech-Pacheco et al. (ICPR 2000). A ideia é aplicar o operador Laplaciano (segunda derivada) e medir a variância do resultado:

def variancia_laplaciano(image):
    """Variância do Laplaciano como métrica de nitidez."""
    return cv2.Laplacian(image, cv2.CV_64F).var()

Uma linha. É difícil ser mais simples que isso. Como as duas abordagens se comparam no experimento de blur progressivo?

Comparação normalizada entre FFT e Laplaciano sob blur progressivo

Ambas as métricas decaem com o blur, mas com comportamentos distintos. O Laplaciano cai muito mais rápido. Com kernel 5, já está perto de zero. A FFT decai mais gradualmente, mantendo sensibilidade em níveis intermediários de blur.

Isso faz sentido: a variância do Laplaciano, sendo baseada na segunda derivada, pondera desproporcionalmente as frequências mais altas. Quando o blur as atenua, a métrica despenca. Já a métrica baseada na FFT mede a magnitude média com peso uniforme nas altas frequências, o que produz um decaimento mais gradual.

Na prática, ambos funcionam bem como baseline. A escolha depende do que você precisa: o Laplaciano é mais simples, mais rápido, e não requer parâmetros (não tem equivalente ao size). A FFT oferece mais controle via tamanho da máscara e mantém sensibilidade em níveis intermediários de blur.

Quando o detector falha?

Nenhum método é perfeito. Existem cenários onde o detector FFT pode dar resultados incorretos:

  • Imagens com pouca textura: um céu limpo ou uma parede branca têm poucas altas frequências mesmo sem blur, gerando falsos positivos.
  • Blur parcial: se apenas parte da imagem está borrada (foco seletivo), a métrica global pode não capturar o problema.
  • Tipos diferentes de blur: blur de movimento atenua frequências em uma direção específica, enquanto o gaussiano atenua uniformemente. O detector trata ambos da mesma forma.
  • Compressão JPEG: artefatos de compressão introduzem altas frequências espúrias (blocos 8×8), o que pode mascarar o blur e gerar falsos negativos.
  • Ruído: imagens com ruído intenso (ISO alto, imagens médicas) apresentam altas frequências elevadas mesmo quando borradas, também gerando falsos negativos.
  • Sensibilidade ao limiar: o valor de thresh precisa ser calibrado para cada aplicação, dependendo da resolução e do conteúdo das imagens.

Para cenários mais robustos, considere análise por patches (como no paper original de Liu et al.), métricas complementares ou modelos de deep learning treinados para classificação de blur.

Takeaways

  • Blur é filtragem passa-baixas: o desfoque gaussiano atenua altas frequências, e a FFT permite visualizar e medir isso diretamente.
  • O detector é simples e eficaz: zerar o centro do espectro e medir a magnitude restante separa imagens nítidas de borradas com poucas linhas de código.
  • Máscara circular é mais adequada: a distância ao centro no espectro corresponde ao módulo da frequência espacial, e a máscara circular respeita essa geometria.
  • A variância do Laplaciano é uma alternativa prática: uma única linha de código e desempenho comparável, ideal quando você precisa de algo rápido.
  • Ambos os métodos servem como baseline: para controle de qualidade de imagens em pipelines de visão computacional, essas métricas clássicas continuam relevantes mesmo na era do deep learning.
CompartilharCompartilharEnviar
Post Anterior

Como a IA Está Ajudando Pacientes a Vencerem os Planos de Saúde nos EUA

Próximo Post

Como conseguir uma Vaga Remota nos EUA: Live com Nicole Barra

Carlos Melo

Carlos Melo

Engenheiro de Visão Computacional graduado em Ciências Aeronáuticas pela Academia da Força Aérea (AFA) e Mestre em Engenharia Aeroespacial pelo Instituto Tecnológico de Aeronáutica (ITA).

Relacionado Artigos

ViT Visual Transformer
Artigos

Vision Transformer (ViT): Implementação com Python

por Carlos Melo
março 18, 2026
Artigos

Visual Search: Como Projetar um Sistema de Busca por Imagens

por Carlos Melo
fevereiro 19, 2026
Compressão de Modelos: Pruning, Destilação e Quantização
Deep Learning

Compressão de Modelos: Pruning, Destilação e Quantização

por Carlos Melo
fevereiro 7, 2026
Como a IA Está Ajudando Pacientes a Vencerem os Planos de Saúde nos EUA
Artigos

Como a IA Está Ajudando Pacientes a Vencerem os Planos de Saúde nos EUA

por Carlos Melo
outubro 17, 2025
Por que o ChatGPT mente para você?
Artigos

Por que o ChatGPT mente para você?

por Carlos Melo
setembro 16, 2025
Próximo Post
Como conseguir uma Vaga Remota nos EUA: Live com Nicole Barra

Como conseguir uma Vaga Remota nos EUA: Live com Nicole Barra

Deixe um comentário Cancelar resposta

O seu endereço de e-mail não será publicado. Campos obrigatórios são marcados com *

Mais Populares

  • ORB-SLAM 3: Tutorial Completo para Mapeamento 3D e Localização em Tempo Real

    455 compartilhamentos
    Compartilhar 182 Tweet 114
  • Introdução ao MediaPipe e Pose Estimation

    548 compartilhamentos
    Compartilhar 219 Tweet 137
  • Vision Transformer (ViT): Implementação com Python

    3 compartilhamentos
    Compartilhar 1 Tweet 1
  • O Que é Amostragem e Quantização no Processamento de Imagens

    45 compartilhamentos
    Compartilhar 18 Tweet 11
  • Processamento de Nuvens de Pontos com Open3D e Python

    74 compartilhamentos
    Compartilhar 30 Tweet 19
  • Em Alta
  • Comentários
  • Mais Recente
Como Tratar Dados Ausentes com Pandas

Como Tratar Dados Ausentes com Pandas

agosto 13, 2019
Como usar o DALL-E 2 para gerar imagens a partir de textos

Como usar o DALL-E 2 para gerar imagens a partir de textos

dezembro 25, 2022
Introdução ao MediaPipe e Pose Estimation

Introdução ao MediaPipe e Pose Estimation

julho 15, 2023

ORB-SLAM 3: Tutorial Completo para Mapeamento 3D e Localização em Tempo Real

abril 10, 2023
Como Analisar Ações da Bolsa com Python

Como Analisar Ações da Bolsa com Python

15
Setembro Amarelo: Análise do Suicídio no Brasil, com Data Science

Setembro Amarelo: Análise do Suicídio no Brasil, com Data Science

13
Como Aprender Data Science?

Como Aprender Data Science?

9
Qual o Cenário de Data Science no Brasil hoje?

Qual o Cenário de Data Science no Brasil hoje?

8
ViT Visual Transformer

Vision Transformer (ViT): Implementação com Python

março 18, 2026

Visual Search: Como Projetar um Sistema de Busca por Imagens

fevereiro 19, 2026
Compressão de Modelos: Pruning, Destilação e Quantização

Compressão de Modelos: Pruning, Destilação e Quantização

fevereiro 7, 2026
Matemática para Machine Learning: O Guia Essencial

Matemática para Machine Learning: O Guia Essencial

janeiro 23, 2026

Seguir

    The Instagram Access Token is expired, Go to the Customizer > JNews : Social, Like & View > Instagram Feed Setting, to refresh it.
Instagram Youtube LinkedIn Twitter
Sigmoidal

O melhor conteúdo técnico de Data Science, com projetos práticos e exemplos do mundo real.

Seguir no Instagram

Categorias

  • Aeroespacial
  • Artigos
  • Blog
  • Carreira
  • Cursos
  • Data Science
  • Deep Learning
  • Destaques
  • Entrevistas
  • IA Generativa
  • Livros
  • Machine Learning
  • Notícias
  • Python
  • Teoria
  • Tutoriais
  • Visão Computacional
  • Youtube

Navegar por Tags

camera calibration carreira chatgpt cientista de dados cnn computer vision Cursos dados desbalanceados data science data science na prática decision tree deep learning deploy gpt-3 histograma IA generativa image formation inteligência artificial jupyter kaggle keras machine learning matplotlib mnist nft openai opencv overfitting pandas profissão python redes neurais redes neurais convolucionais regressão linear regressão logística salário seaborn sklearn tensorflow titanic tutorial visão computacional vídeo youtube árvore de decisão

© 2024 Sigmoidal - Aprenda Data Science, Visão Computacional e Python na prática.

Welcome Back!

Login to your account below

Forgotten Password?

Retrieve your password

Please enter your username or email address to reset your password.

Log In

Add New Playlist

Sem Resultado
Ver Todos Resultados
  • Home
  • Pós-Graduação
  • Blog
  • Sobre Mim
  • Contato
  • English

© 2024 Sigmoidal - Aprenda Data Science, Visão Computacional e Python na prática.