使用OpenCV从屏幕坐标计算世界坐标

9

我使用OpenCV计算了相机的内参和外参。现在,我想从屏幕坐标(u,v)计算世界坐标(x,y,z)。

请问如何实现呢?

注意:由于我使用Kinect,已知z坐标。

非常感谢您的帮助!谢谢!


所以你的意思是你有Xscreen、Yscreen和Zworld?而你想要Xworld、Yworld和Zworld? - Hammer
2个回答

30

我先解释一下如何计算,如果你读一些有关针孔相机模型和简单透视投影的东西,会对你有所帮助。想要快速了解,请查看这里。我将尝试提供更多信息。

因此,让我们从相反的角度开始描述相机的工作原理:将世界坐标系中的3D点投影到我们图像中的2D点。根据相机模型:

P_screen = I * P_world

或(使用齐次坐标)

| x_screen | = I * | x_world |
| y_screen |       | y_world |
|    1     |       | z_world |
                   |    1    |

在哪里

I = | f_x    0    c_x    0 | 
    |  0    f_y   c_y    0 |
    |  0     0     1     0 |

这是一个3x4的内参矩阵,f代表焦点,c代表投影中心。
如果您解决上面的系统,您将得到:
x_screen = (x_world/z_world)*f_x + c_x
y_screen = (y_world/z_world)*f_y + c_y

但是,你想要相反的结果,所以你的答案是:
x_world = (x_screen - c_x) * z_world / f_x
y_world = (y_screen - c_y) * z_world / f_y

z_world是Kinect返回给您的深度信息,您可以从内参校准中得到f和c,因此对于每个像素,您可以应用上述公式来获取实际的世界坐标。

编辑1(为什么上述内容对应世界坐标以及我们在校准过程中得到的外参是什么):

首先,请查看这篇文章,它很好地解释了各种坐标系统。

您的三维坐标系为:物体 ---> 世界 ---> 相机。有一个转换将您从物体坐标系转换到世界坐标系,另一个转换将您从世界坐标系转换到相机坐标系(您所说的外参)。通常情况下,您假设:

  • 要么物体坐标系对应于世界坐标系,
  • 要么相机坐标系对应于世界坐标系

1. 在使用Kinect捕捉物体时

当你使用Kinect来捕捉一个物体时,传感器返回给你的是距离相机的距离。这意味着z坐标已经在相机坐标系中了。通过使用上面的方程式转换x和y,你可以得到相机坐标系中的点。
现在,世界坐标系是由你定义的。一种常见的方法是假设相机位于世界坐标系的(0,0,0)处。因此,在这种情况下,外参矩阵实际上对应于单位矩阵,而你找到的相机坐标对应于世界坐标系
旁注:由于Kinect返回的z是相机坐标系中的,所以也不需要从对象坐标系到世界坐标系的转换。例如,假设你有一个捕捉人脸的不同相机,对于每个点它返回距离鼻子的距离(你认为鼻子是对象坐标系的中心)。在这种情况下,由于返回的值将在对象坐标系中,我们确实需要旋转和平移矩阵将它们带到相机坐标系中。
2. 在校准相机时
我猜您正在使用OpenCV使用一个带有不同姿态的标定板对相机进行校准。通常的方式是假设板子实际上是稳定的,而相机是在移动而不是相反(变换在两种情况下都是相同的)。这意味着现在世界坐标系对应于对象坐标系。这样,对于每一帧,我们找到棋盘格角落并分配它们的3D坐标,做类似这样的事情:
std::vector<cv::Point3f> objectCorners;

for (int i=0; i<noOfCornersInHeight; i++) 
{
    for (int j=0; j<noOfCornersInWidth; j++) 
    {
        objectCorners.push_back(cv::Point3f(float(i*squareSize),float(j*squareSize), 0.0f));
    }
} 

其中noOfCornersInWidthnoOfCornersInHeightsquareSize取决于您的标定板。例如,如果noOfCornersInWidth = 4,noOfCornersInHeight = 3和squareSize = 100,则我们可以得到三维点

(0  ,0,0)  (0  ,100,0)  (0  ,200,0)    (0  ,300,0)
(100,0,0)  (100,100,0)  (100,200,0)    (100,300,0)
(200,0,0)  (200,100,0)  (200,200,0)    (200,300,0)

在这里,我们的坐标实际上是在对象坐标系中。 (我们任意假设棋盘的左上角为(0,0,0),其余角落的坐标根据该坐标确定)。 因此,我们确实需要旋转和变换矩阵将我们从对象(世界)转换到相机系统。 这些是OpenCV为每个帧返回的外部参数。 总之,在Kinect案例中:
  • 相机和世界坐标系被视为相同,因此不需要外部参数。
  • 不需要对象到世界(相机)转换,因为Kinect返回值已经在相机系统中。

编辑2(使用的坐标系):

这是一种惯例,我认为它也取决于你使用哪些驱动程序以及获取的数据类型。例如,检查那个, 那个那个
附注:如果您可视化点云并稍微操作一下,将会对您非常有帮助。您可以将点保存在三维对象格式(例如plyobj)中,然后只需将其导入到像Meshlab这样的程序中(非常易于使用)。

非常感谢。 现在,我可以使用以下外参数,在世界坐标系中传递CAM的坐标吗? - Paul
你在校准时找到了那些外参吧?当你使用Kinect进行捕捉时,是否将相同的标定板放置在同一位置进行捕捉? - Sassa
当我使用Kinect进行捕捉时,我使用相同的板子但不同的位置,然后我获得与使用的图像数量相同的行(每行有6个值)。例如,我已经为棋盘的单个安装获得了这些外部参数(3个旋转和3个平移):1.7261576010447846e-01 3.1158880577193560e-01 1.2720406228471280e-02 -1.1592911113815259e+02 -2.2406582979927950e+02 8.1420941356557194e+02。 - Paul
我想问的是,这些是在校准期间还是在捕捉过程中(如果位置不同)的外部参数?抱歉,我没有从您上面的评论中理解到这一点。 - Sassa
你非常友善和准确。谢谢你。另外,按照惯例,CAM的轴向是否像你链接中所示的那样? - Paul
显示剩余10条评论

0

编辑2(关于使用的坐标系):

这是一种约定俗成的方式,我认为它还取决于您使用的驱动程序和获取的数据类型。例如,请查看那个、那个和那个。

例如,如果您使用微软SDK:那么Z不是到相机的距离,而是到相机的“平面”距离。这可能会改变适当的公式。


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