OpenCV 4.11.0
开源计算机视觉库
加载中…
搜索中…
无匹配项
霍夫线变换

上一教程: Canny 边缘检测器
下一教程: 霍夫圆变换

原作者Ana Huamán
兼容性OpenCV >= 3.0

目标

在本教程中,您将学习如何

  • 使用 OpenCV 函数 HoughLines()HoughLinesP() 检测图像中的线条。

理论

注意
以下解释来自 Bradski 和 Kaehler 编著的 Learning OpenCV 一书。

霍夫线变换

  1. 霍夫线变换是一种用于检测直线的变换。
  2. 要应用此变换,首先需要进行边缘检测预处理。

工作原理?

  1. 如您所知,图像空间中的直线可以用两个变量表示。例如
    1. 笛卡尔坐标系中:参数:\((m,b)\)。
    2. 极坐标系中:参数:\((r,\theta)\)

对于霍夫变换,我们将用极坐标系表示直线。因此,直线方程可以写成

\[y = \left ( -\dfrac{\cos \theta}{\sin \theta} \right ) x + \left ( \dfrac{r}{\sin \theta} \right )\]

整理后:\(r = x \cos \theta + y \sin \theta\)

  1. 通常,对于每个点\((x_{0}, y_{0})\),我们可以定义通过该点的所有直线族为

    \[r_{\theta} = x_{0} \cdot \cos \theta + y_{0} \cdot \sin \theta\]

    这意味着每个\((r_{\theta},\theta)\)对都表示通过\((x_{0}, y_{0})\)的每一条线。

  2. 如果对于给定的\((x_{0}, y_{0})\),我们绘制通过它的所有直线族,我们将得到一个正弦曲线。例如,对于\(x_{0} = 8\)和\(y_{0} = 6\),我们得到以下曲线图(在\(\theta\) - \(r\)平面中)

我们只考虑\(r > 0\)且\(0< \theta < 2 \pi\)的点。

  1. 我们可以对图像中的所有点执行上述相同操作。如果两个不同点的曲线在\(\theta\) - \(r\)平面上相交,则意味着这两个点属于同一条直线。例如,继续上面的例子,再绘制两个点的曲线图:\(x_{1} = 4\),\(y_{1} = 9\)和\(x_{2} = 12\),\(y_{2} = 3\),我们得到

这三条曲线在一个点\((0.925, 9.6)\)相交,这些坐标是直线的参数(\(\theta, r\)),\((x_{0}, y_{0})\),\((x_{1}, y_{1})\)和\((x_{2}, y_{2})\)都位于该直线上。

  1. 以上内容意味着什么?这意味着通常可以通过查找曲线之间交点的数量来检测一条直线。交点越多,表示该交点所代表的直线上的点越多。通常,我们可以定义一个阈值,即检测直线所需的最小交点数。
  2. 这就是霍夫线变换所做的。它跟踪图像中每个点曲线的交点。如果交点数超过某个阈值,则将其声明为一条参数为\((\theta, r_{\theta})\)的直线。

标准霍夫线变换和概率霍夫线变换

OpenCV 实现三种霍夫线变换

a. 标准霍夫变换

  • 它几乎就是我们在上一节中解释的内容。它给出的结果是\(( \theta, r_{\theta})\)对的向量。
  • 在 OpenCV 中,它由函数 HoughLines() 实现。

b. 概率霍夫线变换

  • 霍夫线变换的一种更高效的实现。它输出检测到的直线的端点\((x_{0}, y_{0}, x_{1}, y_{1})\)。
  • 在 OpenCV 中,它由函数 HoughLinesP() 实现。

c. 加权霍夫变换

  • 在标准霍夫变换中使用边缘强度而不是二进制 0 或 1 值。
  • 在 OpenCV 中,它由使用 use_edgeval=true 的 HoughLines() 函数实现。
  • 参见 samples/cpp/tutorial_code/ImgTrans/HoughLines_Demo.cpp 中的示例。

本程序的功能?

  • 加载图像
  • 应用标准霍夫线变换概率霍夫线变换
  • 在三个窗口中显示原始图像和检测到的线条。

代码

解释

加载图像

使用Canny检测器检测图像边缘

现在我们将应用霍夫线变换。我们将解释如何使用为此目的提供的两个OpenCV函数。

标准霍夫线变换

首先,应用变换

  • 参数如下:
    • dst: 边缘检测器的输出。它应该是一个灰度图像(尽管实际上它是二值图像)
    • lines: 一个向量,将存储检测到的线的参数\((r,\theta)\)
    • rho : 参数\(r\)的分辨率(以像素为单位)。我们使用1像素。
    • theta: 参数\(\theta\)的分辨率(以弧度为单位)。我们使用1度 (CV_PI/180)
    • threshold: 检测一条线的最小交叉点数
    • srnstn: 默认参数为零。查看OpenCV参考以了解更多信息。

然后通过绘制线条来显示结果。

概率霍夫线变换

首先应用变换

  • 参数如下:
    • dst: 边缘检测器的输出。它应该是一个灰度图像(尽管实际上它是二值图像)
    • lines: 一个向量,将存储检测到的线的参数\((x_{start}, y_{start}, x_{end}, y_{end})\)
    • rho : 参数\(r\)的分辨率(以像素为单位)。我们使用1像素。
    • theta: 参数\(\theta\)的分辨率(以弧度为单位)。我们使用1度 (CV_PI/180)
    • threshold: 检测一条线的最小交叉点数
    • minLineLength: 可以构成一条线的最小点数。点数少于此值的线将被忽略。
    • maxLineGap: 被认为在同一条线上的两点之间的最大间隙。

然后通过绘制线条来显示结果。

显示原始图像和检测到的线条

等待用户退出程序

结果

注意
以下结果使用我们在“代码”部分提到的稍微高级一点的版本获得。它仍然实现与上面相同的操作,只是添加了用于阈值的Trackbar。

使用诸如数独图像之类的输入图像。使用标准霍夫线变换,我们得到以下结果:

使用概率霍夫线变换:

您可能会注意到,当您更改 *阈值* 时,检测到的线条数量会有所变化。解释很明显:如果您设置更高的阈值,则检测到的线条会更少(因为您需要更多点才能声明检测到一条线)。