用OpenCV进行相机标定(张正友标定,有代码)

1. 内参与畸变

理论部分可以参考其他博客或者视觉slam十四讲
相机标定主要是为了获得相机的内参矩阵K和畸变参数

内参矩阵K
在这里插入图片描述

畸变系数:径向畸变(k1,k2,k3), 切向畸变(p1,p2)
在这里插入图片描述径向畸变公式
在这里插入图片描述切向畸变公式
在这里插入图片描述张正友标定方法能够提供一个比较好的初始解,用于后序的最优化.

这里用棋盘格进行标定,如果能够处理圆的偏心误差问题,用圆形图案标定板可能效果更好.

至少三张图片,一般用10-20张图片为最佳,要保证相机视野内各个角度,各个位置,各个方向都有图像.尽量多角度多位置.

最好用买的标定板,效果好,平.最好是背光板,能够保证足够的亮度和均匀度.

2. 用OpenCV标定相机程序

1,提取角点
2,亚像素角点
3,可视化提取角点(非必须)
4,标定
5,误差计算(重投影误差)

#include <iostream>
#include <fstream>
#include <string>
#include <opencv2/opencv.hpp>
using namespace std;
int main(int argc, char **argv)
{
    string dir = "/home/wfq/MyProjects/cal_images/";  //标定图片所在文件夹
    ifstream fin(dir + "file_images.txt"); //读取标定图片的路径,与cpp程序在同一路径下
    if (!fin)                              //检测是否读取到文件
    {
        cerr << "没有找到文件" << endl;
        return -1;
    }
    ofstream fout(dir + "calibration_result.txt"); //输出结果保存在此文本文件下
    //依次读取每一幅图片,从中提取角点
    cout << "开始提取角点……" << endl;
    int image_nums = 0;  //图片数量
    cv::Size image_size; //图片尺寸
    int points_per_row = 10;  //每行的内点数
    int points_per_col = 7;   //每列的内点数
    cv::Size corner_size = cv::Size(points_per_row, points_per_col); //标定板每行每列角点个数,共10*7个角点
    vector<cv::Point2f> points_per_image;                            //缓存每幅图检测到的角点
    vector<vector<cv::Point2f>> points_all_images;                   //用一个二维数组保存检测到的所有角点
    string image_file_name;                                          //声明一个文件名的字符串

    while (getline(fin, image_file_name)) //逐行读取,将行读入字符串
    {
        image_nums++;
        //读入图片
        cv::Mat image_raw = cv::imread(dir + image_file_name);
        if (image_nums == 1)
        {
            // cout<<"channels = "<<image_raw.channels()<<endl;
            // cout<<image_raw.type()<<endl;  //CV_8UC3
            image_size.width = image_raw.cols;  //图像的宽对应着列数
            image_size.height = image_raw.rows; //图像的高对应着行数
            cout << "image_size.width = " << image_size.width << endl;
            cout << "image_size.height = " << image_size.height << endl;
        }
        //角点检测
        cv::Mat image_gray;                               //存储灰度图的矩阵
        cv::cvtColor(image_raw, image_gray, CV_BGR2GRAY); //将BGR图转化为灰度图
        // cout<<"image_gray.type() = "<<image_gray.type()<<endl;  //CV_8UC1
        //step1 提取角点
        bool success = cv::findChessboardCorners(image_gray, corner_size, points_per_image);
        if (!success)
        {
            cout << "can not find the corners " << endl;
            exit(1);
        }
        else
        {
            //亚像素精确化(两种方法)
            //step2 亚像素角点
            cv::find4QuadCornerSubpix(image_gray, points_per_image, cv::Size(5, 5));
            // cornerSubPix(image_gray,points_per_image,Size(5,5));
            points_all_images.push_back(points_per_image); //保存亚像素角点
            //在图中画出角点位置
            //step3 角点可视化
            cv::drawChessboardCorners(image_raw, corner_size, points_per_image, success); //将角点连线
            cv::imshow("Camera calibration", image_raw);
            cv::waitKey(0); //等待按键输入
        }
    }
    cv::destroyAllWindows();
    //输出图像数目
    int image_sum_nums = points_all_images.size();
    cout << "image_sum_nums = " << image_sum_nums << endl;

    //开始相机标定
    cv::Size block_size(21, 21);                            //每个小方格实际大小, 只会影响最后求解的平移向量t
    cv::Mat camera_K(3, 3, CV_32FC1, cv::Scalar::all(0));   //内参矩阵3*3
    cv::Mat distCoeffs(1, 5, CV_32FC1, cv::Scalar::all(0)); //畸变矩阵1*5
    vector<cv::Mat> rotationMat;                            //旋转矩阵
    vector<cv::Mat> translationMat;                         //平移矩阵
    //初始化角点三维坐标,从左到右,从上到下!!!
    vector<cv::Point3f> points3D_per_image;
    for (int i = 0; i < corner_size.height; i++)
    {
        for (int j = 0; j < corner_size.width; j++)
        {
            points3D_per_image.push_back(cv::Point3f(block_size.width * j, block_size.height * i, 0));
        }
    }
    vector<vector<cv::Point3f>> points3D_all_images(image_nums,points3D_per_image);        //保存所有图像角点的三维坐标, z=0

    int point_counts = corner_size.area(); //每张图片上角点个数
    //!标定
    /**
     * points3D_all_images: 真实三维坐标
     * points_all_images: 提取的角点
     * image_size: 图像尺寸
     * camera_K : 内参矩阵K
     * distCoeffs: 畸变参数
     * rotationMat: 每个图片的旋转向量
     * translationMat: 每个图片的平移向量
     * */
    //step4 标定
    cv::calibrateCamera(points3D_all_images, points_all_images, image_size, camera_K, distCoeffs, rotationMat, translationMat, 0);

    //step5 对标定结果进行评价
    double total_err = 0.0;               //所有图像平均误差总和
    double err = 0.0;                     //每幅图像的平均误差
    vector<cv::Point2f> points_reproject; //重投影点
    cout << "\n\t每幅图像的标定误差:\n";
    fout << "每幅图像的标定误差:\n";
    for (int i = 0; i < image_nums; i++)
    {
        vector<cv::Point3f> points3D_per_image = points3D_all_images[i];
        //通过之前标定得到的相机内外参,对三维点进行重投影
        cv::projectPoints(points3D_per_image, rotationMat[i], translationMat[i], camera_K, distCoeffs, points_reproject);
        //计算两者之间的误差
        vector<cv::Point2f> detect_points = points_all_images[i];  //提取到的图像角点
        cv::Mat detect_points_Mat = cv::Mat(1, detect_points.size(), CV_32FC2); //变为1*70的矩阵,2通道保存提取角点的像素坐标
        cv::Mat points_reproject_Mat = cv::Mat(1, points_reproject.size(), CV_32FC2);  //2通道保存投影角点的像素坐标
        for (int j = 0; j < detect_points.size(); j++)
        {
            detect_points_Mat.at<cv::Vec2f>(0, j) = cv::Vec2f(detect_points[j].x, detect_points[j].y);
            points_reproject_Mat.at<cv::Vec2f>(0, j) = cv::Vec2f(points_reproject[j].x, points_reproject[j].y);
        }
        err = cv::norm(points_reproject_Mat, detect_points_Mat, cv::NormTypes::NORM_L2);
        total_err += err /= point_counts;
        cout << "第" << i + 1 << "幅图像的平均误差为: " << err << "像素" << endl;
        fout << "第" << i + 1 << "幅图像的平均误差为: " << err << "像素" << endl;
    }
    cout << "总体平均误差为: " << total_err / image_nums << "像素" << endl;
    fout << "总体平均误差为: " << total_err / image_nums << "像素" << endl;
    cout << "评价完成!" << endl;

    //将标定结果写入txt文件
    cv::Mat rotate_Mat = cv::Mat(3, 3, CV_32FC1, cv::Scalar::all(0)); //保存旋转矩阵
    cout << "\n相机内参数矩阵:" << endl;
    cout << camera_K << endl<< endl;
    fout << "\n相机内参数矩阵:" << endl;
    fout << camera_K << endl<< endl;
    cout << "畸变系数:\n";
    cout << distCoeffs << endl<< endl<< endl;
    fout << "畸变系数:\n";
    fout << distCoeffs << endl<< endl<< endl;
    for (int i = 0; i < image_nums; i++)
    {
        cv::Rodrigues(rotationMat[i], rotate_Mat); //将旋转向量通过罗德里格斯公式转换为旋转矩阵
        fout << "第" << i + 1 << "幅图像的旋转矩阵为:" << endl;
        fout << rotate_Mat << endl;
        fout << "第" << i + 1 << "幅图像的平移向量为:" << endl;
        fout << translationMat[i] << endl
             << endl;
    }
    fout << endl;
    fout.close();

    return 0;
}

