Confronto di immagini - algoritmo veloce


393

Sto cercando di creare una tabella di immagini di base e quindi confrontare eventuali nuove immagini con quelle per determinare se la nuova immagine è un duplicato esatto (o vicino) della base.

Ad esempio: se si desidera ridurre la memorizzazione della stessa immagine 100 volte, è possibile archiviarne una copia e fornire collegamenti di riferimento ad essa. Quando viene inserita una nuova immagine, si desidera confrontare con un'immagine esistente per assicurarsi che non sia un duplicato ... idee?

Una mia idea era quella di ridurre a una piccola miniatura e quindi selezionare casualmente 100 pixel posizioni e confrontarli.

Risposte:


459

Di seguito sono riportati tre approcci per risolvere questo problema (e ce ne sono molti altri).

  • Il primo è un approccio standard nella visione artificiale, la corrispondenza dei punti chiave. Ciò può richiedere alcune conoscenze di base da implementare e può essere lento.

  • Il secondo metodo utilizza solo l'elaborazione elementare delle immagini ed è potenzialmente più veloce del primo approccio ed è semplice da implementare. Tuttavia, ciò che guadagna in comprensibilità, manca di robustezza: la corrispondenza non riesce su immagini ridimensionate, ruotate o scolorite.

  • Il terzo metodo è rapido e robusto, ma è potenzialmente il più difficile da implementare.

Corrispondenza dei punti chiave

Meglio che raccogliere 100 punti casuali è raccogliere 100 punti importanti . Alcune parti di un'immagine hanno più informazioni di altre (in particolare ai bordi e agli angoli) e queste sono quelle che vorrai utilizzare per una corrispondenza intelligente delle immagini. " Estrazione dei punti chiave " di Google e " corrispondenza dei punti chiave " e troverai molti documenti accademici sull'argomento. In questi giorni, i punti chiave SIFT sono probabilmente i più popolari, poiché possono abbinare le immagini con scale, rotazioni e illuminazione diverse. Alcune implementazioni SIFT sono disponibili qui .

Un aspetto negativo della corrispondenza dei punti chiave è il tempo di esecuzione di un'implementazione ingenua: O (n ^ 2m), dove n è il numero di punti chiave in ogni immagine e m è il numero di immagini nel database. Alcuni algoritmi intelligenti potrebbero trovare la corrispondenza più vicina più velocemente, come i quadrifici o il partizionamento dello spazio binario.


Soluzione alternativa: metodo istogramma

Un'altra soluzione meno robusta ma potenzialmente più veloce è quella di creare istogrammi di funzionalità per ogni immagine e scegliere l'immagine con l'istogramma più vicino all'istogramma dell'immagine di input. Ho implementato questo come un laureando e abbiamo usato 3 istogrammi di colore (rosso, verde e blu) e due istogrammi di trama, direzione e scala. Fornirò i dettagli di seguito, ma dovrei notare che questo ha funzionato bene solo per abbinare immagini MOLTO simili alle immagini del database. Le immagini ridimensionate, ruotate o scolorite possono fallire con questo metodo, ma piccole modifiche come il ritaglio non rompono l'algoritmo

Il calcolo degli istogrammi di colore è semplice: basta selezionare l'intervallo per i secchi dell'istogramma e, per ogni intervallo, calcolare il numero di pixel con un colore in quell'intervallo. Ad esempio, considera l'istogramma "verde" e supponiamo di scegliere 4 secchi per il nostro istogramma: 0-63, 64-127, 128-191 e 192-255. Quindi per ogni pixel, esaminiamo il valore verde e aggiungiamo un conteggio al bucket appropriato. Quando abbiamo terminato il conteggio, dividiamo ogni totale di segmenti per il numero di pixel nell'intera immagine per ottenere un istogramma normalizzato per il canale verde.

Per l'istogramma della direzione della trama, abbiamo iniziato eseguendo il rilevamento dei bordi sull'immagine. Ogni punto del bordo ha un vettore normale che punta nella direzione perpendicolare al bordo. Abbiamo quantizzato l'angolo del vettore normale in uno dei 6 bucket tra 0 e PI (poiché i bordi hanno una simmetria di 180 gradi, abbiamo convertito gli angoli tra -PI e 0 per essere tra 0 e PI). Dopo aver calcolato il numero di punti del bordo in ciascuna direzione, abbiamo un istogramma non normalizzato che rappresenta la direzione della trama, che abbiamo normalizzato dividendo ciascun secchio per il numero totale di punti del bordo nell'immagine.

