Dieser Beitrag war der Ausgangspunkt für meine Lösung, viele gute Ideen hier, so dass ich dachte, ich würde meine Ergebnisse teilen. Die wichtigste Erkenntnis ist, dass ich einen Weg gefunden habe, um die Langsamkeit des Keypoint-basierten Bildabgleichs durch Ausnutzung der Geschwindigkeit von phash zu umgehen.
Für die allgemeine Lösung ist es am besten, mehrere Strategien anzuwenden. Jeder Algorithmus ist für bestimmte Arten von Bildtransformationen am besten geeignet, und das können Sie sich zunutze machen.
Ganz oben stehen die schnellsten Algorithmen, ganz unten die langsamsten (aber genaueren). Sie können die langsamen Algorithmen auslassen, wenn Sie auf der schnelleren Ebene eine gute Übereinstimmung finden.
- Datei-Hash-basiert (md5, sha1, etc.) für exakte Duplikate
- perceptual hashing (phash) für neu skalierte Bilder
- Merkmalsbasiert (SIFT) für modifizierte Bilder
Ich habe sehr gute Ergebnisse mit phash. Die Genauigkeit ist bei neu skalierten Bildern gut. Bei (wahrnehmungsmäßig) veränderten Bildern (beschnitten, gedreht, gespiegelt usw.) ist sie nicht gut. Um die Hash-Geschwindigkeit zu erreichen, müssen wir einen Festplatten-Cache/eine Datenbank verwenden, um die Hashes für den Heuhaufen zu speichern.
Das Schöne an phash ist, dass die Suchvorgänge sehr, sehr schnell sein können, sobald man seine Hash-Datenbank aufgebaut hat (bei mir sind das etwa 1000 Bilder pro Sekunde), insbesondere wenn man die gesamte Hash-Datenbank im Speicher halten kann. Das ist ziemlich praktisch, da ein Hash nur 8 Bytes groß ist.
Wenn Sie beispielsweise 1 Million Bilder haben, würde dies ein Array von 1 Million 64-Bit-Hash-Werten (8 MB) erfordern. Bei einigen CPUs passt dies in den L2/L3-Cache! In der Praxis habe ich eine Corei7 mit über 1 Giga-hamm/sec gesehen, es ist nur eine Frage der Speicherbandbreite zur CPU. Eine Datenbank mit 1 Milliarde Bildern ist auf einer 64-Bit-CPU praktikabel (8 GB RAM erforderlich), und die Suchvorgänge werden nicht länger als 1 Sekunde dauern!
Für veränderte/beschnittene Bilder scheint ein transformationsinvarianter Merkmals-/Schlüsselpunktdetektor wie SIFT der richtige Weg zu sein. SIFT erzeugt gute Keypoints, die Beschnitt/Drehung/Spiegelung usw. erkennen. Allerdings ist der Deskriptorvergleich im Vergleich zu der von phash verwendeten Hamming-Distanz sehr langsam. Dies ist eine große Einschränkung. Es müssen sehr viele Vergleiche durchgeführt werden, da es maximal IxJxK Deskriptorvergleiche gibt, um ein Bild zu durchsuchen (I=Anzahl Heuhaufenbilder, J=Zielkeypunkte pro Heuhaufenbild, K=Zielkeypunkte pro Nadelbild).
Um das Geschwindigkeitsproblem zu umgehen, habe ich versucht, Phash um jeden gefundenen Keypoint herum zu verwenden, wobei ich die Größe/den Radius des Merkmals zur Bestimmung des Sub-Rechtecks verwendet habe. Der Trick dabei ist, den Radius zu vergrößern/verkleinern, um verschiedene Unterrechtebenen (auf dem Nadelbild) zu erzeugen. Normalerweise passt die erste Ebene (nicht skaliert), aber oft braucht es ein paar mehr. Ich bin mir nicht 100%ig sicher, warum das funktioniert, aber ich kann mir vorstellen, dass es Funktionen ermöglicht, die für Phash zu klein sind (Phash skaliert Bilder bis auf 32x32).
Ein weiteres Problem ist, dass SIFT die Keypoints nicht optimal verteilt. Wenn es einen Bildabschnitt mit vielen Kanten gibt, häufen sich die Keypoints dort und in einem anderen Bereich gibt es keine. Ich verwende den GridAdaptedFeatureDetector in OpenCV, um die Verteilung zu verbessern. Ich bin mir nicht sicher, welche Rastergröße am besten ist, ich verwende ein kleines Raster (1x3 oder 3x1, je nach Bildausrichtung).
Wahrscheinlich sollten Sie alle Heuhaufenbilder (und die Nadel) vor der Merkmalserkennung auf eine kleinere Größe skalieren (ich verwende 210 Pixel entlang der maximalen Abmessung). Dadurch wird das Rauschen im Bild reduziert (immer ein Problem für Computer-Vision-Algorithmen), und der Detektor konzentriert sich auf markantere Merkmale.
Bei Bildern von Menschen können Sie die Gesichtserkennung verwenden, um die Bildgröße und die Rastergröße zu bestimmen (z. B. das größte Gesicht wird auf 100 Pixel skaliert). Der Merkmalsdetektor berücksichtigt mehrere Skalierungsstufen (unter Verwendung von Pyramiden), aber es gibt eine Begrenzung, wie viele Stufen er verwendet (dies ist natürlich einstellbar).
Der Keypoint-Detektor funktioniert wahrscheinlich am besten, wenn er weniger als die von Ihnen gewünschte Anzahl von Merkmalen liefert. Wenn Sie z. B. nach 400 Merkmalen fragen und 300 zurückbekommen, ist das gut. Wenn Sie jedes Mal 400 zurückbekommen, mussten wahrscheinlich einige gute Merkmale ausgelassen werden.
Das Nadelbild kann weniger Keypoints haben als die Heuhaufenbilder und trotzdem gute Ergebnisse erzielen. Wenn man mehr Keypoints hinzufügt, erhält man nicht unbedingt einen enormen Zuwachs, z. B. liegt meine Trefferquote bei J=400 und K=40 bei 92 %. Mit J=400 und K=400 steigt die Trefferquote nur noch auf 96 %.
Wir können die extreme Geschwindigkeit der Hamming-Funktion nutzen, um Skalierung, Drehung, Spiegelung usw. zu lösen. Es kann eine Technik mit mehreren Durchläufen verwendet werden. Bei jeder Iteration werden die Teilrechtecke transformiert, neu gehasht und die Suchfunktion erneut ausgeführt.