undistortPoints () kann keine Linsenverzerrungen verarbeiten

8

Ich benutze die openCV-Funktion projectPoints (), um eine Menge von 3D-Punkten zu rotieren, zu übersetzen und zu projizieren, und lösePnp (), um diese Drehung und Übersetzung zu finden. Dies funktioniert gut, wenn die Linsenverzerrungskoeffizienten alle null sind, aber sonst versagen. Es braucht so wenig Verzerrung, um vollständig zu versagen:

%Vor%

Der Code ist unten:

%Vor%

Wenn alle Verzerrungskoeffizienten 0 sind, ist das Ergebnis OK:

Extrinsische Parameter finden (PnP)
Testtransformationen: Rotation: [3, 5, 7]; Übersetzung: [10, 20, 30]
Verteilungskoeffizient: [0, 0, 0, 0, 0]
============= Laufendes PnP ...
Rotation: [2.999999581709123; 4.999997813985293; 6.999999826089725]
Übersetzung [9.999999792663072; 19.99999648222693; 29.99999699621362]

Wenn sie jedoch nicht Null sind, ist das Ergebnis völlig falsch:

Extrinsische Parameter finden (PnP)
Testtransformationen: Rotation: [3, 5, 7]; Übersetzung: [10, 20, 30]
Verteilungskoeffizient: [1.2, 0.2, 0, 0, 0]
============= Laufendes PnP ...
Rotation: [-91.56479629305277; -124.3631985067845; -74.46486950666471]
Übersetzung [-69.72473511009439; -117.7463271636532; -87.27777166027946]

Da die Leute gefragt haben, füge ich intermediate input hinzu - einige 3D-Punkte und ihre Projektionen für Verzerrungskoeffizienten ungleich Null. Meine Kameramatrix war KameraMatrix & lt; & lt; 300, 0, 200, 0, 300, 100, 0, 0, 1 .;

3d Punkte [53.0283, 19.9259, 40.1059]; 2D-Projektion [1060.34, 700.59]
3d Punkte [81.4385, 43.7133, 24.879]; 2D Projektion [6553.88, 5344.22]
3d Punkte [77.3105, 76.2094, 30.7794]; 2D-Projektion [5143.32, 6497.12]
3d Punkte [70.2432, 47.8447, 79.219]; 2D-Projektion [771.497, 611.726]

Eine weitere interessante Beobachtung: die Anwendung von unverzerrt, wenn distCoeff nicht Null ist, funktioniert nicht wirklich (aber es erzeugt identische 2D-Punkte, wenn die Verzerrungskoeffizienten alle 0 sind):

%Vor%

Anwendung undistort ...
2d Original [1060.34, 700.59]; 2d undistort [0, 0]
2d Original [6553.88, 5344.22]; 2d undistort [0, 0]
2d Original [5143.32, 6497.12]; 2d undistort [0, 0]
2d Original [771.497, 611.726]; 2d undistort [0, 0]

Der Grund, warum ich die Unverzerrung () versuchte, liegt darin, dass, wenn man die Wirkung bekannter intrinsischer Parameter rückgängig macht, PnP nur ein minimales Richtungsproblem der Form Ax = 0 wird. Es braucht min. 6 Punkte für eine ungefähr lineare Lösung, die wahrscheinlich mit LMA weiter verbessert wird (flags = CV_ITERATIVE). Technisch gibt es nur 6DOF und somit 3 Punkte, so dass andere Methoden (Flags = CV_P3P, CV_EPNP) weniger Punkte benötigen. Wie auch immer, unabhängig von einer Methode oder Anzahl von Punkten ist das Ergebnis immer noch mit Nicht-Null-Verzerrungskoeffizienten ungültig. Das letzte, was ich versuchen werde, ist, alle Punkte auf eine 3D-Ebene zu legen. Es schlägt immer noch fehl:

%Vor%

Extrinsische Parameter finden (PnP)
Testtransformationen: Rotation: [3, 5, 7]; Übersetzung: [10, 20, 30]
Verteilungskoeffizient: [1.2, 0.2, 0, 0, 0]
============= Laufendes PnP ...
Rotation: [-1830.321574903016; 2542,206083947917; 2532.255948350521]
Übersetzung [1407.918216894239; 1391,373407846455; 556.7108606094299]

    
Vlad 08.04.2014, 00:09
quelle

2 Antworten

5

Wie funktioniert der Code?

Ich kann das beschriebene Verhalten mit dem von Ihnen bereitgestellten Code reproduzieren, jedoch löst eine der beiden folgenden Optionen das Problem:

  • const Point3f tvec(10, 20, 30); durch const Point3f tvec(10, 20, N); ersetzen, wobei N viel niedriger ist als 0 (z. B. -300) oder viel größer als 100 (z. B. 300 ).

  • Ersetzen Sie Ihren Anruf durch einen Aufruf von solvePnP in solvePnPRansac .

Warum behebt jede dieser Änderungen das unerwünschte Verhalten?

Überlegen Sie sich zuerst, was Ihr ursprünglicher Code von der Funktion solvePnP anfordert. Sie verwenden eine Rotation von ziemlich kleiner Größe, daher nehme ich zur Vereinfachung der Erklärung an, dass die Rotation Identität ist. Dann wird die Kamera an den Weltkoordinaten X = 10, Y = 20 und Z = 30 positioniert und Sie erzeugen zufällig Objektpunkte mit Weltkoordinaten (X, Y, Z), die einheitlich in [0,100] 3 gezeichnet sind . Die Kamera befindet sich also in der Mitte des möglichen Bereichs für die Objektpunkte , wie im folgenden Bild dargestellt:

