【Python】OpenCV霍夫变换直线检测
引言
霍夫变换(Hough Transform)是一种常用于图像处理和计算机视觉中的特征提取技术,主要用于检测特定形状,如直线、圆和椭圆等。
本文基于Python语言,记录了本人霍夫变换直线检测的学习步骤。
原理介绍
霍夫变换(Hough Transform)是一种基于参数空间投票的图像处理方法,在直线检测中,霍夫变换的核心思想是将图像中的点从笛卡尔空间映射到霍夫空间,并通过累积投票的方式来找到直线。
1 数据准备
在笛卡尔坐标系内可以用
y
=
k
x
+
b
y = kx + b
y=kx+b表达式来表示一条直线。我们首先绘制一条
k
=
1
k=1
k=1,
b
=
1
b=1
b=1的直线,即
y
=
x
+
1
y = x + 1
y=x+1。
同时,我们在直线上选取三个点
(
x
1
,
y
1
)
(x_1,y_1)
(x1,y1),
(
x
2
,
y
2
)
(x_2,y_2)
(x2,y2),
(
x
3
,
y
3
)
(x_3,y_3)
(x3,y3),三个点的坐标分别为
(
1
,
2
)
(1,2)
(1,2),
(
2
,
3
)
(2,3)
(2,3),
(
3
,
4
)
(3,4)
(3,4)。
下面我们就使用霍夫变换来检测这条直线。

2 空间变换
霍夫变换中最重要的思想就是将笛卡尔空间内的点转到霍夫空间,其中霍夫空间是指的一种参数空间的概念。
针对直线表达式
y
=
k
x
+
b
y = kx + b
y=kx+b,在笛卡尔空间内认为是描述因变量y随着自变量x变化而变化的关系式。
现在我们对直线进行空间变换,将笛卡尔空间转为参数空间。变换的过程就是将原本的x轴替换为k轴,原本的y轴替换为b轴,即在参数空间内,我们希望描述的是因变量b随着自变量k的变化而变化的情况。
下面是进行空间变换的步骤:
- 等式变换
首先,将等式 y = k x + b y = kx + b y=kx+b变换成 b = − x k + y b = -xk + y b=−xk+y,记住,我们现在的因变量是b,自变量是k。 - 带入坐标点
其次,将某个坐标点带入变化后的公式。例如,将点 ( 1 , 2 ) (1,2) (1,2)带入 b = − x k + y b = -xk + y b=−xk+y,得到 b = − k + 2 b = -k + 2 b=−k+2,我们就在参数空间内得到了一条直线表达式。由此可以得出,笛卡尔坐标系内的一个点,在当前的参数空间内表现形式是一条直线。 - 绘制参数空间
现在,我们将 ( 1 , 2 ) (1,2) (1,2), ( 2 , 3 ) (2,3) (2,3), ( 3 , 4 ) (3,4) (3,4)这三个点依次表示在参数空间内,就可以得到下图。

分析上图,
(
1
,
2
)
(1,2)
(1,2),
(
2
,
3
)
(2,3)
(2,3),
(
3
,
4
)
(3,4)
(3,4)这三个点转换后的形成了三条直线,且都经过同一个点
(
1
,
1
)
(1,1)
(1,1),而这个
(
1
,
1
)
(1,1)
(1,1)点恰巧表示的是笛卡尔坐标系内
y
=
x
+
1
y = x + 1
y=x+1这条直线的斜率
k
k
k和截距
b
b
b。
由此可以得出,在k-b参数空间内的一个点代表了笛卡尔空间内的一条线,k-b参数空间内的一条线代表了笛卡尔空间内的一个点。
基于上述理论,如果我们希望找出二值图像中的直线,可以先将图像中的每个点映射到参数空间,然后在参数空间中寻找相交点,若这个相交点经过的直线越多,则在图像中越可能是直线。
3 参数空间优化
但是,在某些情况下k-b参数空间存在弊端,如我在笛卡尔空间内的直线垂直于x轴(例如直线
x
=
2
x = 2
x=2),此时k值无穷大,在k-b参数空间内难以映射。
此使就需要将直角坐标系内的直线转用极坐标表示,将k-b参数空间转为ρ-θ参数空间。
在极坐标系内
ρ
=
c
o
s
θ
∗
x
+
s
i
n
θ
∗
y
ρ = cosθ * x + sinθ * y
ρ=cosθ∗x+sinθ∗y表示一条直线,此时的因变量是ρ,自变量是θ。同理,我们将
(
1
,
2
)
(1,2)
(1,2),
(
2
,
3
)
(2,3)
(2,3),
(
3
,
4
)
(3,4)
(3,4)三个坐标点带入式内,并在ρ-θ参数空间内绘制曲线,它们也表现出经过同一点的特征。

