1.理论基础
这篇【文章】有详细讲解
2.标定板制作
推荐一个【网址】,可以直接制作多种标定板子,如棋盘格、圆形等等,覆盖了几种常用的标定板
推荐直接投到屏幕上拍照,比我打印出来的效果要好许多。拍照时候注意要把手机摄像头广角畸变矫正给关了。
3. 关于角点世界坐标系下坐标
有的直接不带标定板每个方格尺寸,有的程序方格尺寸是毫米,有的是米。实际上这个不会影响内参矩阵和畸变系数,只会影响平移向量。这个加不加距离都不影响,因为单目相机具有尺度不确定性,所以估算出来的相机位姿也不是真正的相机在世界坐标系下的位姿。如何估算单目相机的位姿,可以详细阅读2D-2D对极几何问题,在那里明确说到要想相对准确的估计位姿需要提前初始化。
// 将世界坐标系建在标定板上,所有点的Z坐标全部为0
for (int i{ 0 }; i < corner_size.height; i++)
{
for (int j{ 0 }; j < corner_size.width; j++)
{
cv::Point3f realpoint;
realpoint.x = j * square_size.width;
realpoint.y = i * square_size.height;
realpoint.z = 0;
world.push_back(realpoint);
}
}
4.找到角点并亚像素精准化
推荐使用findChessboardCornersSB 函数比findChessboardCorners性能要好,更接近MATLAB,好像要OpenCV4.1及以上才有
5.相机标定并计算重投影误差
上述工作一个函数就可以搞定,每个图像的重投影误差都可以返回,并且总体的也能返回,不需要自己单独算
total_err=cv::calibrateCamera(objpoints, imgpoints, gray.size(), cameraMatrix, distCoeffs, rvecsMat, tvecsMat, stdDeviationsIntrinsics, stdDeviationsExtrinsics, perViewErrors, cv::CALIB_FIX_K3)
total_err是总体的,perViewErrors是每一个图像的,具体见【官网】,多看看官网上的新版本函数介绍还是很有用的,还有另外一个函数也可以实现标定calibrateCameraRO,参数更多了,但实际没仔细了解过,不知道是不是性能有提升还是什么。
6.和MATLAB标定结果有偏差
OpenCV中的内参数矩阵和MATLAB中的互为转置矩阵,这个一般相差不大。畸变系数这个相差的就比较大了,按照我的理解,这是一个非线性优化问题,算法找到可能为局部最优解。
当设置为两个径向畸变系数的时候,这个一般就一致了,不过切向畸变系数好像也有比较大的偏差,不过还是以实际的矫正效果为准。
所以说不必要拘泥于MATLAB和OpenCV的畸变系数一致,最终还是要看实际的矫正效果的,有时候MATLAB矫正的也不是很好啊,就没必要让OPenCV畸变系数和MATLAB靠近。
7.标定后矫正结果不行
这个问题折磨了我挺久,我一开始以为calibrateCamera这个函数会自动找到三个畸变系数最优解,但是实际情况不是,使用几个畸变系数害的自己设置,当矫正效果不好的时候,用3个径向畸变系数一般分为这几种情况:
- 图片边缘扭曲,矫正太过,使用cv::CALIB_FIX_K3将k3设置为0,不行设置k2和k3都为0
- 图片矫正前后没有变化,也试一下降低畸变模型复杂度,即设置k3或k2为0
我就是设置为k2和k3同时为0的时候才又比较好的矫正效果,边缘啥的都能够矫正回来
所以矫正的时候可以多尝试几下,把高阶项直接设置为0,如k3,效果可能比较好。或者k2、k3都设置为0,只保留k1。
其他的组合也可以,感觉有点玄学了,不断试试就得到比较好的效果。可以在MATLAB中设置2参数和3参数试下,看看哪个效果好,然后再OpenCV中也设置相应的畸变系数个数。重投影误差这个评判标准只能作为参考,最终还是要看实际的矫正效果的,矫正效果不好,重投影误差就图一乐。