5.14. Smussamento gaussiano e bordi¶
Due compiti dominano l’uso delle finestre di vicinato nella visione artificiale classica: smussare in modo pulito le variazioni da pixel a pixel e trovare i bordi dove l’immagine cambia bruscamente. Il filtro gaussiano è lo strumento standard per il primo, i rilevatori basati sul laplaciano lo strumento standard per il secondo, e i due si compongono, perché ogni rilevatore di bordi funziona meglio su un ingresso leggermente smussato.
5.14.1. Il filtro gaussiano¶
gaussian() è il cugino pesato verso il centro di mean(). Entrambi calcolano una media sul vicinato di ciascun pixel, ma i pesi del gaussiano non sono uniformi: i pixel più vicini al centro del vicinato contano di più, i pixel al bordo del vicinato contano di meno, con i pesi che seguono la familiare curva a campana che dà al filtro il suo nome.
La pesatura a forma di campana è ciò che rende un filtro gaussiano più morbido di una media a riquadro. Il filtraggio a media può produrre artefatti visibili ai bordi degli oggetti: un taglio netto nella pesatura introduce piccoli pattern di ringing in corrispondenza di transizioni brusche. I pesi del gaussiano, che decrescono in modo regolare, evitano quel ringing e producono un risultato più simile a ciò che una «sfocatura» dovrebbe apparire. Il costo è una maggiore computazione per pixel rispetto al filtro a media, ma non in modo drammatico: il costo per pixel è comunque molto inferiore a quello del filtro bilaterale.
img.gaussian(1) # 3x3 Gaussian -- a clean light blur
img.gaussian(2) # 5x5 Gaussian -- stronger smoothing
Lo smussamento gaussiano è il primo stadio standard di quasi tutte le pipeline di rilevamento dei bordi. I rilevatori di bordi più sotto amplificano tutti il contenuto ad alta frequenza, incluso il rumore del sensore che l’algoritmo in realtà non vuole rilevare. Eseguire prima un leggero gaussiano sopprime quel rumore senza ammorbidire troppo i bordi reali, lasciando al rilevatore di bordi il compito di trovare bordi reali anziché disturbi.
5.14.2. Maschera di contrasto (unsharp masking)¶
Una copia sfocata in modo gaussiano di un’immagine è la materia prima che la tecnica della maschera di contrasto (unsharp mask) utilizza per la nitidezza classica. Impostare unsharp=True sul filtro lo commuta da «produci l’immagine sfocata» a «sottrai l’immagine sfocata dall’originale e aggiungi nuovamente la differenza all’originale»: l’effetto è che i bordi ad alta frequenza vengono amplificati rispetto agli interni regolari.
img.gaussian(1, unsharp=True)
I parametri opzionali mul e add scalano l’intensità del risultato dell’unsharp; i valori predefiniti (mul=1.0, add=0.0) producono una nitidezza moderata che non esagera il rumore del sensore.
5.14.3. Il filtro laplaciano¶
laplacian() esegue un’approssimazione discreta della derivata seconda spaziale dell’immagine. L’uscita è grande dove i valori dei pixel cambiano rapidamente, prossima a zero dove sono costanti o variano linearmente. La lettura naturale del risultato è una risposta ai bordi: i pixel dove l’immagine cambia rapidamente si illuminano, i pixel negli interni regolari restano scuri.
img.laplacian(1) # 3x3 Laplacian -- edge response
Sono disponibili gli stessi parametri di gaussian. sharpen=True produce un’immagine resa più nitida (il laplaciano aggiunto nuovamente all’originale anziché restituito da solo). mul e add scalano la risposta.
Un uso pratico oltre al rilevamento dei bordi è la misura della messa a fuoco. La risposta laplaciana mediata su una regione fornisce una misura approssimativa di quanto contenuto ad alta frequenza la regione contiene; su un frame ben a fuoco quella media è alta, su un frame sfocato cala. Confrontare la risposta laplaciana tra i frame è il modo economico per chiedersi «l’obiettivo è a fuoco?» senza bisogno di una metrica di contrasto più costosa.
5.14.4. Il metodo find_edges¶
find_edges() esegue una pipeline completa di rilevamento dei bordi anziché un semplice filtro di risposta ai bordi. Funziona su immagini in scala di grigi, e il risultato è un’immagine binaria i cui pixel diversi da zero segnano le posizioni in cui l’ingresso presenta il tipo di variazione di luminosità che dovrebbe contare come bordo.
Il metodo accetta un parametro edge_type che seleziona tra due algoritmi:
EDGE_SIMPLE esegue un filtro passa-alto, applica una soglia e restituisce il risultato. Veloce, ma l’uscita include ogni variazione di luminosità al di sopra della soglia, incluso rumore e texture di cui l’applicazione probabilmente non si cura. Ragionevole per immagini pulite e per i casi in cui il rumore verrà ripulito da un successivo passaggio morfologico.
EDGE_CANNY esegue il rilevatore di bordi Canny, il classico algoritmo multistadio. Calcola il gradiente di luminosità, sopprime ogni risposta non massima lungo la direzione del gradiente (così ogni bordo è largo un pixel) e applica una soglia con isteresi (così un bordo che è forte in un punto viene tracciato anche dove sbiadisce nel mezzo). Il risultato è un insieme pulito, sottile e connesso di pixel di bordo del tipo che ogni algoritmo classico basato sui bordi desidera.
Il parametro threshold è una tupla a due elementi (low, high). Per EDGE_CANNY, il valore high è il taglio al di sopra del quale un pixel è sicuramente un bordo, e il valore low è il taglio al di sopra del quale un pixel è un bordo solo se è connesso a uno sicuro. Per EDGE_SIMPLE, conta solo il valore high; è l’unico taglio al di sopra del quale un pixel conta come bordo. Il valore predefinito (100, 200) è un punto di partenza che vale la pena regolare per la scena specifica.
img.gaussian(1) # pre-smooth
img.find_edges(image.EDGE_CANNY, threshold=(50, 100))
Il rilevatore Canny è la scelta migliore per quasi tutte le applicazioni in cui i bordi contano. Il più veloce EDGE_SIMPLE vale la pena ricordarlo per i casi in cui il costo di Canny è un problema e la reiezione del rumore della sua isteresi non è effettivamente necessaria.
5.14.5. Soglia adattiva sul gaussiano¶
Come i filtri statistici, gaussian() accetta la coppia di parametri threshold=True / offset=N per la soglia adattiva. Il comportamento è lo stesso di mean(): la statistica gaussiana in ciascuna posizione diventa il taglio locale, e il pixel sorgente viene confrontato con la statistica più l’offset per produrre un risultato binario.
La variante gaussiana è di solito la scelta più pulita per la soglia adattiva quando l’ingresso è ragionevolmente privo di rumore. La media pesata fornisce un taglio più regolare di quello prodotto dal filtro a media, con meno artefatti in corrispondenza di transizioni brusche dell’illuminazione.