2010-11-16 20 views
150

J'ai besoin d'un moyen simple et rapide de comparer deux images pour la similarité. C'est à dire. Je veux obtenir une valeur élevée s'ils contiennent exactement la même chose mais peuvent avoir un arrière-plan légèrement différent et peuvent être déplacés/redimensionnés de quelques pixels.Méthode simple et rapide pour comparer les images pour la similarité

(Plus concrètement, si cela importe: L'une image est une icône et l'autre image est un sous-domaine d'une capture d'écran et je veux savoir si cette sous-zone est exactement l'icône ou non.)

Je OpenCV à portée de main mais je n'y suis toujours pas habitué. Une possibilité à laquelle j'ai pensé jusqu'à présent: Divisez les deux images en 10x10 cellules et comparez l'histogramme de couleur pour chacune de ces 100 cellules. Ensuite, je peux définir une valeur de seuil composée et si la valeur obtenue est supérieure à ce seuil, je suppose qu'elles sont similaires.

Je ne l'ai pas encore essayé à quel point cela fonctionne mais je suppose que ce serait assez bon. Les images sont déjà assez similaires (dans mon cas d'utilisation), donc je peux utiliser une valeur seuil assez élevée.

Je suppose qu'il existe des dizaines d'autres solutions possibles pour cela qui fonctionneraient plus ou moins (comme la tâche elle-même est assez simple car je veux seulement détecter la similarité si elles sont vraiment très similaires). Que suggérerais-tu?


Il y a quelques très liés/questions similaires au sujet de l'obtention d'une signature/empreinte/hachage à partir d'une image:

En outre, je suis tombé sur ces mises en œuvre qui ont de telles fonctions pour obtenir une empreinte digitale:

Quelques discussions sur hash image perceptives: here


Un peu offtopic: Il existe de nombreuses méthodes pour créer des empreintes digitales audio. MusicBrainz, un service Web qui fournit une recherche d'empreintes digitales pour les chansons, a un good overview in their wiki.Ils utilisent AcoustID maintenant. C'est pour trouver des correspondances exactes (ou presque exactes). Pour trouver des correspondances similaires (ou si vous avez seulement des extraits ou du bruit élevé), jetez un oeil à Echoprint. Une question SO connexe est here. Donc, il semble que cela soit résolu pour l'audio. Toutes ces solutions fonctionnent assez bien.

Une question un peu plus générique à propos de la recherche floue en général est here. Par exemple. il y a locality-sensitive hashing et nearest neighbor search.

+1

Peut-être que les empreintes digitales pourraient aider? http: // stackoverflow.com/questions/596262/image-empreinte-de-comparer-similitude-de-nombreuses-images – GWW

+0

La métrique de Wasserstein, également connue sous le nom de distance du moteur de la terre (EMD), est quelque chose que les gens semblent ignorer, mais donnerait ce que tu veux ici. – mmgp

+2

