霍夫變換

原理簡介 霍夫變換(Hough Transform) 霍夫變換是圖像處理中從圖像中識別幾何形狀的基本方法之一,應用很普遍,也有不少改進算法。最基本的霍夫變換是從黑白圖像中檢測直線(線段)。算法

 

1詳細內容

咱們先看這樣一個問題:設已知一黑白圖像上畫了一條直線,要求出這條直線所在的位置。咱們知道,直線的方程能夠用y=k*x+b 來表示,其中k和b是參數,分別是斜率和截距。過某一點(x0,y0)的全部直線的參數都會知足方程y0=kx0+b。即點(x0,y0)肯定了一組直線。方程y0=kx0+b在參數k--b平面上是一條直線,(你也能夠是方程b=-x0*k+y0對應的直線)。這樣,圖像x--y平面上的一個前景像素點就對應到參數平面上的一條直線。咱們舉個例子說明解決前面那個問題的原理。設圖像上的直線是y=x, 咱們先取上面的三個點:A(0,0), B(1,1), C(2,2)。能夠求出,過A點的直線的參數要知足方程b=0, 過B點的直線的參數要知足方程1=k+b, 過C點的直線的參數要知足方程2=2k+b, 這三個方程就對應着參數平面上的三條直線,而這三條直線會相交於一點(k=1,b=0)。 同理,原圖像上直線y=x上的其它點(如(3,3),(4,4)等) 對應參數平面上的直線也會經過點(k=1,b=0)。優化

 

2應用

這個性質就爲咱們解決問題提供了方法:spa

首先,咱們初始化一塊緩衝區,對應於參數平面,將其全部數據置爲0.orm

對於圖像上每一前景點,求出參數平面對應的直線,把這直線上的全部點的值都加1。最後,找到參數平面上最大點的位置,這個位置就是原圖像上直線的參數。上面就是霍夫變換的基本思想。就是把圖像平面上的點對應到參數平面上的線,最後經過統計特性來解決問題。假如圖像平面上有兩條直線,那麼最終在參數平面上就會看到兩個峯值點,依此類推。htm

在實際應用中,y=k*x+b形式的直線方程沒有辦法表示x=c形式的直線(這時候,直線的斜率爲無窮大)。因此實際應用中,是採用參數方程p=x*cos(theta)+y*sin(theta)。這樣,圖像平面上的一個點就對應到參數p---theta平面上的一條曲線上。其它的仍是同樣。內存

 

3應用實例1

在看下面一個問題:咱們要從一幅圖像中檢測出半徑已知的圓形來。這個問題比前一個還要直觀。咱們能夠取和圖像平面同樣的參數平面,以圖像上每個前景點爲圓心,以已知的半徑在參數平面上畫圓,並把結果進行累加。最後找出參數平面上的峯值點,這個位置就對應了圖像上的圓心。在這個問題裏,圖像平面上的每一點對應到參數平面上的一個圓。get

把上面的問題改一下,假如咱們不知道半徑的值,而要找出圖像上的圓來。這樣,一個辦法是把參數平面擴大稱爲三維空間。就是說,參數空間變爲x--y--R三維,對應圓的圓心和半徑。數學

圖像平面上的每一點就對應於參數空間中每一個半徑下的一個圓,這其實是一個圓錐。最後固然仍是找參數空間中的峯值點。不過,這個方法顯然須要大量的內存,運行速度也會是很大問題。有什麼更好的方法麼?咱們前面假定的圖像都是黑白圖像(2值圖像),實際上這些2值圖像可能是彩色或灰度圖像經過邊緣提取來的。咱們前面提到過,圖像邊緣除了位置信息,還有方向信息也很重要,這裏就用上了。根據圓的性質,圓的半徑必定在垂直於圓的切線的直線上,也就是說,在圓上任意一點的法線上。這樣,解決上面的問題,咱們仍採用2維的參數空間,對於圖像上的每一前景點,加上它的方向信息,均可以肯定出一條直線,圓的圓心就在這條直線上。這樣一來,問題就會簡單了許多。it

 

4應用實例2

