天天看點

Hough變換檢測圓(附:MATLAB程式)

    Hough變換的基本原理在于,利用點與線的對偶性,将圖像空間的線條變為參數空間的聚集點,進而檢測給定圖像是否存在給定性質的曲線。圓的方程為:(x-a)^2+(y-2)^2=r^2,通過Hough變換,将圖像空間對應到參數空間。附錄中的MATLAB程式為網上比較常見的,實際運作中存在一些問題,這裡進行些修改。

    原理:

    霍夫變換是圖像進行中從圖像中識别幾何形狀的基本方法之一,應用很廣泛,也有很多改進算法。最基本的霍夫變換是從黑白圖像中檢測直線(線段)。

    我們先看這樣一個問題:設已知一黑白圖像上畫了一條直線,要求出這條直線所在的位置。我們知道,直線的方程可以用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(22)。可以求出,過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)。這個性質就為我們解決問題提供了方法:

    首先,我們初始化一塊緩沖區,對應于參數平面,将其所有資料置為0.

    對于圖像上每一前景點,求出參數平面對應的直線,把這直線上的所有點的值都加1。

    最後,找到參數平面上最大點的位置,這個位置就是原圖像上直線的參數。     上面就是霍夫變換的基本思想。就是把圖像平面上的點對應到參數平面上的線,最後通過統計特性來解決問題。假如圖像平面上有兩條直線,那麼最終在參數平面上就會看到兩個峰值點,依此類推。

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

    在看下面一個問題:我們要從一副圖像中檢測出半徑以知的圓形來。這個問題比前一個還要直覺。我們可以取和圖像平面一樣的參數平面,以圖像上每一個前景點為圓心,以已知的半徑在參數平面上畫圓,并把結果進行累加。最後找出參數平面上的峰值點,這個位置就對應了圖像上的圓心。在這個問題裡,圖像平面上的每一點對應到參數平面上的一個圓。

    把上面的問題改一下,假如我們不知道半徑的值,而要找出圖像上的圓來。這樣,一個辦法是把參數平面擴大稱為三維空間。就是說,參數空間變為x--y--R三維,對應圓的圓心和半徑。圖像平面上的每一點就對應于參數空間中每個半徑下的一個圓,這實際上是一個圓錐。最後當然還是找參數空間中的峰值點。不過,這個方法顯然需要大量的記憶體,運作速度也會是很大問題。

    有什麼更好的方法麼?我們前面假定的圖像都是黑白圖像(2值圖像),實際上這些2值圖像多是彩色或灰階圖像通過邊緣提取來的。我們前面提到過,圖像邊緣除了位置資訊,還有方向資訊也很重要,這裡就用上了。根據圓的性質,圓的半徑一定在垂直于圓的切線的直線上,也就是說,在圓上任意一點的法線上。這樣,解決上面的問題,我們仍采用2維的參數空間,對于圖像上的每一前景點,加上它的方向資訊,都可以确定出一條直線,圓的圓心就在這條直線上。這樣一來,問題就會簡單了許多。

    接下來還有許多類似的問題,如檢測出橢圓,正方形,長方形,圓弧等等。這些方法大都類似,關鍵就是需要熟悉這些幾何形狀的數學性質。霍夫變換的應用是很廣泛的,比如我們要做一個支票識别的任務,假設支票上肯定有一個紅顔色的方形印章,我們可以通過霍夫變換來對這個印章進行快速定位,在配合其它手段進行其它處理。霍夫變換由于不受圖像旋轉的影響,是以很容易的可以用來進行定位。     霍夫變換有許多改進方法,一個比較重要的概念是廣義霍夫變換,它是針對所有曲線的,用處也很大。就是針對直線的霍夫變換也有很多改進算法,比如前面的方法我們沒有考慮圖像上的這一直線上的點是否連續的問題,這些都要随着應用的不同而有優化的方法。

    實作:

   上文中提到了檢測圓的切線的方法,這裡暫且不讨論,這裡讨論經典HOUGH算法。下面為我寫的利用極坐标表示圓的一種算法流程。

   1.圖像灰階化,二值化(注意:二值化的好壞對檢測結果有很大影響,常用的有SOBEL算子)

   2.檢測圖像中的邊緣點,并儲存其坐标位置。設定角度theta的變化範圍和步長,半徑r的變換範圍和步長。

   3.利用公式x=a+rcos(theta),y=b+rsin(theta)求出a和b的值。(注意:x和y為實際的圖像空間某個邊緣點的坐标,a和b為其對應的參數空間的坐标),如果a和b的值在合理的範圍之類,則對該位置進行累加。

   例如:

