OpenCV 4.11.0
开源计算机视觉
|
在本章中,
霍夫变换是一种流行的技术,用于检测任何形状,只要您可以用数学形式表示该形状。即使形状稍微断裂或变形,它也能检测到形状。我们将了解它如何用于检测线条。
一条线可以用\(y = mx+c\)表示,也可以用参数形式表示为\(\rho = x \cos \theta + y \sin \theta\),其中\(\rho\)是原点到直线的垂直距离,\(\theta\)是由这条垂直线和水平轴形成的角度(逆时针方向测量)(方向取决于你如何表示坐标系。OpenCV中使用这种表示方法)。请查看下面的图片
因此,如果直线经过原点下方,则其ρ为正值,角度小于180度。如果它经过原点上方,则角度小于180度,ρ取负值(而不是取大于180度的角度)。任何垂直线都将具有0度角,水平线将具有90度角。
现在让我们看看霍夫变换是如何对线条起作用的。任何直线都可以用这两个参数\(\rho, \theta\)表示。首先,它创建一个二维数组或累加器(用于保存这两个参数的值),并将其初始设置为0。行表示\(\rho\),列表示\(\theta\)。数组的大小取决于所需的精度。假设您希望角度精度为1度,则需要180列。对于\(\rho\),最大可能距离是图像的对角线长度。因此,如果采用一个像素的精度,则行数可以是图像的对角线长度。
考虑一个100x100的图像,其中间有一条水平线。取线上的第一个点。您知道它的(x,y)值。现在在线性方程中,将\(\theta = 0,1,2,....,180\)代入,并检查得到的\(\rho\)值。对于每个\((\rho, \theta)\)对,您都会在累加器中相应的\((\rho, \theta)\)单元中将值加1。因此,现在在累加器中,单元(50,90) = 1,以及其他一些单元。
现在取线上的第二个点。执行与上述相同的操作。将您获得的(rho, theta)
对应单元的值加1。这一次,单元(50,90) = 2。您实际上是在对\((\rho, \theta)\)值进行投票。您对线上的每个点都重复此过程。在每个点上,单元(50,90)都会被递增或投票,而其他单元可能被投票也可能不被投票。这样,最终,单元(50,90)将获得最多的票数。因此,如果您搜索累加器以获得最大票数,则会得到值(50,90),这意味着图像中有一条线,距离原点50个像素,角度为90度。在下面的动画中很好地显示了这一点(图片由Amos Storkey提供)
这就是霍夫变换对线条的工作方式。它很简单,您可以自己使用Numpy实现它。下面是一张显示累加器的图像。某些位置的亮点表示它们是图像中可能线条的参数。(图片由Wikipedia提供)
上面解释的所有内容都封装在OpenCV函数cv.HoughLines()中。它只返回一个:math:`(\rho, \theta)`值的数组。\(\rho\)以像素为单位测量,\(\theta\)以弧度为单位测量。第一个参数,输入图像应该是二值图像,因此在应用霍夫变换之前,应用阈值或使用Canny边缘检测。第二个和第三个参数分别是\(\rho\)和\(\theta\)的精度。第四个参数是阈值,这意味着它应该获得的最小投票数才能被视为一条线。请记住,投票数取决于线上的点数。因此,它表示应检测到的线的最小长度。
import cv2 as cv
import numpy as np
img = cv.imread(cv.samples.findFile('sudoku.png'))
gray = cv.cvtColor(img,cv.COLOR_BGR2GRAY)
edges = cv.Canny(gray,50,150,apertureSize = 3)
lines = cv.HoughLines(edges,1,np.pi/180,200)
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))
cv.line(img,(x1,y1),(x2,y2),(0,0,255),2)
cv.imwrite('houghlines3.jpg',img)
查看下面的结果
在霍夫变换中,即使对于只有两个参数的直线,也可以看到它需要大量的计算。概率霍夫变换是对我们看到的霍夫变换的一种优化。它不会考虑所有点。相反,它只考虑足以进行直线检测的随机点子集。我们只需要降低阈值即可。请参见下图,该图比较了霍夫空间中的霍夫变换和概率霍夫变换。(图片提供:Franck Bettinger的主页)
OpenCV 的实现基于 Matas, J. 和 Galambos, C. 和 Kittler, J.V. 的《使用渐进概率霍夫变换进行鲁棒直线检测》。[185]。使用的函数是cv.HoughLinesP()。它有两个新的参数。
最好的一点是,它直接返回直线的两个端点。在之前的案例中,你只得到了直线的参数,并且必须找到所有点。在这里,一切都直接而简单。
import cv2 as cv
import numpy as np
img = cv.imread(cv.samples.findFile('sudoku.png'))
gray = cv.cvtColor(img,cv.COLOR_BGR2GRAY)
edges = cv.Canny(gray,50,150,apertureSize = 3)
for line in lines
请参见下面的结果