5.31. Corrispondenza per spostamento

Il template matching risponde a dove si trova questo riquadro all’interno del frame; lo scoring di similarità risponde a quanto sono simili nel complesso queste due immagini. Tra le due si colloca una domanda diversa: i due frame mostrano la stessa scena, ma la camera (o la scena) si è spostata tra l’uno e l’altro – di quanto? Questo è il problema dello spostamento, e il modulo image lo risolve con un unico metodo a correlazione di fase.

5.31.1. Spostamento per correlazione di fase

find_displacement() stima l’allineamento rigido tra due immagini delle stesse dimensioni usando la correlazione di fase – un metodo nel dominio della frequenza che esegue una trasformata di Fourier veloce (FFT) su ciascuna immagine, ne correla incrociatamente le fasi e individua il picco nel risultato. La posizione del picco è la traslazione che allinea le due immagini:

d = img.find_displacement(template)

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

L’oggetto Displacement restituito contiene x_translation e y_translation – lo spostamento in pixel su ciascun asse – più response, un punteggio di confidenza da 0.0 a 1.0 dove 1.0 è un picco perfetto. Filtrare i rilevamenti al di sotto di response > 0.3 scarta i risultati spuri in cui la correlazione di fase non ha mai trovato un picco netto.

Sia rotation che scale valgono rispettivamente 0.0 e 1.0 nella modalità predefinita; assumono valori reali solo quando logpolar=True (vedi sotto).

Il metodo comporta due vincoli pratici. Il primo riguarda le dimensioni potenza di due: la FFT al cuore della correlazione di fase è più veloce – e sulla camera è pienamente supportata solo – a dimensioni come 32 per 32, 64 per 64 e 128 per 128. La configurazione più pulita è catturare direttamente a una di queste dimensioni, passando la risoluzione a framesize() come tupla:

csi0.framesize((64, 64))

Un’applicazione che ha invece bisogno dello spostamento a partire da un frame più grande ritaglia un riquadro potenza di due dalla regione che le interessa ed esegue il matcher su quello.

Il secondo riguarda gli input delle stesse dimensioni: roi e template_roi devono selezionare larghezze e altezze identiche, altrimenti il matcher rifiuta la chiamata. Due catture dalla stessa camera con la stessa configurazione soddisfano automaticamente questo requisito; un frame catturato confrontato con un riferimento caricato richiede che entrambi vengano prima ritagliati a riquadri potenza di due corrispondenti.

5.31.2. Rotazione e scala tramite log-polare

La modalità predefinita trova solo la traslazione. Quando i due frame differiscono anche in rotazione attorno a un centro scelto o in scala attorno allo stesso centro, eseguire la correlazione di fase sulla riproiezione log-polare di ciascuna immagine trasforma quei parametri in traslazione nel sistema di coordinate log-polare – che lo stesso matcher a correlazione di fase può recuperare:

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

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

Con logpolar=True, il metodo esegue la stessa pipeline di matching sulle immagini proiettate in log-polare anziché sugli originali. I campi rotation e scale del risultato vengono restituiti compilati: rotation è l’angolo in radianti tra i due frame, scale è il fattore di scala tra di essi. x_translation e y_translation perdono significato in questa modalità (la traslazione lungo gli assi log-polari non corrisponde a una traslazione lineare nella sorgente).

La parola chiave fix_rotation_scale=True copre il caso intermedio: le due immagini differiscono sia in traslazione che in rotazione/scala, e l’applicazione ha bisogno solo della traslazione dopo aver corretto rotazione e scala. Il matcher esegue prima il passaggio log-polare per recuperare rotazione e scala, applica l’inversa a una delle immagini, quindi esegue il passaggio di traslazione per recuperare lo spostamento rimanente. Il flag ha significato solo quando logpolar=False – chiede al matcher in modalità traslazione di rimuovere prima la rotazione/scala.

Il pattern delle trasformate polari – cartesiano → polare → match – è ciò che find_displacement() con logpolar=True fa in una sola chiamata. L’applicazione memorizza un riquadro log-polare di riferimento all’avvio, cattura e trasforma in log-polare ogni frame in tempo reale, e il metodo recupera la differenza di rotazione e scala tra di essi. Per le applicazioni che hanno bisogno di un tracker invariante a rotazione e scala – un robot di attracco la cui camera si inclina e zooma mentre si avvicina a un bersaglio, un gimbal stabilizzato che deve sapere come l’immagine sta ruotando rispetto a un riferimento – questa è la costruzione standard.

5.31.3. L’uso classico

L’uso più comune di find_displacement() è la stima del movimento frame-to-frame in una pipeline che elabora una camera in movimento. La cam cattura un piccolo riquadro potenza di 2 al frame N, cattura il riquadro delle stesse dimensioni al frame N+1, esegue find_displacement() sui due e legge lo spostamento in pixel tra di essi. Lo spostamento è il movimento stimato della camera (o della scena, a seconda di quale sistema di riferimento conti) tra le due catture, utile per:

  • Rilevamento in stile flusso ottico – un drone in hovering con una camera rivolta verso il basso usa lo spostamento per frame per stimare il proprio movimento laterale e ritornarlo al controllore di volo.

  • Stabilizzazione dell’immagine – lo spostamento tra frame consecutivi viene sottratto dall’immagine catturata prima che venga registrata o trasmessa, producendo un flusso video più fluido.

  • Allineamento per ispezione – una cam di scansione che si muove lungo un nastro trasportatore usa lo spostamento per frame per registrare ogni frame rispetto al successivo e costruire una vista cucita dell’intero pezzo.

Ognuna di queste applicazioni assume la stessa forma: cattura, sposta, accumula in una stima corrente, cattura di nuovo.