标定系列——基于OpenCV实现普通相机、鱼眼相机不同标定板下的标定(七)

标定系列——基于OpenCV实现相机标定(七)

说明

该程序可以实现多种标定板的相机标定工作

代码解析

VID5.xml

<?xml version="1.0"?>

<!-- 相机拍摄的标定板图像路径名 -->
<opencv_storage>
<images>
images/CameraCalibration/VID5/xx1.jpg
images/CameraCalibration/VID5/xx2.jpg
images/CameraCalibration/VID5/xx3.jpg
images/CameraCalibration/VID5/xx4.jpg
images/CameraCalibration/VID5/xx5.jpg
images/CameraCalibration/VID5/xx6.jpg
images/CameraCalibration/VID5/xx7.jpg
images/CameraCalibration/VID5/xx8.jpg
</images>
</opencv_storage>

in_VID5.xml

<?xml version="1.0"?>
<opencv_storage>
<Settings>
  <!-- 标定板尺寸. (可以是正方形、圆形) -->
  <BoardSize_Width>9</BoardSize_Width>
  <BoardSize_Height>6</BoardSize_Height>
  
  <!-- 用户定义的方格的尺寸 (像素,毫米)-->
  <Square_Size>50</Square_Size>
  <Marker_Size>25</Marker_Size>
  <!-- 相机标定所使用的标定板类型. 可以是CHESSBOARD CHARUCOBOARD CIRCLES_GRID ASYMMETRIC_CIRCLES_GRID -->
  <Calibrate_Pattern>"CHESSBOARD"</Calibrate_Pattern>
  <ArUco_Dict_Name>DICT_4X4_50</ArUco_Dict_Name>
  <ArUco_Dict_File_Name></ArUco_Dict_File_Name>
  <!--  用于标定的输入来源。
        使用输入摄像头 -> 提供摄像头的ID,例如 "1"
        使用输入视频  -> 提供输入视频的路径,例如 "/tmp/x.avi"
        使用图像列表  -> 提供含有图像列表的XML或YAML文件的路径,例如 "/tmp/circles_list.xml"
		-->
  <Input>"images/CameraCalibration/VID5/VID5.xml"</Input>
  <!--  如果为真(非零),则沿水平轴翻转输入图像 -->
  <Input_FlipAroundHorizontalAxis>0</Input_FlipAroundHorizontalAxis>
  
  <!-- 摄像头的帧之间的时间延迟 -->
  <Input_Delay>100</Input_Delay>	
  
  <!--  用于标定的帧数量 -->
  <Calibrate_NrOfFrameToUse>25</Calibrate_NrOfFrameToUse>
  <!-- 只考虑fy作为自由参数,比率fx/fy与输入cameraMatrix中的相同 -->
  <Calibrate_FixAspectRatio> 1 </Calibrate_FixAspectRatio>
  <!-- 如果为真(非零),切向畸变系数将被设置为零并保持为零 -->
  <Calibrate_AssumeZeroTangentialDistortion>1</Calibrate_AssumeZeroTangentialDistortion>
  <!-- 如果为真(非零),在全局优化过程中主点不会改变 -->
  <Calibrate_FixPrincipalPointAtTheCenter> 1 </Calibrate_FixPrincipalPointAtTheCenter>
  
  <!-- 输出日志文件名 -->
  <Write_outputFileName>"out_camera_data.xml"</Write_outputFileName>
  <!-- 如果为真(非零),将检测到的特征点写入输出文件 -->
  <Write_DetectedFeaturePoints>1</Write_DetectedFeaturePoints>
  <!-- 如果为真(非零),我们将外部相机参数写入输出文件 -->
  <Write_extrinsicParameters>1</Write_extrinsicParameters>
  <!--  如果为真(非零),我们将优化后的3D目标网格点写入输出文件 -->
  <Write_gridPoints>1</Write_gridPoints>
  <!-- 如果为真(非零),校准后我们显示无畸变的图像 -->
  <Show_UndistortedImage>1</Show_UndistortedImage>
  <!-- 如果为真(非零),将使用鱼眼相机模型进行标定 -->
  <Calibrate_UseFisheyeModel>0</Calibrate_UseFisheyeModel>
  <!-- 如果为真(非零),畸变系数k1将等于零 -->
  <Fix_K1>0</Fix_K1>
  <!-- 如果为真(非零),畸变系数k2将等于零  -->
  <Fix_K2>0</Fix_K2>
  <!-- 如果为真(非零),畸变系数k3将等于零 -->
  <Fix_K3>0</Fix_K3>
  <!-- 如果为真(非零),畸变系数k4将等于零 -->
  <Fix_K4>1</Fix_K4>
  <!-- 如果为真(非零),畸变系数k5将等于零 -->
  <Fix_K5>1</Fix_K5>
