OpenCV双目视觉

发布时间 2023-08-19 20:18:44作者: Champrin

OpenCV双目视觉

参考资料:
[双目立体视觉:标定和校正
双目立体视觉:标定和校正
双目立体视觉:块匹配视差图计算
教你如何提高双目立体视觉系统的精度

立体标定 cv::stereoCalibrate()

  • flags
    • CV_CALIB_FIX_INTRINSIC: K和D个矩阵是固定的。这是默认标志。如果你校准好你的相机,你可以修正它们,所以你只会得到校正矩阵。
    • CV_CALIB_USE_INTRINSIC_GUESS: K和D个矩阵将被优化。对于这个计算,你应该给出经过良好校准的矩阵,以便(可能)得到更好的结果。
    • CV_CALIB_FIX_PRINCIPAL_POINT: 修复K矩阵中的参考点。
    • CV_CALIB_FIX_FOCAL_LENGTH: 在K矩阵中固定焦距。
    • CV_CALIB_FIX_ASPECT_RATIO: 固定长宽比。
    • CV_CALIB_SAME_FOCAL_LENGTH: 校准焦距,并设置fx和fy相同的校准结果。
    • CV_CALIB_ZERO_TANGENT_DIST: 去掉畸变。
    • CV_CALIB_FIX_K1, …, CV_CALIB_FIX_K6: 移除K1到K6的畸变。

畸变矫正 cv.undistort()

方法官方文档

void cv::undistort(
    InputArray  src,
    OutputArray dst,
    InputArray  cameraMatrix,
    InputArray  distCoeffs,
    InputArray  newCameraMatrix = noArray() 
)	
  • src:[输入] 未畸变矫正的图片
  • dst:[输出] 矫正之后的图片,与src输入的图片具有相同的大小和类型
  • cameraMatrix:[输入] 相机内参矩阵
  • distCoeffs:[输入] 相机畸变参数
    • \((k1,k2,p1,p2[,k3[,k4,k5,k6[,s1,s2,s3,s4[,τx,τy]]]])\)中的4、5、8、12或14个元素
    • 如果设置为空,则假定失真系数为零
    • 一般取标定后相机的\([k1,k2,p1,p2,k3]\)
  • newCameraMatrix:[可选输入] 畸变图像的投影矩阵
    • 默认情况下,它与cameraMatrix相同
    • 也可以使用其他矩阵来缩放和移动结果,可利用cv::getOptimalNewCameraMatrix()设置相关参数以获取新的投影矩阵(相机内参)

立体校正 cv::stereoRectify()

方法官方文档

void cv::stereoRectify(
    InputArray cameraMatrix1,
    InputArray distCoeffs1,
    InputArray cameraMatrix2,
    InputArray distCoeffs2,
    Size imageSize,
    InputArray R,
    InputArray T,
    OutputArray R1,
    OutputArray R2,
    OutputArray P1,
    OutputArray P2,
    OutputArray Q,
    int flags = CALIB_ZERO_DISPARITY,
    double alpha = -1,
    Size newImageSize = Size(), 
    Rect* validPixROI1 = 0, 
    Rect* validPixROI2 = 0
)
  • cameraMatrix1:[输入] 左目内参矩阵
  • distCoeffs1:[输入] 左目畸变向量
  • cameraMatrix2:[输入] 右目内参矩阵
  • distCoeffs2:[输入] 右目畸变向量
  • imageSize:[输入] 图像尺寸,需与标定时的图像尺寸一致
  • R:[输出] 从左目坐标系到右目坐标系的旋转矩阵
    • 意义是左目通过变换R到达右目的位姿
  • T:[输出] 从左目坐标系到右目坐标系的平移向量
  • R1:[输出] 左目立体校正到新坐标系的变换矩阵(校正旋转矩阵)
  • R2:[输出] 右目立体校正到新坐标系的变换矩阵(校正旋转矩阵)
  • P1:[输出] 左目在新坐标系下的投影矩阵
    • 也可称为变换到新坐标系后的新左相机内参
  • P2:[输出] 右目在新坐标系下的投影矩阵
    • 也可称为变换到新坐标系后的新右相机内参
  • Q:[输出] 4*4的深度差异映射矩阵,也称为重投影矩阵
  • flags:[输入] 可选的标志有两种,0或者CV_CALIB_ZERO_DISPARITY,若
    • CV_CALIB_ZERO_DISPARITY,该函数会让两幅校正后的图像的主点有相同的像素坐标。
    • 0,该函数会水平或垂直的移动图像,以使得其有用的范围最大
  • alpha:[输入] 拉伸参数。
    • alpha = -1 时,OpenCV 会优化黑色像素点(立体校正后会出现黑色像素点),自动进行缩放和平移
    • alpha = 0 时,OpenCV对校正后图像缩放和平移,使remap()图像只显示有效像素(没有黑色像素点),适用于机器人避障导航等应用
    • alpha = 1 时,remap()图像将显示所有原图像中包含的像素,该取值适用于畸变系数极少的高端摄像头
    • alpha = 0~1 时,OpenCV 按对应比例保留原图像的边角区域像素
  • newImageSize:[输入] 校正后的图像分辨率,默认为原分辨率大小。
  • validPixROI1:[输出] 可选的输出参数,其内部的所有像素都有效,设置为0表示ROI覆盖整个图像
  • validPixROI2:[输出] 可选的输出参数,其内部的所有像素都有效,设置为0表示ROI覆盖整个图像

立体校正是在把两个图像坐标系不共面的平面,经过旋转相机坐标系,将这两个平面重新投影到一个共面的平面、共面的平面平行于基线、两条光轴互相平行,以此转化为理想情况的模型。

输出参数R1R2,就是使左目和右目经过旋转矩阵R1R2使得左右目的成像平面满足理想模型,行对齐。

输出参数P1P2,是经过R1R2旋转后,相机3D转2D点的新投影矩阵,即\(w\begin{bmatrix} x \\ y \\ 1 \end{bmatrix} = P\begin{bmatrix} X \\ Y \\ Z \\ 1 \end{bmatrix}\),其中\(w\)为缩放因子(Scale factor)

输出参数Q,为重投影矩阵,能够使得2D点重新转换为3D点,即\(Q\begin{bmatrix} x \\ y \\ d \\ 1 \end{bmatrix} = \begin{bmatrix} X \\ Y \\ Z \\ W \end{bmatrix}\),其中\((x, y)\)为2D点坐标,\(d\)为视差,3D点坐标为\((X/W, Y/W, Z/W)\)

映射变换计算 cv::initUndistortRectifyMap()

方法官方文档

void cv::initUndistortRectifyMap(
    InputArray cameraMatrix,
    InputArray distCoeffs,
    InputArray R,
    InputArray newCameraMatrix,
    Size size,
    int m1type,
    OutputArray map1,
    OutputArray map2
)	
  • cameraMatrix:[输入] 相机内参矩阵
  • distCoeffs:[输入] 畸变向量
  • R: [输入] 对象空间中的可选的校正变换(3x3矩阵)
    • 立体校正中,填入cv::stereoRectify()结果的R1R2
    • 畸变矫正中,填入3x3单位矩阵
  • newCameraMatrix:[输入] 新相机内参数的矩阵
    • 立体校正中,填入cv::stereoRectify()结果的P1P2
  • Size:[输入] 原始图像尺寸
  • m1type:[输入] 第一个输出映射的数据类型,可以为CV_32FC1CV_32FC2CV_16SC2类型
  • map1:[输出] 输出的第一个映射变换,图像X方向的映射关系
  • map2:[输出] 输出的第二个映射变换,图像Y方向的映射关系

映射变换计算,得出的两个映射变换可以将原始图像和校正后的图像上的点一一对应。

根据cv::stereoRectify()计算得出的R1P1P2R2,可以计算左右目各自的映射表map1map2

计算得到的映射表map1map2,可以通过cv::remap(),来校正获取到的图像,使得两图像共面,且行对齐。

几何变换 cv::remap()

方法官方文档

void cv::remap(	
    InputArray src,
    OutputArray dst,
    InputArray map1,
    InputArray map2,
    int interpolation,
    int borderMode = BORDER_CONSTANT,
    const Scalar & borderValue = Scalar() 
)	
  • src:[输入] 原始图像
  • dst:[输出] 几何变换后的图像
  • map1:[输入] 第一个映射关系,无论是点(x,y)或者单纯x的值都需要是CV_32FC1CV_32FC2CV_16SC2类型
  • map2:[输入] 第二个映射关系,y需要是CV_32FC1CV_16UC1类型;当map1是点(x,y)时,map2为空映射
  • interpolation:[输入] 插值方法,但是不支持最近邻插值
  • borderMode:[输入] 代表边界模式。当该值为 BORDER_TRANSPARENT 时,表示目标图像内的对应源图像内奇异点(outliers)的像素不会被修改。
  • borderValue:[输入] 代表边界值,该值默认为 0。

参考资料:[OpenCV] cv.remap() 重映射学习笔记/map1 map2易混点

立体匹配

SGBM

  • minDisparity:可能的最小视差值。通常它为零,但有时校正算法会移动图像,因此需要相应地调整此参数。
  • numDisparities:最大差异减去最小差异。
    • SGBM感知的范围,越大生成的精度越好,速度越慢,该值始终大于零。在当前实现中,此参数必须可被16整除。
  • blockSize:匹配的块大小。它必须是大于等于1的奇数,因为奇数大小的块有一个中心。通常,它应该在3~11范围内。深度图成块,blocksize越低,其深度图就越零碎
  • P1:第一个控制视差平滑度参数。惩罚系数,用于控制图像的平滑,具体需要看这个函数的算法,很多博客有介绍P1和P2的惩罚系数
  • P2:第二个控制视差平滑度参数。惩罚系数,值越大,视差越平滑。
    • P1是相邻像素之间视差变化正负1的惩罚。P2是相邻像素之间的视差变化大于1的惩罚。
    • SGBM 算法要求P2>P1。请参见stereo_match.cpp示例,其中显示了一些相当好的P1和P2值(例如分别为8number_of_image_channelsblockSizeblockSize and 32number_of_image_channelsblockSizeblockSize)
  • disp1MaxDiff:视差计算的最大像素差,视差图的像素点检查,设置为1
    • 左右视差检查中允许的最大差值(以整数像素为单位)。将其设置为非正值以禁用检查。
  • preFilterCap:预过滤图像像素的截断值。在块匹配之前,该算法首先计算每个像素的x导数,并按[-preFilterCap,preFilterCap]间隔剪裁其值。结果值被传递到Birchfield-Tomasi像素成本函数处理。
  • uniquenessRatio:最好的代价方程值“赢了”第二好的代价方程值的概率,此值用于比较通常设置为5~15之间效果达到最佳利润率,以百分比表示,最佳(最小)计算成本函数值应“赢得”第二个最佳值,从而认为找到的匹配正确。通常,5-15范围内的值就足够了。
  • speckleWindowSize:过滤删除大的值,得到一个更平滑的图像。针对散斑滤波的窗口大小,如果设置为0则不允许散斑滤波,否则设置为50~200之间平滑视差区域的最大大小,以考虑其噪声斑点和无效。将其设置为0以禁用斑点过滤。否则,将其设置在50-200范围内。
  • speckleRange:相邻像素点的视差值浮动范围,使用领域检查视差得到一个平滑的图像;通常设置为1~2就好了,这个系数会被乘以16输入到程序中;每个连接组件内的最大差异变化。如果执行散斑过滤,将参数设置为正值,它将隐式乘以16。
  • mode:sgbm算法选择模式,以速度由快到慢,精度反之:
    • StereoSGBM::MODE_SGBM_3WAY
    • StereoSGBM::MODE_HH4
    • StereoSGBM::MODE_SGBM
    • StereoSGBM::MODE_HH
      将其设置为StereoSGBM::MODE_HH运行全尺寸两遍动态规划算法。它将消耗O(WHnumDisparities)字节,这对于640x480立体声来说非常大,对于HD大小的图片来说非常大。默认情况下,它设置为false。

WLS滤波平滑优化图像