OpenCV-Python中的圖像處理-霍夫變換
- 霍夫變換
- 霍夫直線變換
- 霍夫圓環變換
霍夫變換
- 霍夫(Hough)變換在檢測各種形狀的技術中非常流行,如果要檢測的形狀可以用數學表達式描述,就可以是使用霍夫變換檢測它。即使要檢測的形狀存在一點破壞或者扭曲也是可以使用。
霍夫直線變換
- Hough直線變換,可以檢測一張圖像中的直線
- cv2.HoughLines(image, rho, theta, threshold)
- return:返回值就是( ρ, θ)。 ρ 的單位是像素, θ 的單位是弧度。
- image:是一個二值化圖像,所以在進行霍夫變換之前要首先進行二值化,或者進行Canny 邊緣檢測。
- rho:代表 ρ 的精確度。
- theta:代表θ 的精確度。
- threshold:閾值,只有累加其中的值高于閾值時才被認為是一條直線,也可以把它看成能檢測到的直線的最短長度(以像素點為單位)。
- cv2.HoughLinesP(image: Mat, rho, theta, threshold, lines=…, minLineLength=…, maxLineGap=…)
- return :返回值就是直線的起點和終點(x1,y1,x2,y2)。
- rho:代表 ρ 的精確度。
- theta:代表θ 的精確度。
- threshold:閾值,只有累加其中的值高于閾值時才被認為是一條直線,也可以把它看成能檢測到的直線的最短長度(以像素點為單位)。
- minLineLength:直線的最短長度。比這個短的線都會被忽略。
- maxLineGap- 兩條線段之間的最大間隔,如果小于此值,這兩條直線就被看成是一條直線。
- 一條直線可以用數學表達式 y = mx + c 或者 ρ = x cos θ + y sin θ 表示。ρ 是從原點到直線的垂直距離, θ 是直線的垂線與橫軸順時針方向的夾角(如果使用的坐標系不同,方向也可能不同,這里是按 OpenCV 使用的坐標系描述的)。如下圖所示:
import numpy as np
import cv2
from matplotlib import pyplot as pltimg = cv2.imread('./resource/opencv/image/sudoku.png', cv2.IMREAD_COLOR)
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
edges = cv2.Canny(gray, 50, 150, apertureSize=3)lines = cv2.HoughLines(edges, 1, np.pi/180, 200)for i in range(len(lines)):
# for rho, thetha in lines[10]:rho = lines[i][0][0]thetha = lines[i][0][1]a = np.cos(thetha)b = np.sin(thetha)x0 = a*rhoy0 = b*rholine_length = 1000 # 線長x1 = int(x0 + line_length*(-b))y1 = int(y0 + line_length*(a))x2 = int(x0 - line_length*(-b))y2 = int(y0 - line_length*(a))cv2.line(img, (x1, y1), (x2, y2), (0, 255, 0), 1)# 因為gray和edges都是單通道的,為了可以和原圖拼接合并,需要merge成3通道圖像數據
gray = cv2.merge((gray, gray, gray))
edges = cv2.merge((edges,edges,edges))# 圖像拼接
res = np.hstack((gray,edges,img))cv2.imshow('res', res)
cv2.waitKey(0)
cv2.destroyAllWindows()
漸進概率式霍夫變換
cv2.HoughLinesP(image: Mat, rho, theta, threshold, lines=…, minLineLength=…, maxLineGap=…)
import numpy as np
import cv2
from matplotlib import pyplot as pltimg = cv2.imread('./resource/opencv/image/sudoku.png', cv2.IMREAD_COLOR)
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
canny = cv2.Canny(gray, 50, 150, apertureSize=3)minLineLength = 100
maxLineGap = 10
# HoughLinesP(image: Mat, rho, theta, threshold, lines=..., minLineLength=..., maxLineGap=...)
lines = cv2.HoughLinesP(canny, 1, np.pi/180, 100, minLineLength, maxLineGap)print(lines.shape)
print(lines[0])for i in range(len(lines)):for x1,y1,x2,y2 in lines[i]:cv2.line(img, (x1, y1), (x2, y2), (0, 255, 0), 1)gray = cv2.merge((gray, gray, gray))
canny = cv2.merge((canny,canny,canny))res = np.hstack((gray, canny, img))
cv2.imshow('res', res)
cv2.waitKey(0)
cv2.destroyAllWindows()
在含有坐標點集合中尋找是否存在直線:
cv2.HoughLinesPointSet(_point, lines_max, threshold, min_rho, max_rho, rho_step, min_theta, max_theta, theta_step, _lines=…)
- _point:輸入點的集合,必須是平面內的2D坐標,數據類型必須是CV_32FC2或CV_32SC2。
- lines_max:檢測直線的最大數目。
- threshold:累加器的閾值,即參數空間中離散化后每個方格被通過的累計次數大于閾值時則被識別為直線,否則不被識別為直線。
- min_rho:檢測直線長度的最小距離,以像素為單位。
- max_rho:檢測直線長度的最大距離,以像素為單位。
- rho_step::以像素為單位的距離分辨率,即距離 離散化時的單位長度。
- min_theta:檢測直線的最小角度值,以弧度為單位。
- max_theta:檢測直線的最大角度值,以弧度為單位。
- theta_step:以弧度為單位的角度分辨率,即夾角 離散化時的單位角度。
- _lines:在輸入點集合中可能存在的直線,每一條直線都具有三個參數,分別是權重、直線距離坐標原點的距離 和坐標原點到直線的垂線與x軸的夾角 。
霍夫圓環變換
- 圓形的數學表達式為 (x ? xcenter)2+(y ? ycenter)2 = r2,其中( xcenter,ycenter)為圓心的坐標, r 為圓的直徑。從這個等式中我們可以看出:一個圓環需要 3個參數來確定。所以進行圓環霍夫變換的累加器必須是 3 維的,這樣的話效率就會很低。所以 OpenCV 用來一個比較巧妙的辦法,霍夫梯度法,它可以使用邊界的梯度信息。
- cv2.HoughCircles(image, method, dp, minDist, circles=…, param1=…, param2=…, minRadius=…, maxRadius=…)
- return:存儲檢測到的圓的輸出矢量。
- image:輸入圖像,數據類型一般用Mat型即可,需要是8位單通道灰度圖像
- method:使用的檢測方法,cv2.HOUGH_GRADIENT,cv2.HOUGH_GRADIENT_ALT。
- dp:double類型的dp,用來檢測圓心的累加器圖像的分辨率于輸入圖像之比的倒數,且此參數允許創建一個比輸入圖像分辨率低的累加器。上述文字不好理解的話,來看例子吧。例如,如果dp= 1時,累加器和輸入圖像具有相同的分辨率。如果dp=2,累加器便有輸入圖像一半那么大的寬度和高度。
- minDist:為霍夫變換檢測到的圓的圓心之間的最小距離。
- circles:可以忽略,存儲檢測到的圓的輸出矢量。
- param1:它是第三個參數method設置的檢測方法的對應的參數。它表示傳遞給canny邊緣檢測算子的高閾值,而低閾值為高閾值的一半。
- param2:也是第三個參數method設置的檢測方法的對應的參數,它表示在檢測階段圓心的累加器閾值。它越小的話,就可以檢測到更多根本不存在的圓,而它越大的話,能通過檢測的圓就更加接近完美的圓形了。
- minRadius:表示圓半徑的最小值。
- maxRadius:表示圓半徑的最大值。
import numpy as np
import cv2img = cv2.imread('./resource/opencv/image/logo/opencv-logo2.png', cv2.IMREAD_GRAYSCALE)
img = cv2.medianBlur(img, 5)
cimg = cv2.cvtColor(img, cv2.COLOR_GRAY2BGR)circles = cv2.HoughCircles(img, cv2.HOUGH_GRADIENT, 1, 20, param1=50, param2=30, minRadius=30, maxRadius=0)print(circles)
circles = np.uint16(circles)
print(circles)for i in circles[0, :]:cv2.circle(cimg, (i[0], i[1]), i[2], (0, 255, 0), 2)cv2.circle(cimg, (i[0], i[1]), 2, (0, 0, 255), 3)cv2.imshow('detected circles', cimg)
cv2.waitKey(0)
cv2.destroyAllWindows()