淺析霍夫變換檢測直線和圓

原文:http://blog.csdn.net/shanchuan2012/article/details/74010561

1.本文目的

結合實例和一些演示來理解霍夫變換中的精髓:空間轉換

我想我把我對空間轉換的理解寫完了,這篇文章也就結束了,所以文章的內容不會那麼完整,不會完整的講霍夫變換(比如一般形狀的檢測,說實話我是初學也不會),也不會講在opencv中的實現(我只能抄代碼,自己也寫不出來)。

爲什麼要寫這篇文章?我是自學+初學圖像處理,沒有人可以問,只能上網搜索,網上的教程總有地方我看不明白,有時候我會懷疑是不是自己理解力不行?其實更多時候我覺得不是,問題其實在於兩點,一是教程講的不好,不能引領新手逐步深入,二是沒有找到合適的教程,不同階段看的東西是不一樣的。所以我把自己的理解寫出來,希望可以幫助跟我有同樣困惑的人,也希望自己不要誤導了別人,讓人產生更多的困惑。

2.簡介

這篇文章不是教程,不講基本概念,要了解霍夫變換可以先參考以下文章:

【OpenCV入門教程之十四】OpenCV霍夫變換:霍夫線變換,霍夫圓變換合輯-毛星雲

以一般化視角串聯霍夫變換(hough transform),從直線到圓再到廣義霍夫變換

Hough transform(霍夫變換)

看了以上資料我有很多地方是想不明白的,特別是看到檢測圓的時候,更別說一般形狀了。

剛看到霍夫變換的時候感覺挺神奇的,它來源於圖像處理中的幾何形狀檢測問題,用什麼辦法能夠檢測直線、圓或者別的形狀呢?最簡單的是直線,如果讓我來解決,要用什麼辦法才能把直線檢測出來呢?一時也想不到什麼辦法(最小二乘可以做擬合,但是一張圖上有多條直線怎麼辦?)。看到霍夫變換的時候想到了傅里葉變換,在這個空間不好解決的問題或者不明顯的特徵,給他來個變換,在另外一個空間上看問題,說不定就清楚了。

3.霍夫變換檢測直線

3.1笛卡爾座標下的例子

假如我們有如下幾個點:

在這裏插入圖片描述
它們完全在一條直線上,可以先告訴你們直線的方程是:y=x+4y=x+4。在我們不知情的情況下要如何檢測?

直線的一般表示是:y=kx+by=kx+bkkbb是兩個參數,現在反過來,將兩個參數當做自變量和變量,將方程寫成:b=xk+yb=-xk+y,這個時候座標軸就發生了變化,也就是空間發生了轉換,原來座標軸是xyxy,現在是kbkb

以點(1,5)(1,5) (該點所在空間此處稱爲圖像空間,因爲我們是在做圖像處理,這些點都對應於圖像的像素)爲例,代入直線方程y=kx+by=kx+b ,確定一條直線至少要兩個點,只有一個點是不能確定一條直線的,但可以知道的是,直線一定過這個點,過一個點的直線有無數條,像這樣(這個時候已經可以思考了,每個點上都有無數條直線經過,那麼怎麼找出同時經過這些點的直線?特徵是什麼?沒錯,用條件:“斜率和截距一樣”就可以把那條直線找出來,這也是做空間變換的原因):

在這裏插入圖片描述
由此得到:b=k+5b=-k+5 ,這是一條直線的方程:

在這裏插入圖片描述
所以在xyxy空間中的一個點,對應於kbkb空間(此處稱爲參數空間)上的一條線。這個比較好理解,所謂的kbkb空間,代表了直線的斜率和截距,既然過一個點的線有無數條,那麼斜率和截距也有無數個,kbkb空間上的線就描述了斜率和截距的無數種組合,所以形成了一條線。將圖像空間上所有對應的點在參數空間上的直線都畫出來就是這樣:

在這裏插入圖片描述
可以看到它們相交於一個點了,這個點是(k=1,b=4)(k=1,b=4) ,沒錯,它就是我們要找的特徵點(前面說過要怎麼從無數條直線中找到經過這些點的直線),這正是圖像空間上所有點連成的直線的斜率和截距。這樣就把圖像空間上的那條直線檢測出來了,方程爲y=1x+4y=1*x+4

但是實際上用極座標,爲什麼要用極座標而不用笛卡爾座標?上面給了回答,這裏不解釋。

3.2極座標下的例子

如何用一個角度和一段距離來表示過一個點的直線呢?方法是從原點作該直線的垂線,原點到該直線的距離用ρ\rho表示,垂線與x軸的夾角用θ\theta表示,這樣就可以用(θ,ρ)(\theta, \rho)來表示過一個點的任意直線了。

