StereoVision--立體視覺(3)
緊接著上次的文章,這次將詳細的說明一下立體匹配(這裡需要說明一樣,這裡所有的圖片都是存在對極約束的,也就是完成過校正,可以說對於左右兩幅影象上的匹配點,它們的y值相等,而x之間的差別就是視差d),並且會給出最近看的一些文章,以及對文章的一些理解。(PS:目前,立體匹配領域,主要有兩個評測網站,一個是KITTI(
http://www。
cvlibs。net/datasets/kit
ti/eval_stereo_flow。php?benchmark=stereo
),另一個是middlebury(
http://
vision。middlebury。edu/s
tereo/
),兩個網站上的演算法都交叉,但是又不完全一樣)
對於影象的匹配我們都知道,就是對兩幅圖片或者更多圖片,找到它們相似的地方。那麼對於立體匹配而言,就是基於同一場景得到的多張二維圖,透過找到相同點進一步還原場景的三維資訊,一般採用的影象是雙目影象,下面給出Middlebury上的一組標準圖,第一幅圖和第二幅圖分別是雙目相機得到的左圖和右圖,第三幅圖就是視差圖。可以很明顯的看出來越亮的地方表示視差越大,也就離相機越近,反之離相機也就越遠。
下面給出立體匹配的四個最基本的步驟:
匹配代價計算(Matching Cost Computation:CC)
代價聚合(Cost Aggregation:CA)
視差計算(Disparity Computation )
視差精化(Disparity Refinement ):對上一步得到的粗估計的視差圖進行精確計算,策略有很多,例如plane fitting,BP,動態規劃等。這裡不再熬述。
同樣的,立體匹配也分為全域性匹配和區域性匹配兩種,這兩種在匹配步驟上也有所差異。
全域性匹配
全域性立體匹配演算法一般來說會省略第二步,主要是採用了全域性的最佳化理論方法估計視差,建立全域性能量函式,透過最小化全域性能量函式得到最優視差值。其能量函式由資料項和平滑項構成。
全域性匹配演算法得到的結果比較準確,但是其執行時間比較長,不適合實時執行。主要的演算法有圖割(graph cuts)、信念傳播(belief propagation)、動態規劃(Dynamic Programming )等演算法。
區域性匹配
基本原理是給定在一幅影象上的某一點,選取該畫素點鄰域內的一個子視窗,在另一幅影象中的一個區域內,根據某種相似性判斷依據,尋找與子視窗影象最為相似的子圖,而其匹配的子圖中對應的畫素點就為該畫素的匹配點。通常方法有SAD、SSD、NCC等等。
下面來詳細的一步一步的介紹立體匹配的每一個步驟,並且結合一個具體的例子來進行說明。
匹配代價計算
一般來說,匹配代價的計算是對左右兩幅影象的每一個畫素點而言的,可以認為定義了一個處理左右兩幅影象中匹配畫素點的函式
,這裡的
是我們定義的視差範圍,也就是對於左圖中的一個畫素點
,我們給它在右圖中定義一個尋找視差的範圍
。
舉個例子:假設我們對於匹配影象定義了視差的尋找範圍為0~16,也就是說
(
是整數),對於左圖的一個畫素點
在右圖中能夠計算匹配代價的區域就是
這之間。
我們是不是可以這樣想,對於每一個畫素點,在給定的視差範圍內,我們找到
,我們是不是就可以認為這個最小值就代表這是正確的匹配點,而這個最小值對應的
就是我們要找的視差(這一步運用的是Winner Take All: WTA策略)。如果對每一個畫素都這樣進行操作的話,我們是不是就可以得到最後的視差圖。顯然這是成立的,但是這樣做的最後的視差圖效果非常的差,在這裡可以給大家展示一下這樣簡單操作後的結果。
左圖(標準圖) 右圖(簡單處理後的視差圖)
代價聚合
我們從上圖中基於點之間的匹配很容易受噪聲的影響,所以我們需要在點的周圍建立一個window,讓畫素塊和畫素塊之間進行比較,這樣肯定靠譜些。代價聚合往往是區域性演算法或者半全域性演算法才會使用,全域性演算法拋棄了window,採用基於全圖資訊的方式建立能量函式。
或者說,我們換一種看法,可以看做是對匹配代價計算的結果進行濾波的一個過程。
舉個例子:假設我們有一個3*3的窗,在這個窗上進行代價聚合的函式為
,首先看對原始左影象一個畫素點的聚合:
(結果是視窗的中心點的值),同理對於右邊的影象,需要在視差範圍內做聚合:
。然後,下一步就需要對處理之後的畫素計算匹配代價,並且找到它們的代價最小值:
。我們可以看到,最後同一的公式是一個關於視差
的函式。
再看一看以濾波的方式來解釋,這裡我們直接對得到的視差圖進行代價聚合:
(
表示視差圖座標),這裡的
就是經過匹配代價計算後的畫素值。可以看到最後兩種方法的結果都是一樣的(可以使用最簡單的Box filter去驗證)。
視差計算
其實在上面兩個步驟的時候,就已經提及到了視差計算。視差計算最常用的策略就是WTA策略。這一步也可以分為區域性演算法與全域性演算法,區域性演算法直接最佳化代價聚合模型。而全域性演算法,要建立一個能量函式。輸出的是一個粗略的視差圖。
視差精化
這一步也就是對得到的視差進行最佳化的過程,通常方法都是“亞畫素求精+均值濾波”。但是不可否認的是,立體匹配最關鍵的步驟仍然是代價計算和代價聚合步驟。
下面以區域性匹配中的SAD做為例子來更好的理解立體匹配,其中著重考慮的是代價計算和代價聚合。
這裡簡單的介紹一下SAD,SAD就是Sum of Absolute differences,下面給出SAD計算公式:
下面給出實現的程式碼:
#include
#include
#include
using
namespace
std
;
using
namespace
cv
;
int
main
(){
int
hWin
=
3
;
//窗的大小為3*3
cout
<<
“hWin: ”
<<
hWin
<<
“; ”
<<
“compare length: ”
<<
compareLength
<<
endl
;
cout
<<
“SAD test”
<<
endl
;
Mat
left_image
=
imread
(
“disp2。png”
);
Mat
right_image
=
imread
(
“disp6。png”
);
int
imageWidth
=
left_image
。
cols
;
int
imageHeight
=
left_image
。
rows
;
//cout << left_image。size();
Mat
SAD_image
=
Mat
(
left_image
。
size
(),
CV_8UC1
,
1
);
Mat
MatchLevel_image
=
Mat
(
left_image
。
size
(),
CV_8UC1
,
1
);
//設定視差尋找範圍d為0~7
int
minDBounds
=
0
;
int
maxDBounds
=
7
;
namedWindow
(
“Left”
);
namedWindow
(
“Right”
);
namedWindow
(
“SAD”
);
namedWindow
(
“MatchLevel”
);
imshow
(
“Left”
,
left_image
);
imshow
(
“Right”
,
right_image
);
/*SAD Transform */
int
i
,
j
,
m
,
n
,
k
;
unsigned
char
centerPixel
=
0
;
unsigned
char
neighborPixel
=
0
;
int
bitCount
=
0
;
unsigned
int
bigger
=
0
;
int
sum
=
0
;
unsigned
int
*
match_level
=
new
unsigned
int
[
maxDBounds
-
minDBounds
+
1
];
int
temp_min
=
0
;
int
temp_index
=
0
;
unsigned
char
*
dst
;
unsigned
char
*
left_src
=
NULL
;
unsigned
char
*
right_src
=
NULL
;
unsigned
char
left_pixel
=
0
;
unsigned
char
right_pixel
=
0
;
unsigned
char
sub_pixel
=
0
;
for
(
i
=
0
;
i
<
imageHeight
;
i
++
)
{
for
(
j
=
0
;
j
<
imageWidth
;
j
++
)
{
//計算每一個畫素的SAD
for
(
k
=
minDBounds
;
k
<=
maxDBounds
;
k
++
)
{
sum
=
0
;
for
(
m
=
i
-
hWin
;
m
<=
i
+
hWin
;
m
++
)
{
for
(
n
=
j
-
hWin
;
n
<=
j
+
hWin
;
n
++
)
{
if
(
m
<
0
||
m
>=
imageHeight
||
n
<
0
||
n
>=
imageWidth
)
{
sub_pixel
=
0
;
}
else
if
(
n
+
k
>=
imageWidth
)
{
sub_pixel
=
0
;
}
else
{
left_src
=
(
unsigned
char
*
)
left_image
。
data
+
m
*
left_image
。
step
+
n
+
k
;
right_src
=
(
unsigned
char
*
)
right_image
。
data
+
m
*
right_image
。
step
+
n
;
left_pixel
=
*
left_src
;
right_pixel
=
*
right_src
;
if
(
left_pixel
>
right_pixel
)
{
sub_pixel
=
left_pixel
-
right_pixel
;
}
else
{
sub_pixel
=
right_pixel
-
left_pixel
;
}
}
sum
+=
sub_pixel
;
}
}
match_level
[
k
]
=
sum
;
//cout<<“SUM:”< } /*尋找最佳匹配點*/ temp_min = 0 ; temp_index = 0 ; for ( m = 1 ; m < maxDBounds - minDBounds + 1 ; m ++ ) { //cout<<“match_level[m]:”< if ( match_level [ m ] < match_level [ temp_index ]) { temp_min = match_level [ m ]; temp_index = m ; } } dst = ( unsigned char * ) SAD_image 。 data + i * SAD_image 。 step + j ; * dst = temp_index * 8 ; dst = ( unsigned char * ) MatchLevel_image 。 data + i * MatchLevel_image 。 step + j ; * dst = temp_min ; } } imshow ( “SAD” , SAD_image ); imshow ( “MatchLevel” , MatchLevel_image ); imwrite ( “MatchLevel。jpg” , MatchLevel_image ); imwrite ( “depth。jpg” , SAD_image ); waitKey ( 0 ); return 0 ; } 下面是標準視差圖和SAD的結果圖 左圖(標準圖) 右圖(SAD的視差圖) 我們可以很明顯的看到,SAD的結果和最終的標準圖還是有很大的差別,圖中的有些特徵也沒有很好的還原出來。 後面將開始著重於對立體匹配文章的解讀,將一些文章的讀後感跟大家分享。