【翻译:OpenCV-Python教程】霍夫线性变换

⚠️由于自己的拖延症,3.4.3翻到一半,OpenCV发布了4.0.0了正式版,所以接下来是按照4.0.0翻译的。

⚠️除了版本之外,其他还是照旧,Hough Line Transform,原文

目标

在本章,

  • 我们将会理解霍夫变换的概念。
  • 我们将看到如何使用它来检测图像中的线条。
  • 我们将看到以下函数:cv.HoughLines()cv.HoughLinesP()

理论

霍夫变换是一种可以检测任何形状的流行技术,如果你能用数学形式来表示这种形状的话。它可以检测形状,即使形状被破坏破或扭曲了一点。我们来看看它是如何作用于直线的。

一条直线可以被表示成 y=mx+c 或者是参量形式(我们更熟悉的叫法是"极坐标公式") ρ=xcosθ+ysinθ 其中 ρ 是从原点到直线的垂直距离,θ 是这条垂线与水平轴逆时针方向构成的角度(这个方向取决于你如何定义坐标系。现在提到的这种表示方案是在OpenCV中使用的方案)。看下面的示意图:

所以如果这条线经过原点以下,它会有一个小于180度的正角度。如果它在原点上方,我们不是取一个大于180的角,而是取一个小于180的负角度。任何垂直线都是0度,水平线是90度。

现在我们来看看霍夫变换是怎么作用于直线的。任何线都可以被表示成这两项,(ρ,θ)。所以首先它创建了一个二维数组,或者是累加器(来保存这两个参数的值)然后他设置0作为初始值。 令(二维数组的)行表示 ρ (译者注:极坐标更常用的符号是r。),令列表示 θ。数组的大小取决于你需要的精准度。假设说你想要角度的精度是精确到1度,那你就需要180列。而对于ρ来说,最大可能的距离是图像的对角线长度。因此,要精确到一个像素的程度,行数应该是图像的对角长度。

想象有一张 100x100 的图像,它上面有一条水平的线条在图像正中间。取这条线的第一个点,你知道它的坐标 (x,y) (译者注(0, 50))值。现在,按照这条直线的等式,把值 θ=0,1,2,....,180 带入,并且查看你获取到的 ρ 值。对每一对 (ρ,θ),我们都把它们在累加器中对应的值计数增加1。而此时,在这个计数器中,单元 (50,90) 和其他单元一样计数等于1。

现在去这条线上的第二个点,重复上面的步骤。增加单元中你拿到的对应的值(rho, theta)。这一次,单元(50,90)的计数增到到了2。你实际上做的是投票投出 (ρ,θ) 值。你不断为这条直线上所有的点继续这个过程。在每一个点,单元(50,90)都会得票,而其他的单元有可能会得票,也有可能不会。按这个方案,最终单元 (50,90) 会获得最高的投票(译者注:在100X100的图像正中水平的直线到原点距离为50,垂角90度)。所以最终搜索我们的累加器来找最高得票的单元,我们就会取到 (50,90),这就说明图像中有一条线距离原点垂距50,它过原点的垂线和水平线夹角为90度。这个过程很好的显示在了以下的动画中。(感谢提供动画:Amos Storkey )

houghlinesdemo.gif

(译者注:该动画的意思是,遍历所有的白点,在每个点都旋转一周,其中每增加θ值一次,计算出ρ,然后就在右侧对应的(ρ,θ)单元上刷上颜色,最终从右上至坐下的某条直线因为穿越了最多的白点,因此被刷的颜色数最多被刷成了黄色。)

这就是霍夫变化针对直线工作的原理。非常简单,也许你可以用Numpy自己来实现它。以下是一张显示了累加器的图像。在某些地方的亮点说明它们是图像中可能线条的参数。 (感谢提供图像:Wikipedia )

houghlines2.jpg

(译者注:该图右侧横纵坐标分别表示r和θ,而点的亮度则表示这个(r,θ)单元获取到的投票数。)

OpenCV里的霍夫变换

上面解释的这一堆东西,在OpenCV里都封装起来成为 cv.HoughLines() 函数了。它简单的返回了一个:math:(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)

看下面的结果:

houghlines3.jpg

概率霍夫变换

在霍夫变换中,你可以发现即使是一条仅有两个参数的直线,也需要用到大量的计算。概率霍夫变换是我们已知的,针对霍夫变换的优化方案。它不去取所有的点来列入考虑,取而代之的是取足够完成直线检测的这些点的随机子集。只要我们把阈值下调一点。下图在霍夫空间中比较了霍夫变换与概率霍夫变换。(感谢提供图像:弗兰克贝廷格的主页 )

houghlines4.png

OpenCV 的实现是基于直线鲁棒检测,使用过的是 Matas, J. 和 Galambos, C. 还有 Kittler, J.V. [133] 发明的累计概率霍夫变换。使用的函数是 cv.HoughLinesP()。它比之前介绍的函数多出来两个参数。

  • minLineLength - 最小线长。比这个值小的线条会被丢弃。
  • maxLineGap - 允许线段之间的最大间隙,以便将(在同一条直线上的)线段视为同一条。

最好的是,它直接返回直线的两个端点。在前面的例子中,你只得到直线的参数,你必须找到所有的点。而这个方法,一切都是那么的直接和简单。

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.HoughLinesP(edges,1,np.pi/180,100,minLineLength=100,maxLineGap=10)
for line in lines:
    x1,y1,x2,y2 = line[0]
    cv.line(img,(x1,y1),(x2,y2),(0,255,0),2)
cv.imwrite('houghlines5.jpg',img)

看以下结果:

houghlines5.jpg

额外资源

练习


上篇:【翻译:OpenCV-Python教程】模板匹配

下篇:【翻译:OpenCV-Python教程】霍夫圆变换

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值