霍夫變換常用來在圖像中提取直線和圓等幾何形狀。如下圖:
我們下面來看看如何使用霍夫變換來檢測直線。一條直線可以用數學表達式 y = mx + 或者 ρ = xcosθ + y sinθ表示(極坐標)
簡單說明一下:
ρ 是從原點到直線的垂直距離,θ是直線的垂線與橫軸順時針方向的夾角,如下圖所示:
首先創建一個2D數組(累加器),初始化累加器,所有的值都為0。行表示 ρ,列表示 θ。這個數組的大小決定了最后結果的准確性。如果你希望角度精確到1°,你就需要180列。對於 ρ,最大值為圖片對角線距離。
想象一下我們有一個大小為100x100的直線位於圖像中央。取直線上的第一個點,我們知道此處的(x,y)值,把x和y帶入公式:ρ = xcosθ + y sinθ,然后遍歷 θ 的取值0, 1, 2, 3,...,180.分別求出對應的 ρ 值,這樣我們就得到了一系列(ρ,θ)的數值對,如果這個數值對在累加器中也存在相應的位置,就在這個位置上加 1。由於同一條直線上的點必然會有同樣的(ρ,θ)。所以現在累加器中的(50,90)=1。現在取直線上的第二個點。重復上邊的過程。更新累加器中的值。現在累加器中(50,90)的值為 2。你每次做的就是更新累加器中的值。對直線上的每個點都執行上邊的操作,每次操作完成之后,累加器中的值就加 1,但其他地方有時會加 1, 有時不會。按照這種方式下去,到最后累加器中(50,90)的值肯定是最大的。如果你搜索累加器中的最大值,並找到其位置(50,90),這就說明圖像中有一條直線,這條直線到原點的距離為 50,它的垂線與橫軸的夾角為 90 度。
OpenCV中首先計算(r,θ) 累加數,累加數超過一定值后就認為在同一直線上(有一個閾值)。
霍夫直線變換
import cv2 import numpy as np # 1.加載圖片,轉為二值圖 img = cv2.imread('shapes.jpg') drawing = np.zeros(img.shape[:], dtype=np.uint8) gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) edges = cv2.Canny(gray, 50, 150) # 2.霍夫直線變換 lines = cv2.HoughLines(edges, 0.8, np.pi / 180, 90)
函數中:
- 參數1:要檢測的二值圖(一般是閾值分割或邊緣檢測后的圖)
- 參數2:距離 ρ 的精度,值越大,考慮越多的線
- 參數3:角度 θ 的精度,值越小,考慮越多的線
- 參數4:累加數閾值,值越小,考慮越多的線
# 3.將檢測的線畫出來(注意是極坐標噢) 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)) cv2.line(drawing, (x1, y1), (x2, y2), (0, 0, 255))
統計概率霍夫直線變換
前面的方法又稱為標准霍夫變換,它會計算圖像中的每一個點,計算量比較大,另外它得到的是整一條線,並不知道原圖中直線的端點。所以提出了概率霍夫直線變換(Probabilistic Hough Transform),是一種改進的霍夫變換:
drawing = np.zeros(img.shape[:], dtype=np.uint8) # 統計概率霍夫線變換 lines = cv2.HoughLinesP(edges, 0.8, np.pi / 180, 90, minLineLength=50, maxLineGap=10)
# 將檢測的線畫出來 for line in lines: x1, y1, x2, y2 = line[0] cv2.line(drawing, (x1, y1), (x2, y2), (0, 255, 0), 1, lineType = cv2.LINE_AA) cv2.imwrite('houghlines4.jpg', drawing)
參數:
minLineLength
:最短長度閾值,比這個長度短的線會被排除- maxLineGap:同一直線兩點之間的最大距離
cv2.LINE_AA:抗鋸齒線型
霍夫圓變換
霍夫圓變換跟直線變換類似,只不過線是用(r,θ),圓是用(x_center,y_center,r)來表示,從二維變成了三維,數據量變大了很多;所以一般使用霍夫梯度法去減少計算量
drawing = np.zeros(img.shape[:], dtype=np.uint8) # 霍夫圓變換 circles = cv2.HoughCircles(edges, cv2.HOUGH_GRADIENT, 1, 20, param2=30) circles = np.int0(np.around(circles)) # 將檢測的圓畫出來 for i in circles[0, :]: cv2.circle(drawing, (i[0], i[1]), i[2], (0, 255, 0), 2) # 畫出外圓 cv2.circle(drawing, (i[0], i[1]), 2, (0, 0, 255), 3) # 畫出圓心 cv2.imwrite('HoughCircles.jpg', drawing)
- 參數2:變換方法,一般使用霍夫梯度法,詳情:HoughModes
- 參數3 dp=1:表示霍夫梯度法中累加器圖像的分辨率與原圖一致
- 參數4:兩個不同圓圓心的最短距離
- 參數5:param2跟霍夫直線變換中的累加數閾值一樣