接下來還有許多相似的問題,如檢測出橢圓,正方形,長方形,圓弧等等。這些方法大都相似,關鍵就是須要熟悉這些幾何形狀的數學性質。霍夫變換的應用是很普遍的,好比咱們要作一個支票識別的任務,假設支票上確定有一個紅顏色的方形印章,咱們能夠經過霍夫變換來對這個印章進行快速定位,在配合其它手段進行其它處理。霍夫變換因爲不受圖像旋轉的影響,因此很容易的能夠用來進行定位。io

霍夫變換有許多改進方法,一個比較重要的概念是廣義霍夫變換,它是針對全部曲線的,用處也很大。就是針對直線的霍夫變換也有不少改進算法,好比前面的方法咱們沒有考慮圖像上的這一直線上的點是否連續的問題,這些都要隨着應用的不一樣而有優化的方法。

順便說一句,搞圖像處理這一行,在理論方面,有幾本雜誌是要看的,天然是英文雜誌,中文期刊好像沒有專門的圖像處理期刊,固然也有很多涉及這方面的期刊,但實事求是來講,的確比英文雜誌水平差不少。

‘IEEE Transactions on Pattern Recognition And Machine Intelligence’

‘IEEE Transactions on Image Processing’

是最重要的兩本,其它的如ICCV、CVPR、ECCV、NIPS、BMVC等的會議文章也很是好

 

1)理論之通俗理解:

1.在圖像中檢測直線的問題,其實質是找到構成直線的全部的像素點。那麼問題就是從找到直線,變成找到符合y=mx+c的全部(x,y)的點的問題。

2.進行座標系變化y=mx+c,變成c=-xm+b。直線上的點(x1,y1),在轉換座標系後爲一條直線。這個原理應該是高中的。

 

3.直線上每個點在MC座標系中都表現爲直線,並且,這些直線都相交於一個點,(m,c)。找到全部點的問題,轉變爲尋找直線的問題。

4.對於圖像中的每個點,在MC座標系中對應着不少的直線。找到直線的交點,就對應着找到圖像中的直線。

實際在使用這一原理的時候,不是採用直線的斜率和截距公式,而是用

如何實現:

1. θ角在-90度到90度的範圍裏,劃分爲不少區間,對全部的像素點(x,y)在全部θ角的時候,求出ρ.從而累加ρ值出現的次數。高於某個閾值的ρ就是一個直線。

2. 這個過程就相似於以下一個二維的表格,橫座標就是θ角,ρ就是到直線的最短距離。

橫座標θ不斷變換,對於全部的不爲0的像素點,計算出ρ,找到ρ在座標(θ,ρ)的位置累加1.

  1. 上圖中局部最大的就是找到的直線的θ和ρ的值。

(2) 具體代碼片斷

複製代碼    for( ang = 0, n = 0; n < numangle; ang += theta, n++ )
   {
       tabSin[n] = (float)(sin(ang) * irho);
       tabCos[n] = (float)(cos(ang) * irho);
   }

   // stage 1. fill accumulator
   for( i = 0; i < height; i++ )
       for( j = 0; j < width; j++ )
       {
           if( image[i * step + j] != 0 )
               for( n = 0; n < numangle; n++ )
               {
                   r = cvRound( j * tabCos[n] + i * tabSin[n] );
                   r += (numrho - 1) / 2;
                   accum[(n+1) * (numrho+2) + r+1]++;
               }
       }

   // stage 2. find local maximums
   for( r = 0; r < numrho; r++ )
       for( n = 0; n < numangle; n++ )
       {
           int base = (n+1) * (numrho+2) + r+1;
           if( accum[base] > threshold &&
               accum[base] > accum[base - 1] && accum[base] >= accum[base + 1] &&
               accum[base] > accum[base - numrho - 2] && accum[base] >= accum[base + numrho + 2] )
               sort_buf[total++] = base;
       }

   // stage 3. sort the detected lines by accumulator value
   icvHoughSortDescent32s( sort_buf, total, accum );

   // stage 4. store the first min(total,linesMax) lines to the output buffer
   linesMax = MIN(linesMax, total);
   scale = 1./(numrho+2);
   for( i = 0; i < linesMax; i++ )
   {
       CvLinePolar line;
       int idx = sort_buf[i];
       int n = cvFloor(idx*scale) - 1;
       int r = idx - (n+1)*(numrho+2) - 1;
       line.rho = (r - (numrho - 1)*0.5f) * rho;        line.angle = n * theta;        cvSeqPush( lines, &line );    }

相關文章
相關標籤/搜索