首页 > 编程知识 正文

张正友相机标定从像坐标系到像素坐标系,张正友相机标定程序

时间:2023-05-04 19:23:53 阅读:242372 作者:937

1.关于资料说明

繁荣的月亮标定2000<<A Flexible New Technique for Camera Calibration>>:https://www.microsoft.com/en-us/research/wp-content/uploads/2016/02/tr98-71.pdf

 

LM算法1978《The levenberg-marquardt algorithm, implementation and theory》:https://www.osti.gov/servlets/purl/7256021/

opencv代码参考:https://blog.csdn.net/hongbin_xu/article/details/78988450

其中关于calibrateCamera函数可见:https://raw.githubusercontent.com/opencv/opencv/master/modules/calib3d/src/calibration.cpp

此函数会输出一个3X3内参矩阵,一个5维畸变系数,n幅图像即对应n个3维平移向量和3X3旋转矩阵。输出都是cv::Mat数据格式。输入是n幅图像的二维像素点坐标及其对应的物理坐标系下的三维坐标(注意:采用棋盘格时,Z轴的坐标是0),数据格式:

vector<vector<cv::Point2f>> corner_points_of_all_imgs;

vector<vector<cv::Point3f>> objectPoints;

工程文件和图片见mail

matlab代码参考:http://www.vision.caltech.edu/bouguetj/calib_doc/#examples

 

2.关于calibrateCamera函数的进一步探究,注: 下载OpenCV, 用cmake 和 vs编译,可以debug到源码

定位到1372行的

static double cvCalibrateCamera2Internal( const CvMat* objectPoints,
                    const CvMat* imagePoints, const CvMat* npoints,
                    CvSize imageSize, int iFixedPoint, CvMat* cameraMatrix, CvMat* distCoeffs,
                    CvMat* rvecs, CvMat* tvecs, CvMat* newObjPoints, CvMat* stdDevs,
                    CvMat* perViewErrors, int flags, CvTermCriteria termCrit )

CvMat* cameraMatrix是地址传递。

代码分为步骤:

0.检查参数和分配内存

1.初始化内参和LM解决器

2.初始化外参

3.开始优化

4.保存结果

 

3.关于LM解决器

CvLevMarq solver( nparams, 0, termCrit );//即初始化类得到实例

关于nparams

const int NINTRINSIC = CV_CALIB_NINTRINSIC;

nparams = NINTRINSIC + nimages*6;

-----------

#define     CV_CALIB_NINTRINSIC  18//#include "opencv2/core/types_c.h"

以下来自:https://blog.csdn.net/xuelangwin/article/details/81095651