Per calcolare l'istogramma della scala di trama, per ciascun punto del bordo, abbiamo misurato la distanza dal punto del bordo più vicino successivo con la stessa direzione. Ad esempio, se il punto del bordo A ha una direzione di 45 gradi, l'algoritmo cammina in quella direzione fino a quando non trova un altro punto del bordo con una direzione di 45 gradi (o entro una deviazione ragionevole). Dopo aver calcolato questa distanza per ciascun punto del bordo, scarichiamo questi valori in un istogramma e lo normalizziamo dividendo per il numero totale di punti del bordo.

Ora hai 5 istogrammi per ogni immagine. Per confrontare due immagini, prendi il valore assoluto della differenza tra ciascun bucket dell'istogramma e quindi somma questi valori. Ad esempio, per confrontare le immagini A e B, dovremmo calcolare

|A.green_histogram.bucket_1 - B.green_histogram.bucket_1| 

per ogni bucket nell'istogramma verde e ripetere per gli altri istogrammi, quindi riassumere tutti i risultati. Più piccolo è il risultato, migliore è la corrispondenza. Ripeti l'operazione per tutte le immagini nel database e vince la corrispondenza con il risultato più piccolo. Probabilmente vorresti avere una soglia, sopra la quale l'algoritmo conclude che non è stata trovata alcuna corrispondenza.


Terza scelta: punti chiave + alberi decisionali

Un terzo approccio che è probabilmente molto più veloce degli altri due è l'utilizzo di foreste di testo semantico (PDF). Ciò comporta l'estrazione di semplici punti chiave e l'utilizzo di alberi delle decisioni di raccolta per classificare l'immagine. Questo è più veloce della semplice corrispondenza dei punti chiave SIFT, perché evita il costoso processo di corrispondenza e i punti chiave sono molto più semplici di SIFT, quindi l'estrazione dei punti chiave è molto più veloce. Tuttavia, conserva l'invarianza del metodo SIFT alla rotazione, alla scala e all'illuminazione, una caratteristica importante che mancava al metodo dell'istogramma.

Aggiornamento :

Il mio errore: il documento Semantic Texton Forests non riguarda specificamente la corrispondenza delle immagini, ma piuttosto l'etichettatura delle regioni. Il documento originale che fa corrispondere è questo: Riconoscimento di punti chiave usando alberi casuali . Inoltre, i documenti di seguito continuano a sviluppare le idee e rappresentano lo stato dell'arte (c. 2010):


L'approccio dell'istogramma sembra avere più senso. Suppongo che sia possibile ruotare l'immagine per eseguire questa operazione su tutti i lati nel caso in cui l'immagine da confrontare sia stata girata (trattando la stessa immagine di 4) - grazie
meade

4
@meade Proprio così. Qualcos'altro da considerare: a seconda del problema, potrebbe non essere necessario utilizzare tutti e 5 gli istogrammi nel proprio algoritmo. Scartare l'istogramma della direzione della trama ti permetterà di abbinare le versioni ruotate dell'immagine. Scartare l'istogramma della scala di trama ti permetterà di abbinare le versioni ridimensionate dell'immagine. Perderai la capacità di confrontare le somiglianze, ma questo potrebbe non essere un problema, a seconda della tua situazione. Inoltre, poiché il calcolo delle informazioni sulla trama è la parte più costosa dell'algoritmo, anche questo renderà il tuo algoritmo veloce.
Kyle Simek,

@redmoskito: ho una domanda. Come si ottiene ad esempio il valore numerico dell'istogramma del verde? Quindi puoi sottrarlo con l'altro istogramma dell'immagine? Supponiamo di avere un istogramma verde con 3 pixel appartenenti al bucket 0-63 e 5 pixel appartenenti al 64-127. Qual è il valore?
dinamico

3
@Ikaso se è esattamente la stessa immagine, probabilmente non vuoi usare nulla del genere e prendere in considerazione l'uso del semplice confronto CRC o MD5. Se ciò non è sufficiente, come se vi fossero pixel singoli diversi o i metadati siano cambiati, anche il metodo dell'istogramma è sufficiente. se le tue immagini sono uguali ma ruotate o ridimensionate, un metodo basato sull'istogramma può essere sufficiente ma forse fallirà. se le tue immagini hanno cambiato colore, devi utilizzare algoritmi basati sui punti di interesse.
Reox,

5
Vorrei aggiungere che al giorno d'oggi esistono molte alternative rapide a SIFT, come il rivelatore FAST e descrittori binari (BRIEF, BRISK, ORB, FREAK, BinBoost) per citarne alcuni. Un tutorial sui descrittori binari può essere trovato qui: gilscvblog.wordpress.com/2013/08/26/…
GilLevi

