Die Beschriftungen werden vorab segmentiert und von dem im vorherigen Artikel beschriebenen neuronalen Netzwerk bereitgestellt.
Wie funktioniert das Kleben im Allgemeinen? Sie müssen zwei überlappende Bilder aufnehmen, die gegenseitige Verschiebung berechnen und übereinander legen. Klingt ziemlich einfach, aber lassen Sie uns die einzelnen Schritte durchgehen.
Um die gegenseitige Verschiebung zu berechnen, müssen Sie einige Objekte finden, die in beiden Bildern vorhanden sind, und irgendwie die Transformation von Punkten von einem Bild zum anderen berechnen. Diese Verschiebung kann durch eine Transformationsmatrix dargestellt werden, in der die Elemente der Matrix mehrere Transformationen gleichzeitig codieren - Skalierung, Translation und Rotation.
Auf Wikipedia gibt es eine hervorragende Tabelle , die zeigt, wie und welche Elemente die Transformation beeinflussen.
Wie Sie im Bild unten sehen können, gibt es genügend gemeinsame Objekte:
Es gibt jedoch ein Problem mit den ausgewählten Objekten - sie sind schwer algorithmisch zu erkennen. Stattdessen ist es üblich, nach einfacheren Objekten zu suchen - den sogenannten "Ecken", sie sind auch Deskriptoren ("Deskriptoren", "Merkmale").
In der OpenCV-Dokumentation finden Sie einen großartigen Artikel darüber, warum Ecken - kurz gesagt, das Definieren einer Linie ist einfach, aber Sie erhalten nur eine Koordinate. Daher ist es auch notwendig, die zweite (nicht parallele) Linie zu erfassen. Wenn sie an einem Punkt konvergieren, ist dieser Ort ideal, um einen Deskriptor zu finden, es ist auch eine Ecke (obwohl echte Deskriptoren keine Ecken im geometrischen Sinne des Wortes sind).
Einer der Algorithmen zum Auffinden von Deskriptoren ist SIFT (Scale-Invariant Feature Transform). Trotz der Tatsache, dass es 1999 erfunden wurde, ist es aufgrund seiner Einfachheit und Zuverlässigkeit sehr beliebt. Dieser Algorithmus wurde patentiert, aber das Patent lief im Frühjahr (2020) aus. Sie haben es jedoch nicht geschafft, es auf den OpenCV-Hauptbuild zu übertragen, sodass Sie einen speziellen nicht freien Build verwenden müssen.
Lassen Sie uns also in beiden Bildern ähnliche Ecken finden:
sift = cv2.xfeatures2d.SIFT_create()
features_left = sift.detectAndCompute(left_image, None)
features_right = sift.detectAndCompute(left_image, None)
Verwenden wir den Flann-Matcher - er hat eine gute Leistung, auch wenn die Anzahl der Deskriptoren groß ist.
KNN = 2
LOWE = 0.7
TREES = 5
CHECKS = 50
matcher = cv2.FlannBasedMatcher({'algorithm': 0, 'trees': TREES}, {'checks': CHECKS})
matches = matcher.knnMatch(left_descriptors, right_descriptors, k=KNN)
logging.debug("filtering matches with lowe test")
positive = []
for left_match, right_match in matches:
if left_match.distance < LOWE * right_match.distance:
positive.append(left_match)
Die gelben Linien zeigen, wie der Matcher Übereinstimmungen gefunden hat.
Wie Sie sehen können, gibt es nur etwa die Hälfte der richtigen Übereinstimmungen. Wenn jedoch korrekte Übereinstimmungen immer dieselbe Transformation ergeben, zeigen falsche Übereinstimmungen eine chaotisch neue Richtung. Jene. theoretisch können sie irgendwie voneinander getrennt werden:
Einer der Algorithmen, um die richtige Transformation zu finden, ist RANSAC. Dieser Algorithmus funktioniert hervorragend, wenn Sie gute Werte vom Rauschen trennen möchten - genau das ist der Fall.
Glücklicherweise verfügt OpenCV bereits über Funktionen, die die Transformationsmatrix durch Übereinstimmungen unter Verwendung von RANSAC finden, d. H. in der Tat müssen Sie nichts schreiben.
Verwenden wir die Funktion EstimationAffinePartial2D, die nach folgenden Transformationen sucht: Rotation, Skalierung und Translation (4 Freiheitsgrade).
H, _ = cv2.estimateAffinePartial2D(right_matches, left_matches, False)
Sobald die Transformationsmatrix gefunden ist, können wir das richtige Bild zum Kleben transformieren.
Linkes Fragment:
Rechtes Fragment:
Verwenden wir zunächst die einfachste Methode zum Zusammenkleben von Fragmenten, wenn jedes Pixel ihres Schnittpunkts als Durchschnitt berechnet wird. Leider ist das Ergebnis mittelmäßig - das Bild verdoppelt sich merklich, insbesondere in der Nähe der Klebelinie.
In der Animation ist der Unterschied zwischen den beiden Bildern deutlicher sichtbar:
Dies ist nicht überraschend - die Fotos wurden aus verschiedenen Winkeln aufgenommen, das neuronale Netzwerk hat sie auch leicht unterschiedlich gedreht, und infolgedessen gab es kleine Abweichungen.
Für ein nahtloses Kleben ist es notwendig, nichtlineare Verzerrungen auszugleichen. Die Verzerrung kann als Vektorfeld mit der gleichen Auflösung wie das Originalbild dargestellt werden. Nur anstelle der Farbe wird in jedem Pixel eine Verschiebung codiert. Dieses Vektorfeld wird als "optischer Fluss" bezeichnet.
Im Allgemeinen gibt es verschiedene Methoden zur Berechnung des optischen Flusses - einige davon sind direkt in OpenCV integriert, und es gibt auch spezielle neuronale Netze.
In unserem Fall werde ich die spezifische Technik weglassen, aber das Ergebnis veröffentlichen: Die
Kompensation muss jedoch proportional zu beiden Fragmenten erfolgen. Dazu teilen wir es in zwei Matrizen auf: Das
linke Fragment wird zunehmend von links nach rechts kompensiert, das rechte umgekehrt.
Jetzt überlappen sich beide Fragmente fast perfekt:
Jetzt ist die Überlagerung geometrisch korrekt, aber wir beobachten einen sehr merklichen Helligkeitssprung an den Nähten:
Dieses Problem kann leicht behoben werden, wenn anstelle von Durchschnittswerten ein Gradient überlagert wird:
Bei diesem Ansatz ist die Naht überhaupt nicht sichtbar:
Grundsätzlich gibt es beispielsweise auch andere Klebetechniken Multiband-Überblendung, die zum Zusammenfügen von Panoramen verwendet wird, jedoch nicht gut mit Text funktioniert - nur durch die optische Flusskompensation können Geisterbilder im Text vollständig entfernt werden.
Jetzt kleben wir das ganze Bild:
Endgültige Version:
Weitere Verbesserungen könnten die Kompensation des Schatteneffekts (rechte Seite des Bildes) oder eine noch stärkere Nachbearbeitung von Farbe und Kontrast sein. Sie können auch sehen, dass die globale Geometrie leicht gelitten hat - die Linien auf der rechten Seite haben sich leicht nach oben geschlichen. Theoretisch kann dieses Problem auch durch Hinzufügen einer globalen Skalierungskorrektur behoben werden, dies ist jedoch auch keine völlig triviale Aufgabe.
Wir haben untersucht, wie das Kleben funktioniert. Eine fertige Lösung ist hier in Form einer REST-API verfügbar. Ich empfehle außerdem, die folgenden Links zu lesen: