Wie kann ich die Kamera Pose mit 3d-zu-2d-Punkt-Korrespondenzen (mit opencv) schätzen

8

Hallo, mein Ziel ist es, Head-Tracking-Funktionen zu entwickeln, die in einem Flugzeug (Simulator) -Cockpit verwendet werden können, um zivile Piloten zu unterstützen, mit schlechten Sichtbedingungen zu landen und zu fliegen.

Mein Ansatz besteht darin, charakteristische Punkte (in den dunklen Simulator-LEDs) zu erkennen, von denen ich die 3D-Koordinaten kenne, und dann die geschätzte (Kopf getragene Kamera) Pose [R | t] zu berechnen (Rotation mit Translation).

Das Problem, das ich habe, ist, dass die geschätzte Pose immer falsch zu sein scheint und eine Projektion meiner 3D-Punkte (die ich auch verwendet habe, um die Pose zu schätzen) sich nicht überschneidet 2D-Bildpunkte (oder nicht sichtbar).

Meine Fragen sind:

Wie kann ich die Kamerapose mit einer bestimmten Menge von 2D-zu-3D-Punktkorrespondenzen schätzen?

Warum funktioniert es nicht, wie ich es versuche und wo Fehlerquellen liegen könnten?

Wie genau müssen die Messungen (von 3D- und 2D-Punkten und der Kameramatrix) sein, damit die theoretische Lösung in einer realen Umgebung funktioniert?

Wird der Ansatz theoretisch für koplanare Punkte (x, y-Achse geändert) funktionieren?

Die Hardware, die ich verwende, ist der Epson BT-200.

Im Flugzeug habe ich eine feste Ordinate definiert, auf die ich relative Übersetzungen und Rotationen als Ergebnis meines Programms erwarte. Das Programm erkennt die Bildkoordinaten von (eindeutigen) LEDs und gleicht sie mit ihrer entsprechenden 3D-Koordinate ab. Mit einer Kameramatrix habe ich unter Verwendung des Open-CV-Beispiel-Android-Codes ( Ссылка ) gewonnen ) Ich versuche die Pose mit solvePnP zu schätzen.

Meine Kameramatrix und Verzerrung variieren geringfügig. Hier sind einige Werte, die ich von der Prozedur erhalten habe. Ich habe dafür gesorgt, dass die Kreisdistanz meines ausgedruckten Kreismusters gleich ist wie im Quellcode (gemessen in Meter).

Hier sind einige Beispiele und wie ich die OpenCV-Mat davon erstelle.

%Vor%

Um die Kamera-Pose zu schätzen, verwende ich solvePnP (und solvePnPRansac ) wie beschrieben an mehreren Standorten ( 1 , 2 , 3 , 4 ). Das Ergebnis von solvePnP verwende ich als Eingabe für die Projektion ( Calib3d.projectPoints ). Die Umkehrung des konkatierten Ergebnisses [R | t] I verwende ich als geschätzte Pose.

Da meine Ergebnisse in der produktiven Umgebung zu schlecht waren, habe ich eine Testumgebung erstellt. In dieser Umgebung platziere ich die Kamera (wegen ihrer 3D-Form (es ist ein Glas) an der Tischkante leicht nach unten gedreht. Diese Kante benutze ich als Ordinate des Weltkoordinatensystems. Ich habe gesucht wie die open-cv Das Koordinatensystem könnte orientiert sein und unterschiedliche Antworten finden (eine auf stackoverflow und einer in einem offiziellen Youtube-Talk über opencv. Jedenfalls habe ich getestet, ob ich das Koordinatensystem richtig durch Projektion von 3D-Punkten (beschrieben in diesem Koordinatensystem) auf ein Bild bekommen und überprüft habe, ob die gegebene Weltform konstant bleibt / p>

So kam ich mit z nach vorne, y nach unten und x nach rechts.

Um näher an meine Lösung zu kommen, habe ich die Pose in meiner Testumgebung geschätzt. Die Übersetzung Vektor-Ausgabe und Euler-Engel-Ausgabe bezieht sich auf die Inverse von [R | t]. Die euler Engel könnten nicht korrekt angezeigt werden (sie könnten vertauscht oder falsch sein, wenn wir die Reihenfolge berücksichtigen), weil ich sie mit den konventionellen Gleichungen (ich nehme Bezug auf das Flugzeugkoordinatensystem) unter Verwendung eines Koordinatensystems mit offenem Koordinatensystem errechne. (Die Berechnung geschieht in der Klasse Pose, die ich anhängen werde). Aber selbst der Übersetzungsvektor (invers) schien falsch zu sein (in meinem einfachen Test).

In einem Test mit diesem Bild hatte ich eine Rolle (die in den Flugzeugkoordinaten eine Tonhöhe haben könnte) von 30 ° und eine Übersetzung von mehr als 50 . Das scheint vernünftiger zu sein. Ich nahm an, weil meine Punkte koplanar sind, könnte ich mehrdeutige Ergebnisse bekommen. Also realisierte ich einen anderen Test mit einem Punkt, der sich in der Z-Achse änderte.Aber mit diesem Test ist sogar die Projektion gescheitert.

Für solvePnP habe ich alle verschiedenen Lösungsalgorithmen-Flags und verschiedene Parameter für den Ransac-Algorithmus ausprobiert.

Vielleicht kannst du mir irgendwie helfen, meinen Fehler zu finden, oder mir einen guten Weg zeigen, mein erstes Problem zu lösen. Ich werde auch meinen Debugging-Quellcode mit vielen println-Anweisungen und den Debugging-Bildern anhängen. Dieser Code enthält meine Punktmessungen .

Vielen Dank für Ihre Hilfe im Voraus.

Klasse Main.java : Klasse Pose.java : 0.png

1.png

EDIT 22.03.2015: Endlich konnte ich Fehler finden, die ich gemacht habe.

  1. Ich habe ein Mat-Objekt in einer for-Schleife modifiziert, weil OpenCV sehr funktioniert mit Anruf durch Verweis, und ich war hier nicht vorsichtig genug. Also die Tvec und Rvec für die Reprojektion waren nicht richtig.
  2. Einer meiner Punkte in der Testumgebung hatte (im Bild Koordinaten), wurde aufgrund einer Verwechslung der Achsenrichtung falsch markiert.

Also meine Vorgehensweise war im Allgemeinen richtig. Ich erhalte in meinem Test-Datensatz zumindest keine (oft) gültigen Reprojektionen.

Leider liefern die OpenCV-PnP-Algorithmen: "ITERATIVE, P3P, EPNP" verschiedene Ergebnisse, und selbst wenn man eine sehr ungenaue aber nahe liegende intrinsische Schätzung verwendet, sind die Ergebnisse nur manchmal korrekt. Der P3P-Algorithmus soll 3 Lösungen bieten, aber OpenCV bietet nur einen. EPNP soll gute Ergebnisse liefern, aber mit EPNP liefert OpenCV die schlechtesten Ergebnisse, die von meinem Menschen ausgewertet wurden obsation.

Das Problem besteht jetzt darin, die ungenauen Werte zu filtern oder sicherzustellen, dass die OpenCV-Funktion gültige Werte zurückgibt. (Vielleicht sollte ich den nativen Code ändern, um 3 Lösungen für PnP zu erhalten).

Die komprimierten Bilder hier (37MB) , zeigen meine aktuelle Ergebnisse (mit dem ITERATIVE PnP-Solver), mit einer intrinsischen Schätzung von Null-Rotation und 75 cm nach oben. Der Ausdruck hat eine X-Achse vor, y-Achse nach links und Z-nach unten und entsprechende Roll-, Nick- und Gierwinkel.

    
Jakob Alexander Eichler 13.03.2015, 15:06
quelle

1 Antwort

3

Eine Sache, die ich beim Versuch, mein Head-Tracking-System zu implementieren, gelernt habe, ist, dass Sie mit einem einfachen Problem beginnen und dann zu einem komplizierteren wechseln sollten. Ihre Frage ist ziemlich ong und unglücklicherweise habe ich keine Zeit, sie zu analysieren und nach einem Fehler oder logischen Fehler in Ihrem Code zu suchen, also werde ich wenigstens versuchen, Ihnen einige Hinweise und Arbeitsbeispiele zu geben.

Hier ist OpenCV Tutorial für Objekt Übersetzung zu finden und Drehung. Es ist in Python geschrieben, wenn es ein Problem ist, hier Teil meines alten c ++ Projekt .
Mein Projekt führt die gleiche Aufgabe mit der Funktion solvePnP oder solvePnPRansac aus (Sie können den Modus ändern). Beachte, dass mein Code Teil eines alten "Spielplatz" -Projekts ist, also auch nach dem, was ich gespielt habe, ist es ziemlich chaotisch. Wenn Sie es ausführen, zeigen Sie der Kamera ein gedrucktes Schachbrett, drücken Sie 'p', um die Positions- und Rotationsschätzung zu starten, 'm', um den Modus zu wechseln (0-ransac, 1-pnp, 2-posit, scheint nicht zu funktionieren ...) oder 'd' mit Disorptionskoeffizienten ein- / ausschalten.
Beide Projekte beruhen auf dem Finden eines Schachbrettmusters, aber es sollte einfach sein, sie zu modifizieren, um einige andere Objekte zu verwenden.

Kamerakalibrierung - während ich an meinem Kopfverfolgungssystem gearbeitet habe, habe ich es noch nie geschafft, die Kamera zweimal mit den gleichen Ergebnissen zu kalibrieren ... Also entschied ich mich, eine Kalibrierungsdatei zu verwenden, die ich auf Github gefunden habe gut funktioniert hat - hier ein litte mehr finden kann Informationen dazu ein Link zu dieser Datei.

bearbeiten:

Versuchen Sie, mit einer so einfachen wie möglichen Lösung zu beginnen, die in einigen (sogar einfachen) Situationen gute Ergebnisse liefert. Ein guter Punkt meiner Meinung nach beginnen, ist ein Blatt Papier aus Ihrer Testumgebung mit aufgedrucktem Schachbrett von Tutorial ( dieses ) und es zum Laufen bringen. Der Wechsel von diesem zu Ihrem Problem wird viel einfacher sein als das von Ihnen ausgehende Problem. Versuchen Sie, jede Arbeitslösung in einem beliebigen Programmiersprache zu machen - prüfen, Python oder C ++ Version von OpenCV mit - es gibt viel mehr Tutorials / Beispiele als Java-Version und Ergebnisse aus dem Code mit den Ergebnissen aus einigen Arbeits Code Vergleich wird es viel einfacher machen. Wenn Sie eine funktionierende Lösung haben, versuchen Sie, sie so zu modifizieren, dass sie mit Ihrer Testumgebung zusammenarbeitet. Es gibt eine Menge Dinge, die dazu führen können, dass es momentan nicht funktioniert - zu wenig Punkte, Fehler in Ihrem Code oder sogar in OpenCV Java Wrapper, schlechte Interpretation der Ergebnisse, etc ...

edit2:

Unter Verwendung von Punkten aus Ihrem Code habe ich folgende Ergebnisse erhalten:

  

rvec = [[-158.56293283], [1.46777938], [-17.32569125]]
  tvec = [[-36.23910413], [-82.83704819], [266.03157578]]

Leider ist es für mich schwer zu sagen, ob die Ergebnisse gut sind oder nicht ... Das einzige, was mir falsch ist ist, dass sich zwei Winkel von 0 (oder 180) unterscheiden. Aber wenn Sie die letzte Zeile von points2d von (355,37), (353,72), (353,101) in

ändern
  

(355,37), (35 5 , 72), (35 5 , 101)

(ich vermute, es ist dein Fehler, kein richtiges Ergebnis), wirst du bekommen:

  

rvec = [[-159.34101842], [1.04951033], [-11.43731376]]   tvec = [[-25.74308282], [-82.58461674], [268.12321097]]

, das dem richtigen Ergebnis viel näher kommen könnte. Ändern der Kameramatrix Änderungen Ergebnisse viel, so betrachten Werte von diesen Beitrag .

Beachten Sie, dass alle rvec-Werte mit 180.0/3.14 multipliziert werden - in c ++ und python rvec vector, die von solvePnPRansac zurückgegeben werden, Winkel im Bogenmaß enthält.

    
cyriel 16.03.2015 13:28
quelle