如何在四个点上校准相机焦距,平移和旋转?

时间:2018-06-01 09:57:07

标签: c++ opencv opencv3.1

我试图在世界空间中找到相机的焦距,位置和方向。

因为我需要将其与分辨率无关,我将我的图像坐标标准化为[-1, 1]范围xy范围稍小(取决于方面)比)。所以(0, 0)是图像的中心。我已经针对镜头失真进行了更正(使用k1k2系数),所以这不会进入图片,除非有时会略微抛出xy [-1, 1]范围。

作为给定,我在已知尺寸的世界空间(毫米)中有一个平面的固定矩形。保证矩形的四个角可见,并在图像中手动标记。例如:

std::vector<cv::Point3f> worldPoints = {
    cv::Point3f(0, 0, 0),
    cv::Point3f(2000, 0, 0),
    cv::Point3f(0, 3000, 0),
    cv::Point3f(2000, 3000, 0),
};
std::vector<cv::Point2f> imagePoints = {
    cv::Point2f(-0.958707, -0.219624),
    cv::Point2f(-1.22234, 0.577061),
    cv::Point2f(0.0837469, -0.1783),
    cv::Point2f(0.205473, 0.428184),
};

实际上,我想要解决的等式是(见equivalent in the OpenCV documentation):

  / xi \   / fx    0 \ /        tx \ / Xi \
s | yi | = |    fy 0 | |  Rxyz  ty | | Yi |
  \ 1  /   \       1 / \        tz / | Zi |
                                     \ 1  /

其中:

  • i1, 2, 3, 4
  • xi, yi是图片中i点的位置(介于-1和1之间)
  • fx, fy是相机在x和y方向的焦距
  • Rxyz是相机的3x3旋转矩阵(只有3个自由度)
  • tx, ty, tz是相机的翻译
  • Xi, Yi, Zi是世界空间中点i的位置(毫米)

所以我有8个方程(每个2个坐标有4个点),我有8个未知数(fx, fyRxyztx, ty, tz)。因此,我总结(除了病理案例)必须存在一种独特的解决方案。

但是,我似乎无法弄清楚如何使用OpenCV计算此解决方案。

我查看了imgproc模块:

  • getPerspectiveTransform有效,但只给我一个3x3矩阵(从2D点到2D点)。如果我能以某种方式从这个矩阵中提取所需的参数,那就太棒了。

我还查看了calib3d模块,其中包含一些很有希望的功能,这些功能几乎可以完成,但并不完全符合我的需求:

  • initCameraMatrix2D听起来几乎是完美的,但是当我通过它时我的四点是这样的:

    cv::Mat cameraMatrix = cv::initCameraMatrix2D(
                std::vector<std::vector<cv::Point3f>>({worldPoints}),
                std::vector<std::vector<cv::Point2f>>({imagePoints}),
                cv::Size2f(2, 2), -1);
    

    它会返回一个fx, fy设置为-inf, inf的相机矩阵。

  • calibrateCamera似乎使用复杂的求解器来处理超定系统和异常值。无论如何我试过了,但我能从中获得的是这样的断言失败:

    OpenCV(3.4.1) Error: Assertion failed (0 <= i && i < (int)vv.size()) in getMat_, file /build/opencv/src/opencv-3.4.1/modules/core/src/matrix_wrap.cpp, line 79
    

有没有办法诱使OpenCV做我需要的事情?如果没有,我怎么能手工完成呢?

1 个答案:

答案 0 :(得分:8)

3x3旋转矩阵有9个元素,但正如你所说,只有3个自由度。一个微妙之处在于利用这个属性使你想要估计的角度中的方程非线性,非线性方程比线性方程更难解决。

这种方程通常通过以下方法解决:

  1. 考虑到P = K。[R | t]矩阵具有12个自由度并使用SVD分解求解得到的线性方程(更多细节见Hartley&amp; Zisserman的“多视图几何”第7.1节)

  2. 将此中间结果分解为非线性方程的初始近似解(请参阅例如cv::decomposeProjectionMatrix

  3. 使用迭代求解器来精化近似解,该求解器能够处理非线性方程并且具有旋转矩阵的减小的自由度(例如Levenberg-Marquard算法)。我不确定OpenCV中是否存在这种通用实现,但使用Ceres Solver library自己实现它并不太复杂。

  4. 但是,您的情况有点特别,因为您没有足够的点匹配来可靠地解决线性公式(即步骤1)。这意味着,正如您所说,您无法初始化迭代精炼算法以获得准确的问题解决方案。

    以下是一些可以尝试的解决方法:

    • 以某种方式得到2个额外的点匹配,导致总共6个匹配因此对你的线性方程有12个约束,允许你使用上面的步骤1,2,3来解决问题。

    • 以某种方式手动猜测您的8个参数(2个焦距,3个角度和3个平移)的初始估计值,并使用迭代求解器直接对其进行细化。请注意,如果您的初始估算太远,迭代过程可能会收敛到错误的解决方案。

    • 减少模型中的未知数。例如,如果您设法修复三个角度中的两个(例如滚动和俯仰),则等式可能会简化很多。此外,两个焦距可能通过纵横比相关,所以如果您知道它并且您的像素是正方形,那么您实际上只有一个未知。

    • 如果所有其他方法都失败了,可能有办法从cv::getPerspectiveTransform估算的整流单应性中提取近似值。

    关于最后一个要点,显然可能与你想要的相反。实际上,可以通过分析来表达整流单应性,知道您想要估计的参数。例如,请参阅this postthis post。 Hartley&amp; amp; amp; amp; amp; amp; amp; amp; amp; amp; Zisserman书(第13章)。

    在你的情况下,你想要反过来,即提取内在的&amp;单应性的外在参数。在OpenCV中有一个相关的函数(cv::decomposeHomographyMat),但它假定K矩阵是已知的,它输出4个候选解。

    在一般情况下,这将是棘手的。但也许在你的情况下,你可以猜测焦距的合理估计,因此对于K,并使用点对应来选择你的问题的良好解决方案。您还可以实现自定义优化算法,测试许多焦距值并使解决方案保持最低的重投影错误。