Stereo Matching文獻筆記之(六):淺談置信度傳播算法(Belief-Propagation)在立體匹配中的應用~

這是我一個糾結過的問題,曾經反反覆覆的看相關的知識,Belief-Propagation是一個伴隨着“馬爾科夫隨機場”提出的優化算法,我對優化算法情有獨鍾,一直覺得搞定了各種優化,機器學習剩下的也就是知識擴展而已

1. BP與stereo-matching

爲什麼要將Belief-Propagation和Stereo-Matching放在一起說?那是因爲現有的全局立體匹配算法,很大一部分都是基於Belief-Propagation來進行視差求精,二者天然的紐帶就是,一幅圖加上對應像素標籤就可以看成是一個馬爾科夫場,只要有馬爾科夫場,很自然地就可以利用BP算法進行優化求解。馬爾科夫場是一種概率圖模型,圖中的節點就是像素,圖中的隱節點就是標籤,在Stereo-Matching裏面,標籤就是像素的視差值,視差值總是會有一個範圍,BP就是在這個自定義空間中找到使得全局能量最小的那些視差值,一般全局能量函數的數學形式如下所示:




其中,D代表着像素本身的代價,又叫做一元勢函數,W代表不同標籤產生的代價,又叫做點對勢函數。這個公式就是想告訴我們:一副圖像的能量由每個節點的代價以及相鄰節點各種標籤下所產生的代價和所決定。如果我們想要這個能量函數最小化,一方面要儘可能壓低各個節點的代價,另一方面,要考慮到各個節點的相互作用。一般來說,相鄰節點標籤越一致,代價越小。但是,如果全部像素都是一個標籤,W值的和就爲0,但是這個時候自身代價一定會非常高。反之,如果D的和想達到最小,那麼只要每個像素取最小代價所對應的標籤即可,但是這個時候往往發現,相鄰像素往往不是一個標籤了,這也不是最理想的結果。標籤分佈結果肯定是既兼顧了自身代價,又兼顧了相鄰像素之間的標籤差異,二者有點此消彼長的意義在裏面。有趣。。。。。


如果我們把它放在Stereo-Matching裏面考慮,意義就更加具體化了,D就代表着代價聚合值,標籤就是視差值。一般的非全局算法,在D求出來之後便戛然而止,直接來一個WTA,就算是把視差求出來了,如果代價聚合準確還可以,如果不準,求出來的視差圖“慘不忍睹”,全局算法就是在代價聚合的基礎上加了一個雙保險,不單單希望代價聚合值的和最小化,還要考慮圖像的區域性,桌面肯定是一個區域,一個人肯定是一個區域等等等等,它們的視差應該差不多,所以直接利用這個數學模型來達到這種需求。


下面的問題就是如何來求解這個數學模型,答案就是:用Belief-Propagation算法,關於BP算法介紹很多,本文只對幾個重要的數學公式進行解釋。


2. 重要公式說明

1. 消息計算公式


這個公式代表的是像素p對其鄰居像素q傳遞的消息向量,注意,一定是一個向量。這個公式曾經讓我困惑,我在想,如果m是一個向量,那麼D和W都應該是向量纔對,那麼問題來了,向量的最小值是什麼意思?W中的兩個標籤向量相減,分量的對應關係又是啥啊?結果我一度懷疑這裏的m不是一個向量,也曾經在其他博客中給博主留言,不知道大家看到這個公式之後有沒有這樣的問題。後來,我下載了幾份BP的源代碼,通過看代碼弄清楚是怎麼一回事,m的確是一個向量,D和W也都可以視作向量,這個公式的正確解釋是:固定fq,fp取各個視差值(假設有n個),計算D、W以及m中的對應分量,共可以得到n個值,取最小的一個作爲向量m中的對應分量值,循環往復,得到的m正是一個n維向量。


這個公式有什麼意義呢?它想告訴我們像素p傳遞給q的消息是一個置信度向量,每個分量是p對q取各個視差值的支持力度。這個支持力度不單單由p本身的代價大小決定,還要有傳遞給p的消息決定,這樣子就會形成一個傳播的態勢。一般都在第一步迭代中,將所有的message都設置爲零向量。在HBP算法中,只在金字塔最頂層的圖像上同樣設定爲零向量,其他層直接利用上一層的最新消息向量做爲初始向量。


論文《Stereo Matching Using Belief Propagation》中認爲,立體匹配要解決的主要問題有兩個,一個是低紋理區域的視差估計,一個是深度不連續區域的視差估計,也就是遮擋區域,作者認爲深度不連續區域往往就是顏色不連續區域(這點多篇論文都這樣提到過,SegmenTree,DoubleBP等等)。而BP有兩大優點,第一個是信息不對稱,第二是BP的消息值是自適應的,第一點我也沒有徹底弄明白原理,關於第二點,BP往往在低紋理區域中能夠將消息傳遞到很遠,但在深度不連續區域卻馬上就會停止傳遞,所以說它是自適應的。


