OpenCV如何从2D像素获取3D坐标

3
我正在撰写本科论文,涉及使用openCV开发iPhone应用程序来检测多米诺骨牌。在近距离区域内,检测效果良好,但是当相机角度改变时,远处的骨牌很难被检测到。
我的解决方法是进行一些空间计算。为此,我需要将2D像素值转换为世界坐标,使用向量计算一个新的3D位置,再将这些坐标转换回2D,并检查该位置的颜色/形状。
此外,我还需要知道增强现实添加的3D位置。
我通过此链接create opencv camera matrix for iPhone 5 solvepnp获得了相机矩阵。
相机的旋转矩阵来自Core Motion。
如果可能的话,我不想使用Aruco标记,因为我无法得到所需的效果。
现在我的问题是,如果我知道例如数字5上圆圈的位置和距离,我不能进行计算吗?我不需要以毫米/英寸作为单位的测量,我可以使用没有测量的向量。
相机需要能够自由旋转。
我尝试反转计算sm'=A[R|t]M'以便能够在3D中计算2D坐标。但是我在纸上反转[R|t]时卡住了,而且我也不知道该如何在Swift或C++中做到这一点。
我已经阅读了许多不同的论坛帖子、书籍等,但我完全陷入困境,感激您能给我的任何帮助/意见。否则我就完蛋了。
非常感谢您的帮助。
更新:
通过使用Micka建议的solvePnP,我能够获得摄像机角度的旋转和平移向量。这意味着如果您能够识别图像中的多个2D点并知道它们各自的3D世界坐标(以毫米、厘米、英寸等为单位),那么您可以获得将点从已知的3D世界坐标投影到相应的2D坐标的机制(使用opencv projectPoints函数)。
接下来我需要解决的是从2D到3D坐标的转换,其中我需要遵循ozlsn的方法,使用从solvePnP得到的矩阵的逆矩阵。 更新2: 通过俯视图,我已经能够很好地检测到瓦片及其在3D世界中的位置: 俯视图下的瓦片

然而,如果我现在倾斜视图,我的计算就不再有效了。例如,我会检查一个9个点组的底部边缘和黑色分割线的中心是否为90°角。如果Corner1->Middle Edge->Bar Center和Corner2->Middle Edge->Bar Center都是90°角,则可以找到中间的条形码并找到瓷砖的位置。

当视图被倾斜时,由于透视关系,这些角度将被移动到130°和50°左右(稍后我会提供一张图片)。

我现在的想法是使用4个点(底部边缘加中间点)进行solvePNP计算,并将需要的点和中心条从2D位置旋转到3D位置(高度应该无关紧要?)。然后,我可以用翻译后的点检查角度是否为90°,并进行其他必要的距离计算。

这是我所要实现的图片: Markings for Problem

首先找到九个点并将它们排列。对于每条边,我会尝试找到黑色的线条。如上所述,从顶部看,蓝色角、绿色中间边缘到黄色线条中心的角度为90°。然而,由于摄像头的角度不同,这个角度不再是90°了。我也不能检查两个角度是否加起来等于180°,这会给我带来错误的结果。所以我想执行以下步骤:

  1. 检测中心点
  2. 检测边缘(3个点)
  3. 使用这4个点进行SolvePnP求解
  4. 将边缘和中心点(坐标)旋转到3D位置
  5. 测量角度(检查两者是否都为90°)

现在我想知道如何将这些点的2D坐标转换成3D坐标。我不关心距离,因为我只是与其他点相对计算(例如,中间-边缘的距离是1.4倍),如果我能测量距离(以毫米为单位),那就更好了,可以得到更好的结果。

使用solvePnP可以得到rvec,然后用Rodrigues()将其转换为旋转矩阵。测量角度时,我的理解是不需要应用solvePnP中的平移(tvec)。
这带来了我的最后一个问题,当使用iPhone时,我不能使用运动检测的角度预先构建旋转矩阵,并仅使用该矩阵将瓷砖旋转以从上方显示吗?我觉得这将使我节省很多CPU时间,因为我不必为每个瓷砖都使用solvePnP(可能最多可达100个瓷砖)。
寻找单应性矩阵
vector<Point2f> tileDots;
tileDots.push_back(corner1);
tileDots.push_back(edgeMiddle);
tileDots.push_back(corner2);
tileDots.push_back(middle.Dot->ellipse.center);

