OpenCV学习笔记(七) 相机标定的函数理解与学习

Opencv实现张正友法相机标定

相机标定的目的:获取摄像机的内参和外参矩阵(同时也会得到每一幅标定图像的选择和平移矩阵),内参和外参系数可以对之后相机拍摄的图像就进行矫正,得到畸变相对很小的图像。

相机标定的输入:标定图像上所有内角点的图像坐标,标定板图像上所有内角点的空间三维坐标(一般情况下假定图像位于Z=0平面上)。

相机标定的输出:摄像机的内参、外参系数。

矫正原始图像的完整流程:

1. 准备标定图片

2. 对每一张标定图片,提取角点信息

3. 对每一张标定图片,进一步提取亚像素角点信息

4. 在棋盘标定图上绘制找到的内角点(非必须,仅为了显示)

5. 相机标定

6. 对标定结果进行评价

7. 查看标定效果——利用标定结果对棋盘图进行矫正

1. 准备标定图片

标定图片需要使用标定板在不同位置、不同角度、不同姿态下拍摄,最少需要3张,以10~20张为宜。标定板需要是黑白相间的矩形构成的棋盘图,制作精度要求较高

2.对每一张标定图片,提取角点信息

需要使用findChessboardCorners函数提取角点,这里的角点专指的是标定板上的内角点,这些角点与标定板的边缘不接触。

findChessboardCorners

CV_EXPORTS_W bool findChessboardCorners( InputArray image, Size patternSize,

                                         OutputArray corners,

                                         int flags=CALIB_CB_ADAPTIVE_THRESH+CALIB_CB_NORMALIZE_IMAGE );

第一个参数Image,传入拍摄的棋盘图Mat图像,必须是8位的灰度或者彩色图像;

第二个参数patternSize,每个棋盘图上内角点的行列数,一般情况下,行列数不要相同,便于后续标定程序识别标定板的方向;

第三个参数corners,用于存储检测到的内角点图像坐标位置,一般用元素是Point2f的向量来表示:vector<Point2f> image_points_buf;

第四个参数flage:用于定义棋盘图上内角点查找的不同处理方式,有默认值。

通过调用cvFindChessboardCorners实现

CvCBQuad用来存储棋盘上的方格信息,CvCBCorner用来存储棋盘方格的四个角点

标识符:CV_CALIB_CB_NORMALIZE_IMAGE  是否进行灰度直方图均衡化

        CV_CALIB_CB_FAST_CHECK    检查是否有要找的棋盘格

        CV_CALIB_CB_ADAPTIVE_THRESH  是否进行自适应阈值

         CV_CALIB_CB_FILTER_QUADS  是否使用其他的准则(如轮廓面积,周长,方形形状)来去除在轮廓检测阶段检测到的错误方块。

1. 将图片从RGB转为GRAY,并进行灰度直方图均衡化cvEqualizeHist,并检查是否有要找的棋盘格cvCheckChessboard  要求:图像为8bit,3通道或单通道

对于下列的操作,如未找到角点,会重复进行6次,在每一次中,图像膨胀的核的大小会不断增大。

2. 根据标识符进行图像二值化,并进行图像膨胀,使用cvStartFindContours寻找轮廓,排除面积小于25的轮廓,并对每个轮廓多次进行多边形逼近cvApproxPoly,获取四边形。之后判断轮廓是否为凸边形cvCheckContourConvexity,最后通过面积、边长进一步筛选。

3. 寻找每个方格的相邻方格,并记相邻方格的个数,连同相邻方格的信息存在相应CvCBQuad结构体中。二值图像在膨胀后原本相邻的方格,分开了,原来相连部分有一个公共点,现在分开变成了两个点。找到相邻的方格之后,计算出原来的公共点,用公共点替代膨胀后分开的点。这主要由icvFindQuadNeighborhors函数完成。

4. 对所有“方格”(包括被误判的)分类,分类的原则是类内所有方格是相邻的。由icvFindConnectedQuads函数完成。

5. 根据已知所求的角点个数,判别每个类中方格是否为所求的棋盘方格,并对棋盘方格排序,即该方格位于哪行那列。在这个过程中,可以添加每类方格总缺少的方格,也可以删除每类方格中多余的方格。icvOrderFoundConnetedQuads函数完成该过程。

6. icvCleanFoundConnectedQuads函数、icvCheckQuadGroup函数根据已知棋盘的方格个数(由棋盘的角点数计算出来)确认方格位置及个数是否正确,并确定粗略强角点的位置(两个方格的相连位置)icvCheckBoardMonotony再次检验棋盘方格是否提取正确。icvCheckBoardMonotony确定每一行中的点在以该行为直线的圆内,同理对每一列进行判断。

