Bestimmen Sie mit opencv die Außenkamera, um sie mit dem Weltraumobjekt zu öffnen

Ich benutze opencv und openframeworks (dh opengl), um eine Kamera (Welttransformation und Projektionsmatrizen) aus einem Bild (und später mehreren Bildern für die Triangulation) zu berechnen.

Für die Zwecke von opencv wird der "Grundriss" zum Objekt (dh zum Schachbrett) mit 0,0,0 zum Mittelpunkt der Welt. Die Welt- / Bodenpositionen sind bekannt, daher muss ich die Projektionsinformationen (Verzerrungskoeffizienten, FOV usw.) und die extrinsischen Koordinaten der Kamera abrufen.

Ich habe die Ansichtspositionen dieser Grundrisspunkte im normalisierten Ansichtsraum auf mein 2D-Bild abgebildet ([0,0] ist oben links, [1,1] ist unten rechts).

Das Objekt (Grundriss / Weltpunkte) befindet sich in der xz-Ebene, -y oben, also konvertiere ich in die xy-Ebene (Ich bin nicht sicher, ob Z-Up negativ oder positiv ist ...) für opencv, da es eben sein muss

ofMatrix4x4 gWorldToCalibration(
    1, 0, 0, 0,
    0, 0, 1, 0,
    0, 1, 0, 0,
    0, 0, 0, 1
    );

Ich übergebe 1,1 als ImageSize, um Kamera zu kalibrieren. Fahnen sindCV_CALIB_FIX_ASPECT_RATIO|V_CALIB_FIX_K4|CV_CALIB_FIX_K5 calibrateCamera Läuft erfolgreich, gibt mir einen Low-Error (in der Regel um0.003).

mitcalibrationMatrixValues Ich bekomme ein vernünftiges FOV, normalerweise um die 50 Grad, also bin ich mir ziemlich sicher, dass die intrinsischen Eigenschaften korrekt sind.

Nun, um die extrinsische Weltraumtransformation der Kamera zu berechnen ... Ich glaube nicht, dass ich sie verwenden musssolvePnP da ich nur ein Objekt habe (obwohl ich das alles vorher damit ausprobiert habe und mit den gleichen Ergebnissen zurückgekommen bin)

//  rot and trans output...
cv::Mat& RotationVector = ObjectRotations[0];
cv::Mat& TranslationVector = ObjectTranslations[0];

//  convert rotation to matrix
cv::Mat expandedRotationVector;
cv::Rodrigues(RotationVector, expandedRotationVector);

//  merge translation and rotation into a model-view matrix
cv::Mat Rt = cv::Mat::zeros(4, 4, CV_64FC1);
for (int y = 0; y < 3; y++)
   for (int x = 0; x < 3; x++) 
        Rt.at<double>(y, x) = expandedRotationVector.at<double>(y, x);
Rt.at<double>(0, 3) = TranslationVector.at<double>(0, 0);
Rt.at<double>(1, 3) = TranslationVector.at<double>(1, 0);
Rt.at<double>(2, 3) = TranslationVector.at<double>(2, 0);
Rt.at<double>(3, 3) = 1.0;

Jetzt habe ich eine Rotations- und Transformationsmatrix, aber sie ist in der Hauptspalte (ich glaube, da das Objekt völlig verzerrt ist, wenn ich es nicht transponiere, und der obige Code sieht für mich in der Hauptspalte aus).

//  convert to openframeworks matrix AND transpose at the same time
ofMatrix4x4 ModelView;
for ( int r=0;  r<4;    r++ )
    for ( int c=0;  c<4;    c++ )
        ModelView(r,c) = Rt.at<double>( c, r ); 

Tauschen Sie meine Flugzeuge mit der Umkehrung der Matrix zuvor in meinen Koordinatenraum (y aufwärts) zurück.

//  swap y & z planes so y is up
ofMatrix4x4 gCalibrationToWorld = gWorldToCalibration.getInverse();
ModelView *= gCalibrationToWorld;

Ich bin mir nicht sicher, ob ich das tun MUSS ... Ich habe die Flugzeuge nicht negiert, als ich sie in die Kalibrierung gesteckt habe ...

//  invert y and z planes for -/+ differences between opencv and opengl
ofMatrix4x4 InvertHandednessMatrix(
    1,  0,  0, 0,
    0,  -1, 0, 0,
    0,  0,  -1, 0,
    0,  0,  0,  1
    );
ModelView *= InvertHandednessMatrix;

Und schließlich ist die Modellansicht objektrelativ zur Kamera und ich möchte sie invertieren, um kamerarelativ zum Objekt zu sein (0,0,0).

ModelView = ModelView.getInverse();

Dies führt dazu, dass sich eine Kamera am falschen Ort befindet und falsch gedreht wird. Es ist nichtauch In der Ferne befindet sich die Kamera auf der rechten Seite der Y-Ebene, die Übersetzung ist nicht so verrückt und ich denke, es ist der richtige Weg nach oben ... nur noch nicht richtig. Der mit Farbe gezeichnete blaue Kreis ist der Ort, an dem sich die Kamera befindet.

Ich habe unzählige SO-Antworten durchgesehen, die Dokumentation ein Dutzend Mal, aber nicht ganz richtig gefunden. Ich bin mir ziemlich sicher, dass ich alles abgedeckt habe, was ich für die Platzkonvertierung benötige, aber vielleicht habe ich etwas Offensichtliches verpasst ? Oder etwas in der falschen Reihenfolge machen?

Update 1 - Weltraumflugzeug ... Ich habe meine Weltraum-Grundfläche auf XY (Z aufwärts) geändert, um sie an die Eingabe für openCV anzupassen. (gWorldToCalibration ist jetzt eine Identitätsmatrix). Die Drehung ist immer noch falsch und die Projektionsausgabe ist dieselbe, aber ich denke, die Übersetzung ist jetzt korrekt (sie befindet sich mit Sicherheit auf der richtigen Seite der Markierungen).

Update2 - Reale Bildgröße Ich spiele mit der Bildgröße, die in die Kamerakalibrierung einfließt. da ich 1,1 verwende, das normalisiert ist, aber der imageSize-Parameter in ganzen Zahlen ist, dachte ich, dass dies signifikant sein könnte ... und ich denke, es ist (das rote Kästchen ist, wo sich die projizierten Ansichtsraumpunkte mit z schneiden) = 0 Grundebene) Ohne Verzerrungskorrektur ist hier das Ergebnis (Es wurde nur die Bildgröße von 1,1 auf 640.480 geändert. Ich multipliziere auch meine normalisierten Eingabe-Ansicht-Raum-Koordinaten mit 640.480.) Ich werde versuchen, eine Verzerrungskorrektur hinzuzufügen, um zu sehen, ob sie in einer Linie liegtperfekt...

Antworten auf die Frage(3)

Ihre Antwort auf die Frage