</Settings>
</opencv_storage>

camera_calibration.cpp

核心代码就是camera_calibration.cpp,主要通过多张标定板图像进行相机的内参和畸变参数的计算,大体看了一下,里面的逻辑很清晰,就不做过多注解了

#include <iostream>
#include <sstream>
#include <string>
#include <ctime>
#include <cstdio>

#include <opencv2/core.hpp>
#include <opencv2/core/utility.hpp>
#include <opencv2/imgproc.hpp>
#include <opencv2/calib3d.hpp>
#include <opencv2/imgcodecs.hpp>
#include <opencv2/videoio.hpp>
#include <opencv2/highgui.hpp>
#include "opencv2/objdetect/charuco_detector.hpp"

using namespace cv;
using namespace std;

class Settings
{
public:
    Settings() : goodInput(false) {}
    enum Pattern { NOT_EXISTING, CHESSBOARD, CHARUCOBOARD, CIRCLES_GRID, ASYMMETRIC_CIRCLES_GRID };
    enum InputType { INVALID, CAMERA, VIDEO_FILE, IMAGE_LIST };

    void write(FileStorage& fs) const                        //将数据写入文件
    {
        fs << "{"
                  << "BoardSize_Width"  << boardSize.width
                  << "BoardSize_Height" << boardSize.height
                  << "Square_Size"         << squareSize
                  << "Marker_Size"      << markerSize
                  << "Calibrate_Pattern" << patternToUse
                  << "ArUco_Dict_Name"   << arucoDictName
                  << "ArUco_Dict_File_Name" << arucoDictFileName
                  << "Calibrate_NrOfFrameToUse" << nrFrames
                  << "Calibrate_FixAspectRatio" << aspectRatio
                  << "Calibrate_AssumeZeroTangentialDistortion" << calibZeroTangentDist
                  << "Calibrate_FixPrincipalPointAtTheCenter" << calibFixPrincipalPoint

                  << "Write_DetectedFeaturePoints" << writePoints
                  << "Write_extrinsicParameters"   << writeExtrinsics
                  << "Write_gridPoints" << writeGrid
                  << "Write_outputFileName"  << outputFileName

                  << "Show_UndistortedImage" << showUndistorted

                  << "Input_FlipAroundHorizontalAxis" << flipVertical
                  << "Input_Delay" << delay
                  << "Input" << input
           << "}";
    }
    void read(const FileNode& node)                          //从文件中读
    {
        node["BoardSize_Width"] >> boardSize.width;
        node["BoardSize_Height"] >> boardSize.height;
        node["Calibrate_Pattern"] >> patternToUse;
        node["ArUco_Dict_Name"] >> arucoDictName;
        node["ArUco_Dict_File_Name"] >> arucoDictFileName;
        node["Square_Size"] >> squareSize;
        node["Marker_Size"] >> markerSize;
        node["Calibrate_NrOfFrameToUse"] >> nrFrames;
        node["Calibrate_FixAspectRatio"] >> aspectRatio;
        node["Write_DetectedFeaturePoints"] >> writePoints;
        node["Write_extrinsicParameters"] >> writeExtrinsics;
        node["Write_gridPoints"] >> writeGrid;
        node["Write_outputFileName"] >> outputFileName;
        node["Calibrate_AssumeZeroTangentialDistortion"] >> calibZeroTangentDist;
        node["Calibrate_FixPrincipalPointAtTheCenter"] >> calibFixPrincipalPoint;
        node["Calibrate_UseFisheyeModel"] >> useFisheye;
        node["Input_FlipAroundHorizontalAxis"] >> flipVertical;
        node["Show_UndistortedImage"] >> showUndistorted;
        node["Input"] >> input;
        node["Input_Delay"] >> delay;
        node["Fix_K1"] >> fixK1;
        node["Fix_K2"] >> fixK2;
        node["Fix_K3"] >> fixK3;
        node["Fix_K4"] >> fixK4;
        node["Fix_K5"] >> fixK5;

        validate();
    }
	
	// 输入值验证
    void validate()
    {
        goodInput = true;
        if (boardSize.width <= 0 || boardSize.height <= 0)
        {
            cerr << "Invalid Board size: " << boardSize.width << " " << boardSize.height << endl;
            goodInput = false;
        }
        if (squareSize <= 10e-6)
        {
            cerr << "Invalid square size " << squareSize << endl;
            goodInput = false;
        }
        if (nrFrames <= 0)
        {
            cerr << "Invalid number of frames " << nrFrames << endl;
            goodInput = false;
        }

        if (input.empty())      // Che
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值