還是在笛卡爾座標下,(θ,ρ)(\theta, \rho)的關係用(x,y)(x,y)來表示,可以寫爲(如果不明白,可以先看看參考資料,這裏面的示意圖講的很清楚):
ρ=xcosθ+ysinθ \rho = x * cos \theta + y * sin \theta
還是一樣的思想,對於圖像空間上的一個點,過這個點的直線有無數條,所以(θ,ρ)(\theta, \rho)也有無數個組合,那麼在參數空間就對應於一條線(這時是一條曲線,管他是什麼線呢,反正它們都是描述那無數條直線的參數組合)。還是以點(1,5)(1,5) 爲例,它對應於參數空間的線:

在這裏插入圖片描述
將圖像空間上所有對應的點在參數空間上的直線都畫出來就是這樣:

在這裏插入圖片描述
它們也相交於一點,把交點找到(我這裏沒有計算交點,就當它是(θ0,ρ0)(\theta_0, \rho_0)吧,大概是(2.3562,2.7266)(2.3562, 2.7266) , 其實對應的是(135,4sin(135/180))(135 ^\circ,4*sin(135/180)) , 這是我反推的)後,代入方程:ρ=xcosθ+ysinθ\rho = x * cos \theta + y * sin \theta 就可以得到過所有點的直線了:
2.7266=xcos(2.3562)+ysin(2.3562) 2.7266 = x * cos(2.3562) + y * sin (2.3562)
整理後就是:y=x+4y=x+4 .到此直線檢測結束。

4.霍夫變換檢測圓

都說檢測圓和檢測直線大體上是類似的,確實,從空間轉換的角度來說是一樣的,橢圓、任意形狀的檢測都是一樣的,只要找到合適的轉換方法。

圓的一般方程是這樣的:
(xa)2+(yb)2=r2 (x-a)^2 + (y-b)^2 = r^2
(a,b)(a,b)爲圓心,rr爲半徑,還是先給一個例子:

在這裏插入圖片描述
圖上面有一系列的點,它們是在同一個圓上,這裏已經把圓的輪廓給出來了,這裏只畫了圓的一半,但已經足夠說明問題了。要檢測這個圖像中的圓應該怎麼辦?還是用同樣的思考方式,來個空間轉換。將上面圓的方程重新寫一下:
(ax)2+(by)2=r2 (a-x)^2 + (b-y)^2 = r^2
這時有三個參數,圓心(a,b)(a,b),半徑rr

過一點的圓有多少個?無數個。以上圖的第1個點(2,1)(-2,1)爲例,把它代入方程(ax)2+(by)2=r2(a-x)^2 + (b-y)^2 = r^2,得到:(a+2)2+(b1)2=r2(a+2)^2 + (b-1)^2 = r^2,這個方程描述的圓都經過點(2,1)(-2,1),它們在參數空間上看起來是這樣的:

在這裏插入圖片描述
這裏rr取的是[1,2,3,4,5][1,2,3,4,5],沒取成無限的,要是取成無限了,圖就沒有辦法看了。每一個參數空間上的圈圈上的都對應於圖像空間上經過點(2,1)(-2,1)的一個圓,如下面這個點:

在這裏插入圖片描述
它代表了什麼意思呢?它代表了一個經過點(2,1)(-2,1)的一個圓,圓心爲(2,0)(-2,0),半徑爲1(圖上的半徑),這樣是不是就清楚了一些?從三維空間來看就是這樣(爲了能看得清楚,只畫了半徑爲1,3,5的三個圓):

在這裏插入圖片描述
上面的圖是圖像空間中一個點對應在參數空間的曲線,將所有點對應在參數空間的曲線都畫出來就是這樣的(同樣爲了看的清楚,只取了3個半徑,分別爲1,3,5):

在這裏插入圖片描述
其實我們的目的就是在參數空間找聚集點,思想和檢測直線是一樣的,找參數空間中曲線交點最多的位置,可以看到在r=3r=3的時候所有圓都相交於同一個點,這個點對應的圓就是圖像空間中經過所有點的那個圓,這樣就把圓給檢測出來了。還可以看出,當r離真實值(r=3r=3)越遠時,圓的相交的就越不明顯。

我想這時候對霍夫變換檢測直線和圓的原理應該說清楚了,從直線到圓,參數從2個變成了3個,對應的參數空間從2維變成了3維。如果是別的形狀,那麼它的參數可能更多,對應的參數空間維數就越多,這時就很難畫圖了。不過通過上面的理解,相信我們已經不需要再借助圖像來理解更高維的變換了,這時我們應該可以進行歸納了,不管多高維,我們都是去找在參數空間中曲線相交最多的位置。

5.數據不是那麼完美的情況

上面給出來的直線和圓都完全在同一條直線和同一個圓上,但實際上可能沒有那精確像下面這樣:

在這裏插入圖片描述
那麼參數空間中對應的相交點也不是完全在一個點上

在這裏插入圖片描述

在這裏插入圖片描述
圓的情況也是類似的,就不再給出。所以一般的做法是將參數空間分成很多個小格子,落在同一個格子裏面的就算同一個交點。

6.小結

我對霍夫變換的理解依然很膚淺,覺得奇妙的地方就是空間轉換

這裏再來看以一般化視角串聯霍夫變換(hough transform),從直線到圓再到廣義霍夫變換對作者簡練的總結更能體會吧。

當然在實際的算法實現中,遠比上面的圖示來的複雜,不然後效率是個問題。

7.作圖代碼

這裏附上作圖用的matlab代碼:

% 霍夫變換理解 圓 2維
close('all');

% 給出一個圓,圓心(1,1),半徑3
r = 3;
x = -2 : 0.5 : 4; % x定義域
y = sqrt(r.^2 - (x-1).^2) + 1 + 0*(rand(1,length(x)) - 0.5); % 半個圓

% 圖像空間上的圓
figure;
plot(x,y,'*'); % 實際點
hold on;
x0 = -2 : 0.01 : 4;
y0 = sqrt(r.^2 - (x0-1).^2) + 1;
plot(x0,y0); % 圓曲線
hold off;
axis equal; % 各個軸比例相同
xlabel('x');
ylabel('y');

% 參數空間
figure;
% set(gcf,'position',[100,100,500,500]);
theta = 0 : 0.01 : 2*pi;
for ir = 1 : 5
    for i = 1 : 1%length(x)
        a = x(i) - ir*cos(theta); % 極座標
        b = y(i) - ir*sin(theta);
        plot(a,b); % 畫圓
        hold on;
        plot(x(i),y(i), '*'); % 畫圓心
    end
end

hold off;
grid on;
set(gca, 'xtick', -6:1:8);
axis equal;
xlabel('a');
ylabel('b');
print(gcf, '-djpeg', 'c2_2.jpg', '-r72');
% 霍夫變換(Hough Transform)
% 直線
clc;clear;close('all');

% 測試點
x = [0,1,2,3,4];
y = x + 4 + 1*(rand(1,length(x))-0.5);
plot(x,y,'*');
hold on;
y1 = x + 4;
plot(x,y1);

xlabel('x');
ylabel('y');
print(gcf, '-djpeg', 'line1.jpg', '-r72');

% 笛卡爾座標
figure;
legd = {};
k = 0:3;
for i = 1 : length(x)
    b = -x(i)*k + y(i);
    plot(k,b)
    hold on;
    legd = [legd, num2str(i)];
end
hold off;
% legend(legd);
grid on;
xlabel('k');
ylabel('b');
print(gcf, '-djpeg', 'line2.jpg', '-r72');

% 極座標
figure;
theta = 0/180*pi : 0.1 : 180/180*pi;
for i = 1 : length(x)
    ruo = x(i)*cos(theta) + y(i)*sin(theta);
    plot(theta, ruo);
    hold on;
end
hold off;
grid on;
xlabel('\theta');
ylabel('\rho');
print(gcf, '-djpeg', 'line3.jpg', '-r72');
% 霍夫變換理解 圓 3維
close('all');

% 給出一個圓,圓心(1,1),半徑3
r = 3;
x = -2 : 0.5 : 4; % x定義域
y = sqrt(r.^2 - (x-1).^2) + 1 + 0*(rand(1,length(x)) - 0.5); % 半個圓

% 圖像空間上的圓
figure;
plot(x,y,'*'); % 實際點
hold on;
x0 = -2 : 0.01 : 4;
y0 = sqrt(r.^2 - (x0-1).^2) + 1;
plot(x0,y0); % 圓曲線
hold off;
axis equal; % 各個軸比例相同
xlabel('x');
ylabel('y');
print(gcf, '-djpeg', 'c1.jpg', '-r72');

% 參數空間
% 3維圖畫的很慢,可能有地方弄錯了
figure;
% set(gcf,'position',[100,100,500,500]);
theta = 0 : 0.01 : 2*pi;
for ir = 1 : 2 : 5
    for i = 1 : 1%length(x)
        a = x(i) - ir*cos(theta); % 極座標
        b = y(i) - ir*sin(theta);
        r = ir*ones(1,length(a));
        plot3(r,a,b,'b'); % 畫圓
        hold on;
        plot3(r,x(i),y(i), 'r*'); % 畫圓心
    end
end

hold off;
grid on;
set(gca, 'xtick', -6:1:8);
% axis equal;
xlabel('r');
ylabel('a');
zlabel('b');
print(gcf, '-djpeg', 'c2.jpg', '-r72');
發佈了133 篇原創文章 · 獲贊 147 · 訪問量 63萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章