7. 以上如果有一步所有方格都不符合要求,则进入一个新的循环。若循环结束,还尚未找到符合要求的方格,则棋盘定位失败,退出函数。最后,调用cvFindCornerSubpix()根据上步的强角点位置,确定强角点的精确位置。

 

3. 对每一张标定图片,进一步提取亚像素角点信息

为了提高标定精度,需要在初步提取的角点信息上进一步提取亚像素信息,降低相机标定偏差,常用的方法是cornerSubPix,另一个方法是使用find4QuadCornerSubpix函数,这个方法是专门用来获取棋盘图上内角点的精确位置的。

find4QuadCornerSubpix

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

第一个参数img,输入的Mat矩阵,最好是8位灰度图像,检测效率更高;

第二个参数corners,初始的角点坐标向量,同时作为亚像素坐标位置的输出,所以需要是浮点型数据,一般用元素是Pointf2f/Point2d的向量来表示:vector<Point2f/Point2d> iamgePointsBuf

第三个参数region_size,角点搜索窗口的尺寸;

对每个角点进行如下操作

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

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

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

4. 以每幅图最小的两个轮廓为研究对象,先进行多边形逼近approxPolyDP,并使用findCorner找到轮廓中到该角点最近的点

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

4. 在棋盘标定图上绘制找到的内角点(非必须,仅为了显示)

drawChessboardCorners

CV_EXPORTS_W void drawChessboardCorners( InputOutputArray image, Size patternSize,  

                                         InputArray corners, bool patternWasFound );  

第一个参数image8位灰度或者彩色图像;

第二个参数patternSize,每张标定棋盘上内角点的行列数;

第三个参数corners,初始的角点坐标向量,同时作为亚像素坐标位置的输出,所以需要是浮点型数据,一般用元素是Pointf2f/Point2d的向量来表示:vector<Point2f/Point2d> iamgePointsBuf

第四个参数patternWasFound,标志位,用来指示定义的棋盘内角点是否被完整的探测到,true表示别完整的探测到,函数会用直线依次连接所有的内角点,作为一个整体,false表示有未被探测到的内角点,这时候函数会以(红色)圆圈标记处检测到的内角点;

patternWasFound=ture时,依次连接各个内角点

patternWasFound=false时,以(红色)圆圈标记处角点位置

通过调用drawChessboardCorners实现

5. 相机标定

获取到棋盘标定图的内角点图像坐标之后,就可以使用calibrateCamera函数进行标定,计算相机内参和外参系数

calibrateCamera

CV_EXPORTS_W double calibrateCamera( InputArrayOfArrays objectPoints,  

                                     InputArrayOfArrays imagePoints,  

                                     Size imageSize,  

                                     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) );  

第一个参数objectPoints,为世界坐标系中的三维点。在使用时,应该输入一个三维坐标点的向量的向量,即vector<vector<Point3f>> object_points。需要依据棋盘上单个黑白矩阵的大小,计算出(初始化)每一个内角点的世界坐标。

第二个参数imagePoints,为每一个内角点对应的图像坐标点。和objectPoints一样,应该输入vector<vector<Point2f>> image_points_seq形式的变量;

第三个参数imageSize,为图像的像素尺寸大小,在计算相机的内参和畸变矩阵时需要使用到该参数;

第四个参数cameraMatrix为相机的内参矩阵。输入一个Mat cameraMatrix即可,如Mat cameraMatrix=Mat(3,3,CV_32FC1,Scalar::all(0));

第五个参数distCoeffs为畸变矩阵。输入一个Mat distCoeffs=Mat(1,5,CV_32FC1,Scalar::all(0))即可;

第六个参数rvecs为旋转向量;应该输入一个Mat类型的vector,即vector<Mat>rvecs;

第七个参数tvecs为位移向量,和rvecs一样,应该为vector<Mat> tvecs

第八个参数flags为标定时所采用的算法。有如下几个参数:

CV_CALIB_USE_INTRINSIC_GUESS:使用该参数时,在cameraMatrix矩阵中应该有fx,fy,u0,v0的估计值。否则的话,将初始化(u0,v0)图像的中心点,使用最小二乘估算出fxfy

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

CV_CALIB_FIX_ASPECT_RATIO:固定fx/fy的比值,只将fy作为可变量,进行优化计算。当CV_CALIB_USE_INTRINSIC_GUESS没有被设置,fxfy将会被忽略。只有fx/fy的比值在计算中会被用到。

CV_CALIB_ZERO_TANGENT_DIST:设定切向畸变参数(p1,p2)为零。

CV_CALIB_FIX_K1,,CV_CALIB_FIX_K6:对应的径向畸变在优化中保持不变。

CV_CALIB_RATIONAL_MODEL:计算k4k5k6三个畸变参数。如果没有设置,则只计算其它5个畸变参数。

第九个参数criteria是最优迭代终止条件设定。

