1. 程式人生 > >python OpenCV學習筆記(二十七):Hough線變換

python OpenCV學習筆記(二十七):Hough線變換

Hough變換是一種很流行的技術,可以檢測任何可以用數學的形式來表示的形狀。即使它被破壞或變形,它也能檢測出它的形狀。我們將會看到它是如何工作的。
一條線可以表示成y = mx + c或引數形式,像ρ=xcosθ+ysinθ,其中ρ是從原點到直線的垂直距離,θ角是由這條垂線和水平軸以逆時針的方向形成的(這個方向取決於你如何表示座標系統。這種表示法在OpenCV中使用)。參照下面的圖片:
這裡寫圖片描述

所以如果這條線在原點下面,它就會有一個正的rho和一個小於180度的角。如果它在原點上方,不是取一個大於180度的角,而是取一個小於180度的角,並且rho為負。任何垂直線都是0度,水平線都是90度。

現在讓我們來看看Hough轉換是如何工作的。任何線都可以用這兩個術語表示(ρ,θ)

。因此,首先它建立一個2D陣列或累加器(用來儲存兩個引數的值),然後它被設定為0。讓行表示ρ,列表示θ。陣列的大小取決於您所需要的精度。假設你想讓角的精度是1度,你需要180列。對於ρ,可能的最大距離是影象的對角線長度。因此,取一個畫素的精度,行數可以是影象的對角線長度。

考慮一個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)

這裡寫圖片描述