合工大苍穹战队视觉组培训Day9——相机标定

目录

学习目标:

学习内容:

学习时间:

学习产出:

一、前期准备

二、采集相关数据

三、进行标定

四、用cv::undistort方法去除畸变

五、效果

 六、心得


学习目标:

  • pnp解算
  • 相机标定
  • 去畸变

学习内容:

         直接用电脑的摄像头标定一个相机,标定完后使用cv::undistort方法去除相机的畸变。

学习时间:

  • 2022年8月1日到2022年8月4日

学习产出:

一、前期准备

这次长话短说吧,网上相关资料很多,可以参考https://blog.csdn.net/LuohenYJ/article/details/104697062

标定板我用的

可以去这个网站:Camera Calibration Pattern Generator – calib.io去生成

摄像头是电脑的摄像头。

二、采集相关数据

#include "opencv2/opencv.hpp"
#include <string>
#include <iostream>
 
using namespace cv;
using namespace std;
 
int main()
{
    VideoCapture inputVideo(0);
    //inputVideo.set(CV_CAP_PROP_FRAME_WIDTH, 320);
    //inputVideo.set(CV_CAP_PROP_FRAME_HEIGHT, 240);
    if (!inputVideo.isOpened())
    {
        cout << "Could not open the input video " << endl;
        return -1;
    }
    Mat frame;
    string imgname;
    int f = 1;
    while (1) //Show the image captured in the window and repeat
    {
        inputVideo >> frame;              // read
        if (frame.empty()) break;         // check if at end
        imshow("Camera", frame);
        char key = waitKey(1);
        if (key == 27)break;
        if (key == 'q' || key == 'Q')
        {
            imgname = to_string(f++) + ".jpg";
            imwrite(imgname, frame);
        }
    }
    cout << "Finished writing" << endl;
    return 0;
}

三、进行标定

//加载图片//
#include <iostream>
#include <opencv2/opencv.hpp>

using namespace std;
using namespace cv;

// 保存多张图片对象点列表
vector<vector<Point3f>> objectPoints;
// 保存多张图片的角点列表
vector<vector<Point2f>> cornerPoints;

int main(){
    // 图片像素尺寸
    Size imgSize;
    // 图片路径
    cv::String src_path = "./assets/camerargb_*.jpg";
    std::vector<String> filenames;
    cv::glob(src_path, filenames);//获取路径下所有文件名
    cout << "filenames.size:" << filenames.size() << endl;
    for (auto& imgName : filenames) {
        // 读取图片
        Mat img = imread(imgName, IMREAD_COLOR);
        // 获取图片像素尺寸
        imgSize = img.size();
        std::cout << "name: " << imgName<< " imgSize: " << imgSize << std::endl;
        //...
    }
    return 0;
}

//查找角点//
// 棋盘格的尺寸(宽6,高9)
const Size patternSize(6, 9);
// 黑方格的大小 20mm
const int squareSize = 20;
/**
 * 在指定图片中查找角点,并将结果输出到corners中
 * @param img     待检测图片
 * @param corners 检测到的焦点列表
 * @return 是否检测到角点(两个黑方格的交点)
 */
bool findCorners(Mat &img, vector<Point2f> &corners) {
    Mat gray;
    // 将图片转成灰度图
    cvtColor(img, gray, COLOR_RGB2GRAY);
    // 查找当前图片所有的角点
    bool patternWasFound = findChessboardCorners(gray, patternSize, corners);

    if (patternWasFound) { // 找到角点
        // 提高角点的精确度
        // 原理:https://docs.opencv.org/4.1.0/dd/d1a/group__imgproc__feature.html#ga354e0d7c86d0d9da75de9b9701a9a87e
        cornerSubPix(gray, corners, Size(11, 11), Size(-1, -1),
                     TermCriteria(TermCriteria::EPS + TermCriteria::COUNT, 30, 0.1));
    }

    // 将所有的焦点在原图中绘制出来
    drawChessboardCorners(img, patternSize, corners, patternWasFound);
    // 绘制完角点之后,显示原图
    imshow("src", img);

    if (!patternWasFound){
        cout << "角点检测失败!" << endl;
    }
    return patternWasFound;
}


