【OpenCV】SIFT原理與源碼分析:DoG尺度空間構造

http://blog.csdn.net/xiaowei_cqu/article/details/8067881

《SIFT原理與源碼分析》系列文章索引:http://blog.csdn.net/xiaowei_cqu/article/details/8069548

尺度空間理論


自然界中的物體隨着觀測尺度不同有不同的表現形態。例如我們形容建築物用“米”,觀測分子、原子等用“納米”。更形象的例子比如Google地圖,滑動鼠標輪可以改變觀測地圖的尺度,看到的地圖繪製也不同;還有電影中的拉伸鏡頭等等……
尺度空間中各尺度圖像的模糊程度逐漸變大,能夠模擬人在距離目標由近到遠時目標在視網膜上的形成過程。
尺度越大圖像越模糊。

爲什麼要討論尺度空間?

用機器視覺系統分析未知場景時,計算機並不預先知道圖像中物體的尺度。我們需要同時考慮圖像在多尺度下的描述,獲知感興趣物體的最佳尺度。另外如果不同的尺度下都有同樣的關鍵點,那麼在不同的尺度的輸入圖像下就都可以檢測出來關鍵點匹配,也就是尺度不變性

圖像的尺度空間表達就是圖像在所有尺度下的描述。


尺度空間表達與金字塔多分辨率表達


高斯模糊

高斯核是唯一可以產生多尺度空間的核(《Scale-space theory: A basic tool for analysing structures at different scales》)。一個圖像的尺度空間L(x,y,σ) ,定義爲原始圖像I(x,y)與一個可變尺度的2維高斯函數G(x,y,σ)卷積運算。 

二維空間高斯函數:


尺度空間:


尺度是自然客觀存在的,不是主觀創造的。高斯卷積只是表現尺度空間的一種形式。

二維空間高斯函數是等高線從中心成正太分佈的同心圓:


分佈不爲零的點組成卷積陣與原始圖像做變換,即每個像素值是周圍相鄰像素值的高斯平均。一個5*5的高斯模版如下所示:


高斯模版是圓對稱的,且卷積的結果使原始像素值有最大的權重,距離中心越遠的相鄰像素值權重也越小。
在實際應用中,在計算高斯函數的離散近似時,在大概距離之外的像素都可以看作不起作用,這些像素的計算也就可以忽略。所以,通常程序只計算(6σ+1)*(6σ+1)就可以保證相關像素影響。

高斯模糊另一個很厲害的性質就是線性可分:使用二維矩陣變換的高斯模糊可以通過在水平和豎直方向各進行一維高斯矩陣變換相加得到。


O(N^2*m*n)次乘法就縮減成了O(N*m*n)+O(N*m*n)次乘法。(N爲高斯核大小,m,n爲二維圖像高和寬)

其實高斯這一部分只需要簡單瞭解就可以了,在OpenCV也只需要一句代碼:

  1. GaussianBlur(dbl, dbl, Size(), sig_diff, sig_diff);  
  1. GaussianBlur(dbl, dbl, Size(), sig_diff, sig_diff);  

我這裏詳寫了一下是因爲這塊兒對分析算法效率比較有用,而且高斯模糊的算法真的很漂亮~

金字塔多分辨率

金字塔是早期圖像多尺度的表示形式。圖像金字塔化一般包括兩個步驟:使用低通濾波器平滑圖像;對平滑圖像進行降採樣(通常是水平,豎直方向1/2),從而得到一系列尺寸縮小的圖像。


上圖中(a)是對原始信號進行低通濾波,(b)是降採樣得到的信號。

而對於二維圖像,一個傳統的金字塔中,每一層圖像由上一層分辨率的長、寬各一半,也就是四分之一的像素組成:


多尺度和多分辨率

尺度空間表達和金字塔多分辨率表達之間最大的不同是:

  • 尺度空間表達是由不同高斯核平滑卷積得到,在所有尺度上有相同的分辨率;
  • 而金字塔多分辨率表達每層分辨率減少固定比率。
所以,金字塔多分辨率生成較快,且佔用存儲空間少;而多尺度表達隨着尺度參數的增加冗餘信息也變多。
多尺度表達的優點在於圖像的局部特徵可以用簡單的形式在不同尺度上描述;而金字塔表達沒有理論基礎,難以分析圖像局部特徵。

DoG(Difference of Gaussian)


高斯拉普拉斯LoG金字塔

結合尺度空間表達和金字塔多分辨率表達,就是在使用尺度空間時使用金字塔表示,也就是計算機視覺中最有名的拉普拉斯金子塔(《The Laplacian pyramid as a compact image code》)。
高斯拉普拉斯LoG(Laplace of Guassian)算子就是對高斯函數進行拉普拉斯變換:

核心思想還是高斯,這個不多敘述。

高斯差分DoG金字塔

DoG(Difference of Gaussian)其實是對高斯拉普拉斯LoG的近似,也就是對的近似。SIFT算法建議,在某一尺度上的特徵檢測可以通過對兩個相鄰高斯尺度空間的圖像相減,得到DoG的響應值圖像D(x,y,σ)。然後仿照LoG方法,通過對響應值圖像D(x,y,σ)進行局部最大值搜索,在空間位置和尺度空間定位局部特徵點。其中:

k爲相鄰兩個尺度空間倍數的常數。

上圖中(a)是DoG的三維圖,(b)是DoG與LoG的對比。

金字塔構建


構建高斯金字塔

