双目立体视觉三维重构总结


前面多多少少记录一些相关知识,由于相关工作还在继续,加上网上的教程总不是十分完善。这里做一个总结,希望自己能够加深对这个过程的整体的理解与认识。

基本步骤

  1. 相机标定
  2. 图片采集
  3. 立体校正
  4. 匹配算法
  5. 三维重构
  6. 点云去噪
  7. 点云显示

相机标定

使用的Matlab标定工具箱,需要注意的点有:

  1. 每个相机单独标定,之后再标定双目相机的位姿
  2. 标定单目时需要选择畸变个数,一般如果不是鱼眼镜头,只需要标定两个径向畸变、两个切向畸变。
  3. 图像采集,和标定精度测试移步这里
  4. Matlab标定参数用于opencv移步这里

图像采集

我使用的非常基础的USB双目相机,两个相机之间居然还存在色差。。。videoCapture就可以了

立体校正

立体校正对于双目的最主要的目的是为稠密匹配算法提供便利。opencv中有具体的立体校正函数

stereoRectify(cameraMatrixL, distCoeffL, cameraMatrixR, distCoeffR,
		imageSize, R, T, Rl, Rr, Pl, Pr, Q, CALIB_ZERO_DISPARITY,
		0, imageSize, &validROIL, &validROIR);
	//再采用映射变换计算函数initUndistortRectifyMap得出校准映射参数,该函数功能是计算畸变矫正和立体校正的映射变换
	initUndistortRectifyMap(cameraMatrixL, distCoeffL, Rl, Pl(Rect(0, 0, 3, 3)),
		imageSize, CV_32FC1, mapLx, mapLy);
	initUndistortRectifyMap(cameraMatrixR, distCoeffR, Rr, Pr(Rect(0, 0, 3, 3)),
		imageSize, CV_32FC1, mapRx, mapRy);
	remap(grayImageL, rectifyImageL, mapLx, mapLy, INTER_LINEAR);
	remap(grayImageR, rectifyImageR, mapRx, mapRy, INTER_LINEAR);//立体校正完成的图片即保存在rectifyImageL/R中

匹配算法

使用SGM算法计算左目视差,其实为了增加准确程度,也会计算右目的视差用于补偿左目视差。但是为了实时性,在这里只计算了左目视差。我自己学习了SGM算法的源代码,也可以使用opencv自带的函数,具体如何使用网上教程很多很多,这里只给出一个示例:

cv::Ptr<cv::StereoSGBM> sgbm = StereoSGBM::create(0, 16, 3);	
sgbm->setPreFilterCap(63);
	int SADWindowSize = 5;
	int sgbmWinSize = SADWindowSize > 0 ? SADWindowSize : 3;
	sgbm->setBlockSize(sgbmWinSize);
	int cn = rectifyImageL.channels();
	sgbm->setP1(8 * cn*sgbmWinSize*sgbmWinSize);
	sgbm->setP2(32 * cn*sgbmWinSize*sgbmWinSize);
	sgbm->setMinDisparity(0);
	sgbm->setNumDisparities(numberOfDisparities);
	sgbm->setUniquenessRatio(10);
	sgbm->setSpeckleWindowSize(100);
	sgbm->setSpeckleRange(1);
	sgbm->setDisp12MaxDiff(1);
	sgbm->setMode(cv::StereoSGBM::MODE_SGBM);
	sgbm->compute(rectifyImageL,//250, 100, 250, 100;150, 100, 400, 300
		rectifyImageR, disp);

三维重构

我只想说,opencv挺好用

	disp.convertTo(disp8, CV_32F, 255 / (numberOfDisparities * 16.));	//1.0/16计算出的视差是CV_16S格式 CV_32F, 255 / ((numDisparities * 16 + 16)*16.) 255 / (numberOfDisparities*16.) 
	reprojectImageTo3D(disp8, xyz, Q, true);//Q是之前立体校正得到的参数计算3D点云保存在xyz中

点云去噪

在上述过程中获得了稠密点的坐标xyz,将其赋值给点云,使用点云滤波即可去噪。PCL点云库的使用移步这里

void two_Eyes::cal_cloud(int disflag)
{
	double plane[3];
	mutex1.lock();
	param_3D.xyz.copyTo(xyz);
	plane[0] = param_3D.plane[0];
	plane[1] = param_3D.plane[1];
	plane[2] = param_3D.plane[2];
	mutex1.unlock();	
	double fenmu = sqrt(powf(plane[0], 2) + powf(plane[1], 2) + 1);
	double Wx, Wy, Wz;
	vector<double>().swap(dis);			//清楚dis中的内容


	double t_c = (double)clock();
	cvtColor(rectifyImageL, rectifyImageL, CV_GRAY2RGB);
	int rowNumber = rectifyImageL.rows;
	int colNumber = rectifyImageL.cols;
	pcl::PointCloud<pcl::PointXYZRGB> cloud_a2,cloud_a3;
	cloud_a2.height = rowNumber;
	cloud_a2.width = colNumber;
	cloud_a2.points.resize(colNumber* rowNumber);
	for (unsigned int u = 0; u < rowNumber; ++u)
	{
		for (unsigned int v = 0; v < colNumber; ++v)
		{
			/*unsigned int num = rowNumber*colNumber-(u*colNumber + v)-1;*/
			unsigned int num = u * colNumber + v;
			cloud_a2.points[num].b = rectifyImageL.at<Vec3b>(u, v)[0];
			cloud_a2.points[num].g = rectifyImageL.at<Vec3b>(u, v)[1];
			cloud_a2.points[num].r = rectifyImageL.at<Vec3b>(u, v)[2];
			
			double Xw = 0, Yw = 0, Zw = 0;
			if (xyz.at<Vec3f>(u, v)[2] < 1000&& xyz.at<Vec3f>(u, v)[2]!=0)					//使用OpenCV计算的结果,感觉更加准确
			{
				Wx= xyz.at<Vec3f>(u, v)[0]; 
				Wy= xyz.at<Vec3f>(u, v)[1]; 
				Wz= xyz.at<Vec3f>(u, v)[2];
				cloud_a2.points[num].x = Wx/1000;
				cloud_a2.points[num].y = Wy/1000;
				cloud_a2.points[num].z = Wz/1000;
					
			}
			
			


		}
	}

///滤波示例
	 创建pcl::StatisticalOutlierRemoval滤波器对象
	//pcl::StatisticalOutlierRemoval<pcl::PointXYZRGB> sor;
	//sor.setInputCloud(cloud_a2.makeShared());
	//sor.setStddevMulThresh(1.0);
	将标准差倍数设为1,意味着超过一点的距离超出平均距离一个标准差以上,
	则该点被标记为离群点,将被移除。
	//sor.filter(cloud_a3);  //滤波结果存储于此 
	
	
	
	
	mutex1.lock();
	param_3D.cloud_a = cloud_a2;
	mutex1.unlock();
	cloud_test.close();
	
}

点云显示

可以直接使用PCL库
我是在QT界面上使用VTK显示的点云,这里涉及到更多的QT界面的基础知识,不再赘述
结果如图:
在这里插入图片描述
点云示例:
在这里插入图片描述
在这里插入图片描述

总结

细心可以避免很多小错误
不怕自己做不好,就怕自己停止尝试

由于相机编号0,1和左右不一定是对应的,所以一定要搞清楚再运行算法,别问我是咋知道的~

  • 5
    点赞
  • 42
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值