Metodo semplice e veloce per confrontare le immagini per somiglianza


192

Ho bisogno di un modo semplice e veloce per confrontare due immagini per somiglianza. Vale a dire che voglio ottenere un valore elevato se contengono esattamente la stessa cosa ma possono avere uno sfondo leggermente diverso e possono essere spostati / ridimensionati di alcuni pixel.

(Più concreto, se questo è importante: una foto è un'icona e l'altra immagine è una sottoarea di uno screenshot e voglio sapere se quella sottoarea è esattamente l'icona o meno.)

Ho OpenCV a portata di mano ma non sono ancora abituato.

Una possibilità a cui ho pensato finora: dividere entrambe le immagini in celle 10x10 e per ognuna di quelle 100 celle, confrontare l'istogramma del colore. Quindi posso impostare un determinato valore soglia e se il valore che ottengo è al di sopra di tale soglia, presumo che siano simili.

Non ho ancora provato quanto funzioni bene, ma credo che sarebbe abbastanza buono. Le immagini sono già abbastanza simili (nel mio caso d'uso), quindi posso usare un valore di soglia piuttosto alto.

Immagino ci siano dozzine di altre possibili soluzioni per questo che funzionerebbero più o meno (dato che l'attività stessa è abbastanza semplice in quanto voglio rilevare la somiglianza solo se sono davvero molto simili). Che cosa suggeriresti?


Ci sono alcune domande molto correlate / simili sull'ottenimento di una firma / impronta digitale / hash da un'immagine:

Inoltre, mi sono imbattuto in queste implementazioni che hanno tali funzioni per ottenere un'impronta digitale:

Alcune discussioni sugli hash delle immagini percettive: qui


Un po 'offtopico: esistono molti metodi per creare impronte digitali. MusicBrainz , un servizio web che fornisce una ricerca basata su impronte digitali per le canzoni, ha una buona panoramica nella loro wiki . Stanno usando AcoustID ora. Questo serve per trovare corrispondenze esatte (o per lo più esatte). Per trovare corrispondenze simili (o se hai solo frammenti o rumori elevati), dai un'occhiata a Echoprint . Una domanda SO correlata è qui . Quindi sembra che questo sia risolto per l'audio. Tutte queste soluzioni funzionano abbastanza bene.

Una domanda un po 'più generica sulla ricerca fuzzy in generale è qui . Ad esempio, esiste un hash sensibile alla località e la ricerca del vicino più vicino .


1
Forse l'impronta digitale delle immagini potrebbe aiutare? stackoverflow.com/questions/596262/…
GWW

1
La metrica di Wasserstein, nota anche come Earth Mover's Distance (EMD), è qualcosa che la gente sembra non conoscere, ma darebbe praticamente quello che vuoi qui.
mmgp

3
possibile duplicato del confronto
sashoalm,

Ciao, ho trovato dHash migliorato - l'ho chiamato IDHash: github.com/Nakilon/dhash-vips
Nakilon

Risposte:


107

Lo screenshot o l'icona può essere trasformato (ridimensionato, ruotato, inclinato ...)? Ci sono alcuni metodi sopra la mia testa che potrebbero aiutarti:

  • Semplice distanza euclidea come menzionato da @carlosdc (non funziona con immagini trasformate e hai bisogno di una soglia).
  • Correlazione incrociata (normalizzata) : semplici metriche che è possibile utilizzare per il confronto delle aree dell'immagine. È più robusto della semplice distanza euclidea, ma non funziona su immagini trasformate e avrai di nuovo bisogno di una soglia.
  • Confronto dell'istogramma : se si utilizzano istogrammi normalizzati, questo metodo funziona bene e non è influenzato dalle trasformazioni affini. Il problema è determinare la soglia corretta. È anche molto sensibile alle variazioni di colore (luminosità, contrasto ecc.). Puoi combinarlo con i due precedenti.
  • Rivelatori di punti / aree salienti - come MSER (Maximally Stable Extremal Regions) , SURF o SIFT . Questi sono algoritmi molto robusti e potrebbero essere troppo complicati per il tuo semplice compito. La cosa buona è che non devi avere un'area esatta con una sola icona, questi rilevatori sono abbastanza potenti da trovare la giusta corrispondenza. Una buona valutazione di questi metodi è in questo documento: Rilevatori di funzionalità invarianti locali: un sondaggio .

Molti di questi sono già implementati in OpenCV - vedi ad esempio il metodo cvMatchTemplate (usa la corrispondenza istogramma): http://dasl.mem.drexel.edu/~noahKuntz/openCVTut6.html . Sono inoltre disponibili i rilevatori di punti / aree salienti - vedere Rilevamento funzioni OpenCV .


1
Può essere ridimensionato o spostato leggermente. Anche lo sfondo dell'icona sarà diverso. Ho provato il confronto dell'istogramma ma ho ottenuto molti falsi positivi. Ho anche provato la distanza euclidea, ma questo dà anche troppi falsi positivi (ma forse posso migliorare leggermente la gestione del valore alfa nell'icona). Ci proverò ancora un po ', altrimenti controllerò MSER, SURF o SIFT.
Albert,

1
Un'altra idea: non funzionerebbe se si usasse il confronto istogramma delle immagini dopo aver applicato un operatore sobel? Ciò confronterebbe solo la somiglianza dei bordi. Potrebbe o potrebbe non funzionare, a seconda di quanto sia "spigoloso" lo sfondo.
Karel Petranek,

44

Di recente ho affrontato gli stessi problemi, per risolvere questo problema (algoritmo semplice e veloce per confrontare due immagini) una volta per tutte, contribuisco con un modulo img_hash a opencv_contrib, puoi trovare i dettagli da questo link .

Il modulo img_hash fornisce sei algoritmi di hash delle immagini, abbastanza facili da usare.

Esempio di codici

origine lenaorigine lena

sfocatura lenasfocatura lena

ridimensiona lenaridimensiona lena

maiusc lenamaiusc lena

#include <opencv2/core.hpp>
#include <opencv2/core/ocl.hpp>
#include <opencv2/highgui.hpp>
#include <opencv2/img_hash.hpp>
#include <opencv2/imgproc.hpp>

#include <iostream>

void compute(cv::Ptr<cv::img_hash::ImgHashBase> algo)
{
    auto input = cv::imread("lena.png");
    cv::Mat similar_img;

    //detect similiar image after blur attack
    cv::GaussianBlur(input, similar_img, {7,7}, 2, 2);
    cv::imwrite("lena_blur.png", similar_img);
    cv::Mat hash_input, hash_similar;
    algo->compute(input, hash_input);
    algo->compute(similar_img, hash_similar);
    std::cout<<"gaussian blur attack : "<<
               algo->compare(hash_input, hash_similar)<<std::endl;

    //detect similar image after shift attack
    similar_img.setTo(0);
    input(cv::Rect(0,10, input.cols,input.rows-10)).
            copyTo(similar_img(cv::Rect(0,0,input.cols,input.rows-10)));
    cv::imwrite("lena_shift.png", similar_img);
    algo->compute(similar_img, hash_similar);
    std::cout<<"shift attack : "<<
               algo->compare(hash_input, hash_similar)<<std::endl;

    //detect similar image after resize
    cv::resize(input, similar_img, {120, 40});
    cv::imwrite("lena_resize.png", similar_img);
    algo->compute(similar_img, hash_similar);
    std::cout<<"resize attack : "<<
               algo->compare(hash_input, hash_similar)<<std::endl;
}

int main()
{
    using namespace cv::img_hash;

    //disable opencl acceleration may(or may not) boost up speed of img_hash
    cv::ocl::setUseOpenCL(false);

    //if the value after compare <= 8, that means the images
    //very similar to each other
    compute(ColorMomentHash::create());

    //there are other algorithms you can try out
    //every algorithms have their pros and cons
    compute(AverageHash::create());
    compute(PHash::create());
    compute(MarrHildrethHash::create());
    compute(RadialVarianceHash::create());
    //BlockMeanHash support mode 0 and mode 1, they associate to
    //mode 1 and mode 2 of PHash library
    compute(BlockMeanHash::create(0));
    compute(BlockMeanHash::create(1));
}

In questo caso, ColorMomentHash ci dà il miglior risultato

  • attacco di sfocatura gaussiana: 0,567521
  • attacco a turni: 0,229728
  • attacco di ridimensionamento: 0.229358

Pro e contro di ogni algoritmo

Prestazioni sotto diversi attacchi

Anche le prestazioni di img_hash sono buone

Confronto della velocità con la libreria PHash (100 immagini da ukbench) prestazioni di calcolo prestazione di confronto

Se vuoi conoscere le soglie consigliate per questi algoritmi, consulta questo post ( http://qtandopencv.blogspot.my/2016/06/introduction-to-image-hash-module-of.html ). Se sei interessato a come misurare le prestazioni dei moduli img_hash (includi velocità e attacchi diversi), controlla questo link ( http://qtandopencv.blogspot.my/2016/06/speed-up-image-hashing-of -opencvimghash.html ).


11

Lo screenshot contiene solo l'icona? In tal caso, la distanza L2 delle due immagini potrebbe essere sufficiente. Se la distanza L2 non funziona, il passo successivo è provare qualcosa di semplice e ben definito, come: Lucas-Kanade . Che sono sicuro è disponibile in OpenCV.


La sottozona contiene esattamente solo l'icona (con uno sfondo casuale) o qualcosa di diverso. Voglio vedere che caso è. Tuttavia, potrebbe essere leggermente spostato o ridimensionato, ecco perché non ero sicuro di poter semplicemente guardare la distanza (in qualsiasi norma). Ma proverò con una versione ridotta.
Albert,


5

Se puoi essere sicuro di avere un allineamento preciso del tuo modello (l'icona) con l'area di test, funzionerà qualsiasi somma precedente di differenze di pixel.

Se l'allineamento sarà solo un po 'fuori, allora puoi passare in basso entrambe le immagini con cv :: GaussianBlur prima di trovare la somma delle differenze di pixel.

Se la qualità dell'allineamento è potenzialmente scadente, consiglierei un istogramma di gradienti orientati o uno dei convenienti algoritmi di rilevamento / descrizione dei punti chiave di OpenCV (come SIFT o SURF ).


4

Se per la corrispondenza di immagini identiche - codice per la distanza L2

// Compare two images by getting the L2 error (square-root of sum of squared error).
double getSimilarity( const Mat A, const Mat B ) {
if ( A.rows > 0 && A.rows == B.rows && A.cols > 0 && A.cols == B.cols ) {
    // Calculate the L2 relative error between images.
    double errorL2 = norm( A, B, CV_L2 );
    // Convert to a reasonable scale, since L2 error is summed across all pixels of the image.
    double similarity = errorL2 / (double)( A.rows * A.cols );
    return similarity;
}
else {
    //Images have a different size
    return 100000000.0;  // Return a bad value
}

Veloce. Ma non robusto ai cambiamenti nell'illuminazione / punto di vista ecc. Fonte


2

Se vuoi confrontare l'immagine per somiglianza, ti suggerisco di usare OpenCV. In OpenCV, ci sono pochi abbinamenti di funzionalità e abbinamenti di modelli. Per la corrispondenza delle caratteristiche, ci sono SURF, SIFT, FAST e così via rilevatore. Puoi usarlo per rilevare, descrivere e quindi abbinare l'immagine. Successivamente, è possibile utilizzare l'indice specifico per trovare il numero di corrispondenza tra le due immagini.


1
hai detto "Successivamente, puoi utilizzare l'indice specifico per trovare il numero di corrispondenza tra le due immagini". quale può essere il numero minimo di corrispondenze tra le due immagini per dire che "contano" lo stesso oggetto?
Inês Martins,
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.