注:在DoubleBP論文中的上述公式有個錯誤,那裏面寫成了argmin,這是不對的,argmin代表的是求出fp的值,而這個公式想要得到的是函數值。


2. 節點p的置信度向量


這是BP算法的最後一個公式,其不參與循環,當每個消息向量穩定之後,或者迭代步驟到達之後(因爲有的BP算法根本不收斂),這時根據像素q的代價值以及周圍像素對其的消息向量,確定q的各個視差值的可能性大小,這就是置信度向量。這個時候直接利用WTA,就能求出像素q的視差值了。


3. 代碼

這部分的代碼來源於CSBP算法,專門記述HBP部分,我添加了部分註釋。

[cpp] view plain copy
  1. short*qx_csbp::disparity(unsigned char*left,unsigned char*right)  
  2. {  
  3.     short *disp=m_data_cost;  
  4.     memset(m_message,0,sizeof(short)*(m_max_nr_message>>1));  
  5.     memset(m_data_cost,0,sizeof(short)*m_h*m_w*m_max_nr_plane_pyramid[0]*2);  
  6.     m_data_cost_selected=&(m_data_cost[(m_h*m_w*m_max_nr_plane_pyramid[0])]);  
  7.   
  8.     // 分層計算,每一層中迭代計算BP  
  9.     for(int i=m_nr_scale-1;i>=0;i--)  
  10.     {  
  11.         // 計算數據項  
  12.         if(i==(m_nr_scale-1))   
  13.         {  
  14.             // 每一層中的節點數據項相等,不論迭代多少次,更改的是消息  
  15.             compute_data_cost_init(left,right,m_h_pyramid[i],m_w_pyramid[i],  
  16.                                    i,m_max_nr_plane_pyramid[i],m_nr_plane,  
  17.                                    m_cost_max_data_term);  
  18.         }  
  19.         else  
  20.         {  
  21.             // 不同層中的節點數據項不等,需要更新  
  22.             compute_data_cost(left,right,m_h_pyramid[i],m_w_pyramid[i],  
  23.                               i,m_max_nr_plane_pyramid[i+1],  
  24.                               m_cost_max_data_term);  
  25.   
  26.             // 初始化消息,每層基於上一層消息向量進行初始化,第一層爲0向量  
  27.             init_message(i);  
  28.         }  
  29.   
  30.         // 同一層中,不斷的更新消息向量  
  31.         for(int j=0;j<m_iteration[i];j++)   
  32.         {  
  33.             compute_message(m_h_pyramid[i],m_w_pyramid[i],m_max_nr_plane_pyramid[i],i);  
  34.         }  
  35.     }  
  36.   
  37.     // 基於原始層計算視差圖  
  38.     compute_disparity(disp,0);  
  39.   
  40.     return(disp);  
  41. }  

[cpp] view plain copy
  1. // 一次迭代中的消息向量更新  
  2. void qx_csbp::compute_message(int h,int w,int nr_plane,int scale)  
  3. {  
  4.     int i,y,x,yy=h-1,xx=w-1;  
  5.     short*c0,*p0,*p1,*p2,*p3,*p4;     
  6.     short*d0,*d1,*d2,*d3,*d4;  
  7.     int count=0;  
  8.     int yshift=w*m_nr_neighbor*nr_plane;  
  9.     int xshift=m_nr_neighbor*nr_plane;  
  10.     int yshiftd=w*nr_plane;  
  11.   
  12.     // 更新整幅圖像中,每個像素對四個鄰居像素的消息向量  
  13.     for(i=0;i<2;i++)        // 挑選像素方式沒有研究?  
  14.     {  
  15.         for(y=1;y<yy;y++)   // 圖像高  
  16.         {  
  17.             int yl=y*yshift;  
  18.             int yld=y*yshiftd;  
  19.             for(x=xx-1+(y+i)%2;x>=1;x-=2) //for(x=(y+i)%2+1;x<xx;x+=2)  
  20.             {  
  21.                 int xl=x*xshift;  
  22.                 int xld=x*nr_plane;  
  23.                 c0=&(m_data_cost_selected[yld+xld]);  
  24.                 p0=&(m_message[yl+xl]);  
  25.                 p1=&(m_message[yl+xl-yshift+2*nr_plane]);  
  26.                 p2=&(m_message[yl+xl-xshift+3*nr_plane]);  
  27.                 p3=&(m_message[yl+xl+yshift]);  
  28.                 p4=&(m_message[yl+xl+xshift+nr_plane]);  
  29.                 d0=&(m_selected_disparity_pyramid[yld+xld]);  
  30.                 d1=&(m_selected_disparity_pyramid[yld+xld-yshiftd]);  
  31.                 d2=&(m_selected_disparity_pyramid[yld+xld-nr_plane]);  
  32.                 d3=&(m_selected_disparity_pyramid[yld+xld+yshiftd]);  
  33.                 d4=&(m_selected_disparity_pyramid[yld+xld+nr_plane]);  
  34.   
  35.                 // 計算當前像素p0對四個鄰居像素的消息向量  
  36.                 compute_message_per_pixel(c0,p0,p1,p2,p3,p4,d0,d1,d2,d3,d4,y,x,nr_plane,scale,count);  
  37.             }  
  38.         }  
  39.     }  
  40. }  

