OpenCV---直线检测

霍夫变换是图像处理中从图像中识别几何形状的基本方法之一,应用很广泛,也有很多改进算法。主要用来从图像中分离出具有某种相同特征的几何形状(如,直线,圆等)。最基本的霍夫变换是从黑白图像中检测直线(线段)。

直线检测

  • 直线的表示方式

对于平面中的一条直线,在笛卡尔坐标系中,常见的有点斜式,两点式两种表示方法。然而在hough变换中,考虑的是另外一种表示方式:使用(r,theta)来表示一条直线。其中r为该直线到原点的距离,theta为该直线的垂线与x轴的夹角。如下图所示。

在这里插入图片描述

也就是霍夫变换中表示一条直线的参数变成了(r,theta)。

  • 如何判断多个点是否在同一直线上

当我们的对象变成点时,我们知道一个点可以发射出无数条直线,根据霍夫变换的直线表达形式,假设这个点为i,则通过这个点的直线我们用(ri,thetai)表示。再假设一个点为j,则通过点j的一系列直线我们用(rj,thetaj)表示。我们知道两点决定一条直线,所以这两个点的直线必定有ri=rj,thetai= thetaj的时候。

那如果是三个点呢,假设第三个点是k,则通过k点的一系列直线为(rk,thetak),如果三点在一条直线上,那必定有某个ri=rj=rk = r,thetai = thetaj= thetak = theta。

在霍夫变换检测直线时我们需要找到这样一样直线,如何找到这条直线呢?

  • 如何检测出直线

假设有N个点,我们要检测其中的直线,也就是我们要找到具体的r和theata。对于上面所说的每个点可以通过无数条直线,这里我们设为n条(通常 n = 180),则我们一起可以找到Nn个(r, theata),对这Nn个(r,theata),我们可以利用统计学,统计到在theta=某个值theta_i时,多个点的r近似相等于r_i。也就是说这多个点都在直线(r_i,theta_i)上。

  • 举例说明

如果空间中有3个点,如何判断这三个点在不在一个直线上,如果在,这条直线是的位置为?

在这里插入图片描述

这个例子中,对于每个点均求过该点的6条直线的(r,theta)坐标,共求了3*6个(r,theta)坐标。可以发现在theta=60时,三个点的r都近似为80.7,由此可判定这三个点都在直线(80.7,60)上。

通过 r-o-theta 坐标系可更直观表示这种关系,如下图:图中三个点的(r,theta)曲线汇集在一起,该交点就是同时经过这三个点的直线。

在这里插入图片描述

在实际的直线检测情况中,如果超过一定数目的点拥有相同的(r,theta)坐标,那么就可以判定此处有一条直线。在r-O-theta 坐标系图中,明显的交汇点就标示一条检测出的直线。
如下图,可以判定出平面上的点共构成了两条直线,即检测出两条直线。

OpenCV的算法中内部实际维护的是一个二维数组,当出现相同的(r,theta)则acc[r][theta]自增一,最后我们通过acc[r][theta]阈值的大小来判断极坐标下的该点对应的是否是一条直线

在这里插入图片描述

上面参考:http://blog.163.com/yuyang_tech/blog/static/21605008320130233343990/

这里插入一个用opencv进行霍夫变化检测直线的例子

http://blog.sina.com.cn/s/blog_60b330b801018md4.html

先进行边缘提取,再设置一些线段的容忍长度,可以检测到直线。

变换图示

霍夫直线检测的两种方法

1.获取灰度图像
2.canny边缘检测
3.获取霍夫直线信息
4.算出直线位置,画出每条直线

一:HoughLines霍夫变换

def line_detection(image):
    gray = cv.cvtColor(image,cv.COLOR_BGR2GRAY)
    edges = cv.Canny(gray,50,150,apertureSize=3)    #apertureSize是sobel算子大小,只能为1,3,5,7
    lines = cv.HoughLines(edges,1,np.pi/180,200)  #函数将通过步长为1的半径和步长为π/180的角来搜索所有可能的直线
    for line in lines:
        rho,theta = line[0]  #获取极值ρ长度和θ角度
        a = np.cos(theta)  #获取角度cos值
        b = np.sin(theta)  #获取角度sin值
        x0 = a * rho  #获取x轴值
        y0 = b * rho  #获取y轴值  x0和y0是直线的中点
        x1 = int(x0 + 1000*(-b))  #获取这条直线最大值点x1
        y1 = int(y0 + 1000*(a))   #获取这条直线最大值点y1
        x2 = int(x0 - 1000 * (-b))  #获取这条直线最小值点x2  
        y2 = int(y0 - 1000 * (a))  #获取这条直线最小值点y2  其中*1000是内部规则
        cv.line(image,(x1,y1),(x2,y2),(0,0,255),2)  #开始划线
    cv.imshow("image line",image)