Dies bedeutet, dass Objektpunkte sehr nahe an der Brennebene (d.h. der Ebene, die durch das optische Zentrum und senkrecht in Bezug auf die optische Achse verläuft) erzeugt werden können. Die Projektion im Kamerabild für solche Objektpunkte ist nicht definiert. In der Praxis ist der nichtlineare Optimierungsalgorithmus für undistortPoints jedoch selbst für Objektpunkte in der Nähe der Fokusebene nicht stabil . Diese Instabilität führt dazu, dass der iterative Algorithmus für undistortPoints divergiert, außer wenn die Koeffizienten alle null sind, da in diesem Fall die Anfangswerte während der Schätzung streng konstant bleiben.

Daher sind die zwei möglichen Lösungen, um dieses Verhalten zu vermeiden, die folgenden:

  • Vermeide es, Objektpunkte in der Nähe der Brennebene der Kamera zu erzeugen, d. h. ändere den Translationsvektor oder den Bereich der Koordinaten der Objektpunkte.

  • Eliminiere die Objektpunkte zu nah an der Brennebene der Kamera, deren unverzerrte Schätzung divergierte (Ausreißer), vor der PnP-Schätzung zum Beispiel mit solvePnPRansac .

Details dazu, warum undistortPoints fehlschlägt:

Hinweis: Da wir die 3D-Weltpunkte kennen, habe ich den folgenden Aufruf verwendet, um die wahren unverzerrten Koordinaten unabhängig vom Ergebnis von undistortPoints zu erhalten:

%Vor%

Die folgende Funktion ist eine vereinfachte Version dessen, was undistortPoints macht:

%Vor%

Wenn Sie Code hinzufügen, um zu überprüfen, wie sich x und y bei jeder Iteration ändern, sehen Sie, dass die iterative Optimierung abweicht, da r2 am Anfang sehr groß ist. Hier ist ein Protokollbeispiel:

%Vor%

Wenn r2 groß ist, ist r2*r2*r2 sehr groß, daher ist icdist sehr klein, daher beginnt die nächste Iteration mit einem sehr kleinen r2 . Wenn r2 sehr klein ist, ist icdist nahe 1, daher sind x und y jeweils auf x0 und y0 gesetzt und wir sind zurück mit einem großen r2 , etc.

Warum ist r2 an erster Stelle so groß? Weil die Punkte nahe der Brennebene erzeugt werden können, sind sie in diesem Fall weit von der optischen Achse entfernt (daher ein sehr großer r2 ). Siehe das folgende Protokollbeispiel:

%Vor%

Sie können sehen, dass für die meisten Punkte r2 sehr groß ist, mit Ausnahme einiger weniger (# 3, # 4 und # 7), die auch mit der besten Genauigkeit der Unverzerrung verbunden sind.

Dieses Problem ist auf den speziellen Open-Source-Algorithmus zurückzuführen, der in OpenCV implementiert wurde und aufgrund seiner Effizienz ausgewählt wurde. Andere nichtlineare Optimierungsalgorithmen (z. B. Levenberg-Marquardt) wären genauer, aber auch viel langsamer und wären in den meisten Anwendungen definitiv ein Overkill.

    
AldurDisciple 11.04.2014, 07:06
quelle
3

Lass mich durch opencv Quellen gehen. Aber zuerst präsentiere ich "pure" opencv-Funktion, die wie in den Quellen funktioniert (bitte lesen Sie unten, wie ich diesen Punkt bekommen habe) mit Ihrem Code zusammen, um zu zeigen, dass es als Bibliotheks-Bibliothek funktioniert:

%Vor%

R ist Rotation, t Translation, k Verzerrung. Schauen Sie sich die 'r2'-Berechnung an - sie ist x * x + y * y, aber x, y ist die Position (skaliert durch z though), unmittelbar nachdem die Translation und Rotation angewendet wurde. Und dieses steht für (wie wikipedia ) für "quadratische Entfernung in Bild projiziert durch das ideale Pinhole-Modell ". Wir können sagen, dass die Implementierung von projectPoints OK ist.

Wie ich zu diesem Ergebnis kam:

Ich grabe Version 2.4.8. Wenn Sie im calib3d-Modul zur Kalibrierung.cpp gehen, beginnen Sie mit

%Vor%

Nichts besonderes, oder? Keine Inhaltsmanipulation überhaupt. Lass uns tiefer gehen:

%Vor%

Der letzte Teil ist wichtig, weil wir mit cv :: Rodriguez eine Rotationsmatrix aus dem Rotationsvektor erzeugen. Und später in der Funktion erstellen wir auch Übersetzungsmatrix, aber immer noch keine Datenmanipulation. Gehen Sie weiter in den ProjectPoints2:

%Vor%

Hier legen wir Brennweiten von Kameramatrix und Hauptpunktkoordinaten fest. Außerdem setzen wir das Array k, das Verzerrungskoeferenzen enthält. Jetzt haben wir die Einrichtung der Variablen abgeschlossen. Gehen wir zu den Berechnungen:

%Vor%

Und wir haben die Funktion genau so, wie ich sie oben dargestellt habe /

    
marol 10.04.2014 23:04
quelle