我正在尝试找出相机在世界空间中的焦距、位置和方向。
因为我需要它与分辨率无关,所以我将我的图像坐标标准化为 [-1, 1]
范围内的 x
,并且稍微小一些y
的范围(取决于纵横比)。所以 (0, 0)
是图像的中心。我已经校正了镜头畸变(使用 k1
和 k2
系数),所以这不会进入画面,除了有时会抛出 x
或y
稍微超出了 [-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 /
哪里:
i
是1, 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 个自由度。一个微妙之处在于,利用此属性使方程在您要估计的角度内呈非线性,并且非线性方程比线性方程更难求解。
这类方程通常用以下方法求解:
考虑到 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 post和 this post . Hartley 和 Zisserman 的书(第 13 章)中也有一整章介绍了这一点。
在你的情况下,你想走另一条路,即从单应性中提取内在和外在参数。 OpenCV 中有一个有点相关的函数 ( cv::decomposeHomographyMat ),但它假设 K 矩阵已知,并输出 4 个候选解。
在一般情况下,这会很棘手。但也许在你的情况下你可以猜测焦距的合理估计,因此对于 K,并使用点对应来选择解决问题的好方法。您还可以实现自定义优化算法,测试许多焦距值并保持导致最低重投影误差的解决方案。
关于c++ - 给定四点如何标定相机焦距和平移旋转?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/50640832/