“Deep learning has instead given us machines with truly impressive abilities but no intelligence.”
The Book of Why, Judea Pearl
Processamento digital de imagens estuda como representar, transformar e analisar imagens para melhorar sua qualidade ou extrair informação útil. A área conecta matemática, percepção visual, computação, estatística e reconhecimento de padrões, com aplicações em medicina, sensoriamento remoto, indústria, segurança e multimídia.
Neste capítulo, o foco está na passagem entre a imagem como dado numérico e a imagem como fonte de informação: da discretização da cena aos filtros, transformadas, técnicas de realce, reconstrução tomográfica e tarefas ligadas à visão computacional.
Uma imagem digital pode ser modelada como uma função bidimensional $f(x,y)$, em que $x$ e $y$ representam as coordenadas espaciais e $f(x,y)$ registra o brilho, o nível de cinza ou a cor associados a cada pixel. Na implementação computacional, essa função é armazenada como uma matriz de valores discretos, cujo intervalo depende da profundidade de bits utilizada.
Em termos didáticos, o processamento digital de imagens costuma ser organizado em três níveis:
Filtrar uma imagem significa recalcular o valor de cada pixel com base em uma vizinhança local. Em vez de tratar cada ponto isoladamente, o algoritmo observa os pixels ao redor e combina essas informações para suavizar ruído, realçar contornos ou evidenciar estruturas. Intuitivamente, é como analisar uma fotografia com uma pequena janela deslizante, decidindo a cada posição qual valor melhor representa aquela região.
Analogia: um filtro pode ser visto como uma votação entre vizinhos. Em alguns casos, todos votam com pesos parecidos; em outros, os mais próximos ou mais parecidos têm mais influência; em outros ainda, valores extremos são ignorados para que um ruído isolado não distorça a decisão final.
Nos filtros espaciais lineares, essa combinação local é descrita por um kernel, máscara ou janela, normalmente uma matriz pequena como 3x3 ou 5x5. O kernel desliza sobre a imagem e produz uma nova saída a partir de uma soma ponderada dos pixels vizinhos. Em uma imagem em tons de cinza, uma forma clássica de escrever essa operação é:
$$ g(x,y)=\sum_{i=-a}^{a}\sum_{j=-b}^{b} h(i,j)\,f(x-i,y-j) $$Nessa expressão, $f(x,y)$ é a imagem original, $h(i,j)$ é o kernel e $g(x,y)$ é a imagem filtrada. A operação é chamada convolução. Em bibliotecas práticas, como OpenCV, a implementação costuma usar uma forma muito próxima à correlação, mas a intuição principal permanece: a saída em cada ponto depende de uma combinação ponderada da vizinhança.
Exemplo de kernel de média 3x3:
$$ \frac{1}{9} \begin{bmatrix} 1 & 1 & 1\\ 1 & 1 & 1\\ 1 & 1 & 1 \end{bmatrix} $$
Esse kernel substitui cada pixel pela média local. O efeito é de suavização, mas detalhes finos e bordas também perdem nitidez.
Uma distinção central é entre filtros lineares e não lineares. Filtros lineares produzem a saída por soma ponderada dos vizinhos; por isso, são elegantes do ponto de vista matemático, eficientes e fáceis de analisar no domínio espacial e no domínio da frequência. Já os filtros não lineares seguem regras que não podem ser reduzidas a uma combinação linear simples, mas costumam ser mais robustos em problemas reais.
Historicamente, a formulação clássica da área nasceu da teoria de sinais e favoreceu filtros lineares. Com o tempo, tornou-se claro que imagens reais exigem métodos mais adaptativos, sobretudo quando a meta é remover ruído sem apagar bordas semanticamente importantes.
| Classe | Ideia central | Vantagem | Limitação típica |
|---|---|---|---|
| Linear | Soma ponderada da vizinhança por um kernel fixo. | Fácil de implementar, analisar e acelerar. | Tende a borrar bordas junto com o ruído. |
| Não linear | Usa regras como mediana, estatísticas de ordem ou pesos dependentes da imagem. | Pode ser mais robusto a ruído impulsivo e preservar melhor contornos. | É mais difícil de analisar e, às vezes, mais caro computacionalmente. |
Do ponto de vista funcional, filtros podem ser entendidos pelo que deixam passar ou atenuam. Filtros passa-baixa reduzem variações bruscas e, por isso, suavizam ruído e detalhes finos. Filtros passa-alta fazem o oposto: enfatizam transições abruptas, sendo úteis para nitidez, realce e detecção de bordas. Em linguagem informal, passa-baixa tende a “embaçar”; passa-alta tende a “acentuar contornos”.
Essa distinção se conecta diretamente à matemática do domínio da frequência: regiões suaves correspondem, em geral, a componentes de baixa frequência espacial, enquanto ruído, textura fina e bordas estão associados a componentes de alta frequência. Essa ponte será aprofundada na seção 23.5, mas já ajuda a entender o comportamento dos filtros mais usuais.
Embora a implementação em computador use pixels e matrizes discretas, boa parte da teoria nasce em modelos contínuos. Em óptica e formação de imagens, por exemplo, a cena é tratada como uma função contínua da posição e da intensidade luminosa. Quando um sensor captura a imagem, ele amostra esse sinal contínuo para formar a grade discreta de pixels. Isso significa que vários problemas de filtragem podem ser entendidos como uma ponte entre dois mundos: o contínuo da física e o discreto da computação.
O filtro gaussiano é um bom exemplo dessa ponte. Sua origem está na função contínua $$ G(x,y)=\frac{1}{2\pi\sigma^2}e^{-\frac{x^2+y^2}{2\sigma^2}} $$ que depois é amostrada para gerar um kernel discreto. O parâmetro $\sigma$ controla a escala da suavização: valores maiores espalham mais a influência da vizinhança. De modo parecido, operadores de borda podem ser vistos como aproximações discretas de derivadas contínuas, e métodos mais sofisticados de suavização, como a difusão anisotrópica, surgem de equações diferenciais parciais. Em outras palavras, a matemática não discreta não é um enfeite teórico: ela fornece o modelo do fenômeno e orienta o desenho dos algoritmos discretos usados na prática.
O filtro da média é a forma mais simples de suavização: todos os vizinhos contribuem com o mesmo peso. Ele é didático e barato, mas borra bordas com facilidade. O filtro gaussiano melhora essa ideia ao dar maior peso aos pixels mais próximos do centro; por isso, suaviza de maneira mais natural e é uma das ferramentas clássicas mais importantes do processamento de imagens.
O filtro de mediana, por sua vez, é não linear. Em vez de calcular uma média, ele ordena os valores da vizinhança e escolhe o valor central. Isso o torna especialmente eficaz contra ruído impulsivo, como o ruído “sal e pimenta”, em que poucos pixels assumem valores muito altos ou muito baixos.
Analogia: se uma sala tem nove pessoas e uma delas dá uma opinião completamente fora do padrão, a média tende a ser puxada por esse valor extremo; a mediana, em geral, continua representando melhor o comportamento típico do grupo.
O dilema clássico da filtragem é simples de formular: o mesmo mecanismo que remove ruído também pode apagar informação importante. Em visão computacional, isso é crítico, porque bordas carregam estrutura geométrica e ajudam a delimitar objetos, superfícies e regiões de interesse.
Esse problema levou ao desenvolvimento de filtros adaptativos e preservadores de borda. Entre os marcos históricos mais conhecidos estão a difusão anisotrópica, proposta por Perona e Malik em 1990, e o filtro bilateral, popularizado por Tomasi e Manduchi em 1998. Ambos tentam suavizar dentro de regiões homogêneas sem misturar intensidades muito diferentes ao atravessar bordas. Em 2010, o guided filter de He, Sun e Tang ofereceu uma alternativa eficiente ao bilateral, com bom comportamento próximo a contornos e custo linear no número de pixels.
Esse tema continua atual. Mesmo com o avanço de técnicas baseadas em aprendizado profundo, filtros clássicos seguem relevantes como pré-processamento, como blocos interpretáveis em pipelines híbridos e como fundamento conceitual para entender operações convolucionais em redes neurais.
Em código, um filtro passa-baixa costuma aparecer como uma suavização por média ou por gaussiana. No exemplo abaixo, o objetivo é reduzir ruído e pequenas oscilações locais:
import cv2
img = cv2.imread("imagem.png", cv2.IMREAD_GRAYSCALE)
# Passa-baixa: suaviza ruído e pequenas variações
passa_baixa_media = cv2.blur(img, (5, 5))
passa_baixa_gauss = cv2.GaussianBlur(img, (5, 5), 1.2)
Nesse caso, `blur` usa uma média simples, enquanto `GaussianBlur` usa pesos maiores perto do centro da janela. Os dois reduzem altas frequências, mas o gaussiano tende a introduzir menos artefatos e por isso é muito mais comum em pipelines reais.
Já um filtro passa-alta procura enfatizar transições intensas. Uma forma simples de fazer isso é usar derivadas discretas, como Sobel ou Laplaciano:
import cv2
img = cv2.imread("imagem.png", cv2.IMREAD_GRAYSCALE)
# Passa-alta: destaca mudanças abruptas de intensidade
grad_x = cv2.Sobel(img, cv2.CV_32F, 1, 0, ksize=3)
grad_y = cv2.Sobel(img, cv2.CV_32F, 0, 1, ksize=3)
laplace = cv2.Laplacian(img, cv2.CV_32F, ksize=3)
Aqui, `grad_x` e `grad_y` medem variações horizontais e verticais, enquanto o Laplaciano resume mudanças de segunda ordem e costuma responder fortemente em contornos e detalhes finos. Em tarefas de detecção de borda, esses operadores frequentemente são aplicados depois de uma suavização inicial, justamente para diminuir a sensibilidade ao ruído.
Filtros exigem também decisões de implementação. Uma delas é o tratamento das bordas da imagem, já que o kernel ultrapassa os limites do domínio ao se aproximar das extremidades. Bibliotecas costumam resolver isso com estratégias como repetição, reflexão ou preenchimento constante. Esse detalhe altera a resposta do filtro e pode introduzir artefatos visíveis.
Outro ponto importante é que alguns filtros são separáveis, como o gaussiano, o que permite aplicar primeiro em uma direção e depois na outra, reduzindo custo computacional. Em contrapartida, filtros adaptativos mais sofisticados, como bilateral e guided filter, exigem mais cuidado com desempenho, especialmente em vídeo, robótica e dispositivos móveis.
Métodos de espaço de estados entram em cena quando a imagem deixa de ser analisada quadro a quadro, isoladamente, e passa a ser tratada como parte de um processo dinâmico. Em vídeos, rastreamento, estabilização e reconstrução temporal, o objetivo raramente é apenas interpretar uma imagem estática: queremos estimar como um objeto se move, como sua posição evolui, quais medições são confiáveis e como combinar observações ruidosas ao longo do tempo.
Analogia: imagine acompanhar um carro em uma estrada com neblina usando um GPS impreciso. Em alguns instantes, a medição é ruidosa; em outros, o carro fica parcialmente oculto. Um modelo de espaço de estados combina o que se espera do movimento com aquilo que foi medido, em vez de confiar cegamente em uma única leitura.
A ideia central é representar o sistema por um vetor de estado, isto é, um conjunto pequeno de variáveis que resume o que realmente importa para o problema. Em visão computacional, esse estado pode conter posição, velocidade, orientação, escala, profundidade ou até parâmetros de iluminação. A câmera, porém, não observa diretamente esse estado ideal: ela fornece medidas parciais e ruidosas, como coordenadas detectadas na imagem, caixas delimitadoras ou pontos-chave extraídos por um detector.
No caso discreto, a formulação mais comum é:
$$ x_k = A x_{k-1} + B u_{k-1} + w_{k-1} $$ $$ z_k = H x_k + v_k $$O vetor $x_k$ representa o estado no instante $k$, $z_k$ representa a medição observada, $A$ modela a dinâmica do sistema, $H$ relaciona estado e observação, e os termos $w_k$ e $v_k$ representam ruído de processo e ruído de medição. A interpretação é importante: o primeiro modelo diz como o sistema evolui; o segundo diz como a câmera ou o sensor enxerga esse sistema.
Exemplo matemático: movimento com velocidade aproximadamente constante
Se quisermos rastrear um ponto 2D, um estado simples pode ser
$$ x_k = \begin{bmatrix} p_x\\ p_y\\ v_x\\ v_y \end{bmatrix}, \qquad z_k = \begin{bmatrix} p_x\\ p_y \end{bmatrix} $$
e a dinâmica, para intervalo de tempo $\Delta t$, pode ser modelada por
$$ A = \begin{bmatrix} 1 & 0 & \Delta t & 0\\ 0 & 1 & 0 & \Delta t\\ 0 & 0 & 1 & 0\\ 0 & 0 & 0 & 1 \end{bmatrix}, \qquad H = \begin{bmatrix} 1 & 0 & 0 & 0\\ 0 & 1 & 0 & 0 \end{bmatrix} $$
Ou seja, o modelo interno estima posição e velocidade, mas a câmera mede apenas posição. Essa diferença entre “o que o sistema é” e “o que o sensor vê” é exatamente o coração do método.
Os algoritmos de espaço de estados funcionam em ciclo. Primeiro, produzem uma predição do próximo estado com base no histórico; depois, recebem uma nova medição; por fim, fazem a correção da estimativa. A diferença entre a medição recebida e a medição prevista pelo modelo é chamada de inovação ou residual. Se essa diferença é pequena, o modelo estava coerente; se é grande, algo inesperado ocorreu, como uma oclusão, uma mudança brusca de direção ou uma medição ruim.
Didaticamente, pode-se pensar assim: o modelo faz uma aposta sobre onde o objeto estará no próximo quadro, e a imagem fornece uma evidência para revisar essa aposta. O problema inteiro é um equilíbrio entre previsão e observação.
Essa formulação tem raízes na teoria clássica de filtragem e controle. O problema foi reformulado de modo decisivo por Kalman em 1960, ao tratar a estimação a partir do ponto de vista de estados dinâmicos e atualização recursiva. Em 1961, Kalman e Bucy desenvolveram a versão contínua do problema, hoje conhecida como filtro de Kalman-Bucy, importante quando o modelo físico é descrito por equações diferenciais no tempo contínuo.
No contexto de visão computacional, a ideia se consolidou em rastreamento e navegação visual. Quando os modelos lineares e gaussianos se mostraram insuficientes para movimentos não lineares, ambiguidade ou múltiplas hipóteses simultâneas, surgiram extensões como o filtro de Kalman estendido, variantes bayesianas mais gerais e, em especial, os filtros de partículas. Um marco importante foi o algoritmo CONDENSATION, de Isard e Blake, em 1998, pensado para rastrear curvas em ambientes visuais complexos. Mais tarde, em 2016, o rastreador SORT mostrou como um modelo simples com filtro de Kalman e associação de dados ainda podia ser extremamente competitivo em rastreamento em tempo real.
O filtro de Kalman clássico supõe dinâmica linear e ruídos gaussianos. Isso funciona muito bem em muitos cenários de rastreamento suave, mas há limites claros. Em visão, objetos aceleram, mudam de escala, desaparecem temporariamente, cruzam uns com os outros e podem gerar múltiplas hipóteses plausíveis. Nesses casos, um modelo linear simples pode ficar otimista demais ou incapaz de representar a verdadeira incerteza.
Quando a dinâmica é não linear, uma estratégia frequente é linearizar localmente o modelo, levando ao filtro de Kalman estendido. Outra opção é propagar pontos de amostragem mais informativos, como no filtro de Kalman unscented. Se a distribuição da incerteza deixa de ser aproximadamente gaussiana ou se há hipóteses múltiplas, filtros de partículas tornam-se especialmente úteis, pois representam a distribuição por um conjunto de amostras ponderadas.
Embora muitos algoritmos sejam implementados em tempo discreto, a modelagem frequentemente começa em tempo contínuo. Em física do movimento, por exemplo, aceleração, velocidade e posição são descritas por equações diferenciais; em navegação visual e fusão com sensores inerciais, o estado pode obedecer a sistemas contínuos do tipo
$$ \dot{x}(t)=F x(t)+G w(t) $$e as observações visuais aparecem apenas em instantes amostrados, quando um quadro é capturado. Essa mistura de dinâmica contínua e observação discreta é muito comum em aplicações reais. Mais uma vez, o ponto não é apenas matemático: a teoria contínua ajuda a escolher modelos fisicamente coerentes, enquanto a forma discreta permite computação eficiente quadro a quadro.
O exemplo abaixo mostra um filtro de Kalman simples para rastrear a posição 2D de um ponto detectado em vídeo. O estado possui posição e velocidade; a medição contém apenas posição:
import cv2
import numpy as np
dt = 1.0
kf = cv2.KalmanFilter(4, 2)
kf.transitionMatrix = np.array([
[1, 0, dt, 0],
[0, 1, 0, dt],
[0, 0, 1, 0],
[0, 0, 0, 1],
], dtype=np.float32)
kf.measurementMatrix = np.array([
[1, 0, 0, 0],
[0, 1, 0, 0],
], dtype=np.float32)
kf.processNoiseCov = 1e-3 * np.eye(4, dtype=np.float32)
kf.measurementNoiseCov = 1e-2 * np.eye(2, dtype=np.float32)
kf.errorCovPost = np.eye(4, dtype=np.float32)
medicao = np.array([[120.0], [80.0]], dtype=np.float32)
predicao = kf.predict()
estimativa = kf.correct(medicao)
Nesse código, `predict()` avança o estado estimado segundo o modelo de movimento, e `correct()` ajusta essa predição com base na medição. Em uma aplicação real, isso ocorre continuamente a cada quadro do vídeo, com novas detecções entrando no ciclo.
Hoje, métodos de espaço de estados continuam muito presentes em rastreamento por detecção, fusão entre câmera e IMU, estabilização de vídeo, robótica móvel, veículos autônomos e análise médica temporal. Em muitos sistemas modernos, redes neurais cuidam da detecção ou da extração de atributos, enquanto o modelo de espaço de estados impõe coerência temporal e ajuda a lidar com ruído, quadros perdidos e oclusões.
Os principais desafios atuais incluem associação de múltiplos objetos, mudanças bruscas de dinâmica, incerteza mal calibrada, alta taxa de quadros, execução embarcada e combinação entre modelos físicos interpretáveis e modelos aprendidos. Em rastreamento multiobjeto, por exemplo, a dificuldade não é apenas prever o próximo estado, mas decidir qual detecção pertence a qual trajetória.
Esta seção se conecta fortemente com álgebra linear, probabilidade, estatística bayesiana e equações diferenciais. Matrizes descrevem transições de estado e modelos de observação; covariâncias modelam incerteza; distribuições gaussianas permitem soluções fechadas; e a forma contínua do problema leva a sistemas diferenciais e à equação de Riccati. Em termos aplicados, os métodos de espaço de estados são um encontro direto entre modelagem matemática e inferência recursiva.
Percepção visual humana interessa ao processamento de imagens porque a meta de muitos algoritmos não é apenas alterar números em uma matriz, mas produzir resultados que pareçam mais nítidos, mais naturais ou mais informativos para um observador. Em outras palavras, entender como o sistema visual funciona ajuda a explicar por que certos realces melhoram uma imagem, por que alguns artefatos incomodam mais do que outros e por que a mesma cena pode parecer diferente dependendo do contexto.
Analogia: o olho humano não funciona como uma câmera passiva que apenas registra luz. Ele se parece mais com um sistema completo de captura e interpretação: uma lente forma a imagem, a retina faz parte do pré-processamento, e o cérebro reconstrói significado usando contexto, memória e expectativa.
A visão humana opera sobre uma faixa estreita do espectro eletromagnético, aproximadamente entre 380 e 700 nanômetros. Isso significa que vemos apenas uma pequena parte das radiações presentes no ambiente. Ondas de rádio, micro-ondas, infravermelho, ultravioleta, raios X e raios gama existem fisicamente, mas não produzem sensação visual direta. Essa limitação é importante em computação porque muitos sistemas de imageamento trabalham justamente fora da faixa visível, como câmeras térmicas, radiografias e sensores multiespectrais.
Quando a luz entra no olho, ela atravessa córnea, pupila e cristalino até formar uma imagem sobre a retina. Ali ocorre a fototransdução: energia luminosa é convertida em sinais eletroquímicos. Esse processo já não é apenas óptico, mas neurobiológico. A retina extrai contraste, movimento e variações locais antes mesmo de a informação chegar ao córtex visual.
A retina contém dois tipos principais de fotorreceptores: bastonetes e cones. Bastonetes são mais sensíveis em baixa iluminação e dominam a visão noturna, mas oferecem baixa resolução espacial e praticamente nenhuma discriminação de cor. Cones exigem mais luz, porém sustentam alta acuidade e visão cromática. A distribuição deles não é uniforme: a fóvea, no centro da retina, concentra cones e é especializada em detalhes finos, enquanto a periferia visual privilegia sensibilidade e detecção de movimento.
Essa organização explica uma observação cotidiana importante: enxergamos muito melhor no ponto em que fixamos o olhar do que nas extremidades do campo visual. Esse fato, que parece apenas biológico, tem impacto computacional direto. Técnicas modernas como foveated rendering em realidade virtual exploram justamente a queda de acuidade na periferia para renderizar menos detalhes fora da região observada.
Nem toda informação visual é percebida com a mesma facilidade. O sistema visual responde muito bem a bordas, contraste local e variações estruturais, mas é menos confiável em iluminação muito baixa, em texturas extremamente finas ou em cenas com grande faixa dinâmica. Por isso, em processamento de imagens, melhorar uma imagem não significa necessariamente aumentar todos os valores de intensidade; muitas vezes significa reorganizar contraste e luminância de modo coerente com a percepção humana.
Exemplo matemático: uma medida simples de contraste para um alvo sobre fundo uniforme é o contraste de Weber
$$ C_W = \frac{L_{obj}-L_{fundo}}{L_{fundo}} $$
em que $L_{obj}$ é a luminância do objeto e $L_{fundo}$ é a luminância do fundo. Já o tamanho aparente de um detalhe pode ser aproximado por um ângulo visual
$$ \theta \approx 2\arctan\left(\frac{s}{2d}\right) $$
em que $s$ é o tamanho físico do detalhe e $d$ é a distância ao observador. Essas grandezas ajudam a explicar por que um mesmo objeto pode ser nítido em uma tela grande e quase invisível em uma miniatura.
Outro ponto central é a adaptação luminosa. O sistema visual consegue operar em faixas muito diferentes de iluminação, do ambiente noturno a cenas ensolaradas, mas não faz isso com a mesma sensibilidade em todos os níveis. Isso ajuda a entender por que técnicas como correção de gama, equalização de histograma e mapeamento de tons fazem sentido: elas tentam redistribuir valores da imagem para aproximar a representação computacional do que efetivamente conseguimos perceber.
A percepção de cor também não depende apenas do comprimento de onda que chega ao olho. Ela emerge da combinação entre três famílias de cones, do processamento oponente entre canais cromáticos e da influência do contexto visual. Por isso, a cor percebida de um objeto pode parecer relativamente estável mesmo quando a iluminação muda, fenômeno conhecido como constância de cor. Em termos didáticos, o cérebro tenta responder não apenas "quanta luz chegou?", mas "que material ou superfície provavelmente gerou esse padrão de luz?".
Algo semelhante ocorre com profundidade e forma. A percepção espacial combina disparidade binocular, perspectiva linear, oclusão, sombra e paralaxe de movimento. Nenhuma pista isolada resolve todos os casos; a robustez nasce da fusão de evidências. Essa mesma ideia reaparece em visão computacional quando diferentes atributos ou sensores são combinados para reduzir ambiguidade.
Uma forma útil de organizar o problema é distinguir processamento bottom-up e top-down. No primeiro caso, a interpretação é guiada pelos estímulos disponíveis, como contraste, orientação, cor ou movimento. No segundo, conhecimento prévio, expectativa, tarefa e atenção influenciam o que será percebido como relevante. Isso explica por que ilusões visuais são tão instrutivas: elas mostram que enxergar não é copiar o mundo, mas inferi-lo.
Historicamente, a ciência da visão foi moldada por várias ideias complementares. Newton mostrou que a luz branca pode ser decomposta em espectro. No século XIX, Young e Helmholtz formularam a teoria tricromática, segundo a qual a visão de cor depende de três tipos de receptores. Pouco depois, Hering destacou a importância de mecanismos oponentes, antecipando a ideia de canais cromáticos antagônicos. No século XX, Hubel e Wiesel mostraram que neurônios do córtex visual respondem seletivamente a orientações e estruturas locais, consolidando a noção de que a visão extrai características em vários níveis.
Do ponto de vista computacional, vários modelos perceptuais começam por luminância e contraste local. O exemplo abaixo calcula luminância aproximada, contraste de Weber entre duas regiões e uma correção simples de gama:
import cv2
import numpy as np
img = cv2.imread("imagem.png").astype(np.float32) / 255.0
# Luminância aproximada no padrão sRGB
luminancia = 0.2126 * img[:, :, 2] + 0.7152 * img[:, :, 1] + 0.0722 * img[:, :, 0]
# Contraste de Weber entre um objeto e um fundo escolhidos
L_obj = np.mean(luminancia[80:140, 120:180])
L_fundo = np.mean(luminancia[20:80, 20:80])
contraste_weber = (L_obj - L_fundo) / max(L_fundo, 1e-6)
# Correção de gama simples para aproximar percepção em exibição
gamma = 2.2
ajustada = np.power(img, 1.0 / gamma)
Esse tipo de operação aparece em pipelines de exibição, compressão e avaliação perceptual. A ideia não é modelar toda a neurobiologia da visão, mas capturar aproximações úteis de como brilho e contraste se tornam visivelmente relevantes.
Amostragem e quantização ganharam importância decisiva quando a imagem deixou de ser apenas fenômeno óptico e passou a ser tratada como dado computacional. A teoria da amostragem se consolidou ao longo do século XX com contribuições de Nyquist, Whittaker, Kotelnikov e Shannon, inicialmente no contexto de sinais e telecomunicações. Quando sensores, scanners, câmeras digitais e monitores se tornaram onipresentes, essas ideias passaram a determinar diretamente a qualidade visual, o custo de armazenamento e a estabilidade dos algoritmos de visão computacional.
Nesse contexto, amostragem e quantização são as duas decisões fundamentais que transformam uma cena contínua em uma imagem digital. A primeira decide onde medir o sinal luminoso; a segunda decide com quantos níveis cada medida será armazenada. Em termos práticos, isso significa escolher a malha espacial dos pixels e a precisão numérica com que brilho ou cor serão representados.
Analogia: imagine desenhar uma paisagem em papel quadriculado. A amostragem corresponde ao tamanho dos quadrados do papel: quanto menores, mais detalhes cabem. A quantização corresponde ao número de tons disponíveis no lápis ou na paleta: quanto mais níveis, mais suave e fiel será a transição entre claro e escuro.
Na formação de imagens, a cena física é melhor modelada como uma função contínua de posição e intensidade luminosa. O sensor, porém, não armazena essa função inteira: ele mede valores em pontos ou pequenas áreas discretas. Uma forma compacta de escrever essa passagem é
$$ f[m,n] = Q\left\{\, g(m\Delta x, n\Delta y) \,\right\} $$
em que $g(x,y)$ representa a imagem contínua, $\Delta x$ e $\Delta y$ definem o espaçamento da malha de amostragem e $Q\{\cdot\}$ representa a quantização. Essa expressão resume uma ideia central do capítulo: a imagem digital nasce de uma combinação entre geometria do sensor e aproximação numérica.
A taxa de amostragem espacial determina quantos pixels são usados para descrever uma região da cena. Em geral, mais amostras permitem preservar mais detalhes finos. No entanto, há um limite importante: se a cena contiver variações espaciais mais rápidas do que a malha consegue registrar, surgem artefatos de aliasing, como serrilhados, padrões falsos e efeito moiré. Em linguagem visual, o sistema passa a “inventar” estruturas que não estavam presentes daquela forma na cena original. Esse problema continua muito atual em sensores pequenos de celulares, em texturas repetitivas de roupas e fachadas e em pipelines de redimensionamento usados antes de classificação por redes neurais.
Esse problema se conecta diretamente ao teorema de Nyquist-Shannon. Em termos intuitivos, para representar sem ambiguidades um padrão oscilatório, a frequência de amostragem precisa ser pelo menos duas vezes maior que a maior frequência espacial presente no sinal. Quando isso não é possível, aplica-se antes uma filtragem passa-baixa, chamada anti-aliasing, para remover parte dos detalhes que não poderão ser representados de forma estável.
Exemplo matemático: se um sensor mede intensidades ao longo de uma linha com passo espacial $\Delta x$, a frequência de Nyquist é
$$ f_N = \frac{1}{2\Delta x} $$
Logo, padrões cuja frequência espacial exceda $f_N$ podem ser confundidos com frequências mais baixas no sinal digital. É por isso que tecidos listrados, grades e fachadas repetitivas frequentemente produzem moiré em câmeras e vídeos.
Se a amostragem responde pela posição dos pixels, a quantização responde pelos valores possíveis em cada pixel. Uma imagem em escala de cinza com $b$ bits possui, em princípio, $L = 2^b$ níveis disponíveis. Assim, 8 bits produzem 256 níveis, 10 bits produzem 1024 e 16 bits produzem 65.536. Quanto maior a profundidade de bits, menor tende a ser o erro de arredondamento e mais suaves ficam gradientes e transições tonais.
Uma forma simples de modelar o passo de quantização é
$$ \Delta = \frac{I_{\max} - I_{\min}}{L-1} $$
em que $I_{\max}$ e $I_{\min}$ delimitam a faixa dinâmica representada e $L$ é o número de níveis. A diferença entre o valor original e o valor quantizado é chamada erro de quantização. Quando os níveis são poucos, esse erro pode se tornar visível como banding, isto é, faixas artificiais em regiões que deveriam variar suavemente. Em imagens médicas, científicas e astronômicas, esse ponto é crítico: profundidade de bits insuficiente pode ocultar sutilezas relevantes, mesmo quando a resolução espacial é alta.
Em alguns cenários, adiciona-se dithering para distribuir visualmente esse erro e reduzir a percepção de bandas. A ideia parece contraintuitiva, porque envolve inserir um padrão controlado de ruído, mas funciona bem justamente por trocar erro estruturado por um erro menos perceptível ao observador. Em compressão, exibição em telas limitadas e conversão para paletas pequenas, essa técnica ainda aparece com frequência.
Na prática, amostragem aparece o tempo todo em redimensionamento de imagens. Ao reduzir uma imagem, não basta “pular pixels”; é preciso escolher como combinar informações da vizinhança e como evitar aliasing. Ao ampliar, não basta duplicar pontos; é preciso interpolar valores intermediários. Por isso, bibliotecas de visão computacional oferecem diferentes estratégias, como vizinho mais próximo, bilinear, bicúbica e métodos específicos para redução, como `INTER_AREA` no OpenCV. Em aplicações modernas, essa escolha afeta diretamente treinamento e inferência de modelos de visão, porque mudanças no método de reamostragem alteram textura, contorno e estatísticas locais.
Didaticamente, pode-se pensar assim: reduzir uma imagem é decidir o que pode ser descartado com o menor dano possível; ampliar uma imagem é decidir como preencher lacunas de forma plausível. Nenhum método cria informação nova de fato, mas alguns preservam melhor contornos, textura e estabilidade visual.
O exemplo abaixo combina redução espacial com anti-aliasing e quantização de paleta. Ele ilustra duas decisões comuns: como reduzir uma imagem evitando aliasing e como limitar o número de cores controlando o uso de dithering.
import cv2
from PIL import Image
# Leitura com OpenCV
img_bgr = cv2.imread("imagem.png")
# Suavização prévia para reduzir altas frequências antes da redução
suave = cv2.GaussianBlur(img_bgr, (5, 5), 1.0)
# Redução espacial: OpenCV recomenda INTER_AREA para encolher imagens
reduzida = cv2.resize(suave, None, fx=0.5, fy=0.5, interpolation=cv2.INTER_AREA)
# Conversão para Pillow para demonstrar quantização de paleta
img_rgb = cv2.cvtColor(reduzida, cv2.COLOR_BGR2RGB)
pil_img = Image.fromarray(img_rgb)
# Quantização com 32 cores, com e sem dithering
quantizada = pil_img.quantize(colors=32, dither=Image.Dither.FLOYDSTEINBERG)
quantizada_sem_dither = pil_img.quantize(colors=32, dither=Image.Dither.NONE)
quantizada.save("imagem_32_cores.png")
quantizada_sem_dither.save("imagem_32_cores_sem_dither.png")
Nesse exemplo, a suavização gaussiana reduz parte das altas frequências antes do reamostramento; `INTER_AREA` produz uma redução mais estável; e a quantização com e sem dithering mostra a diferença entre um erro mais distribuído e um banding mais visível.
Esta seção se conecta com análise de sinais, séries e transformadas de Fourier, teorema de amostragem, convolução, interpolação, erro de aproximação e estatística do ruído de quantização. Também dialoga com geometria do sensor, já que o espaçamento entre amostras define a resolução espacial disponível para reconstruir a cena.
Transformadas de imagens surgem quando a representação por pixels deixa de ser a forma mais conveniente de pensar o problema. Em muitos casos, a imagem no domínio espacial esconde estruturas que ficam mais claras em outro domínio: frequências ajudam a separar borramento e ruído periódico; parâmetros geométricos ajudam a detectar linhas e círculos; projeções ajudam a formular reconstrução tomográfica; decomposições em múltiplas escalas ajudam a separar estrutura global e detalhe local.
Historicamente, essa ideia atravessa boa parte da matemática aplicada moderna. Fourier abriu o caminho ao representar sinais como soma de frequências. Radon, em 1917, formalizou integrais ao longo de retas, hoje essenciais em tomografia. Hough, nos anos 1960, levou a mudança de representação para o reconhecimento de formas. A partir dos anos 1970 e 1980, DCT e wavelets ganharam destaque com compressão digital, análise multirresolução e processamento eficiente em larga escala.
Analogia: aplicar uma transformada é como trocar o tipo de mapa usado para estudar uma cidade. Um mapa de ruas é ótimo para navegação local; um mapa de metrô destaca conexões; um mapa topográfico destaca relevo. A cidade é a mesma, mas cada representação torna certas perguntas mais fáceis de responder.
Do ponto de vista matemático, pode-se escrever uma transformada como
$$ G = T\{F\} $$
em que $F$ representa a imagem original, $T$ é a transformação aplicada e $G$ é a nova representação. Diferentemente de operações pontuais simples, a saída não precisa ter a mesma interpretação dos pixels originais: ela pode representar frequências, parâmetros geométricos, projeções ou coeficientes em múltiplas escalas. Em muitos casos, existe também uma transformação inversa, que reconstrói a imagem a partir dessa representação transformada.
A família de Fourier é a porta de entrada mais clássica. A ideia central é decompor a imagem em componentes oscilatórias de diferentes frequências. Em imagens, baixas frequências costumam capturar iluminação e estrutura global, enquanto altas frequências concentram bordas, textura fina e ruído. Essa mudança de domínio é especialmente útil quando o problema é mais simples de descrever em termos espectrais do que em termos de pixels individuais.
Para uma imagem discreta $f[x,y]$ de tamanho $M \times N$, a Transformada Discreta de Fourier bidimensional pode ser escrita como
$$ F[u,v] = \sum_{x=0}^{M-1}\sum_{y=0}^{N-1} f[x,y]\, e^{-j2\pi\left(\frac{ux}{M}+\frac{vy}{N}\right)} $$
Essa fórmula mostra que cada coeficiente $F[u,v]$ mede a contribuição de uma frequência espacial específica. Em termos didáticos, é como perguntar: “quanto desta imagem se comporta como uma ondulação horizontal e vertical de certa periodicidade?”
Na prática, o cálculo direto da DFT é caro, e por isso usa-se quase sempre a FFT (Fast Fourier Transform), que reduz drasticamente o custo computacional. Isso mantém Fourier muito atual: FFTs ainda aparecem em correlação por fase, remoção de ruído periódico, deconvolução, restauração, filtros espectrais e até em acelerações internas de convoluções usadas por bibliotecas numéricas.
Há, porém, uma limitação conceitual importante: a base de Fourier é global. Cada coeficiente “enxerga” a imagem inteira. Isso funciona muito bem para periodicidade e análise espectral, mas é menos natural quando o interesse principal está em eventos localizados, como descontinuidades curtas ou estruturas que mudam de escala ao longo da imagem.
Exemplo matemático: se um ruído periódico aparece como listras quase regulares em uma imagem, ele tende a gerar picos concentrados em certas frequências do espectro. Ao atenuar apenas esses coeficientes e aplicar a transformada inversa, é possível reduzir esse ruído com mais controle do que no domínio espacial.
A Transformada Discreta do Cosseno (DCT) pode ser vista como uma parente da DFT adaptada a sinais reais, usando apenas cossenos. Na prática, ela se tornou extremamente importante porque tende a concentrar grande parte da energia visual de uma imagem em poucos coeficientes, especialmente nos de baixa frequência. Esse fenômeno de compactação de energia é uma das razões do sucesso da DCT em compressão de imagens e vídeo.
Em JPEG, por exemplo, a imagem é dividida em blocos pequenos, tradicionalmente 8x8. Cada bloco é transformado, e então seus coeficientes de alta frequência podem ser quantizados com mais agressividade, já que muitos deles têm menor impacto perceptual. O coeficiente de mais baixa frequência, frequentemente chamado de DC, representa a média do bloco; os demais descrevem variações espaciais progressivamente mais rápidas.
Esse processo resolve um problema muito moderno: reduzir armazenamento e largura de banda sem destruir completamente a qualidade visual. Ao mesmo tempo, ele introduz artefatos característicos, como blocagem e perda de textura fina quando a quantização é excessiva. Mesmo em uma era de codecs mais sofisticados e modelos aprendidos de compressão, a DCT continua sendo referência conceitual e técnica.
Wavelets surgem justamente para responder a uma limitação da análise puramente global. Em vez de descrever a imagem apenas em frequências, elas descrevem também onde os detalhes aparecem e em que escala aparecem. Por isso, a transformada wavelet é especialmente útil quando a imagem contém estruturas localizadas, transientes ou detalhes relevantes em múltiplos níveis de resolução.
Em uma decomposição 2D simples, a imagem é separada em um bloco de aproximação e três blocos de detalhe: horizontal, vertical e diagonal. Esse tipo de organização favorece compressão, denoising e análise multiescala. Em vez de perguntar apenas “quais frequências existem?”, a wavelet pergunta também “em que região e em que escala elas se manifestam?”.
Essa ideia continua atual em compressão, restauração e análise científica. Métodos multirresolução ajudam quando uma imagem contém tanto estruturas largas quanto detalhes sutis, e por isso aparecem em processamento médico, sensoriamento remoto e modelos híbridos com aprendizado profundo. Em compensação, o custo conceitual é maior: interpretar coeficientes wavelet requer mais intimidade com bases, filtros e sub-bandas do que interpretar uma simples imagem de pixels.
A transformada de Hough muda o tipo de pergunta. Em vez de decompor intensidades em frequências, ela transforma evidências locais da imagem em votos para hipóteses geométricas. No caso clássico de linhas, cada ponto de borda $(x,y)$ vota em todas as retas que poderiam passar por ele, usualmente parametrizadas por $(\rho,\theta)$ para evitar o problema de inclinações infinitas.
Essa mudança leva a um espaço de parâmetros, também chamado acumulador. Picos nesse espaço indicam hipóteses geométricas fortemente apoiadas pelos dados. Didaticamente, é como trocar uma coleção de pistas locais por uma eleição global: uma linha real aparece quando muitos pontos independentes “concordam” no mesmo par de parâmetros.
A Hough continua muito útil para detecção de retas, círculos e formas simples em visão industrial, robótica e análise documental. O desafio moderno é o custo: acumuladores grandes, ruído e múltiplas hipóteses podem tornar a técnica pesada. Por isso, variantes probabilísticas e estratégias guiadas por detectores aprendidos são comuns em pipelines atuais.
A transformada de Radon é outra mudança de representação, mas agora baseada em integrais ao longo de retas. Em vez de perguntar qual é a frequência dominante ou que forma geométrica recebeu mais votos, ela pergunta qual é a soma acumulada de intensidades ao longo de um feixe de direção fixa. Ao variar o ângulo, obtemos um conjunto de projeções que forma o sinograma.
Essa representação é central em tomografia computadorizada e aparece novamente, com mais detalhe, na seção 23.8. Aqui, o mais importante é a intuição: uma imagem 2D pode ser descrita indiretamente por muitas projeções 1D, e a reconstrução depende de inverter essa relação de maneira numericamente estável. O problema moderno aparece quando há poucos ângulos, ruído alto ou limitações de dose de radiação, levando a cenários de limited-angle tomography e reconstrução iterativa.
O exemplo abaixo ilustra dois usos distintos de transformadas: análise espectral por FFT e detecção geométrica por Hough.
import cv2
import numpy as np
from skimage.transform import hough_line, hough_line_peaks
img = cv2.imread("imagem.png", cv2.IMREAD_GRAYSCALE)
# FFT 2D: espectro de magnitude
F = np.fft.fft2(img)
F_shift = np.fft.fftshift(F)
espectro = np.log1p(np.abs(F_shift))
# Hough de linhas: primeiro detecta bordas, depois vota no espaço de parâmetros
bordas = cv2.Canny(img, 80, 160)
hspace, angulos, distancias = hough_line(bordas)
acumulados = list(hough_line_peaks(hspace, angulos, distancias))
Nesse código, o espectro mostra como a energia da imagem se distribui em frequências espaciais, enquanto a transformada de Hough procura picos que correspondem a linhas prováveis. O contraste entre os dois casos é didático: em um, a pergunta é espectral; no outro, geométrica.
Esta seção se conecta com álgebra linear, números complexos, bases ortogonais, séries e transformadas de Fourier, integração ao longo de retas, mudança de variáveis, convolução, filtragem e análise multiescala. Também dialoga com otimização numérica e reconstrução inversa, especialmente quando a transformada é usada não só para analisar a imagem, mas para reconstruí-la a partir de medições indiretas.
Realce de imagens reúne técnicas cujo objetivo é tornar certas informações mais visíveis, mais discrimináveis ou mais agradáveis ao observador, sem necessariamente alterar o conteúdo semântico da cena. Historicamente, a ideia vem da fotografia química, da radiologia e da interpretação visual de sinais científicos: antes mesmo da fotografia digital, já se buscava controlar contraste, brilho e gradação tonal para revelar estruturas pouco visíveis. No ambiente computacional, esse problema ganhou nova escala, porque agora o realce precisa atender não só ao olho humano, mas também a pipelines automáticos de segmentação, detecção e reconhecimento.
É importante distinguir realce de restauração. No realce, o objetivo principal é melhorar a apresentação ou a utilidade visual da imagem segundo um critério prático. Na restauração, o objetivo é aproximar a imagem observada de um modelo ideal do sinal original. Em outras palavras, realce pergunta “como tornar isso mais legível ou mais útil?”; restauração pergunta “como recuperar o que foi degradado?”.
Analogia: realçar uma imagem é como ajustar a iluminação de uma vitrine. Os objetos continuam os mesmos, mas a forma de distribuí-los à luz muda quais detalhes saltam aos olhos e quais passam despercebidos.
As formas mais simples de realce atuam diretamente sobre os valores dos pixels por meio de uma transformação global. Um modelo clássico é o ajuste linear
$$ g(x,y)=\alpha f(x,y)+\beta $$
em que $\alpha$ controla ganho de contraste e $\beta$ desloca brilho. Se $\alpha > 1$, as diferenças de intensidade são ampliadas; se $\beta > 0$, a imagem tende a ficar mais clara. Essa transformação é simples, rápida e ainda muito usada em câmeras, scanners, visualização médica e pré-processamento de dados para visão computacional.
O problema moderno aparece quando a cena é heterogênea: um único ajuste global pode melhorar uma região e piorar outra. Em imagens de vigilância, por exemplo, elevar brilho para revelar pessoas em sombra pode saturar faróis, janelas ou céu. Em imagens científicas, um ganho excessivo pode amplificar não apenas sinal útil, mas também ruído do sensor.
Nem todo realce é bem modelado por uma reta. Em muitos casos, interessa comprimir ou expandir seletivamente regiões escuras ou claras da escala tonal. A correção de gama é uma das transformações não lineares mais clássicas:
$$ s = c\,r^{\gamma} $$
em que $r$ é a intensidade de entrada normalizada, $s$ é a saída, $c$ é um fator de ganho e $\gamma$ controla a curvatura da transformação. Quando $\gamma < 1$, as sombras tendem a ser abertas; quando $\gamma > 1$, regiões escuras são comprimidas e a imagem tende a escurecer. Essa ideia é central tanto em exibição quanto em aquisição, porque monitores, sensores e codificações digitais não respondem linearmente da mesma maneira.
Essa transformação se conecta diretamente à seção 23.3: realce eficaz depende de como o sistema visual percebe luminância e contraste. Em fotografia computacional moderna, correções desse tipo aparecem embutidas em pipelines de câmera, em ajuste de RAW e em modelos de tone mapping usados por celulares e sistemas HDR.
Exemplo matemático: se $r=0{,}25$ e $\gamma=0{,}5$, então $s=\sqrt{0{,}25}=0{,}5$. O pixel sobe bastante de intensidade. Já se $\gamma=2$, o mesmo valor vira $s=0{,}0625$, ficando mais escuro. A fórmula é simples, mas o efeito perceptual pode ser grande.
Outra família importante de técnicas olha menos para pixels isolados e mais para a distribuição global de intensidades, isto é, o histograma. Se uma imagem ocupa apenas uma pequena faixa tonal, ela tende a parecer “lavada” ou “achatada”. O contrast stretching amplia a faixa útil de intensidades; a equalização de histograma vai além e tenta redistribuir os valores para ocupar melhor o intervalo disponível.
Em sua forma clássica, a equalização usa a função de distribuição acumulada (CDF) do histograma para construir o remapeamento. Em uma imagem discreta com $L$ níveis, uma forma simplificada do mapeamento é
$$ s_k = (L-1)\sum_{j=0}^{k} p(r_j) $$
em que $p(r_j)$ representa a probabilidade do nível $r_j$. Didaticamente, isso significa que níveis muito concentrados são “espalhados” ao longo do intervalo disponível. O efeito costuma melhorar contraste global, mas não é neutro: também pode produzir aparência artificial, alterar brilho médio e exagerar ruído em regiões pobres em sinal.
Esse ponto é especialmente relevante hoje. Em aplicações voltadas para humanos, uma equalização agressiva pode deixar o resultado pouco natural; em aplicações para aprendizado profundo, ela pode mudar estatísticas da imagem e impactar desempenho do modelo. Por isso, o critério de qualidade depende do uso final, e não apenas de a imagem “parecer mais forte”.
Quando o contraste varia muito ao longo da cena, métodos globais costumam falhar. É aí que entram métodos locais. A equalização adaptativa de histograma (AHE) aplica remapeamentos em janelas menores, e sua variante mais conhecida, a CLAHE (Contrast Limited Adaptive Histogram Equalization), limita o ganho local para reduzir amplificação exagerada de ruído.
Em termos intuitivos, é como ajustar a iluminação de vários bairros de uma cidade separadamente, em vez de usar um único refletor para a cidade inteira. Essa estratégia é muito útil em radiografias, imagens submarinas, cenas urbanas noturnas e documentos escaneados com iluminação irregular. Ao mesmo tempo, seu uso exige cuidado: melhorar contraste local demais pode realçar textura irrelevante, ruído ou artefatos de compressão.
Outra linha importante é a família Retinex, inspirada em modelos de percepção visual e constância de cor. A ideia básica é separar, de forma aproximada, componentes de iluminação e refletância, de modo que o realce atue mais na iluminação do que na estrutura intrínseca do objeto. Mesmo quando a formulação usada em software é simplificada, a intuição continua poderosa: o sistema tenta responder “o que está escuro por falta de luz?” e “o que está realmente escuro como material?”. Essa distinção segue muito presente em métodos modernos de realce em baixa iluminação, inclusive em arquiteturas de aprendizado profundo inspiradas em Retinex.
Uma cena real pode ter faixa dinâmica muito maior do que a que cabe em um sensor ou em uma tela comum. Em tais casos, realce deixa de ser apenas ajuste fino e passa a envolver fusão de exposições, imagem HDR e mapeamento de tons. A ideia é capturar ou reconstruir mais informação luminosa do que um único disparo LDR mostraria e, depois, comprimir essa faixa para um dispositivo de exibição limitado.
Isso resolve um problema muito contemporâneo: preservar simultaneamente detalhe em sombras e altas luzes em cenas de alto contraste, como interiores com janelas fortes, paisagens ao pôr do sol ou direção autônoma sob iluminação adversa. O desafio é que tone mapping não é apenas compressão matemática; ele também envolve percepção. Algoritmos como os de Reinhard, Drago e Mantiuk fazem escolhas diferentes sobre contraste local, saturação e naturalidade visual.
Em smartphones, fotografia computacional e vídeo HDR, esses ajustes já são parte do pipeline padrão. Em visão computacional, porém, eles trazem um custo: realces muito agressivos podem melhorar a imagem para humanos e, ao mesmo tempo, distorcer informações radiométricas importantes para medições e estimação geométrica.
O trecho abaixo mostra um pipeline simples de realce para imagem em tons de cinza, combinando ajuste global, correção não linear e contraste local:
import cv2
import numpy as np
img = cv2.imread("imagem.png", cv2.IMREAD_GRAYSCALE)
# 1. Ajuste linear de brilho e contraste
linear = cv2.convertScaleAbs(img, alpha=1.2, beta=12)
# 2. Correção de gama via LUT
gamma = 0.8
lut = np.array([((i / 255.0) ** gamma) * 255 for i in range(256)], dtype=np.uint8)
gamma_corrigida = cv2.LUT(linear, lut)
# 3. Realce local com CLAHE
clahe = cv2.createCLAHE(clipLimit=2.0, tileGridSize=(8, 8))
realcada = clahe.apply(gamma_corrigida)
Nesse exemplo, o ajuste linear expande brilho e contraste globais, a correção de gama abre regiões mais escuras e a CLAHE atua localmente para recuperar contraste em áreas específicas. O ponto importante é que essas etapas não são neutras: dependendo do sensor e da cena, a mesma sequência pode melhorar legibilidade ou exagerar ruído.
Esta seção se conecta com transformações lineares e não lineares, funções monotônicas, histogramas e distribuições acumuladas, estatística de intensidades, modelagem perceptual, otimização de contraste local e compressão de faixa dinâmica. Em aplicações mais sofisticadas, também dialoga com decomposição iluminação-refletância, equações diferenciais e aprendizado de funções de mapeamento em pipelines treinados por dados.
Depois da introdução aos filtros na seção 23.1, o foco aqui passa da pergunta “como transformar uma imagem?” para a pergunta “como recuperar uma imagem degradada?”. Esse deslocamento é importante. Filtrar pode significar apenas suavizar, realçar ou destacar estruturas. Restaurar significa enfrentar um problema inverso: a imagem observada já chega misturada com ruído, borramento, falhas de aquisição, perdas de amostragem ou defeitos do sensor, e o objetivo é reconstruir uma versão mais fiel do sinal de interesse.
Livros de referência clássicos, como os de Gonzalez e Woods, costumam organizar esse tema em torno de alguns subtópicos recorrentes: modelo de degradação, remoção de ruído, deconvolução e restauração por filtros inversos, restauração estatística, inpainting e operadores morfológicos. Seguindo essa linha, esta seção organiza a discussão em torno de quatro ideias centrais: modelar a degradação, restaurar por filtragem e deconvolução, limpar estruturas por morfologia e reconhecer os limites dos métodos clássicos diante de problemas reais e modernos.
Analogia: restaurar uma imagem é como tentar reconstruir uma frase depois que ela foi borrada, cortada e contaminada por ruído. Se soubermos como a frase foi degradada, a reconstrução fica mais plausível; se não soubermos, o problema fica muito mais ambíguo.
A formulação clássica parte de um modelo simples e poderoso:
$$ g(x,y)=h(x,y)\ast f(x,y)+\eta(x,y) $$
em que $f(x,y)$ é a imagem ideal, $h(x,y)$ representa a resposta do sistema de formação de imagem, frequentemente modelada como PSF (point spread function), $\eta(x,y)$ é o ruído e $g(x,y)$ é a imagem observada. Em termos físicos, o borramento pode vir de desfoque óptico, tremor de câmera, movimento relativo, difração ou limitações do sensor; o ruído pode vir de eletrônica, baixa iluminação, compressão ou aquisição médica.
Essa equação é didática porque explicita por que restauração é difícil: vários mecanismos diferentes podem produzir observações visualmente parecidas. Um borramento suave com pouco ruído, por exemplo, pode exigir estratégia bem diferente de uma imagem quase nítida, mas contaminada por ruído impulsivo. Em câmeras móveis e vídeo de baixa luz, esse problema continua central: o pipeline moderno precisa compensar movimento, baixa exposição e ruído ao mesmo tempo.
Uma parte importante da restauração lida com remoção de ruído. Se o problema dominante for ruído gaussiano aditivo, filtros lineares ou modelos variacionais podem funcionar bem. Se houver ruído impulsivo, como “sal e pimenta”, técnicas robustas como mediana e operadores morfológicos costumam ser mais adequados. Aqui reaparece uma distinção essencial da 23.1: o melhor filtro depende do tipo de degradação, não apenas da aparência final desejada.
Em livros e sumários de referência, essa etapa costuma aparecer ao lado de métodos mais modernos de denoising, como variação total, non-local means e denoising por wavelets. A lógica por trás deles é preservar estruturas relevantes, especialmente bordas, sem repetir o erro clássico de borrar tudo junto. Em imagens reais, esse equilíbrio é delicado: denoising agressivo pode “plastificar” textura, enquanto denoising fraco pode deixar ruído suficiente para atrapalhar segmentação, OCR ou detecção de objetos.
Exemplo matemático: no modelo clássico de denoising por variação total, busca-se uma imagem $u$ que minimize
$$ \min_u \; \int |\nabla u|\,dxdy + \frac{\lambda}{2}\int (u-f)^2\,dxdy $$
O primeiro termo penaliza oscilações excessivas e tende a preservar contornos; o segundo força a solução a permanecer próxima da imagem observada. A escolha de $\lambda$ controla o compromisso entre suavidade e fidelidade.
Quando a degradação dominante é o borramento descrito por uma PSF conhecida ou estimável, o problema passa a ser de deconvolução. Em princípio, se a convolução no modelo direto é conhecida, poderíamos tentar inverter essa operação. Na prática, isso é instável: frequências muito pequenas ou regiões dominadas por ruído tornam a inversão direta extremamente sensível.
Por isso, livros-texto tradicionalmente apresentam uma sequência didática de subtópicos: filtro inverso, filtro de Wiener e métodos iterativos como Richardson-Lucy. O filtro inverso é conceitualmente simples, mas amplifica ruído com facilidade. O filtro de Wiener incorpora um raciocínio estatístico e busca minimizar erro quadrático médio sob hipóteses sobre sinal e ruído. Já Richardson-Lucy é iterativo e aparece com frequência em microscopia, astronomia e deblurring quando se deseja impor positividade e refinar a solução passo a passo.
Esse cenário também tem uma versão moderna. Em restauração cega (blind deconvolution), a PSF não é conhecida; em restauração real, várias degradações coexistem. É por isso que métodos contemporâneos combinam regularização clássica com modelos aprendidos, e mais recentemente com priors generativos e modelos de difusão. Ainda assim, a intuição clássica continua válida: restaurar é sempre um compromisso entre fidelidade ao dado, conhecimento do processo de degradação e suposições sobre a estrutura da imagem.
Os operadores morfológicos entram quando o interesse principal não é reconstruir radiometria fina, mas corrigir estrutura e conectividade de formas. Diferentemente da convolução, morfologia não combina pixels por soma ponderada; ela opera por comparações definidas por um elemento estruturante. Em imagens binárias, e também em versões para tons de cinza, erosão e dilatação são os blocos fundamentais.
A erosão reduz regiões claras do primeiro plano e tende a remover pequenos objetos ou pontas finas. A dilatação faz o oposto: expande regiões claras, preenche pequenas lacunas e conecta trechos próximos. A abertura combina erosão seguida de dilatação, sendo útil para remover ruído pontual sem preservar pequenos objetos espúrios; o fechamento combina dilatação seguida de erosão, sendo útil para fechar furos ou descontinuidades pequenas. Em processamento documental, inspeção industrial e máscaras de segmentação, esses operadores continuam extremamente atuais porque corrigem estruturas de forma interpretável e barata.
Vale notar que morfologia também aparece em versões mais sofisticadas, como gradiente morfológico, top-hat e reconstrução morfológica. Esses métodos são especialmente úteis quando o ruído é mais “geométrico” do que estatístico, isto é, quando o problema está menos em valores contínuos de intensidade e mais na forma de componentes conectados.
Há situações em que a imagem não está apenas ruidosa ou borrada, mas parcialmente ausente: riscos em fotografias antigas, pixels mortos, regiões mascaradas, texto removido ou falhas de transmissão. Nesses casos, fala-se em inpainting. A ideia é preencher regiões faltantes com base na vizinhança, seja por propagação geométrica, seja por modelagem estatística, seja por síntese baseada em textura.
Esse subtópico costuma aparecer em livros mais avançados e faz a ponte entre restauração clássica e fotografia computacional moderna. Em aplicações atuais, o problema vai de remover poeira em digitalização histórica até reconstruir áreas faltantes em vídeo e usar preenchimento guiado por modelos generativos. O ganho visual pode ser grande, mas o risco conceitual também: inpainting pode produzir uma imagem plausível sem que ela seja uma reconstrução fiel do conteúdo original.
O trecho abaixo ilustra duas frentes diferentes da seção: denoising/deconvolução e morfologia estrutural.
import cv2
import numpy as np
from skimage.restoration import richardson_lucy
img = cv2.imread("imagem.png", cv2.IMREAD_GRAYSCALE)
# 1. Restauração iterativa por Richardson-Lucy
psf = np.ones((5, 5), dtype=np.float32) / 25.0
restaurada = richardson_lucy(img / 255.0, psf, num_iter=15)
# 2. Morfologia para limpar uma máscara binária
_, mascara = cv2.threshold(img, 0, 255, cv2.THRESH_BINARY + cv2.THRESH_OTSU)
kernel = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (3, 3))
abertura = cv2.morphologyEx(mascara, cv2.MORPH_OPEN, kernel)
fechamento = cv2.morphologyEx(abertura, cv2.MORPH_CLOSE, kernel)
Nesse exemplo, utilizando a biblioteca "richardson_lucy" do Python, há tentativa de reverter um borramento conhecido pela PSF, enquanto abertura e fechamento atuam sobre a estrutura binária para remover ruído pontual e preencher falhas pequenas. O contraste é intencional: uma etapa trabalha no nível da formação da imagem; a outra trabalha no nível da forma dos objetos.
Esta seção se conecta com convolução, deconvolução, resposta ao impulso, teoria de ruído, estimação estatística, otimização variacional, regularização, operadores máximos e mínimos em morfologia, conectividade e análise de componentes. Em problemas mais modernos, também dialoga com inferência bayesiana, métodos iterativos e modelos generativos para restauração cega e reconstrução de conteúdo ausente.
A reconstrução tomográfica é um dos exemplos mais importantes de problema inverso em processamento de imagens. Historicamente, a base matemática remonta à transformada de Radon, formulada em 1917, enquanto a consolidação clínica da tomografia computadorizada (TC) ocorreu nas décadas de 1960 e 1970 com o trabalho de Allan Cormack e Godfrey Hounsfield, reconhecido com o Nobel de 1979. O ponto central não é apenas “tirar muitas radiografias”, mas reconstruir computacionalmente um corte interno do corpo a partir de projeções medidas em vários ângulos.
Uma analogia útil é pensar em tentar descobrir a forma de um objeto observando apenas suas sombras. Cada projeção de raios X informa quanto o feixe foi atenuado ao atravessar o corpo ao longo de uma trajetória. Uma radiografia comum superpõe todas essas contribuições em uma única imagem; já a tomografia coleta muitas projeções em ângulos diferentes para recuperar um corte transversal. Depois, vários cortes podem ser empilhados para formar um volume 3D composto por voxels.
Em TC, o que se quer estimar é o coeficiente de atenuação em cada ponto do corte. Em termos práticos, isso produz uma imagem quantitativa: ar, gordura, tecidos moles e osso atenuam raios X de formas diferentes, e os valores reconstruídos costumam ser expressos em unidades de Hounsfield. Segmentação, registro e análise clínica continuam importantes, mas aparecem depois da reconstrução; eles não são o mecanismo principal pelo qual o corte é obtido.
Em forma contínua, uma projeção ideal pode ser escrita de maneira simplificada como \( p_{\theta}(s) = \int_{L} \mu(x, y)\,dl \), em que \( \mu(x, y) \) representa o coeficiente de atenuação do material e a integral acumula o efeito ao longo do raio. Isso conecta diretamente a tomografia à transformada de Radon, já introduzida na seção 23.7: em vez de observar pixels diretamente, observamos integrais de linha e precisamos inverter essa relação de forma numericamente estável.
No modelo discreto, a reconstrução costuma ser escrita como \( Ax = b \). O vetor \( x \) contém os valores desconhecidos dos pixels ou voxels, \( b \) contém as medições do detector e \( A \) descreve quanto cada raio atravessa cada célula da grade. Essa formulação ajuda a enxergar por que o problema é delicado: o sistema pode ser grande, esparso, ruidoso e mal condicionado, especialmente quando o número de projeções é pequeno ou quando alguns ângulos estão ausentes.
Essa organização também aparece nos livros clássicos da área. Sumários como os de Kak e Slaney e de Gabor Herman separam o tema em coleta de dados, problemas físicos da aquisição, retroprojeção, retroprojeção filtrada, métodos algébricos, otimização e reconstrução tridimensional. Didaticamente, essa sequência faz sentido: primeiro entendemos como os dados são medidos, depois por que a inversão é difícil, e só então comparamos os algoritmos disponíveis e seus compromissos entre velocidade, ruído e dose.
A retroprojeção pura “espalha de volta” cada medida ao longo do caminho correspondente do raio. O problema é que isso gera borramento, porque cada sombra é redistribuída sobre muitas posições possíveis. A ideia da retroprojeção filtrada é compensar esse borramento antes de projetar os dados de volta, tipicamente usando um filtro rampa no domínio da frequência. A ligação teórica com Fourier aparece no teorema da fatia central, que relaciona projeções 1D às fatias da transformada 2D do objeto.
A retroprojeção filtrada continua sendo um método clássico porque é rápida, interpretável e funciona muito bem quando há boa cobertura angular e relação sinal-ruído razoável. Em contrapartida, ela tende a sofrer mais quando a aquisição é escassa, quando há movimento do paciente, quando existem metais no campo de visão ou quando a dose é reduzida a ponto de aumentar significativamente o ruído das medições.
Quando as condições ideais falham, entram os métodos iterativos. Técnicas como ART e SART tratam a reconstrução como solução sucessiva do sistema \( Ax = b \), refinando a imagem passo a passo. Isso permite incorporar restrições e regularizações, como positividade, suavidade controlada, variação total ou modelos estatísticos mais realistas para o ruído de contagem dos fótons.
Esse ponto é central em problemas modernos, mas ele precisa aparecer integrado ao fluxo do assunto e não isolado artificialmente no final. Em low-dose CT, por exemplo, aceita-se menos informação para reduzir exposição à radiação; em limited-angle tomography, parte dos ângulos simplesmente não pode ser medida; em tomografia helicoidal, cone-beam e aquisição rápida, a geometria real do sistema se afasta do caso paralelo ideal. Nesses cenários, a reconstrução passa a depender cada vez mais de modelagem física, otimização e, mais recentemente, regularizadores aprendidos por dados.
import numpy as np
from skimage.data import shepp_logan_phantom
from skimage.transform import radon, iradon, iradon_sart, rescale
imagem = shepp_logan_phantom()
imagem = rescale(imagem, scale=0.4, mode="reflect", channel_axis=None)
# Poucos ângulos para simular um cenário mais difícil
theta = np.linspace(0.0, 180.0, 30, endpoint=False)
sinograma = radon(imagem, theta=theta)
recon_fbp = iradon(sinograma, theta=theta, filter_name="ramp")
recon_sart = iradon_sart(sinograma, theta=theta)
erro_fbp = np.sqrt(np.mean((recon_fbp - imagem) ** 2))
erro_sart = np.sqrt(np.mean((recon_sart - imagem) ** 2))
print(f"Erro RMS FBP: {erro_fbp:.4f}")
print(f"Erro RMS SART: {erro_sart:.4f}")
O exemplo usa o fantoma de Shepp-Logan, clássico em tomografia, para comparar um método analítico rápido com um método iterativo. Com poucas projeções, a retroprojeção filtrada tende a produzir estrias mais visíveis, enquanto SART costuma aproveitar melhor a estrutura esparsa do problema. Em aplicações reais, porém, a escolha do método depende de custo computacional, geometria do equipamento, modelo de ruído e restrições clínicas de dose e tempo.
Esta seção se conecta diretamente a integrais de linha, transformada de Radon, teorema da fatia central de Fourier, sistemas lineares esparsos, mínimos quadrados, regularização, interpolação, otimização convexa e inferência estatística. Em extensões modernas, dialoga ainda com reconstrução em geometrias fan-beam e cone-beam, redução de artefatos metálicos, problemas mal postos e aprendizado de priors para reconstrução com poucos dados.
Depois de discutir filtros, amostragem, transformadas, realce, restauração e reconstrução, vale reunir alguns exemplos curtos de implementação. A ideia desta seção não é avançar para interpretação semântica da cena, que ficará para 23.10 e 23.11, mas mostrar como várias operações clássicas de processamento de imagens aparecem em código de forma direta e reproduzível.
Historicamente, esse tipo de experimentação foi decisivo para a área. Muitos algoritmos nasceram em ambientes científicos como MATLAB, C e bibliotecas acadêmicas, e hoje migraram para ecossistemas como OpenCV, NumPy e scikit-image. Isso mudou a escala do trabalho: operações que antes eram testadas em poucas imagens agora podem ser aplicadas em grandes conjuntos de dados, com análise quantitativa e comparação sistemática entre métodos.
Transformações pontuais recalculam cada pixel de forma independente, sem olhar vizinhos. É o caso mais simples de processamento: o novo valor depende apenas do valor antigo. Em forma linear, um ajuste global de brilho e contraste pode ser escrito como \( I'(x,y) = \alpha I(x,y) + \beta \), em que \( \alpha \) controla o contraste e \( \beta \) desloca a intensidade média. Já a correção de gama usa uma transformação não linear, tipicamente \( I' = 255\left(\frac{I}{255}\right)^{\gamma} \), útil quando se quer abrir sombras ou comprimir altas luzes.
Uma analogia simples é pensar no volume e na curva tonal de um sistema de áudio: uma transformação linear aumenta ou reduz tudo de maneira uniforme; uma transformação não linear remodela a resposta e pode privilegiar certas faixas mais do que outras.
import cv2
import numpy as np
img = cv2.imread("imagem.png", cv2.IMREAD_GRAYSCALE)
# Contraste e brilho: I' = alpha * I + beta
alpha = 1.25
beta = 18
ajustada = cv2.convertScaleAbs(img, alpha=alpha, beta=beta)
# Correcao de gama com tabela de consulta
gamma = 0.8
tabela = np.array([
((i / 255.0) ** gamma) * 255 for i in range(256)
], dtype=np.uint8)
gama = cv2.LUT(img, tabela)
A vantagem dessas operações é a simplicidade e o baixo custo computacional. A limitação é igualmente importante: como a transformação é aplicada pixel a pixel, ela não distingue estrutura útil de ruído nem adapta o ganho ao contexto local da imagem.
Quando o valor de cada pixel passa a depender da vizinhança, entramos no regime dos filtros espaciais. Aqui aparecem suavização, redução de ruído e operadores de derivada. Em muitos problemas, o fluxo prático segue uma lógica clássica: primeiro suavizar para reduzir flutuações espúrias; depois calcular gradientes ou bordas para destacar transições estruturais.
import cv2
img = cv2.imread("imagem.png", cv2.IMREAD_GRAYSCALE)
# Suavizacao para reduzir ruido de alta frequencia
gauss = cv2.GaussianBlur(img, (5, 5), 1.2)
mediana = cv2.medianBlur(img, 5)
# Realce de mudancas abruptas
sobel_x = cv2.Sobel(gauss, cv2.CV_32F, 1, 0, ksize=3)
sobel_y = cv2.Sobel(gauss, cv2.CV_32F, 0, 1, ksize=3)
magnitude = cv2.magnitude(sobel_x, sobel_y)
Do ponto de vista matemático, Sobel e operadores semelhantes podem ser entendidos como aproximações discretas de derivadas. Isso explica por que eles respondem fortemente em contornos: onde a intensidade muda rapidamente, a derivada cresce. Em imagens muito ruidosas, porém, derivadas também amplificam ruído; por isso a suavização anterior não é detalhe de implementação, mas parte do desenho do algoritmo.
Uma imagem pode ter baixo contraste não porque todos os pixels estejam errados, mas porque a distribuição tonal ocupa uma faixa estreita da escala disponível. Nesses casos, histograma e equalização oferecem um caminho natural. A equalização global redistribui intensidades com base na função acumulada do histograma; CLAHE aplica a mesma intuição localmente, limitando o ganho para não amplificar ruído demais.
import cv2
img = cv2.imread("imagem.png", cv2.IMREAD_GRAYSCALE)
equalizada = cv2.equalizeHist(img)
clahe = cv2.createCLAHE(clipLimit=2.0, tileGridSize=(8, 8))
local = clahe.apply(img)
Na prática, a diferença entre os dois métodos é importante. A equalização global pode melhorar o contraste geral, mas também exagerar regiões já bem distribuídas; CLAHE costuma funcionar melhor em radiologia, documentos e cenas com iluminação desigual, justamente porque adapta o ganho ao contexto espacial.
Também é possível transformar em código a ideia central da tomografia vista em 23.8. A imagem original não é observada diretamente; primeiro geramos projeções ao longo de vários ângulos, formando um sinograma, e depois tentamos reconstruir o corte por um método analítico ou iterativo. Esse exemplo é especialmente útil porque mostra um problema em que processar imagem significa resolver uma inversão, e não apenas aplicar um filtro local.
import numpy as np
from skimage.data import shepp_logan_phantom
from skimage.transform import radon, iradon
phantom = shepp_logan_phantom()
angulos = np.linspace(0.0, 180.0, 60, endpoint=False)
sinograma = radon(phantom, theta=angulos)
reconstruida = iradon(sinograma, theta=angulos, filter_name="ramp")
Esse trecho é pequeno, mas condensa muita matemática: integrais de linha, amostragem angular, inversão da transformada de Radon e uso do filtro rampa. Em experimentos reais, basta reduzir o número de ângulos ou adicionar ruído para que o problema fique claramente mal posto, o que motiva os métodos iterativos e regularizados discutidos anteriormente.
Em codificação científica, não basta “rodar e olhar”. O procedimento mais sólido é comparar entrada e saída visualmente, acompanhar histogramas, medir tempo de execução e, quando houver imagem de referência, usar métricas como erro quadrático médio ou SSIM. Em tomografia, além da qualidade visual, também interessam dose, número de projeções, presença de artefatos e estabilidade numérica. Em outras palavras, a implementação deve ser tratada como experimento, não como demonstração isolada.
Esta seção articula funções afins e não lineares para intensidade, convolução, aproximações discretas de derivadas, histogramas acumulados, transformada de Radon, operadores lineares, reconstrução inversa e análise quantitativa por métricas de erro. Esses elementos formam a ponte entre a formulação matemática dos algoritmos e sua execução concreta em bibliotecas de uso científico.
Depois de restaurar, realçar ou reconstruir uma imagem, o passo seguinte costuma ser analisá-la. Aqui aparece uma distinção importante: processamento de imagens altera a representação visual para melhorar qualidade ou destacar estruturas; análise de imagens transforma pixels em medições, regiões e relações geométricas; visão computacional, por sua vez, usa essas estruturas para fazer inferências sobre objetos, movimento e cena. Uma analogia útil é esta: processar é limpar a lente; analisar é medir o que aparece; reconhecer é decidir o que aquilo significa.
Historicamente, essa passagem do baixo nível para níveis mais interpretativos ganhou força a partir das décadas de 1970 e 1980, quando a área começou a articular bordas, regiões, textura, movimento e geometria em pipelines coerentes de visão. A chamada early vision buscava extrair estrutura local da imagem antes de qualquer decisão semântica forte. Mais tarde, trabalhos sobre fluxo óptico, descritores locais, correspondência e rastreamento consolidaram a ideia de que ver computacionalmente não é apenas classificar, mas também medir, relacionar e acompanhar evidências visuais no espaço e no tempo.
Uma das tarefas centrais da análise é particionar a imagem em unidades manipuláveis. Em vez de trabalhar com milhões de pixels isolados, passamos a trabalhar com regiões conectadas, contornos, componentes e máscaras. Em forma abstrata, isso pode ser representado por um mapa de rótulos \( L(x,y) \in \{0,1,\dots,K\} \), em que cada posição da imagem recebe o índice da região à qual pertence. Esse passo pode vir de limiarização, crescimento de regiões, morfologia, contornos, watershed ou métodos mais modernos guiados por dados.
Esse raciocínio é importante porque muitas medidas úteis só fazem sentido depois da segmentação. Área, perímetro, orientação, caixa delimitadora, circularidade e posição do centro de massa são propriedades de regiões, não de pixels isolados. Em inspeção industrial, isso permite medir defeitos; em imagens médicas, permite isolar estruturas anatômicas; em documentos, ajuda a separar texto, ruído e margens.
Uma vez obtidas as regiões, a análise passa a extrair atributos ou descritores. Esses valores resumem partes importantes da estrutura visual em um vetor compacto. Em um objeto binário simples, por exemplo, os momentos geométricos já permitem estimar área e centroide. Se \( M_{00} \) representa a massa da região, então o centroide pode ser calculado por \( C_x = \frac{M_{10}}{M_{00}} \) e \( C_y = \frac{M_{01}}{M_{00}} \).
Em cenários mais ricos, os descritores podem incluir textura, distribuição de cores, bordas orientadas, pontos de interesse ou representações aprendidas. A ideia, porém, continua a mesma: substituir dados brutos por uma descrição mais estruturada. É essa etapa que prepara naturalmente a próxima seção, em que esses atributos passam a alimentar algoritmos de reconhecimento de padrões.
Analisar uma imagem estática é apenas uma parte do problema. Em vídeo, a questão central passa a ser: o que mudou entre dois quadros? O fluxo óptico modela esse deslocamento aparente como um campo vetorial \( [u(x,y,t), v(x,y,t)] \), em que cada pixel recebe uma velocidade horizontal e vertical estimada. Em abordagens clássicas, parte-se da hipótese de constância de brilho e obtém-se a equação
\[ I_x u + I_y v + I_t = 0 \]
em que \( I_x \) e \( I_y \) são derivadas espaciais e \( I_t \) é a derivada temporal. O ponto conceitualmente mais importante é que essa equação sozinha não basta para determinar \( u \) e \( v \) de forma única, levando ao chamado problema da abertura. Por isso, métodos como Horn-Schunck e Lucas-Kanade introduzem hipóteses adicionais de suavidade ou coerência local. Em termos intuitivos, quando observamos apenas um pequeno trecho de uma borda, conseguimos perceber que algo se moveu, mas não necessariamente em qual direção completa.
À medida que atributos locais, regiões e movimento se tornam disponíveis, a análise começa a tocar problemas de geometria da cena: correspondência entre pontos, estimativa de profundidade, orientação de superfícies, rastreamento e estabilização. Mesmo quando a meta final é semântica, como detectar um pedestre, muita informação útil continua sendo geométrica: contorno, escala aparente, coerência temporal, posição relativa e trajetória. Isso explica por que a visão computacional clássica ainda é valiosa mesmo em pipelines modernos baseados em aprendizado.
Essa integração também aparece em aplicações atuais. Sistemas embarcados para robótica e direção assistida precisam combinar segmentação, movimento e geometria sob restrições duras de latência; em medicina, a análise deve lidar com ruído, oclusão e variabilidade anatômica; em monitoramento, mudanças de iluminação e fundo dinâmico podem enganar algoritmos aparentemente simples. O problema moderno não é apenas reconhecer, mas reconhecer com estabilidade em cenários imperfeitos.
O trecho a seguir ilustra uma cadeia típica de análise em imagem estática: segmentar, extrair contornos e medir propriedades geométricas da maior região encontrada.
import cv2
import numpy as np
img = cv2.imread("objeto.png", cv2.IMREAD_GRAYSCALE)
# Segmentacao simples por Otsu
_, binaria = cv2.threshold(
img, 0, 255, cv2.THRESH_BINARY + cv2.THRESH_OTSU
)
contornos, _ = cv2.findContours(
binaria, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE
)
maior = max(contornos, key=cv2.contourArea)
M = cv2.moments(maior)
area = cv2.contourArea(maior)
perimetro = cv2.arcLength(maior, True)
cx = M["m10"] / M["m00"]
cy = M["m01"] / M["m00"]
x, y, w, h = cv2.boundingRect(maior)
print(f"area = {area:.1f}")
print(f"perimetro = {perimetro:.1f}")
print(f"centroide = ({cx:.1f}, {cy:.1f})")
print(f"bounding box = {(x, y, w, h)}")
Esse exemplo ainda não “reconhece” o objeto; ele o descreve. Isso é exatamente o papel da análise: transformar a imagem em medidas estruturadas que podem alimentar uma decisão posterior. Em problemas mais avançados, a mesma lógica continua válida, apenas com descritores mais ricos e modelos de segmentação mais robustos.
Em sequências temporais, um exemplo clássico é estimar o fluxo óptico de pontos de interesse entre dois quadros consecutivos.
import cv2
import numpy as np
quadro1 = cv2.imread("frame1.png", cv2.IMREAD_GRAYSCALE)
quadro2 = cv2.imread("frame2.png", cv2.IMREAD_GRAYSCALE)
pontos = cv2.goodFeaturesToTrack(
quadro1, maxCorners=100, qualityLevel=0.3, minDistance=7
)
proximos, status, erro = cv2.calcOpticalFlowPyrLK(
quadro1, quadro2, pontos, None
)
bons_anteriores = pontos[status == 1]
bons_novos = proximos[status == 1]
Esse tipo de estimativa é útil em rastreamento, estabilização de vídeo, navegação visual e análise de movimento. Ao mesmo tempo, ele deixa claras várias dificuldades reais da área: oclusão, regiões sem textura, mudanças de iluminação e deslocamentos grandes entre quadros podem degradar a estimativa. É por isso que a análise moderna combina métodos clássicos, modelagem geométrica e, cada vez mais, representações aprendidas por dados.
Esta seção se conecta a conectividade, rotulagem de componentes, momentos geométricos, descritores vetoriais, derivadas espaciais e temporais, sistemas subdeterminados, regularização, campos vetoriais, estimação de movimento, correspondência e geometria projetiva. Em extensões contemporâneas, dialoga também com otimização em grafos, representação aprendida e segmentação guiada por transformadores visuais.
Se a seção 23.10 termina com descritores e medições, esta seção começa exatamente aí: reconhecer um padrão é decidir, a partir de um conjunto de atributos, a que classe uma amostra provavelmente pertence. Em vez de olhar diretamente para a imagem bruta, trabalhamos em um espaço de características, no qual cada amostra é representada por um vetor \( \mathbf{x} \). Um classificador então implementa uma regra \( f(\mathbf{x}) \rightarrow y \), produzindo um rótulo \( y \) ou uma distribuição de probabilidades sobre classes possíveis.
Uma analogia útil é pensar em triagem médica. O médico não decide apenas “olhando pixels”; ele observa sinais, mede pressão, frequência, histórico e exames, e então compara esse conjunto de evidências com padrões conhecidos. Reconhecimento de padrões faz algo análogo: transforma observações em atributos e usa uma regra de decisão para distinguir categorias, anomalias ou grupos semelhantes.
Historicamente, o reconhecimento de padrões surgiu da interseção entre estatística, engenharia e inteligência artificial simbólica. Nas décadas de 1960 e 1970, o foco estava em regras de decisão, classificadores lineares e extração manual de atributos. Mais tarde, métodos estatísticos mais robustos, como vizinhos mais próximos, árvores, misturas gaussianas e máquinas de vetores de suporte, ampliaram a capacidade de separar classes complexas. A partir dos anos 2010, redes profundas passaram a aprender não apenas a fronteira de decisão, mas também a própria representação dos dados, reduzindo a dependência de atributos projetados manualmente.
Essa mudança, porém, não tornou os métodos clássicos obsoletos. Em muitos problemas com poucos dados, necessidade de interpretabilidade, custo computacional restrito ou exigência de validação cuidadosa, classificadores tradicionais continuam extremamente relevantes. Em aplicações médicas, jurídicas e industriais, entender por que o modelo errou pode ser tão importante quanto melhorar alguns pontos percentuais de acurácia.
No regime supervisionado, cada vetor \( \mathbf{x}_i \) vem acompanhado de um rótulo \( y_i \), e o objetivo é aprender uma regra que generalize para novos exemplos. No regime não supervisionado, não há rótulos explícitos; busca-se organizar os dados por similaridade, agrupamento ou estrutura latente. Em ambos os casos, a geometria do espaço de características importa: distância, escala, redundância e separabilidade influenciam diretamente o desempenho.
É por isso que redução de dimensionalidade e seleção de atributos entram no pipeline antes da classificação final. Técnicas como PCA não são classificadores em si, mas podem comprimir informação, remover correlação e facilitar a separação entre classes. Em termos didáticos, é como reorganizar uma mesa bagunçada antes de tentar identificar padrões nos objetos que estão sobre ela.
Os classificadores clássicos podem ser entendidos pelo tipo de fronteira de decisão que impõem no espaço de características. O KNN classifica uma amostra pelo voto dos vizinhos mais próximos; por isso, a fronteira pode ser bastante irregular e adaptada aos dados locais. As máquinas de vetores de suporte procuram um hiperplano que separe as classes com margem máxima. Árvores de decisão fazem partições sucessivas por regras simples, produzindo modelos mais interpretáveis, embora sujeitos a instabilidade se não forem regularizados.
Essa diversidade mostra que não existe método universalmente melhor. Se as classes estiverem bem separadas e os atributos forem informativos, um classificador simples pode funcionar muito bem. Se houver ruído, alta dimensionalidade, classes sobrepostas ou desbalanceamento, a escolha do modelo, da métrica de distância e da estratégia de validação passa a ser parte essencial do problema.
Em imagens, o avanço mais marcante das últimas décadas foi aprender representações automaticamente. Em vez de depender apenas de bordas, textura, cor, contornos ou momentos construídos manualmente, redes neurais convolucionais passaram a extrair hierarquias de características diretamente dos dados. Camadas iniciais respondem a padrões locais simples; camadas mais profundas combinam essas respostas em formas, partes e categorias mais abstratas.
Mesmo assim, o problema moderno não é apenas aumentar poder de representação. É preciso lidar com bases pequenas, ruído de anotação, mudança de domínio, classes raras, custo energético e vieses nos dados. Em tarefas reais, muitas falhas não vêm de “pouca inteligência” do modelo, mas de desbalanceamento, rotulagem inconsistente, distribuição de teste diferente da distribuição de treino ou critérios de avaliação mal escolhidos.
Reconhecer padrões não é apenas ajustar um modelo aos dados observados, mas verificar se ele generaliza. Por isso, separar conjuntos de treino e teste é fundamental. Também é por isso que sobreajuste aparece tão cedo: um modelo pode memorizar os exemplos disponíveis e ainda assim falhar diante de dados novos. Em classificação, uma ferramenta central de avaliação é a matriz de confusão \( C \), em que \( C_{i,j} \) conta quantas amostras da classe real \( i \) foram previstas como classe \( j \).
Da matriz de confusão derivam medidas como acurácia, precisão e revocação. Em classes desbalanceadas, acurácia isolada pode ser enganosa: um detector de doença rara pode atingir valor alto simplesmente prevendo “negativo” para quase tudo. É nesse ponto que a estatística do reconhecimento de padrões deixa de ser detalhe e vira parte do núcleo conceitual da disciplina.
O trecho a seguir usa um conjunto clássico de dígitos manuscritos já vetorizados para ilustrar o fluxo mínimo: separar treino e teste, ajustar um classificador simples e avaliar o resultado.
from sklearn.datasets import load_digits
from sklearn.model_selection import train_test_split
from sklearn.neighbors import KNeighborsClassifier
from sklearn.metrics import confusion_matrix, classification_report
digits = load_digits()
X = digits.data
y = digits.target
X_train, X_test, y_train, y_test = train_test_split(
X, y, test_size=0.25, random_state=0, stratify=y
)
clf = KNeighborsClassifier(n_neighbors=5)
clf.fit(X_train, y_train)
y_pred = clf.predict(X_test)
print(confusion_matrix(y_test, y_pred))
print(classification_report(y_test, y_pred))
Didaticamente, esse exemplo é útil porque separa bem as etapas. Cada imagem já está representada por um vetor de intensidades, o classificador usa vizinhança no espaço de características e a avaliação mostra onde o modelo confunde classes semelhantes. Em um problema real de imagens, os atributos poderiam vir de contornos, textura, cor, CNNs ou descritores extraídos na etapa de análise.
Hoje, a dificuldade raramente está apenas em implementar um classificador. Em muitos cenários, o problema real é treinar com poucos rótulos, ajustar limiares de decisão para custos assimétricos, manter robustez diante de ruído e oclusão, e detectar quando o modelo está sendo aplicado fora do domínio para o qual foi treinado. Em sistemas críticos, também se exige rastreabilidade: por que o modelo decidiu isso, com que confiança e sob quais limitações?
Esse é o ponto em que reconhecimento de padrões se encontra com aprendizado estatístico moderno. O desafio não é só reconhecer mais, mas reconhecer melhor, com validação adequada, custo justificável e comportamento previsível em dados novos.
Esta seção se conecta a álgebra linear em espaços vetoriais de características, métricas de distância, probabilidade e inferência bayesiana, otimização, hiperplanos separadores, redução de dimensionalidade, teoria de decisão, validação estatística, análise de erro e medidas derivadas da matriz de confusão. Em extensões contemporâneas, dialoga ainda com aprendizado profundo, calibração de probabilidades, aprendizado com poucos exemplos e adaptação de domínio.
Glossário
Referências: