文章目录
- 1.霍夫线变换
- 目标
- 理论
- OpenCV 中的霍夫变换
- 概率霍夫变换
- 其他资源
- 2.霍夫圆变换
- 目标
- 理论
1.霍夫线变换
目标
在本章中,
- 我们将了解霍夫变换的概念。
- 我们将了解如何使用它来检测图像中的线条。
- 我们将看到以下函数:cv.HoughLines()、cv.HoughLinesP()
理论
如果您可以用数学形式表示形状,霍夫变换是一种检测任何形状的流行技术。即使形状有一点损坏或扭曲,它也可以检测到形状。我们将看到它如何适用于一条线。
一条直线可以表示为 y = m x + c y = mx+c y=mx+c,也可以表示为参数形式,即 ρ = x cos θ + y sin θ \rho = x \cos \theta + y \sin \theta ρ=xcosθ+ysinθ,其中 ρ \rho ρ 是从原点到直线的垂直距离, θ \theta θ 是这条垂直线与水平轴形成的夹角,逆时针测量(该方向取决于您如何表示坐标系。此表示法用于 OpenCV)。查看下图:
因此,如果直线从原点下方经过,则其 rho 为正,角度小于 180。如果直线从原点上方经过,则角度将小于 180,而不是大于 180,并且 rho 为负。任何垂直线的 rho 为 0 度,水平线的 rho 为 90 度。
现在让我们看看霍夫变换如何对直线起作用。任何直线都可以用这两个项表示, ( ρ , θ ) (\rho, \theta) (ρ,θ)。因此,它首先创建一个 2D 数组或累加器(用于保存两个参数的值),并且最初将其设置为 0。让行表示 ρ \rho ρ,让列表示 θ \theta θ。数组的大小取决于您需要的精度。假设您希望角度的精度为 1 度,则需要 180 列。对于 ρ \rho ρ,可能的最大距离是图像的对角线长度。因此,以一个像素为精度,行数可以是图像的对角线长度。
考虑一个 100x100 的图像,中间有一条水平线。取该线的第一个点。您知道它的 (x,y) 值。现在在线方程中,输入值 θ = 0 , 1 , 2 , . . . . , 180 \theta = 0,1,2,....,180 θ=0,1,2,....,180 并检查您得到的 ρ \rho ρ。对于每个 ( ρ , θ ) (\rho, \theta) (ρ,θ) 对,您将累加器中其对应的 ( ρ , θ ) (\rho, \theta) (ρ,θ) 单元中的值增加一。因此现在在累加器中,单元 (50,90) = 1 以及一些其他单元。
现在取线上的第二个点。执行与上述相同的操作。增加单元格中与你得到的 (rho, theta)
对应的值。这次,单元格 (50,90) = 2。你实际上做的是投票
(
ρ
,
θ
)
(\rho, \theta)
(ρ,θ) 值。你对线上的每个点继续此过程。在每个点,单元格 (50,90) 将增加或投票,而其他单元格可能会或可能不会被投票。这样,最后,单元格 (50,90) 将获得最高票数。因此,如果你在累加器中搜索最高票数,你将获得值 (50,90),它表示,此图像中有一条线,距离原点 50 度,角度为 90 度。下面的动画很好地显示了这一点(图片来源:Amos Storkey )
这就是霍夫变换对线的工作原理。它很简单,也许你可以自己用 Numpy 实现它。下面是一张显示累加器的图像。某些位置的亮点表示它们是图像中可能的线的参数。(图片来源:维基百科 )
OpenCV 中的霍夫变换
以上解释的所有内容都封装在 OpenCV 函数 cv.HoughLines() 中。它只是返回一个 :math:(rho,
theta)` 值的数组。
ρ
\rho
ρ 以像素为单位,
θ
\theta
θ 以弧度为单位。第一个参数,输入图像应该是二值图像,因此在应用霍夫变换之前应用阈值或使用 Canny 边缘检测。第二个和第三个参数分别是
ρ
\rho
ρ 和
θ
\theta
θ 精度。第四个参数是阈值,这意味着它应该获得的最小投票数被视为一条线。请记住,投票数取决于线上的点数。因此它表示应该检测到的线的最小长度。
@include hough_line_transform.py
检查以下结果:
概率霍夫变换
在霍夫变换中,你可以看到,即使对于具有两个参数的线,也需要大量的计算。概率霍夫变换是我们看到过的霍夫变换的优化。它不会考虑所有点。相反,它只采用足以进行线检测的随机点子集。我们只需降低阈值即可。参见下图,其中比较了霍夫空间中的霍夫变换和概率霍夫变换。(图片来源:Franck Bettinger 的主页)
OpenCV 实现基于 Matas, J. 和 Galambos, C. 以及 Kittler, J.V. @cite Matas00 的“使用渐进概率霍夫变换进行稳健的直线检测”。使用的函数是 cv.HoughLinesP()。它有两个新参数。
- minLineLength - 直线的最小长度。短于此长度的线段将被拒绝。
- maxLineGap - 允许的线段间最大间隙,以将它们视为一条直线。
最好的是,它直接返回直线的两个端点。在前一种情况下,您只获得直线的参数,并且必须找到所有点。在这里,一切都直接而简单。
@include probabilistic_hough_line_transform.py
查看以下结果:
其他资源
- 维基百科上的霍夫变换
2.霍夫圆变换
目标
在本章中,
- 我们将学习使用霍夫变换在图像中查找圆。
- 我们将看到这些函数:cv.HoughCircles()
理论
圆在数学上表示为 ( x − x c e n t e r ) 2 + ( y − y c e n t e r ) 2 = r 2 (x-x_{center})^2 + (y - y_{center})^2 = r^2 (x−xcenter)2+(y−ycenter)2=r2,其中 ( x c e n t e r , y c e n t e r ) (x_{center},y_{center}) (xcenter,ycenter) 是圆的中心, r r r 是圆的半径。从等式中,我们可以看到我们有 3 个参数,所以我们需要一个用于霍夫变换的 3D 累加器,这将非常低效。因此,OpenCV 使用更棘手的方法,即Hough 梯度法,它使用边缘的梯度信息。
我们在这里使用的函数是cv.HoughCircles()。它有很多参数,文档中对此进行了很好的解释。所以我们直接看代码。
import numpy as np
import cv2 as cv
img = cv.imread('opencv-logo-white.png',0)
img = cv.medianBlur(img,5)
cimg = cv.cvtColor(img,cv.COLOR_GRAY2BGR)
circles = cv.HoughCircles(img,cv.HOUGH_GRADIENT,1,20,
param1=50,param2=30,minRadius=0,maxRadius=0)
circles = np.uint16(np.around(circles))
for i in circles[0,:]:
# draw the outer circle
cv.circle(cimg,(i[0],i[1]),i[2],(0,255,0),2)
# draw the center of the circle
cv.circle(cimg,(i[0],i[1]),2,(0,0,255),3)
cv.imshow('detected circles',cimg)
cv.waitKey(0)
cv.destroyAllWindows()
查看以下结果: