python OpenCV學習筆記(二十七):Hough線變換
Hough變換是一種很流行的技術,可以檢測任何可以用數學的形式來表示的形狀。即使它被破壞或變形,它也能檢測出它的形狀。我們將會看到它是如何工作的。
一條線可以表示成y = mx + c
或引數形式,像ρ=xcosθ+ysinθ
,其中ρ是從原點到直線的垂直距離,θ角是由這條垂線和水平軸以逆時針的方向形成的(這個方向取決於你如何表示座標系統。這種表示法在OpenCV中使用)。參照下面的圖片:
所以如果這條線在原點下面,它就會有一個正的rho和一個小於180度的角。如果它在原點上方,不是取一個大於180度的角,而是取一個小於180度的角,並且rho為負。任何垂直線都是0度,水平線都是90度。
現在讓我們來看看Hough轉換是如何工作的。任何線都可以用這兩個術語表示(ρ,θ)
考慮一個100x100的影象,中間有一條水平線。取直線的第一點。你知道它的(x,y)值。現在在直線方程中,把值=0,1,2,。180,檢查一下你得到的ρ值。對於每一個(ρ,θ)對,在對應的(ρ,θ)單元格中,我們的累加器增加一個值。所以現在在累加器中,我們有(50,90)=1和其他一些單元格。
現在在直線上取第二個點。和上面一樣。增加與(ρ,θ)對應的單元格的值。這一次,單元格(50,90)=2。實際上,您所做的是對(ρ,θ)值進行投票。你在這條線上的每一點都繼續這個過程。在每一個點上,單元格(50,90)將被增加或投票,而其他的單元格可能投票或不被投票。這樣,在最後,單元格(50,90)將獲得最大的選票。所以如果你在累加器中尋找最大的選票,你就會得到這個值(50,90),這幅圖中有一條線距原點50度,角90度。它在下面的動畫中很好地展示了:
這就是hough變換對線條的作用。它很簡單,您可以自己使用Numpy實現它。下面是一個顯示累加器的影象。某些位置的亮點表示它們是影象中可能的線的引數。
OpenCV中的Hough變換
以上所解釋的一切都封裝在OpenCV函式cv.HoughLines()
中。它只是返回一個數組:math:(ρ,θ)的值。ρ是畫素和θ測量是用弧度。 第一個引數,輸入影象應該是一個二進位制影象,因此在應用hough變換之前應用閾值或使用Canny邊緣檢測。第二和第三個引數分別是ρ和θ的精度。第四個引數是閾值,這意味著它應該被視為一條直線。記住,選票的數量取決於直線上的點的數量。所以它表示應該檢測到的最小長度。
import cv2 as cv
import numpy as np
img = cv.imread('sudoku.jpg')
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)
概率Hough變換
在hough轉換中,你可以看到,即使對於一個有兩個引數的線,它也需要大量的計算。概率Hough變換是我們所見的Hough變換的一個優化。它並沒有把所有的要點都考慮進去。相反,它只需要一個隨機子集,對行檢測來說足夠了。我們必須降低閾值。下面的圖片是Hough空間的Hough變換和概率Hough變換。
OpenCV使用函式cv.HoughLinesP()
。它有兩個新引數
- minLineLength:最小長度的線。比這更短的線段被拒絕了。
- maxLineGap:最大限度允許線段之間的間隙把它們當作一條線來對待。
最重要的是,它直接返回線的兩個端點。在前面的例子中,你只得到了直線的引數,你必須找到所有的點。在這裡,一切都是直接而簡單的。
import cv2 as cv
import numpy as np
img = cv.imread('sudoku.png')
gray = cv.cvtColor(img, cv.COLOR_BGR2GRAY)
edges = cv.Canny(gray, 50, 150, apertureSize=3)
lines = cv.HoughLinesP(deges, 1, np.pi/180, 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)