让我讲一些背景。考虑下面的图片,(来自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 and T,您可以执行以下操作:
修复你的世界参考系,例如地面可以是(x,y)平面,并为其选择一个原点。
在该参考系中设置一些已知坐标的点,例如地板上的方格中的点。
拍摄一张照片并获取对应的2D图像坐标。
-
Use solvePnP获得旋转和平移,参数如下:
- objectPoints:世界参考系中的 3D 点。
- imagePoints:图像中对应的2D点以相同的顺序作为对象点。
- cameraMatris:您已有的内在矩阵。
- distCoeffs:您已有的失真系数。
-
rvec, tvec:这些将是输出。
- useExtrinsicGuess: false
- 标志:您可以使用 CV_ITERATIVE
最后,得到R from rvec与罗德里格斯功能。
您将需要至少 3 个具有相应 3D-2D 坐标的非共线点,solvePnP 才能工作(link),但越多越好。为了获得高质量的点,您可以打印一个大的棋盘图案,将其平放在地板上,并将其用作网格。重要的是图像中的图案不能太小(越大,校准就越稳定)。
And, 很重要:对于内在校准,您使用了具有一定大小的方块的国际象棋图案,但您告诉算法(它对每个图案进行求解 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,然后替换 fx 和 fy Lfx and Lfy 在调用solvePnP 之前。