张正友标定相机内参是非常经典的标定算法,现在代码已经被集成到MATLAB和opencv里面。不过因为算法涉及到基础的相机坐标系、图像坐标系、公式推导,以及优化算法,故根据张正友论文进行分模块代码编写。
https://github.com/Shelfcol/Zhangzhengyou_calib_cam_intrinsic
此C++代码是根据张正友的步骤进行分模块编写的,自认为逻辑还是比较清晰。分别为:
求H、求K、求旋转平移、求畸变稀疏、Ceres优化
bool CamIntrCalib::Calibrate()
{
std::cout << "Calibrate with Zhangzhengyou" << std::endl;
if (ReadPics() && GetKeyPoints())
{
CalcH();
// CalcHWithCV();
// ValidateH();
CalcK();
CalcT();
CalcDistCoeff();
std::cout << "reproject error before ceres optimizing:" << std::endl;
CalcRepjErr();
Optimize();
std::cout << "reproject error after ceres optimizing:" << std::endl;
CalcRepjErr();
return true;
}
return false;
}
代码里面也对比实现了调用opencv算法进行标定,主要的函数就是
cv::calibrateCamera(corner_3d_vec, points_2d_vec_, cv::Size(points_per_col_, points_per_row_),
K, dist_coef, rvecs, tvecs, CV_CALIB_FIX_K3 | CV_CALIB_ZERO_TANGENT_DIST);
最终跟opencv算法的结果对比下,内参和冲投影误差的差别都在0.01以内,精度较高
参数 |
zhang(我们) |
opencv |
相机内参 |
fx=589.9421322083375 fy=589.0314402379594 u0=323.830691209651 v0=240.716451101392 |
fx=589.9534727708858 fy=589.043571094065 u0=323.8345405252027 v0=240.7136136327917 |
畸变系数 |
k1=0.08908862963423921 k2=-0.0927595079832754 |
k1=0.08907964252174579 k2=-0.09266222570771097 |
重投影误差 |
0.275026 |
0.274992 |
在编写代码的时候也遇到了一个跟平常算法不一样的结构体,卡了很久,就是cv::Size(col,row)是列在前。
具体算法公式的详细推导都写在论文后面了,代码里面也是对应着写的,所以大家可以直接去看github地址里面的论文和代码进行学习。
有人问我opencv都有现成的库,为啥还要自己实现,我认为:
1)对论文的理解会在你真正编写和调试成功之后有质的飞跃,虽然你原本看似成功推导了公式,但是代码实现的时候你也会遇到很多问题,而一个个问题的解决会加深你的理解;
2)对opencv、ceres等库的使用也会更加熟悉;
3)对C++代码能力提升也是很有帮助的;
4)参加工作之后,我才深刻意识到,现实的工作需求是千奇百怪的,绝大多数优秀的开源代码并不是直接照搬就可以解决老板分配给你的任务,里面包含的思想才是真正对你有帮助的。而想对一个算法举一反三,就必须要对其具体思想和实现有深刻理解,而在时间有空的情况下,自己实现一遍理解是更加深刻的;如果没有那么多时间的话,对开源代码进行改进,或者给开源代码加一些其他开源代码的模块,比如给ORBSLAM加动态物去除,G2O改为Ceres之类的,你可以说有的代码已经实现了,但是我觉得你自己再去自己加一下,调试一下,你对整个代码的理解肯定会不一样的。
纸上得来终觉浅,绝知此事要躬行,加油吧,少年。
最后说一句,大家要是觉得对自己有帮助的话,还望不吝star哦。
论文: "A Flexible New Technique for Camera Calibration" (2000).
代码参考:
https://github.com/zhiyuanyou/Calibration-ZhangZhengyou-Method
https://github.com/SHU-FLYMAN/ZhangZhengYou