标定系列——基于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

最低0.47元/天 解锁文章
4767

被折叠的 条评论
为什么被折叠?



