5.31. Correspondência de deslocamento

A correspondência por modelo responde a onde está este fragmento dentro do fotograma; a pontuação de similaridade responde a quão semelhantes são estas duas imagens no geral. Uma questão diferente situa-se entre elas: os dois fotogramas mostram a mesma cena, mas a câmara (ou a cena) moveu-se entre eles – em quanto? Esse é o problema do deslocamento, e o módulo de imagem resolve-o com um único método de correlação de fase.

5.31.1. Deslocamento por correlação de fase

find_displacement() estima o alinhamento rígido entre duas imagens do mesmo tamanho usando correlação de fase – um método no domínio da frequência que executa uma transformada de Fourier rápida (FFT) em cada imagem, correlaciona cruzadamente as suas fases, e localiza o pico no resultado. A posição do pico é a translação que alinha as duas imagens:

d = img.find_displacement(template)

print("shift:", d.x_translation, d.y_translation,
      " response:", d.response)

O objeto Displacement devolvido contém x_translation e y_translation – o deslocamento em pixeis em cada eixo – mais response, uma pontuação de confiança de 0.0 a 1.0 onde 1.0 é um pico perfeito. Filtrar deteções abaixo de response > 0.3 descarta resultados espúrios nos quais a correlação de fase nunca encontrou um pico limpo.

Tanto rotation como scale são 0.0 e 1.0 respetivamente no modo predefinido; só assumem valores reais quando logpolar=True (ver abaixo).

O método tem duas restrições práticas. A primeira são as dimensões potência de dois: a FFT no coração da correlação de fase é mais rápida – e na câmara, apenas totalmente suportada – a tamanhos como 32 por 32, 64 por 64, e 128 por 128. A configuração mais limpa é capturar diretamente a um desses tamanhos, passando a resolução para framesize() como um tuplo:

csi0.framesize((64, 64))

Uma aplicação que necessite de deslocamento a partir de um fotograma maior recorta em alternativa um fragmento potência de dois da região que lhe interessa e executa o comparador nesse fragmento.

A segunda é entradas do mesmo tamanho: roi e template_roi devem selecionar larguras e alturas idênticas, ou o comparador recusa a chamada. Duas capturas da mesma câmara na mesma configuração satisfazem isto automaticamente; um fotograma capturado comparado com uma referência carregada precisa que ambos sejam recortados para fragmentos potência de dois correspondentes primeiro.

5.31.2. Rotação e escala via log-polar

O modo predefinido encontra apenas translação. Quando os dois fotogramas também diferem em rotação em torno de um centro escolhido ou em escala em torno do mesmo centro, executar a correlação de fase na re-projeção log-polar de cada imagem converte esses parâmetros em translação no sistema de coordenadas log-polar – que o mesmo comparador de correlação de fase consegue recuperar:

d = img.find_displacement(template, logpolar=True)

print("rotation rad:", d.rotation,
      " scale:", d.scale,
      " response:", d.response)

Com logpolar=True, o método executa o mesmo pipeline de correspondência contra as imagens projetadas em log-polar em vez dos originais. Os campos rotation e scale do resultado ficam preenchidos: rotation é o ângulo em radianos entre os dois fotogramas, scale é o fator de escala entre eles. x_translation e y_translation tornam-se sem significado neste modo (a translação ao longo dos eixos log-polar não corresponde a uma translação linear na fonte).

A palavra-chave fix_rotation_scale=True cobre o caso intermédio: as duas imagens diferem tanto em translação como em rotação/escala, e a aplicação precisa apenas de translação após corrigir a rotação e a escala. O comparador executa primeiro o passo log-polar para recuperar a rotação e a escala, aplica o inverso a uma das imagens, depois executa o passo de translação para recuperar o deslocamento restante. O parâmetro só é relevante quando logpolar=False – pede ao comparador em modo de translação que primeiro elimine a rotação/escala.

O padrão das transformações polares – Cartesiano → polar → correspondência – é o que find_displacement() com logpolar=True faz numa única chamada. A aplicação armazena um fragmento log-polar de referência no arranque, captura e transforma log-polarmente cada fotograma ao vivo, e o método recupera a diferença de rotação e escala entre eles. Para aplicações que necessitem de um rastreador invariante a rotação e escala – um robô de acoplagem cuja câmara inclina e faz zoom ao aproximar-se de um alvo, um cardan estabilizado que precisa de saber como a imagem está a rodar relativamente a uma referência – esta é a construção padrão.

5.31.3. A utilização clássica

A utilização mais comum de find_displacement() é a estimativa de movimento fotograma a fotograma numa pipeline que processa uma câmara em movimento. A câmara captura um pequeno fragmento potência de 2 no fotograma N, captura o fragmento do mesmo tamanho no fotograma N+1, executa find_displacement() nos dois, e lê o deslocamento em pixeis entre eles. O deslocamento é o movimento estimado da câmara (ou da cena, dependendo do referencial que importa) entre as duas capturas, útil para:

  • Sensação estilo fluxo óptico – um drone pairador com uma câmara apontada para baixo utiliza o deslocamento por fotograma para estimar o seu movimento lateral e realimentá-lo no controlador de voo.

  • Estabilização de imagem – o deslocamento entre fotogramas consecutivos é subtraído da imagem capturada antes de ser gravada ou transmitida, produzindo um fluxo de vídeo mais suave.

  • Alinhamento de inspeção – uma câmara de digitalização que se move ao longo de um tapete rolante utiliza o deslocamento por fotograma para registar cada fotograma em relação ao seguinte e construir uma vista unida de toda a peça.

Cada uma dessas aplicações tem a mesma forma: capturar, deslocar, acumular numa estimativa corrente, capturar novamente.