草庐IT

OpenCV相机标定全过程

可为测控 2023-04-13 原文

一、OpenCV标定的几个常用函数


findChessboardCorners() 棋盘格角点检测

bool findChessboardCorners( InputArray image,
                                Size patternSize,
                                OutputArray corners,
                                int flags = CALIB_CB_ADAPTIVE_THRESH +
                                CALIB_CB_NORMALIZE_IMAGE );

第一个参数是输入的棋盘格图像(可以是8位单通道或三通道图像);

第二个参数是棋盘格内部的角点的行列数(注意:不是棋盘格的行列数,如棋盘格的行列数分别为4、8,而内部角点的行列数分别是3、7,因此这里应该指定为cv::Size(3, 7));

第三个参数是检测到的棋盘格角点,类型为std::vectorcv::Point2f。

第四个参数flag,用于指定在检测棋盘格角点的过程中所应用的一种或多种过滤方法,可以使用下面的一种或多种,如果都是用则使用OR:

cv::CALIB_CB_ADAPTIVE_THRESH:使用自适应阈值将图像转化成二值图像

cv::CALIB_CB_NORMALIZE_IMAGE:归一化图像灰度系数(用直方图均衡化或者自适应阈值)

cv::CALIB_CB_FILTER_QUADS:在轮廓提取阶段,使用附加条件排除错误的假设

cv::CALIB_CV_FAST_CHECK:快速检测

cv::drawChessboardCorners() 棋盘格角点的绘制

drawChessboardCorners( InputOutputArray image,

                           Size patternSize,
                           InputArray corners,
                           bool patternWasFound );

image为8-bit,三通道图像

patternSize,每一行每一列的角

corners,已经检测到的角

patternWasFound,findChessboardCorners的返回值


find4QuadCornerSubpix() 对粗提取的角点进行精确化

find4QuadCornerSubpix( InputArray img,
                           InputOutputArray corners,
                           Size region_size );

image源图像

corners,提供角点的初始坐标

region_size: 搜索窗口的一般尺寸

cornerSubPix() 亚像素检测

void cornerSubPix( InputArray image,
                       InputOutputArray corners,
                       Size winSize,
                       Size zeroZone,
                       TermCriteria criteria );

image源图像

corners,提供角点的初始坐标,返回更加精确的点

winSize,搜索窗口的一般尺寸,如果winSize=Size(5,5),则search windows为1111

winSize,死区的一般尺寸,用来避免自相关矩阵的奇点,(-1,-1)表示没有死区

criteria,控制迭代次数和精度


calibrateCamera()求解摄像机的内在参数和外在参数

double calibrateCamera( InputArrayOfArrays objectPoints,

                            InputArrayOfArrays imagePoints,
                            Size imageSize,
                            InputOutputArray cameraMatrix,
                            InputOutputArray distCoeffs,
                            OutputArrayOfArrays rvecs, OutputArrayOfArrays tvecs,
                            int flags = 0,
                            TermCriteria criteria = TermCriteria(TermCriteria::COUNT +
                            TermCriteria::EPS, 30, DBL_EPSILON) );

objectPoints,世界坐标,用vector,输入x,y坐标,z坐标为0

imagePoints,图像坐标,vector

imageSize,图像的大小用于初始化标定摄像机的image的size

cameraMatrix,内参数矩阵

distCoeffs,畸变矩阵

rvecs,位移向量

tvecs,旋转向量

flags,可以组合:


CV_CALIB_USE_INTRINSIC_GUESS:使用该参数时,将包含有效的fx,fy,cx,cy的估计值的内参矩阵cameraMatrix,作为初始值输入,然后函数对其做进一步优化。如果不使用这个参数,用图像的中心点初始化光轴点坐标(cx, cy),使用最小二乘估算出fx,fy(这种求法好像和张正友的论文不一样,不知道为何要这样处理)。注意,如果已知内部参数(内参矩阵和畸变系数),就不需要使用这个函数来估计外参,可以使用solvepnp()函数计算外参数矩阵。

