双目相机标定、校正及匹配过程
总结双目视觉从标定到校正到匹配的过程中所用到的一些函数及方法
0、两条路线
- 先单目标定再双目标定:
- findChessboardCorners:寻找棋盘图中棋盘角点
- find4QuadCornerSubpix:对粗提取的角点进行精确化
- cornerSubPix:在角点检测中精确化角点位置
- drawChessboardCorners:将发现到的所有角点绘制到所提供的图像上
- calibrateCamera:利用定标来计算摄像机的内参数和外参数
- projectPoints:通过得到的摄像机内外参数,对空间的三维点进行重新投影计算,得到新的投影点
- 利用标定结果对棋盘图进行矫正
(1). initUndistortRectifyMap用来计算畸变映射,remap把求得的映射应用到图像上
(2). 或者undistort一个函数搞定 - stereoCalibrate:双目摄像机标定,计算了两个摄像头进行立体像对之间的转换关系,根据左右相机的参数矩阵,生成两个相机之间的关系矩阵,以及基本和本质矩阵
- stereoRectify:作用是为每个摄像头计算立体校正的映射矩阵。所以其运行结果并不是直接将图片进行立体矫正,而是得出进行立体矫正所需要的映射矩阵
- 直接进行双目标定
- findChessboardCorners找到角点
- drawChessboardCorners画出角点
- stereoCalibrate直接计算左右相机内参矩阵、畸变量、R、T、E、F
- undistortPoints去畸变
- computeCorrespondEpilines
- stereoRectify
- initUndistortRectifyMap
- remap
路线2的精度会下降
一、单目标定
1.提取角点(findChessboardCorners)
从文件中读入图像,通过findChessboardCorners找到角点的图像坐标系坐标,这个函数采用Harris算法获得角点。
CV_EXPORTS_W bool findChessboardCorners(InputArray image,
Size patternSize,
OutputArray corners,
int flags = CALIB_CB_ADAPTIVE_THRESH + CALIB_CB_NORMALIZE_IMAGE );
参数说明:
- Image:输入的棋盘图,必须是8位的灰度或者彩色图像,类型为cv::Mat
- patternSize:棋盘图中每行和每列角点的个数,类型为cv::Size()
- corners:检测到的角点,类型为std::vector< cv::Point2f >。
- flags :不同的操作标记
- CALIB_CB_ADAPTIVE_THRESH:1,使用自适应阈值法把图像转换为黑白图,而不是使用一个固定的阈值。
- CALIB_CB_NORMALIZE_IMAGE:2,在利用固定阈值或自适应阈值法二值化图像之前,利用直方图均衡化图像。
- CALIB_CB_FILTER_QUADS:4,使用额外的标准(如轮廓面积,周长,正方形形状)来过滤掉在轮廓检索阶段提取的假四边形。
- CALIB_CB_FAST_CHECK:8,对图像运行一个快速检查机制以查找棋盘板的角点,如果没有找到角点则返回一个快捷提醒。当没有观察到棋盘时,可以极大地加快在退化条件下的调用。
bool ok = findChessboardCorners(imageInput, board_size, image_points, CALIB_CB_ADAPTIVE_THRESH + CALIB_CB_NORMALIZE_IMAGE);
2.对提取到的角点进行亚像素精确化(find4QuadCornerSubpix或cornerSubPix)
两种方法,可取其一
2.1 find4QuadCornerSubpix
bool cv::find4QuadCornerSubpix ( InputArray img,
InputOutputArray corners,
Size region_size
)
参数说明:
img:输入的Mat矩阵,最好是8位灰度图像,检测效率更高;
corners:初始的角点坐标向量,同时作为亚像素坐标位置的输出,所以需要是浮点型数据,一般用元素是Pointf2f/Point2d的向量来表示。
region_size:角点搜索窗口的尺寸,优化坐标时考虑的邻域范围。
find4QuadCornerSubpix(view_gray, image_points, Size(5, 5));
2.2 cornerSubPix
在角点检测中精确化角点位置,其函数原型如下:
void cornerSubPix( InputArray image,
InputOutputArray corners,
Size winSize,
Size zeroZone,
TermCriteria criteria);
函数参数说明如下:
image:输入图像
corners:输入角点的初始坐标以及精准化后的坐标用于输出。
winSize:搜索窗口边长的一半,例如如果winSize=Size(5,5),则一个大小为的搜索窗口将被使用。
zeroZone:搜索区域中间的dead region边长的一半,有时用于避免自相关矩阵的奇异性。如果值设为(-1,-1)则表示没有这个区域。
criteria:角点精准化迭代过程的终止条件。也就是当迭代次数超过criteria.maxCount,或者角点位置变化小于criteria.epsilon时,停止迭代过程。
cv::TermCriteria criteria = cv::TermCriteria(cv::TermCriteria::MAX_ITER + cv::TermCriteria::EPS, 100, 1e-5);
cv::cornerSubPix(view_gray, image_points, cv::Size(5, 5), cv::Size(-1, -1), criteria);
3.显示角点(drawChessboardCorners)
使用**drawChessboardCorners()**函数进行棋盘格绘制显示
void cv::drawChessboardCorners(
cv::InputOutputArray image, // 棋盘格图像(8UC3)即是输入也是输出
cv::Size patternSize, // 棋盘格内部角点的行、列数
cv::InputArray corners, // findChessboardCorners()输出的角点
bool patternWasFound // findChessboardCorners()的返回值
);
参数说明:
- image:棋盘格图像(8UC3),既是输入也是输出
- patternSize:棋盘格内部角点的行、列,和cv::findChessboardCorners()指定的相同
- corners:检测到的棋盘格角点
- patternWasFound :cv::findChessboardCorners()的返回值
drawChessboardCorners(imageInput, this->board_size, image_points, ok);
4.单目相机标定(calibrateCamera)
【OpenCV3学习笔记 】相机标定函数 calibrateCamera( ) 使用详解(附相机标定程序和数据)
求解出该相机的内参数和每一个视角的外参数。
double cv::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< 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< Mat >。
- flags :标定函数是所采用的模型(重点)
- CV_CALIB_USE_INTRINSIC_GUESS:使用该参数时,将包含有效的fx,fy,cx,cy的估计值的内参矩阵cameraMatrix作为初始值输入,然后函数对其做进一步优化。如果不使用这个参数,用图像的中心点初始化光轴点坐标(cx, cy),使用最小二乘估算出fx,fy(这种求法好像和张正友的论文不一样,不知道为何要这样处理)。注意,如果已知内部参数(内参矩阵和畸变系数),就不需要使用这个函数来估计外参,可以使用solvepnp()函数计算外参数矩阵
- 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_USE_INTRINSIC_GUESS参数,就从提供的畸变系数矩阵中得到。否则,设置为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。
- criteria: 迭代优化算法的终止准则
函数返回:重投影的总的均方根误差。
在相机标定之后,可以得到相机内参数矩阵与畸变系数cameraMatrix,distCoeffs
5.重投影计算(projectPoints)
cv::projectPoints()详细解释
使用的函数为projectPoints。
函数的作用是将输入的三维点坐标根据相机的内外参数进行投影,得到对应的二维图像坐标。投影的过程包括将三维点坐标通过旋转和平移变换到相机坐标系下,然后应用相机的内参数矩阵进行透视投影,并根据畸变参数进行畸变校正。最终得到的二维图像坐标可以用于在图像上绘制或进行其他处理。
计算投影坐标和亚像素角点坐标之间的偏差,偏差越小,标定结果越好。
void cv::projectPoints(
InputArray objectPoints,
InputArray rvec,
InputArray tvec,
InputArray cameraMatrix,
InputArray distCoeffs,
OutputArray imagePoints,
OutputArray jacobian = noArray()
)
参数说明:
- objectPoints:输入的三维点坐标,可以是一个包含三维点的数组或矩阵。每个三维点都表示为一个 cv::Point3f 或 cv::Point3d 对象。
- rvec:相机的旋转向量,通常由旋转矩阵转换而来。可以使用 cv::Rodrigues() 函数将旋转矩阵转换为旋转向量。旋转向量表示相机的旋转姿态。
- tvec:相机的平移向量,表示相机位置相对于世界坐标系的平移。通常以 cv::Mat 对象的形式提供。
- cameraMatrix:相机的内参数矩阵,也称为相机矩阵。它包含相机的焦距、主点坐标和畸变参数等信息。通常以 cv::Mat 对象的形式提供。
- distCoeffs:相机的畸变参数,用于校正图像中的畸变。通常以 cv::Mat 对象的形式提供。
- imagePoints:输出的二维图像坐标,用于存储投影后的二维点坐标。可以是一个包含二维点的数组或矩阵。每个二维点都表示为一个 cv::Point2f 或 cv::Point2d 对象。
- jacobian(可选参数):输出的雅可比矩阵,用于表示投影函数相对于输入参数的导数。如果不需要该信息,可以忽略此参数。
使用cv::norm() 函数计算偏差。
cv::norm 详细解释
double norm(InputArray src1, InputArray src2, int normType = NORM_L2);
参数说明:
- src1 和 src2 是输入矩阵,可以是 cv::Mat、cv::Mat_ 或 std::vector 类型的对象。
- normType 是一个可选参数,用于指定计算误差的类型。常见的取值为 NORM_L1、NORM_L2 和 NORM_INF,分别表示计算 L1 范数、L2 范数和无穷范数。默认值为 NORM_L2。
L1 范数,也称为曼哈顿范数。对于一维数组,它表示数组中所有元素的绝对值之和。对于多维数组,它表示所有元素绝对值之和的最大值。
L2 范数,也称为欧几里得范数。对于一维数组,它表示数组中所有元素的平方和的平方根。对于多维数组,它表示所有元素平方和的平方根。
无穷范数,也称为最大绝对值范数。对于一维数组,它表示数组中绝对值最大的元素的绝对值。对于多维数组,它表示所有元素绝对值的最大值。
6.校正图像(initUndistortRectifyMap和remap,undistort)
6.1 方法一:使用initUndistortRectifyMap和remap两个函数配合实现
void initUndistortRectifyMap( InputArray cameraMatrix, InputArray distCoeffs,
InputArray R, InputArray newCameraMatrix,
Size size, int m1type, OutputArray map1, OutputArray map2 );
函数说明:
这个函数用于计算无畸变和修正转换关系,为了重映射,将结果以映射的形式表达。无畸变的图像看起来就像原始的图像,就像这个图像是用内参为newCameraMatrix的且无畸变的相机采集得到的。
在单目相机例子中,newCameraMatrix一般和cameraMatrix相等,或者可以用cv::getOptimalNewCameraMatrix来计算,获得一个更好的有尺度的控制结果。
在双目相机例子中,newCameraMatrix一般是用cv::stereoRectify计算而来的,设置为P1或P2。 此外,根据R,新的相机在坐标空间中的取向是不同的。
例如,它帮助配准双目相机的两个相机方向,从而使得两个图像的极线是水平的,且y坐标相同(在双目相机的两个相机谁水平放置的情况下)。 该函数实际上为反向映射算法构建映射,供反向映射使用。也就是,对于在已经修正畸变的图像中的每个像素(u,v),该函数计算原来图像(从相机中获得的原始图像)中对应的坐标系。
函数输出得到map1和map2,然后使用remap()函数
void remap( InputArray src, OutputArray dst,
InputArray map1, InputArray map2,
int interpolation, int borderMode=BORDER_CONSTANT,
const Scalar& borderValue=Scalar());
第一个参数:输入图像,即原图像,需要单通道8位或者浮点类型的图像
第二个参数:输出图像,即目标图像,需和原图形一样的尺寸和类型
第三个参数:它有两种可能表示的对象:(1)表示点(x,y)的第一个映射;(2)表示CV_16SC2,CV_32FC1等
第四个参数:它有两种可能表示的对象:(1)若map1表示点(x,y)时,这个参数不代表任何值;(2)表示 CV_16UC1,CV_32FC1类型的Y值
第五个参数:插值方式,有四种插值方式:
(1)INTER_NEAREST——最近邻插值
(2)INTER_LINEAR——双线性插值(默认)
(3)INTER_CUBIC——双三样条插值(默认)
(4)INTER_LANCZOS4——lanczos插值(默认)
第六个参数:边界模式,默认BORDER_CONSTANT
第七个参数:边界颜色,默认Scalar()黑色
i
nitUndistortRectifyMap(cameraMatrix, distCoeffs, Mat::eye(3, 3, CV_32F), cameraMatrix, image_size, CV_32FC1, mapx, mapy);
remap(imageSource, newimage, mapx, mapy, INTER_LINEAR);
方法二:不需要转换矩阵的方式,使用undistort函数实现
void undistort( InputArray src, //输入原图
OutputArray dst,//输出矫正后的图像
InputArray cameraMatrix,//内参矩阵
InputArray distCoeffs,//畸变系数
InputArray newCameraMatrix=noArray() );
有时不需要矫正整个图像,而仅仅计算图像中特定点的位置,这时可以使用undistortPoints函数:
void undistortPoints( InputArray src, OutputArray dst,
InputArray cameraMatrix, InputArray distCoeffs,
InputArray R=noArray(), InputArray P=noArray());
undistortPoints函数与undistort()的区别在于:参数src,dst是二维点的向量,std::vectorcv::Point2f ,P对应cameraMatrix。该参数与立体校正方面的使用有关。
相机标定(4) 矫正畸变 undistort()和initUndistortRectifyMap()
二、双目标定
stereoCalibrate进行双目摄像机标定,生成两个相机之间的关系矩阵以及基本和本质矩阵;stereoRectify函数得到矫正的双目图像所需的变换矩阵和投影矩阵,然后可以传给 initUndistortRectifyMap 函数生成矫正图像到原始图像的像素坐标的映射,最后使用 remap 函数得到校正的双目图像。
1.双目标定函数(stereoCalibrate)
stereoCalibrate()
双目标定中stereoCalibrate()函数
使用到stereoCalibrate()函数
double cv::stereoCalibrate ( InputArrayOfArrays objectPoints,
InputArrayOfArrays imagePoints1,
InputArrayOfArrays imagePoints2,
InputOutputArray cameraMatrix1,
InputOutputArray distCoeffs1,
InputOutputArray cameraMatrix2,
InputOutputArray distCoeffs2,
Size imageSize,
InputOutputArray R,
InputOutputArray T,
OutputArray E,
OutputArray F,
OutputArray perViewErrors, // 可以省略
int flags = CALIB_FIX_INTRINSIC,
TermCriteria criteria = TermCriteria(TermCriteria::COUNT+TermCriteria::EPS, 30, 1e-6)
)
参数补充说明
flag说明:
1.CALIB_FIX_INTRINSIC:固定相机内参矩阵和畸变系数,只估计R, T, E和F矩阵。
CALIB_USE_INTRINSIC_GUESS:根据指定的flag优化部分或所有的相机内参数。初始值由用户提供。
CALIB_USE_EXTRINSIC_GUESS:R和T包含用于进一步优化的有效初始值。否则,R和T被初始化为视图的中值(每个维度独立)。
CALIB_FIX_PRINCIPAL_POINT:在优化过程中固定主点。
CALIB_FIX_FOCAL_LENGTH:固定左右相机的和。
CALIB_FIX_ASPECT_RATIO:优化左右相机的,固定的比例。
CALIB_SAME_FOCAL_LENGTH:令右相机的和与左相机相等。
CALIB_ZERO_TANGENT_DIST:设置每个相机的切向畸变系数为零并固定。
CALIB_FIX_K1,…, CALIB_FIX_K6:在优化过程中不改变相应的径向畸变系数。如果设置了CALIB_USE_INTRINSIC_GUESS,则使用提供的distCoeffs矩阵中的系数。否则,它被设置为0。
CALIB_RATIONAL_MODEL:启用系数k4, k5和k6。为了提供向后兼容性,应该明确指定这个额外的标志,以使标定函数使用合理模型并返回8个系数。如果没有设置该标志,该函数只计算并返回5个畸变系数。
CALIB_THIN_PRISM_MODEL:s1、s2、s3和s4的系数被启用。为了提供向后兼容性,应该明确指定这个额外的标志,使标定函数使用薄棱镜模型并返回12个系数。如果没有设置该标志,该函数只计算并返回5个畸变系数。
CALIB_FIX_S1_S2_S3_S4:在优化过程中固定薄棱镜的畸变系数。如果设置了 CALIB_USE_INTRINSIC_GUESS,则使用提供的distCoeffs矩阵中的系数。否则,它被设置为0。
CALIB_TILTED_MODEL:启用系数tauX和tauY。为了提供向后兼容性,应该明确指定这个额外的标志,以使标定函数使用倾斜传感器模型并返回14个系数。如果没有设置该标志,该函数只计算并返回5个畸变系数。
CALIB_FIX_TAUX_TAUY:在优化过程中固定倾斜传感器模型的系数。如果设置了 CALIB_USE_INTRINSIC_GUESS,则使用提供的distCoeffs矩阵中的系数。否则,它被设置为0。
criteria说明:
[学习笔记-opencv篇]基础函数TermCriteria
用于判断函数的迭代优化终止条件,常常使用TermCriteria函数来表示
cv::TermCriteria::TermCriteria ( int type, int maxCount, double epsilon)
参数说明:
- type:判定迭代终止的条件类型。有三种类型
- TermCriteria::COUNT 最大迭代次数;
- TermCriteria::EPS 要求的收敛阈值;
- TermCriteria::COUNT + TermCriteria::EPS 达到2个条件之一即可。
- maxCount:即最大迭代次数。
- epsilon:即要求的收敛阈值。
函数本身会返回一个Double类型的数值,这代表了经过标定后,所有不同视角图像的对应的均方根重投影误差的均方根误差(非常绕,公式:
C++ OpenCV V4.x中的新版双目标定函数stereoCalibrate() 参数说明【新增perViewErrors】
double rms = stereoCalibrate(object_points_seq, image_points_seq_l, image_points_seq_r,
cameraMatrixL, distCoeffL, cameraMatrixR, distCoeffR,
image_size, R, T, E, F, ERRO,
cv::CALIB_FIX_ASPECT_RATIO +
cv::CALIB_ZERO_TANGENT_DIST +
cv::CALIB_SAME_FOCAL_LENGTH +
CALIB_USE_INTRINSIC_GUESS +
CALIB_RATIONAL_MODEL +
CALIB_FIX_K3 + CALIB_FIX_K4 + CALIB_FIX_K5,
TermCriteria(TermCriteria::COUNT + TermCriteria::EPS, 100, 1e-5));
2. 去畸变(undistortPoints)
undistortPoints()函数用法总结:有原理说明,数学推导
利用undistortPoints()函数将拍摄的图像矫正为正常的视角,便于检测。
功能:通过旋转平移变换,将观察点转换到理想的点坐标下。
void cv::undistortPoints( InputArray src,
OutputArray dst,
InputArray cameraMatrix,
InputArray distCoeffs,
InputArray R = noArray(),
InputArray P = noArray()
)
参数说明:
- src:输入原始图像角点坐标
- dst:输出理想点坐标,无失真和反向透视变换。
- cameraMatrix:对应相机的内参矩阵
- distCoeffs:对应相机的畸变向量
- R参数是用在双目里的,单目里置为空矩阵
- P矩阵值为空时,得到的结果的点坐标是相机的归一化坐标( x , y ) (x, y)(x,y),这时候数值就会明显很小;设置相机内参会进行以下计算:u’ = xfx’ + cx’ v’ = yfy’ + cy’,这时候得到的才是特征点消畸变后的像素坐标。通常使用时是想得到在同一个相机下的真实像素,所以P设置为内参就可以了。
cv::undistortPoints(pt0, pt0, cameraMatrixL, distCoeffL, cv::Mat(), cameraMatrixL);
3. 极线计算(computeCorrespondEpilines )
void cv::computeCorrespondEpilines ( InputArray points,
int whichImage,
InputArray F,
OutputArray lines
)
参数说明:
- points:输入角点
- whichImage:指示是第一幅图片还是第二幅图片的参数,取1或2
- F:使用findFundamentalMat或stereoRectify计算得到的基本矩阵
- lines:输出向量,对应第一/二幅图片的对极线
cv::computeCorrespondEpilines(pt0, 1, F, lines[0]);
cv::computeCorrespondEpilines(pt1, 2, F, lines[1]);
三、双目校正
1.计算立体校正的映射矩阵
(1)使用Bouguet算法进行计算
stereoRectify函数
CV_EXPORTS_W void 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(),
CV_OUT Rect* validPixROI1 = 0,
CV_OUT Rect* validPixROI2 = 0 );
参数说明:
- cameraMatrix1 第一个相机内参矩阵
- distCoeffs1 第一个相机畸变参数
- cameraMatrix1 第二个相机内参矩阵
- distCoeffs1 第二个相机畸变参数
- imageSize 用于相机标定的图像尺寸
- R 从第一个相机到第二个相机坐标系系统的旋转矩阵
- T 从第一个相机到第二个相机的坐标系的平移矩阵
- R1 输出参数,为第一个相机输出3x3的修正矩阵(旋转矩阵)。该矩阵将未经过校正的第一个摄像机坐标系中给出的点引入到经过校正的第一个摄像机坐标系中的点。用更专业的术语来说,它执行了一个基础的改变,从未矫正的第一个摄像机的坐标系到矫正的第一个摄像机的坐标系。
- R2 输出参数,为第二个相机输出3x3的校正变换(旋转矩阵)。该矩阵将未校正的第二摄像机坐标系中给定的点引入已校正的第二摄像机坐标系中的点。用更专业的术语来说,它执行了一个基础的改变,从未矫正的第二个摄像机的坐标系到矫正的第二个摄像机的坐标系。
- P1 输出参数,为第一个摄像机输出新的(经校正的)坐标系统中的3x4投影矩阵,即它将经校正的第一个摄像机坐标系统中给定的点投影到被校正的第一个摄像机的图像中。
- P2 输出参数,为第二个摄像机输出新的(经校正的)坐标系统中的3x4投影矩阵,即将经校正的第一个摄像机坐标系统中给定的点投影到经校正的第二个摄像机的图像中。
- Q 输出参数,输出4x4的视差-深度映射矩阵
- flags 操作标志,可以是零或CALIB_ZERO_DISPARITY。如果设置了标志,该函数将使每个摄像机的主点在校正视图中具有相同的像素坐标。如果没有设置标志,函数仍然可以在水平或垂直方向(取决于极线的方向)移动图像,以最大化有用的图像区域。
- alpha 自由的尺度参数。如果是-1或不设置,函数将执行默认的缩放。否则,参数应该在0和1之间。alpha=0表示校正后的图像被缩放和移动,只有有效像素可见(校正后没有黑色区域)。alpha=1表示对校正后的图像进行抽取和移位,使所有来自相机的原始图像的像素都保留在校正后的图像中(无源图像像素丢失)。任何中间值都是这两个极端情况的中间结果。
- newImageSize 校正后图像分辨率。同样的大小也应该传递给initUndistortRectifyMap (参见OpenCV样本目录中的stereo_calib.cpp样本)。当传递(0,0)时(默认),将其设置为原始imageSize。将其设置为较大的值可以帮助您保存原始图像的细节,特别是当有一个大的径向失真。
- validPixROI1 矫正图像内的可选的输出矩形,其中所有像素是有效的。如果alpha=0, ROI覆盖整个图像。否则,它们可能会更小
- validPixROI2 矫正图像内的可选的输出矩形,其中所有像素是有效的。如果alpha=0, ROI覆盖整个图像。否则,它们可能会更小
该函数为每个摄像机计算旋转矩阵,使两个摄像机的图像平面(实际上)成为同一平面。因此,这使得所有的极线平行,从而简化了稠密的立体对应问题。
该函数以stereoCalibrate计算的矩阵作为输入。作为输出,它提供了新坐标中的两个旋转矩阵和两个投影矩阵。该函数区分了以下两种情况:
- 水平立体双目视觉:第一和第二摄像机视图主要沿x轴相对移动(可能有较小的垂直移动)。在校正后的图像中,左右相机对应的极线是水平的,且y坐标相同。
- 垂直立体双目视觉:第一和第二摄像机的视图主要在垂直方向上相对移动(可能在水平方向上也有一点)。经校正的图像中的极线是垂直的,并且有相同的x坐标。
P1和P2的前三列将有效地成为新的“校正”相机矩阵,连同R1和R2矩阵,然后可以被传递到initUndistortRectifyMap,以初始化每个摄像机的双目校正映射。
stereoRectify(cameraMatrixL, distCoeffL, cameraMatrixR, distCoeffR, image_size, R, T,
Rl, Rr, Pl, Pr, Q,
//CALIB_ZERO_DISPARITY,
0,
-1, image_size
//, &validROIL, &validROIR
);
initUndistortRectifyMap()函数
用于计算无畸变和修正转换关系
CV_EXPORTS_W void initUndistortRectifyMap( InputArray cameraMatrix,
InputArray distCoeffs,
InputArray R,
InputArray newCameraMatrix,
Size size,
int m1type,
OutputArray map1,
OutputArray map2 );
参数说明:
- cameraMatrix——输入的摄像头内参数矩阵(3X3矩阵)
- distCoeffs——输入的摄像头畸变系数矩阵(5X1矩阵)
- R——输入的第一和第二摄像头坐标系之间的旋转矩阵,通过stereoRectify计算得来的R1或R2可以放在这里。
- newCameraMatrix——输入的校正后的3X3摄像机矩阵
- size——摄像头采集的无失真图像尺寸,去畸变后图像的尺寸
- m1type——map1的数据类型,可以是CV_32FC1或CV_16SC2
- map1——输出的X坐标重映射参数
- map2——输出的Y坐标重映射参数
这个函数用于计算无畸变和修正转换关系,为了重映射,将结果以映射的形式表达。无畸变的图像看起来就想原始的图像,就像这个图像是用内参为newCameraMatrix的且无畸变的相机采集得到的。
在单目相机例子中,newCameraMatrix一般和cameraMatrix相等,或者可以用cv::getOptimalNewCameraMatrix来计算,获得一个更好的有尺度的控制结果。
在双目相机例子中,newCameraMatrix一般是用cv::stereoRectify计算而来的,设置为P1或P2。
Size newimage_size = Size(imageWidth*2 , imageHeight*2);
initUndistortRectifyMap(cameraMatrixL, distCoeffL, Rl, Pl, newimage_size, CV_16SC2, mapLx, mapLy);
关于校正后的图片全黑或无法正常显示
initUndistortRectifyMap函数的imagesize取更大值,表示画在更大的画布上,校正后再对画布进行截取
(2)使用HARTLEY算法进行计算
findFundamentalMat
特征提取与匹配中使用cv::findFundamentalMat要注意的几点
使用OpenCV进行摄像机标定
根据配对点的像素坐标(齐次坐标)求基础矩阵F
Mat findFundamentalMat( InputArray points1,
InputArray points2,
int method = FM_RANSAC,
double param1 = 3.,
double param2 = 0.99,
OutputArray mask = noArray()
)
参数说明
- points1:第一幅图像点的数组
- points2:第二幅图像点的数组
- method = FM_RANSAC:计算基础矩阵的方法
- FM_7POINT = CV_FM_7POINT,7-点算法,点数目= 7
- FM_8POINT = CV_FM_8POINT,8-点算法,点数目 >= 8
- FM_LMEDS = CV_FM_LMEDS,LMedS 算法,点数目 >= 8
- FM_RANSAC = CV_FM_RANSAC,RANSAC 算法,点数目 >= 8
- param1 = 3:点到对极线的最大距离,超过这个值的点将被舍弃
- param2 = 0.99:矩阵正确的可信度
- mask = noArray():内点向量索引
要注意的是,points1和points2矩阵的类型不能是CV_64F,也就是说findFundamentalMat内部解析points1和points2时是按照float类型去解析的,设为CV_64F将导致读取数据失败,程序崩溃。最好设为CV_32F。
函数 FindFundamentalMat 利用上面列出的四种方法之一计算基本矩阵,并返回基本矩阵的值:没有找到矩阵,返回0,找到一个矩阵返回1,多个矩阵返回3。 计算出的基本矩阵可以传递给函数cvComputeCorrespondEpilines来计算指定点的对极线。
stereoRectifyUncalibrated
OpenCV在未知相机内参数情况下的立体图像矫正方法及注意事项
bool stereoRectifyUncalibrated( const Mat& points1,
const Mat& points2,
const Mat& F,
Size imgSize,
CV_OUT Mat& H1,
CV_OUT Mat& H2,
double threshold=5
);
参数说明:
- points1:第一幅图像点的数组
- points2:第二幅图像点的数组
- F:基本矩阵
- imgSize:图片尺寸
- H1和H2:两幅图像各自对应的单应变换矩阵
stereoRectifyUncalibrated函数默认原始图像是没有径向畸变的,因此在矫正图像之前,最好先对原始图像做径向矫正。
然后对两幅图像按照H1和H2做单应变换,即可得到矫正后图像。
注意函数返回的单应变换矩阵H1和H2都是double类型,也即CV_64F类型,若不是该类型的矩阵,与之相乘会报错
3. 进行重映射(remap)
remap()函数
重映射,就是把一幅图像中某位置的像素放置到另一个图片指定位置的过程。
用函数remap( )来实现简单重映射
void remap(InputArray src, //输入图像
OutputArray dst, //输出图像
InputArray map1, //第一个映射
InputArray map2, //第二个映射
int interpolation, //插值
int borderMode=BORDER_CONSTANT,
const Scalar& borderValue=Scalar()
)
参数说明:
- src——输入图像,即原图像,需要单通道8位或者浮点类型的图像
- dst——输出图像,即目标图像,需和原图形一样的尺寸和类型
- map1——它有两种可能表示的对象:(1)表示点(x,y)的第一个映射;(2)表示CV_16SC2,CV_32FC1等
- map2——有两种可能表示的对象:(1)若map1表示点(x,y)时,这个参数不代表任何值;(2)表示 CV_16UC1,CV_32FC1类型的Y值
- intermap2polation——插值方式,有四种插值方式:
INTER_NEAREST——最近邻插值、
INTER_LINEAR——双线性插值(默认)、
INTER_CUBIC——双三样条插值(默认)、
INTER_LANCZOS4——lanczos插值(默认) - intborderMode——边界模式,默认BORDER_CONSTANT,表示目标图像中“离群点(outliers)”的像素值不会被此函数修改。
- borderValue——边界颜色,默认Scalar()黑色,当有常数边界时使用的值
四、立体匹配
1. 立体匹配算法
OpenCV实现了两种立体匹配算法,并且它们共享通用的对象接口。
- 快匹配算法(block matching,BM),它使用小的 “绝对值之差的和” (SAD)窗口来寻找左右立体校正图像之间的匹配点,更适合与强纹理图像;
- 半全局块匹配算法(semi-global block matching,SGBM),为SGM的变形形式。
使用BM算法和SGBM算法:
这两个算法都需要调用create()函数获取一个智能指针指向cv::StereoBM对象或者 cv::StereoSGBM对象,但在参数设置上是不相同的。
1.BM算法
Opencv立体匹配算法BM、SGBM、GC算法的状态参数
静态create()函数有两个参数,numDisparities 和 blockSize;
Ptr<StereoBM> bm =StereoBM::create (
int numDisparities = 0,
int blockSize = 21
)
- numDisparities:视差搜索范围。对于每个像素,算法将找到从0(默认最小视差)到numdifferences的最佳视差。然后可以通过改变最小视差来移动搜索范围。
- blockSize 设置每个像素周围的区域大小,也会计算 “绝对差分符号” 度量。
这个值越大找到的错误匹配越少。但是请注意,算法的计算成本不仅与窗口面积(即窗口大小的平方)相关,靠近不连续处(对象的边缘)将可能找不到匹配,结果是空白区域,因为在接近对象边缘处不存在视差。且窗口越大,空白区域的厚度越大;窗口越大,获得的深度图越模糊,意味着视差图中对象的剪影将越平滑,以一种近似的方式捕捉实际剪影。
在调用create()函数创建BM算法的智能指针后,还可以通过一系列set*()函数进一步配置。
可设置的参数如下:
- numDisparities:最大视差与最小视差的差值,该值总是大于零,且必须能被16整除。
- PreFilterCap:预滤波图像像素的截断值。该算法首先在每个像素处计算x导数,并按[-preFilterCap, preFilterCap]间隔剪辑其值。结果值被传递给Birchfield-Tomasi像素代价函数。
- PreFilterSize:用于设置滤波器的窗口大小
- PreFilterType:用于设置滤波器的类型
- ROI1:与setROI2设置对矫正后的图像的裁剪区域
- ROI2:与setROI1设置对矫正后的图像的裁剪区域
- TextureThreshold:用于设置低纹理区域的判断阈值
- UniquenessRatio:计算出的最佳(最小)成本函数值应该“赢得”第二个最佳值的百分比余量,从而认为找到的匹配是正确的。通常情况下,5-15范围内的值就足够了。
- speckleWindowSize:检查视差连通区域变化度的窗口大小, 值为 0 时取消 speckle 检查,int 型
- speckleRange:视差变化阈值,当窗口内视差变化大于阈值时,该窗口内的视差清零,int 型
- PreFilterType :滤波器类型,int类型
set函数指的是“set”+“参数名称”+“()”,且()*中写入该参数的取值,具体设置的方法如下
bm->setROI1(validROIL);
bm->setROI2(validROIR);
bm->setPreFilterCap(31);
bm->setMinDisparity(0);
bm->setNumDisparities(numDisparities * 16 + 16);
bm->setTextureThreshold(10);
bm->setUniquenessRatio(uniquenessRatio);
bm->setSpeckleWindowSize(100);
bm->setSpeckleRange(32);
bm->setDisp12MaxDiff(-1);
2. SGBM算法
SGBM算法在利用create()函数创建cv::StereoSGBM对象的智能指针时,就设置了其用到的所有参数。
在OpenCV StereoSGBM类中给出的create()函数声明是这样的
static Ptr<StereoSGBM> cv::StereoSGBM::create(
int minDisparity = 0,
int numDisparities = 16,
int blockSize = 3,
int P1 = 0,
int P2 = 0,
int disp12MaxDiff = 0,
int preFilterCap = 0,
int uniquenessRatio = 0,
int speckleWindowSize = 0,
int speckleRange = 0,
int mode = StereoSGBM::MODE_SGBM
)
参数含义如下
- minDisparity:最小可能的视差值。通常为零,但有时校正算法会使图像移位,因此需要对该参数进行相应调整
- numDisparities:最大视差减去最小视差。该值总是大于零。在当前实现中,该参数必须能被16整除
- blockSize:匹配的块大小。必须是奇数>=1。通常情况下,它应该在3-11范围
- P1:控制视差平滑的第一个参数,P1是对相邻像素之间视差变化的加减1的惩罚,8number_of_image_channelsSADWindowSize*SADWindowSize
- P2:第二个控制视差平滑的参数。值越大,视差越平滑。P2是相邻像素之间视差变化大于1的惩罚。该算法要求P2 > P1,32number_of_image_channelsSADWindowSize*SADWindowSize
- disp12MaxDiff:左右的最大允许差异(以整数像素单位)差异检查,将其设置为非正数以禁用检查
- preFilterCap:预滤波图像像素的截断值。首先是算法在每个像素处计算x导数,并按[-preFilterCap, preFilterCap]间隔剪辑其值
- uniquenessRatio:最佳(最小)计算成本函数的百分比边际Value应该“赢得”第二个最佳值,从而认为找到的匹配是正确的。通常在5-15个范围内
- speckleWindowSize:平滑视差区域的最大大小,以考虑它们的噪点和无效。将其设置为0以禁用散斑过滤。否则,将其设置在50 - 200的范围内
- speckleRange:每个连接组件内的最大视差变化,如果进行散斑过滤,将参数设置为正值,将隐式地乘以16。通常1或2就足够了
- mode:枚举类型,选择MODE_SGBM = 0,MODE_HH = 1,MODE_SGBM_3WAY = 2三种计算模式。
SGBM算法也可以像BM算法那样使用一些列set*函数设置其参数。
一个使用实例: C++实现opencv中的SGBM匹配算法并实现调参
2. 计算视差矩阵
用opencv的stereoSGBM的compute函数得到的视差图的注意事项
以上述的SGBM算法为例,计算视差矩阵使用的是StereoMatcher类的compute函数。
StereoMatcher是stereoSGBM的基类。
virtual void cv::stereoMatcher::compute( InputArray left,
InputArray right,
OutputArray disparity
)
参数:
- left:左边的8位单通道图片
- right:与左边图片同类型的右边图片
- disparity:输出视差图。一些算法,如stereoBM或stereosGBM计算16位定点视差图(其中每个视差值有4个小数位),而其他算法输出32位浮点视差图。
注意事项参考上述链接:
在用opencv的stereoSGBM类得到视差图后,想得到深度图,很多都是通过normalize函数或者converTo函数,把这个16位的视差图转为8位的也就是像素值在0-255之间的视差图,而且把这个转化过程叫归一化。
这就是坑点所在,这么转确实可以让这个作为深度图的灰度图看起来层次感更为明显,但是也让这个作为深度图的图像像素值的单位没法严格确定。
我们通常深度图的像素值是以毫米为单位的,包括D435i的深度图,这样你发出的深度图话题再被别人订阅时,别人也好按照统一的尺度去处理。不能单纯为了显示得更有层次感而让像素值失去现实尺度意义。
3. 显示深度图
首先将视差图转换为深度图,很多都是通过normalize函数或者converTo函数,把这个16位的视差图转为8位的也就是像素值在0-255之间的深度图,而且把这个转化过程叫归一化。
OPENCV函数介绍:normalize()
void cv::normalize( InputArray src,
InputOutputArray dst,
double alpha = (1.0),
double beta = (0.0),
int norm_type = 4,
int dtype = -1,
InputArray mask = noArray()
)
参数说明:
- src:输入数组,输入视差矩阵
- dst:输出数组,与src大小一样
- alpha :用来规范值或者规范范围,并且是下限
- beta :只用来规范范围并且是上限,因此只在NORM_MINMAX中起作用
- norm_type :归一化选择的数学公式类型
- dtype:当为负值,输出数组在大小、深度、通道数上都等于输入;当为正,输出数组只在深度与输入数组不同,不同的地方由dtype决定
- mask:掩码。选择感兴趣区域,选定后只能对该区域进行操作。
其实alpha和beta的大小关系并不重要,alpha和beta只是确定范围的两个边界值,其确定的范围可以表示为:[min(alpha,beta), max(alpha,beta)]
归一化选择的数学公式类型介绍
opencv中归一化函数cv2.normalize()的原理
-
NORM_MINMAX:数组的数值被平移或缩放到一个指定的范围,线性归一化。
-
NORM_INF: 归一化数组的(切比雪夫距离)L∞范数(绝对值的最大值)
-
NORM_L1 : 归一化数组的(曼哈顿距离)L1-范数(绝对值的和)
-
NORM_L2: 归一化数组的(欧几里德距离)L2-范数
四种norm_type的区别:
NORM_L1、NORM_INF、NORM_L2模式下归一化结果与beta无关,只与alpha有关
NORM_MINMAX中alpha、beta都起作用,同时需要注意的是alpha和beta的取值顺序与归一化结果无关。即alpha=255,beta=0和alpha=0,beta=255最后的归一化结果是相同的。
开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!
更多推荐
所有评论(0)