如何校准相机的焦距,平移和旋转给出四点?

Tho*_*mas 7 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),
};
Run Code Online (Sandbox Code Playgroud)

实际上,我认为我试图解决的等式是(参见OpenCV文档中等价物):

  / xi \   / fx    0 \ /        tx \ / Xi \
s | yi | = |    fy 0 | |  Rxyz  ty | | Yi |
  \ 1  /   \       1 / \        tz / | Zi |
                                     \ 1  /
Run Code Online (Sandbox Code Playgroud)

哪里:

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

所以,我有8个等式(2 4个点坐标的每个),我有8个未知数(fx, fy,Rxyz,tx, 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);
    
    Run Code Online (Sandbox Code Playgroud)

    它返回一个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
    
    Run Code Online (Sandbox Code Playgroud)

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

BCo*_*nic 9

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

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

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

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

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

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

您可以尝试以下几种解决方法:

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

  • 不知何故手动猜测你的8个参数(2个焦距,3个角度和3个平移)的初始估计,并使用迭代求解器直接精炼它们.请注意,如果您的初始估算太远,迭代过程可能会收敛到错误的解决方案.

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

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


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

在你的情况下,你想要反过来,即从单应性中提取内在和外在参数.在OpenCV(cv :: decomposeHomographyMat)中有一个相关的功能,但它假设K矩阵是已知的并且它输出4个候选解.

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