Einfache und schnelle Methode zum Vergleichen von Bildern auf Ähnlichkeit

192

Ich brauche eine einfache und schnelle Möglichkeit, zwei Bilder auf Ähnlichkeit zu vergleichen. Das heißt, ich möchte einen hohen Wert erzielen, wenn sie genau dasselbe enthalten, aber möglicherweise einen etwas anderen Hintergrund haben und um einige Pixel verschoben / in der Größe geändert werden können.

(Konkreter, wenn das wichtig ist: Das eine Bild ist ein Symbol und das andere Bild ist ein Teilbereich eines Screenshots, und ich möchte wissen, ob dieser Teilbereich genau das Symbol ist oder nicht.)

Ich habe OpenCV zur Hand, aber ich bin immer noch nicht so daran gewöhnt .

Eine Möglichkeit, über die ich bisher nachgedacht habe: Teilen Sie beide Bilder in 10 x 10 Zellen und vergleichen Sie für jede dieser 100 Zellen das Farbhistogramm. Dann kann ich einen bestimmten Schwellenwert festlegen, und wenn der Wert, den ich erhalte, über diesem Schwellenwert liegt, gehe ich davon aus, dass sie ähnlich sind.

Ich habe es noch nicht ausprobiert, wie gut das funktioniert, aber ich denke, es wäre gut genug. Die Bilder sind bereits ziemlich ähnlich (in meinem Anwendungsfall), so dass ich einen ziemlich hohen Schwellenwert verwenden kann.

Ich denke, es gibt Dutzende anderer möglicher Lösungen dafür, die mehr oder weniger funktionieren würden (da die Aufgabe selbst recht einfach ist, da ich Ähnlichkeit nur erkennen möchte, wenn sie wirklich sehr ähnlich sind). Was würdest du vorschlagen?


Es gibt einige sehr verwandte / ähnliche Fragen zum Abrufen einer Signatur / eines Fingerabdrucks / eines Hash von einem Bild:

Außerdem bin ich auf diese Implementierungen gestoßen, die solche Funktionen haben, um einen Fingerabdruck zu erhalten:

Einige Diskussionen über Wahrnehmungsbild-Hashes: hier


Ein bisschen offtopic: Es gibt viele Methoden, um Audio-Fingerabdrücke zu erstellen. MusicBrainz , ein Webdienst, der eine auf Fingerabdrücken basierende Suche nach Songs ermöglicht, hat einen guten Überblick in seinem Wiki . Sie verwenden jetzt AcoustID . Dies dient zum Finden genauer (oder größtenteils genauer) Übereinstimmungen. Werfen Sie einen Blick auf Echoprint , um ähnliche Übereinstimmungen zu finden (oder wenn Sie nur einige Schnipsel oder ein hohes Rauschen haben) . Eine verwandte SO-Frage ist hier . Es scheint also, dass dies für Audio gelöst ist. Alle diese Lösungen funktionieren recht gut.

Eine etwas allgemeinere Frage zur Fuzzy-Suche im Allgemeinen ist hier . Zum Beispiel gibt es ortsabhängiges Hashing und die Suche nach dem nächsten Nachbarn .

Albert
quelle
1
Vielleicht könnte ein Bildfingerabdruck helfen? stackoverflow.com/questions/596262/…
GWW
Die Wasserstein-Metrik, auch als Earth Mover's Distance (EMD) bekannt, ist etwas, von dem die Leute anscheinend nichts wissen, das aber so ziemlich alles geben würde, was Sie hier wollen.
mmgp
3
mögliches Duplikat des Bildvergleichs - schneller Algorithmus
Sashoalm
Hallo, ich habe mir ein verbessertes dHash ausgedacht - ich habe es IDHash genannt: github.com/Nakilon/dhash-vips
Nakilon

Antworten:

107

