张正友标定论文的解读和C++代码编写

文章介绍了基于张正友方法的相机内参标定算法,提供了C++代码实现,并与opencv内置的calibrateCamera函数进行了对比,结果显示内参和重投影误差的差异极小。作者强调自己实现算法能深化对论文理解,提高对opencv和Ceres库的运用,并有助于提升编程技能,对于应对多样化的工作需求至关重要。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1.概述

张正友标定相机内参是非常经典的标定算法,现在代码已经被集成到MATLAB和opencv里面。不过因为算法涉及到基础的相机坐标系、图像坐标系、公式推导,以及优化算法,故根据张正友论文进行分模块代码编写。

2.代码地址

https://github.com/Shelfcol/Zhangzhengyou_calib_cam_intrinsic

3.简单解析

此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地址里面的论文和代码进行学习。

4.一点学习感受

有人问我opencv都有现成的库,为啥还要自己实现,我认为:

1)对论文的理解会在你真正编写和调试成功之后有质的飞跃,虽然你原本看似成功推导了公式,但是代码实现的时候你也会遇到很多问题,而一个个问题的解决会加深你的理解;

2)对opencv、ceres等库的使用也会更加熟悉;

3)对C++代码能力提升也是很有帮助的;

4)参加工作之后,我才深刻意识到,现实的工作需求是千奇百怪的,绝大多数优秀的开源代码并不是直接照搬就可以解决老板分配给你的任务,里面包含的思想才是真正对你有帮助的。而想对一个算法举一反三,就必须要对其具体思想和实现有深刻理解,而在时间有空的情况下,自己实现一遍理解是更加深刻的;如果没有那么多时间的话,对开源代码进行改进,或者给开源代码加一些其他开源代码的模块,比如给ORBSLAM加动态物去除,G2O改为Ceres之类的,你可以说有的代码已经实现了,但是我觉得你自己再去自己加一下,调试一下,你对整个代码的理解肯定会不一样的。

纸上得来终觉浅,绝知此事要躬行,加油吧,少年。

最后说一句,大家要是觉得对自己有帮助的话,还望不吝star哦。😃😃😃

5.Reference

论文: "A Flexible New Technique for Camera Calibration" (2000).

代码参考:

https://github.com/zhiyuanyou/Calibration-ZhangZhengyou-Method

https://github.com/SHU-FLYMAN/ZhangZhengYou

### 使用 OpenCV 进行张正友相机标定的方法 #### 准备工作 为了成功执行张正友相机标定,需准备特定工具环境设置。推荐使用购买的专业标定板来获得更佳的效果平整度[^3]。 #### 加载所需库文件并初始化变量 在编写代码前,确保加载必要的OpenCV库其他辅助库: ```cpp #include <opencv2/opencv.hpp> #include <iostream> #include <vector> #include <fstream> ``` 定义存储角点坐标的向量以及其他必要参数: ```cpp std::vector<std::vector<cv::Point3f>> objectPoints; std::vector<std::vector<cv::Point2f>> imagePoints; cv::Size boardSize(9, 6); // 棋盘格内部角点数量 float squareSize = 1.0f; // 单位长度 ``` #### 获取图像中的角点位置 通过遍历一系列拍摄好的图片集获取棋盘格内角点的位置数据。对于每一幅图,尝试检测其中是否存在完整的棋盘模式,并保存找到的角点坐标至`imagePoints`列表中[^2]: ```cpp for (const auto& filename : imagesFilenames) { cv::Mat img = cv::imread(filename); std::vector<cv::Point2f> corners; bool found = cv::findChessboardCorners(img, boardSize, corners); if (!found || corners.empty()) continue; cv::cornerSubPix(img, corners, {11, 11}, {-1, -1}, {cv::TermCriteria(cv::TermCriteria::EPS + cv::TermCriteria::COUNT, 30, 0.1)}); imagePoints.push_back(corners); } ``` #### 计算内外参矩阵及畸变系数 当收集到足够多的有效样本后,调用`calibrateCamera()`函数计算摄像机内参矩阵、外参矩阵以及径向切向畸变系数[^1]: ```cpp std::vector<cv::Vec3d> rvecs, tvecs; cv::Mat cameraMatrix, distCoeffs; double rms = cv::calibrateCamera(objectPoints, imagePoints, imageSize, cameraMatrix, distCoeffs, rvecs, tvecs); ``` 上述过程完成后即可得到所需的校准参数,可用于后续处理阶段如去除镜头失真等操作。
评论 18
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值