一、Hough 直線變換(Hough Line Transform )
目標(biāo)
? 理解霍夫變換的概念
? 學(xué)習(xí)如何在一張圖片中檢測直線
? 學(xué)習(xí)函數(shù): cv2.HoughLines(), cv2.HoughLinesP()
原理
霍夫變換在檢測各種形狀的的技術(shù)中非常流行,
如果你要檢測的形狀可以用數(shù)學(xué)表達(dá)式寫出,你就可以是使用霍夫變換檢測它,即使檢測的形狀存在一點破壞或者扭曲也可以使用
。我們下面就看看如何使用霍夫變換檢測直線。?
一條直線可以用數(shù)學(xué)表達(dá)式?
或者?
表示。ρ 是從原點到直線的垂直距離, θ 是直線的垂線與橫軸順時針方向的夾角(如果你使用的坐標(biāo)系不同方向也可能不同,這里是按 OpenCV 使用的坐標(biāo)系描述的)。如下圖所示:?
所以如果一條線在原點下方經(jīng)過, ρ 的值就應(yīng)該大于 0,角度小于 180。但是如果從原點上方經(jīng)過的話,角度不是大于 180,而是小于 180,但 ρ 的值小于 0。垂直線的角度為 0 度,水平線的角度為 90 度。
讓我們來看看霍夫變換是如何工作的。每一條直線都可以用 (ρ,?θ) 表示。所以首先創(chuàng)建一個 2D 數(shù)組(累加器),初始化累加器,所有的值都為 0。行表示 ρ,列表示 θ。這個數(shù)組的大小決定了最后結(jié)果的準(zhǔn)確性。如果你希望角度精確到 1 度,你就需要 180 列。對于 ρ,最大值為圖片對角線的距離。所以如果精確度要達(dá)到一個像素的級別,行數(shù)就應(yīng)該與圖像對角線的距離相等。?
想象一下我們有一個大小為 100x100 的直線位于圖像的中央。取直線上的第一個點,我們知道此處的(x, y)值。把 x 和 y 帶入上邊的方程組,然后遍歷 θ 的取值: 0, 1, 2, 3, ..., 180。分別求出與其對應(yīng)的 ρ 的值,這樣我們就得到一系列(ρ,θ)的數(shù)值對,如果這個數(shù)值對在累加器中也存在相應(yīng)的位置,就在這個位置上加 1。所以現(xiàn)在累加器中的(50, 90)=1。(一個點可能存在于多條直線中,所以對于直線上的每一個點可能是累加器中的多個值同時加 1)
現(xiàn)在取直線上的第二個點。重復(fù)上邊的過程。更新累加器中的值。現(xiàn)在累加器中(50,90)的值為 2。你每次做的就是更新累加器中的值。對直線上的每個點都執(zhí)行上邊的操作,每次操作完成之后,累加器中的值就加 1,但其他地方有時會加 1, 有時不會。按照這種方式下去,到最后累加器中(50,90)的值肯定是最大的。如果你搜索累加器中的最大值,并找到其位置(50,90),這就說明圖像中有一條直線,這條直線到原點的距離為 50,它的垂線與橫軸的夾角為 90 度。下面的動畫很好的演示了這個過程(Image Courtesy: Amos Storkey )。
這就是霍夫直線變換工作的方式。很簡單,也許你自己就可以使用 Numpy搞定它。下圖顯示了一個累加器。其中最亮的兩個點代表了圖像中兩條直線的參數(shù)。(Image courtesy: Wikipedia)。
1.1?OpenCV 中的霍夫變換
上面介紹的整個過程在 OpenCV 中都被封裝進(jìn)了一個函數(shù): cv2.HoughLines() 。返回值就是一個數(shù)組(ρ,?θ)。 ρ 的單位是像素, θ 的單位是弧度。這個 函數(shù)的第一個參數(shù)是一個二值化圖像,所以在進(jìn)行霍夫變換之前要首先進(jìn)行二值化,或者進(jìn)行Canny 邊緣檢測 。第二和第三個值分別代表 ρ 和 θ 的精確度。第四個參數(shù)是閾值,只有累加其中的值高于閾值時才被認(rèn)為是一條直線,也可以把它看成能檢測到的直線的最短長度(以像素點為單位)。示例如下:
import cv2 as cv
import numpy as np
img = cv.imread(cv.samples.findFile('sudoku.png'))
gray = cv.cvtColor(img, cv.COLOR_BGR2GRAY)
edges = cv.Canny(gray, 50, 150, apertureSize=3)
lines = cv.HoughLines(edges, 1, np.pi / 180, 200)
for line in lines:
rho, theta = line[0]
a = np.cos(theta)
b = np.sin(theta)
x0 = a * rho
y0 = b * rho
x1 = int(x0 + 1000 * (-b))
y1 = int(y0 + 1000 * (a))
x2 = int(x0 - 1000 * (-b))
y2 = int(y0 - 1000 * (a))
cv.line(img, (x1, y1), (x2, y2), (0, 0, 255), 2)
cv.imwrite('houghlines3.jpg', img)
結(jié)果:
1.2 Probabilistic Hough Transform
從上邊的過程我們可以發(fā)現(xiàn):僅僅是一條直線都需要兩個參數(shù),這需要大量的計算。 Probabilistic_Hough_Transform 是對霍夫變換的一種優(yōu)化 。它不會對每一個點都進(jìn)行計算,而是從一幅圖像中隨機選取一個點集進(jìn)行計算,對于直線檢測來說這已經(jīng)足夠了。但是使用這種變換我們必須要降低閾值(總的點數(shù)都少了,閾值肯定也要小呀!)。下圖是對兩種方法的對比。(Image Courtesy : Franck Bettinger’s homepage)
OpenCV 中使用由?Matas, J. , Galambos, C. 和 Kittler, J.V. 提出的Progressive Probabilistic Hough Transform。這個函數(shù)是 cv2.HoughLinesP() ,它有兩個參數(shù):?
? minLineLength - 線的最短長度。比這個短的線都會被忽略。
? MaxLineGap - 兩條線段之間的最大間隔,如果小于此值,這兩條直線就被看成是一條直線。?
更加給力的是,這個函數(shù)的返回值就是直線的起點和終點。而在前面的例子中,我們只得到了直線的參數(shù),并且你必須要找到所有的點。而在這里一切都很直接很簡單。
import cv2 as cv
import numpy as np
img = cv.imread(cv.samples.findFile('sudoku.png'))
gray = cv.cvtColor(img,cv.COLOR_BGR2GRAY)
edges = cv.Canny(gray,50,150,apertureSize = 3)
lines = cv.HoughLinesP(edges,1,np.pi/180,100,minLineLength=100,maxLineGap=10)
for line in lines:
x1,y1,x2,y2 = line[0]
cv.line(img,(x1,y1),(x2,y2),(0,255,0),2)
cv.imwrite('houghlines5.jpg',img)
?
?
二、Hough圓形變換(Hough Circle Transform)
目標(biāo)
? 學(xué)習(xí)使用霍夫變換在圖像中找圓形(環(huán))。
? 學(xué)習(xí)函數(shù): cv2.HoughCircles()
原理
圓形的數(shù)學(xué)表達(dá)式為
,其中
?為圓心的坐標(biāo), r 為圓的半徑。從這個方程中我們可以看出:一個圓環(huán)需要 3個參數(shù)來確定。所以進(jìn)行圓形霍夫變換的累加器必須是 3 維的,這樣的話效率就會很低。OpenCV 用了一個比較巧妙的辦法,
霍夫梯度法
,它可以使用邊界的梯度信息。我們要使用的函數(shù)為
cv2.HoughCircles()
。文檔中對它的參數(shù)有詳細(xì)的解釋。這里我們就直接看代碼吧。?
import numpy as np
import cv2 as cv
img = cv.imread('opencv-logo-white.png',0)
img = cv.medianBlur(img,5)
cimg = cv.cvtColor(img,cv.COLOR_GRAY2BGR)
circles = cv.HoughCircles(img,cv.HOUGH_GRADIENT,1,20,
param1=50,param2=30,minRadius=0,maxRadius=0)
circles = np.uint16(np.around(circles))
for i in circles[0,:]:
# draw the outer circle
cv.circle(cimg,(i[0],i[1]),i[2],(0,255,0),2)
# draw the center of the circle
cv.circle(cimg,(i[0],i[1]),2,(0,0,255),3)
cv.imshow('detected circles',cimg)
cv.waitKey(0)
cv.destroyAllWindows()
?結(jié)果:
?
參考資料:
1.?OpenCV-Python官方文檔-Hough Line Transform
2.?OpenCV-Python官方文檔-Hough Circle Transform
3.《OpenCV-Python 中文教程》(段力輝 譯)
?
?
更多文章、技術(shù)交流、商務(wù)合作、聯(lián)系博主
微信掃碼或搜索:z360901061

微信掃一掃加我為好友
QQ號聯(lián)系: 360901061
您的支持是博主寫作最大的動力,如果您喜歡我的文章,感覺我的文章對您有幫助,請用微信掃描下面二維碼支持博主2元、5元、10元、20元等您想捐的金額吧,狠狠點擊下面給點支持吧,站長非常感激您!手機微信長按不能支付解決辦法:請將微信支付二維碼保存到相冊,切換到微信,然后點擊微信右上角掃一掃功能,選擇支付二維碼完成支付。
【本文對您有幫助就好】元
