基于OpenCV的纯双目三维重建流程

一、双目立体标定

使用stereoCalibrate()进行标定,生成第一个相机点转换为第二个相机点的旋转矩阵R和平移向量T。(这里和Halcon的相反,halcon是第二相机点转换到第一相机点)
在这里插入图片描述

二、双目极线校正

1.使用stereoRectify()计算两个相机的极线校正矩阵(Bouguet法)。

void cv::stereoRectify 	( 	
        InputArray 	cameraMatrix1,           //输入第一个相机的内参矩阵
		InputArray 	distCoeffs1,             //输入第一个相机的畸变系数
		InputArray 	cameraMatrix2,           //输入第二个相机的内参矩阵
		InputArray 	distCoeffs2,             //输入第二个相机的畸变系数
		Size 	imageSize,                   //输入图像的尺寸大小
		InputArray 	R,                       //输入stereoCalibrate()生成的双目标定旋转矩阵
		InputArray 	T,                       //输入stereoCalibrate()生成的双目标定平移向量
		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 )

在这里插入图片描述
2.使用initUndistortRectifyMap()计算畸变矫正和校正变换映射。
在OpenCV中可以通过initUndistortRectifyMap()函数获得原始图像和矫正+校正图像之间的映射表。
在这里插入图片描述
3.使用remap()函数根据映射表对整个图像进行映射处理实现去畸变及极线校正。

注:stereoRectify()是需要标定参数,stereoRectifyUncalibrated()(Hartley法)不用标定内参参数需要基础矩阵F,stereoRectify()的精度会高一些。
可通过initInverseRectificationMap() 计算极线校正后图像转换为极线校正前图像,用于投影仪的逆相机变换。
Computes the projection and inverse-rectification transformation map. In essense, this is the inverse of initUndistortRectifyMap to accomodate stereo-rectification of projectors (‘inverse-cameras’) in projector-camera pairs.
在这里插入图片描述

三、寻找同名点

opencv有对应的函数这里不展开描述。
在这里插入图片描述
1、局部匹配
2、半全局匹配
3、全局匹配

四、计算三维点

使用reprojectImageTo3D()通过视差图计算三维点,计算的三维点的坐标系是校正后的第一个相机坐标系的。

void cv::reprojectImageTo3D( 	
        InputArray 	    disparity,       //输入视差图
		OutputArray 	_3dImage,        //输出三维点图,生成的点是在第一个相机校正后的坐标系下的点
		InputArray 	    Q,               //输入极线校正生成的Q矩阵
		bool 	        handleMissingValues = false,
		int 	        ddepth = -1 )

在这里插入图片描述
在这里插入图片描述
注意实际计算并非像官方给出的直接左乘Q这么简单,实际代码是左乘Q后,还要除以左乘Q结果的第四项。

    //opencv源码计算三维点部分核心代码
        for( x = 0; x < cols; x++)
        {
            double d = sptr[x];
            Vec4d homg_pt = _Q*Vec4d(x, y, d, 1.0);
            dptr[x] = Vec3d(homg_pt.val);
            dptr[x] /= homg_pt[3];
 
            if( fabs(d-minDisparity) <= FLT_EPSILON )
                dptr[x][2] = bigZ;
        }

三维空间点 P 在相机的成像图如下图所示。由图可以看出,根据相似三角形原理, 有以下关系:
在这里插入图片描述
在这里插入图片描述

这里x和y是像素坐标,x0和y0是图像坐标系转像素坐标系的原点差(是图像x和y的一半,也就是Cx和Cy)。
(x-x0)和(y-y0)就是将像素坐标转换为图像坐标的过程。
在这里插入图片描述

因此,当已知三维空间上任意一点的在不同图像上的视差,再根据相机的参数,就可以知道该点在相机坐标系的三维坐标。

五、转换为原主相机坐标系(可选,用于手眼标定等)

使用极线校正生成第一个相机坐标系校正矩阵R的逆矩阵转换为原第一个相机坐标系。
机械手基坐标系下的点 = 手眼标定转换矩阵 * R的逆矩阵 * 三维点,这里将R的逆矩阵转换为齐次坐标后左乘手眼标定转换矩阵,得到最终转换矩阵。
或者
使用极线校正后的图像进行手眼标定。

整体代码后续择机补充!

参考:

