雙目立體視覺一直是機器視覺研究領域的發展熱點和難點,“熱”是因為雙目立體視覺有着及其廣闊的應用前景,且随着光學、計算機科學等學科的不斷發展,雙目立體技術将不斷進步直到應用到人類生活的方方面面。“難”則是因為受到錄影機、鏡頭等硬體裝置及一些相關算法的限制,雙目立體視覺的研究及如何更好的應用到生産實際中仍有待在座的各位去進行突破。
一.簡介
雙目立體視覺是機器視覺中的一個重要分支,自上世紀60年代中期開創以來,經過幾十年的發展,如今在機器人視覺、航空測繪、軍事應及醫學成像、工業檢測上應用極其廣泛。雙目立體視覺基于視差原理并利用成像裝置從不同的位置擷取被測物體的左右兩幅圖像,然後根據三角測量原理計算空間點在二維圖像的位置偏差,最後再利用位置偏差進行三維重建來擷取被測物體的三維幾何資訊(本文不對雙目立體視覺的數學原理進行詳細介紹)。
二.雙目立體視覺的三大基本算法的原理及其代碼實作(基于opencv)
雙目立體視覺中常用的基于區域的局部比對準則主要有圖像序列中對應像素差的絕對值之和SAD(sum of absolute differences)、對應像素差的平方之和SSD(sum of squared differences)及半全局比對算法SGM(semi—global matching)。
2.1 SAD(sum of absolute differences)的原理
比對算法SAD的基本思想是對經行對準後的左右視圖圖像的對應像素塊的對應像素差的絕對值進行求和。
其數學公式如下:
SAD比對算法的基本流程如下:
①輸入兩幅已經校正實作行對準的左視圖(Left-Image)及右視圖(Right-Image)。
②對左視圖Left-Image進行掃描標明一個錨點并建構一個類似于卷積核的小視窗。
③用此小視窗覆寫Left-Image,并選擇出小視窗覆寫區域的全部像素點
④同樣用此小視窗覆寫Right-Image,并選擇出小視窗覆寫區域的全部像素點。
⑤Left-Image覆寫區域的像素減去Right-Image覆寫區域的像素,并求出所有像素點的差的絕對值之和。
⑥移動Right-Image的小視窗并重複④—⑤的操作(注意此處會設定一個搜尋範圍,超過此範圍則跳出)
⑦找到這個範圍内SAD值最小的小視窗,此時便找到了與Left-Image錨點的最佳比對的像素塊。
2.1.1 SAD(sum of absolute differences)的基于opencv的C++代碼實作
首先先定義一個SAD 算法的頭檔案(SAD_Algorithm.h):
#include"iostream"
#include"opencv2/opencv.hpp"
#include"iomanip"
using namespace std;
using namespace cv;
class SAD
{
public:
SAD() :winSize(7), DSR(30) {}
SAD(int _winSize, int _DSR) :winSize(_winSize), DSR(_DSR) {}
Mat computerSAD(Mat &L, Mat &R); //計算SAD
private:
int winSize; //卷積核的尺寸
int DSR; //視差搜尋範圍
};
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));
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++) //左圖從DSR開始周遊
{
for (int j = 0; j<Height - winSize; j++)
{
Kernel_L = L(Rect(i, j, winSize, winSize));
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;
//int loc=DSR-loc;
Disparity.at<char>(j, i) = loc * 16;
}
double rate = double(i) / (Width);
cout << "已完成" << setprecision(2) << rate * 100 << "%" << endl; //顯示處理進度
}
return Disparity;
}
調用示例:
#include"SAD_Algorithm.h"
int main(int argc, char* argv[])
{
Mat Img_L = imread("Teddy_L.png", 0); //此處調用的圖像已放入項目檔案夾中
Mat Img_R = imread("Teddy_R.png", 0);
Mat Disparity; //建立視差圖
SAD mySAD(7, 30); //給出SAD的參數
Disparity = mySAD.computerSAD(Img_L, Img_R);
imshow("Teddy_L", Img_L);
imshow("Teddy_R", Img_R);
imshow("Disparity", Disparity); //顯示視差圖
waitKey();
system("pause"); //按任意鍵退出
return 0;
}
複制
2.1.2 SAD算法的運作效果
可以看出SAD算法雖然運作較快,但效果較差。
2.2 SSD(sum of squared differences)的原理
SSD(sum of squared differences)算法大緻與SAD(sum of absolute differences)相似。
其數學公式如下:
因SSD比對算法與SAD比對算法的過程及代碼實作相類似,考慮到篇幅長度的原因,故SSD算法的基本過程及代碼實作在本文中不在贅述,讀者可去自行實作。
2.3 SGBM(semi-global block matching)的原理
SGM(semi-global matching)是一種用于計算雙目立體視覺中的disparity的半全局比對算法。其在opencv中的實作為SGBM(semi-global block matching)。
SGBM的原理:設定一個和disparity map(由每個像素點的disparity所構成)相關的全局能量函數,使這個能量函數最小。
原始文獻:Heiko Hirschmuller. Stereo processing by semiglobal matching and mutual information.Pattern Analysis and Machine Intelligence, IEEE Transactions on, 30(2):328–341, 2008.
其能量函數如下:
D--disparity map(視差圖)
p、q—圖像中的某個像素
Np—像素點Pd 相鄰像素點(一般認為是8連通)
C(P,Dp)--目前像素點的disparity為Dp時,該像素點的cost
P1、P2—懲罰系數,分别适用于當像素P相鄰像素中的disparity值與P的disparity內插補點為1和大于1時
I[]—當[]内的參數為真時傳回1,否則傳回0
SGBM算法的基本流程如下:
①預處理:使用sobel算子對源圖像進行處理,并将經sobel算子處理後的圖像映射為新圖像,并得到圖像的梯度資訊用于後續的計算代價。
②代價計算:使用采樣方法對經預處理得到的圖像梯度資訊計算梯度代價、使用采樣方法對源圖像計算SAD代價。
③動态規劃:預設四條路經,并對路徑規劃的參數P1,P2進行設定(包括P1、P2、cn(圖像通道數量)以及SADWindowsize(SAD視窗大小)的設定)。
④後處理:包括唯一性檢測、亞像素插值、左右一緻性檢測、連通區域的檢測。
2.3.1 SGBM(semi-global block matching)的基于opencv的C++代碼實作
首先先定義一個SGBM算法的頭檔案(SGBM_Algorithm.h):
具體參數見代碼及其注釋(若讀者需優化可自行調整),不再贅述
enum { STEREO_BM = 0, STEREO_SGBM = 1, STEREO_HH = 2, STEREO_VAR = 3, STEREO_3WAY = 4 };
#include"iostream"
#include"opencv2/opencv.hpp"
using namespace std;
using namespace cv;
void calDispWithSGBM(Mat Img_L, Mat Img_R, Mat &imgDisparity8U)
{
Size imgSize = Img_L.size();
int numberOfDisparities = ((imgSize.width / 8) + 15) & -16;
Ptr<StereoSGBM> sgbm = StereoSGBM::create(0, 16, 3);
int cn = Img_L.channels(); //左圖像的通道數
int SADWindowSize = 9;
int sgbmWinSize = SADWindowSize > 0 ? SADWindowSize : 3;
sgbm->setMinDisparity(0); //minDisparity最小視差預設為0;
sgbm->setNumDisparities(numberOfDisparities); //numDisparity視差搜尋範圍,其值必須為16的整數倍;
sgbm->setP1(8 * cn*sgbmWinSize*sgbmWinSize);
sgbm->setP2(32 * cn*sgbmWinSize*sgbmWinSize); //一般建議懲罰系數P1、P2取此兩值,P1、P2控制視差圖的光滑度
//P2越大,視差圖越平滑
sgbm->setDisp12MaxDiff(1); //左右一緻性檢測最大容許誤差門檻值
sgbm->setPreFilterCap(31); //預處理濾波器的截斷值,預處理的輸出值僅保留
//[-preFilterCap, preFilterCap]範圍内的值,參數範圍:1 - 31
sgbm->setUniquenessRatio(10); //視差唯一性百分比:視差視窗範圍内最低代價是次低代價的(1 + uniquenessRatio/100)倍時
//最低代價對應的視內插補點才是該像素點的視差,否則該像素點的視差為 0 ,不能為負值,一般去5——15
sgbm->setSpeckleWindowSize(100); //視差連通區域像素點個數的大小:對于每一個視差點,當其連通區域的像素點個數小于
//speckleWindowSize時,認為該視內插補點無效,是噪點。
sgbm->setSpeckleRange(32); //視差連通條件:在計算一個視差點的連通區域時,當下一個像素點視差變化絕對值大于
//speckleRange就認為下一個視差像素點和目前視差像素點是不連通的。
sgbm->setMode(0); //模式選擇
sgbm->setBlockSize(sgbmWinSize); //設定SAD代價計算視窗,一般在3*3到21*21之間
//blockSize(SADWindowSize) 越小,也就是比對代價計算的視窗越小,視差圖噪聲越大;
//blockSize越大,視差圖越平滑;
//太大的size容易導緻過平滑,并且誤比對增多,展現在視差圖中空洞增多
//三種模式選擇(HH、SGBM、3WAY)
int algorithm = STEREO_SGBM;
if (algorithm == STEREO_HH)
sgbm->setMode(StereoSGBM::MODE_HH);
else if (algorithm == STEREO_SGBM)
sgbm->setMode(StereoSGBM::MODE_SGBM);
else if (algorithm == STEREO_3WAY)
sgbm->setMode(StereoSGBM::MODE_SGBM_3WAY);
Mat imgDisparity16S = Mat(Img_L.rows, Img_L.cols, CV_16S);
sgbm->compute(Img_L, Img_R, imgDisparity16S);
//--Display it as a CV_8UC1 image:16位有符号轉為8位無符号
imgDisparity16S.convertTo(imgDisparity8U, CV_8U, 255 / (numberOfDisparities*16.));
}
調用示例:
#include"SGBM_Algorithm.h"
int main()
{
Mat Img_L = imread("Teddy_L.png", 0);
Mat Img_R = imread("Teddy_R.png", 0);
Mat Disparity8U = Mat(Img_L.rows, Img_R.cols, CV_8UC1);//建立一個Disparity圖像
calDispWithSGBM(Img_L, Img_R, Disparity8U);
imshow("Teddy_L", Img_L);
imshow("Teddy_R", Img_R);
imshow("Disparity", Disparity8U);
waitKey();
system("pause"); //按任意鍵退出
return 0;
}
複制
2.3.2 SGBM算法的運作效果
我還順便調整了SADWindowsize的大小來給讀者探讨并展示當設定不同SADWindowsize大小時對Disparity效果圖的影響,其結果如下(皆為MODE_SGBM模式下):
由上述在不同SADWindowsize大小設定(其他參數保持不變)的效果圖對比下我們可得知如下結論:
SADWindowsize過小時,視差圖的噪聲較多;随着SADWindowsize的增大,視圖越平滑,但當SADWindowsize過大時,視差圖中的空洞現象會增加;故在選擇SADWindowsize的大小時,應選取合适的大小(建議選擇SADWindowsize=9)。
三.雙目立體視覺的發展現狀
目前在國外,雙目立體視覺技術已廣泛運用于生産生活實際中,但在我國,雙目立體視覺技術仍處在起步階段,仍需要在座的各位發奮圖強、力争創新。
3.1 雙目立體視覺的發展方向
就雙目立體視覺的發展現況和發展目标(達到類似于人眼的通用雙目立體視覺)仍是路漫漫其修遠兮,我認為進一步的發展方向可以歸納如下:
①探索新的更具有通用性的計算理論和比對算法結構,以解決目前存在的灰階失真、噪聲幹擾以及幾何畸變的問題。
②提高算法的性能,對算法進行優化,盡可能向實時效果推進。
③建立更有效的雙目體視模型能更充分地反映立體視覺不确定性的本質屬性,為比對提供更多的限制資訊,降低立體比對的難度。
④強調場景與任務的限制,建立适用于不同場景和任務的雙目立體視覺系統的标準和方法。
3.2 雙目立體視覺的國内外發展動态
雙目體視目前主要應用于四個領域:機器人導航、微作業系統的參數檢測、三維測量和虛拟現實。
目前在國外,日本大阪大學自适應機械系統研究院研制了一種自适應雙目視覺伺服系統,利用雙目體視的原理,如每幅圖像中相對靜止的三個标志為參考,實時計算目标圖像的雅可比短陣,進而預測出目标下一步運動方向,實作了對動方式未知的目标的自适應跟蹤。該系統僅要求兩幅圖像中都有靜止的參考标志,無需錄影機參數。
日本奈良科技大學資訊科學學院提出了一種基于雙目立體視覺的增強現實系統(ar)注冊方法,通過動态修正特征點的位置提高注冊精度。
日本東京大學将實時雙目立體視覺和機器人整體姿态資訊內建,開發了仿真機器人動态行長導航系統,為機器人根據實時情況建立實時地圖進而實作障礙物檢測。
日本岡山大學使用立體顯微鏡、兩個ccd攝像頭、微操作器等研制了使用立體顯微鏡控制微操作器的視覺回報系統,用于對細胞進行操作,對鐘子進行基因注射和微裝配等。
麻省理工學院計算機系統提出了一種新的用于智能交通工具的傳感器融合方式,由雷達系統提供目标深度的大緻範圍,利用雙目立體視覺提供粗略的目标深度資訊,結合改進的圖像分割算法,進而實作在高速環境下對視訊圖像中的目标位置進行分割。
華盛頓大學與微軟公司合作為火星衛星“探測者”号研制了寬基線立體視覺系統,使“探測者”号能夠在火星上對其即将跨越的幾千米内的地形進行精确的定位及導航。
在國内,浙江大學機械系統完全利用透視成像原理,采用雙目體視方法實作了對多自由度機械裝置的動态、精确位姿檢測,僅需從兩幅對應圖像中抽取必要的特征點的三維坐标,資訊量少,處理速度快,尤其适于動态情況。
維視圖像公司采用雙目ccd相機,從工業相機内參标定、鏡頭畸變标定、立體比對、特征點分割處理等方面給出了詳細的數學模型和算法接口。其雙目标定軟體ccas采用了張正友平面标定法,可以實作機器人導航、微作業系統的參數檢測、三維測量和虛拟現實等應用。
東南大學電子工程系基于雙目立體視覺,提出了一種灰階相關多峰值視差絕對值極小化立體比對新方法,可對三維不規則物體(偏轉線圈)的三維空間坐标進行非接觸精密測量。
哈工大采用異構雙目活動視覺系統實作了全自主足球機器人導航。将一個固定錄影機和一個可以水準旋轉的錄影機,分别安裝在機器人的頂部和中下部,可以同時監視不同方位視點,展現出比人類視覺優越的一面。即使在實際比賽中當其他傳感器失效的情況下,僅僅依靠雙目協調仍然可以實作全自主足球機器人導航。
火星863計劃課題“人體三維尺寸的非接觸測量”,采用“雙視點投影光栅三維測量”原理,由雙錄影機擷取圖像對,通過計算機進行圖像資料處理,不僅可以擷取服裝設計所需的特征尺寸,還可根據需要擷取人體圖像上任意一點的三維坐标。
四.筆者總結
本文從雙目立體視覺的三個最基本的比對算法出發,講述了其基本原理、步驟及其opencv代碼實作并對雙目立體視覺的發展趨勢及現狀做了總結,可供讀者參考借鑒,若有纰漏,請見諒!