85

Il metodo migliore che conosco è usare un Hash percettivo. Sembra che ci sia una buona implementazione open source di tale hash disponibile su:

http://phash.org/

L'idea principale è che ogni immagine è ridotta a un piccolo codice hash o 'impronta digitale' identificando le caratteristiche salienti nel file di immagine originale e hashing una rappresentazione compatta di quelle funzionalità (piuttosto che eseguire l'hashing dei dati dell'immagine direttamente). Ciò significa che il tasso di falsi positivi è molto ridotto rispetto a un approccio semplicistico come la riduzione di immagini fino a un'immagine di dimensioni minuscole di impronte digitali e il confronto di impronte digitali.

phash offre diversi tipi di hash e può essere utilizzato per immagini, audio o video.


Chi è interessante in questo metodo può trovare la realizzazione
dell'Hash

@AlexeyVoitenko È compatibile con gli hash prodotti da phash.org nella sua configurazione predefinita?
Michael,

1
Nella mia esperienza phash funziona bene per trovare dimensioni diverse della stessa immagine, ma non per immagini simili. Ad esempio, due diverse foto dello stesso oggetto potrebbero avere hash molto diversi.
Rena,

39

Questo post è stato il punto di partenza della mia soluzione, molte buone idee qui quindi ho pensato di condividere i miei risultati. L'intuizione principale è che ho trovato un modo per aggirare la lentezza della corrispondenza delle immagini basata su punti chiave sfruttando la velocità di phash.

Per la soluzione generale, è meglio utilizzare diverse strategie. Ogni algoritmo è più adatto per determinati tipi di trasformazioni di immagini e puoi trarne vantaggio.

Nella parte superiore, gli algoritmi più veloci; in fondo il più lento (anche se più preciso). Potresti saltare quelli lenti se si trova una buona corrispondenza al livello più veloce.

  • basato su file hash (md5, sha1, ecc.) per duplicati esatti
  • hashing percettivo (phash) per le immagini ridimensionate
  • basato su funzionalità (SIFT) per immagini modificate

Sto ottenendo ottimi risultati con Phash. La precisione è buona per le immagini ridimensionate. Non va bene per le immagini (percettivamente) modificate (ritagliate, ruotate, specchiate, ecc.). Per gestire la velocità di hashing dobbiamo utilizzare una cache / database del disco per mantenere gli hash per il pagliaio.

La cosa veramente bella di phash è che una volta creato il tuo database di hash (che per me è di circa 1000 immagini / sec), le ricerche possono essere molto, molto veloci, in particolare quando puoi tenere in memoria l'intero database di hash. Questo è abbastanza pratico poiché un hash ha solo 8 byte.

Ad esempio, se si dispone di 1 milione di immagini, sarebbe necessario un array di 1 milione di valori hash a 64 bit (8 MB). Su alcune CPU questo si adatta alla cache L2 / L3! Nell'uso pratico ho visto un corei7 comparare a oltre 1 Giga-hamm / sec, è solo una questione di larghezza di banda di memoria della CPU. Un database da 1 miliardo di immagini è pratico su una CPU a 64 bit (sono necessari 8 GB di RAM) e le ricerche non supereranno 1 secondo!

Per le immagini modificate / ritagliate sembrerebbe una caratteristica invariante trasformata / rilevatore di punti chiave come SIFT è la strada da percorrere. SIFT produrrà buoni punti chiave che rileveranno ritaglia / ruota / specchia ecc. Tuttavia, il confronto del descrittore è molto lento rispetto alla distanza di percussione usata da Phash. Questa è una grande limitazione. Ci sono molti paragoni da fare, poiché ci sono paragoni IxJxK massimi comparabili per cercare un'immagine (I = immagini del pagliaio num, J = punti chiave target per immagine del pagliaio, K = punti chiave target per immagine dell'ago).

Per aggirare il problema della velocità, ho provato a usare phash attorno a ciascun punto chiave trovato, usando la dimensione / raggio della funzione per determinare il rettangolo secondario. Il trucco per far funzionare bene questo è aumentare / ridurre il raggio per generare diversi livelli di sottoretto (sull'immagine dell'ago). In genere il primo livello (senza scala) corrisponderà, tuttavia spesso ne occorrono alcuni di più. Non sono sicuro al 100% del perché funzioni, ma posso immaginare che abiliti funzionalità troppo piccole per far funzionare phash (phash ridimensiona le immagini fino a 32x32).

