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

因为我需要它与分辨率无关,所以我将我的图像坐标标准化为 [-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, 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 做我需要的事情?如果没有,我怎么能手工做呢?

    最佳答案

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

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

  • 考虑到 P=K.[R | t] 矩阵具有 12 个自由度,并使用 SVD 分解求解所得线性方程(有关详细信息,请参阅 Hartley & Zisserman 的“多 View 几何”的第 7.1 节)
  • 将此中间结果分解为非线性方程的初始近似解(参见例如 cv::decomposeProjectionMatrix )
  • 使用迭代求解器细化近似解,该求解器能够处理非线性方程和旋转矩阵的降低自由度(例如 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/

    10-15 06:39