雙目立體視覺三維重構總結


前面多多少少記錄一些相關知識,由於相關工作還在繼續,加上網上的教程總不是十分完善。這裏做一個總結,希望自己能夠加深對這個過程的整體的理解與認識。

基本步驟

  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和左右不一定是對應的,所以一定要搞清楚再運行算法,別問我是咋知道的~

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章