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

使用OpenCV从屏幕坐标计算世界坐标,opencv,kinect,camera-calibration,calibration,Opencv,Kinect,Camera Calibration,Calibration,我用OpenCV计算了相机的内部和外部参数。 现在,我想从屏幕坐标(u,v)计算世界坐标(x,y,z) 我是怎么做到的 注意:当我使用kinect时,我已经知道z坐标 非常感谢您的帮助。谢谢 首先要了解如何计算,如果您阅读一些关于针孔相机模型和简单透视投影的内容,这将对您有所帮助。要快速浏览,请查看。我会尝试更新更多 那么,让我们从描述相机工作原理的相反方向开始:将世界坐标系中的3d点投影到图像中的2d点。根据相机型号: p_屏幕=I*p_世界 或(使用齐次坐标) 在哪里 I = | f_x

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

我是怎么做到的

注意:当我使用kinect时,我已经知道z坐标


非常感谢您的帮助。谢谢

首先要了解如何计算,如果您阅读一些关于针孔相机模型和简单透视投影的内容,这将对您有所帮助。要快速浏览,请查看。我会尝试更新更多

那么,让我们从描述相机工作原理的相反方向开始:将世界坐标系中的3d点投影到图像中的2d点。根据相机型号:

p_屏幕=I*p_世界

或(使用齐次坐标)

在哪里

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)处。因此,在这种情况下,Exterinstics矩阵实际上对应于身份矩阵,而您找到的摄影机坐标对应于世界坐标

旁注:由于Kinect返回摄影机坐标中的z,因此也不需要从对象坐标系转换到世界坐标系。例如,假设您有一个不同的相机捕捉人脸,对于每个点,它返回到鼻子的距离(您认为是对象坐标系的中心)。在这种情况下,由于返回的值将位于对象坐标系中,因此我们确实需要一个旋转和平移矩阵将它们带到摄影机坐标系中

2。校准摄像机时

我想您正在使用OpenCV使用具有各种姿势的校准板校准相机。通常的方法是假设电路板实际上是稳定的,相机是移动的,而不是相反的(两种情况下的变换是相同的)这意味着现在世界坐标系对应于对象坐标系。这样,对于每一帧,我们都会找到棋盘格角点,并将其指定为三维坐标,执行如下操作:

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));
    }
} 
因此,这里我们的坐标实际上在对象坐标系中。(我们任意假设电路板的左上角为(0,0,0),其余角的坐标与该坐标一致)。因此,这里我们确实需要旋转和变换矩阵,将我们从对象(世界)带到相机系统。这些是OpenCV为每个帧返回的外部信息

总结一下Kinect的案例:

  • 摄影机和世界坐标系被认为是相同的,因此不需要外部系统
  • 不需要对象到世界(摄影机)转换,因为Kinect返回值已经在摄影机系统中
编辑2(在使用的坐标系上):

这是一个惯例,我认为这还取决于你使用的驱动程序和你得到的数据类型。检查例如,和

旁注:如果你可视化一个点云并对其进行一些处理,这将对你有很大帮助。您可以将点保存为三维对象格式(例如或),然后将其导入到程序中,如(非常易于使用)。

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

这是一个惯例,我认为这还取决于你使用的驱动程序和你得到的数据类型。例如,检查那个,那个和那个

例如,如果使用microsoft sdk:Z,则Z不是到摄影机的距离,而是到摄影机的“平面”距离。这可能会改变相应的公式


你是说你有Xscreen,Yscreen和Zworld?你想要Xworld,Yworld,Zworld?非常感谢。现在使用以下外部参数,我可以通过坐标世界中CAM的坐标吗?校准我有以下外部参数(对于单板安装):1.7261576010447846e-01 3.1158880577193560e-01 1.2720406228471280e-02-1.1592911113815259e+02-2.2406582979927950e+02 8.1420941356557194e+02您在校准期间找到了这些外部参数,对吗?使用Kinect进行捕获时,是否在同一位置捕获同一块板?使用Kinect进行捕获时,我使用同一块板,但位置不同,然后获得的行数(6个值)与使用的图像数相同。例如
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));
    }
} 
(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)