Ich habe eine kalibrierte Kamera (intrinsische Matrix und Verzerrungskoeffizienten) und möchte die Kameraposition wissen, indem ich einige 3D-Punkte und ihre entsprechenden Punkte im Bild (2D-Punkte) kenne.
ich weiß das cv::solvePnP
könnte mir helfen, und nachdem ich dies und das gelesen habe, verstehe ich, dass ich die Ausgaben von PnP löse rvec
und tvec
sind die Rotation und Translation des Objekts im Kamerakoordinatensystem.
Also muss ich die Kamerarotation / -translation im Weltkoordinatensystem herausfinden.
Aus den obigen Links geht hervor, dass der Code in Python einfach ist:
found,rvec,tvec = cv2.solvePnP(object_3d_points, object_2d_points, camera_matrix, dist_coefs)
rotM = cv2.Rodrigues(rvec)[0]
cameraPosition = -np.matrix(rotM).T * np.matrix(tvec)
Ich kenne Python/Numpy-Zeugs nicht (ich verwende C++), aber das macht für mich nicht viel Sinn:
- rvec, tvec-Ausgaben von solvePnP sind 3×1-Matrix, 3-Element-Vektoren
- cv2.Rodrigues(rvec) ist eine 3×3-Matrix
- cv2.Rodrigues(rvec)[0] ist eine 3×1-Matrix, 3 Elementvektoren
- cameraPosition ist eine 3×1 * 1×3-Matrixmultiplikation, die eine 3×3-Matrix ist. wie kann ich das in opengl mit simple
glTranslatef
und glRotate
Anrufe?
Wenn Sie mit “Weltkoordinaten” “Objektkoordinaten” meinen, müssen Sie die inverse Transformation des vom pnp-Algorithmus gelieferten Ergebnisses erhalten.
Es gibt einen Trick zum Invertieren von Transformationsmatrizen, mit dem Sie sich die normalerweise teure Invertierungsoperation sparen können, und der den Code in Python erklärt. Angesichts einer Transformation [R|t]
wir haben das inv([R|t]) = [R'|-R'*t]
wo R'
ist die Transponierte von R
. Sie können also codieren (nicht getestet):
cv::Mat rvec, tvec;
solvePnP(..., rvec, tvec, ...);
// rvec is 3x1, tvec is 3x1
cv::Mat R;
cv::Rodrigues(rvec, R); // R is 3x3
R = R.t(); // rotation of inverse
tvec = -R * tvec; // translation of inverse
cv::Mat T = cv::Mat::eye(4, 4, R.type()); // T is 4x4
T( cv::Range(0,3), cv::Range(0,3) ) = R * 1; // copies R into T
T( cv::Range(0,3), cv::Range(3,4) ) = tvec * 1; // copies tvec into T
// T is a 4x4 matrix with the pose of the camera in the object frame
Aktualisieren: Später zu verwenden T
Bei OpenGL müssen Sie beachten, dass sich die Achsen des Kamerarahmens zwischen OpenCV und OpenGL unterscheiden.
OpenCV verwendet die normalerweise in Computer Vision verwendete Referenz: X zeigt nach rechts, Y nach unten, Z nach vorne (wie in dieses Bild). Der Rahmen der Kamera in OpenGL ist: X zeigt nach rechts, Y nach oben, Z nach hinten (wie auf der linken Seite von dieses Bild). Sie müssen also eine Drehung um die X-Achse von 180 Grad anwenden. Die Formel dieser Rotationsmatrix ist in Wikipedia.
// T is your 4x4 matrix in the OpenCV frame
cv::Mat RotX = ...; // 4x4 matrix with a 180 deg rotation around X
cv::Mat Tgl = T * RotX; // OpenGL camera in the object frame
Diese Transformationen sind immer verwirrend und ich kann mich bei einem Schritt irren, also nehmen Sie dies mit einem Körnchen Salz.
Berücksichtigen Sie schließlich, dass Matrizen in OpenCV in der Reihenfolge der wichtigsten Zeilen im Speicher gespeichert werden und die von OpenGL in der Reihenfolge der Spalten.
Wenn Sie es in eine Standard-4×4-Pose-Matrix umwandeln möchten, die die Position Ihrer Kamera angibt. Verwenden Sie rotM als oberes linkes 3×3-Quadrat, tvec als die 3 Elemente auf der rechten Seite und 0,0,0,1 als untere Reihe
pose = [rotation tvec(0)
matrix tvec(1)
here tvec(2)
0 , 0, 0, 1]
dann invertieren (um die Pose der Kamera anstelle der Pose der Welt zu erhalten)
.