OpenCV摄像机标定例程笔记

通过500行代码实现摄像机标定,适用于焦距不变的情况。使用OpenCV处理棋盘格、圆形阵列或环形阵列的标定板,通过VS2010和opencv2.4.6进行调试。流程包括读取配置、提取关键点、显示进度,最终保存标定结果。涉及文件包括主程序camera_calibration.cpp、配置文件in_VID5.xml、照片信息文件VID5.xml及输出标定结果文件out_camera_data.yml。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

摄像机该怎么标定,OpenCV告诉你奋斗,500行代码统统搞定。下面直接上代码,注释全在代码中,吧啦吧啦。。。

适用范围:摄像机在拍摄过程中焦距不变

标定数据源:拍摄对象为标定板的---- 多张无序照片 or 单个视频 or 摄像头实时输入

标定板类型:棋盘格 or 圆形阵列 or 环形阵列

调试环境:opencv2.4.6 + VS2010 

大致流程:

        1.读取配置文件

        2.循环开始:获取一张照片,如果照片已足够则进行标定,保存标定结果,跳出循环。不够继续执行3,4步。

        3.提取照片中keypoint位置,圆形阵列或环形阵列定位一次即可,单对于棋盘格分为初步定为和精确定位两个阶段

        4.显示提取keypoint以后的照片,并标上当前的序号和标定状态。回到2开始下一次循环。


涉及文件:

camera_calibration.cpp:标定主程序

in_VID5.xml:作为输入的配置文件

VID5.xml:存有照片路径信息的文件

out_camera_data.yml:作为输出标定结果的文件


配置文件(in_VID5.xml)中的重要参数:

        1.BoardSize_Width 和 BoardSize_Height分别表示横向,纵向棋盘格keypoint个数。

        2.Square_Size:以毫米或者像素为单位的keypoint之间间隔距离

        3.Calibrate_Pattern:可以设置为CHESSBOARD /CIRCLES_GRID /ASYMMETRIC_CIRCLES_GRID三种格式

        4.Input:输入类型,摄像头实时捕捉直接输入摄像机编号(编号从0开始),视频文件直接写入文件名称。照片序列则写入存有照片序列信息的文件名称,这里是VID5.xml

        5.Calibrate_NrOfFrameToUse:标定需要用到的图片数量,图片序列标定以实际图片数量为准。


输出文件(out_camera_data.yml)中的一些参数:

<Image_points type_id="opencv-matrix">
  <rows>25</rows>
  <cols>70</cols>
  <dt>"2f"</dt>      这里的2f表示2维浮点数类型的数据
  <data>
    3.79758453e+002 2.20568024e+002 4.28894653e+002 2.21272049e+002
    4.77973450e+002 2.21748367e+002 5.27806030e+002 2.21833710e+002
    .
    .
    .
  </data>
  </Image_points>

详细注释见代码。。。

//使用opencv2.4.6中samples/cpp/tutorial_code/calib3d/camera_calibration/camera_calibration.cpp

#include <iostream>
#include <sstream>
#include <time.h>
#include <stdio.h>

#include <opencv2/core/core.hpp>
#include <opencv2/imgproc/imgproc.hpp>
#include <opencv2/calib3d/calib3d.hpp>
#include <opencv2/highgui/highgui.hpp>

using namespace cv;
using namespace std;

static void help()
{
    cout <<  "This is a camera calibration sample." << endl
         <<  "Usage: calibration configurationFile"  << endl
         <<  "Near the sample file you'll find the configuration file, which has detailed help of "
             "how to edit it.  It may be any OpenCV supported file format XML/YAML." << endl;
}
class Settings
{
public:
    Settings() : goodInput(false) {}
    enum Pattern { NOT_EXISTING, CHESSBOARD, CIRCLES_GRID, ASYMMETRIC_CIRCLES_GRID };
    enum InputType {INVALID, CAMERA, VIDEO_FILE, IMAGE_LIST};

    void write(FileStorage& fs) const                        //Write serialization for this class
    {
        fs << "{" << "BoardSize_Width"  << boardSize.width
                  << "BoardSize_Height" << boardSize.height
                  << "Square_Size"         << squareSize
                  << "Calibrate_Pattern" << patternToUse
                  << "Calibrate_NrOfFrameToUse" << nrFrames
                  << "Calibrate_FixAspectRatio" << aspectRatio
                  << "Calibrate_AssumeZeroTangentialDistortion" << calibZeroTangentDist
                  << "Calibrate_FixPrincipalPointAtTheCenter" << calibFixPrincipalPoint

                  << "Write_DetectedFeaturePoints" << bwritePoints
                  << "Write_extrinsicParameters"   << bwriteExtrinsics
                  << "Write_outputFileName"  << outputFileName

                  << "Show_UndistortedImage" << showUndistorsed

                  << "Input_FlipAroundHorizontalAxis" << flipVertical
                  << "Input_Delay" << delay
                  << "Input" << input
           << "}";
    }
    void read(const FileNode& node)                          //Read serialization for this class
    {
        node["BoardSize_Width" ] >> boardSize.width;
        node["BoardSize_Height"] >> boardSize.height;
        node["Calibrate_Pattern"] >> patternToUse;
        node["Square_Size"]  >> squareSize;
        node["Calibrate_NrOfFrameToUse"] >> nrFrames;
        node["Calibrate_FixAspectRatio"] >> aspectRatio;
        node["Write_DetectedFeaturePoints"] >> bwritePoints;
        node["Write_extrinsicParameters"] >> bwriteExtrinsics;
        node["Write_outputFileName"] >> outputFileName;
        node["Calibrate_AssumeZeroTangentialDistortion"] >> calibZeroTangentDist;
        node["Calibrate_FixPrincipalPointAtTheCenter"] >> calibFixPrincipalPoint;
        node["Input_FlipAroundHorizontalAxis"] >> flipVertical;
        node["Show_UndistortedImage"] >> showUndistorsed;
        node["Input"] >> input;
        node["Input_Delay"] >> delay;
        interprate();
    }
    void interprate()
    {
        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())      // Check for valid input
                inputType = INVALID;
        else
        {//分摄像头输入,视频,照片序列三种输入情况
            if (input[0] >= '0' && input[0] <= '9')
            {
                stringstream ss(input);
                ss >> cameraID;
                inputType = CAMERA;
            }
            else
            {
		//原始版本在读取视频文件时报错,我修改了一下(因为FileStorage读取文件只支持.xml或者yml格式)
                /*if (readStringList(input, imageList))
                    {
                        inputType = IMAGE_LIST;
                        nrFrames = (nrFrames < (int)imageList.size()) ? nrFrames : (int)imageList.size();
                    }
                else
                    inputType = VIDEO_FILE;*/
		if(input.find(".mp4")!=string::npos || input.find(".avi")!=string::npos || input.find(".rmvb")!=string::npos || input.find(".wmv")!=string::npos)
			inputType = VIDEO_FILE;
		else if (readStringList(input, imageList))
			{
			inputType = IMAGE_LIST;
			nrFrames = (nrFrames < (int)imageList.size()) ? nrFrames : (int)imageList.size();
			} 
		else
		         inputType = INVALID;
            }
            if (inputType == CAMERA)
                inputCapture.open(cameraID);
            if (inputType == VIDEO_FILE)
                inputCapture.open(input);
            if (inputType != IMAGE_LIST && !inputCapture.isOpened())
                    inputType = INVALID;
        }
        if (inputType == INVALID)
        {
            cerr << " Inexistent input
评论 7
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值