3.画棋盘标定板

//函数声明,默认每行11个block, 没列8个block, block大小为75个像素. 也就是10*7个内点
void drawChessBoard(int blocks_per_row=11, int blocks_per_col=8, int block_size = 75);

// 11  8  75
void drawChessBoard(int blocks_per_row, int blocks_per_col, int block_size)
{
    //blocks_per_row=11 //每行11个格子,也就是10个点
    //blocks_per_col=8  //每列8个格子,也就是7个点
    //block_size=75     //每个格子的像素大小
    cv::Size board_size = cv::Size(block_size * blocks_per_row, block_size * blocks_per_col);
    cv::Mat chessboard = cv::Mat(board_size, CV_8UC1);
    unsigned char color = 0;
    for (int i = 0; i < blocks_per_row; i++)
    {
        color = ~color;
        for (int j = 0; j < blocks_per_col; j++)
        {
            chessboard(cv::Rect(i * block_size, j * block_size, block_size, block_size)).setTo(color);
            color = ~color;
        }
    }
    cv::Mat chess_board = cv::Mat(board_size.height + 100, board_size.width + 100, CV_8UC1, cv::Scalar::all(256)); //上下左右留出50个像素空白
    chessboard.copyTo(chess_board.rowRange(50, 50 + board_size.height).colRange(50, 50 + board_size.width));
    cv::imshow("chess_board", chess_board);
    cv::imwrite("chess_board.png", chess_board);
    cv::waitKey(-1);
    cv::destroyAllWindows();
}

