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

OpenCV get 3D coordinates from 2D pixel

本文关键字:3D 坐标 获取 像素 2D OpenCV      更新时间:2023-10-16

对于我的本科论文,我正在开发一个使用openCV来检测多米诺骨牌的iPhone应用程序。检测在近距离区域效果很好,但是当相机倾斜时,很难检测到远处的瓷砖。 我解决这个问题的方法我想做一些空间计算。为此,我需要将 2D 像素值转换为世界坐标,使用矢量计算新的 3D 位置并将这些坐标转换回 2D,然后检查该位置的颜色/形状。

此外,我需要知道增强现实添加的 3D 位置。

相机矩阵我通过这个链接为iPhone 5创建OpenCV相机矩阵解决pnp

我从核心运动中获得的相机的旋转矩阵。

使用 Aruco 标记将是我最后的手段,因为我无法获得纸张所需的决定性效果。

现在我的问题是,当我知道圆圈的位置和距离时,我能不计算吗? 我不需要以毫米/英寸为单位进行测量,我可以在没有测量的情况下使用矢量。

相机需要能够自由旋转。

我试图反转计算 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° 角。如果角 1 -> 中间边缘 -> 条形图中心和角 2 -> 中间边缘 -> 条形图中心均为 90° 角,则可以找到中间的条形图并找到拼贴的位置。

当视图倾斜时,这些角度将因透视而移动,例如 130° 和 50°。(稍后我将提供图像)。

我现在的想法是制作一个 4 点(底部边缘加中间)的求解 PNP,将 solvePNP 压缩,然后将所需的点和中心栏从 2d 位置旋转到 3d 位置(高度应该无关紧要?然后,我可以检查平移的点角度是否为90°,并进行其他所需的距离计算。

这是我试图完成的图片: 问题标记

我首先找到9个点并排列它们。对于每个边缘,我尝试找到黑条。如上所述,从顶部看,蓝色角,绿色中间边缘到黄色条的中心是90°。 但是,由于相机倾斜,角度不再是90°。我也无法检查两个角度是否是 180° 在一起,那会给我误报。 所以我想做以下步骤:

  1. 检测中心
  2. 检测边缘(3 点)
  3. 用这 4 点解决 PnP
  4. 将边缘和中心点(坐标)旋转到 3D 位置
  5. 测量角度(检查是否均为 90°)

现在我想知道如何将这些点的 2D 坐标转换为 3D。我不在乎距离,因为我只是参考其他距离(例如 1.4 倍的距离中边缘)等计算距离,如果我能以毫米为单位测量距离,那就更好了。会给我更好的结果。

使用 solvePnP,我得到了可以更改为旋转矩阵的 rvec(我相信使用 Rodrigues()。为了测量角度,我的理解是我不需要应用solvePnP的平移(tvec)。

这就引出了我的最后一个问题,在使用iPhone时,我不能使用运动检测的角度事先构建旋转矩阵,而只用它来旋转磁贴以从顶部显示吗?我觉得这将为我节省大量的 CPU 时间,因为我不必为每个图块解决 PnP(最多可以有大约 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://i.stack.imgur.com/c67EH.png
World Coordinates https://i.stack.imgur.com/Im6M8.png
Points to Transform https://i.stack.imgur.com/hHjBM.png
Transformed Points https://i.stack.imgur.com/P6lLS.png

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

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

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

使用求解 PnP 查找条形中心的结果

矩阵 [R|t] 不是平方的,因此根据定义,您不能反转它。然而,这个矩阵存在于射影空间中,它只不过是 R^n(欧几里得空间)的扩展,其中添加了"1"作为 (n+1)st 元素。对于兼容性问题,与射影空间向量相乘的矩阵在其右下角附加一个"1"。那就是:R变成

[R|0]
[0|1]

在您的情况下 [R|t] 变为

[R|t]
[0|1]

你可以取它的反转,读作

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

其中'是转置。您需要的部分是顶行。

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