爲了得到DoG圖像,先要構造高斯金字塔。我們回過頭來繼續說高斯金字塔~
高斯金字塔在多分辨率金字塔簡單降採樣基礎上加了高斯濾波,也就是對金字塔每層圖像用不同參數的σ做高斯模糊,使得每層金字塔有多張高斯模糊圖像。金字塔每層多張圖像合稱爲一組(Octave),每組有多張(也叫層Interval)圖像。另外,降採樣時,金字塔上邊一組圖像的第一張圖像(最底層的一張)是由前一組(金字塔下面一組)圖像的倒數第三張隔點採樣得到。


以下是OpenCV中構建高斯金字塔的代碼,我加了相應的註釋:
  1. // 構建nOctaves組(每組nOctaves+3層)高斯金字塔  
  2. void SIFT::buildGaussianPyramid( const Mat& base, vector<Mat>& pyr, int nOctaves ) const  
  3. {  
  4.     vector<double> sig(nOctaveLayers + 3);  
  5.     pyr.resize(nOctaves*(nOctaveLayers + 3));  
  6.   
  7.     // precompute Gaussian sigmas using the following formula:  
  8.     //  \sigma_{total}^2 = \sigma_{i}^2 + \sigma_{i-1}^2、  
  9.     // 計算對圖像做不同尺度高斯模糊的尺度因子  
  10.     sig[0] = sigma;  
  11.     double k = pow( 2., 1. / nOctaveLayers );  
  12.     for( int i = 1; i < nOctaveLayers + 3; i++ )  
  13.     {  
  14.         double sig_prev = pow(k, (double)(i-1))*sigma;  
  15.         double sig_total = sig_prev*k;  
  16.         sig[i] = std::sqrt(sig_total*sig_total - sig_prev*sig_prev);  
  17.     }  
  18.   
  19.     for( int o = 0; o < nOctaves; o++ )  
  20.     {  
  21.         // DoG金子塔需要nOctaveLayers+2層圖像來檢測nOctaves層尺度  
  22.         // 所以高斯金字塔需要nOctaveLayers+3層圖像得到nOctaveLayers+2層DoG金字塔  
  23.         for( int i = 0; i < nOctaveLayers + 3; i++ )  
  24.         {  
  25.             // dst爲第o組(Octave)金字塔  
  26.             Mat& dst = pyr[o*(nOctaveLayers + 3) + i];  
  27.             // 第0組第0層爲原始圖像  
  28.             if( o == 0  &&  i == 0 )  
  29.                 dst = base;  
  30.               
  31.             // base of new octave is halved image from end of previous octave  
  32.             // 每一組第0副圖像時上一組倒數第三幅圖像隔點採樣得到  
  33.             else if( i == 0 )  
  34.             {  
  35.                 const Mat& src = pyr[(o-1)*(nOctaveLayers + 3) + nOctaveLayers];  
  36.                 resize(src, dst, Size(src.cols/2, src.rows/2),  
  37.                        0, 0, INTER_NEAREST);  
  38.             }  
  39.             // 每一組第i副圖像是由第i-1副圖像進行sig[i]的高斯模糊得到  
  40.             // 也就是本組圖像在sig[i]的尺度空間下的圖像  
  41.             else  
  42.             {  
  43.                 const Mat& src = pyr[o*(nOctaveLayers + 3) + i-1];  
  44.                 GaussianBlur(src, dst, Size(), sig[i], sig[i]);  
  45.             }  
  46.         }  
  47.     }  
  48. }  


 

高斯金字塔的組數爲:


 

代碼10-17行是計算高斯模糊的係數σ,具體關係如下:

其中,σ爲尺度空間座標,s爲每組中層座標,σ0爲初始尺度,S爲每組層數(一般爲3~5)。根據這個公式,我們可以得到金字塔組內各層尺度以及組間各圖像尺度關係。
組內相鄰圖像尺度關係:

相鄰組間尺度關係:

所以,相鄰兩組的同一層尺度爲2倍的關係
最終尺度序列總結爲:

o爲金字塔組數,n爲每組金字塔層數。

構建DoG金字塔

構建高斯金字塔之後,就是用金字塔相鄰圖像相減構造DoG金字塔。



下面爲構造DoG的代碼:
  1. // 構建nOctaves組(每組nOctaves+2層)高斯差分金字塔  
  2. void SIFT::buildDoGPyramid( const vector<Mat>& gpyr, vector<Mat>& dogpyr ) const  
  3. {  
  4.     int nOctaves = (int)gpyr.size()/(nOctaveLayers + 3);  
  5.     dogpyr.resize( nOctaves*(nOctaveLayers + 2) );  
  6.   
  7.     for( int o = 0; o < nOctaves; o++ )  
  8.     {  
  9.         for( int i = 0; i < nOctaveLayers + 2; i++ )  
  10.         {  
  11.             // 第o組第i副圖像爲高斯金字塔中第o組第i+1和i組圖像相減得到  
  12.             const Mat& src1 = gpyr[o*(nOctaveLayers + 3) + i];  
  13.             const Mat& src2 = gpyr[o*(nOctaveLayers + 3) + i + 1];  
  14.             Mat& dst = dogpyr[o*(nOctaveLayers + 2) + i];  
  15.             subtract(src2, src1, dst, noArray(), CV_16S);  
  16.         }  
  17.     }  
  18. }  


這個比較簡單,就是一個subtract()函數。


至此,SIFT第一步就完成了。參見《SIFT原理與源碼分析

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