Invertieren eines reellwertigen Indexgitters

8

remap() verwendet einen reellwertigen Index Raster abzutasten ein Raster von Werten aus einem Bild mit bilinearer Interpolation und gibt das Mustergitter als neues Bild zurück.

Um genau zu sein, lassen Sie:

%Vor%

Dann für alle Pixelkoordinaten i, j,

%Vor%

Wo die Rund Zahnspange notation A(x, y) bedeutet, unter Verwendung von bilinearer Interpolation für den Pixelwert des Bildes zu lösen, um einer Verwendung von Floatbewertet Koord x und y .

Meine Frage ist: Da ein Index grid X , Y , wie kann ich ein "inverses grid" X^-1 erzeugen, Y^-1 , so dass:

%Vor%

Und

%Vor%

Für alle Integer-Pixelkoordinaten i, j ?

FWIW, die Bild- und Indexkarten X und Y haben die gleiche Form. Allerdings gibt es keine a-priori-Struktur auf die Indexkarten X und Y. Zum Beispiel sind sie nicht notwendigerweise affin oder starre Transformationen. Sie können sogar nicht konvertierbar sein, z.B. wenn X, Y mehr Pixel in A auf exakt das gleiche Pixel abbildet in B. koordiniert ich nach Ideen für ein Verfahren freu die eine angemessene inverse Karte finden, wenn ein solches vorhanden ist.

Die Lösung muss nicht OpenCV-basierte, wie ich bin nicht OpenCV verwenden, sondern eine andere Bibliothek, die eine remap() Implementierung hat. Während irgendwelche Vorschläge willkommen sind, bin ich besonders an etwas interessiert, das "mathematisch korrekt" ist, d. H. Wenn meine Map M perfekt invertierbar ist, sollte die Methode das perfekte Inverse innerhalb eines kleinen Bereichs der Maschinengenauigkeit finden.

    
SuperElectric 17.01.2017, 17:17
quelle

5 Antworten

2

Nun, ich musste dieses Remap-Inversionsproblem selbst lösen und werde meine Lösung skizzieren.

Gegeben X , Y für die Funktion remap() , die Folgendes tut:

%Vor%

Ich habe Xinv , Yinv berechnet, das von der Funktion remap() verwendet werden kann, um den Prozess umzukehren:

%Vor%

