2019-01-16

SGBM算法獲取視差圖

立體校正后的左右兩幅圖像得到后素征,匹配點是在同一行上的昙啄,可以使用OpenCV中的BM算法或者SGBM算法計算視差圖。由于SGBM算法的表現(xiàn)要遠遠優(yōu)于BM算法狭吼,因此采用SGBM算法獲取視差圖赠幕。SGBM中的參數(shù)設置如下:

int numberOfDisparities = ((imgSize.width / 8) + 15) & -16;

??? cv::Ptr sgbm =cv::StereoSGBM::create(0, 16, 3);

??? sgbm->setPreFilterCap(32);

??? int SADWindowSize = 9;

??? int sgbmWinSize = SADWindowSize > 0 ? SADWindowSize : 3;

??? sgbm->setBlockSize(sgbmWinSize);

??? intcn = imgL.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(32);

??? sgbm->setDisp12MaxDiff(1);

??? intalg = STEREO_SGBM;

??? if(alg == STEREO_HH)

???????sgbm->setMode(cv::StereoSGBM::MODE_HH);

??? else if(alg == STEREO_SGBM)

???????sgbm->setMode(cv::StereoSGBM::MODE_SGBM);

??? else if(alg == STEREO_3WAY)

???????sgbm->setMode(cv::StereoSGBM::MODE_SGBM_3WAY);

??? sgbm->compute(imgL, imgR, disp);

默認計算出的是左視差圖,如果需要計算右視差圖,則將上面加粗的三條語句替換為下面前三條語句膨蛮。由于視差值計算出來為負值叠纹,disp類型為16SC1,因此需要取絕對值敞葛,然后保存:

sgbm->setMinDisparity(-numberOfDisparities);

???sgbm->setNumDisparities(numberOfDisparities);

??? sgbm->compute(imgR, imgL, disp);

??? disp = abs(disp);

SGBM算法得到的左誉察、右視差圖如下,左視差圖的數(shù)據(jù)類型為CV_16UC1制肮,右視差圖的數(shù)據(jù)類型為CV_16SC1?(SGBM中視差圖中不可靠的視差值設置為最小視差(mindisp-1)*16冒窍。因此在此例中,左視差圖中不可靠視差值設置為-16豺鼻,截斷值為0综液;右視差圖中不可靠視差值設置為(-numberOfDisparities-1)*16,取絕對值后為(numberOfDisparities+1)*16儒飒,所以兩幅圖會有較大差別):

左視差圖(不可靠視差值為0)

右視差圖(不可靠視差值為 (numberOfDisparities+1)*16?)


如果將右視差圖不可靠視差值也設置為0谬莹,則如下

至此,左視差圖和右視差圖遙相呼應桩了。

2. 視差圖空洞填充

視差圖中視差值不可靠的視差大多數(shù)是由于遮擋引起附帽,或者光照不均勻引起。既然牛逼如SGBM也覺得不可靠井誉,那與其留著做個空洞蕉扮,倒不如用附近可靠的視差值填充一下。

空洞填充也有很多方法颗圣,在這里我檢測出空洞區(qū)域喳钟,然后用附近可靠視差值的均值進行填充。填充后的視差圖

填充后左視差圖

填充后右視差圖


3. 視差圖轉換為深度圖

視差的單位是像素(pixel)在岂,深度的單位往往是毫米(mm)表示奔则。而根據(jù)平行雙目視覺的幾何關系(此處不再畫圖推導,很簡單)蔽午,可以得到下面的視差與深度的轉換公式:

?depth = ( f * baseline) / disp

?上式中易茬,depth表示深度圖;f表示歸一化的焦距及老,也就是內(nèi)參中的fx抽莱; baseline是兩個相機光心之間的距離,稱作基線距離骄恶;disp是視差值岸蜗。等式后面的均已知,深度值即可算出叠蝇。

在上面我們用SGBM算法獲取了視差圖,接下來轉換為深度圖,函數(shù)代碼如下:

