第十一节 图像处理之霍夫检测直线

时间:2022-12-24 02:30:55
【文件属性】:

文件名称:第十一节 图像处理之霍夫检测直线

文件大小:163KB

文件格式:DOCX

更新时间:2022-12-24 02:30:55

opencv

import cv2 as cv import numpy as np #直线检测 #使用霍夫直线变换做直线检测,前提条件:边缘检测已经完成 #标准霍夫线变换 def line_detection(image): gray = cv.cvtColor(image, cv.COLOR_RGB2GRAY) edges = cv.Canny(gray, 50, 150) #apertureSize参数默认其实就是3 cv.imshow("edges", edges) #cv.HoughLines参数设置:参数1,灰度图像;参数二,以像素为单位的距离精度(一般都是1,进度高,但是速度会慢一点) #参数三,以弧度为单位的角度精度(一般是1rad);参数四,阈值,大于阈值threshold的线段才可以被检测通过并返回到结果中 #该函数返回值为rho与theta lines = cv.HoughLines(edges, 1, np.pi/180, 200) for line in lines: rho, theta = line[0] #line[0]存储的是点到直线的极径和极角,其中极角是弧度表示的。 a = np.cos(theta) #theta是弧度 b = np.sin(theta) x0 = a * rho #代表x = r * cos(theta) y0 = b * rho #代表y = r * sin(theta) x1 = int(x0 + 1000 * (-b)) #计算直线起点横坐标 y1 = int(y0 + 1000 * a) #计算起始起点纵坐标 x2 = int(x0 - 1000 * (-b)) #计算直线终点横坐标 y2 = int(y0 - 1000 * a) #计算直线终点纵坐标 注:这里的数值1000给出了画出的线段长度范围大小,数值越小,画出的线段越短,数值越大,画出的线段越长 cv.line(image, (x1, y1), (x2, y2), (0, 0, 255), 2) #点的坐标必须是元组,不能是列表。 cv.imshow("image-lines", image) #统计概率霍夫线变换 def line_detect_possible_demo(image): gray = cv.cvtColor(image, cv.COLOR_RGB2GRAY) edges = cv.Canny(gray, 50, 150, apertureSize=3) # apertureSize参数默认其实就是3 lines = cv.HoughLinesP(edges, 1, np.pi / 180, 60, minLineLength=60, maxLineGap=5) for line in lines: x1, y1, x2, y2 = line[0] cv.line(image, (x1, y1), (x2, y2), (0, 0, 255), 2) cv.imshow("line_detect_possible_demo",image) src = cv.imread("E:/opencv/picture/track.jpg") print(src.shape) cv.namedWindow('input_image', cv.WINDOW_AUTOSIZE) cv.imshow('input_image', src) line_detection(src) src = cv.imread("E:/opencv/picture/track.jpg") #调用上一个函数后,会把传入的src数组改变,所以调用下一个函数时,要重新读取图片 line_detect_possible_demo(src) cv.waitKey(0) cv.destroyAllWindows() 霍夫检测直线原理: 关于hough变换,核心以及难点就是关于就是有原始空间到参数空间的变换上。以直线检测为例,假设有一条直线L,原点到该直线的垂直距离为p,垂线与x轴夹角为θθ,那么这条直线是唯一的,且直线的方程为 ρ=xcosθ+ysinθρ=xcosθ+ysinθ, 如下图所示: 可以看到的是这条直线在极坐标系下只有一个(ρ,θ)与之对应,随便改变其中一个参数的大小,变换到空间域上的这个直线将会改变。好了,再回来看看这个空间域上的这条直线上的所有点吧,你会发现,这条直线上的所有点都可以是在极坐标为(ρ,θ)所表示的直线上的,为什么说是都可以在,因为其中随便的一个点也可以在其他的(ρ,θ)(ρ,θ)所表示的直线上,就比如上述的(x,y)吧,它可以再很多直线上,准确的说,在经过这个点的直线上,随便画两条如下: 可以看到,光是空间上的一个点在极坐标系下就可能在很多极坐标对所对应的直线上,具体有多少个极坐标对呢?那得看你的θ的步长了,我们可以看到θ无非是从0-360度(0−2π)变化,假设我们每10度一走取一个直线(这个点在这个直线上),那么我们走一圈是不是取了36条直线,也就对应36个极坐标对没错吧,那么这个极坐标对,画在坐标轴上是什么样子的呢?因为θ是从0−2π,并且一个点定了,如果一个θ也定了,你想想它对应的直线的ρ会怎么样,自然也是唯一的。那么这个点在极坐标下对应的(ρ,θ)画出来一个周期可能就是这样的,以θ为x轴的话: ok前面说的是单单这一个点对应的极坐标系下的参数对,那么如果每个点都这么找一圈呢?也就是每个点在参数空间上都对应一系列参数对吧,现在把它们华仔同一个坐标系下会怎么样呢?为了方便,假设在这个直线上取3个点画一下: 那么可以看到,首先对于每一个点,在极坐标下,会存在一个周期的曲线来表示通过这个点,其次,这三个极坐标曲线同时经过一个点,要搞清楚的是,极坐标上每一个点对 (ρ,θ)在空间坐标上都是对应一条直线的。好了,同时经过的这一个点有什么含义呢?它表示在空间坐标系下,有一条直线可以经过点1,经过点2,经过点3,这是什么意思?说明这三个点在一条直线上吧。反过来再来看这个极坐标系下的曲线,那么我们只需要找到交点最多的点,把它返回到空间域就是这个需要找的直线了。为什么是找相交最多的点,因为上面这只是三个点的曲线,当空间上很多点都画出来的时候,那么相交的点可能就不知上述看到的一个点了,可能有多个曲线相交点,但是有一点,势必是一条直线上的所有点汇成的交点是曲线相交次数最多的。 再来分析这个算法。可以看到hough变换就是参数映射变换。对每一个点都进行映射,并且每一个映射还不止一次,(ρ,θ)都是存在步长的,像一个点映射成一个 (ρ,θ),以θ取步长为例,当θ取得步长大的时候,映射的(ρ,θ)对少些,反之则多,但是我们有看到,映射后的点对是需要求交点的,上述画出来的曲线是连续的,然而实际上因为θ步长的存在,他不可能是连续的,像上个图一样,是离散的。那么当θ步长取得比较大的时候,你还想有很多交点是不可能的,因为这个时候是离散的曲线然后再去相交,所以说θ步长不能太大,理论上是越小效果越好,因为越小,越接近于连续曲线,也就越容易相交,但是越小带来的问题就是需要非常多的内存,计算机不会有那么多内存给你的,并且越小,计算量越大,想想一个点就需要映射那么多次,每次映射是需要计算的,耗时的。那么再想想对于一副图像所有点都进行映射,随便假设一副100*100的图像(很小吧),就有10000个点,对每个点假设就映射36组(ρ,θ)参数(此时角度的步长是10度了,10度,已经非常大的一个概念了),那么总共需要映射360000次,在考虑每次映射计算的时间吧。可想而知,hough是多么耗时耗力。所以必须对其进行改进。首先就是对图像进行改进,100*100的图像,10000个点,是不是每个点都要计算?大可不必,我们只需要在开始把图像进行一个轮廓提取,一般使用canny算子就可以,生成黑白二值图像,白的是轮廓,那么在映射的时候,只需要把轮廓上的点进行参数空间变换,为什么提轮廓?想想无论检测图像中存在的直线呀圆呀,它们都是轮廓鲜明的。那么需要变换的点可能就从10000个点降到可能1000个点了,这也就是为什么看到许多hough变换提取形状时为什么要把图像提取轮廓,变成二值图像了。 继续算法,分析这么多,可想而知那么一个hough变换在算法设计上就可以如下步骤: (1)将参数空间(ρ,θ)量化,赋初值一个二维矩阵M, M(ρ,θ)就是一个累加器了。 (2)然后对图像边界上的每一个点进行变换,变换到属于哪一组(ρ,θ),就把该组(ρ,θ)对应的累加器数加1,这里的需要变换的点就是上面说的经过边缘提取以后的图像了。 (3)当所有点处理完成后,就来分析得到的M(ρ,θ),设置一个阈值T,认为当M(ρ,θ)>T,就认为存在一条有意义的直线存在。而对应的M(ρ,θ)就是这组直线的参数,至于T是多少,自己去式,试的比较合适为止。 (4)有了M(ρ,θ)和点(x,y)计算出来这个直线就ok了。 api指令分析: cv2.HoughLines() dst: 输出图像. 它应该是个灰度图 (但事实上是个二值化图) lines: 储存着检测到的直线的参数对 (r,\theta) 的容器 rho : 参数极径 r 以像素值为单位的分辨率. 我们使用 1 像素. theta: 参数极角 \theta 以弧度为单位的分辨率. 我们使用 1度 (即CV_PI/180) threshold: 设置阈值: 一条直线所需最少的的曲线交点 srn and stn: 参数默认为0 首先使用Canny算子获得图像边缘,然后使用Hough变换检测直线。其中HoughLines函数的参数3和4对应直线搜索的步长。 在本例中:函数将通过步长为1的半径和步长为π/180的角来搜索所有可能的直线。最后一个参数是经过某一点曲线的数量的阈值,超过这个阈值,就表示这个交点所代表的参数对(ρ , θ)在原图像中为一条直线。 函数cv2.HoughLinesP()是一种概率直线检测,我们知道,原理上讲hough变换是一个耗时耗力的算法,尤其是每一个点计算,即使经过了canny转换了有的时候点的个数依然是庞大的,这个时候我们采取一种概率挑选机制,不是所有的点都计算,而是随机的选取一些个点来计算,相当于降采样了。这样的话我们的阈值设置上也要降低一些。在参数输入输出上,输入不过多了两个参数:minLineLengh(线的最短长度,比这个短的都被忽略)和MaxLineCap(两条直线之间的最大间隔,小于此值,认为是一条直线)。输出上也变了,不再是直线参数的,这个函数输出的直接就是直线点的坐标位置,这样可以省去一系列for循环中的由参数空间到图像的实际坐标点的转换。 api指令: cv2.HoughLinesP(dst, lines, 1, CV_PI/180, 50, 50, 10 ) dst: 输出图像. 它应该是个灰度图 (但事实上是个二值化图) lines: 储存着检测到的直线的参数对 (x_{start}, y_{start}, x_{end}, y_{end}) 的容器 rho : 参数极径 r 以像素值为单位的分辨率. 我们使用 1 像素. theta: 参数极角 \theta 以弧度为单位的分辨率. 我们使用 1度 (即CV_PI/180) threshold:


网友评论