代码实现
接下来我们使用cv2的相关接口来实现图像的直线检测。
运行环境:python=3.10 opencv-python=4.11.0.86
- 边缘检测
霍夫直线检测算法的输入需为一个二值图像,因此首先需要对图像进行预处理。我们采用Canny边缘检测获取图像的边缘信息。
代码如下:
# 读取图片
in_path = './X.png'
img = cv2.imread(in_path)
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
# 边缘检测
edge = cv2.Canny(gray, 50, 150)
# 展示结果
fig, ax = plt.subplots(nrows=1, ncols=2, figsize=(10, 8), dpi=100)
ax[0].imshow(gray, cmap='gray')
ax[0].set_title('Original Image')
ax[1].imshow(edge, cmap='gray')
ax[1].set_title('Canny Result')
plt.show()

- 直线检测
# 霍夫直线检测
lines = cv2.HoughLines(edge, 1, np.pi / 180, 55)
cv2.HoughLines(img, rho, theta, threshold)
rho参数:表示 rho 的分辨率,即直线搜索时的距离精度,单位为像素。rho=1表示以1像素为单位进行搜索。
theta参数:表示 theta 的分辨率,即直线搜索时的角度精度,单位为弧度。np.pi/180 表示以1度为单位进行搜索。
threshold参数:表示在霍夫空间内线相交的次数阈值,大于该阈值的直线会被保留为结果。
在直线检测过程中,内部确实会创建一个以ρ和θ为轴的二维数组,其具体步骤如下
(1)初始化累加器: 先创建一个所有元素都初始化为0的二维数组。数组的大小由ρ的最大值和θ的范围确定。每个单元格代表了在特定的ρ和 θ值下直线出现的“票数”。
(2)对于图像中的每一个非零像素点(即边缘检测后的有效点),算法会遍历所有可能的θ值,根据直线方程
ρ
=
c
o
s
θ
∗
x
+
s
i
n
θ
∗
y
ρ = cosθ * x + sinθ * y
ρ=cosθ∗x+sinθ∗y 计算对应的ρ,这里x和y是该像素点的坐标。
(3)对于计算得到的每一对 (ρ,θ) ,增加相应累加器单元格中的值(即“投票”)。这意味着对于图像中的每个点,它所能构成的所有直线在累加器中对应的 (ρ,θ) 单元格的票数都会增加。
(4) 经过上述步骤后,累加器中票数高于某个阈值的,对应的直线就被认为是图像中实际存在的直线。这些被选中的 (ρ,θ)对就是最终检测到的直线参数。
图像的检测结果如下:

小结
总结来说,霍夫变换直线检测是计算机视觉中的一项基础且有力的技术,适用于多种场景,如道路检测、建筑物轮廓提取等。掌握它不仅能够增强我们对图像处理技术的理解,也能够为解决复杂的视觉识别问题提供支持。希望本教程能帮助你更好地理解霍夫变换直线检测的原理和方法,为你的项目带来价值。
更多推荐



所有评论(0)