/*

函數(shù)作用:視差圖轉深度圖輸入:dispMap ----視差圖悔捶,8位單通道铃慷,CV_8UC1

K?????? ----內(nèi)參矩陣,float類型輸出:depthMap ----深度圖蜕该,16位無符號單通道犁柜,CV_16UC1

*/

voiddisp2Depth(cv::Mat dispMap, cv::Mat&depthMap, cv::Mat K)

{

??? inttype = dispMap.type();

??? float fx = K.at<float>(0, 0);

??? float fy = K.at<float>(1, 1);

??? float cx = K.at<float>(0, 2);

??? float cy = K.at<float>(1, 2);

??? float baseline = 65; //基線距離65mm

??? if(type == CV_8U)

??? {

??????? const float PI = 3.14159265358;

??????? intheight = dispMap.rows;

??????? intwidth = dispMap.cols;

??????? uchar* dispData = (uchar*)dispMap.data;

??????? ushort* depthData = (ushort*)depthMap.data;

??????? for (int i = 0; i < height; i++)

??????? {

??????????? for (int j = 0; j < width; j++)

??????????? {

??????????????? intid = i*width + j;

??????????????? if(!dispData[id])? continue;? //防止0除

??????????????? depthData[id] =ushort( (float)fx *baseline / ((float)dispData[id]) );

??????????? }

??????? }

??? }

??? else

??? {

??????? cout <<"please confirm dispImg's type!"<< endl;

??????? cv::waitKey(0);

??? }

}

注:png的圖像格式可以保存16位無符號精度,即保存范圍為0-65535堂淡,如果是mm為單位馋缅,則最大能表示約65米的深度,足夠了绢淀。

上面代碼中我設置深度圖的精度為CV_16UC1萤悴,也就是ushort類型,將baseline設置為65mm皆的,轉換后保存為png格式即可覆履。如果保存為jpg或者bmp等圖像格式,會將數(shù)據(jù)截斷為0-255费薄。所以保存深度圖硝全,png格式是理想的選擇。(如果不是為了獲取精確的深度圖楞抡,可以將baseline設置為1伟众,這樣獲取的是相對深度圖,深度值也是相對的深度值)

轉換后的深度圖如下;

?左深度圖

? 右深度圖

空洞填充后的深度圖召廷,如下:

左深度圖(空洞填充后)

?右深度圖(空洞填充后)

視差圖到深度圖完成凳厢。

注:視差圖和深度圖中均有計算不正確的點,此文意在介紹整個流程柱恤,不特別注重算法的優(yōu)化数初,如有大神望不吝賜教。

附:視差圖和深度圖的空洞填充

?步驟如下:

① 以視差圖dispImg為例梗顺。計算圖像的積分圖integral泡孩,并保存對應積分圖中每個積分值處所有累加的像素點個數(shù)n(空洞處的像素點不計入n中,因為空洞處像素值為0寺谤,對積分值沒有任何作用仑鸥,反而會平滑圖像)。

② 采用多層次均值濾波变屁。首先以一個較大的初始窗口去做均值濾波(積分圖實現(xiàn)均值濾波就不多做介紹了眼俊,可以參考我之前的一篇博客),將大區(qū)域的空洞賦值粟关。然后下次濾波時疮胖,將窗口尺寸縮小為原來的一半,利用原來的積分圖再次濾波,給較小的空洞賦值(覆蓋原來的值)澎灸;依次類推院塞,直至窗口大小變?yōu)?x3,此時停止濾波性昭,得到最終結果拦止。

③ 多層次濾波考慮的是對于初始較大的空洞區(qū)域,需要參考更多的鄰域值糜颠,如果采用較小的濾波窗口汹族,不能夠完全填充,而如果全部采用較大的窗口其兴,則圖像會被嚴重平滑顶瞒。因此根據(jù)空洞的大小,不斷調(diào)整濾波窗口忌警。先用大窗口給所有空洞賦值搁拙,然后利用逐漸變成小窗口濾波覆蓋原來的值,這樣既能保證空洞能被填充上法绵,也能保證圖像不會被過度平滑箕速。