Un altro problema è che SIFT non distribuirà i punti chiave in modo ottimale. Se c'è una sezione dell'immagine con molti bordi, i punti chiave si raggrupperanno lì e non ne otterrai nessuno in un'altra area. Sto usando GridAdaptedFeatureDetector in OpenCV per migliorare la distribuzione. Non sono sicuro di quale sia la dimensione migliore della griglia, sto usando una piccola griglia (1x3 o 3x1 a seconda dell'orientamento dell'immagine).

Probabilmente vuoi ridimensionare tutte le immagini del pagliaio (e dell'ago) su una dimensione più piccola prima del rilevamento delle caratteristiche (io uso 210 px lungo la dimensione massima). Ciò ridurrà il rumore nell'immagine (sempre un problema per gli algoritmi di visione artificiale), inoltre focalizzerà il rilevatore su funzioni più importanti.

Per le immagini di persone, potresti provare il rilevamento del viso e utilizzarlo per determinare la dimensione dell'immagine su cui ridimensionare e la dimensione della griglia (ad esempio il viso più grande ridimensionato su 100px). Il rilevatore di funzionalità tiene conto di più livelli di scala (usando le piramidi) ma esiste un limite al numero di livelli che utilizzerà (questo è ovviamente sintonizzabile).

Il rilevatore di punti chiave probabilmente funziona meglio quando restituisce meno del numero di funzioni desiderate. Ad esempio, se chiedi 400 e ne ricevi 300, va bene. Se ne ritorni 400 ogni volta, probabilmente alcune caratteristiche interessanti dovevano essere tralasciate.

L'immagine dell'ago può avere meno punti chiave rispetto alle immagini del pagliaio e ottenere comunque buoni risultati. Aggiungere di più non comporta necessariamente enormi guadagni, ad esempio con J = 400 e K = 40 il mio tasso di successo è di circa il 92%. Con J = 400 e K = 400 la percentuale di successo arriva solo al 96%.

Possiamo sfruttare l'estrema velocità della funzione di martellamento per risolvere il ridimensionamento, la rotazione, il mirroring, ecc. È possibile utilizzare una tecnica a più passaggi. Ad ogni iterazione, trasforma i rettangoli secondari, ri-hash ed esegui di nuovo la funzione di ricerca.


8

Come ha sottolineato Cartman, è possibile utilizzare qualsiasi tipo di valore hash per trovare duplicati esatti.

Un punto di partenza per trovare immagini vicine potrebbe essere qui . Questo è uno strumento utilizzato dalle società CG per verificare se le immagini rinnovate mostrano ancora essenzialmente la stessa scena.


7

Ho un'idea, che può funzionare e molto probabilmente sarà molto veloce. Puoi sottocampionare un'immagine per dire una risoluzione di 80x60 o simile e convertirla in scala di grigi (dopo il sottocampionamento sarà più veloce). Elabora entrambe le immagini che desideri confrontare. Quindi esegui la somma normalizzata delle differenze al quadrato tra due immagini (l'immagine della query e ciascuna dal db), o ancora meglio la Correlazione incrociata normalizzata, che dà una risposta più vicina a 1, se entrambe le immagini sono simili. Quindi se le immagini sono simili puoi procedere a tecniche più sofisticate per verificare che siano le stesse immagini. Ovviamente questo algoritmo è lineare in termini di numero di immagini nel database, quindi anche se sarà molto veloce fino a 10000 immagini al secondo sull'hardware moderno. Se hai bisogno di invarianza alla rotazione, puoi calcolare una sfumatura dominante per questa piccola immagine, e quindi l'intero sistema di coordinate può essere ruotato secondo l'orientamento canonico, tuttavia questo sarà più lento. E no, non c'è invarianza da ridimensionare qui.

Se vuoi qualcosa di più generale o utilizzare grandi database (milioni di immagini), allora devi esaminare la teoria del recupero delle immagini (un sacco di documenti è apparso negli ultimi 5 anni). Ci sono alcuni suggerimenti in altre risposte. Ma potrebbe essere eccessivo e l'approccio dell'istogramma suggerito farà il lavoro. Anche se penso che la combinazione di molti approcci rapidi sarà ancora migliore.


7

La mia azienda ha circa 24 milioni di immagini provenienti dai produttori ogni mese. Stavo cercando una soluzione rapida per assicurarmi che le immagini che cariciamo nel nostro catalogo siano nuove immagini.

Voglio dire che ho cercato su Internet in lungo e in largo per cercare di trovare una soluzione ideale. Ho persino sviluppato il mio algoritmo di rilevamento dei bordi.
Ho valutato la velocità e la precisione di più modelli. Le mie immagini, che hanno sfondi bianchi, funzionano molto bene con il phasing. Come ha detto redcalx , raccomando phash o ahash. NON utilizzare Hashing MD5 o altri hash crittografici. A meno che non si desideri solo corrispondenze esatte di immagini. Qualsiasi ridimensionamento o manipolazione che si verifica tra le immagini produrrà un hash diverso.

Per phash / ahash, dai un'occhiata a : imagehash

Volevo estendere il post di * redcalx pubblicando il mio codice e la mia precisione.

Quello che faccio:

from PIL import Image
from PIL import ImageFilter
import imagehash

img1=Image.open(r"C:\yourlocation")
img2=Image.open(r"C:\yourlocation")
if img1.width<img2.width:
    img2=img2.resize((img1.width,img1.height))
else:
    img1=img1.resize((img2.width,img2.height))
img1=img1.filter(ImageFilter.BoxBlur(radius=3))
img2=img2.filter(ImageFilter.BoxBlur(radius=3))
phashvalue=imagehash.phash(img1)-imagehash.phash(img2)
ahashvalue=imagehash.average_hash(img1)-imagehash.average_hash(img2)
totalaccuracy=phashvalue+ahashvalue

Ecco alcuni dei miei risultati:

item1  item2  totalsimilarity
desk1  desk1       3
desk1  phone1     22
chair1 desk1      17
phone1 chair1     34

Spero che sia di aiuto!


6

Credo che ridurre le dimensioni dell'immagine fino a raggiungere quasi le dimensioni di un'icona, diciamo 48x48, quindi convertirle in scala di grigi, quindi prendere la differenza tra i pixel o Delta, dovrebbe funzionare bene. Poiché stiamo confrontando la variazione del colore dei pixel, piuttosto che il colore reale dei pixel, non importa se l'immagine è leggermente più chiara o più scura. Le grandi modifiche saranno importanti poiché i pixel che diventano troppo chiari / scuri andranno persi. Puoi applicarlo su una riga o quanti ne desideri per aumentare la precisione. Al massimo avresti 47x47 = 2.209 sottrazioni da fare per formare una chiave comparabile.


3

Scegliere 100 punti casuali potrebbe significare che immagini simili (o occasionalmente anche diverse) verrebbero contrassegnate come uguali, che presumo non sia ciò che desideri. Gli hash MD5 non funzionerebbero se le immagini avessero formati diversi (png, jpeg, ecc.), Avessero dimensioni diverse o metadati diversi. Ridurre tutte le immagini a una dimensione inferiore è una buona scommessa, fare un confronto pixel per pixel non dovrebbe richiedere troppo tempo finché si utilizza una buona libreria di immagini / linguaggio veloce e la dimensione è abbastanza piccola.

Potresti provare a renderli piccoli, quindi se sono gli stessi esegui un altro confronto su una dimensione più grande - potrebbe essere una buona combinazione di velocità e precisione ...


Se stai cercando duplicati esatti ma con formati / metadati diversi, puoi eseguire un hash (ad esempio MD5) dei valori di pixel effettivi. Imagemagick chiama questa una firma (non correlata alla firma crittografica). Potresti anche ridurlo prima, ad esempio troncando a 4 bit per pixel per ridurre l'impatto dei manufatti JPEG o convertendolo in scala di grigi per abbinare immagini leggermente ricolorate.
Rena

2

Se hai un gran numero di immagini, cerca in un filtro Bloom , che utilizza più hash per un risultato probablistic ma efficiente. Se il numero di immagini non è enorme, un hash crittografico come md5 dovrebbe essere sufficiente.


Quindi (cercando di capire il filtro Bloom) - significa che selezioni punti pixel casuali sull'immagine di base, ottieni casualmente un valore rosso / verde / blu del pixel - quindi confronta con la nuova immagine? e quindi utilizzare un livello di probabilità (corrispondenza del 90%) per determinare quanto sono simili le due immagini?
Meade,

5
Questo non è un controllo di somiglianza, è un controllo di equivalenza. Se hai bisogno di somiglianza, l'hashing non è l'approccio giusto. L'idea alla base di Bloom è quella di utilizzare più algoritmi di hash per aumentare la probabilità di identificazione univoca. La selezione di punti casuali non è l'approccio migliore per un algoritmo di hashing perché produrrà risultati diversi ogni volta.
jdigital
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.