59

我有一个校准的相机(固有矩阵和失真系数),我想知道相机位置,知道图像中的一些 3d 点及其对应点(2d 点)。

我知道这cv::solvePnP对我有帮助,在阅读了这个这个之后,我明白我是solvePnP的输出,rvectvec相机坐标系中物体的旋转和平移。

所以我需要找出世界坐标系中的相机旋转/平移。

从上面的链接看来,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)

我不知道 python/numpy 的东西(我正在使用 C++),但这对我来说没有多大意义:

  • rvec,solvePnP 的 tvec 输出是 3x1 矩阵,3 个元素向量
  • cv2.Rodrigues(rvec) 是一个 3x3 矩阵
  • cv2.Rodrigues(rvec)[0] 是一个 3x1 矩阵,3 个元素向量
  • cameraPosition 是一个 3x1 * 1x3 矩阵乘法,它是一个.. 3x3 矩阵。如何在 opengl 中通过简单的glTranslatefglRotate调用来使用它?
4

2 回答 2

64

如果使用“世界坐标”是指“对象坐标”,则必须得到 pnp 算法给出的结果的逆变换。

有一个反转变换矩阵的技巧,可以让您保存通常很昂贵的反转操作,并且可以解释 Python 中的代码。给定一个变换[R|t],我们就有了inv([R|t]) = [R'|-R'*t]R'的转置在哪里R。因此,您可以编写代码(未经测试):

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

更新:稍后,要T与 OpenGL 一起使用,您必须记住 OpenCV 和 OpenGL 之间相机帧的轴不同。

OpenCV 使用计算机视觉中通常使用的参考:X 指向右侧,Y 向下,Z 指向前面(如本图所示)。OpenGL 中相机的帧是:X 指向右侧,Y 向上,Z 指向后面(如该图像的左侧)。因此,您需要围绕 X 轴旋转 180 度。这个旋转矩阵的公式在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

这些转换总是令人困惑,我可能在某些步骤上错了,所以对此持保留态度。

最后,考虑到 OpenCV 中的矩阵以行优先顺序存储在内存中,而 OpenGL 矩阵以列优先顺序存储。

于 2013-09-05T18:46:36.037 回答
8

如果您想将其转换为指定相机位置的标准 4x4 姿势矩阵。使用 rotM 作为左上角的 3x3 正方形, tvec 作为右边的 3 个元素, 0,0,0,1 作为底行

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

然后反转它(以获得相机的姿势而不是世界的姿势)

于 2013-09-05T17:20:29.663 回答