opencv单目相机标定-示例代码分析

  Opencv自带的sample code有关于camera calibration的示例代码,但是在这里我使用的是Learning OpenCV3的示例,在其代码基础上上稍微做了一点改动。之所以不用opencv自带的例子,是因为Learning OpenCV3的代码更加简单,可以更容易的抓住代码的核心。本节使用的项目代码可以在这里下载到。

一、运行示例

  在下载完整个工程以后,按照工程使用说明,下载配置Opencv,运行VS2019项目即可。正常情况下,运行结果如下图所示:

opencv单目相机标定-示例代码分析_第1张图片
  上图显示了标定代码的三个主要步骤,实际上代码中还有一步,但是在dos窗口中没有打印任何信息,这个在后面的代码分析中再进一步说明。
  程序运行的第一步是加载图片信息,程序中只是获取了图片序列的文件名。第二步则是棋盘格角点检测,对序列图片中的每一张图片进行角点检测,并选出角点检测成功的图片打印信息,仔细观察打印信息发现没有IMG_0195.jpg的信息,说明这张图片角点检测失败了,猜测是因为标定板严重倾斜导致。第三步就是标定,将检测的棋盘格角点以及设置的3D坐标点输入到opencv现成的函数中即可得到相机内参、畸变系数以及相机外参,这里程序还计算了重投影误差,可以根据重投影误差来帮助我们判断标定的精度。

二、代码分析

  这里主要是对上面运行的代码做一些简单的介绍,因为Opencv封装的已经很好了,所以代码很简短,说明起来也很轻松。Opencv单目标定流程主要分为三步:

  • 提取棋盘格角点
  • 标定参数计算
  • 去畸变

1. 检测角点

CV_EXPORTS_W bool findChessboardCorners(InputArray image, 
										Size patternSize,
										OutputArray corners,
                                        int flags = CALIB_CB_ADAPTIVE_THRESH + CALIB_CB_NORMALIZE_IMAGE );
  • image:输入的棋盘格图像(可以是8位单通道或三通道图像);
  • patternSize:棋盘格内部的角点的行列数(棋盘格数量-1);
  • corners:检测到的棋盘格角点。
  • flag:用于指定在检测棋盘格角点的过程中所应用的一种或多种过滤方法,可以使用下面的一种或多种:
    cv::CALIB_CB_ADAPTIVE_THRESH:使用自适应阈值将图像转化成二值图像
    cv::CALIB_CB_NORMALIZE_IMAGE:归一化图像灰度系数(用直方图均衡化或者自适应阈值)
    cv::CALIB_CB_FILTER_QUADS:在轮廓提取阶段,使用附加条件排除错误的假设
    cv::CALIB_CV_FAST_CHECK:快速检测

这个函数检测效果貌似精度不是很高,配合cornerSubPix可以进一步提高检测精度,进而提高标定精度。

2. 标定参数计算

CV_EXPORTS_W 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:棋盘格角点在定义的3D空间中的坐标;
  • imagePoints:图像上的棋盘格角点坐标;
  • imageSize:拍摄图像尺寸;
  • cameraMatrix:标定得到的相机内参矩阵;
  • distCoeffs:标定得到的相机畸变参数;
  • rvecs:标定计算得到的旋转向量(外参);
  • tvecs:标定得到的平移向量(外参);
  • flags:设置标定参数是否参与优化,很重要的一个参数,在本项目中设置为cv::CALIB_ZERO_TANGENT_DIST | cv::CALIB_FIX_PRINCIPAL_POINT;这里cv::CALIB_ZERO_TANGENT_DIST表示不考虑切向畸变系数,cv::CALIB_FIX_PRINCIPAL_POINT表示固定主点坐标定;或表示两个宏都要设置,这样设置的结果就是切向畸变系数(p1,p2)都是0,主点(cx,cy)在图像的中心,计算和优化参数过程中,这四个参数不参与计算。
  • criteria:这个是内部迭代优化的终止条件参数设置,一般不怎么改动。

3. 去畸变

CV_EXPORTS_W void initUndistortRectifyMap(InputArray cameraMatrix, 
										  InputArray distCoeffs,
                           				  InputArray R, 
                           				  InputArray newCameraMatrix,
                           				  Size size, 
                           				  int m1type, 
                           				  OutputArray map1, 
                           				  OutputArray map2 );
  • cameraMatrix:标定得到的相机内参矩阵;
  • distCoeffs:标定得到的畸变系数;
  • R:双目校正会设置,这里被设置为空矩阵;
  • newCameraMatrix:校正后的相机内参矩阵,本项目使用的是标定内参矩阵,也可以通过cv::getOptimalNewCameraMatrix计算获得;
  • size:去畸变图像大小;
  • m1type:第一个map的数据类型,通常是CV_32FC1, CV_32FC2或CV_16SC2;
  • map1:第一个映射矩阵;
  • map2:第二个映射矩阵;

这个函数计算得到两个map,然后利用cv::remap函数对畸变图像进行映射变换,得到最终的去畸变图像。

三、总结

  • 本项目的标定程序还有很多可以改进的地方,比如角点检测的精度,可以针对当前的例子做更多的改动,并对比不同设置下,标定结果的差异。
  • Opencv算法实现的已经非常好用了,但是对于内部的算法细节,需要更多的精力去了解,在时间允许的情况下,我会对算法实现进行总结。

你可能感兴趣的:(calibration,计算机视觉)