1. 程式人生 > >【雙目視覺探索路2】獲取視差圖(未完待續)

【雙目視覺探索路2】獲取視差圖(未完待續)

如前所述,本系列首先通過視差圖的實現,從直觀的角度對雙目立體視覺首先進行個大概的瞭解。

立體視覺的整體概述:

雙目立體視覺測物體空間資訊的流程順序應為: 如前一講所述,整個雙目立體視覺的實現流程可分為標定以及立體視覺的實現。

標定:

標定的目的為獲得相機的內外參,其基本原理是基於相機成像平面與空間的對映對應關係(單應性)。內參為:主點位置(cx,cy),橫縱向的焦距(fx,fy);外參為相機成像平面與標定平面的R(rotation)、T(transtion)矩陣。

立體視覺實現:

後面的立體標定、立體矯正、立體匹配以及對映到三維的深度對映四個部分歸屬於立體視覺實現這一部分內容。 由上一講可知:立體視覺的基本模型是基於雙目前向平行,而現實中基本不可能做到這一點,故需要通過演算法進行解決。立體標定、矯正以及匹配的大部分就是基於這個目的而採用的方法。 立體標定
的目的是獲取左右相機的空間轉換關係,通過這一步可以獲取兩種矩陣:本徵矩陣(E)以及基礎矩陣(F)。 立體矯正的目的是使得原來不平行的影象成為行對準影象,其利用了立體標定的R/T陣,輸出行對準影象,為下一步匹配的工作量減少提供了便捷。 立體匹配的目的是輸出視差圖,輸入為經過立體矯正的影象,輸出服務於下一步:對映到三維空間座標。 立體標定 立體校正

參考資料:

依據雙目標定可以計算出兩個相機座標系關係的矩陣:R&T,立體校正的目的就是轉換為前向平行的雙目配置。 Bouguet校正的原理為:將Rrect左乘到R分解後作用於左右座標系的矩陣。

重投影矩陣

重投影矩陣Q實現了世界座標系與畫素座標系之間的轉換 Q[x,y,d,1]T=[X,Y,Z,W]T

程式碼實現

一共有三個過程 計算R&T矩陣->計算校正LUT->校正兩幅影象檢驗結果 計算R&T矩陣 呼叫stereoRectify函式即可 計算校正查詢對映表 該部分作用是將原始影象和矯正後的影象上的點進行一一對應,呼叫函式為initUndistortRectifyMap函式。請注意:是分別對左右相機進行校正查詢對映 --------------------------------------------------------------------------------------------------------------------

由已知內外引數輸出視差圖

實現這一過程主要涉及的內容為立體矯正以及立體匹配。立體矯正可以通過呼叫opencv中的函式進行實現,而立體匹配有幾種不同的方法,可以從中選一種自己看的比較順眼的匹配方法進行探索。 這一部分首先將扔出一個相關的實驗程式以及最終的實驗結果;在實驗的結果上對這一部分內容進行補充。 再扔一下這部分涉及的連結:

程式實現:

Raw data:

Experiment Results:

SAD視差圖

Coding:

1,SAD

class SAD{
private:
	int winSize;//卷積核尺寸
	int DSR;//視差搜尋範圍
public:
	SAD() :winSize(7), DSR(30){}
	SAD(int _winSize, int _DSR) :winSize(_winSize), DSR(_DSR){}
	Mat computerSAD(Mat&L, Mat&R);//計算SAD
};
Mat SAD::computerSAD(Mat&L, Mat&R){
	int Height = L.rows;
	int Width = L.cols;
	Mat Kernel_L(Size(winSize, winSize), CV_8U, Scalar::all(0));
	//CV_8U:0~255的值,大多數影象/視訊的格式,該段設定全0矩陣
	Mat Kernel_R(Size(winSize, winSize), CV_8U, Scalar::all(0));
	Mat Disparity(Height, Width, CV_8U, Scalar(0));


	for (int i = 0; i < Width - winSize; ++i){
		for (int j = 0; j < Height - winSize; ++j){
			Kernel_L = L(Rect(i, j, winSize, winSize));//L為做影象,Kernel為這個範圍內的左圖
			Mat MM(1, DSR, CV_32F, Scalar(0));//定義匹配範圍

			for (int k = 0; k < DSR; ++k){
				int x = i - k;
				if (x >= 0){
					Kernel_R = R(Rect(x, j, winSize, winSize));
					Mat Dif;
					absdiff(Kernel_L, Kernel_R, Dif);
					Scalar ADD = sum(Dif);
					float a = ADD[0];
					MM.at<float>(k) = a;
				}
				Point minLoc;
				minMaxLoc(MM, NULL, NULL, &minLoc, NULL);

				int loc = minLoc.x;
				Disparity.at<char>(j, i) = loc * 16;
			}
			double rate = double(i) / (Width);
			cout << "已完成" << setprecision(2) << rate * 100 << "%" << endl;
		}
	}
	return Disparity;
}

int main(){
	Mat left = imread("1.png");
	Mat right = imread("2.png");
	//-------影象顯示-----------
	namedWindow("leftimag");
	imshow("leftimag", left);

	namedWindow("rightimag");
	imshow("rightimag", right);
	//--------由SAD求取視差圖-----
	Mat Disparity;

	SAD mySAD(7, 30);
	Disparity = mySAD.computerSAD(left, right);
	//-------結果顯示------
	namedWindow("Disparity");
	imshow("Disparity",Disparity);
	//-------收尾------
	waitKey(0);
	return 0;
}

程式解讀:

可以看到SAD演算法的效果確實很一般,而且在實際執行過程中,結果圖的邊界會出現黑色塊的現象。

主程式中先定義了卷積核尺寸為7,視差搜尋範圍為30,參見建構函式Mat SAD():winSize(),DSR(){}

computerSAD計算出視差圖。

其程式實現如下:

首先定義左右兩幅影象的kernel,大小為winSize尺寸,並定義視差圖Mat矩陣

----------------------------------------------------------------------------------------------------------------

第二步就是在影象內實現塊的遍歷匹配

塊的大小為winSize,範圍為(0:width-winSize,0:height-winSize);

遍歷方法如下

		for (int k = 0; k < DSR; ++k){
				int x = i - k;
				if (x >= 0){
					Kernel_R = R(Rect(x, j, winSize, winSize));
					Mat Dif;
					absdiff(Kernel_L, Kernel_R, Dif);
					Scalar ADD = sum(Dif);
					float a = ADD[0];
					MM.at<float>(k) = a;
				}

排序後便可輸出視差圖。

SGBM演算法是一種全域性匹配演算法,立體匹配的效果明顯好於區域性匹配演算法,在OpenCV中是把SGBM作為一個類對該演算法進行實現的。