CV_CALIB_FIX_PRINCIPAL_POINT:在进行优化时会固定光轴点,光轴点将保持为图像的中心点。当CV_CALIB_USE_INTRINSIC_GUESS参数被设置,保持为输入的值。

CV_CALIB_FIX_ASPECT_RATIO:固定fx/fy的比值,只将fy作为可变量,进行优化计算。

当CV_CALIB_USE_INTRINSIC_GUESS没有被设置,fx和fy的实际输入值将会被忽略,只有fx/fy的比值被计算和使用。

CV_CALIB_ZERO_TANGENT_DIST:切向畸变系数(P1,P2)被设置为零并保持为零。

CV_CALIB_FIX_K1,…,CV_CALIB_FIX_K6:对应的径向畸变系数在优化中保持不变。如果设置了CV_CALIB_USE_INTRINSIC_GUESS参数,就从提供的畸变系数矩阵中得到。否则,设置为0。

CV_CALIB_RATIONAL_MODEL(理想模型):启用畸变k4,k5,k6三个畸变参数。使标定函数使用有理模型,返回8个系数。如果没有设置,则只计算其它5个畸变参数。

CALIB_THIN_PRISM_MODEL (薄棱镜畸变模型):启用畸变系数S1、S2、S3和S4。使标定函数使用薄棱柱模型并返回12个系数。如果不设置标志,则函数计算并返回只有5个失真系数。

CALIB_FIX_S1_S2_S3_S4 :优化过程中不改变薄棱镜畸变系数S1、S2、S3、S4。如果cv_calib_use_intrinsic_guess设置,使用提供的畸变系数矩阵中的值。否则,设置为0。

CALIB_TILTED_MODEL (倾斜模型):启用畸变系数tauX and tauY。标定函数使用倾斜传感器模型并返回14个系数。如果不设置标志,则函数计算并返回只有5个失真系数。

CALIB_FIX_TAUX_TAUY :在优化过程中,倾斜传感器模型的系数不被改变。如果cv_calib_use_intrinsic_guess设置,从提供的畸变系数矩阵中得到。否则,设置为0。

initUndistortRectifyMap() 计算畸变参数

void initUndistortRectifyMap(InputArray cameraMatrix,
                                InputArray distCoeffs,
                                InputArray R,
                                InputArray newCameraMatrix,
                                Size size,
                                int m1type,
                                OutputArray map1,
                                OutputArray map2)

cameraMatrix,摄像机内参数矩阵

distCoeffs, 摄像机的5个畸变系数,(k1,k2,p1,p2[,k3[,k4,k5,k6]])

R,在客观空间中的转换对象

newCameraMatrix,新的33的浮点型矩矩阵

size,为失真图像的大小

m1type,第一个输出的map,类型为CV_32FC1或CV_16SC2

map1,x映射函数

map2,y映射函数


二、绘制棋盘格,拍摄照片这里自己画一个棋盘格用作标定,长度为1280像素,宽490像素,横向10方格,纵向7方格

std_cb = Vision::makeCheckerboard(1280, 490, 10, 7, 0,
(char )”../blizzard/res/calibration/std_cb.png”); 

效果如图


Vision是我个人创建的视觉类,可以用来绘制标准的棋盘格。

头文件vision.h

//

// Created by czh on 18-10-16.
//

#ifndef OPENGL_PRO_VISION_H
#define OPENGL_PRO_VISION_H

#include “opencv2/opencv.hpp”
#include 
#include 
#include 

#include “iostream”

class Vision {
public:
    static cv::Mat read(std::string file_path, int flags = cv::IMREAD_ANYCOLOR | cv::IMREAD_ANYDEPTH);

    static cv::Mat write(std::string file_path, int flags = cv::IMREAD_ANYCOLOR | cv::IMREAD_ANYDEPTH);

    static void dispConfig(cv::Mat img);

