Hough 变换是一个非常有用的技术,可以检测任何形状,只要那个形状可以通过数学方程表示出来,即使检测的形状断裂或者轻微变形。
直线方程为:
y = m x + c y=mx+c y=mx+c
或者极坐标参数表示形式:
ρ = x ∗ cos θ + y ∗ sin θ \rho=x*\cos\theta+y*\sin\theta ρ=x∗cosθ+y∗sinθ
其中, ρ \rho ρ是原点到直线的垂直距离, θ \theta θ是垂直线与逆时针测量的水平轴形成的角度。
任何直线都可以用 ( ρ , θ ) (\rho,\theta) (ρ,θ) 来表示。Hough变换是如何检测直线的呢?首先它创建一个2D数组或累加器,并将其初始设置为 0,设行表示 ρ \rho ρ,列表示 θ \theta θ。数组的大小取决于精度,假设角度的精度为1度,则需要180列。对于 ρ \rho ρ,可能的最大距离是图像的对角线长度。因此,以一个像素的精度计算,行数可以是图像的对角线长度。
假设一个 100 ∗ 100 100*100 100∗100的图像,在图像中间有一个水平线,取这条线的第一个点 ( x , y ) (x,y) (x,y),计算 ( ρ , θ ) (\rho,\theta) (ρ,θ),对于每一个 ( ρ , θ ) (\rho,\theta) (ρ,θ) ,如果 ( x , y ) (x,y) (x,y)能找到对应的 ( ρ , θ ) (\rho,\theta) (ρ,θ),增加递增器的值。循环往复,对图像中所有的点都进行查找搜寻,不停的对每个 ( ρ , θ ) (\rho,\theta) (ρ,θ) 进行投票,假设在 ( 50 , 90 ) (50,90) (50,90)投票最多,请看下面的动画。
在Hough变换中,尽管一条直线只有两个参数,但是还需要很多计算量,概率 Hough 变换是其中的一种优化,它不会将所有的点都考虑进去,相反,它会考虑一些能够充分进行直线检测的随机点集。我们必须降低 threshold,流程图如下:
image
Image Courtesy : Franck Bettinger's home page
OpenCV 的实现基于 Robust Detection of Lines Using the Progressive Probabilistic Hough Transform by Matas, J. and Galambos, C. and Kittler, J.V.
[166]. 函数为 cv2.HoughLinesP() 。
import cv2
import numpy as np
# 读入图像
img = cv2.imread('assets/sudoku.png')
# 灰度化
gray = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)
# 边缘检测
edges = cv2.Canny(gray, threshold1=50, threshold2=150, apertureSize = 3)
# 直线检测
lines = cv2.HoughLines(edges, rho=1, theta=np.pi/180, threshold=150 )
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(img,(x1,y1),(x2,y2),(0,0,255),2)
cv2.imshow("line", img)
cv2.waitKey(0)
cv2.destroyAllWindows()
import cv2
import numpy as np
# 读入图像
img = cv2.imread('assets/sudoku.png')
# 灰度化
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
# 边缘检测
edges = cv2.Canny(gray, threshold1=50, threshold2=150, apertureSize=3)
# 直线检测
lines = cv2.HoughLinesP(edges, rho=1, theta=np.pi/180, threshold=100, minLineLength=100, maxLineGap=10)
for line in lines:
x1, y1, x2, y2 = line[0]
cv2.line(img, (x1, y1), (x2, y2), color=(0, 255, 0), thickness=2)
cv2.imshow("line", img)
cv2.waitKey(0)
cv2.destroyAllWindows()
cv.HoughLines( image, rho, theta, threshold[, lines[, srn[, stn[, min_theta[, max_theta]]]]] ) -> lines
用标准的Hough变换找二值图像中的直线
- image: 8位单通道二值图像,图像可能被篡改
- lines: 输出的直线 vector/list,每个直线是 ( ρ , θ , v o t e s ) (\rho, \theta, votes) (ρ,θ,votes) 表示, ρ \rho ρ是图像原点(左上角)到直线的距离, θ \theta θ是旋转的弧度,(0~vertical_line, pi/2 ~ horizontal line), v o t e s votes votes是投票值。
- rho: 距离精度,单位像素
- theta: 角度精度,单位弧度
- threshold: 累积阈值,主要用于对投票值进行限制,只返 v o t e s votes votes 大于该值的 line
- srn: 多尺度的
Hough
变换,是rho
的除数,如果srn
为0和stn
=0时,则使用经典Hough
变换,否则,这两个参数都应为正值,- stn: 多尺度的
Hough
变换,是theta
的除数,- min_theta: 对于标准和多尺度的
Hough
变换,检测线条的最小角度,必须介于0 ~ max_theta
之间。- max_theta: 对于标准和多尺度的
Hough
变换,检测线条的最大角度,必须介于min_theta ~ CV_PI
之间。
cv.HoughLinesP( image, rho, theta, threshold[, lines[, minLineLength[, maxLineGap]]] ) -> lines
利用概率
Hough
变换在二值图中找直线
- image: 8位单通道二值图像,图像可能被篡改
- lines: 输出的直线 vector/list,每个直线是 ( x 1 , y 1 , x 2 , y 2 ) (x1,y1, x2, y2) (x1,y1,x2,y2), 他们分别是线段的端点
- rho: 距离精度,单位像素
- theta: 角度精度,单位弧度
- threshold: 累积阈值,主要用于对投票值进行限制,只返 v o t e s votes votes 大于该值的 line
- minLineLength: 最小的直线长度,小于该长度的被拒绝掉。
- maxLineGap: 允许同一条直线上连接两点之间最大的间隔。