duplication possible de [Comparaison d'images - algorithme rapide] (http://stackoverflow.com/questions/843972/image-comparison-fast-algorithm) – sashoalm

Répondre

86

La capture d'écran ou l'icône peut-elle être transformée (mise à l'échelle, pivotée, inclinée ...)? Il y a quelques méthodes sur le dessus de ma tête qui pourrait vous aider:

  • simple distance euclidienne comme mentionné par @carlosdc (ne fonctionne pas avec des images transformées et vous avez besoin d'un seuil).
  • (Normalized) Cross Correlation - une métrique simple que vous pouvez utiliser pour comparer des zones d'image. Il est plus robuste que la simple distance euclidienne mais ne fonctionne pas sur les images transformées et vous aurez à nouveau besoin d'un seuil.
  • Comparaison d'histogrammes - si vous utilisez des histogrammes normalisés, cette méthode fonctionne bien et n'est pas affectée par les transformations affines. Le problème consiste à déterminer le seuil correct. Il est également très sensible aux changements de couleur (luminosité, contraste, etc.). Vous pouvez le combiner avec les deux précédents.
  • Détecteurs de points/zones saillants - tels que MSER (Maximally Stable Extremal Regions), SURF ou SIFT. Ce sont des algorithmes très robustes et ils pourraient être trop compliqués pour votre tâche simple. La bonne chose est que vous ne devez pas avoir une zone exacte avec une seule icône, ces détecteurs sont assez puissants pour trouver le bon match. Une bonne évaluation de ces méthodes est dans cet article: Local invariant feature detectors: a survey.

La plupart d'entre elles sont déjà implémentées dans OpenCV - voir par exemple la méthode cvMatchTemplate (utilise la correspondance d'histogramme): http://dasl.mem.drexel.edu/~noahKuntz/openCVTut6.html. Les détecteurs point/zone saillants sont également disponibles - voir OpenCV Feature Detection.

+1

Il peut être redimensionné ou déplacé légèrement. L'arrière-plan de l'icône sera également différent. J'ai essayé la comparaison d'histogrammes mais j'ai eu beaucoup de faux positifs. J'ai aussi essayé la distance euclidienne mais cela donne aussi trop de faux positifs (mais peut-être que je peux améliorer la gestion de la valeur alpha de l'icône). Je vais essayer un peu plus loin, sinon je vais vérifier MSER, SURF ou SIFT. – Albert

+0

Une autre idée - cela ne fonctionnerait-il pas si vous utilisiez une comparaison d'histogramme des images après l'application d'un opérateur sobel? Cela ne ferait que comparer la similitude des bords. Peut-être ou ne pourrait pas fonctionner, selon la façon dont «l'arrière-plan» est «énervé». –

8

La capture d'écran contient-elle uniquement l'icône? Si c'est le cas, la distance L2 des deux images peut suffire. Si la distance L2 ne fonctionne pas, l'étape suivante consiste à essayer quelque chose de simple et de bien établi, comme: Lucas-Kanade. Ce que je suis sûr est disponible dans OpenCV.

+0

La sous-zone contient soit exactement seulement l'icône (avec un arrière-plan aléatoire) ou quelque chose de différent. Je veux voir quel est le cas. Bien que, il peut être très légèrement décalé ou redimensionné, c'est pourquoi je n'étais pas sûr de pouvoir regarder la distance (quelle que soit la norme). Mais je vais essayer avec une version réduite. – Albert

3

Si vous pouvez être sûr d'avoir un alignement précis de votre modèle (l'icône) dans la région de test, alors toute ancienne somme de différences de pixels fonctionnera.

Si l'alignement ne doit être que légèrement réduit, vous pouvez passer les deux images avec cv::GaussianBlur avant de trouver la somme des différences de pixels.

Si la qualité de l'alignement est potentiellement pauvre alors je vous recommande soit un Histogram of Oriented Gradients ou l'un des algorithmes de détection/descripteurs de pratique de OpenCV points-clés (comme SIFT ou SURF).

2

Si vous voulez comparer l'image pour la similarité, je vous suggère d'utiliser OpenCV. Dans OpenCV, il existe peu de correspondance de caractéristiques et de correspondance de modèles. Pour la correspondance de caractéristiques, il existe des détecteurs SURF, SIFT, FAST et ainsi de suite. Vous pouvez l'utiliser pour détecter, décrire et faire correspondre l'image. Après cela, vous pouvez utiliser l'index spécifique pour trouver le nombre de correspondance entre les deux images.

+0

vous avez dit "Après cela, vous pouvez utiliser l'index spécifique pour trouver le nombre de correspondance entre les deux images." quel peut être le nombre minimum de correspondances entre les deux images pour dire qu'elles "contais" le même objet? –

4

Si vous voulez obtenir un index sur la similarité des deux images, je vous suggère à partir des métriques l'indice SSIM. C'est plus cohérent avec l'oeil humain. Voici un article sur le sujet: Structural Similarity Index

Il est mis en œuvre OpenCV aussi, et il peut être accéléré avec GPU: OpenCV SSIM with GPU

3

Si pour faire correspondre les images identiques - code distance 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 
} 

rapide . Mais pas robuste aux changements d'éclairage/point de vue, etc. Source

26

Je face aux mêmes problèmes récemment, pour résoudre ce problème (simple et rapide algorithme pour comparer deux images) une fois pour toutes, je contribue un img_hash module à opencv_contrib, vous pouvez trouver les détails de this link.

Le module img_hash fournit six algorithmes de hachage d'image, assez faciles à utiliser.

Codes exemple

origine origin lena lena

blur lena flou lena

resize lena redimensionnement lena

shift lena décalage 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)); 
} 

Dans ce cas, ColorMomentHash nous donne le meilleur résultat

  • attaque Gaussian Blur: 0,567521
  • attaque de changement: 0,229728
  • Redimensionner attaque: 0.229358

Avantages et inconvénients de chaque algorithme

Performance under different attacks

La performance de img_hash est bon aussi

Comparaison de la vitesse avec la bibliothèque phash (100 images de ukbench) compute performance comparison performance

Si vous voulez connaître le recommander des seuils pour ces algorithmes, s'il vous plaît vérifier ce poste (http://qtandopencv.blogspot.my/2016/06/introduction-to-image-hash-module-of.html). Si vous voulez savoir comment mesurer les performances des modules img_hash (inclure la vitesse et les différentes attaques), veuillez cliquer sur ce lien (http://qtandopencv.blogspot.my/2016/06/speed-up-image-hashing-of-opencvimghash.html).

+2

Merci pour la bonne réponse. Pourriez-vous nous dire la source de la comparaison de vitesse? – ocolot

+2

@ocolot Bien sûr, je rends le lien dans ce post plus évident, il suffit de cliquer dessus et vous trouverez votre réponse. Je développe une application basée sur ce module, à l'avenir je soutiendrai également l'empreinte digitale – StereoMatching