[cpp] view plain copy
  1. // 計算當前像素對鄰居像素的消息向量  
  2. void qx_csbp::compute_message_per_pixel(short*c0,short *p0,short *p1,short *p2,short *p3,short *p4,  
  3.                                         short*d0,short*d1,short*d2, short*d3,short*d4,  
  4.                                         int y,int x,int nr_plane,int scale,int &count)  
  5. {  
  6.     short minimum[4]={30000,30000,30000,30000};  
  7.     short *p0u=p0;  
  8.     short *p0l=&(p0[nr_plane]);  
  9.     short *p0d=&(p0[nr_plane+nr_plane]);  
  10.     short *p0r=&(p0[nr_plane+nr_plane+nr_plane]);  
  11.     count++;  
  12.   
  13.     // 先計算jump cost  
  14.     for(int d=0;d<nr_plane;d++)  
  15.     {  
  16.         // 計算周圍三個像素對當前像素的jump cost  
  17.         p0u[d]=c0[d]+p2[d]+p3[d]+p4[d];  
  18.         p0l[d]=c0[d]+p1[d]+p3[d]+p4[d];  
  19.         p0d[d]=c0[d]+p1[d]+p2[d]+p4[d];  
  20.         p0r[d]=c0[d]+p1[d]+p2[d]+p3[d];  
  21.   
  22.         // 計算最小的jump cost值  
  23.         if(p0u[d]<minimum[0]) minimum[0]=p0u[d];  
  24.         if(p0l[d]<minimum[1]) minimum[1]=p0l[d];  
  25.         if(p0d[d]<minimum[2]) minimum[2]=p0d[d];  
  26.         if(p0r[d]<minimum[3]) minimum[3]=p0r[d];  
  27.     }  
  28.   
  29.     // 當前像素傳遞給每個鄰居像素消息向量  
  30.     compute_message_per_pixel_per_neighbor(p0u,minimum[0],d0,d1,nr_plane,scale);  
  31.     compute_message_per_pixel_per_neighbor(p0l,minimum[1],d0,d2,nr_plane,scale);   
  32.     compute_message_per_pixel_per_neighbor(p0d,minimum[2],d0,d3,nr_plane,scale);  
  33.     compute_message_per_pixel_per_neighbor(p0r,minimum[3],d0,d4,nr_plane,scale);  
  34. }  

[cpp] view plain copy
  1. // 計算當前像素對鄰居像素的消息向量  
  2. void qx_csbp::compute_message_per_pixel_per_neighbor(short *comp_func_sub,short minimum,  
  3.                                                      short *disp_left,short *disp_right,  
  4.                                                      int nr_plane,int scale)  
  5. {  
  6.     // 計算當前像素對特定鄰居像素的消息向量  
  7.     for(int d=0;d<nr_plane;d++)  
  8.     {  
  9.         short cost_min=minimum+m_cost_max_discontinuity;  
  10.   
  11.         // 計算fq取每個分量下,以fp爲自變量的消息最小值  
  12.         for(int i=0;i<nr_plane;i++)  
  13.         {  
  14.             // m_discontinuity_cost_single_jump*abs(disp_left[i]-disp_right[d])就是點對勢函數  
  15.             // abs(disp_left[i]-disp_right[d]) - V(fp - fq)  
  16.             // 沒有考慮到“discontimuity preserving”  
  17.             cost_min=min(cost_min,comp_func_sub[i]+m_discontinuity_cost_single_jump*abs(disp_left[i]-disp_right[d]));  
  18.         }  
  19.   
  20.         // message(fq) = min(fp)  
  21.         m_temp[d]=cost_min;  
  22.     }  
  23.     memcpy(comp_func_sub,m_temp,sizeof(short)*nr_plane);  
  24.   
  25.     // 對消息向量進行中心化處理  
  26.     bpstereo_normalize(comp_func_sub,nr_plane);  
  27. }  


4. 總結

總結:本文主要說說BP算法與stereo-matching之間的關係,主要對兩個公式進行解釋。



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