7.5 直线检测算法
7.5 直线检测算法
在解决机器视觉方面的任务时,经常会涉及某些简单的直线、圆形、椭圆形的检测。在多数情况下,边缘检测算法会先做图片预处理,将原始图片变成只含有边缘的图片。因为图片不完美或边缘检测不完美,会导致一些像素缺漏,或是有噪声使得边缘检测算法所得的边界偏离了实际的边界。霍夫变换通过投票步骤,比较好地解决了上述问题。
以霍夫变换检测直线为例,在典型的直角坐标系中通过水平的x轴和垂直的y轴的坐标来描述点,直线方程可以表示为:
y=kx+b
其中k称为斜率,b称为截距或者偏移,通过k和b可以唯一确定一条直线,或者说通过(k,b)可以唯一确定一条直线。
但是当直线平行y轴时,斜率不存在,这个时候通过k和b就无法表示该直线了,我们需要另外一个方式描述一条直线,如图7-22所示。
图7-22 通过r和描述直线
假设原点到直线做一条垂线段,垂线段的长度为原点到该直线的距离记为r,角度记为θ,通过(r,θ)可以唯一确定一条直线,并且存在以下关系:
r=x cosθ+y sinθ
在极坐标中,r为纵轴,θ为横轴,该直线可以表示为其中的一个点(θ,r)。我们把r为纵轴,θ为横轴的空间称为霍夫空间,也就是说可以把原有的图像空间中的一条直线转换成霍夫空间的一个点。
在图像空间中,通过某一点(a,b)的直线有无数条,对应到霍夫空间,满足如下关系:
r=a cosθ+b sinθ
由平面几何知识可知,点(θ,r)组成了一条曲线。假设a和b均为1,θ取值为0到π,那么:
r=cosθ+sinθ
如图7-23所示,在霍夫空间画出该曲线。换言之,原有的图像空间中通过某一个点的全部直线转换成了霍夫空间的一条曲线。
import numpy as np
import matplotlib.pyplot as plt
o=np.arange(0,np.pi,0.01)
r=np.cos()+np.sin()
plt.xlabel('')
plt.ylabel("r")
plt.plot(,r)
plt.show()
图7-23 霍夫空间中r和的关系
回到直线检测的问题,在图像空间中判断若干点是否在同一直线的问题,可以转换成霍夫空间中对应的多条曲线是否能相交于一点的问题。
假设图像平面中存在三个点(3,5),(6,8)和(7,9),我们想判断这三点是否在一条直线上,那么可以转换到霍夫空间,如图7-24所示,转换成霍夫空间平面中三条曲线是否相交于一点的问题。我们把边缘检测算法识别出的边缘所在点转换到霍夫空间,并在霍夫空间中对应的点进行加1操作,即给该点投票,当有一个点超过设定阈值票数时,我们就可以认定其为直线。这里需要指出的是,为了计算方便,霍夫空间中的θ取固定间隔的几个离散值。
=np.arange(0,np.pi,0.01)
r1=3*np.cos()+5*np.sin()
r2=6*np.cos()+8*np.sin()
r3=7*np.cos()+9*np.sin()
plt.xlabel('')
plt.ylabel("r")
plt.plot(,r1)
plt.plot(,r2)
plt.plot(,r3)
plt.show()
图7-24 在图像空间中判断若干点是否在同一直线的问题
基于霍夫变换的直线检测在opencv中的函数定义如下:
houghlines(image, rho, theta, threshold[, lines[, srn[, stn]]])
其中主要的参数如下。
? rh:以像素为单位的累加器的距离分辨率,通常取1。
? theta:在弧度内的蓄能器的角度分辨率,通常取np.pi/180。
? threshold:当累加器超过该阈值才看作是直线。
下面我们演示如何检测跑道,相应的示例代码位于:
https://github.com/duoergun0729/adversarial_examples/blob/master/code/10-case2.ipynb
首先我们加载测试图片并转换成灰度图像,便于canny进行边缘检测,同时构造一个纯白的背景便于显示检测出的直线。
img=cv2.imread("../picture/road.jpg")
gray_img=cv2.cvtcolor(img, cv2.color_rgb2gray)
show_img=gray_img.copy()
#canny边缘检测
edges = cv2.canny(gray_img, 150, 300)
#对二值图像进行反转,黑白颠倒
notedges=cv2.bitwise_not(edges)
#构造纯白背景
clean_img=np.ones_like(show_img)
然后运行直线检测算法,其中为了限制检测的直线的长度,设置阈值为100。如图7-25所示,一共有四个子图,子图a是原始图像,子图b是使用了canny算法进行了边缘检测,子图c是识别出的直线,子图d是在原始图像上画上了识别出的直线。
#检测直线
lines = cv2.houghlines(edges, 1, np.pi/180, 100)
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))
cv2.line(show_img, (x1, y1), (x2, y2), (0, 0, 0), 2)
cv2.line(clean_img, (x1, y1), (x2, y2), (0, 0, 0), 2)
图7-25 直线检测示例
在解决机器视觉方面的任务时,经常会涉及某些简单的直线、圆形、椭圆形的检测。在多数情况下,边缘检测算法会先做图片预处理,将原始图片变成只含有边缘的图片。因为图片不完美或边缘检测不完美,会导致一些像素缺漏,或是有噪声使得边缘检测算法所得的边界偏离了实际的边界。霍夫变换通过投票步骤,比较好地解决了上述问题。
以霍夫变换检测直线为例,在典型的直角坐标系中通过水平的x轴和垂直的y轴的坐标来描述点,直线方程可以表示为:
y=kx+b
其中k称为斜率,b称为截距或者偏移,通过k和b可以唯一确定一条直线,或者说通过(k,b)可以唯一确定一条直线。
但是当直线平行y轴时,斜率不存在,这个时候通过k和b就无法表示该直线了,我们需要另外一个方式描述一条直线,如图7-22所示。
图7-22 通过r和描述直线
假设原点到直线做一条垂线段,垂线段的长度为原点到该直线的距离记为r,角度记为θ,通过(r,θ)可以唯一确定一条直线,并且存在以下关系:
r=x cosθ+y sinθ
在极坐标中,r为纵轴,θ为横轴,该直线可以表示为其中的一个点(θ,r)。我们把r为纵轴,θ为横轴的空间称为霍夫空间,也就是说可以把原有的图像空间中的一条直线转换成霍夫空间的一个点。
在图像空间中,通过某一点(a,b)的直线有无数条,对应到霍夫空间,满足如下关系:
r=a cosθ+b sinθ
由平面几何知识可知,点(θ,r)组成了一条曲线。假设a和b均为1,θ取值为0到π,那么:
r=cosθ+sinθ
如图7-23所示,在霍夫空间画出该曲线。换言之,原有的图像空间中通过某一个点的全部直线转换成了霍夫空间的一条曲线。
import numpy as np
import matplotlib.pyplot as plt
o=np.arange(0,np.pi,0.01)
r=np.cos()+np.sin()
plt.xlabel('')
plt.ylabel("r")
plt.plot(,r)
plt.show()
图7-23 霍夫空间中r和的关系
回到直线检测的问题,在图像空间中判断若干点是否在同一直线的问题,可以转换成霍夫空间中对应的多条曲线是否能相交于一点的问题。
假设图像平面中存在三个点(3,5),(6,8)和(7,9),我们想判断这三点是否在一条直线上,那么可以转换到霍夫空间,如图7-24所示,转换成霍夫空间平面中三条曲线是否相交于一点的问题。我们把边缘检测算法识别出的边缘所在点转换到霍夫空间,并在霍夫空间中对应的点进行加1操作,即给该点投票,当有一个点超过设定阈值票数时,我们就可以认定其为直线。这里需要指出的是,为了计算方便,霍夫空间中的θ取固定间隔的几个离散值。
=np.arange(0,np.pi,0.01)
r1=3*np.cos()+5*np.sin()
r2=6*np.cos()+8*np.sin()
r3=7*np.cos()+9*np.sin()
plt.xlabel('')
plt.ylabel("r")
plt.plot(,r1)
plt.plot(,r2)
plt.plot(,r3)
plt.show()
图7-24 在图像空间中判断若干点是否在同一直线的问题
基于霍夫变换的直线检测在opencv中的函数定义如下:
houghlines(image, rho, theta, threshold[, lines[, srn[, stn]]])
其中主要的参数如下。
? rh:以像素为单位的累加器的距离分辨率,通常取1。
? theta:在弧度内的蓄能器的角度分辨率,通常取np.pi/180。
? threshold:当累加器超过该阈值才看作是直线。
下面我们演示如何检测跑道,相应的示例代码位于:
https://github.com/duoergun0729/adversarial_examples/blob/master/code/10-case2.ipynb
首先我们加载测试图片并转换成灰度图像,便于canny进行边缘检测,同时构造一个纯白的背景便于显示检测出的直线。
img=cv2.imread("../picture/road.jpg")
gray_img=cv2.cvtcolor(img, cv2.color_rgb2gray)
show_img=gray_img.copy()
#canny边缘检测
edges = cv2.canny(gray_img, 150, 300)
#对二值图像进行反转,黑白颠倒
notedges=cv2.bitwise_not(edges)
#构造纯白背景
clean_img=np.ones_like(show_img)
然后运行直线检测算法,其中为了限制检测的直线的长度,设置阈值为100。如图7-25所示,一共有四个子图,子图a是原始图像,子图b是使用了canny算法进行了边缘检测,子图c是识别出的直线,子图d是在原始图像上画上了识别出的直线。
#检测直线
lines = cv2.houghlines(edges, 1, np.pi/180, 100)
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))
cv2.line(show_img, (x1, y1), (x2, y2), (0, 0, 0), 2)
cv2.line(clean_img, (x1, y1), (x2, y2), (0, 0, 0), 2)
图7-25 直线检测示例