霍夫变换是一种可用于隔离图像中特定形状的特征的技术,在给定局部特征(可能有噪声)的情况下,对于计算特征的全局描述(无需事先知道解决方案类别的数量)特别有用。 霍夫变换用于直线检测的动机是,每个输入测量值(例如坐标点)表明其对全局一致解(例如产生该图像点的直线)的贡献。
检测直线原理
在直角坐标系中,直线的表示形式为:
y
=
a
⋅
x
+
b
y=a \cdot x+b
y=a⋅x+b
同样一条直线,在仿射坐标系
(
r
,
θ
)
(r,\theta)
(r,θ)中的表示形式为:
r = x ⋅ cos θ + y ⋅ sin θ r=x \cdot \cos \theta+y \cdot \sin \theta r=x⋅cosθ+y⋅sinθ
- r r r 表示坐标原点与直线的距离
- θ θ θ 表示 r r r 向量与 x x x 坐标轴之间的夹角
- 对于直线上的任意一点 ( x , y ) (x,y) (x,y), r r r 和 θ θ θ 是常数(几何上可证明)
在直角坐标系中绘制过任意一点 ( x 0 , y 0 ) (x_{0},y_{0}) (x0,y0)直线的 ( r , θ ) (r,\theta) (r,θ)曲线,此时 r r r和 θ θ θ不断变化,曲线反映的是描述过任意一点直线的两个特征:原点矩 r r r和夹角 θ θ θ。假设A、B两点在同一直线上,因为在同一直线上的点,不管 x , y x,y x,y 如何变化,其 r r r 和 θ θ θ 均为定值,所以A、B两者的 ( r , θ ) (r,θ) (r,θ)曲线会在某一点上相交,这一点表示所在直线的 r r r和 θ θ θ。
所以,霍夫变换的原理就是检查输入点的 ( r , θ ) (r,θ) (r,θ)曲线,如果他们满足一定的重合阈值条件,则判定他们是同一条直线。
基本霍夫变换-直线实例
OpenCV-Python中使用霍夫变换检测直线的函数是cv2.HoughLines()
,函数原型如下:
lines = cv.HoughLines(image, rho, theta, threshold[, lines[, srn[, stn[, min_theta[, max_theta]]]]])
- image:要检测的二值图
- rho:距离 r r r的精度,值越大,考虑越多的线
- theta:角度 θ θ θ的精度,值越小,考虑越多的线
- threshold:累加数阈值,值越小,考虑越多的线
参数的详细介绍,参考cv2.HoughLines
import cv2
import numpy as np
img = cv2.imread('shapes.jpg')
drawing = np.zeros(img.shape,dtype=np.uint8)
gray = cv2.cvtColor(img,cv2.COLOR_RGB2GRAY)
edges = cv2.Canny(gray,50,150)
lines = cv2.HoughLines(edges,0.8,np.pi/180,90)
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))
cv2.imshow('res',drawing)
cv2.waitKey()
参考链接:http://homepages.inf.ed.ac.uk/rbf/HIPR2/hough.htm
统计概率霍夫变换-直线实例
上面介绍的是最基本的霍夫变换,缺点是计算量较大,而且算出来的结果是 r , θ r,θ r,θ并不直观,改进的方法称为统计概率霍夫变换,在opencv-python中的函数原型是cv.HoughLinesP:
lines = cv.HoughLinesP(image, rho, theta, threshold[, lines[, minLineLength[, maxLineGap]]])
- minLineLength:线段的最小长度
- maxLineGap:线段的最大长度
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)