?空洞填充的函數(shù)代碼如下,僅供參考:

??voidinsertDepth32f(cv::Mat& depth)

??{

???? const intwidth = depth.cols;

???? const intheight = depth.rows;

???? float* data = (float*)depth.data;

????cv::Mat integralMap = cv::Mat::zeros(height, width, CV_64F);

????cv::Mat ptsMap = cv::Mat::zeros(height, width, CV_32S);

?? ??double* integral = (double*)integralMap.data;

?? ??int* ptsIntegral = (int*)ptsMap.data;

???? memset(integral,0, sizeof(double) * width * height);

???? memset(ptsIntegral,0, sizeof(int) * width * height);

???? for (int i = 0; i < height; ++i)

???? {

???????? intid1 = i * width;

???????? for (int j = 0; j < width; ++j)

???????? {

???????? ????intid2 = id1 + j;

???????????? if (data[id2] > 1e-3)

???????????? {

???????????????? integral[id2] = data[id2];

???????????????? ptsIntegral[id2] =1;

???????????? }

???????? }

???? }

???? // 積分區(qū)間

???? for (int i = 0; i < height; ++i)

???? {

???????? intid1 = i * width;

???????? for (int j = 1; j < width; ++j)

???????? {

???????????? intid2 = id1 + j;

???????????? integral[id2] += integral[id2 -1];

???????????? ptsIntegral[id2] +=ptsIntegral[id2 -1];

???????? }

???? }

???? for (int i = 1; i < height; ++i)

???? {

???????? intid1 = i * width;

???????? for (int j = 0; j < width; ++j)

???????? {

???????????? intid2 = id1 + j;

???????????? integral[id2] += integral[id2 -width];

???????????? ptsIntegral[id2] +=ptsIntegral[id2 - width];

???????? }

???? }

???? intwnd;

???? double dWnd = 2;

???? while (dWnd > 1)

???? {

???????? wnd =int(dWnd);

???????? dWnd /=2;

???????? for (int i = 0; i < height; ++i)

???????? {

????? ???????intid1 = i * width;

???????????? for (int j = 0; j < width; ++j)

???????????? {

???????????????? intid2 = id1 + j;

???????????????? int left = j - wnd - 1;

???????????????? intright = j + wnd;

???????????????? int top = i - wnd - 1;

???????????????? intbot = i + wnd;

???????????????? left = max(0, left);

???????????????? right = min(right,width -1);

???????????????? top = max(0, top);

???????????????? bot = min(bot, height -1);

???????????????? intdx = right - left;

???????????????? intdy = (bot - top) * width;

???????????????? intidLeftTop = top * width + left;

???????????????? intidRightTop = idLeftTop + dx;

???????????????? intidLeftBot = idLeftTop + dy;

???????????????? intidRightBot = idLeftBot + dx;

???????????????? intptsCnt = ptsIntegral[idRightBot] + ptsIntegral[idLeftTop] -(ptsIntegral[idLeftBot] + ptsIntegral[idRightTop]);

???????????????? doublesumGray = integral[idRightBot] +integral[idLeftTop] - (integral[idLeftBot] + integral[idRightTop]);

???????????????? if (ptsCnt <= 0)

???????????????? {

???????????????????? continue;

???????????????? }

???????????????? data[id2] =float(sumGray / ptsCnt);

? ????????????}

???????? }

???????? int s = wnd / 2 * 2 + 1;

???????? if (s > 201)

???????? {

???????????? s =201;

???????? }

???????? cv::GaussianBlur(depth, depth,cv::Size(s, s), s, s);

???? }

?}