    static cv::Mat makeCheckerboard(int bkgWidth, int bkgHeight, int sqXnum, int sqYnum = 0, int borderThickness = 0, char savePath = NULL);
private:

};

#endif //OPENGL_PRO_VISION_H

用A4纸打印棋盘格,相机拍摄照片。

我偷懒,拿了别人的标定照片


三、相机标定

下面是相机标定代码

cv::imwrite(“../blizzard/res/calibration/cb_source.png”, cb_source);

    printf(“#Start scan corner\n”);
    cv::Mat img;
    std::vector image_points;
    std::vector> image_points_seq; / 保存检测到的所有角点 /
    if (cv::findChessboardCorners(cb_source, cv::Size(aqXnum, aqYnum), image_points, 0) == 0) {
        printf(“#Error: Corners not find “);
        return 0;
    } else {
        cvtColor(cb_source, img, CV_RGBA2GRAY);
        cv::imwrite(“../blizzard/res/calibration/cb_gray.png”, img);
        //find4QuadCornerSubpix(img, image_points, cv::Size(5, 5));

        cv::cornerSubPix(img, image_points, cv::Size(11, 11), cv::Size(-1, -1),
                         cv::TermCriteria(CV_TERMCRIT_ITER + CV_TERMCRIT_EPS, 30, 0.01));

        image_points_seq.push_back(image_points);

        cv::Mat cb_corner;
        cb_corner = cb_source.clone();
        drawChessboardCorners(cb_corner, cv::Size(aqXnum, aqYnum), image_points, true);
        cv::imwrite(“../blizzard/res/calibration/cb_corner.png”, cb_corner);
    }

    printf(“#Start calibrate\n”);
    cv::Size square_size = cv::Size(14.2222, 12);
    std::vector> object_points; / 保存标定板上角点的三维坐标 /
    cv::Mat cameraMatrix = cv::Mat(3, 3, CV_32FC1, cv::Scalar::all(0)); / 摄像机内参数矩阵 /
    cv::Mat distCoeffs = cv::Mat(1, 5, CV_32FC1, cv::Scalar::all(0)); / 摄像机的5个畸变系数:k1,k2,p1,p2,k3 /
    std::vector tvecsMat;  / 每幅图像的旋转向量 /
    std::vector rvecsMat;  / 每幅图像的平移向量 /

    std::vector realPoint;
    for (int i = 0; i < aqYnum; i++) { for (int j = 0; j < aqXnum; j++) { cv::Point3f tempPoint; / 假设标定板放在世界坐标系中z=0的平面上 /
            tempPoint.x = i  square_size.width;
            tempPoint.y = j  square_size.height;
            tempPoint.z = 0;
            realPoint.push_back(tempPoint);
        }
    }
    object_points.push_back(realPoint);

    printf(“#objectPoints: %ld\n”, sizeof(object_points[0]));
    std::cout << object_points[0] << std::endl; printf(“#image_points: %ld\n”, sizeof(image_points_seq[0])); std::cout << image_points << std::endl; printf(“#image size\n”); std::cout << SCREEN_WIDTH << “*” << SCREEN_HEIGHT << std::endl; cv::calibrateCamera(object_points, image_points_seq, cb_source.size(), cameraMatrix, distCoeffs, rvecsMat, tvecsMat, CV_CALIB_FIX_K3); std::cout << “tvecsMat:\n” << tvecsMat[0] << std::endl; std::cout << “rvecsMat:\n” << rvecsMat[0] << std::endl; std::cout << “#cameraMatrix:\n” << cameraMatrix << std::endl; std::cout << “#distCoeffs:\n” << distCoeffs << std::endl; 

四、对图片进行校正

cv::Mat cb_final;

    cv::Mat mapx = cv::Mat(cb_source.size(), CV_32FC1);
    cv::Mat mapy = cv::Mat(cb_source.size(), CV_32FC1);
    cv::Mat R = cv::Mat::eye(3, 3, CV_32F);
    //initUndistortRectifyMap(cameraMatrix, distCoeffs, R, cv::Mat(), cb_source.size(), CV_32FC1,
    //                        mapx, mapy);
    //cv::remap(cb_source, cb_final, mapx, mapy, cv::INTER_LINEAR);

    undistort(cb_source, cb_final, cameraMatrix, distCoeffs);

    cv::imwrite(“../blizzard/res/calibration/cb_final.png”, cb_final);

1.校正前的图片

2.校正后的图片

有关OpenCV相机标定全过程的更多相关文章

  1. Vscode+Cmake配置并运行opencv环境(Windows和Ubuntu大同小异) - 2

    之前在培训新生的时候,windows环境下配置opencv环境一直教的都是网上主流的vsstudio配置属性表,但是这个似乎对新生来说难度略高(虽然个人觉得完全是他们自己的问题),加之暑假之后对cmake实在是爱不释手,且这样配置确实十分简单(其实都不需要配置),故斗胆妄言vscode下配置CV之法。其实极为简单,图比较多所以很长。如果你看此文还配不好,你应该思考一下是不是自己的问题。闲话少说,直接开始。0.CMkae简介有的人到大二了都不知道cmake是什么,我不说是谁。CMake是一个开源免费并且跨平台的构建工具,可以用简单的语句来描述所有平台的编译过程。它能够根据当前所在平台输出对应的m

  2. [工业相机] 分辨率、精度和公差之间的关系 - 2

    📢博客主页:https://blog.csdn.net/weixin_43197380📢欢迎点赞👍收藏⭐留言📝如有错误敬请指正!📢本文由Loewen丶原创,首发于CSDN,转载注明出处🙉📢现在的付出,都会是一种沉淀,只为让你成为更好的人✨文章预览:一.分辨率(Resolution)1、工业相机的分辨率是如何定义的?2、工业相机的分辨率是如何选择的?二.精度(Accuracy)1、像素精度(PixelAccuracy)2、定位精度和重复定位精度(RepeatPrecision)三.公差(Tolerance)四.课后作业(Post-ClassExercises)视觉行业的初学者,甚至是做了1~2年

  3. ruby - Ruby 中的 block 和过程 - 2

    我已经开始学习Ruby,我已经阅读了一些教程,甚至还买了一本书(“ProgrammingRuby1.9-ThePragmaticProgrammers'Guide”),我遇到了一些以前从未见过的新东西使用我知道的任何其他语言(我是一名PHP网络开发人员)。block和过程。我想我明白它们是什么,但我不明白的是为什么它们如此伟大,以及我应该在何时何地使用它们。我到处都看到他们说block和过程是Ruby中的一个很棒的特性,但我不理解它们。这里有人能给像我这样的Ruby新手一些解释吗? 最佳答案 block有很多好处。电梯演讲:bloc

  4. ruby-on-rails - Rails 中 View 的解析过程 - 2

    rails中View的解析过程是怎样的?我对View中erb标记中原始html与ruby​​代码的解析顺序部分感兴趣。我认为这是View代码被解析并最终发送给请求者的顺序:Controller调用ViewView代码从上到下解析当Rails在解析过程中遇到erb标记时:rails解析它并将结果附加到解析的html(这包括erb标签引用助手)一旦整个View被解析,整体结果将发送给请求者这似乎并非如此。看来View代码会扫描任何erb片段并首先解析那些片段(包括对助手的引用)。之后,rails然后从上到下解析所有View代码并将结果发送给请求者。以这个View为例:#_form.html

  5. Cesium源码解析一(terrain文件的加载、解析与渲染全过程梳理) - 2

    快速导航(持续更新中…)Cesium源码解析一(terrain文件的加载、解析与渲染全过程梳理)Cesium源码解析二(metadataAvailability的含义)Cesium源码解析三(metadata元数据拓展中行列号的分块规则解析)Cesium源码解析四(Quantized-Mesh(.terrain)格式文件在CesiumJS和UE中加载情况的对比)目录1.前言2.本篇的由来3.terrain文件的加载3.1更新环境3.2更新和执行渲染命令3.3数据优化3.4结束当前帧4.总结1.前言  目前市场上三维比较火的实现方案主要有两种,b/s的方案主要是Cesium,c/s的方案主要是u

  6. 计算机网络笔记:TCP三次握手和四次挥手过程 - 2

    TCP是面向连接的协议,连接的建立和释放是每一次面向连接的通信中必不可少的过程。TCP连接的管理就是使连接的建立和释放都能正常地进行。三次握手TCP连接的建立—三次握手建立TCP连接①若主机A中运行了一个客户进程,当它需要主机B的服务时,就发起TCP连接请求,并在所发送的分段中用SYN=1表示连接请求,并产生一个随机发送序号x,如果连接成功,A将以x作为其发送序号的初始值:seq=x。主机B收到A的连接请求报文,就完成了第一次握手。客户端发送SYN=1表示连接请求客户端发送一个随机发送序号x,如果连接成功,A将以x作为其发送序号的初始值:seq=x②主机B如果同意建立连接,则向主机A发送确认报

  7. 关于如何为 PostgreSQL 编写存储过程的 Ruby 教程? - 2

    听说PostgreSQL的可以用Ruby写存储过程但我一直没能找到更多关于它的信息,教人们如何实际去做。有人可以为此推荐好的资源。谢谢 最佳答案 显然,您需要安装PL/Ruby。之后,你可以写:CREATEFUNCTIONruby_max(int4,int4)RETURNSint4AS'ifargs[0].to_i>args[1].to_ireturnargs[0]elsereturnargs[1]end'LANGUAGE'plruby';查看其GitHubrepository安装说明。

  8. ruby - ruby 中的过程和数据抽象 - 2

    我是Ruby新手。我正在学习ruby​​中的抽象原则。据我了解,过程抽象是对用户隐藏实现细节,或者只是专注于要点而忽略细节。我关心的是如何实现它1)是不是一个简单的函数调用就这样#functiontosortarray#@paramsarray[Array]tobesortdefmy_sort(array)returnarrayifarray.sizearray[i+1]array[i],array[i+1]=array[i+1],array[i]swapped=trueendendendarrayend然后这样调用sorted_array=my_sort([12,34,123,43,

  9. ruby - 如何将 Interactive Ruby 整合到我的开发过程中? - 2

    我正在尝试找到一种更好的方法将IRB与我的常规ruby​​开发集成。目前我很少在我的代码中使用IRB。我只用它来验证语法或尝试一些小的东西。我知道我可以将我自己的代码加载到ruby​​中作为一个require'mycode'但这通常不符合我的编程风格。有时我要检查的变量超出范围或在循环内。有没有一种简单的方法可以启动我的脚本并在IRB内的某个点卡住?我想我正在寻找一种更简单的方法来调试我的ruby​​代码而不破坏我的F5(编译)键。也许有经验的ruby开发者可以和我分享一个更精简的开发方法。 最佳答案 安装ruby​​-debugg

  10. 相机校准—外参矩阵 - 2

    在本文中,我们将探讨摄影机的外参,并通过Python中的一个实践示例来加强我们的理解。相机外参摄像头可以位于世界任何地方,并且可以指向任何方向。我们想从摄像机的角度来观察世界上的物体,这种从世界坐标系到摄像机坐标系的转换被称为摄像机外参。那么,我们怎样才能找到相机外参呢?一旦我们弄清楚相机是如何变换的,我们就可以找到从世界坐标系到相机坐标系的基变换的变化。我们将详细探讨这个想法。具体来说,我们需要知道相机是如何定位的,以及它在世界空间中的位置,有两种转换可以帮助我们:有助于确定摄影机方向的旋转变换。有助于移动相机的平移变换。让我们详细看看每一个。旋转通过旋转改变坐标让我们看一下将点旋转一个角度

随机推荐