Risposte rapide alle tue domande individuali
Cosa fai se la tua immagine non ha le stesse dimensioni su ciascun asse?
Il documento utilizza immagini quadrate con lunghezze laterali che hanno una potenza di 2. Questo è per facilità di spiegazione, ma non è necessario per il funzionamento dell'algoritmo. Vedi sezione 3.1:
Senza perdita di generalità, possiamo supporre che n sia un potere di 2.
Cioè, questo presupposto non è necessario per il funzionamento dell'algoritmo.
È possibile eseguire JFA su ciascun asse separatamente e ottenere comunque risultati decenti?
L'esecuzione su ciascun asse separatamente può dare risultati pixel più errati e richiedere più tempo per l'esecuzione nella maggior parte dei casi. In casi estremi in cui una delle lunghezze laterali dell'immagine è inferiore a 8 (il numero di direzioni di salto), potrebbe essere più veloce in quanto l'algoritmo tratta sequenzialmente queste 8 direzioni, ma per ogni immagine più ampia, separando gli assi si perde il vantaggio di trattarli in parallelo.
Nella mia situazione idealmente sto cercando di supportare sia semi a singolo punto, sia semi di forma arbitraria
L'articolo menziona semi di forma arbitraria nella sezione 6 della sottovoce "Diagramma Voronoi generalizzato":
... i nostri algoritmi trattano tali semi generalizzati come raccolte di semi di punti e quindi si aspettano di ereditare le buone prestazioni ottenute per i semi di punti.
Quindi, purché si adatti al tuo scopo di modellare forme arbitrarie come raccolte di pixel, non è necessario apportare alcuna modifica all'algoritmo. Inserisci semplicemente una trama che etichetta tutti i pixel in un seme di forma arbitraria con lo stesso numero di seme, ma in posizioni diverse.
Probabilmente anche semi ponderati, in cui la distanza da un seme viene regolata da un moltiplicatore e / o un sommatore per dargli più o meno influenza
Per "ponderazione su semi come moltiplicativi e additivi", l'articolo menziona solo la possibilità di passare nella sezione 8, come potenziale lavoro futuro. Tuttavia, questo dovrebbe essere semplice da implementare a condizione che la ponderazione desiderata possa essere inclusa nei dati di seed passati da pixel a pixel.
L'algoritmo corrente passa <s, position(s)>
per specificare un seme e la sua posizione e viene memorizzato un solo seme per pixel alla volta. L'estensione di questo per memorizzare <s, position(s), weight(s)>
fornisce tutte le informazioni necessarie per ponderare la funzione di distanza e calcolare se il nuovo seme che viene passato a un pixel è più vicino ad esso rispetto a quello che sta attualmente memorizzando.
È anche possibile includere due pesi, uno moltiplicativo e uno additivo, e impostare solo quello moltiplicativo su 1 e quello additivo su 0 quando non richiesto. Quindi il tuo algoritmo includerebbe la possibilità di essere utilizzato per semi con peso moltiplicato, semi con peso additivo o anche una combinazione di entrambi contemporaneamente o alcuni di ciascuno. Questo avrebbe solo bisogno
<s, position(s), multiplicative(s), additive(s)>
e l'attuale algoritmo sarebbe equivalente a questo nuovo algoritmo usando
<s, position(s), 1, 0>
Spiegazione dettagliata del perché
log()
Non è necessario che l'algoritmo sia adattato per diverse lunghezze laterali
Se le lunghezze laterali non sono uguali e non hanno potenze di 2, non è necessario adattare l'algoritmo. Si occupa già di pixel sul bordo dell'immagine per i quali alcune delle direzioni di salto portano all'esterno dell'immagine. Poiché l'algoritmo omette già le informazioni sul seme per le direzioni che conducono all'esterno dell'immagine, una larghezza o altezza che non è una potenza di 2 non sarà un problema. Per un'immagine di larghezza W e altezza H, la dimensione massima del salto richiesta sarà
2⌈log(max(W,H))⌉−1
Nel caso di uguale larghezza e altezza N, ciò si riduce a
2⌈log(N)⌉−1
Nel caso di una lunghezza laterale N che è una potenza di 2, questo si riduce a
2log(N)−1=N/2
come usato nella carta.
In termini più intuitivi, arrotondare la lunghezza massima del lato fino alla potenza successiva di 2, quindi dimezzarlo per ottenere la massima dimensione del salto.
Questo è sempre sufficiente per coprire ogni pixel nell'immagine da ogni altro pixel nell'immagine, poiché l'offset a qualsiasi pixel sarà compreso nell'intervallo da 0 a N-1 se la lunghezza del lato più lungo è N. Combinazioni delle potenze di 2 da Da 0 a N / 2 coprirà ogni numero intero fino a N-1 se N è una potenza di 2, e se N non è una potenza di 2 l'intervallo coperto può essere solo maggiore del necessario, a causa della presa del massimale del logaritmo ( arrotondando per eccesso alla potenza successiva di 2).
Le immagini con lati diversi da una potenza di 2 non saranno drasticamente meno efficienti
Il numero di salti dipende dalla lunghezza del lato più lungo, ad esempio L. Se L è una potenza di 2, il numero di salti è . Se L non è una potenza di 2, il numero di salti è . Per un'immagine abbastanza grande questa non sarà una grande differenza.log(L)⌈log(L)⌉
Ad esempio, un'immagine 1024 per 1024 richiederà 10 iterazioni di salto. Un'immagine 512 per 512 richiederà 9 iterazioni di salto. Qualunque cosa tra le due dimensioni richiederà anche 10 iterazioni. Anche nel peggiore dei casi di un'immagine con appena una potenza di 2, come un'immagine 513 per 513, richiederà solo 1 iterazione aggiuntiva, che su questa scala è circa l'11% in più (10 anziché 9).
Le immagini non quadrate sono meno efficienti per area
Poiché il numero di iterazioni richieste è determinato dalla lunghezza laterale più lunga, il tempo impiegato per un'immagine 1024 per 1024 sarà lo stesso di un'immagine 1024 per 16. Un'immagine quadrata consente di coprire un'area più ampia nello stesso numero di iterazioni.
È probabile che la separazione degli assi riduca la qualità
La sezione 5 del documento descrive possibili errori. Ogni pixel è raggiungibile da un percorso da ogni altro pixel, ma alcuni percorsi non portano il seme più vicino corretto, poiché quel seme non è il più vicino a un pixel precedente nel percorso. Un pixel che non consente a un seme di propagarsi oltre si dice che abbia "ucciso" quel seme. Se il seme più vicino a un pixel viene ucciso su tutti i percorsi di quel pixel, il pixel registrerà un altro seme e ci sarà un colore errato nell'immagine finale.
È necessario un solo percorso che non uccida il seme affinché il risultato finale sia corretto. I colori errati si verificano solo quando vengono bloccati tutti i percorsi dal seme corretto a un determinato pixel.
Se un percorso prevede alternanza di salti orizzontali e verticali, la separazione degli assi renderà impossibile questo percorso (tutti i salti orizzontali verranno effettuati prima di tutti i salti verticali, rendendo impossibile l'alternanza). I salti diagonali non saranno affatto possibili. Pertanto, qualsiasi percorso che alterna o contiene salti diagonali sarà escluso. Ogni pixel avrà comunque un percorso per ogni altro pixel, ma poiché ora ci sono meno percorsi, è più probabile che un determinato pixel venga bloccato dalla ricezione del seme corretto, quindi il risultato finale sarà più soggetto a errori.
È probabile che la separazione degli assi richieda più tempo per l'algoritmo
L'efficienza verrebbe probabilmente ridotta separando gli assi, poiché l'inondazione non verrebbe più eseguita in parallelo, ma sarebbe invece ripetuta per ciascun asse. Per il 2D ciò richiederebbe probabilmente circa il doppio del tempo e per il 3D circa il triplo del tempo.
Ciò può essere in qualche modo mitigato dalla mancanza di salti diagonali, ma mi aspetterei comunque una riduzione complessiva dell'efficienza.