class CV_EXPORTS CvLevMarq{public: CvLevMarq(); CvLevMarq( int nparams, int nerrs, CvTermCriteria criteria= cvTermCriteria(CV_TERMCRIT_EPS+CV_TERMCRIT_ITER,30,DBL_EPSILON), bool completeSymmFlag=false ); ~CvLevMarq();/** 功能:* 参数:* [in] nparams 优化变量的参数* [in] nerrs nerrs代表样本的个数(一个测量点代表两个样本x和y)。 nerrs = 0,优化时使用updateAlt函数,因为此函数不关心样本个数(JtJ外部计算,自然不需要知道J的大小) nerrs > 0,优化时使用update函数,应为内部需要使用J的大小,所以J的维度需要提前声明,其维度大小为nerrs*nparams* [in] criteria 迭代停止标准* [in] completeSymmFlag 当nerrs=0时有效。防止外部计算得到的JtJ不对称。此标记位不管是false和true都会使JtJ 变为对称。** 返回值:** 备注:* 此函数相对于updateAlt函数,计算相对简单,但自己自由发挥的空间较少。例如代权重的最耍酷的老鼠乘此函数就无法实现。**/ void init( int nparams, int nerrs, CvTermCriteria criteria= cvTermCriteria(CV_TERMCRIT_EPS+CV_TERMCRIT_ITER,30,DBL_EPSILON), bool completeSymmFlag=false ); /** 功能: * 参数:* [out] param 需要优化的参数,根据内部参数的优化状况,对当前参数进行输出,方便外部误差的计算* [in] J 目标函数的偏导数,内部计算JtJ* [in] err 内部计算JtErr 和 errNorm** 返回值:** 备注:* 此函数相对于updateAlt函数,计算相对简单,但自己自由发挥的空间较少。例如代权重的最耍酷的老鼠乘此函数就无法实现。**/ bool update( const CvMat*& param, CvMat*& J, CvMat*& err );/** 功能:* 参数:* [out] param 需要优化的参数,根据内部参数的优化状况,对当前参数进行输出,方便外部误差的计算* [in] JtJ 正规方程左侧的部分,此变量与类内部成员变量关联,在此函数后面对其赋值。可以变成J'ΩJ* [in] JtErr 正规方程右侧的部分,此变量与类内部成员变量关联,在此函数后面对其赋值。* [in] errNorm 测量误差(测量值减去计算出的测量值的模),此变量与类内部成员变量关联,在此函数后面对其赋值。** 返回值:** 备注:* 目标函数的导数,正规方程的左侧和右侧计算都是在外部完成计算的,测量误差的计算也是在外部完成的。 */bool updateAlt( const CvMat*& param, CvMat*& JtJ, CvMat*& JtErr, double*& errNorm ); // 控制优化过程中的逻辑步骤(DONE, STARTED, CALC_J, CHECK_ERR) void clear(); void step(); // 计算正规方程的解,用SVD计算 enum { DONE=0, STARTED=1, CALC_J=2, CHECK_ERR=3 }; cv::Ptr<CvMat> mask; // 标记优化过程中不优化的量。0代表不优化,1代表优化。大小跟param相同 cv::Ptr<CvMat> prevParam; // 前一步的优化参数,用途有两个:1、判断变量是否在变化,不变化停止迭代。2、在此参数上减去一个高斯方向得到下一个参数。 cv::Ptr<CvMat> param; // 所有要优化的变脸集合 cv::Ptr<CvMat> J; // 目标函数的偏导数 cv::Ptr<CvMat> err; // 测量误差向量 cv::Ptr<CvMat> JtJ; // 正规方程左侧的部分 cv::Ptr<CvMat> JtJN; // 去掉非优化变量后的正规方程左侧部分 cv::Ptr<CvMat> JtErr; // 正规方程右侧的部分 cv::Ptr<CvMat> JtJV; // 去掉非优化变量的JtErr, cv::Ptr<CvMat> JtJW; // 去掉非优化变量后待求向量 double prevErrNorm, errNorm; // 测量误差,更新前的测量误差,更新后的测量误差 int lambdaLg10; // LM 算法中的lanbda,此处的lamdaLg10 加1代表lambda变大10倍,减1缩小10倍 CvTermCriteria criteria; int state; // 优化步骤中的状态 int iters; // 记录迭代的次数 bool completeSymmFlag; // 使去掉非优化变量后的JtJN变成对称矩阵,只是在updateAlt函数是有用。 int solveMethod; // 正规方程的解法};

繁荣的月亮标定相机opencv源码简化版【单独抠出opencv源码用于个人相机标定】:https://github.com/Dhtu/question8

python版标定:https://github.com/Dhtu/-question6

4.内参的初始化

在初始化内参的过程中,会先计算单应矩阵,再根据r1和r2的正交性得出一个非齐次线性方程组(见公式张论文中3和4公式,由于这里先把有平移向量的单应矩阵转换为无平移向量的单应矩阵,故这里会只有二个参数,二个式子,对一幅图像而言),接着求解出n幅图像的单应矩阵,列出n个非齐次线性方程组,但是参数只有两个1/fx^2和1/fy^2,接着利用

cvSolve(jpdxbw, _b, &_focal_length, CV_NORMAL + CV_SVD);//Least-squares solution

最耍酷的老鼠乘算法求解出这两个参数。主点就是图像的中心点。

这一共四个参数作为LM的初始值。

5.外参的初始化

在内参的基础上,利用张论文中9公式后的公式计算得出r1,r2,r3和t,其中主要涉及矩阵求逆、矩阵相乘和向量叉乘运算。

为了进一步优化外参,这里也采用LM算法对外参进行了优化。

CvLevMarq solver(6, count * 2, cvTermCriteria(CV_TERMCRIT_EPS + CV_TERMCRIT_ITER, max_iter, FLT_EPSILON), true);

6.关于LM算法的计算过程

解决非线性最小化问题

x是一个n维向量,F是一个映射,该映射通过参数与向量做运算,最终输出一个m维向量,向量的范数最小,即求得最小时参数的值是多少。其中fi是一个多元函数,即输入一个多元变量,输出一个值,这里有m个多元函数映射,即对x做m次映射,将映射后的值的平方相加再除以2,记为代价函数的值。

其中繁荣的月亮标定算法中,第一个包括4个内参+5个畸变系数;第二个包括n幅图像对应的3维旋转向量和3维平移向量。

 

版权声明:该文观点仅代表作者本人。处理文章:请发送邮件至 三1五14八八95#扣扣.com 举报,一经查实,本站将立刻删除。