Kann der Screenshot oder das Symbol transformiert werden (skaliert, gedreht, schief ...)? Es gibt einige Methoden auf meinem Kopf, die Ihnen möglicherweise helfen könnten:

  • Einfache euklidische Entfernung, wie von @carlosdc erwähnt (funktioniert nicht mit transformierten Bildern und Sie benötigen einen Schwellenwert).
  • (Normalisierte) Kreuzkorrelation - eine einfache Metrik, mit der Sie Bildbereiche vergleichen können. Es ist robuster als der einfache euklidische Abstand, funktioniert jedoch nicht bei transformierten Bildern und Sie benötigen erneut einen Schwellenwert.
  • Histogrammvergleich - Wenn Sie normalisierte Histogramme verwenden, funktioniert diese Methode gut und wird von affinen Transformationen nicht beeinflusst. Das Problem besteht darin, den richtigen Schwellenwert zu bestimmen. Es ist auch sehr empfindlich gegenüber Farbänderungen (Helligkeit, Kontrast usw.). Sie können es mit den beiden vorherigen kombinieren.
  • Detektoren für hervorstechende Punkte / Bereiche - wie MSER (Maximal Stable Extremal Regions) , SURF oder SIFT . Dies sind sehr robuste Algorithmen, die für Ihre einfache Aufgabe möglicherweise zu kompliziert sind. Gut ist, dass Sie keinen exakten Bereich mit nur einem Symbol haben müssen. Diese Detektoren sind leistungsstark genug, um die richtige Übereinstimmung zu finden. Eine schöne Bewertung dieser Methoden finden Sie in diesem Artikel: Lokale Detektoren für invariante Merkmale: eine Umfrage .

Die meisten davon sind bereits in OpenCV implementiert - siehe zum Beispiel die cvMatchTemplate-Methode (verwendet Histogrammabgleich): http://dasl.mem.drexel.edu/~noahKuntz/openCVTut6.html . Die markanten Punkt- / Flächendetektoren sind ebenfalls verfügbar - siehe OpenCV Feature Detection .

Karel Petranek
quelle
1
Es kann skaliert oder leicht verschoben werden. Auch der Hintergrund des Symbols wird anders sein. Ich habe versucht, einen Histogrammvergleich durchzuführen, aber ich habe viele falsch positive Ergebnisse erhalten. Ich habe auch versucht, euklidische Distanz, aber das gibt auch zu viele Fehlalarme (aber vielleicht kann ich das etwas besser machen, um den Alpha-Wert im Symbol zu behandeln). Ich werde das etwas weiter versuchen, sonst werde ich MSER, SURF oder SIFT ausprobieren.
Albert
1
Eine andere Idee - würde es nicht funktionieren, wenn Sie nach dem Anwenden eines Sobel-Operators einen Histogrammvergleich der Bilder verwenden würden? Das würde nur die Ähnlichkeit von Kanten vergleichen. Könnte funktionieren oder auch nicht, je nachdem, wie "nervös" der Hintergrund ist.
Karel Petranek
44

Ich habe in letzter Zeit die gleichen Probleme. Um dieses Problem zu lösen (einfacher und schneller Algorithmus zum Vergleichen von zwei Bildern), trage ich ein img_hash-Modul zu opencv_contrib bei. Die Details finden Sie unter diesem Link .

Das Modul img_hash bietet sechs Bild-Hash-Algorithmen, die recht einfach zu verwenden sind.

Beispiel für Codes

Herkunft LenaHerkunft Lena

verwischen lenaverwischen lena

Größe ändern lenaGröße ändern lena

Schicht LenaSchicht 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 diesem Fall liefert ColorMomentHash das beste Ergebnis

  • Gaußscher Unschärfeangriff: 0,567521
  • Schichtangriff: 0,229728
  • Größenänderungsangriff: 0.229358

Vor- und Nachteile jedes Algorithmus

Leistung unter verschiedenen Angriffen

Die Leistung von img_hash ist auch gut

