让我先给您提供一些背景信息。请考虑以下图片(来自https://docs.opencv.org/2.4/modules/calib3d/doc/camera_calibration_and_3d_reconstruction.html):
![enter image description here](https://istack.dev59.com/wkixM.webp)
相机已经“附加”了一个刚性参考框架(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)平面,并选择一个原点。
在这个参考系中设置一些具有已知坐标的点,例如地板上的方形网格中的点。
拍照并获取相应的2D图像坐标。
使用 solvePnP 获取旋转和平移,使用以下参数:
- objectPoints:世界参考系中的3D点。
- imagePoints:与objectPoints顺序相同的图像中对应的2D点。
- cameraMatris:您已经拥有的内部矩阵。
- distCoeffs:您已经拥有的畸变系数。
- rvec,tvec:这些将是输出。
- useExtrinsicGuess:false
- flags:您可以使用CV_ITERATIVE
最后,使用Rodrigues函数从rvec获取R。
您需要至少三个不共线的点和相应的3D-2D坐标才能使用solvePnP(
link),但是更多点会更好。为了获得高质量的点,您可以打印一个大棋盘图案,将其平放在地板上,并将其用作网格。重要的是图案在图像中不要太小(越大,您的校准就越稳定)。
而且,非常重要的是:对于内部校准,您使用了具有特定大小正方形的棋盘图案,但是您告诉算法(对每个模式都进行了某种形式的solvePnPs),每个正方形的大小为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。