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

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

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

目标

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

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

理论

注意
以下解释来源于Bradski和Kaehler合著的《学习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)\)相交,这些坐标是\((x_{0}, y_{0})\)、\((x_{1}, y_{1})\)和\((x_{2}, y_{2})\)所在的直线的参数(\(\theta, r\))。

  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中,它由函数HoughLines()实现,参数use_edgeval=true。
  • 请参阅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: 同一条线上两个点之间的最大允许间隙。

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

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

等待用户退出程序

结果

注意
以下结果是使用我们在*代码*部分中提到的略微更高级的版本获得的。它仍然实现了与上述相同的功能,只是为阈值添加了轨迹条。

使用例如数独图像作为输入。通过使用标准霍夫线变换,我们得到以下结果:

通过使用概率霍夫线变换:

你可能会观察到,当你改变*阈值*时,检测到的线条数量会发生变化。解释很明显:如果你设置更高的阈值,将检测到更少的线条(因为你需要更多的点才能声明检测到一条线)。