Geschwindigkeitsvergleich mit PHash-Bibliothek (100 Bilder von ukbench) Rechenleistung Vergleichsleistung

Wenn Sie die empfohlenen Schwellenwerte für diese Algorithmen kennen möchten, lesen Sie bitte diesen Beitrag ( http://qtandopencv.blogspot.my/2016/06/introduction-to-image-hash-module-of.html ). Wenn Sie interessiert sind, wie ich die Leistung von img_hash-Modulen messen kann (einschließlich Geschwindigkeit und verschiedener Angriffe), überprüfen Sie bitte diesen Link ( http://qtandopencv.blogspot.my/2016/06/speed-up-image-hashing-of) -opencvimghash.html ).

StereoMatching
quelle
11

Enthält der Screenshot nur das Symbol? In diesem Fall kann der L2-Abstand der beiden Bilder ausreichen. Wenn die L2-Distanz nicht funktioniert, versuchen Sie im nächsten Schritt etwas Einfaches und Etabliertes wie: Lucas-Kanade . Was ich sicher bin, ist in OpenCV verfügbar.

carlosdc
quelle
Der Unterbereich enthält entweder genau nur das Symbol (mit zufälligem Hintergrund) oder etwas anderes. Ich möchte sehen, um welchen Fall es sich handelt. Obwohl es sehr leicht verschoben oder in der Größe verändert sein kann, war ich mir nicht sicher, ob ich nur die Entfernung betrachten konnte (in welcher Norm auch immer). Aber ich werde es mit einer verkleinerten Version versuchen.
Albert
6

Wenn Sie einen Index über die Ähnlichkeit der beiden Bilder erhalten möchten, empfehle ich Ihnen aus den Metriken den SSIM-Index. Es ist konsistenter mit dem menschlichen Auge. Hier ist ein Artikel darüber: Struktureller Ähnlichkeitsindex

Es ist auch in OpenCV implementiert und kann mit der GPU beschleunigt werden: OpenCV SSIM mit der GPU

Milan Tenk
quelle
5

Wenn Sie sicher sein können, dass Ihre Vorlage (das Symbol) genau auf den Testbereich ausgerichtet ist, funktioniert jede alte Summe von Pixelunterschieden.

Wenn die Ausrichtung nur geringfügig abweicht, können Sie beide Bilder mit cv :: GaussianBlur tiefpassieren, bevor Sie die Summe der Pixeldifferenzen ermitteln.

Wenn die Qualität der Ausrichtung möglicherweise schlecht ist, würde ich entweder ein Histogramm orientierter Verläufe oder einen der praktischen OpenCV-Algorithmen zur Erkennung / Beschreibung von Schlüsselpunkten (wie SIFT oder SURF ) empfehlen .

rcv
quelle
4

Wenn für identische Bilder übereinstimmen - Code für L2-Abstand

// 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
}

Schnell. Aber nicht robust gegenüber Änderungen der Beleuchtung / des Blickwinkels usw. Quelle

Kiran
quelle
2

Wenn Sie das Bild auf Ähnlichkeit vergleichen möchten, empfehle ich Ihnen, OpenCV zu verwenden. In OpenCV gibt es nur wenige Feature-Matching und Template-Matching. Für die Merkmalsanpassung gibt es SURF-, SIFT-, FAST- usw. Detektor. Sie können dies verwenden, um das Bild zu erkennen, zu beschreiben und dann abzugleichen. Danach können Sie den spezifischen Index verwenden, um die Anzahl der Übereinstimmungen zwischen den beiden Bildern zu ermitteln.

Hua Er Lim
quelle
1
Sie sagten: "Danach können Sie den spezifischen Index verwenden, um die Anzahl der Übereinstimmungen zwischen den beiden Bildern zu ermitteln." Was kann die Mindestanzahl von Übereinstimmungen zwischen den beiden Bildern sein, um zu sagen, dass sie dasselbe Objekt "enthalten"?
Inês Martins