findChessboardCorners
函数简介
在相机标定过程中,如采用棋盘格标定板进行标定,则需找到棋盘格内角点,根据棋盘格内角点在像面中的像素坐标和各点对应的棋盘世界坐标,拍摄多个位置下的棋盘格,多点求解相机内外参。OpenCV中的findChessboardCorners()函数即用于棋盘格角点检测。
1 findChessboardCorners(InputArray image,
2 Size patternSize,OutputArray corners,
3 int flags=CALIB_CB_ADAPTIVE_THRESH+CALIB_CB_NORMALIZE_IMAGE)
功能
找到标定板内角点位置(标定板是专用器具,需要有严格的规格控制,标定板的制作精度直接影响标定精度;角点是指黑白色相接的方块定点部分;内角点是不与标定板边缘接触的内部角点)
参数
- 输入的图像矩阵,必须是8-bit灰度图或者彩色图像,在图像传入函数之前,一般经过灰度处理,还有滤波操作。
- 内角点的size,表示方式是定义Size PatSize(m,n),将PatSize作为参数传入。这里是内角点的行列数,不包括边缘角点行列数;行数和列数不要相同,这样的话函数会辨别出标定板的方向,如果行列数相同,那么函数每次画出来的角点起始位置会变化,不利于标定。
- 存储角点的数组,一般用 vector<vector<point2f>>
- 标志位,有默认值。
- CV_CALIB_CB_ADAPTIVE_THRESH:函数默认方式,根据图像的平均亮度值进行图像二值化,设立此标志位的含义是采用变化的阈值进行自适应二值化;
- CV_CALIB_CB_NORMALIZE_IMAGE:在二值化之前,调用EqualizeHist()函数进行图像归一化处理(直方图均衡化);
- CV_CALIB_CB_FILTER_QUADS:二值化完成后,函数开始定位图像中的四边形(这里不应该称之为正方形,因为存在畸变),这个标志设立后,函数开始使用面积、周长等参数来筛选方块(过滤掉在轮廓检索阶段提取的假四边形),从而使得角点检测更准确更严格。(个人理解这个不常用,因为拍摄的时候肯定会出现畸变,如果加入这个标志位在,则可能滤掉一些棋盘格,那么角点检测的时候就不是一个完整的棋盘格)
- CALIB_CB_FAST_CHECK:快速检测选项,对于检测角点极可能不成功检测的情况,这个标志位可以使函数效率提升。(对图像运行一个快速检查机制以查找棋盘板的角点,如果没有找到角点则返回一个快捷提醒。当没有观察到棋盘时,可以极大地加快在退化条件下的调用)(该标志位的作用就是快速检测一下图像中是否有棋盘格)
注意:标志位可组合使用。CALIB_CB_FAST_CHECK一般用于快速检测,很有可能检测不成功,特别是棋盘光线不均匀时。总体来说,CV_CALIB_CB_ADAPTIVE_THRESH是最可能检测到棋盘格的方式,组合使用推荐CV_CALIB_CB_ADAPTIVE_THRESH|CV_CALIB_CB_NORMALIZE_IMAGE(二值化处理|直方图均衡化)(flags | CALIB_FIX_K3 | CALIB_USE_LU表示设置了这两个标志位,通过flags&=CV_CALIB_CB_ADAPTIVE_THRESH判断是否设置了CV_CALIB_CB_ADAPTIVE_THRESH,flags如果设置了CV_CALIB_CB_ADAPTIVE_THRESH则flags对应位为1,则相与后为1,表明设置了该位,如果没有设置该位则相与后的结果为0),如果默认方式或这个组合方式检测不到角点,基本就需要重新采图
________________________________________________________
备注:
二值化:
简单二值化
自适应二值化
大津法
[2] 图像处理之----二值化处理_牛右刀薛面的博客-CSDN博客
【智能车】图像二值化算法--大津法OTSU_Ethan-Code的博客-CSDN博客
直方图均衡化:
增加图像的对比度,使图像在0~255范围内每个灰度级的像素频次都差不多,不要集中在某个灰度级范围内
原来的灰度直方图
直方图均衡化后
_______________________________________________________
总结
该函数的功能就是判断图像内是否包含完整的棋盘图,如果能够检测完全,就把他们的角点坐标按 顺序(逐行,从左到右)记录下来,并返回非0数,否则返回0。 这里对size参数要求非常严格,函数必须检测到相同的size才会返回非0,否则返回0,这里一定要注意。
该函数检测的角点的坐标是不精确的,获得角点精确坐标,可使用 cornerSubPix()函数,进行角点亚像素提取。
参考原文链接:
(99条消息) findChessboardCorners()角点检测详解_棋盘格角点检测_对望小秘的博客-CSDN博客
(99条消息) openCV函数用法之 findChessboardCorners_tiger&sheep的博客-CSDN博客
cornerSubPix()
函数参数
-
image:输入图像
-
corners:输入角点的初始坐标以及精准化后的坐标用于输出。
-
winSize:搜索窗口边长的一半,例如如果winSize=Size(5,5),则一个大小为(5*2+1)*(5*2+1)=11*11的搜索窗口将被使用。
-
zeroZone:搜索区域中间的dead region边长的一半,有时用于避免自相关矩阵的奇异性。如果值设为(-1,-1)则表示没有这个区域。
-
criteria:角点精准化迭代过程的终止条件。也就是当迭代次数超过criteria.maxCount,或者角点位置变化小于criteria.epsilon时,停止迭代过程。
——————————————————————————————————————
亚像素点检测:
亚像素点和邻域内任何像素 像素坐标相减组成的向量和邻域内的梯度点乘为0
Gi为pi的梯度
参考原文链接:
(原理分析)
OpenCV亚像素角点cornerSubPixel()源代码分析 - 一度逍遥 - 博客园 (cnblogs.com)
———————————————————————————————————————
drawChessboardCorners
函数原型
1 void cv::drawChessboardCorners( 2 cv::InputOutputArray image, // 棋盘格图像(8UC3)即是输入也是输出 3 cv::Size patternSize, // 棋盘格内部角点的行、列数 4 cv::InputArray corners, // findChessboardCorners()输出的角点 5 bool patternWasFound // findChessboardCorners()的返回值 6 );
函数参数
-
第一个参数是棋盘格图像(8UC3)(检测到角点并且将角点显示在原图像上)
-
第二个参数是棋盘格内部角点的行、列,和cv::findChessboardCorners()指定的相同
-
第三个参数是检测到的棋盘格角点(角点的位置)
-
第四个参数是cv::findChessboardCorners()的返回值(如果检测到角点则findChessboardCorners()返回true,然后将角点显示到原图像上)
参考原文链接:
calibrateCamera
根据校准模式的几个视图(也就是相机拍的几张不同的图片),求解摄像机的内在参数和外在参数。
在每个视图中,必须指定三维物体点及其相应的二维投影的坐标。这可以通过使用已知几何形状和易于检测特征点的对象来实现。这样的对象称为标定或校准模式,而且OpenCV有对棋盘标定的内置支持(见findchessboardcorners)。目前,固有参数的初始化(当cv_calib_use_intrinsic_guess未设置)只实现平面校准模式(对象点的z坐标,必须全部为零)。
只要提供初始相机内参矩阵cameramatrix,也可以用于三维标定装置。
该算法执行以下步骤:
- 计算初始的内在参数(只能用于平面校准模式的选项)或从输入参数中读取它们。畸变系数都为零开始,除非有cv_calib_fix_k指定。
- 估计初始相机的姿态,就像内在参数已经已知一样。这是通过使用solvepnp。
- 运行的全局Levenberg Marquardt优化算法来最小化投影误差,投影误差等于所提取到的图像上的特征点坐标和三维点根据使用相机的参数和姿态计算得到的图像上的投影坐标的距离的之和。
函数参数
- objectPoints :世界坐标系中的点。在使用时,应该输入vector< vector< Point3f > >。
- imagePoints :其对应的图像点。和objectPoints一样,应该输入vector< vector< Point2f > >型的变量。
- imageSize :图像的大小,在计算相机的内参数和畸变矩阵需要用到;
- cameraMatrix :内参数矩阵。输入一个Mat cameraMatrix即可。
- distCoeffs :畸变矩阵。输入一个Mat distCoeffs即可。
- rvecs :旋转向量;应该输入一个Mat的vector,即vector< Mat > rvecs因为每个vector< Point3f >会得到一个rvecs。
- tvecs :位移向量;和rvecs一样,也应该为vector tvecs。
- stdDeviationsIntrinsics :内参数的输出向量。输出顺序为: (fx,fy,cx,cy,k1,k2,p1,p2,k3,k4,k5,k6,s1,s2,s3,s4,τx,τy) ,如果不估计其中某一个参数,值等于0
- stdDeviationsExtrinsics :外参数的输出向量。输出顺序: (R1,T1,…,RM,TM) ,M是标定图片的个数, Ri,Ti 是1x3的向量 。
- perViewErrors 每个标定图片的重投影均方根误差的输出向量。
- criteria: 迭代优化算法的终止准则
- flags :标定函数是所采用的模型(重点)”。
可输入如下某个或者某几个参数:
- CV_CALIB_USE_INTRINSIC_GUESS:使用该参数时,将包含有效的fx,fy,cx,cy的估计值的内参矩阵cameraMatrix,作为初始值输入(已经有标定结果,对结果进行优化),然后函数对其做进一步优化。如果不使用这个参数,用图像的中心点初始化光轴点坐标(cx, cy)(也就是光轴和像平面交于图像1中心,(cx, cy)代表主点坐标),使用最小二乘估算出fx,fy(这种求法好像和张正友的论文不一样,不知道为何要这样处理)。注意,如果已知内部参数(内参矩阵和畸变系数),就不需要使用这个函数来估计外参,可以使用solvepnp()函数计算外参数矩阵。总的来说该标志位被设定就会有个优化后的fx,fy,主点坐标(cx,cy)也会有个优化后的坐标值;如果该标志位不设定则将图像中心作为主点的坐标值
- CV_CALIB_FIX_PRINCIPAL_POINT:在进行优化时会固定光轴点,光轴点将保持为图像的中心点。当CV_CALIB_USE_INTRINSIC_GUESS参数被设置,保持为输入的值。
- CV_CALIB_FIX_ASPECT_RATIO:固定fx/fy的比值,只将fy作为可变量(比值不变,因此fx也同时会变),进行优化计算。当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(初次获取内参矩阵可能会把它设置为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。
函数返回
重投影的总的均方根误差。
参考原文链接:
projectPoints
将三维点重投影到二维点
1 void projectPoints(InputArray objectPoints, InputArray rvec, InputArray tvec, 2 InputArray cameraMatrix, InputArray distCoeffs, 3 OutputArray imagePoints, OutputArray jacobian=noArray(), 4 double aspectRatio=0 )
initUndistortRectifyMap
该函数返回原始图像中某一像素应该在去畸变后的图像中的u坐标,v坐标
函数原型
void initUndistortRectifyMap( InputArray cameraMatrix, InputArray distCoeffs, InputArray R, InputArray newCameraMatrix, Size size, int m1type, OutputArray map1, OutputArray map2 );
配合remap进行畸变校正
map1i:某一像素在去畸变后的图像的u坐标
map2j:某一像素在去畸变后的图像的v坐标
map1:map1i的集合
map2:map2j的集合
remap
将原图像中的像素映射到去畸变的图像中(map1i,map2j)的位置
void remap( InputArray src, OutputArray dst, InputArray map1, InputArray map2, int interpolation, int borderMode=BORDER_CONSTANT, const Scalar& borderValue=Scalar());
关于OpenCV中的去畸变 - 知乎 (zhihu.com)
stereoCalibrate
1 double cv::stereoCalibrate ( InputArrayOfArrays objectPoints, 2 InputArrayOfArrays imagePoints1, 3 InputArrayOfArrays imagePoints2, 4 InputOutputArray cameraMatrix1, 5 InputOutputArray distCoeffs1, 6 InputOutputArray cameraMatrix2, 7 InputOutputArray distCoeffs2, 8 Size imageSize, 9 InputOutputArray R, 10 InputOutputArray T, 11 OutputArray E, 12 OutputArray F, 13 OutputArray perViewErrors, 14 int flags = CALIB_FIX_INTRINSIC, 15 TermCriteria criteria = TermCriteria(TermCriteria::COUNT+TermCriteria::EPS, 30, 1e-6) 16 )
该函数首先利用两个相机的旋转平移矩阵,求出两个相机之间的旋转平移矩阵,利用两个相机之间的旋转平移矩阵求出E,进而求出F
stereoRectify
1 CV_EXPORTS_W void stereoRectify(InputArray cameraMatrix1, 2 InputArray distCoeffs1, 3 InputArray cameraMatrix2, 4 InputArray distCoeffs2, 5 Size imageSize, InputArray R, InputArray T, 6 OutputArray R1, OutputArray R2, 7 OutputArray P1, OutputArray P2, 8 OutputArray Q, int flags = CALIB_ZERO_DISPARITY, 9 double alpha = -1, Size newImageSize =Size(), 10 CV_OUT Rect* validPixROI1 = 0, CV_OUT Rect* validPixROI2 = 0 );
作用是立体校正
- 首先根据RT矩阵经过以下的步骤:
得到Rl和Rr也就是R1,R2,根据R1,R2可以得到三维点立体校正之后的坐标,然后又求得P1,P2,它们是校正后的三维坐标到各自像素坐标的映射矩阵(3*4)
然后根据
initUndistortRectifyMap(camera.cameraMatrixL, camera.distCoeffL, Rl, Pl, image_size, CV_32FC1, mapLx, mapLy);
initUndistortRectifyMap(camera.cameraMatrixR, camera.distCoeffR, Rr, Pr, image_size, CV_32FC1, mapRx, mapRy);
可以根据P1,P2求得原图像的像素点在校正后的图像上的像素坐标,然后根据畸变系数利用畸变模型求出校正后的图像的像素点在无畸变的图像上的像素坐标
然后利用
remap(imgL, rectifiedL, mapLx, mapLy, INTER_LINEAR);
remap(imgR, rectifiedR, mapRx, mapRy, INTER_LINEAR);
将原图像的像素映射到无畸变的图像上去
- alpha=0保留黑边(无效点),alpha=1不保留黑边
sgbm->compute(imgL, imgR, dispL)
SGM视差计算
//除以16得到真实视差(因为SGBM算法得到的视差是×16的)
//clip_min(dispL,0,0);
dispL.convertTo(dispL, CV_32F, 1.0 / 16);
reprojectImageTo3D(disp, points_3d, Q, true)
视差图转换成深度图
平行视图满足(为什么是Tx负的不大理解)