Le trasformazioni 2D si scrivono in modo compatto usando le coordinate omogenee, la cui intuizione geometrica è: un punto nell'immagine è un raggio nello spazio proiettivo. Un punto (x, y) sul piano z = 1 è rappresentato dal raggio (sx, sy, s): tutti i punti del raggio sono equivalenti, perché vengono proiettati nello stesso punto sul piano dell'immagine:
(x, y, 1) ≅ (sx, sy, s)
La conversione da coordinate omogenee a coordinate non omogenee è immediata: si divide per la coordinata w:
(x, y, w) ⇒ (x/w, y/w)
Da qui una proprietà centrale: le coordinate omogenee sono invarianti per cambiamenti di scala — moltiplicare tutte le coordinate per una costante non cambia il punto rappresentato.
Muovi il fattore di scala s: il punto (sx, sy, s) scorre lungo il raggio, ma la proiezione sul piano z = 1 resta fissa. È il motivo per cui la matrice dell'omografia è definita a meno di un fattore di scala.
L'image stitching (o costruzione di panorami) parte da una domanda: come mettere in relazione due immagini acquisite dalla stessa camera? Dato il punto di osservazione (la camera) e il punto proiettato nel piano P₁ è possibile identificare il raggio; per sapere dove quello stesso raggio viene proiettato nel piano P₂ non è necessario avere informazioni sulla posizione del raggio nel «mondo».
Il mapping tra le due immagini non richiede quindi informazioni 3D: può essere rappresentato semplicemente come trasformazione 2D (warping). L'idea è proiettare le immagini acquisite da diverse angolazioni su un unico piano: l'immagine panoramica.
Questo modello funziona per campi visivi limitati. Nel caso più generale la proiezione non avviene su un piano ma su un cilindro, che viene poi «srotolato» per ottenere una rappresentazione lineare.
I problemi da affrontare sono due: l'allineamento delle diverse immagini e il blending, con attenzione alle variazioni che possono interessare le immagini (per esempio la luce).
Le possibili trasformazioni tra immagini formano una gerarchia: ogni livello è un caso particolare del successivo, e ogni livello preserva un sottoinsieme diverso di proprietà. Le cinque famiglie, in ordine di generalità crescente:
| Trasformazione | Preserva | Forma della matrice |
|---|---|---|
| Traslazione | Lunghezze/aree, angoli, orientazioni, linee | [1 0 t_x; 0 1 t_y; 0 0 1] |
| Cambiamento di scala | Rapporto tra lunghezze/aree, angoli, orientazioni, linee | [s_x 0 0; 0 s_y 0; 0 0 1] |
| Euclidea (rigid body) | Lunghezze/aree, angoli, linee | [cosθ −sinθ t_x; sinθ cosθ t_y; 0 0 1] |
| Affine | Rapporto tra aree, linee parallele, rapporto tra linee | [a b c; d e f; 0 0 1] |
| Proiettiva (omografia) | Linee | [a b c; d e f; g h 1] |
Leggere la tabella dal basso verso l'alto è il modo migliore per fissarla: la proiettiva preserva solo le linee; l'affine in più il parallelismo e i rapporti di area; l'euclidea le lunghezze e gli angoli; la traslazione perfino le orientazioni. La scala è un caso particolare di affine (matrice diagonale).
La traslazione sposta il punto di t_x, t_y. Preserva lunghezze/aree, angoli, orientazioni e linee:
⎡x′⎤ ⎡1 0 t_x⎤ ⎡x⎤ x′ = x + t_x
⎢y′⎥ = ⎢0 1 t_y⎥ ⎢y⎥ y′ = y + t_y
⎣1 ⎦ ⎣0 0 1 ⎦ ⎣1⎦
La scala moltiplica le coordinate per s_x e s_y. Preserva il rapporto tra lunghezze/aree (non le lunghezze assolute), gli angoli, le orientazioni e le linee:
⎡x′⎤ ⎡s_x 0 0⎤ ⎡x⎤ x′ = s_x · x
⎢y′⎥ = ⎢0 s_y 0⎥ ⎢y⎥ y′ = s_y · y
⎣1 ⎦ ⎣0 0 1⎦ ⎣1⎦
«Quali proprietà preserva la traslazione e quali la scala?» La differenza sottile è lunghezze contro rapporti tra lunghezze: la scala non conserva le dimensioni assolute, ma conserva i rapporti. Entrambe conservano angoli e orientazioni, a differenza della rotazione.
Combina rotazione (angolo θ) e traslazione. Preserva lunghezze/aree, angoli e linee; perde l'invarianza delle orientazioni (l'oggetto ruota con la scena):
⎡x′⎤ ⎡ cosθ −sinθ t_x⎤ ⎡x⎤ x′ = x·cosθ − y·sinθ + t_x
⎢y′⎥ = ⎢ sinθ cosθ t_y⎥ ⎢y⎥ y′ = x·sinθ + y·cosθ + t_y
⎣1 ⎦ ⎣ 0 0 1 ⎦ ⎣1⎦
Generalizza le precedenti con sei parametri a, b, c, d, e, f. Preserva il rapporto tra aree, le linee parallele e il rapporto tra linee — il parallelismo è la proprietà che la distingue dalla proiettiva:
⎡x′⎤ ⎡a b c⎤ ⎡x⎤ x′ = a·x + b·y + c
⎢y′⎥ = ⎢d e f⎥ ⎢y⎥ y′ = d·x + e·y + f
⎣1 ⎦ ⎣0 0 1⎦ ⎣1⎦
Con la scomposizione a = s·cosθ, b = −s·sinθ, c = t_x, d = s·sinθ, e = s·cosθ, f = t_y si ottiene il caso particolare «scala uniforme + rotazione + traslazione».
La trasformazione proiettiva (o omografia) è la più generale tra quelle studiate e preserva soltanto le linee. Nella terza riga compaiono i parametri g e h, che introducono la prospettiva:
⎡x′⎤ ⎡a b c⎤ ⎡x⎤
⎢y′⎥ = ⎢d e f⎥ ⎢y⎥ p′ = H·p
⎣w′⎦ ⎣g h 1⎦ ⎣1⎦
Notare la terza coordinata w′: per tornare alle coordinate dell'immagine si divide per w′, esattamente come nella sezione 1. È la divisione per w′ a rompere il parallelismo: due rette parallele possono convergere in un punto di fuga.
L'omografia è la trasformazione giusta per l'image stitching: quando la camera ruota (o si sposta di poco), la scena fotografata da due posizioni diverse è legata da un'omografia. Per questo il problema dell'allineamento si riduce a: trovare la matrice H che mappa una foto nell'altra.
Scegli una famiglia di trasformazioni e muovi i suoi parametri: la casa grigia è l'immagine originale, quella blu la trasformata, il punto vermiglio è il colmo del tetto. Osserva quali proprietà geometriche sopravvivono (parallelismo, angoli, lunghezze, ...).
Per determinare l'omografia che rappresenta la trasformazione tra due immagini servono (almeno) 4 coppie di punti corrispondenti. Date le corrispondenze è possibile risalire alla matrice H attraverso SVD o il metodo dei minimi quadrati.
L'immagine panoramica si ottiene allineando progressivamente le immagini due a due: prima si stimano le omografie tra coppie consecutive, poi si applicano le trasformazioni e si fondono i risultati. Uno step finale di «normalizzazione» è spesso necessario per rendere invisibile la transizione tra immagini diverse (il blending della sezione 2).
Ma da dove arrivano le corrispondenze? Questo è il ponte verso il prossimo capitolo: i keypoint e i descrittori locali servono proprio a trovare in modo automatico i punti corrispondenti tra due immagini.
Sia f la funzione che rappresenta la trasformazione da applicare. Eseguendo la trasformazione a partire da una scansione dell'immagine di partenza (mapping diretto), si incontrano tre problemi:
Un modo efficace di risolvere questi problemi è il mapping inverso: si scandisce la nuova immagine e, per ogni pixel [x_new, y_new], si determina il punto di riferimento [x_old, y_old] sulla vecchia immagine attraverso la funzione inversa f⁻¹. Per una trasformazione affine:
⎡x_old⎤ ⎡1/s 0 ⎤ ⎡ cos(−θ) −sin(−θ) ⎤ ⎛ ⎡x_new⎤ ⎡t_x⎤ ⎞
⎢y_old⎥ = ⎢ 0 1/s ⎥ ⎢ sin(−θ) cos(−θ) ⎥ · ⎜ ⎢y_new⎥ − ⎢t_y⎥ ⎟
⎣ 1 ⎦ ⎣ 0 0 ⎦ ⎣ 0 0 ⎦ ⎝ ⎣ 1 ⎦ ⎣ 1 ⎦ ⎠
Il punto di riferimento [x_old, y_old], in coordinate continue (floating point), può cadere:
Se [x_old, y_old] cade a metà strada tra quattro pixel A, B, C, D, il valore di intensità del nuovo pixel si calcola interpolando i valori di intensità dei 4 pixel circostanti. L'interpolazione di Lagrange cerca il piano che meglio approssima i 4 punti (ai minimi quadrati):
New I(x_new, y_new) = ( Old I(A)·w_A + Old I(B)·w_B + Old I(C)·w_C + Old I(D)·w_D ) / ( w_A + w_B + w_C + w_D )
con i pesi (indicando con x_old, y_old le coordinate frazionarie dentro la cella, e con x_i, y_j il vertice in basso a sinistra):
w_A = (x_i+1 − x_old)·(y_j+1 − y_old) w_C = (x_old − x_i)·(y_old − y_j)
w_B = (x_old − x_i)·(y_j+1 − y_old) w_D = (x_i+1 − x_old)·(y_old − y_j)
La lettura è semplice: ogni peso è il prodotto delle distanze dal vertice opposto — un vertice vicino al punto riceve un peso grande, uno lontano un peso piccolo.
I quattro vertici della cella hanno intensità A = 40, B = 190, C = 230, D = 95. Muovi il punto frazionario dentro la cella: il valore interpolato segue i pesi della formula di Lagrange.
Perché un punto (x, y, 1) è rappresentato dal raggio (sx, sy, s): tutti i punti del raggio sono equivalenti, essendo proiettati nello stesso punto sul piano dell'immagine. La conversione (x, y, w) → (x/w, y/w) elimina il fattore di scala.
Perché, noti il punto di osservazione e il punto proiettato nel piano P₁, è possibile identificare il raggio; il punto in cui quel raggio viene proiettato nel piano P₂ si determina senza conoscere la posizione del raggio nel mondo. Il mapping è quindi una trasformazione 2D (warping).
L'allineamento delle diverse immagini e il blending, con attenzione alle variazioni che possono interessare le immagini (es. luce). Per campi visivi ampi la proiezione avviene su un cilindro poi srotolato, non su un piano.
Traslazione: lunghezze/aree, angoli, orientazioni, linee. Scala: rapporto tra lunghezze/aree, angoli, orientazioni, linee. Euclidea: lunghezze/aree, angoli, linee. Affine: rapporto tra aree, linee parallele, rapporto tra linee. Proiettiva: linee.
Almeno 4 coppie di punti corrispondenti. Data la matrice p′ = H·p, l'omografia ha 8 parametri liberi (la matrice 3×3 è definita a meno di un fattore di scala); con le corrispondenze si risale a H tramite SVD o metodo dei minimi quadrati.
Allineando progressivamente le immagini due a due: per ogni coppia si stimano le corrispondenze (≥ 4 coppie) e l'omografia, si applica il warping e si fonde. Uno step finale di normalizzazione rende invisibile la transizione tra immagini diverse.
I valori dei nuovi pixel non sono necessariamente interi (serve approssimazione); alcuni pixel vengono mappati fuori dalla nuova immagine; alcuni pixel della nuova immagine non vengono colpiti, lasciando dei buchi.
Si scandisce la nuova immagine e per ogni pixel [x_new, y_new] si determina il punto [x_old, y_old] nella vecchia immagine con la funzione inversa f⁻¹. Se cade fuori dalla vecchia immagine si imposta un valore di base (es. nero); se cade su un pixel si copia l'intensità; se cade tra 4 pixel si interpola.
Come media pesata: New I = (I(A)·w_A + I(B)·w_B + I(C)·w_C + I(D)·w_D) / (w_A + w_B + w_C + w_D), dove ogni peso è il prodotto delle distanze dal vertice opposto: w_A = (x_i+1 − x_old)(y_j+1 − y_old), w_B = (x_old − x_i)(y_j+1 − y_old), w_C = (x_old − x_i)(y_old − y_j), w_D = (x_i+1 − x_old)(y_old − y_j).
L'affine ha l'ultima riga [0 0 1] (6 parametri) e preserva parallelismo e rapporti di aree; la proiettiva ha l'ultima riga [g h 1] (8 parametri) e preserva solo le linee: la divisione per w′ rompe il parallelismo, producendo punti di fuga.