// 保存多张图片对象点列表
vector<vector<Point3f>> objectPoints;
// 保存多张图片的角点列表
vector<vector<Point2f>> cornerPoints;

void calcObjectPoints(vector<Point3f> &objPoint) {
    // 计算uv空间中角点对应的相机坐标系坐标值,设Z为0
    for (int i = 0; i < patternSize.height; ++i)
        for (int j = 0; j < patternSize.width; ++j)
            objPoint.emplace_back(j * squareSize, i * squareSize, 0);
}

// 图片像素尺寸
Size imgSize;

int main(){
    // 图片路径
    cv::String src_path = "./assets/camerargb_*.jpg";
    std::vector<String> filenames;
    cv::glob(src_path, filenames);//获取路径下所有文件名
    cout << "filenames.size:" << filenames.size() << endl;
    for (auto& imgName : filenames) {
        // 读取图片
        Mat img = imread(imgName, IMREAD_COLOR);
        // 获取图片像素尺寸
        imgSize = img.size();
        std::cout << "name: " << imgName<< " imgSize: " << imgSize << std::endl;
        // 声明每张图片的角点
        vector<Point2f> corners;
        bool found = findCorners(img, corners);
        if (found) {
            vector<Point3f> objPoints;
            calcObjectPoints(objPoints);
            // 找到角点,证明这张图是有效的
            objectPoints.push_back(objPoints);
            cornerPoints.push_back(corners);
        }
    }
    return 0;
}

//执行相机标定//
Mat cameraMatrix; // 相机参数矩阵
Mat disCoffes; // 失真系数 distortion coefficients
Mat rvecs; // 图片旋转向量
Mat tvecs; // 图片平移向量

calibrateCamera(objectPoints, cornerPoints, imgSize, cameraMatrix, disCoffes, rvecs, tvecs);

cout << "标定矩阵:" << cameraMatrix << endl;
cout << "畸变矩阵:" << disCoffes << endl;
// save2xml(cameraMatrix, distCoffes);

waitKey();

//保存标定结果//
void save2xml(const Mat &cameraMatrix, const Mat &disCoffes) {
    // 获取当前时间
    time_t tm;
    time(&tm);
    struct tm *t2 = localtime(&tm);
    char buf[1024];
    strftime(buf, sizeof(buf), "%c", t2);

    // 写出数据
    String inCailFilePath = "./inCailFilePath.xml";
    FileStorage inCailFs(inCailFilePath, FileStorage::WRITE);
    inCailFs << "calibration_time" << buf;
    inCailFs << "cameraMatrix" << cameraMatrix;
    inCailFs << "distCoffes" << disCoffes;
    inCailFs.release();
}


//我最后输出了yml文件,也可以是xml文件格式//

四、用cv::undistort方法去除畸变

#include <iostream>
#include <opencv2/opencv.hpp>
#include <opencv2/imgproc.hpp>

using namespace cv;
using namespace std;

int main(int argc, char **argv) {

    // 读取相机矩阵、畸变系数
    cv::FileStorage fs("你的yml文件", FileStorage::READ);
    int image_width{0}, image_height{0};
    fs["image_width"] >> image_width;
    fs["image_height"] >> image_height;
    Size image_size = Size(image_width, image_height);

    Mat intrinsic_matrix, distortion_coeffs;
    fs["cameraMatrix"] >> intrinsic_matrix;
    fs["distCoeffs"] >> distortion_coeffs;
    fs.release();
    std::cout << intrinsic_matrix << std::endl;
    std::cout << distortion_coeffs << std::endl;
    std::cout << image_size << std::endl;

    const Mat &image0 = imread("./calib_chess_img/image_0.jpg", IMREAD_COLOR);
    Mat image;

    undistort(image0, image, intrinsic_matrix, distortion_coeffs);

    imshow("original", image0);
    imshow("undistorted", image);

    waitKey();
    return 0;
}

PS:代码仅供参考。

五、效果

 六、心得

我觉得做的不是很好很对,很多东西还是得进一步弄好,等我多学点再后续更新吧。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

工大电科小趴菜

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值