目录

棋盘格标定

Matlab相机工具包计算相机内参:详情参考链接

OpenCV函数调用:

实心圆点标定

相机标定

标定结果-重投影误差

完整代码展示


在相机内参标定中,采用二维靶标标定主要分为两种方式:棋盘格标定、实心圆点标定。注意棋盘格和实心圆点在标定过程中注意保持清晰,且与水平(竖直)成一定角度15-30°,实验所得,仅供参考。

棋盘格标定

棋盘格标定相机内参主要采用两种方法:Matlab的相机工具包、OpenCV函数findChessboardCorner函数调用。

Matlab相机工具包计算相机内参:详情参考链接

OpenCV函数调用:

findChessboardCorners参数设置,该函数检测鲁棒性较差,提取易失败,不同背景下的提取效果也不同,背景白色较多时提取成功率较高

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

关键参数:

OutputArray corners–

提取的棋盘格角点像素坐标,数据类型为vector<vector>;

int flags–

滤波处理:CALIB_CB_ADAPTIVE_THRESH自适应阈值法+CALIB_CB_NORMALIZE_IMAGE归一化(默认值)

find4QuadCornerSubpix参数设置,亚像素坐标提取函数

bool cv::find4QuadCornerSubpix  (  InputArray  img,             //8位灰度图  InputOutputArray  corners,   //保存计算所得亚像素坐标,数据类型同findChessboardCorners  Size  region_size            //设置搜索框尺寸 )  

Size region_size —

角点搜索具体操作:

1. 根据窗口尺寸,确定以该角点为中心的ROI区域,并计算该区域的直方图

2. 找出直方图中最大的一段segment,以此进行图像二值化,之后进行腐蚀操作,共进行两次

3. 对两幅图使用findContours进行轮廓提取,对于轮廓根据到该角点的最小距离排序

4. 以每幅图最小的两个轮廓为研究对象,先进行多边形逼近approxPolyDP(道格拉斯普克算子),并使用findCorner找到轮廓中到该角点最近的点

5. 对找到的四个点使用findLinesCrossPoint,获取精确的角点位置

drawChessboardCorners函数,角点绘制函数

void cv::drawChessboardCorners  (   InputOutputArray  image,    Size  patternSize,      //棋盘格行列数(下角有标注)  InputArray  corners,      bool  patternWasFound   //true检测到棋盘,将corners连接起来;                          //false未检测到棋盘,将检测到的角点用红圈标注 )  

具体实现参考点云侠-OpenCV——单目视觉:方形标定板角点提取

实心圆点标定

实心圆点标定板又分为对称型和非对称型,本文主要探讨对称型。

findCircleGrid参数设置,源码实质是调用findCIirclesGrid2函数(这里不做解析);

bool findCirclesGrid( InputArray _image, Size patternSize, OutputArray _centers, int flags, const Ptr &blobDetector){  return cv::findCirclesGrid2(_image, patternSize, _centers,   flags, blobDetector, CirclesGridFinderParameters2());}

关键参数:

int flags—

对称型圆心 cv::CALIB_CB_SYMMETRIC_GRID;

非对称型圆心 cv::CALIB_CB_ASYMMETRIC_GRID;

cv::CALIB_CB_CLUSTEREING透镜失真严重时使用 通常用于宽视野相机(用“|”与对称型和非对称型配合使用)

const Ptr &blobDetector:创建用于检测圆的智能指针(方法Blob算子)

对于大图片一定要设置检测圆面积阈值,本文图片分辨率为5472×3648

相机标定

calibrateCamera函数,相机标定函数,用于标定相机内外参数

CV_EXPORTS_W double calibrateCamera(       InputArrayOfArrays objectPoints,      InputArrayOfArrays imagePoints,      CV_OUT InputOutputArray cameraMatrix,      CV_OUT InputOutputArray distCoeffs,      OutputArrayOfArrays rvecs,       OutputArrayOfArrays tvecs,      int flags=0, TermCriteria criteria = TermCriteria(TermCriteria::COUNT+TermCriteria::EPS, 30, DBL_EPSILON) );

函数的12个参数(一般设置前7个参数即可,后面默认。
1.objectPoints :世界坐标系中的点。在使用时,应该输入vector< vector >。
2.imagePoints :其对应的图像点。和objectPoints一样,应该输入vector< vector >型的变量。
3.imageSize :图像的大小,在计算相机的内参数和畸变矩阵需要用到;
4.cameraMatrix :内参数矩阵。输入一个Mat cameraMatrix即可。
5.distCoeffs :畸变矩阵。输入一个Mat distCoeffs即可。
6.rvecs :旋转向量;应该输入一个Mat的vector,即vector rvecs因为每个vector会得到一个rvecs。
7.tvecs :位移向量;和rvecs一样,也应该为vector tvecs。
8.stdDeviationsIntrinsics :内参数的输出向量。输出顺序为: (fx,fy,cx,cy,k1,k2,p1,p2,k3,k4,k5,k6,s1,s2,s3,s4,τx,τy) ,如果不估计其中某一个参数,值等于0
9.stdDeviationsExtrinsics :外参数的输出向量。输出顺序: (R1,T1,…,RM,TM) ,M是标定图片的个数, Ri,Ti 是1×3的向量 。
10.perViewErrors 每个标定图片的重投影均方根误差的输出向量。
11.criteria: 迭代优化算法的终止准则
12.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。

vector —-用于存放float类型的数据,这里是二维点向量,也可以将Point2f换成int等其他类型;
vector —-用于存放int类型的数据;
vector —-用于存放double类型的数据;
vector<vector> points;—-表示定义一个二维数组,
其中的points[0].size(),表示第一行的列数;points[0]第一行。ponts[1]第二行

Vec4f为二维直线类型;Vec6f为三维直线类型
Vec4f line ; line[0].line[1]存放的是直线方向向量,斜率为line[1]/line[0] point.x=line[2];point.y=line[3];

标定结果-重投影误差

注意:重投影误差reprojectionError不能完全作为标定精度的评判标准,重投影误差结果的好坏与图像大小,图像质量等等有关。

建议:通过标定的内外参数计算角点/圆心靶标坐标系下坐标与实际坐标(角点间距、圆点中心距已知)进行比较。

关键函数:projectPoints函数

void projectPoints(    InputArray objectPoints,          //靶标坐标系角点坐标    InputArray rvec,                  //外参旋转向量    InputArray tvec,                  //外参平移向量    InputArray cameraMatrix,          //内参矩阵    InputArray distCoeffs,            //畸变系数    OutputArray imagePoints,          //输出像素坐标(重新投影)    OutputArray jacobian=noArray(),   //雅可比行列式    double aspectRatio=0 )

雅可比行列式不做解释,没整明白(以后补充,欢迎大佬解释),一般参数设置到第6个即可

完整代码展示

structure_light .h

// use for calibration#ifndef STRUCTURE_LIGHT_H#define STRUCTURE_LIGHT_H#include "struct_light_calib.h"class structure_light {public:structure_light(int x, cv::Size patternSize, cv::Size patternLength);cv::Mat cameraMatrix;cv::Mat distCoeffs;/*cv::Mat cam2laser;*/std::vector R;std::vector T;std::vector isRecognize;std::vector<std::vector> calibBoardPoint;std::vector<std::vector> calibImagePoint;std::vector<std::vector>featureOriImagePoint;//特征点的像素中心点坐标std::vector<std::vector>featureSubImagePoint;//特征点的亚像素中心点坐标std::vector laserPoints;//特征点的三维坐标int imageCount;cv::Size patternSize;cv::Size PatternLength;};#endif

structure_light.cpp

// use for calibration#include "structure_light.h"#include "struct_light_calib.h"//函数定义构造函数structure_lightstructure_light::structure_light(int x, cv::Size patternSize, cv::Size patternLength) {structure_light::imageCount = x;structure_light::patternSize = patternSize;structure_light::PatternLength = patternLength;}

struct_light_calib.h

#ifndef TTTTT_STRUCT_LIGHT_CALIB_H#define TTTTT_STRUCT_LIGHT_CALIB_H#include #include #include //#include "structure_light.h"using namespace std;//using namespace cv;class structure_light;int cameraCalib(structure_light &a, double &reprojectionError);#endif //TTTTT_STRUCT_LIGHT_CALIB_H

struct_light_calib.cpp

//摄像机标定int cameraCalib(structure_light &a, double &reprojectionError){string format = ".jpg";for (int i = 0; i < a.imageCount; i++){string index = to_string(i);string name = "./calib_picture/calib" + index + format;cv::Mat pic = cv::imread(name);/*Mat greyImage;cvtColor(pic, greyImage, COLOR_BGR2GRAY);*/bool result = true;vector targetPoint;//圆形靶标提取圆心点if (iscircle){//图像尺寸大,需要对检测圆面积的阈值进行设定cv::SimpleBlobDetector::Params params;params.maxArea = 90000;params.minArea = 500;params.filterByArea = true;cv::Ptr blobDetector = cv::SimpleBlobDetector::create(params);if (0 == findCirclesGrid(pic, a.patternSize, targetPoint, cv::CALIB_CB_SYMMETRIC_GRID | cv::CALIB_CB_CLUSTERING, blobDetector))//提取靶标上圆斑的圆心{result = false;a.isRecognize.push_back(result);cout << "false-calib" << endl;continue;}else{result = true;a.isRecognize.push_back(result);a.calibImagePoint.push_back(targetPoint);cout << "true-calib" << endl;}}//棋盘格靶标提取角点else{if (0 == findChessboardCorners(pic, a.patternSize, targetPoint)){result = false;a.isRecognize.push_back(result);cout << "false-calib" << endl;continue;}else{result = true;a.isRecognize.push_back(result);find4QuadCornerSubpix(pic, targetPoint, cv::Size(5, 5));a.calibImagePoint.push_back(targetPoint);drawChessboardCorners(pic, patternSize, targetPoint, true);cout << "true-calib" << endl;imwrite("./save/Corner.jpg", pic);}}}a.generateCalibboardPoint();//自定义的标定板原点靶标坐标/* 摄像头标定得到相机内参和畸变矩阵以及外参(旋转矩阵R和平移矩阵T)*/double rms = calibrateCamera(a.calibBoardPoint, a.calibImagePoint, image_size, a.cameraMatrix, a.distCoeffs, a.R, a.T);cout << "Reprojection error:" << rms << endl;a.Rw = a.R[0];a.Tw = a.T[0];/* 重投影评估单目摄像头标定精度 */double err = 0.0;double mean_err = 0.0;double total_err = 0.0;int i;vector reprojectionPoint;for (i = 0; i < a.calibBoardPoint.size(); i++){vector tempPointSet = a.calibBoardPoint[i];//标定板圆点靶标三维坐标/*cout << "标定板圆点三维坐标" << a.calibBoardPoint[i] << endl;*//* 通过得到的摄像机内外参数,对空间的三维点进行重新投影计算,得到新的投影点 */projectPoints(tempPointSet, a.R[i], a.T[i], a.cameraMatrix, a.distCoeffs, reprojectionPoint);/* 计算新的投影点和旧的投影点之间的误差*/vector tempImagePoint = a.calibImagePoint[i];cv::Mat tempImagePointMat = cv::Mat(1, tempImagePoint.size(), CV_32FC2);//旧投影点(49个点)cv::Mat image_points2Mat = cv::Mat(1, reprojectionPoint.size(), CV_32FC2);//新投影点(49个点(畸变校正后))for (int j = 0; j < tempImagePoint.size(); j++){image_points2Mat.at(0, j) = cv::Vec2f(reprojectionPoint[j].x, reprojectionPoint[j].y);tempImagePointMat.at(0, j) = cv::Vec2f(tempImagePoint[j].x, tempImagePoint[j].y);}err = norm(image_points2Mat, tempImagePointMat, cv::NORM_L2);mean_err = err / (a.patternSize.width*a.patternSize.height);total_err += mean_err;}reprojectionError = total_err / a.calibBoardPoint.size();return 0;}

主函数

#include "structure_light.h"#include "struct_light_calib.h"#include #include #include   #include #include #include #include #include #include //定义一些全局变量作为标定的靶标参数int imageCount = 11;//图像的数量cv::Size patternSize(7, 7);//靶标(圆的数量)cv::Size2f patternLength(15.0, 15.0);//两个圆形标记之间的距离cv::Size image_size(5472,3468);//patternType://0:circle;//1:chessboard;bool iscircle = true;int main(){    //structure_light为自定义类,参数可自行定义放入函数即可    structure_light lineStructureLight(imageCount, patternSize, patternLength);    double reprojectionError = 0.0;    cameraCalib(lineStructureLight, reprojectionError);//摄像机标定    cout << "Camera calibration reprojection error: " << reprojectionError << " pixels." << endl;    return 0;}