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

这篇具有很好参考价值的文章主要介绍了用OpenCV进行相机标定(张正友标定,有代码)。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

1. 内参与畸变

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

内参矩阵K
用OpenCV进行相机标定(张正友标定,有代码)

畸变系数:径向畸变(k1,k2,k3), 切向畸变(p1,p2)
用OpenCV进行相机标定(张正友标定,有代码)径向畸变公式
用OpenCV进行相机标定(张正友标定,有代码)切向畸变公式
用OpenCV进行相机标定(张正友标定,有代码)张正友标定方法能够提供一个比较好的初始解,用于后序的最优化.

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

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

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

2. 用OpenCV标定相机程序

1,提取角点
2,亚像素角点
3,可视化提取角点(非必须)
4,标定
5,误差计算(重投影误差)文章来源地址https://www.toymoban.com/news/detail-468988.html

#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;
}

到了这里,关于用OpenCV进行相机标定(张正友标定,有代码)的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 相机标定张正友、opencv和halcon对比(1)

    本文将从基本标定开始,结合实际工作经验,分析张正友、opencv和halcon三者相机标定的深层原理与不同之处,内容比较多,如果出现错误请指正。 我们使用的镜头都是由多组镜片组成,它实际上是一种厚透镜模型,但是目前所有的相机标定是基于针孔模型来进行标定的,因此

    2024年02月03日
    浏览(38)
  • 【相机标定】opencv python 标定相机内参时不计算 k3 畸变参数

    畸变参数 k3 通常用于描述径向畸变的更高阶效应,即在需要高精度的应用中可以用到,一般的应用中 k1, k2 足矣。 常见的应用中, orbslam3 中是否传入 k3 是可选的,而 kalibr 标定中则只需要传入 k1, k2 。但计算 k3 时的 k1, k2 不等于不计算 k3 时的 k1, k2 ,因此需要学会两种场景下

    2024年02月09日
    浏览(41)
  • OpenCV开发笔记(七十七):相机标定(二):通过棋盘标定计算相机内参矩阵矫正畸变摄像头图像

    若该文为原创文章,转载请注明原文出处 本文章博客地址:https://hpzwl.blog.csdn.net/article/details/136616551 各位读者,知识无穷而人力有穷,要么改需求,要么找专业人士,要么自己研究 红胖子(红模仿)的博文大全:开发技术集合(包含Qt实用技术、树莓派、三维、OpenCV、OpenGL、

    2024年03月13日
    浏览(48)
  • 相机的内外参数标定和畸变矫正原理和代码

    相机的成像过程实质上是坐标系转换。首先空间中的点坐标由世界坐标系转换到相机坐标系,然后将其投影到成像平面(图像物理坐标系),最后再将成像平面上的数据转换到图像像素坐标系。但是由于透镜制造精度及组装工艺的差别会引入畸变,导致原始图像的失真。镜头

    2024年04月16日
    浏览(46)
  • 相机标定—— 张正友标定法(1)

    我们首先要明白两个问题:1、相机是如何成像的?2、相机标定的目的是什么? 相机的成像过程涉及了 4 种坐标系与 3 种变换关系,这 3种变换关系分别是刚体变换、投影变换和离散化。 图 1 : 四 种 坐 标 系 的 关 系 图1 :四种坐标系的关系 图 1 : 四 种 坐 标 系 的 关 系

    2023年04月09日
    浏览(84)
  • 相机标定-张正友棋盘格标定法

    目录 1.针孔相机模型 2.相机成像过程 2.1  各个坐标系之间的转换 2.1.1 图像坐标系到像素坐标系  2.1.2 相机坐标系到图像坐标系  2.1.3世界坐标系到相机坐标系  2.1.4世界坐标系到像素坐标系 3.畸变与畸变矫正 3.1 畸变 3.2 畸变公式 4.相机标定原理 5.张正友标定法介绍 5.1张正友

    2024年01月16日
    浏览(45)
  • 【三维重建】相机标定:张正友标定法

    本系列开始于2022.12.25,开始记录三维重建项目课题研究时的学习笔记,其中主要分为以下几部分组成: 一、相机成像及坐标系之间的转换关系 二、相机标定:张正友标定法 三、特征检测与匹配 四、运动恢复结构法 目录 系列文章目录 文章目录 前言 一、 标定目的 二、 张正

    2024年02月07日
    浏览(42)
  • 张正友相机标定法原理与实现

    张正友相机标定法是张正友教授1998年提出的单平面棋盘格的相机标定方法。传统标定法的标定板是需要三维的,需要非常精确,这很难制作,而张正友教授提出的方法介于传统标定法和自标定法之间,但克服了传统标定法需要的高精度标定物的缺点,而仅需使用一个打印出来

    2024年02月04日
    浏览(47)
  • opencv对相机进行畸变校正,及校正前后的坐标对应

    目前有个项目,需要用到热成像相机。但是这个热成像相机它的畸变比较厉害,因此需要用标定板进行标定,从而消除镜头畸变。 同时需要实现用户用鼠标点击校正后的画面后,显示用户点击位置的像素所代表的温度。 另外热成像sdk中还有个功能:选定一个rect,可以返回这

    2024年02月14日
    浏览(47)
  • opencv对相机进行畸变矫正,及矫正前后的坐标对应

    目前有个项目,需要用到热成像相机。但是这个热成像相机它的畸变比较厉害,因此需要用标定板进行标定,从而消除镜头畸变。 同时需要实现用户用鼠标点击校正后的画面后,显示用户点击位置的像素所代表的温度。 另外热成像sdk中还有个功能:选定一个rect,可以返回这

    2024年02月15日
    浏览(35)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包