for i=1:ecount
    for r=1:size_r
        for k=1:size_angle
            a = round(rows(i)-(r_min+(r-1)*step_r)*cos(k*step_angle));
            b = round(cols(i)-(r_min+(r-1)*step_r)*sin(k*step_angle));
            if(a>0&a<=m&b>0&b<=n)
                hough_space(a,b,r) = hough_space(a,b,r)+1;
            end
        end
    end
end
           

  4.檢索完畢,尋找最大值,求出圓心坐标與半徑,儲存。

  附錄:程式中紅色的部分是我修改的。修改後編譯通過。

function [hough_space,hough_circle,para] = hough_circle(BW,step_r,step_angle,r_min,r_max,p);
%[HOUGH_SPACE,HOUGH_CIRCLE,PARA] = HOUGH_CIRCLE(BW,STEP_R,STEP_ANGLE,R_MAX,P)
%------------------------------算法概述-----------------------------
% 該算法通過a = x-r*cos(angle),b = y-r*sin(angle)将圓圖像中的邊緣點
% 映射到參數空間(a,b,r)中,由于是數字圖像且采取極坐标,angle和r都取
% 一定的範圍和步長,這樣通過兩重循環(angle循環和r循環)即可将原圖像
% 空間的點映射到參數空間中,再在參數空間(即一個由許多小立方體組成的
% 大立方體)中尋找圓心,然後求出半徑坐标。
%-------------------------------------------------------------------

%------------------------------輸入參數-----------------------------
% BW:二值圖像;
% step_r:檢測的圓半徑步長
% step_angle:角度步長,機關為弧度
% r_min:最小圓半徑
% r_max:最大圓半徑
% p:以p*hough_space的最大值為門檻值,p取0,1之間的數
%-------------------------------------------------------------------

%------------------------------輸出參數-----------------------------
% hough_space:參數空間,h(a,b,r)表示圓心在(a,b)半徑為r的圓上的點數
% hough_circl:二值圖像,檢測到的圓
% para:檢測到的圓的圓心、半徑
%-------------------------------------------------------------------

% From Internet,Modified by mhjerry,2011-12-11

[m,n] = size(BW);
size_r = round((r_max-r_min)/step_r)+1;
size_angle = round(2*pi/step_angle);
 
hough_space = zeros(m,n,size_r);
 
[rows,cols] = find(BW);
ecount = size(rows);
 
% Hough變換
% 将圖像空間(x,y)對應到參數空間(a,b,r)
% a = x-r*cos(angle)
% b = y-r*sin(angle)
for i=1:ecount
    for r=1:size_r
        for k=1:size_angle
            a = round(rows(i)-(r_min+(r-1)*step_r)*cos(k*step_angle));
            b = round(cols(i)-(r_min+(r-1)*step_r)*sin(k*step_angle));
            if(a>0&a<=m&b>0&b<=n)
                hough_space(a,b,r) = hough_space(a,b,r)+1;
            end
        end
    end
end
 
% 搜尋超過門檻值的聚集點
max_para = max(max(max(hough_space)));
index = find(hough_space>=max_para*p);
length = size(index);
hough_circle=zeros(m,n);
for i=1:ecount
    for k=1:length
        par3 = floor(index(k)/(m*n))+1;
        par2 = floor((index(k)-(par3-1)*(m*n))/m)+1;
        par1 = index(k)-(par3-1)*(m*n)-(par2-1)*m;
        if((rows(i)-par1)^2+(cols(i)-par2)^2<(r_min+(par3-1)*step_r)^2+5&...
                (rows(i)-par1)^2+(cols(i)-par2)^2>(r_min+(par3-1)*step_r)^2-5)
            hough_circle(rows(i),cols(i)) = 1;
        end
    end
end
 
% 列印結果
for k=1:length
    par3 = floor(index(k)/(m*n))+1;
    par2 = floor((index(k)-(par3-1)*(m*n))/m)+1;
    par1 = index(k)-(par3-1)*(m*n)-(par2-1)*m;
    par3 = r_min+(par3-1)*step_r;
    fprintf(1,'Center %d %d radius %d\n',par1,par2,par3);
    para(:,k) = [par1,par2,par3]';
end
           

代碼我已經上傳到我的資源裡,需要下載下傳的,可以進我的空間下載下傳。

注意半徑範圍的選取,直接影響到你想要檢測的圓。而且,如果圖像太大,且步長取得太小,可能會存在記憶體不夠的情況。

程式:

運作結果:

原圖:

Hough變換檢測圓(附:MATLAB程式)

邊緣檢測後:

Hough變換檢測圓(附:MATLAB程式)

檢測結果:Center 62 59 radius 52

Hough變換檢測圓(附:MATLAB程式)

繼續閱讀