5.31. Correspondência por deslocamento

A correspondência por modelo responde onde está este trecho dentro do quadro; a pontuação de similaridade responde quão parecidas são essas duas imagens no geral. Uma questão diferente fica entre elas: os dois quadros mostram a mesma cena, mas a câmera (ou a cena) se moveu entre eles – por quanto? Esse é o problema do deslocamento, e o módulo image o resolve 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 de mesmo tamanho usando correlação de fase – um método no domínio da frequência que executa uma transformada rápida de Fourier (FFT) em cada imagem, faz a correlação cruzada de 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 Displacement retornado carrega x_translation e y_translation – o deslocamento em pixels em cada eixo – além de response, uma pontuação de confiança de 0.0 a 1.0 em que 1.0 é um pico perfeito. Filtrar as detecções abaixo de response > 0.3 descarta resultados espúrios nos quais a correlação de fase nunca encontrou um pico limpo.

Tanto rotation quanto scale são 0.0 e 1.0 respectivamente no modo padrão; eles assumem valores reais somente quando logpolar=True (veja abaixo).

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

csi0.framesize((64, 64))

Uma aplicação que precisa do deslocamento a partir de um quadro maior, em vez disso, recorta um trecho em potência de dois da região com a qual se importa e executa o correspondente sobre ele.

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

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

O modo padrão encontra apenas a translação. Quando os dois quadros 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 sobre a reprojeção log-polar de cada imagem transforma esses parâmetros em translação no sistema de coordenadas log-polar – que o mesmo correspondente por 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 das originais. Os campos rotation e scale do resultado voltam preenchidos: rotation é o ângulo em radianos entre os dois quadros, scale é o fator de escala entre eles. x_translation e y_translation tornam-se sem significado nesse modo (a translação ao longo dos eixos log-polar não corresponde a uma translação linear na origem).

A palavra-chave fix_rotation_scale=True cobre o caso intermediário: as duas imagens diferem em ambos translação e rotação/escala, e a aplicação precisa de apenas a translação após corrigir a rotação e a escala. O correspondente executa primeiro a passagem log-polar para recuperar a rotação e a escala, aplica a inversa a uma das imagens, e então executa a passagem de translação para recuperar o deslocamento restante. A flag só é significativa quando logpolar=False – ela pede ao correspondente em modo de translação que primeiro remova a rotação/escala.

O padrão das transformações polares – cartesiano → polar → correspondência – é o que find_displacement() com logpolar=True faz em uma única chamada. A aplicação armazena um trecho log-polar de referência na inicialização, captura e transforma em log-polar cada quadro ao vivo, e o método recupera a diferença de rotação-e-escala entre eles. Para aplicações que precisam de um rastreador invariante a rotação e a escala – um robô de acoplamento cuja câmera inclina e dá zoom à medida que se aproxima de um alvo, um gimbal estabilizado que precisa saber como a imagem está rotacionando em relação a uma referência – esta é a construção padrão.

5.31.3. O uso clássico

O uso mais comum de find_displacement() é a estimativa de movimento quadro a quadro em um pipeline que processa uma câmera em movimento. A câmera captura um pequeno trecho em potência de 2 no quadro N, captura o trecho de mesmo tamanho no quadro N+1, executa find_displacement() sobre os dois e lê o deslocamento em pixels entre eles. O deslocamento é o movimento estimado da câmera (ou da cena, dependendo de qual quadro de referência importa) entre as duas capturas, útil para:

  • Sensoriamento no estilo fluxo óptico – um drone em voo pairado com uma câmera apontada para baixo usa o deslocamento por quadro para estimar seu movimento lateral e realimentá-lo no controlador de voo.

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

  • Alinhamento para inspeção – uma câmera de varredura movendo-se ao longo de uma esteira usa o deslocamento por quadro para registrar cada quadro em relação ao próximo e construir uma visão costurada da peça inteira.

Cada uma dessas aplicações assume a mesma forma: capturar, deslocar, acumular em uma estimativa contínua, capturar de novo.