opencv c++实现棋盘格标定

程序流程

  1. 准备好一系列用来相机标定的图片;
  2. 对每张图片提取角点信息;
  3. 由于角点信息不够精确,进一步提取亚像素角点信息;
  4. 在图片中画出提取出的角点;
  5. 相机标定;
  6. 对标定结果评价,计算误差;
  7. 使用标定结果对原图片进行矫正;

opencv实现代码:

#include <iostream>
#include <vector>
#include <fstream>
#include <string>
#include <opencv2/opencv.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <opencv2/imgproc/imgproc.hpp>
#include <opencv2/calib3d/calib3d.hpp>

using namespace std;
using namespace cv;

int main()
{
    ifstream inImgPath("calibdata.txt");    //标定所用图像文件的路径
    vector<string> imgList;
    vector<string>::iterator p;
    string temp;
    if (!inImgPath.is_open())
    {
        cout << "没有找到文件" << endl;
    }
    //读取文件中保存的图片文件路径,并存放在数组中
    while (getline(inImgPath, temp))
    {
        imgList.push_back(temp);
    }

    ofstream fout("caliberation_result.txt");   //保存标定结果的文件

    cout << "开始提取角点......" << endl;
    cv::Size image_size;//保存图片大小
    cv::Size pattern_size = cv::Size(9, 10);//标定板上每行、每列的角点数;测试图片中的标定板上内角点数为8*6
    vector<cv::Point2f> corner_points_buf;//建一个数组缓存检测到的角点,通常采用Point2f形式,以图像的左上角为原点,而不是以棋盘的左上角为原点
    vector<cv::Point2f>::iterator corner_points_buf_ptr;
    vector<vector<cv::Point2f>> corner_points_of_all_imgs;   //所有图片的角点信息
    int image_num = 0;
    string filename;
    while(image_num < imgList.size())
    {
        filename = imgList[image_num++];
        cout << "image_num = " << image_num << endl;
        cout << filename << endl;
        cv::Mat imageInput = cv::imread(filename);
        if (image_num == 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;
        }

        if (findChessboardCorners(imageInput, pattern_size, corner_points_buf) == 0)
        {
            cout << "can not find all chessboard corners!\n";   //找不到角点
            exit(1);
        }
        else
        {
            cv::Mat gray;
            cv::cvtColor(imageInput, gray, COLOR_RGB2GRAY);              //将原来的图片转换为灰度图片
            cv::find4QuadCornerSubpix(gray, corner_points_buf, cv::Size(5, 5));   //提取亚像素角点,Size(5, 5),角点搜索窗口的大小。
            corner_points_of_all_imgs.push_back(corner_points_buf);     
            cv::drawChessboardCorners(gray, pattern_size, corner_points_buf, true);   
            cv::imshow("camera calibration", gray);
            cv::waitKey(100);
        }
    }

    int total = corner_points_of_all_imgs.size();
    cout << "total=" << total << endl;
    int cornerNum = pattern_size.width * pattern_size.height;//每张图片上的总的角点数
   // cout<<cornerNum<<endl;
    for (int i = 0; i < total;i++)
    {
        cout << "--> 第" << i + 1 << "幅图片的数据 -->:" << endl;
        for (int j = 0;j < cornerNum;j++)
        {
            cout << "-->" << corner_points_of_all_imgs[i][j].x;
            cout << "-->" << corner_points_of_all_imgs[i][j].y;
            if ((j + 1) % 3 == 0)
            {
                cout << endl;
            }
            else
            {
                cout.width(10);
            }
        }
        cout << endl;
    }

    cout << endl << "角点提取完成" << endl;

    //摄像机标定
    cout << "开始标定………………" << endl;
    cv::Mat cameraMatrix = cv::Mat(3, 3, CV_32FC1, cv::Scalar::all(0));//内外参矩阵,H——单应性矩阵
    cv::Mat distCoefficients = cv::Mat(1, 5, CV_32FC1, cv::Scalar::all(0));//摄像机的5个畸变系数:k1,k2,p1,p2,k3
    vector<cv::Mat> tvecsMat;//每幅图像的平移向量,t,若干张图片的,不是一张图像的
    vector<cv::Mat> rvecsMat;//每幅图像的旋转向量(罗德里格旋转向量,若干张图片的,不是一张图像的
    vector<vector<cv::Point3f>> objectPoints;//保存所有图片的角点的三维坐标,所有图片的
                                             //初始化每一张图片中标定板上角点的三维坐标                 //世界坐标系,以棋盘格的左上角为坐标原点
    int i, j, k;
    for (k = 0;k < image_num;k++)//遍历每一张图片
    {
        vector<cv::Point3f> tempCornerPoints;//每一幅图片对应的角点数组
        //遍历所有的角点
        for (i = 0;i < pattern_size.height;i++)
        {
            for (j = 0;j < pattern_size.width;j++)
            {
                cv::Point3f singleRealPoint;//一个角点的坐标,初始化三维坐标
                singleRealPoint.x = i * 10;     //10是长/宽,根据黑白格子的长和宽,计算出世界坐标系(x,y,z)
                singleRealPoint.y = j * 10;
                singleRealPoint.z = 0;//假设z=0
                tempCornerPoints.push_back(singleRealPoint);
            }
        }
        objectPoints.push_back(tempCornerPoints);
    }

    cv::calibrateCamera(objectPoints, corner_points_of_all_imgs, image_size, cameraMatrix, distCoefficients, rvecsMat, tvecsMat, 0);
    cout << "标定完成" << endl;

    //开始保存标定结果
    cout << "开始保存标定结果" << endl;

    cout << endl << "相机相关参数:" << endl;
    fout << "相机相关参数:" << endl;
    cout << "1.内外参数矩阵:" << endl;
    fout << "1.内外参数矩阵:" << endl;
    cout << "大小:" << cameraMatrix.size() << endl;
    fout << "大小:" << cameraMatrix.size() << endl;
    //cout<<cameraMatrix.depth()<<endl;tempCornerPoints
    fout << "大小:" << distCoefficients.size() << endl;
    cout << distCoefficients << endl;
    fout << distCoefficients << endl;

    cout << endl << "图像相关参数:" << endl;
    fout << endl << "图像相关参数:" << endl;
    cv::Mat rotation_Matrix = cv::Mat(3, 3, CV_32FC1, cv::Scalar::all(0));//旋转矩阵
    for (i = 0;i < image_num;i++)
    {
        cout << "第" << i + 1 << "幅图像的旋转向量:" << endl;
        fout << "第" << i + 1 << "幅图像的旋转向量:" << endl;
        cout << rvecsMat[i] << endl;
        fout << rvecsMat[i] << endl;
        cout << "第" << i + 1 << "幅图像的旋转矩阵:" << endl;
        fout << "第" << i + 1 << "幅图像的旋转矩阵:" << endl;
        cv::Rodrigues(rvecsMat[i], rotation_Matrix);//将旋转向量转换为相对应的旋转矩阵
        cout << rotation_Matrix << endl;
        fout << rotation_Matrix << endl;
        cout << "第" << i + 1 << "幅图像的平移向量:" << endl;
        fout << "第" << i + 1 << "幅图像的平移向量:" << endl;
        cout << tvecsMat[i] << endl;
        fout << tvecsMat[i] << endl;
    }

    cout << "结果保存完毕" << endl;

    //对标定结果进行评价
    cout << "开始评价标定结果......" << endl;

    //计算每幅图像中的角点数量,假设全部角点都检测到了
    int corner_points_counts;
    corner_points_counts = pattern_size.width * pattern_size.height;

    cout << "每幅图像的标定误差:" << endl;
    fout << "每幅图像的标定误差:" << endl;
    double err = 0;//单张图像的误差
    double total_err = 0;//所有图像的平均误差
    for (i = 0;i < image_num;i++)
    {
        vector<cv::Point2f> image_points_calculated;//存放新计算出的投影点的坐标
        vector<cv::Point3f> tempPointSet = objectPoints[i];
        cv::projectPoints(tempPointSet, rvecsMat[i], tvecsMat[i], cameraMatrix, distCoefficients, image_points_calculated);  //计算根据内外参等重投影出来的新的二维坐标,输出到image_points_calculated
        //计算新的投影点与旧的投影点之间的误差
        vector<cv::Point2f> image_points_old = corner_points_of_all_imgs[i];    //向量
        //将两组数据换成Mat格式
        cv::Mat image_points_calculated_mat = cv::Mat(1, image_points_calculated.size(), CV_32FC2);   //将mat矩阵转成1维的向量
        cv::Mat image_points_old_mat = cv::Mat(1, image_points_old.size(), CV_32FC2);
        for (j = 0;j < tempPointSet.size();j++)
        {
            image_points_calculated_mat.at<cv::Vec2f>(0, j) = cv::Vec2f(image_points_calculated[j].x, image_points_calculated[j].y);  //vec2f->一个二维的向量
            image_points_old_mat.at<cv::Vec2f>(0, j) = cv::Vec2f(image_points_old[j].x, image_points_old[j].y);   //直接调用函数,不用定义对象
        }
        err = cv::norm(image_points_calculated_mat, image_points_old_mat, cv::NORM_L2); //输入的是矩阵
        err /= corner_points_counts;      //每个角点的误差
        total_err += err;
        cout << "第" << i + 1 << "幅图像的平均误差:" << err << "像素" << endl;
        fout << "第" << i + 1 << "幅图像的平均误差:" << err << "像素" << endl;
    }
    cout << "总体平均误差:" << total_err / image_num << "像素" << endl;
    fout << "总体平均误差:" << total_err / image_num << "像素" << endl;
    cout << "评价完成" << endl;


    //下面就是矫正图像的代码
    cv::Mat mapx = cv::Mat(image_size, CV_32FC1);  //对应坐标的重映射参数
    cv::Mat mapy = cv::Mat(image_size, CV_32FC1);
    cv::Mat R = cv::Mat::eye(3, 3, CV_32F);   //定义的对角线为1的对角矩阵
    cout << "保存矫正图像" << endl;
    string imageFileName;
    std::stringstream StrStm;
    for (int i = 0;i < image_num;i++)
    {
        cout << "Frame #" << i + 1 << endl;
        cv::initUndistortRectifyMap(cameraMatrix, distCoefficients, R, cameraMatrix, image_size, CV_32FC1, mapx, mapy);      //输入内参,纠正后的内参,外参等,计算输出矫正的重映射参数(相片尺寸 width*height),每个像素点都需要转换
        cv::Mat src_image = cv::imread(imgList[i], 1);     
        cv::Mat new_image = src_image.clone();
        cv::remap(src_image, new_image, mapx, mapy, cv::INTER_LINEAR);    
        imshow("原始图像", src_image);
        imshow("矫正后图像", new_image);

        StrStm.clear();
        imageFileName.clear();
        StrStm << i + 1;
        StrStm >> imageFileName;
        imageFileName += "_d.jpg";
        cv::imwrite(imageFileName, new_image);

        cv::waitKey(200);
    }
    cout << "保存结束" << endl;
    fout.close();  //
    cv::waitKey(0);

    return 0;
}

 相关文章:三维重建学习(4):张正友相机标定程序实现(OpenCV)_hongbin_xu的博客-CSDN博客_标定程序

  • 3
    点赞
  • 27
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
好的,下面是使用 OpenCVC++ 中进行棋盘标定的步骤: 1. 准备标定图像:使用相机拍摄多张包含完整棋盘的图像,并保存在本地。 2. 定义棋盘参数:定义棋盘的行数、列数、每个方的大小(单位为毫米)等参数。 3. 导入图像并查找棋盘角点:使用 OpenCV 中的 `findChessboardCorners()` 函数在图像中查找棋盘角点,并将其保存在一个数组中。 4. 绘制角点:使用 `drawChessboardCorners()` 函数绘制被找到的角点。 5. 标定相机:使用 `calibrateCamera()` 函数标定相机,并计算出相机的内部参数和畸变矩阵。 6. 评估标定结果:使用 `getOptimalNewCameraMatrix()` 函数来优化相机矩阵,并使用 `undistort()` 函数来矫正图像的畸变。 下面是示例代码,可以根据自己的需要进行修改: ```c++ #include <opencv2/opencv.hpp> #include <iostream> using namespace cv; using namespace std; int main() { // 定义棋盘参数 int board_width = 9; int board_height = 6; float square_size = 25.0; // 每个方的大小,单位为毫米 // 准备标定图像 vector<vector<Point3f>> object_points; // 保存棋盘角点的三维坐标 vector<vector<Point2f>> image_points; // 保存棋盘角点的图像坐标 vector<Point2f> corners; // 保存每张图像中被找到的角点 Mat image, gray; Size image_size; // 循环读取图像并查找角点 for (int i = 1; i <= 20; i++) { // 读取图像 string filename = "image" + to_string(i) + ".jpg"; image = imread(filename); if (image.empty()) { cout << "Can't read image " << filename << endl; continue; } image_size = image.size(); // 转换为灰度图像 cvtColor(image, gray, COLOR_BGR2GRAY); // 查找角点 bool found = findChessboardCorners(gray, Size(board_width, board_height), corners); if (found) { // 亚像素精确化 cornerSubPix(gray, corners, Size(11, 11), Size(-1, -1), TermCriteria(TermCriteria::EPS + TermCriteria::COUNT, 30, 0.1)); // 绘制角点 drawChessboardCorners(image, Size(board_width, board_height), corners, found); imshow("Chessboard", image); waitKey(500); // 暂停一下,方便观察 // 保存角点 vector<Point3f> object_corner; for (int j = 0; j < board_height; j++) { for (int k = 0; k < board_width; k++) { Point3f corner((float)k * square_size, (float)j * square_size, 0); object_corner.push_back(corner); } } object_points.push_back(object_corner); image_points.push_back(corners); } } // 标定相机 Mat camera_matrix, dist_coeffs; vector<Mat> rvecs, tvecs; calibrateCamera(object_points, image_points, image_size, camera_matrix, dist_coeffs, rvecs, tvecs); // 评估标定结果 Mat mapx, mapy; Mat new_camera_matrix = getOptimalNewCameraMatrix(camera_matrix, dist_coeffs, image_size, 1, image_size, 0); initUndistortRectifyMap(camera_matrix, dist_coeffs, Mat(), new_camera_matrix, image_size, CV_16SC2, mapx, mapy); // 矫正图像 for (int i = 1; i <= 20; i++) { // 读取图像 string filename = "image" + to_string(i) + ".jpg"; image = imread(filename); if (image.empty()) { cout << "Can't read image " << filename << endl; continue; } // 矫正图像 Mat undistorted; remap(image, undistorted, mapx, mapy, INTER_LINEAR); // 显示矫正前后的图像 imshow("Original Image", image); imshow("Undistorted Image", undistorted); waitKey(0); } return 0; } ```

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值