?著作權歸作者所有,轉載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末朋譬,一起剝皮案震驚了整個濱河市盐茎,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌徙赢,老刑警劉巖字柠,帶你破解...
    沈念sama閱讀 217,657評論 6 505
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異狡赐,居然都是意外死亡窑业,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,889評論 3 394
  • 文/潘曉璐 我一進店門枕屉,熙熙樓的掌柜王于貴愁眉苦臉地迎上來常柄,“玉大人,你說我怎么就攤上這事搀擂∥髋耍” “怎么了?”我有些...
    開封第一講書人閱讀 164,057評論 0 354
  • 文/不壞的土叔 我叫張陵哨颂,是天一觀的道長喷市。 經(jīng)常有香客問我,道長威恼,這世上最難降的妖魔是什么品姓? 我笑而不...
    開封第一講書人閱讀 58,509評論 1 293
  • 正文 為了忘掉前任寝并,我火速辦了婚禮,結果婚禮上缭黔,老公的妹妹穿的比我還像新娘食茎。我一直安慰自己,他們只是感情好馏谨,可當我...
    茶點故事閱讀 67,562評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著附迷,像睡著了一般惧互。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上喇伯,一...
    開封第一講書人閱讀 51,443評論 1 302
  • 那天喊儡,我揣著相機與錄音,去河邊找鬼稻据。 笑死艾猜,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的捻悯。 我是一名探鬼主播匆赃,決...
    沈念sama閱讀 40,251評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼今缚!你這毒婦竟也來了算柳?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 39,129評論 0 276
  • 序言:老撾萬榮一對情侶失蹤姓言,失蹤者是張志新(化名)和其女友劉穎瞬项,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體何荚,經(jīng)...
    沈念sama閱讀 45,561評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡囱淋,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,779評論 3 335
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了餐塘。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片妥衣。...
    茶點故事閱讀 39,902評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖唠倦,靈堂內(nèi)的尸體忽然破棺而出称鳞,到底是詐尸還是另有隱情,我是刑警寧澤稠鼻,帶...
    沈念sama閱讀 35,621評論 5 345
  • 正文 年R本政府宣布冈止,位于F島的核電站,受9級特大地震影響候齿,放射性物質發(fā)生泄漏熙暴。R本人自食惡果不足惜闺属,卻給世界環(huán)境...
    茶點故事閱讀 41,220評論 3 328
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望周霉。 院中可真熱鬧掂器,春花似錦、人聲如沸俱箱。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,838評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽狞谱。三九已至乃摹,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間跟衅,已是汗流浹背孵睬。 一陣腳步聲響...
    開封第一講書人閱讀 32,971評論 1 269
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留伶跷,地道東北人掰读。 一個月前我還...
    沈念sama閱讀 48,025評論 2 370
  • 正文 我出身青樓,卻偏偏與公主長得像叭莫,于是被迫代替她去往敵國和親蹈集。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 44,843評論 2 354

推薦閱讀更多精彩內(nèi)容

  • 1.垃圾收集算法的核心思想 Java語言建立了垃圾收集機制食寡,用以跟蹤正在使用的對象和發(fā)現(xiàn)并回收不再使用(引用)的對...
    王培921223閱讀 1,426評論 0 1
  • 寫一個正則表達式判斷一個字符串是否是ip地址規(guī)則:一個ip地址由4個數(shù)字組成雾狈,每個數(shù)字之間用.連接。每個數(shù)字的大小...
    QiuXian閱讀 216評論 0 0
  • # 歡迎使用Markdown編輯器 你好抵皱! 這是你第一次使用 **Markdown編輯器** 所展示的歡迎頁善榛。如果...
    沈陽_fe65閱讀 278評論 0 0
  • 高階函數(shù) 高階函數(shù)可以將函數(shù)作為參數(shù)或者是返回值 forEach提供遍歷集合的功能,forEach其實是IntAr...
    Guow110閱讀 411評論 0 0
  • 基于Linux下C語言的Socket網(wǎng)絡編程 網(wǎng)絡上的兩個程序通過一個雙向的通信連接實現(xiàn)數(shù)據(jù)的交換呻畸,這個連接的一端...
    步懶尋床閱讀 1,513評論 0 3