当前位置:   article > 正文

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

双目 重构


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

基本步骤

  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中

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

匹配算法

使用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);
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17

三维重构

我只想说,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中

  • 1
  • 2
  • 3

点云去噪

在上述过程中获得了稠密点的坐标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();
	
}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69

点云显示

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

总结

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

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

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/你好赵伟/article/detail/637929
推荐阅读
相关标签
  

闽ICP备14008679号