让我介绍一些上下文。考虑下图,(来自https://docs.opencv.org/2.4/modules/calib3d/doc/camera_calibration_and_3d_reconstruction.html):
相机已“附加”了一个刚性参考系 (Xc,Yc,Zc)。您成功执行的内在校准允许您将点 (Xc,Yc,Zc) 转换为其在图像 (u,v) 上的投影,以及图像中的一个点 (u,v)到 (Xc,Yc,Zc) 中的一条射线(你只能将它提高到一个比例因子)。
在实践中,您希望将相机放置在外部“世界”参考系中,我们称之为 (X,Y,Z)。然后是一个刚性变换,由一个旋转矩阵R和一个平移向量T表示,这样:
|Xc| |X|
|Yc|= R |Y| + T
|Zc| |Z|
这就是外在校准(也可以写成4x4矩阵,也就是你所说的外在矩阵)。
现在,答案。要获取 R 和 T,您可以执行以下操作:
修复您的世界参考系,例如地面可以是 (x,y) 平面,并为其选择原点。
在此参考系中设置一些坐标已知的点,例如,地板中方格中的点。
拍照,获取对应的二维图像坐标。
-
使用solvePnP获取旋转和平移,参数如下:
- objectPoints:世界参考系中的 3D 点。
- imagePoints:图像中对应的二维点与objectPoints的顺序相同。
- cameraMatris:你已经拥有的内在矩阵。
- distCoeffs:你已经拥有的失真系数。
-
rvec、tvec:这些将是输出。
- useExtrinsicGuess: false
- 标志:您可以使用 CV_ITERATIVE
最后,使用 Rodrigues 函数从 rvec 获取 R。
您将需要至少 3 个非共线点和对应的 3D-2D 坐标,solvePnP 才能工作 (link),但越多越好。为了获得高质量的分数,您可以打印一个大棋盘图案,将其平放在地板上,并将其用作网格。重要的是图像中的图案不能太小(越大,校准越稳定)。
而且,非常重要:对于内在校准,您使用了具有一定大小方格的国际象棋图案,但您告诉算法(它对每个图案进行求解 PnP),每个方块的大小为1。这不是明确的,而是在示例代码的第 10 行完成,其中网格是使用坐标 0,1,2,...构建的:
objp[:,:2] = np.mgrid[0:7,0:6].T.reshape(-1,2)
而外在校准的世界尺度必须与此相匹配,所以你有几种可能:
使用相同的比例,例如使用相同的网格或以相同比例测量“世界”平面的坐标。在这种情况下,您的“世界”将不会处于正确的比例。
-
建议:用正确的比例重新进行内在校准,例如:
objp[:,:2] = (size_of_a_square*np.mgrid[0:7,0:6]).T.reshape(-1,2)
其中 size_of_a_square 是正方形的实际大小。
(没做过,但理论上是可以的,做不到就做 2)通过缩放 fx 和 fy 重用内在校准。这是可能的,因为相机看到的所有内容都达到了比例因子,并且声明的正方形大小只会改变 fx 和 fy(以及每个正方形姿势中的 T,但这是另一回事)。如果正方形的实际大小为L,则在调用solvePnP之前替换fx和fy Lfx和Lfy。