c++ - 给定四点如何标定相机焦距和平移旋转?

标签 c++ opencv opencv3.1

我正在尝试找出相机在世界空间中的焦距、位置和方向。

因为我需要它与分辨率无关,所以我将我的图像坐标标准化为 [-1, 1] 范围内的 x,并且稍微小一些y 的范围(取决于纵横比)。所以 (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 个方程(4 个点,每个点有 2 个坐标),还有 8 个未知数(fx, fy, Rxyz, tx, ty,兹)。因此,我得出结论(除了病理情况)必须存在唯一的解决方案。

但是,我似乎无法弄清楚如何使用 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 做我需要的事情?如果没有,我怎么能手工完成呢?

最佳答案

3x3 旋转矩阵有 9 个元素,但如您所说,只有 3 个自由度。一个微妙之处在于,利用此属性使方程在您要估计的角度内呈非线性,并且非线性方程比线性方程更难求解。

这类方程通常用以下方法求解:

  1. 考虑到 P=K。[R | t] 矩阵具有 12 个自由度并使用 SVD 分解求解生成的线性方程(有关详细信息,请参阅 Hartley 和 Zisserman 的“多 View 几何”的第 7.1 节)

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

  3. 使用迭代求解器改进近似解,该求解器能够处理非线性方程和旋转矩阵的自由度降低(例如 Levenberg-Marquard 算法)。我不确定在 OpenCV 中是否有这个的通用实现,但是使用 Ceres Solver library 自己实现一个并不太复杂。 .

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

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

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

    <
  • 以某种方式手动猜测 8 个参数(2 个焦距、3 个角度和 3 个平移)的初始估计值,然后使用迭代求解器直接优化它们。请注意,如果您的初始估计相差太大,迭代过程可能会收敛到错误的解决方案。

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

  • 如果所有其他方法都失败了,可能有一种方法可以从 cv::getPerspectiveTransform 估计的校正单应性中提取近似值。


关于最后一个要点,与您想要的相反显然是可能的。事实上,在知道您想要估计的参数的情况下,可以分析地表达校正单应性。参见例如 this postthis post . Hartley 和 Zisserman 的书(第 13 章)中也有一整章介绍了这一点。

在你的情况下,你想走另一条路,即从单应性中提取内在和外在参数。 OpenCV 中有一个有点相关的函数 ( cv::decomposeHomographyMat ),但它假设 K 矩阵已知,并输出 4 个候选解。

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

关于c++ - 给定四点如何标定相机焦距和平移旋转?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/50640832/

相关文章:

opencv - RQT ROS中的轨迹栏

python - OpenCV - 改变 filter2D 功能

c++ - 跨平台、基于交互式文本的界面,具有命令完成功能

c++ - Mangle dll 使用 DEF 文件导出名称

c++嵌套类,其中1个对象为私有(private)

opencv - 检测着陆平台的位置/角度以引导无人机

c++ - 通过智能指针给类成员赋值

opencv - 灰度图像的手写文本变形

python - 如何在 OpenCV 3 中使用 Python 的 PCACompute 函数?

ios - OpenCV iOS - 预期标识符(MACRO)