【OpenCV3学习笔记 】相机标定函数 calibrateCamera( ) 使用详解(附相机标定程序和数据)

函数作用:

根据校准模式的几个视图(也就是相机拍的几张不同的图片),求解摄像机的内在参数和外在参数。
在每个视图中,必须指定三维物体点及其相应的二维投影的坐标。这可以通过使用已知几何形状和易于检测特征点的对象来实现。这样的对象称为标定或校准模式,而且OpenCV有对棋盘标定的内置支持(见findchessboardcorners)。目前,固有参数的初始化(当cv_calib_use_intrinsic_guess未设置)只实现平面校准模式(对象点的z坐标,必须全部为零)。
只要提供初始相机内参矩阵cameramatrix,也可以用于三维标定装置。

该算法执行以下步骤:
计算初始的内在参数(只能用于平面校准模式的选项)或从输入参数中读取它们。畸变系数都为零开始,除非有cv_calib_fix_k指定。
估计初始相机的姿态,就像内在参数已经已知一样。这是通过使用solvepnp。
运行的全局Levenberg Marquardt优化算法来最小化投影误差,投影误差等于所提取到的图像上的特征点坐标和三维点根据使用相机的参数和姿态计算得到的图像上的投影坐标的距离的综合。

函数原型:(opencv3帮助文档

double cv::calibrateCamera  (   
InputArrayOfArrays  objectPoints,
InputArrayOfArrays  imagePoints,
Size    imageSize,
InputOutputArray    cameraMatrix,
InputOutputArray    distCoeffs,
OutputArrayOfArrays     rvecs,
OutputArrayOfArrays     tvecs,
OutputArray     stdDeviationsIntrinsics,
OutputArray     stdDeviationsExtrinsics,
OutputArray     perViewErrors,
int             flags = 0,
TermCriteria    criteria = TermCriteria(TermCriteria::COUNT+TermCriteria::EPS, 30, DBL_EPSILON) 
)       
double cv::calibrateCamera  (   
InputArrayOfArrays  objectPoints,
InputArrayOfArrays  imagePoints,
Size    imageSize,
InputOutputArray    cameraMatrix,
InputOutputArray    distCoeffs,
OutputArrayOfArrays     rvecs,
OutputArrayOfArrays     tvecs,
int     flags = 0,
TermCriteria    criteria = TermCriteria(TermCriteria::COUNT+TermCriteria::EPS, 30, DBL_EPSILON) 
)   

参数

objectPoints :世界坐标系中的点。在使用时,应该输入vector< vector< Point3f > >。

imagePoints :其对应的图像点。和objectPoints一样,应该输入vector< vector< Point2f > >型的变量。

imageSize :图像的大小,在计算相机的内参数和畸变矩阵需要用到;

cameraMatrix :内参数矩阵。输入一个Mat cameraMatrix即可。

distCoeffs :畸变矩阵。输入一个Mat distCoeffs即可。

rvecs :旋转向量;应该输入一个Mat的vector,即vector< Mat > rvecs因为每个vector< Point3f >会得到一个rvecs。

tvecs :位移向量;和rvecs一样,也应该为vector tvecs。

stdDeviationsIntrinsics :内参数的输出向量。输出顺序为: (fx,fy,cx,cy,k1,k2,p1,p2,k3,k4,k5,k6,s1,s2,s3,s4,τx,τy) ,如果不估计其中某一个参数,值等于0

stdDeviationsExtrinsics :外参数的输出向量。输出顺序: (R1,T1,…,RM,TM) ,M是标定图片的个数, Ri,Ti 是1x3的向量 。

perViewErrors 每个标定图片的重投影均方根误差的输出向量。

criteria: 迭代优化算法的终止准则

flags :标定函数是所采用的模型(重点)”
可输入如下某个或者某几个参数:

CV_CALIB_USE_INTRINSIC_GUESS:使用该参数时,将包含有效的fx,fy,cx,cy的估计值的内参矩阵cameraMatrix,作为初始值输入,然后函数对其做进一步优化。如果不使用这个参数,用图像的中心点初始化光轴点坐标(cx, cy),使用最小二乘估算出fx,fy(这种求法好像和张正友的论文不一样,不知道为何要这样处理)。注意,如果已知内部参数(内参矩阵和畸变系数),就不需要使用这个函数来估计外参,可以使用solvepnp()函数计算外参数矩阵。

CV_CALIB_FIX_PRINCIPAL_POINT:在进行优化时会固定光轴点,光轴点将保持为图像的中心点。当CV_CALIB_USE_INTRINSIC_GUESS参数被设置,保持为输入的值。

CV_CALIB_FIX_ASPECT_RATIO:固定fx/fy的比值,只将fy作为可变量,进行优化计算。当CV_CALIB_USE_INTRINSIC_GUESS没有被设置,fx和fy的实际输入值将会被忽略,只有fx/fy的比值被计算和使用。

CV_CALIB_ZERO_TANGENT_DIST:切向畸变系数(P1,P2)被设置为零并保持为零。

CV_CALIB_FIX_K1,…,CV_CALIB_FIX_K6:对应的径向畸变系数在优化中保持不变。如果设置了CV_CALIB_USE_INTRINSIC_GUESS参数,就从提供的畸变系数矩阵中得到。否则,设置为0。

CV_CALIB_RATIONAL_MODEL(理想模型):启用畸变k4,k5,k6三个畸变参数。使标定函数使用有理模型,返回8个系数。如果没有设置,则只计算其它5个畸变参数。

CALIB_THIN_PRISM_MODEL (薄棱镜畸变模型):启用畸变系数S1、S2、S3和S4。使标定函数使用薄棱柱模型并返回12个系数。如果不设置标志,则函数计算并返回只有5个失真系数。

CALIB_FIX_S1_S2_S3_S4 :优化过程中不改变薄棱镜畸变系数S1、S2、S3、S4。如果cv_calib_use_intrinsic_guess设置,使用提供的畸变系数矩阵中的值。否则,设置为0。

CALIB_TILTED_MODEL (倾斜模型):启用畸变系数tauX and tauY。标定函数使用倾斜传感器模型并返回14个系数。如果不设置标志,则函数计算并返回只有5个失真系数。

CALIB_FIX_TAUX_TAUY :在优化过程中,倾斜传感器模型的系数不被改变。如果cv_calib_use_intrinsic_guess设置,从提供的畸变系数矩阵中得到。否则,设置为0。

函数返回

重投影的总的均方根误差。

完整的程序

#include "opencv.hpp"

#include <iostream>
#include <fstream> 
#include <io.h>  
#include <string>  
#include<vector>

using namespace cv;
using namespace std;

/* 
@param File_Directory 为文件夹目录
@param FileType 为需要查找的文件类型
@param FilesName 为存放文件名的容器
*/
void getFilesName(string &File_Directory, string &FileType, vector<string>&FilesName)
{
    string buffer = File_Directory + "\\*" + FileType;

    _finddata_t c_file;   // 存放文件名的结构体

    long hFile;
    hFile = _findfirst(buffer.c_str(), &c_file);   //找第一个文件命

    if (hFile == -1L)   // 检查文件夹目录下存在需要查找的文件
        printf("No %s files in current directory!\n", FileType);
    else
    {
        string fullFilePath;
        do
        {
            fullFilePath.clear();

            //名字
            fullFilePath = File_Directory + "\\" + c_file.name;

            FilesName.push_back(fullFilePath);

        } while (_findnext(hFile, &c_file) == 0);  //如果找到下个文件的名字成功的话就返回0,否则返回-1  
        _findclose(hFile);
    }
}

void m_calibration(vector<string> &FilesName, Size board_size, Size square_size, Mat &cameraMatrix, Mat &distCoeffs, vector<Mat> &rvecsMat, vector<Mat> &tvecsMat)
{
    ofstream fout("caliberation_result.txt");                       // 保存标定结果的文件 

    cout << "开始提取角点………………" << endl;
    int image_count = 0;                                            // 图像数量 
    Size image_size;                                                // 图像的尺寸 

    vector<Point2f> image_points;                                   // 缓存每幅图像上检测到的角点
    vector<vector<Point2f>> image_points_seq;                       // 保存检测到的所有角点

    for (int i = 0;i < FilesName.size();i++)
    {
        image_count++;

        // 用于观察检验输出
        cout << "image_count = " << image_count << endl;
        Mat imageInput = imread(FilesName[i]);
        if (image_count == 1)  //读入第一张图片时获取图像宽高信息
        {
            image_size.width = imageInput.cols;
            image_size.height = imageInput.rows;
            cout << "image_size.width = " << image_size.width << endl;
            cout << "image_size.height = " << image_size.height << endl;
        }

        /* 提取角点 */
        bool ok = findChessboardCorners(imageInput, board_size, image_points, CV_CALIB_CB_ADAPTIVE_THRESH | CV_CALIB_CB_NORMALIZE_IMAGE);
        if (0 == ok)
        {
            cout <<"第"<< image_count <<"张照片提取角点失败,请删除后,重新标定!"<<endl; //找不到角点
            imshow("失败照片", imageInput);
            waitKey(0);
        }
        else
        {
            Mat view_gray;
            cout << "imageInput.channels()=" << imageInput.channels() << endl;
            cvtColor(imageInput, view_gray, CV_RGB2GRAY);

            /* 亚像素精确化 */
            //find4QuadCornerSubpix(view_gray, image_points, Size(5, 5)); //对粗提取的角点进行精确化
            cv::cornerSubPix(view_gray, image_points, cv::Size(11, 11), cv::Size(-1, -1), cv::TermCriteria(CV_TERMCRIT_ITER + CV_TERMCRIT_EPS, 20, 0.01));

            image_points_seq.push_back(image_points);  //保存亚像素角点

            /* 在图像上显示角点位置 */
            drawChessboardCorners(view_gray, board_size, image_points, true); 

            //imshow("Camera Calibration", view_gray);//显示图片
            //waitKey(100);//暂停0.1S     
        }
    }
    cout << "角点提取完成!!!" << endl;


    /*棋盘三维信息*/
    vector<vector<Point3f>> object_points_seq;                     // 保存标定板上角点的三维坐标

    for (int t = 0;t < image_count;t++)
    {
        vector<Point3f> object_points;
        for (int i = 0;i < board_size.height;i++)
        {
            for (int j = 0;j < board_size.width;j++)
            {
                Point3f realPoint;
                /* 假设标定板放在世界坐标系中z=0的平面上 */
                realPoint.x = i*square_size.width;
                realPoint.y = j*square_size.height;
                realPoint.z = 0;
                object_points.push_back(realPoint);
            }
        }
        object_points_seq.push_back(object_points);
    }

    /* 运行标定函数 */
    double err_first = calibrateCamera(object_points_seq, image_points_seq, image_size, cameraMatrix, distCoeffs, rvecsMat, tvecsMat, CV_CALIB_FIX_K3);
    fout << "重投影误差1:" << err_first << "像素" << endl << endl;
    cout << "标定完成!!!" << endl;


    cout << "开始评价标定结果………………";
    double total_err = 0.0;            // 所有图像的平均误差的总和 
    double err = 0.0;                  // 每幅图像的平均误差
    double totalErr = 0.0;
    double totalPoints = 0.0;
    vector<Point2f> image_points_pro;     // 保存重新计算得到的投影点

    for (int i = 0;i < image_count;i++)
    {

        projectPoints(object_points_seq[i], rvecsMat[i], tvecsMat[i], cameraMatrix, distCoeffs, image_points_pro);   //通过得到的摄像机内外参数,对角点的空间三维坐标进行重新投影计算

        err = norm(Mat(image_points_seq[i]), Mat(image_points_pro), NORM_L2);

        totalErr += err*err;
        totalPoints += object_points_seq[i].size();

        err /= object_points_seq[i].size();
        //fout << "第" << i + 1 << "幅图像的平均误差:" << err << "像素" << endl;
        total_err += err;
    }
    fout << "重投影误差2:" << sqrt(totalErr / totalPoints) << "像素" << endl << endl;
    fout << "重投影误差3:" << total_err / image_count << "像素" << endl << endl;


    //保存定标结果    
    cout << "开始保存定标结果………………" << endl;
    Mat rotation_matrix = Mat(3, 3, CV_32FC1, Scalar::all(0)); /* 保存每幅图像的旋转矩阵 */
    fout << "相机内参数矩阵:" << endl;
    fout << cameraMatrix << endl << endl;
    fout << "畸变系数:\n";
    fout << distCoeffs << endl << endl << endl;
    for (int i = 0; i < image_count; i++)
    {
        fout << "第" << i + 1 << "幅图像的旋转向量:" << endl;
        fout << rvecsMat[i] << endl;

        /* 将旋转向量转换为相对应的旋转矩阵 */
        Rodrigues(rvecsMat[i], rotation_matrix);
        fout << "第" << i + 1 << "幅图像的旋转矩阵:" << endl;
        fout << rotation_matrix << endl;
        fout << "第" << i + 1 << "幅图像的平移向量:" << endl;
        fout << tvecsMat[i] << endl << endl;
    }
    cout << "定标结果完成保存!!!" << endl;
    fout << endl;
}

void m_undistort(vector<string> &FilesName, Size image_size, Mat &cameraMatrix, Mat &distCoeffs)
{

    Mat mapx = Mat(image_size, CV_32FC1);   //X 坐标重映射参数
    Mat mapy = Mat(image_size, CV_32FC1);   //Y 坐标重映射参数
    Mat R = Mat::eye(3, 3, CV_32F);
    cout << "保存矫正图像" << endl;
    string imageFileName;                  //校正后图像的保存路径
    stringstream StrStm;
    string temp;

    for (int i = 0; i < FilesName.size(); i++)
    {
        Mat imageSource = imread(FilesName[i]);

        Mat newimage = imageSource.clone();

        //方法一:使用initUndistortRectifyMap和remap两个函数配合实现
        //initUndistortRectifyMap(cameraMatrix,distCoeffs,R, Mat(),image_size,CV_32FC1,mapx,mapy);
        //  remap(imageSource,newimage,mapx, mapy, INTER_LINEAR);

        //方法二:不需要转换矩阵的方式,使用undistort函数实现
        undistort(imageSource, newimage, cameraMatrix, distCoeffs);

        StrStm << i + 1;
        StrStm >> temp;
        imageFileName = "矫正后图像//" + temp + "_d.jpg";
        imwrite(imageFileName, newimage);

        StrStm.clear();
        imageFileName.clear();
    }
    std::cout << "保存结束" << endl;
}

void main()
{
    string File_Directory1 = "E:\\标定实验\\相机6\\多帧\\03";   //文件夹目录1

    string FileType = ".jpg";    // 需要查找的文件类型

    vector<string>FilesName1;    //存放文件名的容器

    getFilesName(File_Directory1, FileType, FilesName1);   // 标定所用图像文件的路径


    Size board_size = Size(11, 8);                         // 标定板上每行、列的角点数 
    Size square_size = Size(30, 30);                       // 实际测量得到的标定板上每个棋盘格的物理尺寸,单位mm

    Mat cameraMatrix = Mat(3, 3, CV_32FC1, Scalar::all(0));        // 摄像机内参数矩阵
    Mat distCoeffs = Mat(1, 5, CV_32FC1, Scalar::all(0));          // 摄像机的5个畸变系数:k1,k2,p1,p2,k3
    vector<Mat> rvecsMat;                                          // 存放所有图像的旋转向量,每一副图像的旋转向量为一个mat
    vector<Mat> tvecsMat;                                          // 存放所有图像的平移向量,每一副图像的平移向量为一个mat

    m_calibration(FilesName1, board_size, square_size, cameraMatrix, distCoeffs, rvecsMat, tvecsMat);

    //m_undistort(FilesName1, image_size, cameraMatrix, distCoeffs);

    return;
}

工程和数据我打包好了,我使用的是VS2015,64位的opencv3.2,下载地址:
OpenCV张正友相机标定程序,附实验数据

  • 47
    点赞
  • 392
    收藏
    觉得还不错? 一键收藏
  • 60
    评论
### 回答1: calibratecamera 函数的flags 可以通过设置 CALIB_USE_INTRINSIC_GUESS 或者 CALIB_FIX_ASPECT_RATIO 来实现。具体方法是:将CALIB_USE_INTRINSIC_GUESS设置为True,CALIB_FIX_ASPECT_RATIO设置为False,然后它会自动寻找针孔模型的参数。 ### 回答2: 标定OpenCV相机的针孔模型需要设置calibratecamera函数的flags参数。 calibrateCamera函数OpenCV中用于相机标定函数,它可以根据一组已知的三维物体点和对应的图像点来计算相机的内参矩阵和畸变系数。 flags参数是一个整数,用于指定标定过程中的一些选项。根据不同的需求,可以设置不同的标定标志位来调整标定的精确度和速度。 常用的标定标志位包括: 1. CALIB_USE_INTRINSIC_GUESS:使用初始猜测的内参矩阵和畸变系数进行标定。这样可以加快标定的速度,但精度可能会降低。 2. CALIB_FIX_INTRINSIC:固定内参矩阵和畸变系数。如果已经有了一个较好的内参矩阵和畸变系数,可以使用这个标志位来固定它们,仅求解外参矩阵。 3. CALIB_FIX_PRINCIPAL_POINT:固定主点坐标。如果已经知道主点的大致位置,可以使用这个标志位来固定主点坐标,仅求解其他参数。 4. CALIB_FIX_FOCAL_LENGTH:固定焦距。如果已经知道焦距的大致值,可以使用这个标志位来固定焦距,仅求解其他参数。 5. CALIB_FIX_ASPECT_RATIO:固定纵横比。如果已经知道相机的纵横比,可以使用这个标志位来固定纵横比,仅求解其他参数。 6. CALIB_ZERO_TANGENT_DIST:固定切向畸变系数。如果认为切向畸变系数为0,可以使用这个标志位来固定切向畸变系数,仅求解其他参数。 通过设置不同的标志位,可以根据实际需求来调整相机标定的过程,以达到较好的标定效果。 ### 回答3: 标定OpenCV相机的针孔模型需要使用`calibrateCamera`函数,并且可以通过设置`flags`参数来进行不同的标定方式选择。`flags`参数是一个整数,用于设定标定的方法和其他细节。 常见的flags参数设置方法有以下几种: 1. `cv2.CALIB_USE_INTRINSIC_GUESS`:使用已知的初始相机矩阵进行标定,加速标定过程; 2. `cv2.CALIB_FIX_PRINCIPAL_POINT`:固定主点坐标,不进行优化; 3. `cv2.CALIB_FIX_ASPECT_RATIO`:固定像素宽高比,不进行优化; 4. `cv2.CALIB_FIX_FOCAL_LENGTH`:固定焦距,不进行优化; 5. `cv2.CALIB_ZERO_TANGENT_DIST`:切向畸变系数初始化为0; 6. `cv2.CALIB_FIX_K1`:固定径向畸变系数k1,不进行优化; 7. `cv2.CALIB_FIX_K2`:固定径向畸变系数k2,不进行优化; 8. `cv2.CALIB_FIX_K3`:固定径向畸变系数k3,不进行优化; 9. `cv2.CALIB_FIX_K4`:固定径向畸变系数k4,不进行优化; 10. `cv2.CALIB_FIX_K5`:固定径向畸变系数k5,不进行优化; 11. `cv2.CALIB_FIX_K6`:固定径向畸变系数k6,不进行优化; 12. `cv2.CALIB_RATIONAL_MODEL`:使用鱼眼模型进行标定。 除了以上常见的flags参数设置方法,还有一些其他可选的标定方式可以根据实际需求进行设置。在调用`calibrateCamera`函数时,将`flags`参数设置为所需的数值即可选择相应的标定方法。这些标定方法的选择将直接影响标定结果的准确性和效果。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 60
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值