La position de la caméra dans le monde de coordonnées de cv::solvePnP

j'ai une caméra calibrée (matrice intrinsèque et coefficients de distorsion) et je veux connaître la position de la caméra en connaissant quelques points 3d et leurs points correspondants dans l'image (points 2d).

je sais que cv::solvePnP pourrait m'aider, et après avoir lu ce et ce je comprends que je les sorties de solvePnP rvec et tvec sont la rotation et la traduction de l'objet à caméra système de coordonnées.

donc je dois trouver la rotation de la caméra/traduction dans le système de coordonnées du monde.

D'après les liens ci-dessus, il semble que le code soit simple, en python:

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)

Je ne sais pas python / numpy stuffs (j'utilise C++) mais cela n'a pas beaucoup de sens pour moi:

  • rvec, tvec sortie de solvePnP sont 3x1 de la matrice, 3 éléments vecteurs
  • cv2.Rodrigues (rvec) est une matrice 3x3
  • cv2.Rodrigues (rvec) [0] est une matrice 3x1, vecteurs 3 éléments
  • cameraPosition est un 3x1 * 1x3 multiplication de matrice qui est une.. Matrice de 3x3. Comment puis-je l'utiliser dans opengl avec des appels simples glTranslatef et glRotate ?
41
demandé sur Community 2013-09-05 17:20:01

2 réponses

si avec " coordonnées du monde "vous voulez dire" coordonnées de l'objet", vous devez obtenir la transformation inverse du résultat donné par l'algorithme pnp.

il y a un truc pour inverser les matrices de transformation qui vous permet de sauvegarder l'opération d'inversion, qui est habituellement coûteuse, et qui explique le code en Python. Étant donné une transformation [R|t] , nous avons que inv([R|t]) = [R'|-R'*t] , où R' est la transposition de R . Ainsi, vous pouvez de code (pas testé):

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

mise à jour: plus tard, pour utiliser T avec OpenGL vous devez garder à l'esprit que les axes du cadre de la caméra diffèrent entre OpenCV et OpenGL.

OpenCV utilise la référence habituellement utilisée dans la vision par ordinateur: X pointe vers la droite, Y vers le bas, Z vers l'avant (comme dans this image ). Le cadre de la caméra à OpenGL est: X pointe vers la droite, Y vers le haut, Z vers L'arrière (comme dans cette image ). Donc, vous devez appliquer une rotation autour de l'axe X de 180 degrés. La formule de cette matrice de rotation est dans 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

ces transformations sont toujours déroutantes et je peux me tromper à un moment donné, alors prenez ceci avec un grain de sel.

enfin, tenir compte du fait que les matrices dans OpenCV sont stockées en ligne-Ordre majeur dans la mémoire, et OpenGL ceux, dans colonne-Ordre majeur.

45
répondu ChronoTrigger 2018-05-02 01:05:23

si vous voulez le transformer en une matrice de pose 4x4 standard spécifiant la position de votre caméra. Utilisez rotM comme 3x3 square en haut à gauche, tvec comme les 3 éléments à droite, et 0,0,0,1 comme la rangée du bas

pose = [rotation   tvec(0)
        matrix     tvec(1)
        here       tvec(2)
        0  , 0, 0,  1]

puis l'Inverser (pour obtenir la pose de la caméra au lieu de la pose du monde)

3
répondu Hammer 2013-09-05 19:14:56