vector<Point2f> realLivePos;
realLivePos.push_back(Point2f(5.5,19.44));
realLivePos.push_back(Point2f(12.53,19.44));
realLivePos.push_back(Point2f(19.56,19.44));
realLivePos.push_back(Point2f(12.53,12.19));

Mat M = findHomography(tileDots, realLivePos, CV_RANSAC);

cout << "M = "<< endl << " "  << M << endl << endl;

vector<Point2f> barPerspective;
barPerspective.push_back(corner1);
barPerspective.push_back(edgeMiddle);
barPerspective.push_back(corner2);
barPerspective.push_back(middle.Dot->ellipse.center);
barPerspective.push_back(possibleBar.center);
vector<Point2f> barTransformed;

if (countNonZero(M) < 1)
{
    cout << "No Homography found" << endl;
} else {
    perspectiveTransform(barPerspective, barTransformed, M);
}

然而,这给我带来了错误的值,我不知道该去哪里寻找(Sehe den Wald vor lauter Bäumen nicht mehr)。

Image Coordinates https://istack.dev59.com/c67EH.webp
World Coordinates https://istack.dev59.com/Im6M8.webp
Points to Transform https://istack.dev59.com/hHjBM.webp
Transformed Points https://istack.dev59.com/P6lLS.webp

你看,我甚至太蠢了,无法在这里发布4张图片??!

第4个索引项应该在x 2007 y 717处。 我不知道我在这里做错了什么。

更新3: 我找到了以下帖子从图像点计算x、y坐标(3D),它正好做我需要的事情。我不知道也许有更快的方法可以做到这一点,但是如果不这样做,我就无法找到它。目前我可以进行检查,但仍然需要测试算法是否足够稳健。

使用SolvePnP找到栏中心的结果


是的,其他瓷砖都在同一平面上,但是我不需要知道从相机到“5”瓷砖的距离才能给出正确的x、y、z坐标吗?还是我只需给出其中一个点0,0,0? - Maverick2805
如果您想要固定坐标系,那么该瓷砖(或任何其他您选择的参考对象)需要是静态的,或者相机必须是静态的。 - Micka
我觉得我需要放弃了,我对这个太笨了。当将透视变换应用于这些点时,我得到的结果值是错误的。 - Maverick2805
也许你可以测量并使用石头中间黑色条的角落? - Micka
我在Photoshop中添加了线条和交叉点。也许它们有点偏移。更多是为了展示一种表示方法。对于我大部分测试图像,解决PnP都运行良好。一旦我回到家,我可以将结果添加到问题中。但是对于某些灰色点,我有一个问题,因为它无法检测到某些圆形(浅灰色与瓷砖的白色太接近了)。我使用HSV转换成白色/灰色范围,并使用Canny来查找圆圈。也许这不是最好的方法,但是对我来说是唯一有效的方法。 - Maverick2805
显示剩余13条评论
1个回答

0
矩阵[R|t]不是方阵,因此按定义,您无法对其进行反演。但是,该矩阵存在于投影空间中,这只是R ^ n(欧几里得空间)的扩展,其中添加了'1'作为(n + 1)个元素。出于兼容性问题,与投影空间向量相乘的矩阵在其右下角附加了'1'。也就是说:R变成了
[R|0]
[0|1]

在你的情况下,[R|t] 变成了:
[R|t]
[0|1]

而且你可以取它的逆,它读作

[R'|-Rt]
[0 | 1 ]

其中'表示转置。你需要的部分是顶行。

由于手机在三维空间中进行翻译,所以你需要考虑像素的距离。这意味着关于你是否需要以毫米/英寸为单位的距离的问题的答案是肯定的。只有当你可以假设相机平移与深度之比非常小,并且这被称为弱透视相机时,答案才会改变。你正在尝试解决的问题并不容易。仍然有人在攻读博士学位时对此进行研究。


我尝试解决这个问题,但是我得到的数字让我感到困惑,似乎无法相加。rvec = [-0.6460095212173805; 2.037458031110235; -1.796950744317753],其结果为旋转矩阵:罗德里格斯旋转矩阵 = [-0.8358446378210687,-0.1072498757737782,0.5383875978997618; -0.5475693554041162,0.0929663142410786,-0.8315798611310578; 0.03913492619243286,-0.9898761177859267,-0.1364321406010136]。 如果我转置R,并将-R乘以t,然后再将其乘以原始坐标(2009|871|1|1),我得到的是-14'201 | 18657,这似乎非常错误。 - Maverick2805

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