多语言展示
当前在线:1417今日阅读:86今日分享:14

OpenCV:相机标定函数和程序

绪:OpenCV相机标定采用张正友标定法,以棋盘格为工具,以棋盘角点图像像素坐标和棋盘角点世界坐标系坐标为输入,输出相机的内参、外参。本经验主要介绍:(1)棋盘角点图像像素坐标系坐标;(2)棋盘角点世界坐标系坐标;(3)相机标定;(4)相机标定结果评价;(5)图像畸变矫正;(6)标定程序;
工具/原料

OpenCV2410

方法/步骤
1

棋盘角点图像像素坐标步骤如下:①提取棋盘格图像内角点-粗角点坐标;③对粗角点坐标进行亚像素角点提取;④在棋盘标定图上绘制找到的亚像素内角点(仅显示内角点);①棋盘格内角点提取函数findChessboardCorners:bool findChessboardCorners( InputArray image,Size patternSize,  //内角点行列数OutputArray corners,          int flags=CALIB_CB_ADAPTIVE_THRESH+CALIB_CB_NORMALIZE_IMAGE);第一个参数image,是8位灰度、彩色图像;第二个参数patternSize,每个棋盘图上内角点的行列数,一般情况下,行列数不要相同,便于后续标定程序识别标定板的方向;第三个参数corners,用于存储检测到的内角点图像坐标位置,一般用元素是Point2f的向量来表示:vector image_points_buf;第四个参数flags:用于定义棋盘图上内角点查找的不同处理方式,有默认值。②提取亚像素角点信息:为了提高标定精度,在提取的角点信息上进一步提取亚像素信息,降低相机标定偏差;两种方法;一种方法cornerSubPix;另一种方法find4QuadCornerSubpix函数;void cornerSubPix( InputArray image,InputOutputArray corners,                 Size winSize,Size zeroZone,                 TermCriteria criteria ); 第一个参数image,是8位灰度图像;第二个参数corners,输入初始的角点坐标向量,输出亚像素坐标位置,浮点型数据,一般用元素是Pointf2f/Point2d的向量来表示:vector imagePointsBuf;第三个参数winSize,大小为搜索窗口的一半;第四个参数zeroZone,死区的一半尺寸,死区为不对搜索区的中央位置做求和运算的区域。它是用来避免自相关矩阵出现某些可能的奇异性。当值为(-1,-1)时表示没有死区;第五个参数criteria,迭代过程的终止条件,可以为迭代次数和角点精度两者的组合;bool find4QuadCornerSubpix(InputArray img,InputOutputArray corners,Size region_size); 第一个参数img,是8位灰度图像;第二个参数corners,输入初始的角点坐标向量,输出亚像素坐标位置,浮点型数据,一般用元素是Pointf2f/Point2d的向量来表示:vector imagePointsBuf;第三个参数region_size,角点搜索窗口的尺寸;对比cornerSubPix和find4QuadCornerSubpix:在标定图上分别运行cornerSubPix和find4QuadCornerSubpix寻找亚像素角点,两者定位到的亚像素角点坐标分别为:虽然两者有一定差距,但偏差基本都控制在0.5个像素之内。③在图像上绘制内角点drawChessboardCorners用于绘制被成功标定的角点void drawChessboardCorners( InputOutputArray image,Size patternSize,                          InputArray corners,//亚像素角点bool patternWasFound ); 第一个参数image,8位灰度或者彩色图像;第二个参数patternSize,每张标定棋盘上内角点的行列数;第三个参数corners,输入亚像素坐标位置,浮点型数据,一般用元素是Pointf2f/Point2d的向量来表示:vector iamgePointsBuf;第四个参数patternWasFound,标志位,用来指示定义的棋盘内角点是否被完整的探测到,true表示别完整的探测到,函数会用直线依次连接所有的内角点,作为一个整体,false表示有未被探测到的内角点,这时候函数会以圆圈标记处检测到的内角点;patternWasFound设置为true和false时内角点的绘制效果:ture时,依次连接各个内角点;false圆圈表示;

2

棋盘角点世界坐标系坐标:以棋盘格左上角为世界坐标系原点,建立世界坐标系,则棋盘角点世界坐标系下的坐标计算,如下:/*棋盘三维信息*/ Size square_size = Size(10,10);  /* 每个棋盘格真实尺寸 */ vector> object_points; /* 标定板上角点的三维坐标 */ /* 初始化标定板上角点的三维坐标 */ int i,j,t; for (t=0;t tempPointSet;    for (i=0;i

3

相机标定计算相机内外参数;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> object_points。依据棋盘上单个黑白矩阵的大小,计算出每一个内角点的世界坐标。第二个参数imagePoints,输入每一个内角点对应的图像坐标点,vector> 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,即vectorrvecs;第七个参数tvecs为位移向量,和rvecs一样,应该为vector tvecs;第八个参数flags为标定时所采用的算法。有如下几个参数:CV_CALIB_USE_INTRINSIC_GUESS:使用该参数时,在cameraMatrix矩阵中应该有fx,fy,u0,v0的估计值。否则,将初始化(u0,v0)图像的中心点,使用最小二乘估算出fx,fy。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_RATIONAL_MODEL:计算k4,k5,k6三个畸变参数。如果没有设置,则只计算其它5个畸变参数。第九个参数criteria是最优迭代终止条件设定。在使用该函数进行标定运算之前,需要对棋盘上每一个内角点的空间坐标系的位置坐标进行初始化,标定的结果是生成相机的内参矩阵cameraMatrix、相机的5个畸变系数distCoeffs,另外每张图像都会生成属于自己的平移向量和旋转向量。

4

相机标定结果评价projectPoints:以获取的相机内外参数为输入,计算空间三维点在图像上的重投影点坐标和亚像素角点坐标之间的偏差;偏差越小,标定结果越好;①对空间三维坐标点进行反向投影的函数是projectPoints; void projectPoints( InputArray objectPoints,                 InputArray rvec,InputArray tvec,                 InputArray cameraMatrix,InputArray distCoeffs,                 OutputArray imagePoints, //投影点                OutputArray jacobian=noArray(),                 double aspectRatio=0 ); 第一个参数objectPoints,为相机坐标系中的三维点坐标;第二个参数rvec为旋转向量,每一张图像都有自己的旋转向量;第三个参数tvec为平移向量,每一张图像都有自己的平移向量;第四个参数cameraMatrix为相机的内参数矩阵;第五个参数distCoeffs为相机的畸变矩阵;第六个参数imagePoints为每一个内角点对应的图像上的坐标点;第七个参数jacobian是雅可比行列式;第八个参数aspectRatio是跟相机传感器的感光单元有关的可选参数,如果设置为非0,则函数默认感光单元的dx/dy是固定的,会依此对雅可比矩阵进行调整; ②误差计算:以opencv里的norm把这里的两个通道分别分开来计算的(X1-X2)^2的值,然后统一求和,最后进行根号;double norm(InputArray src1, InputArray src2,           int normType=NORM_L2,InputArray mask=noArray());以图片的亚像素角点坐标和根据标定结果把空间三维坐标点映射到图像坐标点的对比:

5

图像矫正:利用标定结果对图像进行矫正方法一:initUndistortRectifyMap和remap相结合实现。initUndistortRectifyMap计算畸变映射,remap把求得的畸变映射应用到图像上。void initUndistortRectifyMap(InputArray cameraMatrix, InputArray distCoeffs,                          InputArray R,InputArray newCameraMatrix,                          Size size,int m1type,OutputArray map1,OutputArray map2 ); 第一个参数cameraMatrix为之前求得的相机的内参矩阵;第二个参数distCoeffs为之前求得的相机畸变矩阵;第三个参数R,旋转矩阵;第四个参数newCameraMatrix,输入的校正后的3X3摄像机矩阵;第五个参数size,摄像机采集的无失真的图像尺寸;第六个参数m1type,定义map1的数据类型,可以是CV_32FC1或者CV_16SC2;第七个参数map1和第八个参数map2,输出的X/Y坐标重映射参数;void remap( InputArray src,OutputArray dst,           InputArray map1,InputArray map2,           int interpolation,int borderMode=BORDER_CONSTANT,           const Scalar& borderValue=Scalar()); 第一个参数src,输入参数,代表畸变的原始图像;第二个参数dst,矫正后的输出图像,跟输入图像具有相同的类型和大小;第三个参数map1和第四个参数map2,X坐标和Y坐标的映射;第五个参数interpolation,定义图像的插值方式;第六个参数borderMode,定义边界填充方式;  方法二:undistort函数实现void undistort( InputArray src,OutputArray dst,              InputArray cameraMatrix,              InputArray distCoeffs,              InputArray newCameraMatrix=noArray() ); 第一个参数src,输入参数,代表畸变的原始图像;第二个参数dst,矫正后的输出图像,跟输入图像具有相同的类型和大小;第三个参数cameraMatrix为之前求得的相机的内参矩阵;第四个参数distCoeffs为之前求得的相机畸变矩阵;第五个参数newCameraMatrix,默认跟cameraMatrix保持一致; 方法一相比方法二执行效率更高一些,推荐使用。矫正结果比较:

注意事项

OpenCV标定程序完整版请私信!!!

推荐信息