第16章:霍夫变换
第16章:霍夫變換
- 一、霍夫直線變換:
- 1. 霍夫直線變換原理:
- 2. HoughLines函數(shù):
- 3. HoughLinesP函數(shù):
- 2. 霍夫圓環(huán)變換:
- 霍夫變換是一種在圖像中尋找直線、圓形以及其他簡(jiǎn)單形狀的方法。
- 霍夫變換采用類似于投票的方式來獲取當(dāng)前圖像內(nèi)的形狀集合,該變換由Paul Hough(霍夫)于1962年首次提出。
最初的霍夫變換只能用于檢測(cè)直線,經(jīng)過發(fā)展后,霍夫變換不僅能夠識(shí)別直線,還能識(shí)別其他簡(jiǎn)單的圖形結(jié)構(gòu),常見的有圓、橢圓等。實(shí)際上,只要是能夠用一個(gè)參數(shù)方程表示的對(duì)象,都適合用霍夫變換來檢測(cè)。
下面主要介紹霍夫直線變換和霍夫圓變換。
- 霍夫直線變換用來在圖像內(nèi)尋找直線。在 OpenCV 中,可以用函數(shù) cv2.HoughLines()和函數(shù)cv2.HoughLinesP()實(shí)現(xiàn)。
- 霍夫圓變換用來在圖像內(nèi)尋找圓。以用函數(shù)cv2.HoughCircles()實(shí)現(xiàn)。
一、霍夫直線變換:
OpenCV 提供了函數(shù) cv2.HoughLines()和函數(shù) cv2.HoughLinesP()用來實(shí)現(xiàn)霍夫直線變換。下面首先介紹霍夫變換的基本原理,然后分別介紹這兩個(gè)函數(shù)的基本使用方法。
1. 霍夫直線變換原理:
? 為了方便說明問題,先以我們熟悉的笛卡兒坐標(biāo)系(即平面直角坐標(biāo)系,與笛卡兒空間對(duì)應(yīng))為例來說明霍夫變換的基本原理。與笛卡兒坐標(biāo)系對(duì)應(yīng),我們構(gòu)造一個(gè)霍夫坐標(biāo)系(對(duì)應(yīng)于霍夫空間)。在霍夫坐標(biāo)系中,橫坐標(biāo)采用笛卡兒坐標(biāo)系中直線的斜率k,縱坐標(biāo)使用笛卡兒坐標(biāo)系中直線的截距b。
? 首先,我們觀察笛卡兒空間中的一條直線在霍夫空間內(nèi)的映射情況。例如下圖中,左圖是笛卡兒x-y坐標(biāo)系(笛卡兒空間),右圖是霍夫k-b坐標(biāo)系(霍夫空間)。在笛卡兒空間中,存在著一條直線y=k0x+b0,該直線的截距k0是已知的常量,截距b0也是已知的常量。將該直線映射到霍夫空間內(nèi),找到已知的點(diǎn)(k0,b0),即完成映射。
? 從上述分析中可知,笛卡兒空間內(nèi)的一條直線,其斜率為k,截距為b,映射到霍夫空間內(nèi)成為一個(gè)點(diǎn)(k,b)。或者,可以這樣理解,霍夫空間內(nèi)的一個(gè)點(diǎn)(k0,b0),映射到笛卡兒空間,就是一條直線y=k0x+b0。
? 這里,我們用“映射”這個(gè)詞表達(dá)不同的空間(坐標(biāo)系)之間的對(duì)應(yīng)關(guān)系,也可以表述為“確定”。例如,上述關(guān)系可以表述為:
- 笛卡兒空間內(nèi)的一條直線確定了霍夫空間內(nèi)的一個(gè)點(diǎn)。
- 霍夫空間內(nèi)的一個(gè)點(diǎn)確定了笛卡兒空間內(nèi)的一條直線。
? 接下來,觀察笛卡兒空間中的一個(gè)點(diǎn)在霍夫空間內(nèi)的映射情況。如下圖所示,在笛卡兒空間內(nèi)存在一個(gè)點(diǎn)(x0,y0),通過該點(diǎn)的直線可以表示為y0=kx0+b。其中,(x0,y0)是已知的常量,(k,b)是變量。
? 對(duì)于表達(dá)式y(tǒng)0=kx0+b,通過算術(shù)運(yùn)算的左右移項(xiàng),可以表示為b=?x0k+y0。將點(diǎn)(x0,y0)映射到霍夫空間時(shí),可以認(rèn)為對(duì)應(yīng)的直線斜率為?x0,截距為y0,即b=?x0k+y0,如下圖中右圖的直線所示。
從上述分析可知:
- 笛卡兒空間內(nèi)的點(diǎn)(x0,y0)映射到霍夫空間,就是直線b=?x0k+y0。
- 霍夫空間內(nèi)的直線b=?x0k+y0映射到笛卡兒空間,就是點(diǎn)(x0,y0)。
下面我們看看笛卡兒空間中的兩個(gè)點(diǎn)映射到霍夫空間的情況。例如,在下圖中,左圖的笛卡兒空間中存在著兩個(gè)點(diǎn)(x0,y0)、(x1,y1),分析這兩個(gè)點(diǎn)映射到霍夫空間的情況。
為了方便理解,我們從不同的角度分析笛卡兒空間中這兩個(gè)點(diǎn)到霍夫空間的映射情況。
-
角度1:笛卡兒空間的一個(gè)點(diǎn)會(huì)映射為霍夫空間的一條線。
在笛卡兒空間內(nèi),存在著任意兩個(gè)點(diǎn)(x0,y0)、(x1,y1)。在霍夫空間中,這兩個(gè)點(diǎn)對(duì)應(yīng)著兩條不同的直線。當(dāng)然,通過分析可知,一條直線是b=?x0k+y0,另外一條直線是b=?x1k+y1。
-
角度2:笛卡兒空間的一條線會(huì)映射為霍夫空間的一個(gè)點(diǎn)
在笛卡兒空間內(nèi),存在著任意兩個(gè)點(diǎn)(x0,y0)、(x1,y1)。這兩個(gè)點(diǎn)一定能夠用一條直線連接,將連接它們的直線標(biāo)記為y=k1x+b1,則該直線的截距和斜率是(k1,b1)。也就是說,該直線在霍夫空間內(nèi)映射為點(diǎn)(k1,b1)。
從上述分析可知:
- 笛卡兒空間內(nèi)的兩個(gè)點(diǎn)會(huì)映射為霍夫空間內(nèi)兩條相交于(k1,b1)的直線。
- 這兩個(gè)點(diǎn)對(duì)應(yīng)的直線會(huì)映射為霍夫空間內(nèi)的點(diǎn)(k1,b1)。
換句話說,角度1決定了線條的數(shù)量,角度2決定了兩條線相交的點(diǎn)。
這說明,如果在笛卡兒空間內(nèi)有兩個(gè)點(diǎn)A、B,它們能夠連成一條直線y=k1x+b1,那么在霍夫空間中的點(diǎn)(k1,b1)上會(huì)有兩條直線,分別對(duì)應(yīng)著笛卡兒空間內(nèi)的兩個(gè)點(diǎn)A、B。
下面我們看看笛卡兒空間中的三個(gè)點(diǎn)映射到霍夫空間的情況。在下圖中,左圖是笛卡兒空間,其中存在(0,1)、(1,2)、(2,3)三個(gè)點(diǎn)。
下面從不同的角度分析笛卡兒空間中這三個(gè)點(diǎn)映射到霍夫空間的情況。
-
角度1:笛卡兒空間內(nèi)的一個(gè)點(diǎn)會(huì)映射為霍夫空間的一條線。
例如,笛卡兒空間中的(0,1)、(1,2)、(2,3)三個(gè)點(diǎn)映射到霍夫空間時(shí),每個(gè)點(diǎn)對(duì)應(yīng)著一條直線,對(duì)應(yīng)關(guān)系如表所示。
根據(jù)對(duì)應(yīng)關(guān)系可知:
- 笛卡兒空間內(nèi)的點(diǎn)(0,1),對(duì)應(yīng)著霍夫空間內(nèi)的直線b=1。
- 笛卡兒空間內(nèi)的點(diǎn)(1,2),對(duì)應(yīng)著霍夫空間內(nèi)的直線b=-k+2。
- 笛卡兒空間內(nèi)的點(diǎn)(2,3),對(duì)應(yīng)著霍夫空間內(nèi)的直線b=-2k+3。
從上述分析可知,笛卡兒空間內(nèi)的三個(gè)點(diǎn)映射為霍夫空間內(nèi)的三條直線。
-
角度2:笛卡兒空間內(nèi)的一條線會(huì)映射為霍夫空間的一個(gè)點(diǎn)。
例如,笛卡兒空間中的(0,1)、(1,2)、(2,3)三個(gè)點(diǎn)對(duì)應(yīng)著直線y=x+1,斜率k為1,截距b為1。該直線y=x+1 映射到霍夫空間內(nèi)的點(diǎn)(1,1)。
從上述角度1和角度2的分析可知:
- 笛卡兒空間中的(0,1)、(1,2)、(2,3)三個(gè)點(diǎn)會(huì)映射為霍夫空間內(nèi)相交于點(diǎn)(1,1)的三條直線。
- 笛卡兒空間中的(0,1)、(1,2)、(2,3)三個(gè)點(diǎn)所連成(確定)的直線映射為霍夫空間內(nèi)的點(diǎn)(1,1)
? 這說明,如果在笛卡兒空間內(nèi)有三個(gè)點(diǎn),并且它們能夠連成一條y=k1x+b1的直線,那么在霍夫空間中,對(duì)應(yīng)的點(diǎn)(k1,b1)上會(huì)有三條直線,分別對(duì)應(yīng)著笛卡兒空間內(nèi)的三個(gè)點(diǎn)。
? 到此,我們已經(jīng)發(fā)現(xiàn),如果在笛卡兒空間內(nèi),有N個(gè)點(diǎn)能夠連成一條直線y=k1x+b1,那么在霍夫空間內(nèi)就會(huì)有N條直線穿過對(duì)應(yīng)的點(diǎn)(k1,b1)。或者反過來說,如果在霍夫空間中,有越多的直線穿過點(diǎn)(k1,b1),就說明在笛卡兒空間內(nèi)有越多的點(diǎn)位于斜率為k1,截距為b1的直線y=k1x+b1上。
? 現(xiàn)在,我們看一個(gè)在笛卡兒空間內(nèi)更多個(gè)點(diǎn)映射到霍夫空間的例子,也驗(yàn)證一下上述觀點(diǎn)。在下圖中,左圖所示的是笛卡兒空間,其中有6個(gè)點(diǎn),下面從不同的角度看下這6個(gè)點(diǎn)在右圖霍夫空間的映射情況。
- 角度1:笛卡兒空間的一點(diǎn)會(huì)映射為霍夫空間的一條線。
笛卡兒空間中的6個(gè)點(diǎn):(0,1)、(1,2)、(2,3)、(3,4)、(3,2)、(1,4),映射到霍夫空間時(shí),每個(gè)點(diǎn)對(duì)應(yīng)著一條直線,對(duì)應(yīng)關(guān)系如表所示
根據(jù)對(duì)應(yīng)關(guān)系可知:
- 笛卡兒空間內(nèi)的點(diǎn)(0,1),對(duì)應(yīng)著霍夫空間內(nèi)的直線b=1。
- 笛卡兒空間內(nèi)的點(diǎn)(1,2),對(duì)應(yīng)著霍夫空間內(nèi)的直線b=-k+2。
- 笛卡兒空間內(nèi)的點(diǎn)(2,3),對(duì)應(yīng)著霍夫空間內(nèi)的直線b=-2*k+3。
- 笛卡兒空間內(nèi)的點(diǎn)(3,4),對(duì)應(yīng)著霍夫空間內(nèi)的直線b=-3*k+4。
- 笛卡兒空間內(nèi)的點(diǎn)(3,2),對(duì)應(yīng)著霍夫空間內(nèi)的直線b=-3*k+2。
- 笛卡兒空間內(nèi)的點(diǎn)(1,4),對(duì)應(yīng)著霍夫空間內(nèi)的直線b=-1*k+4。
從上述分析可知,笛卡兒空間內(nèi)的6個(gè)點(diǎn)映射為霍夫空間內(nèi)的6條直線
-
角度2:笛卡兒空間的一條線會(huì)映射為霍夫空間的一個(gè)點(diǎn)。
這里為了觀察方便,將笛卡兒空間內(nèi)連接了較多點(diǎn)的線繪制出來:連接點(diǎn)(0,1)、(1,2)、(2,3)、(3,4)的線LineA,連接點(diǎn)(2,3)、(3,2)、(1,4)的線LineB,連接點(diǎn)(0,1)、(3,2)的線LineC
需要注意,在笛卡兒空間內(nèi),各個(gè)點(diǎn)之間存在多條直線。例如在點(diǎn)(1,2)、(3,2)之間,點(diǎn)(3,2)、(3,4)之間,點(diǎn)(1,4)、(3,4)之間都存在著直線,這里做了簡(jiǎn)化,沒有將上述直線都繪制出來。
下面分析笛卡兒空間內(nèi)的三條直線LineA、LineB、LineC在霍夫空間內(nèi)的映射情況。
- 直線LineA經(jīng)過了4個(gè)點(diǎn),表達(dá)式為y=1 * x+1,斜率k為1,截距b為1,在霍夫空間內(nèi)對(duì)應(yīng)于點(diǎn)A(1,1)。
- 直線LineB經(jīng)過了3個(gè)點(diǎn),表達(dá)式為y=-1 * x+5,斜率k為-1,截距b為5,在霍夫空間內(nèi)對(duì)應(yīng)于點(diǎn)B(-1,5)。
- 直線LineC經(jīng)過了2個(gè)點(diǎn),表達(dá)式為y=-1/3 * x+1,斜率k為-1/3,截距b為1,在霍夫空間內(nèi)對(duì)應(yīng)于點(diǎn)C(-1/3,1)。
在上圖中可以看到,右圖所示的霍夫空間內(nèi)點(diǎn)A有4條直線穿過,點(diǎn)B有3條直線穿過,點(diǎn)C有2條直線穿過。分析上述關(guān)系:
- 霍夫空間內(nèi)有4條直線穿過點(diǎn)A。點(diǎn)A確定了笛卡兒空間內(nèi)的一條直線,同時(shí)該直線穿過4個(gè)點(diǎn),即霍夫空間內(nèi)的點(diǎn)A確定了笛卡兒空間內(nèi)的LineA,該直線上包含(0,1)、(1,2)、(2,3)、(3,4)共4個(gè)點(diǎn)。
- 霍夫空間內(nèi)有3條直線穿過點(diǎn)B。點(diǎn)B確定了笛卡兒空間內(nèi)的一條直線,同時(shí)該直線穿過3個(gè)點(diǎn),即霍夫空間內(nèi)的點(diǎn)B確定了笛卡兒空間內(nèi)的LineB,該直線上包含(2,3)、(3,2)、(1,4)共3個(gè)點(diǎn)。
- 霍夫空間內(nèi)有2條直線穿過點(diǎn)C。點(diǎn)C確定了笛卡兒空間內(nèi)的一條直線,同時(shí)該直線穿過2個(gè)點(diǎn),即霍夫空間內(nèi)的點(diǎn)C確定了笛卡兒空間內(nèi)的LineC,該直線上包含(0,1)、(2,3)共2個(gè)點(diǎn)。
? 綜上所述,在霍夫空間內(nèi),經(jīng)過一個(gè)點(diǎn)的直線越多,說明其在笛卡兒空間內(nèi)映射的直線,是由越多的點(diǎn)所構(gòu)成(穿過)的。我們知道,兩個(gè)點(diǎn)就能構(gòu)成一條直線。但是,如果有一個(gè)點(diǎn)是因?yàn)橛?jì)算錯(cuò)誤而產(chǎn)生的,那么它和另外一個(gè)點(diǎn),也會(huì)構(gòu)成一條直線,此時(shí)就會(huì)憑空構(gòu)造出一條實(shí)際上并不存在的直線。這種情況是要極力避免的。
? 因此,在計(jì)算中,我們希望用更多的點(diǎn)構(gòu)造一條直線,以提高直線的可靠性。也就是說,如果一條直線是由越多點(diǎn)所構(gòu)成的,那么它實(shí)際存在的可能性就越大,它的可靠性也就越高。
? 因此,霍夫變換選擇直線的基本思路是:選擇有盡可能多直線交匯的點(diǎn)。
上面都是以我們熟悉的笛卡兒空間為例說明的。在笛卡兒空間中,可能存在諸如x=x0的垂線LineA的形式
此時(shí),斜率k為無窮大,截距b無法取值。因此,上圖中的垂線LineA無法映射到霍夫空間內(nèi)。為了解決上述問題,可以考慮將笛卡兒坐標(biāo)系映射到極坐標(biāo)系上,如圖所示。
在笛卡兒坐標(biāo)系內(nèi)使用的是斜率k和截距b,即用(k,b)表示一條直線。在極坐標(biāo)系內(nèi),采用極徑r(有時(shí)也用ρ表示)和極角θ來表示,即(r,θ)來表示。極坐標(biāo)系中的直線可以表示為:
- r=xcosθ+ysinθ
? 例如,上圖中的直線LineA,可以使用極坐標(biāo)的極徑r和極角θ來表示。其中,r是直線LineA與圖像原點(diǎn)O之間的距離,參數(shù)θ是直線LineA的垂線LineB與x軸的角度。在這種表示方法中,圖像中的直線有一個(gè)(0~π)的角θ,而r的最大值是圖像對(duì)角線的長(zhǎng)度。用這種表示方法,可以很方便地表示上圖中的3個(gè)點(diǎn)所構(gòu)成的直線。
與笛卡兒空間和霍夫空間的映射關(guān)系類似:
- 極坐標(biāo)系內(nèi)的一個(gè)點(diǎn)映射為霍夫坐標(biāo)系(霍夫空間)內(nèi)的一條線(曲線)。
- 極坐標(biāo)系內(nèi)的一條線映射為霍夫坐標(biāo)系內(nèi)的一個(gè)點(diǎn)。
? 一般來說,在極坐標(biāo)系內(nèi)的一條直線能夠通過在霍夫坐標(biāo)系內(nèi)相交于一點(diǎn)的線的數(shù)量來評(píng)估。在霍夫坐標(biāo)系內(nèi),經(jīng)過一個(gè)點(diǎn)的線越多,說明其映射在極坐標(biāo)系內(nèi)的直線,是由越多的點(diǎn)所構(gòu)成(穿過)的。因此,霍夫變換選擇直線的基本思路是:選擇由盡可能多條線匯成的點(diǎn)。
? 通常情況下,設(shè)置一個(gè)閾值,當(dāng)霍夫坐標(biāo)系內(nèi)交于某點(diǎn)的曲線達(dá)到了閾值,就認(rèn)為在對(duì)應(yīng)的極坐標(biāo)系內(nèi)存在(檢測(cè)到)一條直線。
? 上述內(nèi)容是霍夫變換的原理,即使完全不理解上述原理,也不影響我們使用OpenCV提供的霍夫變換函數(shù)來進(jìn)行霍夫變換。OpenCV本身是一個(gè)黑盒子,它給我們提供了接口(參數(shù)、返回值),我們只需要掌握接口的正確使用方法,就可以正確地處理圖像問題,無須掌握其內(nèi)部工作原理。
? 在某種情況下,OpenCV庫和Photoshop等圖像處理軟件是類似的,只要掌握了它們的使用方法,就能夠得到正確的處理結(jié)果。在進(jìn)行圖像處理時(shí),并不需要我們關(guān)注其實(shí)現(xiàn)原理等技術(shù)細(xì)節(jié)。但是,如果我們進(jìn)一步了解其工作原理,對(duì)我們的工作也是有大有裨益的。
2. HoughLines函數(shù):
OpenCV 提供了函數(shù) cv2.HoughLines()用來實(shí)現(xiàn)霍夫直線變換,該函數(shù)要求所操作的源圖像是一個(gè)二值圖像,所以在進(jìn)行霍夫變換之前要先將源圖像進(jìn)行二值化,或者進(jìn)行 Canny 邊緣檢測(cè)。
函數(shù)cv2.HoughLines()的語法格式為:
- lines=cv2.HoughLines(image,rho,theta,threshold)
- image:是輸入圖像,即源圖像,必須是8位的單通道二值圖像。如果是其他類型的圖像,在進(jìn)行霍夫變換之前,需要將其修改為指定格式。
- rho:為以像素為單位的距離r的精度。一般情況下,使用的精度是1。
- theta:為角度θ的精度。一般情況下,使用的精度是π/180,表示要搜索所有可能的角度。
- threshold:是閾值。該值越小,判定出的直線就越多。通過上一節(jié)的分析可知,識(shí)別直線時(shí),要判定有多少個(gè)點(diǎn)位于該直線上。在判定直線是否存在時(shí),對(duì)直線所穿過的點(diǎn)的數(shù)量進(jìn)行評(píng)估,如果直線所穿過的點(diǎn)的數(shù)量小于閾值,則認(rèn)為這些點(diǎn)恰好(偶然)在算法上構(gòu)成直線,但是在源圖像中該直線并不存在;如果大于閾值,則認(rèn)為直線存在。所以,如果閾值較小,就會(huì)得到較多的直線;閾值較大,就會(huì)得到較少的直線。
- lines:返回值 ,每個(gè)元素都是一對(duì)浮點(diǎn)數(shù),表示檢測(cè)到的直線的參數(shù),即(r,θ),是numpy.ndarray類型。
注意:
? 使用函數(shù) cv2.HoughLines()檢測(cè)到的是圖像中的直線而不是線段,因此檢測(cè)到的直線是沒有端點(diǎn)的。所以,我們?cè)谶M(jìn)行霍夫直線變換時(shí)所繪制的直線都是穿過整幅圖像的。
? 繪制直線的方法是,對(duì)于垂直方向的直線(不是指垂線,是指垂直方向上的各種角度的直線),計(jì)算它與圖像水平邊界(即圖像中的第一行和最后一行)的交叉點(diǎn),然后在這兩個(gè)交叉點(diǎn)之間畫線。對(duì)于水平方向上的直線,采用類似的方式完成,只不過用到的是圖像的第一列和最后一列。
? 在繪制線時(shí),所使用的函數(shù)是cv2.line()。該函數(shù)方便的地方在于,即使點(diǎn)的坐標(biāo)超出了圖像的范圍,它也能正確地畫出線來,因此沒有必要檢查交叉點(diǎn)是否位于圖像內(nèi)部。遍歷函數(shù)cv2.HoughLines()的返回值lines
import cv2 import numpy as np import matplotlib.pyplot as pltimg = cv2.imread('./buliding.jpg') gray_img = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) edges = cv2.Canny(gray_img, 50, 150, apertureSize=3)rgb_img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB) new_img = rgb_img.copy() print(lines) for line in lines:rho, theta = line[0]a = np.cos(theta)b = np.sin(theta)x0 = a * rhoy0 = b * rhox1 = int(x0 + 1000 * (-b))y1 = int(y0 + 1000 * (a))x2 = int(x0 - 1000 * (-b))y2 = int(y0 - 1000 * (a))cv2.line(new_img, (x1, y1), (x2, y2), (0, 0, 255), 2)plt.subplot(131) plt.imshow(rgb_img) plt.axis('off')plt.subplot(132) plt.imshow(edges) plt.axis('off')plt.subplot(133) plt.imshow(new_img) plt.axis('off')plt.show()補(bǔ)充:OpenCV中HoughLines兩個(gè)點(diǎn)(x1, y1),(x2, y2)是如何求出的。
上圖顯示怎么放大線段到1000即當(dāng)r=1000時(shí)的計(jì)算方法。可以是300也可是500、800。
3. HoughLinesP函數(shù):
概率霍夫變換對(duì)基本霍夫變換算法進(jìn)行了一些修正,是霍夫變換算法的優(yōu)化。它沒有考慮所有的點(diǎn)。相反,它只需要一個(gè)足以進(jìn)行線檢測(cè)的隨機(jī)點(diǎn)子集即可。
為了更好地判斷直線(線段),概率霍夫變換算法還對(duì)選取直線的方法作了兩點(diǎn)改進(jìn):
- **所接受直線的最小長(zhǎng)度。**如果有超過閾值個(gè)數(shù)的像素點(diǎn)構(gòu)成了一條直線,但是這條直線很短,那么就不會(huì)接受該直線作為判斷結(jié)果,而認(rèn)為這條直線僅僅是圖像中的若干個(gè)像素點(diǎn)恰好隨機(jī)構(gòu)成了一種算法上的直線關(guān)系而已,實(shí)際上原圖中并不存在這條直線。
- **接受直線時(shí)允許的最大像素點(diǎn)間距。**如果有超過閾值個(gè)數(shù)的像素點(diǎn)構(gòu)成了一條直線,但是這組像素點(diǎn)之間的距離都很遠(yuǎn),就不會(huì)接受該直線作為判斷結(jié)果,而認(rèn)為這條直線僅僅是圖像中的若干個(gè)像素點(diǎn)恰好隨機(jī)構(gòu)成了一種算法上的直線關(guān)系而已,實(shí)際上原始圖像中并不存在這條直線。
在OpenCV中通過cv2.HoughLinesP()函數(shù)實(shí)現(xiàn)概率霍夫變換:
- lines = cv2.HoughLiesP(image, rho, theta, threshold, minLineLength, maxLineGap)
- image:是輸入圖像,即原圖像,必須為8位的單通道二值圖像。對(duì)于其他類型的圖像,在進(jìn)行霍夫變換之前,需要將其修改為這個(gè)指定的格式。
- rho:為以像素為單位的距離r的精度。一般情況下,使用的精度是1。
- theta:是角度θ的精度。一般情況下,使用的精度是np.pi/180,表示要搜索可能的角度。
- threshold:是閾值。該值越小,判定出的直線越多;值越大,判定出的直線就越少。
- minLineLength:用來控制“接受直線的最小長(zhǎng)度”的值,默認(rèn)值為0。
- maxLineGap:用來控制接受共線線段之間的最小間隔,即在一條線中兩點(diǎn)的最大間隔。
如果兩點(diǎn)間的間隔超過了參數(shù)maxLineGap的值,就認(rèn)為這兩點(diǎn)不在一條線上。默認(rèn)值為0。 - lines:返回值。是由numpy.ndarray類型的元素構(gòu)成的,其中每個(gè)元素都是一對(duì)浮點(diǎn)數(shù),表示檢測(cè)到的直線的參數(shù),即(x1, y1)、(x2, y2)。
2. 霍夫圓環(huán)變換:
? 霍夫變換除了用來檢測(cè)直線外,也能用來檢測(cè)其他幾何對(duì)象。實(shí)際上,只要是能夠用一個(gè)參數(shù)方程表示的對(duì)象,都適合用霍夫變換來檢測(cè)。
? 用霍夫圓變換來檢測(cè)圖像中的圓,與使用霍夫直線變換檢測(cè)直線的原理類似。在霍夫圓變換中,需要考慮圓半徑和圓心(x坐標(biāo)、y坐標(biāo))共3個(gè)參數(shù)。在OpenCV中,采用的策略是兩輪篩選。第1輪篩選找出可能存在圓的位置(圓心);第2輪再根據(jù)第1輪的結(jié)果篩選出半徑大小。
? 與用來決定是否接受直線的兩個(gè)參數(shù)“接受直線的最小長(zhǎng)度(minLineLength)”和“接受直線時(shí)允許的最大像素點(diǎn)間距(MaxLineGap)”類似,霍夫圓變換也有幾個(gè)用于決定是否接受圓的參數(shù):圓心間的最小距離、圓的最小半徑、圓的最大半徑。
在OpenCV中,通過函數(shù)cv2.HoughCircles()實(shí)現(xiàn)霍夫圓變換,**該函數(shù)將Canny邊緣檢測(cè)和霍夫變換結(jié)合。**其語法格式為:
- circles = cv2.HoughCircles(image, method, dp, minDist, param1, param2, minRadius, maxradius)
- image:輸入圖像,即源圖像,類型為8位的單通道灰度圖像。
- method:檢測(cè)方法。截止到OpenCV 4.0.0-pre版本,HOUGH_GRADIENT是唯一可用的參數(shù)值。該參數(shù)代表的是霍夫圓檢測(cè)中兩輪檢測(cè)所使用的方法。
- dp:累計(jì)器分辨率,它是一個(gè)分割比率,用來指定圖像分辨率與圓心累加器分辨率的比例。例如,如果dp=1,則輸入圖像和累加器具有相同的分辨率。
- minDist:圓心間的最小間距。該值被作為閾值使用,如果存在圓心間距離小于該值的多個(gè)圓,則僅有一個(gè)會(huì)被檢測(cè)出來。因此,如果該值太小,則會(huì)有多個(gè)臨近的圓被檢測(cè)出來;如果該值太大,則可能會(huì)在檢測(cè)時(shí)漏掉一些圓。
- param1:該參數(shù)是缺省的,在缺省時(shí)默認(rèn)值為100。它對(duì)應(yīng)的是Canny邊緣檢測(cè)器的高閾值(低閾值是高閾值的二分之一)。
- param2:圓心位置必須收到的投票數(shù)。只有在第1輪篩選過程中,投票數(shù)超過該值的圓,才有資格進(jìn)入第2輪的篩選。因此,該值越大,檢測(cè)到的圓越少;該值越小,檢測(cè)到的圓越多。這個(gè)參數(shù)是缺省的,在缺省時(shí)具有默認(rèn)值100。
- minRadius:圓半徑的最小值,小于該值的圓不會(huì)被檢測(cè)出來。該參數(shù)是缺省的,在缺省時(shí)具有默認(rèn)值0,此時(shí)該參數(shù)不起作用。
- maxRadius:圓半徑的最大值,大于該值的圓不會(huì)被檢測(cè)出來。該參數(shù)是缺省的,在缺省時(shí)具有默認(rèn)值0,此時(shí)該參數(shù)不起作用。
- circles:返回值,由圓心坐標(biāo)和半徑構(gòu)成的numpy.ndarray。
需要特別注意,在調(diào)用函數(shù) cv2.HoughLinesCircles()之前,要對(duì)源圖像進(jìn)行平滑操作,以減少圖像中的噪聲,避免發(fā)生誤判。
import cv2 import numpy as np import matplotlib.pyplot as pltimg = cv2.imread('xiangqi.jpg') gray_img = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) rgb_img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB) new_img = rgb_img.copy()circles = cv2.HoughCircles(gray_img, cv2.HOUGH_GRADIENT, 1, 100, param1=100, param2=30, minRadius=50, maxRadius=100) print(circles) circles = np.uint16(np.around(circles)) print(circles) for i in circles[0, :]:cv2.circle(new_img, (i[0], i[1]), i[2], (255, 0, 0), 10)# 圓心cv2.circle(new_img, (i[0], i[1]), 2, (255, 0, 0), 10)plt.subplot(121) plt.imshow(rgb_img) plt.title('img') plt.axis('off')plt.subplot(122) plt.imshow(new_img) plt.title('rst') plt.axis('off') plt.show()總結(jié)
- 上一篇: 【预测模型】基于天牛须算法优化ELman
- 下一篇: 【好程序员笔记分享】C语言之break和