Zuerst baue ich einen KD-Tree für den 2D-Punktsatz {(X[i,j],Y[i,j]} , damit ich den N nächste Nachbarn zu einem bestimmten Punkt (x,y). Ich verwende euklidische Distanz für meine Distanzmetrik. Ich fand eine großartige C ++ Header-Lib für KD-Trees auf GitHub.

Dann gehe ich durch alle (x,y) -Werte in A 's Gitter und finde die N = 5 nächsten Nachbarn {(X[i_k,j_k],Y[i_k,j_k]) | k = 0 .. N-1} in meinem Punktsatz.

  • Wenn distance d_k == 0 für einige k , dann Xinv[x,y] = i_k und Yinv[x,y] = j_k , sonst ...

  • Verwenden Sie die Inverse-Distanz-Gewichtung (IDW) , um einen interpolierten Wert zu berechnen:

    • let Gewicht w_k = 1 / pow(d_k, p) (Ich benutze p = 2 )
    • Xinv[x,y] = (sum_k w_k * i_k)/(sum_k w_k)
    • Yinv[x,y] = (sum_k w_k * j_k)/(sum_k w_k)

Beachten Sie, dass, wenn B ein W x H image ist, X und Y W x H Arrays von Floats sind. Wenn A ein w x h image ist, dann sind Xinv und Yinv w x h Arrays für Floats. Es ist wichtig, dass Sie mit der Bild- und Kartengröße übereinstimmen.

Funktioniert wie ein Zauber! In meiner ersten Version habe ich versucht, die Suche brutal zu erzwingen und ich habe nie darauf gewartet, dass es fertig ist. Ich wechselte zu einem KD-Baum, dann begann ich vernünftige Laufzeiten zu bekommen. Wenn ich jemals Zeit bekomme, möchte ich dies zu OpenCV hinzufügen.

Das zweite Bild unten verwendet remap() , um die Linsenverzerrung aus dem ersten Bild zu entfernen. Das dritte Bild ist ein Ergebnis der Invertierung des Prozesses.

< a href="https://i.stack.imgur.com/qhWA4.png">

    
wcochran 02.09.2017, 00:37
quelle
2

Es gibt keine Standardmethode, dies mit OpenCV zu tun.

Wenn Sie nach einer vollständigen gebrauchsfertigen Lösung suchen, bin ich mir nicht sicher, ob ich Ihnen helfen kann, aber ich kann zumindest eine Methode beschreiben, die ich vor einigen Jahren für diese Aufgabe verwendet habe.

Zunächst sollten Sie Remapping-Maps mit derselben Dimension wie Ihr Quellbild erstellen. Ich habe Karten mit größeren Dimensionen für eine einfachere Interpolation erstellt und im letzten Schritt auf die richtige Größe zugeschnitten. Dann sollten Sie sie mit Werten füllen, die in vorherigen Neuzuordnungs-Maps vorhanden sind (nicht so schwierig: Iterieren Sie einfach über sie und wenn Kartenkoordinaten x und y in Grenzen Ihres Bildes liegen, nehmen Sie ihre Zeile und Spalte als neues y und x, und plazieren Sie in alt x und y Spalte und Zeile der neuen Karte). Es ist eine ziemlich einfache Lösung, aber es gibt ein ziemlich gutes Ergebnis. Für die perfekte Interpolation sollten Sie alte x und y auf ganzzahlige Werte interpolieren, indem Sie Ihre Interpolationsmethode und Nachbarpixel verwenden.

Danach sollten Sie Pixelfarben entweder manuell neu zuordnen oder Ihre Neuzuordnungskarte vollständig mit Pixelkoordinaten füllen und die Version von OpenCV verwenden.

Sie werden ziemlich herausfordernde Aufgabe erfüllen: Sie sollten Pixel in leeren Bereichen interpolieren. Mit anderen Worten, Sie sollten Entfernungen zu nächsten Nicht-Null-Pixelkoordinaten nehmen und die Farben (wenn Sie Farben neu zuordnen) oder Koordinaten (wenn Sie mit der vollständigen Kartenberechnung fortfahren) nach diesen Abständen aufteilen. Eigentlich ist es auch nicht so schwierig für die lineare Interpolation, und Sie können sogar in die remap() Implementierung auf der OpenCV github Seite schauen. Für NN-Interpolation wird es mir viel einfacher - nehmen Sie einfach die Farbe / Koordinate des nächsten Nachbarn.

Und eine abschließende Aufgabe ist die Extrapolation von Bereichen außerhalb der Grenzen von neu zugeordneten Pixelbereichen. Auch ein Algorithmus von OpenCV kann als Referenz verwendet werden.

    
avtomaton 26.01.2017 19:43
quelle
1

Wenn Sie eine Karte aus einer Homographie H ableiten, können Sie H invertieren und die inverse Karten mit cv :: initUndistortRectifyMap () direkt erstellen.

z.B. in Python:

%Vor%

In der OpenCV-Dokumentation wird über initUndistortRectifyMap () angegeben: "Die Funktion erstellt tatsächlich die Zuordnungen für den Inversabbildungsalgorithmus, der von remap () verwendet wird. Das heißt, für jedes Pixel (u, v) im Zielbild berechnet die Funktion die entsprechenden Koordinaten im Quellbild."

Falls Sie die Karten gerade gegeben haben, müssen Sie das selbst tun. Hoewever, Interpolation der Koordinaten der neuen Karten ist nicht trivial, weil die Unterstützungsregion für ein Pixel sehr groß sein könnte.

Hier ist eine einfache Python-Lösung, die die Maps durch Point-to-Point-Mapping invertiert. Dies wird wahrscheinlich einige Koordinaten nicht zugewiesen lassen, während andere mehrmals aktualisiert werden. So kann es Löcher in der Karte geben.

Hier ist ein kleines Python-Programm, das beide Ansätze demonstriert:

%Vor%     
Tobias 01.02.2017 14:40
quelle
0

Von dem, was ich verstehe, haben Sie ein Originalbild und ein transformiertes Bild, und Sie möchten die Natur der Transformation wiederherstellen, die angewendet wurde, ohne es zu wissen, aber vorausgesetzt, dass es etwas Sinnvolles ist, wie eine Rotation oder ein Fisch- Augen verzerren.

Ich würde versuchen, das Bild so zu schwelen, dass es sowohl im Indexbild als auch im normalen Bild in ein Binärbild umgewandelt wird. Versuchen Sie dann, Objekte zu identifizieren. Die meisten Zuordnungen behalten zumindest die Konnektivität und die Euler-Nummer bei. Das größte Objekt im Index ist immer noch das größte Objekt in der Ebene.

Nehmen Sie sich dann Zeit für Ihr angepasstes Bild / Ihre indizierten Paare und sehen Sie, ob Sie die Übersetzung, Rotation und Skalierung entfernen können. Das gibt Ihnen mehrere reverse Karten, die Sie dann zusammennähen können. (Schwer, wenn die Transformation nicht einfach ist, aber das allgemeine Problem der Rekonstitution einer beliebigen Transformation kann nicht gelöst werden).

    
Malcolm McLean 31.01.2017 18:05
quelle
0

OP hier. Ich denke, ich habe eine Antwort gefunden. Ich habe es noch nicht implementiert, und wenn jemand mit einer weniger findigen Lösung kommt (oder etwas falsch mit diesem findet), wähle ich stattdessen ihre Antwort.

Problemstellung

Sei A das Quellenbild, B sei das Zielbild und M sei die Abbildung von A's Koordinaten zu B's Koordinaten, d.h.:

%Vor%

... wobei eckige Klammern die Array-Suche mit ganzzahligen Indizes anzeigen und runde Klammern die bilineare Interpolations-Suche mit Gleitkomma-Indizes anzeigen. Wir wiederholen das obige mit der wirtschaftlicheren Notation:

%Vor%

Wir möchten eine inverse Abbildung N finden, die B so gut wie möglich auf A abbildet:

%Vor%

Das Problem kann ohne Bezug auf A oder B angegeben werden:

%Vor%

... Dabei steht ||*|| für die Frobenius-Norm und I_n für die Identitätskarte mit den gleichen Dimensionen wie für N, d. h. für eine Karte mit:

%Vor%

Naive Lösung

Wenn Ms Werte ganze Zahlen sind und M ein Isomorphismus ist, dann können Sie N direkt als:

konstruieren %Vor%

Oder in unserer vereinfachten Schreibweise:

%Vor%

... wobei I_m die Identitätskarte mit den gleichen Dimensionen wie M ist.

Es gibt zwei Probleme:

  1. M ist kein Isomorphismus, daher werden die obigen "Löcher" in N bei N [i, j,:] für jedes [i, j] nicht unter den Werten in M.
  2. belassen
  3. Die Werte von M sind Gleitpunktkoordinaten [i, j], keine ganzzahligen Koordinaten. Wir können der bilinear-interpolierten Größe N (i, j, :) für Float-Wert i, j nicht einfach einen Wert zuweisen. Um den äquivalenten Effekt zu erzielen, müssen wir stattdessen die Werte von [i, j] 's vier umgebenden Ecken N [Boden (i), Boden (j),:], N [Boden (i), Ceil (j), :], N [ceil (i), floor (j),:], N [ceil (i), ceil (j),:] so dass der interpolierte Wert N (i, j, :) gleich dem gewünschten Wert [ k, l] für alle Pixelzuordnungen [i, j] - & gt; [k, l] in M.

Lösung

Konstruiere leeres N als 3D Tensor von Schwimmern:

%Vor%

Für jede Koordinate [i, j] im Koordinatenraum von A:

  1. Finde das 2x2 Gitter von A-Koordinaten in M, in dem [i, j] liegt. Berechnen Sie die Homographie-Matrix H, die diese A-Koordinaten ihren entsprechenden B-Koordinaten (gegeben durch die Pixelindizes des 2x2-Gitters) zuordnet.
  2. Setze N [i, j,:] = Matul (H, [i, j])

Der potentiell teure Schritt hier wäre die Suche in Schritt 1 für das 2x2-Gitter von A-Koordinaten in M, das [i, j] umgibt. Eine Brute-Force-Suche würde diesen ganzen Algorithmus zu O (n * m) machen, wobei n die Anzahl der Pixel in A und m die Anzahl der Pixel in B ist.

Um dies auf O (n) zu reduzieren, könnte man stattdessen einen Scanline-Algorithmus innerhalb jedes A-Koordinaten-Vierecks ausführen, um alle darin enthaltenen ganzzahligen Koordinaten [i, j] zu identifizieren. Dies könnte als hashmap vorberechnet werden, das ganzzahlige A-Koordinaten [i, j] auf die obere linke Ecke der B-Koordinaten des umschließenden Vierecks [k, l] abbildet.

    
SuperElectric 02.02.2017 14:59
quelle