Come viene implementata la sfocatura gaussiana?


42

Ho letto che la sfocatura viene eseguita in grafica in tempo reale eseguendola su un asse e poi sull'altro.

Ho fatto un po 'di convoluzione in 1D in passato, ma non mi sento molto a mio agio con esso, né so cosa convolgere esattamente in questo caso.

Qualcuno può spiegare in termini semplici come viene eseguita una sfocatura gaussiana 2D di un'immagine?

Ho anche sentito che il raggio della sfocatura può influire sulle prestazioni. Ciò è dovuto al fatto di dover fare una convoluzione più ampia?

Risposte:


48

In convoluzione, due funzioni matematiche sono combinate per produrre una terza funzione. Le funzioni di elaborazione delle immagini sono generalmente chiamate kernel. Un kernel non è altro che una matrice (quadrata) di pixel (una piccola immagine per così dire). Di solito, i valori nel kernel si sommano a uno. Questo per assicurarsi che non venga aggiunta o rimossa energia dall'immagine dopo l'operazione.

In particolare, un kernel gaussiano (usato per la sfocatura gaussiana) è una matrice quadrata di pixel in cui i valori dei pixel corrispondono ai valori di una curva gaussiana (in 2D).

Immagine collegata da http://homepages.inf.ed.ac.uk/rbf/HIPR2/gsmooth.htm

Ogni pixel nell'immagine viene moltiplicato per il kernel gaussiano. Questo viene fatto posizionando il pixel centrale del kernel sul pixel dell'immagine e moltiplicando i valori nell'immagine originale con i pixel nel kernel che si sovrappongono. I valori risultanti da queste moltiplicazioni vengono sommati e quel risultato viene utilizzato per il valore nel pixel di destinazione. Guardando l'immagine, si moltiplicherebbe il valore in (0,0) nell'array di input per il valore in (i) nell'array del kernel, il valore in (1,0) nell'array in input per il valore in (h ) nell'array del kernel e così via. e quindi aggiungere tutti questi valori per ottenere il valore per (1,1) nell'immagine di output.

Immagine collegata da http://www.songho.ca/dsp/convolution/convolution.html

Per rispondere prima alla tua seconda domanda, più grande è il kernel, più costosa è l'operazione. Quindi, maggiore è il raggio della sfocatura, maggiore sarà il tempo necessario per l'operazione.

Per rispondere alla tua prima domanda, come spiegato sopra, la convoluzione può essere fatta moltiplicando ciascun pixel di input con l'intero kernel. Tuttavia, se il kernel è simmetrico (come è un kernel gaussiano), puoi anche moltiplicare ogni asse (xey) in modo indipendente, il che ridurrà il numero totale di moltiplicazioni. In termini matematici appropriati, se una matrice è separabile, può essere scomposta in matrici (M × 1) e (1 × N). Per il kernel gaussiano sopra questo significa che puoi anche usare i seguenti kernel:

1256[1464141624164624362464162416414641]=1256[14641][14641]

Ora moltiplicheresti ogni pixel nell'immagine di input con entrambi i kernel e aggiungeresti i valori risultanti per ottenere il valore per il pixel di output.

Per maggiori informazioni su come vedere se un kernel è separabile, segui questo link .

Modifica: i due kernel mostrati sopra usano valori leggermente diversi. Questo perché il parametro (sigma) utilizzato per la curva gaussiana per creare questi kernel era leggermente diverso in entrambi i casi. Per una spiegazione su quali parametri influenzano la forma della curva gaussiana e quindi i valori nel kernel seguono questo link

Modifica: nella seconda immagine sopra dice che il kernel che viene usato è capovolto. Questo ovviamente fa la differenza solo se il kernel che usi non è simmetrico. Il motivo per cui è necessario capovolgere il kernel ha a che fare con le proprietà matematiche dell'operazione di convoluzione (vedere il link per una spiegazione più approfondita sulla convoluzione). In poche parole: se non si gira il kernel, il risultato dell'operazione di convoluzione verrà capovolto. Lanciando il kernel, si ottiene il risultato corretto.


1
Potresti aggiungere una breve nota per spiegare perché i due diversi kernel 5 per 5 hanno numeri leggermente diversi (uno sommando a 273, l'altro sommando a 256)? Sembra una potenziale confusione per qualcuno di nuovo a questo.
trichoplax,

Allo stesso modo, potresti spiegare perché il kernel è capovolto nel secondo diagramma? Non penso sia rilevante per la spiegazione, ma il fatto che si tratti di un evidente passaggio in più può ostacolare la comprensione di qualcuno che non sa che non è necessario.
trichoplax,

non dimenticare di lavorare in uno spazio cromatico lineare per risultati corretti.
v.

16

Ecco il miglior articolo che ho letto sull'argomento: efficiente sfocatura gaussiana con campionamento lineare . Affronta tutte le tue domande ed è davvero accessibile.

Per i laici spiegazione molto breve: il gaussiano è una funzione con la bella proprietà di essere separabile, il che significa che una funzione gaussiana 2D può essere calcolata combinando due funzioni gaussiane 1D.

Quindi per una dimensione ( ), devi solo valutare valori ( ), che è significativamente inferiore. Se la tua operazione consiste nella lettura di un elemento texture (comunemente chiamato "tap" ), è una buona notizia: meno tocchi è più economico perché un recupero di texture ha un costo.n×nO(n2)2×nO(n)

Ecco perché gli algoritmi di sfocatura utilizzano quella proprietà facendo due passaggi, uno per sfocare orizzontalmente raccogliendo pixel orizzontali e uno per sfocare verticalmente raccogliendo pixel verticali. Il risultato è il colore finale del pixel sfocato.nn


13

In generale, una convoluzione viene eseguita prendendo l'integrale del prodotto di due funzioni in una finestra scorrevole, ma se non provieni da un background matematico, questa non è una spiegazione molto utile e sicuramente non ti darà un'intuizione utile per questo. Più intuitivamente, una convoluzione consente a più punti in un segnale di ingresso di influenzare un singolo punto su un segnale di uscita.

Dal momento che non ti senti a tuo agio con le convoluzioni, esaminiamo prima cosa significa una convoluzione in un contesto discreto come questo, quindi passiamo a una sfocatura più semplice.

Nel nostro contesto discreto, possiamo moltiplicare i nostri due segnali semplicemente moltiplicando ciascun campione corrispondente. Anche l'integrale è semplice da eseguire in modo discreto, sommiamo ogni campione nell'intervallo su cui ci stiamo integrando. Una semplice convoluzione discreta sta calcolando una media mobile. Se vuoi prendere la media mobile di 10 campioni, questo può essere considerato come un convolgere il tuo segnale da una distribuzione lunga 10 campioni e alta 0,1, ogni campione nella finestra viene prima moltiplicato per 0,1, quindi tutti e 10 vengono sommati per produrre la media. Ciò rivela anche una distinzione interessante e importante, quando si confonde con una convoluzione, la distribuzione che si usa dovrebbe sommarsi a 1,0 su tutti i suoi campioni, altrimenti aumenterà o diminuirà la luminosità complessiva dell'immagine quando la si applica.

Ora che abbiamo esaminato le convoluzioni, possiamo passare alle sfocature. Una sfocatura gaussiana viene implementata contorcendo un'immagine di una distribuzione gaussiana. Altre sfocature sono generalmente implementate contorcendo l'immagine con altre distribuzioni. La sfocatura più semplice è la sfocatura del riquadro, che utilizza la stessa distribuzione descritta in precedenza, un riquadro con l'area dell'unità. Se vogliamo sfocare un'area 10x10, moltiplichiamo ogni campione nella casella per 0,01 e quindi li sommiamo tutti insieme per produrre il pixel centrale. Dobbiamo ancora assicurarci che la somma totale di tutti i campioni nella nostra distribuzione della sfocatura sia 1.0 per assicurarci che l'immagine non diventi più chiara o più scura.

r

e-X2/22π

O(n2)O(n).


1
Guardando la tua altra risposta, sembra che il tuo background in matematica sia migliore di quello con cui stavo lavorando, ma spero che sia ancora nei dettagli sufficienti per essere utile. Volevo che fosse utile per le persone di qualsiasi estrazione.
porglezomp,

1
Se stai parlando con me, per niente. La tua risposta e quella di Bert sono incredibilmente illuminanti. Grazie mille! Ora devo digerire un po 'le informazioni (:
Alan Wolfe il

11

O(n2)O(n)

Ma ci sono altri due trucchi che potresti voler prendere in considerazione in un'implementazione effettiva:

Il filtro ha un certo raggio e per questo, proprio ai bordi, dovrai calcolare con pixel che cadono al di fuori dell'immagine. In tal caso, potresti provare uno dei seguenti: per i pixel esterni prendi semplicemente l'ultimo valore possibile (cioè il pixel proprio sul bordo, come in max(x, 0). O potresti "riflettere" l'immagine verso l'esterno (come in x < 0 ? -x : x). Oppure potresti semplicemente fermarti al confine, ma allora dovresti regolare il denominatore nel filtro di convoluzione in modo che somma fino a 1. Ad esempio:

somma1256[1464141624164624362464162416414641]=somma1225[0000001624160024361600162416000000]=1.
     1
    1 1
   1 2 1
  1 3 3 1
[1 4 6 4 1]
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.