OpenCV三角化点的左右手规则问题

27

我有两个摄像头,它们被刚性地并排固定在一起,朝向平行方向。

相机的投影矩阵为Projection matrix for left camera

相机的投影矩阵为Projection matrix for right camera

当我对应点的两个向量执行triangulatePoints时,我得到了3D空间中的点集。所有3D空间中的点都具有负Z坐标

所以,要找出问题的根源...

我的假设是OpenCV使用右手坐标系。

提醒右手法则: Handedness reminder

我假设每个相机的初始方向都指向正Z轴方向

因此,通过使用像我在开头介绍的那样的投影矩阵,我会假设相机在空间中的位置如下:

How I imagine cameras are positioned and oriented in space

这个假设与我观察到的当我得到三角化点的负Z坐标相矛盾。我能想到的唯一解释是OpenCV实际上使用左手坐标系。因此,根据我在开头所述的投影矩阵,摄像机在空间中的位置如下:

Cameras in Left Hand coordinate system

这表明在这种情况下,我的左侧相机不在左侧。这就是为什么我得到负点深度的原因。

此外,如果我尝试将triangulatePointssolvePnP结合使用,就会遇到问题。

我使用triangulatePoints的输出作为solvePnP的输入。我期望获得靠近三维坐标系原点的相机坐标。我期望计算出的相机位置与最初使用的投影矩阵相匹配。但是这并没有发生。我得到了一些完全荒谬的结果,距离基线长度超过10倍的预期值。

示例

这个示例更完整地表示了上述问题。

points3D enter image description here

这里是生成这些点的代码。

继续前进,设置相机A和相机D...

Mat cameraMatrix = (Mat_<double>(3, 3) <<
    716.731, 0, 660.749,
    0, 716.731, 360.754,
    0, 0, 1);
Mat distCoeffs = (Mat_<double>(5, 1) << 0, 0, 0, 0, 0);



Mat rotation_a = Mat::eye(3, 3, CV_64F); // no rotation
Mat translation_a = (Mat_<double>(3, 1) << 0, 0, 0); // no translation
Mat rt_a;
hconcat(rotation_a, translation_a, rt_a);
Mat projectionMatrix_a = cameraMatrix * rt_a; 

Mat rotation_d = (Mat_<double>(3, 1) << 0, CV_PI / 6.0, 0); // 30° rotation about Y axis
Rodrigues(rotation_d, rotation_d); // convert to 3x3 matrix
Mat translation_d = (Mat_<double>(3, 1) << 100, 0, 0);
Mat rt_d;
hconcat(rotation_d, translation_d, rt_d);
Mat projectionMatrix_d = cameraMatrix * rt_d;

当被投影AD观察时,points3D的像素坐标是什么?

Mat points2D_a = projectionMatrix_a * points3D;
Mat points2D_d = projectionMatrix_d * points3D;

我把它们放进向量中:

vector<Point2f> points2Dvector_a, points2Dvector_d;

接着,我再次生成3D点。

Mat points3DHomogeneous;
triangulatePoints(projectionMatrix_a, projectionMatrix_d, points2Dvector_a, points2Dvector_d, points3DHomogeneous);
Mat triangulatedPoints3D;
transpose(points3DHomogeneous, triangulatedPoints3D);
convertPointsFromHomogeneous(triangulatedPoints3D, triangulatedPoints3D);

现在,triangulatedPoints3D的初始状态如下:

enter image description here

它们与points3D完全相同。

然后是最后一步。

Mat rvec, tvec;
solvePnP(triangulatedPoints3D, points2Dvector_d, cameraMatrix, distCoeffs, rvec, tvec);

最终的rvectvec

enter image description hereenter image description here

我原本希望得到更类似于创建 projectionMatrix_d 所使用的变换,即 (100, 0, 0) 的平移和围绕 Y 轴旋转 30°。
如果在创建投影矩阵时使用了反向变换,就像这样:
Mat rotation_d = (Mat_<double>(3, 1) << 0, CV_PI / 6.0, 0); // 30° rotation about Y axis
Rodrigues(-rotation_d, rotation_d); // NEGATIVE ROTATION
Mat translation_d = (Mat_<double>(3, 1) << 100, 0, 0);
Mat rt_d;
hconcat(rotation_d, -translation_d, rt_d); // NEGATIVE TRANSLATION
Mat projectionMatrix_d = cameraMatrix * rt_d;

然后我得到 rvectvec

enter image description hereenter image description here

这样就更有意义了。但是我改变了起始转换,使得旋转为负数 CV_PI / 6.0 -> -CV_PI / 6.0,得到的 rvectvec 分别为:

enter image description hereenter image description here

我想找到一个解释,为什么会发生这种情况。为什么我从 solvePnP 得到如此奇怪的结果。


1
我可以确认OpenCV使用右手坐标系,我也经常使用triangulatePointssolvePnP。我不确定你的问题是什么,但根据你问题的描述,我建议你先调查一下你的输入数据。为了确切地找出问题所在,需要更多的细节信息(例如相机矩阵、实际代码、输入数据样本等)。 - BConic
谢谢您的回复。我扩展了问题。 - AndroC
我可以确认,在生成投影矩阵时所用的旋转和平移值不应该反映相机的移动,而是点的移动。因此,如果相机向左移动了100个单位,那么就相当于点向右移动了100个单位。但是,向右移动的运动应该在投影矩阵的计算中使用。 - AndroC
我有同样的问题... 有人可以帮忙吗? - Mirnyy
1个回答

3

OpenCV坐标系统是右手坐标系,这里的答案提供了一个关于OpenCV相机系统的图示例。我猜测困惑在于rvectvec,后者不是给出相机的平移,而是指向世界原点。第一个答案基于一个例子解释了它。你可以通过简单的矩阵乘法从solvePnP的输出中获得实际的投影矩阵,详情在此答案的第一个回答中。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接