src = cv.imread("./l.png")  #读取图片
cv.namedWindow("input image",cv.WINDOW_AUTOSIZE)    #创建GUI窗口,形式为自适应
cv.imshow("input image",src)    #通过名字将图像和窗口联系

line_detect_possible_demo(src)

cv.waitKey(0)   #等待用户操作,里面等待参数是毫秒,我们填写0,代表是永远,等待用户操作
cv.destroyAllWindows()  #销毁所有窗口

相关知识补充

(一)求取直线的最大值和最小值

pt1.x = cvRound(x0 + 1000*(-b));这段代码。一开始可能不是很好理解。查阅了资料和原理,现在写下来分享。

这里的pt1和pt2是一条直线的两个端点,这里已知下图中的rh0 和θ,现在只需要求图中“任意”两点, 使用OpenCV的cvLine函数画出pt1点到pt2的直线 。

看看下图 就明白这里1000什么的是为什么了。

0_1325123993yZ4k.gif

这里是取了点(x0,y0)在直线上上下1000的距离,那么用cvLine画出来的线段就是从pt1 -> pt2的了。那么pt1->pt2的直线距离就是2000。可以取其他的距离,不一定去1000,如600也可以,具体的数字可以自己定义。

1.这个地方也许会出现检测出来的线段长度比pt1->pt2还大,即包含了我们画的线段 , 这是肯定的。

2. 还会出现本来线段没有pt1->pt2这么长,那么我们画的就会过长了。 也是肯定会出现的情况。

因为:CV_HOUGH_STANDARD方法 只能得出rh0 和 θ的值。 这两个值只能确定直线,而不能确定线段是从哪开始到哪结束。

此方法标准型的霍夫变换检测只能确定线段对应的直线。如果你想得到一条直线的两个端点的具体坐标,可以使用CV_HOUGH_PROBABILISTIC(概率型霍夫变换).


(二)HoughLines方法

def HoughLines(image, rho, theta, threshold, lines=None, srn=None, stn=None, min_theta=None, max_theta=None): # real signature unknown; restored from __doc__
cv.HoughLines(edges,1,np.pi/180,200)
cv2.HoughLines函数输出的是[float, float]形式的ndarray,其中每个值表示检测到的线(ρ , θ)中浮点点值的参数。
第一个参数image:是canny边缘检测后的图像

第二个参数rho和第三个参数theta:对应直线搜索的步长。在本例中,函数将通过步长为1的半径和步长为π/180的角来搜索所有可能的直线。

最后一个参数threshold:是经过某一点曲线的数量的阈值,超过这个阈值,就表示这个交点所代表的参数对(rho, theta)在原图像中为一条直线
观察前面的例子得到的结果图片,其中Hough变换看起来就像在图像中查找对齐的边界像素点集合。
但这样会在一些情况下导致虚假检测,如像素偶然对齐或多条直线穿过同样的对齐像素造成的多重检测。

二:HoughLinesP概率霍夫变换(是加强版)使用简单,效果更好,检测图像中分段的直线(而不是贯穿整个图像的直线)

def line_detect_possible_demo(image):
    gray = cv.cvtColor(image, cv.COLOR_BGR2GRAY)
    edges = cv.Canny(gray, 50, 150, apertureSize=3)  # apertureSize是sobel算子大小,只能为1,3,5,7
    lines = cv.HoughLinesP(edges, 1, np.pi / 180, 100, minLineLength=50,maxLineGap=10)  #函数将通过步长为1的半径和步长为π/180的角来搜索所有可能的直线
    for line in lines:
        print(type(line))   #多维数组
        x1,y1,x2,y2 = line[0]
        cv.line(image,(x1,y1),(x2,y2),(0,0,255),2)
    cv.imshow("line_detect_possible_demo",image)

相关知识补充:

(一)HoughLinesP方法

def HoughLinesP(image, rho, theta, threshold, lines=None, minLineLength=None, maxLineGap=None): # real signature unknown; restored from __doc__
cv.HoughLinesP(edges, 1, np.pi / 180, 100, minLineLength=50,maxLineGap=10)

第一个参数是需要处理的原图像,该图像必须为cannay边缘检测后的图像;

第二和第三参数:步长为1的半径和步长为π/180的角来搜索所有可能的直线

第四个参数是阈值,概念同霍夫变换

第五个参数:minLineLength-线的最短长度,比这个线短的都会被忽略。

第六个参数:maxLineGap-两条线之间的最大间隔,如果小于此值,这两条线就会被看成一条线。

这个函数的返回值就是直线的起点和终点。
  • 1
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值