从特征点中提取OpenCV相机的外部参数

12
当我有每个相机的物体图像时,如何使用OpenCV检索旋转矩阵、平移向量和一些缩放因子?对于每张图片,我都有几个特征点的图像坐标。并非所有特征点在所有图像中都可见。我想将计算出的对象的3D坐标映射到一个稍微不同的对象上,以将第二个对象的形状与第一个对象对齐。
听说可以使用cv::calibrateCamera(...)来实现,但我无法完全理解。
有没有人遇到过这种问题?

以上内容不清楚你是否知道在不同图像中观察到的点的三维世界坐标。如果是这种情况,这就是一个透视n点问题,你可以使用EPnP算法校准每个相机的参数,该算法可在此处获得:http://cvlab.epfl.ch/software/EPnP/index.php。否则,请参见我下面的答案。 - Rulle
1
拍摄对象的三维坐标未知。 - Martin Hennig
如果物体上的点的三维世界坐标是未知的,我认为cv::calibrateCamera不会起作用,因为它似乎假定物体点是已知的。 - Rulle
你可能也想看一下捆绑调整:http://en.wikipedia.org/wiki/Bundle_adjustment。这假设你有相机姿态的初始估计。问题是重建所有点和姿态。 - Rulle
这似乎正是我所需要的!感谢您持续的关注和时间! - Martin Hennig
2个回答

12

我遇到了和你同样的问题,使用OpenCV处理立体图像对,想要计算摄像机的外部参数和所有观察点的世界坐标。这个问题在这里得到了解决:

Berthold K. P. Horn. Relative orientation revisited. Berthold K. P. Horn. Massachusetts Institute of Technology人工智能实验室,545 Technology...

http://citeseer.ist.psu.edu/viewdoc/summary?doi=10.1.1.64.4700

然而,我没有找到一个合适的实现方法(也许你能找到一个)。由于时间限制,我没有时间理解论文中的所有数学知识并自己实现它,所以我想出了一个快速且简单的解决方案,并且对我有用。我将解释我是如何解决这个问题的:

假设我们有两台相机,第一台相机的外部参数为RT = Matx::eye()。现在猜测第二台相机的旋转R。对于两幅图像中都被观察到的每一对图像点,我们计算它们在世界坐标系中对应射线的方向,并将其存储在二维数组dirs中(编辑:假设已知内部相机参数)。我们可以这样做是因为我们假设已经知道每个相机的方向。现在,我们建立一个超定线性系统AC = 0,其中C是第二台相机的中心。我提供了计算A的函数:

Mat buildA(Matx<double, 3, 3> &R, Array<Vec3d, 2> dirs)
{
    CV_Assert(dirs.size(0) == 2);
    int pointCount = dirs.size(1);
    Mat A(pointCount, 3, DataType<double>::type);
    Vec3d *a = (Vec3d *)A.data;
    for (int i = 0; i < pointCount; i++)
    {
        a[i] = dirs(0, i).cross(toVec(R*dirs(1, i)));
        double length = norm(a[i]);
        if (length == 0.0)
        {
            CV_Assert(false);
        }
        else
        {
            a[i] *= (1.0/length);
        }
    }
    return A;
}
调用cv::SVD::solveZ(A)将为您提供该系统1范数的最小二乘解。这样,您就可以获得第二个相机的旋转和平移。然而,由于我只是猜测第二个相机的旋转,所以我对其旋转进行了多次猜测(使用3x1向量ω进行参数化,从中计算旋转矩阵使用cv :: Rodrigues)。然后,我通过在Levenberg-Marquardt优化器中重复求解系统AC = 0来改进此猜测,并使用数值雅可比矩阵。它对我很有用,但有点不干净,因此如果您有时间,我鼓励您实现论文中所述的内容。

编辑:

以下是Levenberg-Marquardt优化器中用于评估残差向量的例程:

void Stereo::eval(Mat &X, Mat &residues, Mat &weights)
{

        Matx<double, 3, 3> R2Ref = getRot(X); // Map the 3x1 euler angle to a rotation matrix
        Mat A = buildA(R2Ref, _dirs); // Compute the A matrix that measures the distance between ray pairs
        Vec3d c;
        Mat cMat(c, false);
        SVD::solveZ(A, cMat); // Find the optimum camera centre of the second camera at distance 1 from the first camera
        residues = A*cMat; // Compute the  output vector whose length we are minimizing
    weights.setTo(1.0);
}

顺便说一下,我在互联网上搜索了一些其他代码,可以用于计算相机之间的相对定位。虽然我还没有尝试过这些代码,但它们似乎很有用:

http://www9.in.tum.de/praktika/ppbv.WS02/doc/html/reference/cpp/toc_tools_stereo.html

http://lear.inrialpes.fr/people/triggs/src/

http://www.maths.lth.se/vision/downloads/


非常感谢您的回答。我认为我理解了您所写的和编码的内容,这将对我找到可能的解决方案有很大帮助。然而,我相信cv::calibrateCamera(...)的工作方式与您提出的方法非常相似。他们将算法描述如下: - Martin Hennig
  1. 首先,它计算初始内部参数(该选项仅适用于平面标定模式)或从输入参数中读取它们。畸变系数最初都设置为零(除非指定了一些CV_CALIB_FIX_K?)。
  2. 如果已知内部参数,则估计初始相机姿态。这是使用FindExtrinsicCameraParams2完成的。
- Martin Hennig
这是 cv::calibrateCamera 文档的链接:http://opencv.willowgarage.com/documentation/cpp/camera_calibration_and_3d_reconstruction.html - Martin Hennig
以上解决方案假设3D世界坐标未知。如果已知,cv::calibrateCamera可能会很好地工作。 - Rulle
@Jonas Östlund,能否请你用一个小例子来说明你的代码呢? - SecStone
显示剩余3条评论

2
这些摄像头是您希望在未来用作立体对?如果是这种情况,您将想要使用 cv::stereoCalibrate()函数进行校准。OpenCV包含一些示例代码,其中之一是stereo_calib.cpp,值得研究。

谢谢您的回答,但我对使用立体声配对没有兴趣。我只有一个摄像头,用于从不同的角度观察同一物体。然后,在每张图片中标记的特征点应该帮助我计算虚拟对象对应点的新位置,这个虚拟对象稍微变形后应该具有与被拍摄的真实对象完全相同的几何形状。顺便说一下,cv::calibrateCamera(...) 在计算过程中使用了 cv::stereoCalibrate() - Martin Hennig
好的,你的单个相机已经校准了吗(即你知道内部参数 - 焦距、像素偏移、主点、畸变系数)? - Chris
我可以估计它们,但真实值是未知的。 - Martin Hennig

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