https://docs.opencv.org/4.x/d9/d0c/group__calib3d.html#ga1bc1152bd57d63bc524204f21fde6e02
https://docs.opencv.org/4.x/d9/d0c/group__calib3d.html#ga617b1685d4059c6040827800e72ad2b6
双目相机标定以及立体测距原理及OpenCV实现(上)https://zhuanlan.zhihu.com/p/561399264
双目视觉:reprojectImageTo3D函数https://blog.youkuaiyun.com/qq_49110168/article/details/144836805

### 使用 OpenCV 实现双目视觉 3D 重建 #### 双目标定 为了实现精确的三维重建,首先需要对两个相机进行标定。这一步骤是为了获取内参矩阵和畸变系数。 ```cpp #include "opencv2/calib3d.hpp" // ...其他必要的头文件... std::vector<std::vector<cv::Point3f>> objectPoints; std::vector<std::vector<cv::Point2f>> imagePointsL, imagePointsR; cv::Mat cameraMatrixL, distCoeffsL, cameraMatrixR, distCoeffsR; cv::stereoCalibrate(objectPoints, imagePointsL, imagePointsR, cameraMatrixL, distCoeffsL, cameraMatrixR, distCoeffsR, imageSize); ``` 此过程涉及棋盘格图案检测以及内外参数计算[^1]。 #### 立体校正 完成标定之后,下一步是对图像执行立体校正操作,使得两幅图像是在同一平面上拍摄的效果,从而简化后续处理流程。 ```cpp cv::Rect validRoi[2]; cv::stereoRectify(cameraMatrixL, distCoeffsL, cameraMatrixR, distCoeffsR, imageSize, R, T, RL, RR, P1, P2, Q, cv::CALIB_ZERO_DISPARITY, 1, imageSize, &validRoi[0], &validRoi[1]); cv::initUndistortRectifyMap(cameraMatrixL, distCoeffsL, RL, P1, imageSize, CV_16SC2, mapL1, mapL2); cv::initUndistortRectifyMap(cameraMatrixR, distCoeffsR, RR, P2, imageSize, CV_16SC2, mapR1, mapR2); cv::remap(imgL, imgLUndistorted, mapL1, mapL2, INTER_LINEAR); cv::remap(imgR, imgRUndistorted, mapR1, mapR2, INTER_LINEAR); ``` 通过上述代码可以得到矫正后的左视图`imgLUndistorted` 和右视图 `imgRUndistorted` 。 #### 计算视差图 利用半全局匹配算法(SGBM),可以从一对经过校准的图片中提取出密集的像素级对应关系——即所谓的视差图(disparity map)。 ```cpp int minDisparity = 0; int numDisparities = 16*5; // 必须是16的倍数 int blockSize = 9; int disp12MaxDiff = 1; int uniquenessRatio = 10; int speckleWindowSize = 100; int speckleRange = 32; cv::Ptr<cv::StereoSGBM> sgbm = cv:: StereoSGBM :: create( minDisparity, numDisparities, blockSize, p1, p2, disp12MaxDiff, preFilterCap, uniquenessRatio, speckleWindowSize, speckleRange, mode); sgbm->compute(imgLUndistorted, imgRUndistorted, disparity); ``` 这段程序创建了一个 SGBM 对象并设置了各项参数来优化性能与准确性[^2]。 #### 构建点云模型 最后,在获得了高质量的视差数据后,就可以借助重投影矩阵Q将二维坐标转换成三维空间中的位置信息,进而形成完整的物体表面表示形式—也就是常说的“点云”。 ```cpp cv::reprojectImageTo3D(disparity, points3D, Q); pcl::PointCloud<pcl::PointXYZ>::Ptr cloud(new pcl::PointCloud<pcl::PointXYZ>); for(int v=0;v<disparity.rows;++v){ for(int u=0;u<disparity.cols;++u){ if(points3D.at<cv::Vec3f>(v,u)[2]>0){ // 排除负深度值或零深度值 pcl::PointXYZ point; point.x = points3D.at<cv::Vec3f>(v,u)[0]; point.y = points3D.at<cv::Vec3f>(v,u)[1]; point.z = points3D.at<cv::Vec3f>(v,u)[2]; cloud->points.push_back(point); } } } cloud->width = (int)cloud->points.size(); cloud->height = 1; ``` 以上就是基于OpenCV库构建简单版本的双目视觉系统的全过程概述。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值