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

Pau*_*aul 8 opencv calibration camera-calibration kinect

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

我是怎么做到的

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

任何帮助深表感谢.谢谢!

Sas*_*ssa 26

首先要了解你如何计算它,如果你读了一些关于针孔相机模型和简单透视投影的东西,它会对你有所帮助.如需快速了解,请查看此内容.我会尝试更新更新.

因此,让我们从描述相机如何工作的相反方向开始:将世界坐标系中的3d点投影到图像中的2d点.根据相机型号:

P_screen = I*P_world

或(使用齐次坐标)

| x_screen | = I * | x_world |
| y_screen |       | y_world |
|    1     |       | z_world |
                   |    1    |
Run Code Online (Sandbox Code Playgroud)

哪里

I = | f_x    0    c_x    0 | 
    |  0    f_y   c_y    0 |
    |  0     0     1     0 |
Run Code Online (Sandbox Code Playgroud)

是3x4内在函数矩阵,f是焦点,c是投影中心.

如果你解决了上面的系统,你得到:

x_screen = (x_world/z_world)*f_x + c_x
y_screen = (y_world/z_world)*f_y + c_y
Run Code Online (Sandbox Code Playgroud)

但是,你想要反过来,所以你的答案是:

x_world = (x_screen - c_x) * z_world / f_x
y_world = (y_screen - c_y) * z_world / f_y
Run Code Online (Sandbox Code Playgroud)

z_world是Kinect返回给你的深度,你知道你的内在校准中的f和c,所以对于每个像素,你应用上面的方法来获得实际的世界坐标.

编辑1(为什么以上对应于世界坐标以及我们在校准期间获得的外部因素):

首先,检查一下,它很好地解释了各种坐标系.

你的三维坐标系是:对象--->世界--->相机.有一个转换可以将你从物体坐标系带到世界,另一个带你从世界到相机(你所指的外在物).通常你认为:

  • 对象系统与World系统对应,
  • 或者,Camera系统与World系统对应

1.使用Kinect捕捉物体时

当您使用Kinect捕捉物体时,传感器返回的是与相机的距离.这意味着z坐标已经在相机坐标中.通过使用上面的等式转换x和y,您可以获得相机坐标中的点.

现在,世界坐标系由您定义.一种常见的方法是假设摄像机位于世界坐标系的(0,0,0)处.因此,在这种情况下,extrinsics矩阵实际上对应于单位矩阵,您找到的摄像机坐标对应于世界坐标.

旁注:因为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));
    }
} 
Run Code Online (Sandbox Code Playgroud)

在哪里noOfCornersInWidth,noOfCornersInHeightsquareSize取决于您的校准板.如果例如noOfCornersInWidth = 4,noOfCornersInHeight = 3和squareSize = 100,我们得到3d点

(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)
Run Code Online (Sandbox Code Playgroud)

所以,这里我们的坐标实际上在对象坐标系中.(我们任意假设棋盘的左上角是(0,0,0),其余角落的坐标是根据那个).所以在这里我们确实需要旋转和变换矩阵来将我们从对象(世界)带到相机系统.这些是OpenCV为每个帧返回的外在函数.

总结一下Kinect案例:

  • Camera和World coodinate系统被认为是相同的,所以不需要那里的extrinsics.
  • 不需要对象世界(相机)转换,因为Kinect返回值已经在相机系统中.

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

这是一个惯例,我认为它还取决于您使用的驱动程序和您获得的数据类型.检查那个,那个那个.

旁注:如果你想象一个点云并用它做一点点,它会对你有很大的帮助.您可以将点保存为三维对象格式(例如plyobj),然后将其导入到Meshlab程序中(非常易于使用).