在使用该函数进行标定运算之前,需要对棋盘上每一个内角点的空间坐标系的位置坐标进行初始化,标定的结果是生成相机的内参矩阵cameraMatrix、相机的5个畸变系数distCoeffs,另外每张图像都会生成属于自己的平移向量和旋转向量。

通过调用cvCalibrateCamera2实现

1. 使用cvConvertPointsHomogeneous将图像坐标点与世界坐标点转换为齐次坐标

2. 对于每一张图片,使用cvFindHomography计算单应矩阵

3. 根据约束条件,计算出相机内参

4. 使用cvFindExtrinsicCameraParams2计算相机外参

5. 通过最小二乘法估算,实际存在径向畸变下的畸变系数

6. 极大似然法,优化估计,提升估计精度

6. 对标定结果进行评价

对标定结果进行评价的方法是通过得到的摄像机内外参数,对空间的三维点进行重新投影计算,得到空间三维点在图像上新的投影点的坐标,计算投影坐标和亚像素角点坐标之间的偏差,偏差越小,标定结果越好。

对空间三维坐标点进行反向投影的函数是projectPoints

projectPoints

void cv::projectPoints( InputArray _opoints,

                        InputArray _rvec,

                        InputArray _tvec,

                        InputArray _cameraMatrix,

                        InputArray _distCoeffs,

                        OutputArray _ipoints,

                        OutputArray _jacobian,

                        double aspectRatio )

_points

物体点的坐标,为3xN或者Nx3的矩阵,这儿N是视图中的所有所有点的数目。

_rvec

旋转向量,1x3或者3x1。

_tvec

平移向量,1x3或者3x1。

_cameraMatrix

摄像机内参数矩阵A:

_distCoeffs

形变参数向量,4x1或者1x4,为[k1,k2,p1,p2]。如果是NULL,所有形变系数都设为0。

_ipoints

输出数组,存储图像点坐标。大小为2xN或者Nx2,这儿N是视图中的所有点的数目。

_jacobian

可选参数Nx10矩阵从上到下分为4个部分。

dpdrot

关于旋转向量部分的图像上点的导数,Nx3矩阵。

dpdt

关于平移向量部分的图像上点的导数,Nx3矩阵。

dpdf

关于fx和fy的图像上点的导数,Nx2矩阵。

dpdc

关于cx和cy的图像上点的导数,Nx2矩阵。

aspectRatio

可选参数,关于形变系数的图像上点的导数,Nx4矩阵。

通过调用cvProjectPoints2实现

Norm范数

定义1. 设 ,满足

1. 正定性:║x║≥0,║x║=0 iff x=0

2. 齐次性:║cx║=│c│║x║,

3. 三角不等式:║x+y║≤║x║+║y║

则称Cn中定义了向量范数,║x║为向量x的范数.

可见向量范数是向量的一种具有特殊性质的实值函数.

常用向量范数有,令x=( x1,x2,…,xn)T

1-范数:║x║1=│x1│+│x2│+…+│xn│    //向量中的绝对值求和,

2-范数:║x║2=(│x1│2+│x2│2+…+│xn│2)^1/2   //就是欧几里得距离,其跟勾股定理不太一样

∞-范数:║x║∞=max(│x1│,│x2│,…,│xn│)  //向量中的maxm范数求解函数

1.   #define CV_C            1          //[1]先对数组中的所有元素取绝对值,然后返回这些绝对值之间最大的那个数,作为cvNorm()函数的返回值  

2.         #define CV_L1           2          //[2]先对数组中的所有元素取绝对值,然后对这些数进行绝对值累加----绝对值的累加和  

3.         #define CV_L2           4          //[3]数组中所有元素平方的累加和  

计算新的投影点和旧的投影点之间的误差

7. 查看标定效果——利用标定结果对棋盘图进行矫正

利用求得的相机的内参和外参数据,可以对图像进行畸变的矫正,这里有两种方法可以达到矫正的目的,分别说明一下。

方法一:使用initUndistortRectifyMapremap两个函数配合实现。

initUndistortRectifyMap用来计算畸变映射,remap把求得的映射应用到图像上。

方法二:使用undistort函数实现

正向矫正的流程为:畸变像素坐标畸变物理坐标标准物理坐标标准像素坐标

逆向矫正的流程为:标准像素坐标标准物理坐标畸变物理坐标畸变像素坐标

UndistortPoints就是执行的正向矫正过程,而initUndistortRectifyMap执行的是逆向矫正过程。

initUndistortRectifyMap

cv::initUndistortRectifyMap( InputArray _cameraMatrix, InputArray _distCoeffs,

                              InputArray _matR, InputArray _newCameraMatrix,

                              Size size, int m1type, OutputArray _map1, OutputArray _map2 )

猜你喜欢

转载自blog.csdn.net/xuxunjie147/article/details/79219774