4.OpenCV拍照

#include <iostream>
#include <opencv2/opencv.hpp>
using namespace std;
int main(int argc, char **argv)
{
    cv::namedWindow("Camera",cv::WINDOW_AUTOSIZE);

    cv::VideoCapture cap;
    cap.open(0);

    if(!cap.isOpened())
    {
        cout<<"camera open failed!\n";
        return -1;
    }

    cv::Mat image;
    int id=1;
    char symbol;
    while(id<=6)
    {
        cap>>image;
        if(image.empty())
            break;
        cout<<"y or n"<<endl;
        cin>>symbol;
        if(symbol=='y')
        {
            cv::imwrite(to_string(id)+".png",image);
            cout<<"第"<<id<<"张图片"<<endl;
            id++;
        }
    }
    return 0;
}
  • 24
    点赞
  • 254
    收藏
    觉得还不错? 一键收藏
  • 20
    评论
### 回答1: 双目相机标定是用来确定相机内外参数的一种常见方法。相机内参数包括主点坐标、像素宽高比、焦距和畸变系数等,而相机外参数包括旋转矩阵和平移向量。双目相机标定代码c的主要目的是通过一系列图像来计算这些参数。 在双目相机标定代码c中,一般需要使用棋盘格或者标定板来进行标定。首先,需要用棋盘格或者标定板拍摄多张不同位置的图像。然后,通过对这些图像进行处理,可以得到每张图像上棋盘格或标定板角点的像素坐标。 接下来,需要将每张图像的角点像素坐标和实际世界坐标进行对应。实际世界坐标一般可以通过标定板的大小和格子之间的间隔得到。通过这些对应关系,可以计算相机内参数和外参数。 相机内参数通常使用标定矩阵来表示,其中包括焦距、主点坐标 和像素宽高比等信息。而畸变系数则可以用一组参数来描述相机镜头的畸变特性,比如径向和切向畸变等。 相机外参数则表示相机在世界坐标系中的位置和姿态。通过双目相机标定代码c可以计算得到旋转矩阵和平移向量,用来描述相机坐标系相对于世界坐标系的变换关系。 通过双目相机标定可以得到相机内外参数,从而在后续的双目视觉应用中进行立体匹配和三维重建等任务。双目相机标定代码c提供了一种自动计算相机参数的方法,减少了手动操作的复杂性和错误。 总之,双目相机标定代码c可以帮助我们准确计算相机的内外参数,以便在后续的双目视觉应用中有效地进行图像处理和计算。 ### 回答2: 双目相机标定是一种常用的技术,旨在通过对双目相机的参数进行精确测量和校正,来获取两个相机之间的相对位置和方向关系,实现双目视觉的准确测量和三维重建等应用。 双目相机标定代码(C)通常包括以下几个步骤: 1. 准备标定板:首先需要准备一个已知尺寸的标定板,在两个相机的视野范围内进行放置和拍摄。标定板可以是平面的,上面有特殊的几何图案。 2. 拍摄图像:使用双目相机分别对标定板拍摄一组图像,确保拍摄过程中相机的位置和方向保持稳定。拍摄图像时,需要保证标定板在不同位置、角度和距离上都有充分的覆盖,以获取更好的标定结果。 3. 提取角点:对拍摄的图像进行角点提取,使用角点检测算法寻找标定板上的角点位置。角点提取可以使用OpenCV中的函数,如cv::findChessboardCorners()。 4. 标定计算:根据提取的角点位置数据,使用双目标定算法计算相机的内参矩阵、畸变系数、外参矩阵等相机参数。常用的标定算法包括Tsai算法、Zhang算法等。 5. 评估标定结果:完成标定计算后,需要对标定结果进行评估,通常使用重投影误差来评估标定的精度和准确性。重投影误差是指标定结果与实际角点位置之间的差异。 6. 应用标定结果:标定完成后,可以使用所得到的相机参数来进行双目视觉应用,如深度估计、三维重建、立体匹配等。校正双目图像是其中的一个重要应用,通过校正可以将两个相机的图像对齐,方便后续的立体匹配和深度计算。 双目相机标定代码(C)的实现可以使用OpenCV等图像处理库进行开发,这些库提供了丰富的函数和工具,用于在图像、角点、相机参数等之间进行转换和计算。标定结果的准确性和精度受到标定板的选择、角点提取的精度和标定算法的选择等因素的影响,因此,在实际应用中需要综合考虑各种因素,并进行必要的优化和调整。 ### 回答3: 双目相机标定是指通过对双目相机进行特定的数据收集和处理,以确定相机的内外参数,以及两个相机之间的相对位置和朝向关系,在三维定位和测量中起到重要的作用。 双目相机标定代码C语言实现的步骤主要包括以下几个方面: 1. 定义标定板:首先需要定义一个标定板,一般使用黑白相间的棋盘格,通过编写C代码来定义标定板的大小和格子的尺寸。 2. 采集标定图像:将标定板放置在不同位置和角度下,使用双目相机来采集标定图像,将图像保存为文件。 3. 检测角点:对标定图像进行处理,使用角点检测算法(如OpenCV库中的函数)来检测标定板上的角点位置。 4. 生成标定图像点对:将检测到的角点坐标与实际标定板上的角点坐标进行对应,并保存这些点对。 5. 生成内参数矩阵:根据标定图像点对,利用内参数标定方法(如高斯-牛顿法)来求解相机的内参数矩阵。 6. 生成外参数矩阵:利用内参数矩阵和标定图像点对,通过三维-二维坐标变换方法(如张正友标定法)来求解相机的外参数矩阵。 7. 标定结果输出:将求解得到的内外参数矩阵输出到文件中,以便后续使用。 以上就是使用C语言实现双目相机标定代码的基本步骤,根据具体需求,还可以添加一些其他的功能,如畸变矫正等。使用C语言编写标定代码需要熟悉相机模型、线性代数、图像处理等知识,同时需要利用图像处理库(如OpenCV)来实现相关的功能。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值