使用霍夫变换检测车道线

重磅干货,第一时间送达

车道线检测是自动驾驶汽车的重要组成部分之一,有很多方法可以做到这一点。本文,我们将使用最简单的霍夫变换方法。
本文分为三个部分:
  • 第一部分:高斯模糊+ Canny边缘检测

  • 第二部分:霍夫变换

  • 第三部分:优化+显示线条

第1部分和第3部分的重点是编码,第2部分更面向理论。接下来,让我们开始第一部分。
第一部分:高斯模糊+Canny边缘检测
导入必需的库:

    import numpy as np import cv2 import matplotlib.pyplot as plt

    • 第1行:Numpy用于执行数学计算,我们要用它来创建和操作数组。

    • 第3行:使用Matplotlib可视化图像。

    接下来,让我们从集合中加载一张图片来测试算法

      image_path = r"D:\users\new owner\Desktop\TKS\Article Lane Detection\udacity\solidWhiteCurve.jpg" image1 = cv2.imread(image_path) plt.imshow(image1)

      在这里,我们在第4行将图像加载到笔记本中,然后我们将在第5行和第6行读取图像并将其可视化。现在是处理图像的时候了,主要分为以下三步:

        def grey(image): return cv2.cvtColor(image, cv2.COLOR_RGB2GRAY)def gauss(image): return cv2.GaussianBlur(image, (5, 5), 0)def canny(image): edges = cv2.Canny(image,50,150) return edges 

        在最后一个代码块中,我们定义了3个函数:
        Greyscale the image:这有助于增加颜色的对比度,使它更容易识别像素强度的变化。
        Gaussian Filter:高斯滤波器的目的是减少图像中的噪声。我们这样做是因为Canny中的梯度对噪声非常敏感,所以我们想尽可能地消除噪声。cv2.高斯模糊函数有三个参数:
        • img参数定义了我们要进行归一化(减少噪声)的图像。这个函数使用一个称为高斯核的核函数,用于对图像进行归一化。

        • sigma参数定义沿x轴的标准偏差。标准偏差衡量图像中像素的分布,我们希望像素扩散是一致的,因此标准偏差为0。


        Canny:这是我们检测图像边缘的地方,它所做的是计算像素强度的变化(亮度的变化)在一个图像的特定部分。幸运的是,OpenCV使它变得非常简单。
        cv2.Canny函数有3个参数,(img, threshold-1, threshold-2)。
        • img参数定义了我们要检测边缘的图像。

        • threshold-1参数过滤所有低于这个数字的梯度(它们不被认为是边缘)。

        • threshold-2参数决定了边缘的有效值。

        • 如果两个阈值之间的任何梯度连接到另一个高于阈值2的梯度,则将考虑该梯度。

        现在我们已经定义了图像中的所有边缘,我们需要分割与车道线相对应的边缘,操作步骤如下:

          def region(image): height, width = image.shape triangle = np.array([ [(100, height), (475, 325), (width, height)] ]) mask = np.zeros_like(image) mask = cv2.fillPoly(mask, triangle, 255) mask = cv2.bitwise_and(image, mask) return mask

          这个函数将分割图像中车道线所在的某个硬编码区域,它以Canny图像为参数,输出孤立区域。
          在第1行中,我们将使用numpy.shape函数提取图像的维数。
          在第2-4行中,我们要定义一个三角形的尺寸,也就是我们要隔离的区域。
          在第5和第6中,我们要创建一个黑色的平面,然后我们要定义一个白色的三角形,它的尺寸和第2行中定义的一样。
          在第7行中,我们将执行位运算和运算,使我们能够隔离与车道线对应的边缘。
          更深入的解释位运算
          在我们的图像中,有两种像素强度:黑色和白色。黑色像素的值为0,白色像素的值为255。在8位二进制中,0转换为00000000,255转换为11111111。对于位运算和运算,我们将使用像素的二进制值。现在,我们将在img1和img2相同的位置上乘以两个像素(我们将img1定义为带有边缘检测的平面,img2定义为我们创建的掩码)。

          :Img1,右图:Img2(实际上,它是白色的,但我们把它改成了黄色)

          例如,img1上(0,0)处的像素将与img2上(0,0)处的像素相乘(同样地,图像上其他位置的每一个像素也是如此)。
          如果img1中的(0,0)像素是白色的(意味着它是一条边),img2中的(0,0)像素是黑色的(意味着这个点不是我们的车道线所在的孤立区域的一部分),操作看起来像11111111* 0000000,等于0000000(一个黑色像素)。
          我们将对图像上的每个像素重复这个操作,导致只输出掩码中的边缘。

          其他一切都被忽略了,仅输出隔离区域中的边。

          现在我们已经定义了我们想要的边,接着让我们定义一个函数把这些边变成线:

            lines = cv2.HoughLinesP(isolated, rho=2, theta=np.pi/180, threshold=100, np.array([]), minLineLength=40, maxLineGap=5)

            这一行代码是整个算法的核心,它被称为霍夫变换(Hough Transform),将孤立区域的白色像素簇转换为实际的线条。
            • 参数1:孤立梯度

            • 参数5:占位符数组

            • 参数6:最小行长

            • 参数7:最大行间距

            下面的部分将深入到算法背后的具体细节,所以在你们读完第二部分后,你们可以回到这部分,希望这部分会更有意义。
            第二部分:霍夫变换
            简单说明一下,这部分仅仅是理论,如果你们想跳过这一部分,可以继续阅读第3部分,但鼓励小伙伴们通读一遍。
            来谈谈霍夫变换。在笛卡尔平面(x和y轴)中,直线由公式y=mx+b定义,其中x和y对应于直线上的一个特定点,m和b分别对应于斜率和y轴截距。

            笛卡尔坐标空间中的直线

            平面被绘制成x和y值的函数,这意味着我们显示的是这条直线有多少(x, y)对组成(有无穷多的x, y对组成任何一条线,这就是为什么线延伸到无穷远的原因)。
            但是,可以用它的m和b值绘制直线,这是在一个叫做霍夫空间的平面上完成的。为了理解Hough变换算法,我们需要了解Hough空间是如何工作的。
            霍夫空间的解释
            在我们的用例中,我们可以将霍夫空间总结为两行:
            • 笛卡尔平面上的点在霍夫空间中变成直线

            • 笛卡尔平面上的直线在霍夫空间上变成点

            想想线的概念,一条线基本上是由一个接一个有序排列的无穷长的点组成的。因为在笛卡尔平面上,我们画的线是x和y的函数,线被显示为无限长因为有无限多的(x, y)对组成了这条线。
            现在在霍夫空间中,我们画出直线作为m和b值的函数。因为每条笛卡尔直线上只有一个m和b值,所以这条直线可以表示为一个点。
            例如,方程y=2x+1表示笛卡尔平面上的一条直线。它的m和b值分别是' 2 '和' 1 ',这是这个方程唯一可能的m和b值。另一方面,这个方程可以有很多x和y的值,使得这个方程成立(左边=右边)。
            如果我们要用m和b的值来画这个方程,我们只会用点(2,1);如果我们要用x和y的值来画这个方程,我们将会有无穷多的选择因为有无穷多的(x, y)对。
            把θ看成b, r看成m。稍后我们会在文章中解释θ和r的相关性。
            那么为什么霍夫空间中的线在笛卡尔平面上被表示为点(如果你们从之前的解释中很好地理解了这个理论,我们希望小伙伴们在没有阅读解释的情况下就能解决这个问题)。
            现在我们考虑笛卡尔平面上的一点。笛卡尔平面上的一个点只有一个可能的(x, y)对可以表示它,因此它是一个点,不是无限长。关于一个点,还有一个事实就是有无限多的可能的线可以通过这个点,换句话说,这个点可以满足无穷多个方程(y=mx + b)(LS=RS)。
            目前,在笛卡尔平面中,我们根据x和y值绘制这个点。但是在霍夫空间中,我们根据它的m和b值来画这个点,因为有无限条线穿过这个点,所以在霍夫空间中会得到一条无限长的线。
            以点(3,4)为例,可以通过该点的直线有:y= -4x+16, y= -8/3x + 12和y= -4/3x + 8(直线有无穷多,但为了简单起见,我们用3条直线)。
            如果你们在霍夫空间中绘制每一条直线([- 4,16],[-8/ 3,12],[-4/ 3,8]),在笛卡尔空间中代表每条直线的点将在霍夫空间中形成一条直线(这条直线对应于点(3,4))。

            每个点代表前面显示的线(匹配颜色)

            现在如果我们在个笛卡尔平面上放置另一个点呢?这在霍夫空间会有什么结果呢?通过霍夫空间,我们可以找到笛卡尔平面上最适合这两点的直线。
            我们可以通过在霍夫空间中绘制与笛卡尔空间中两点相对应的直线,并找到这两条直线在霍夫空间中相交的点(a.k.a它们的POI,交叉点)。
            总结上述内容:
            • 笛卡尔平面上的直线在霍夫空间中表示为点

            • 笛卡儿平面上的点在霍夫空间中表示为直线

            • 通过求霍夫空间中与这两个点对应的两条直线的POI的m和b坐标,可以找到笛卡尔空间中两点的最佳拟合直线,然后根据这些m和b的值组成一条直线。

            回到解释:
            虽然这些概念比较好,但它们为什么重要呢?还记得我们之前提到过的Canny边缘检测吗?它使用梯度来测量图像中的像素强度并输出边缘。
            在其核心,梯度只是图像上的点。所以我们能做的就是找到最适合每一组点的直线(图像左边的梯度和图像右边的梯度),这些最合适的线是我们的车道线。为了更好地理解它是如何工作的,让我们再深入了解一下!
            我们只是解释了如何通过查看m和b值来找到最合适的线对应于霍夫空间中的点的两条线的POI。然而,当我们的数据集增长时,并不总是有一条线完全适合我们数据。
            这就是我们不得不使用容器的原因。当合并容器时,我们将霍夫平面划分为等距部分。每个部分都称为容器,通过关注容器中POI的数量,使我们能够确定一条与我们的数据具有良好相关性的线。一旦找到有最多交集的容器,我们就可以使用m和b值,它们与该容器相对应,并在笛卡尔空间中形成一条直线,这条线就是最适合我们的数据的线。
            但是在垂直线上,斜率是无穷大的,我们不能在霍夫空间中表示无穷,这将导致程序崩溃。所以我们不用y=mx+b来表示直线方程,我们用P()和θ()来定义直线,这也被称为极坐标系统。
            在极坐标下,直线用方程P=xsinθ + ysinθ表示。在我们深入研究之前,让我们定义一下这些变量的含义:
            • P表示从原点垂直于直线的距离。

            • θ表示从正x轴到直线的俯角。

            • xcosθ表示x方向上的距离。

            • ysinθ表示y方向上的距离。

            这是对极坐标含义的直观解释

            用极坐标系统,即使有一条垂直线,也不会有任何误差。例如,取点(6,4)代入方程  P=xcosθ+ ysinθ。现在,我们取经过这个点x=6的垂直线,把它代入极坐标方程,P = 6cos(90) + 4sin(90)
            • θ是一条垂直线的90度,因为它从正x轴到直线本身的俯角是90度。θ的另一种表示方法是π/2(弧度)。如果你们想了解更多关于弧度的知识,以及我们为什么要使用它们,这里有一个很好的视频。然而,没有必要知道弧度是什么。

            • X和Y取点(6,4)的值因为这是我们在这个例子中使用的点。

            现在我们把这个方程解出来:

            P = 6cos(90) + 4sin(90)

            P = 6(1) + 4(0)

            P = 6

            如我们所见,我们不会以错误结束。事实上,我们甚至不需要做这个计算,因为我们在开始之前就已经知道P是多少了。注意,这和从原点到x轴的距离是一样的。

            我们想解释的东西的图像。

            那么现在这已经解决了问题,我们准备好回去编码了吗?不是现在。还记得之前我们在笛卡尔平面上画点的时候吗?我们最终会得到霍夫空间中的直线?当我们使用极坐标时,我们会得到一条曲线而不是一条直线。然而,概念是一样的,我们将找到具有大多数交叉点并使用那些m和b值来确定最佳拟合线。
            第三部分:优化+显示
            这一节是为了优化算法,如果我们不平均这些线,它们看起来很不稳定,因为cv2.HoughLinesP输出一串小线段,而不是一条大线。
            为了平均这些线,我们将定义一个“average”函数。

              def average(image, lines): left = [] right = [] for line in lines: slope = parameters[0] y_int = parameters[1] if slope < 0: left.append((slope, y_int)) else: right.append((slope, y_int))

              这个函数对cv2.HoughLinesP函数中生成的行进行平均,它会找到左右两个线段的平均斜率和y轴截距,并输出两条实线(一条在左边,另一条在右边)。在cv2.HoughLinesP函数的输出中,每个线段有两个坐标:一个表示直线的开始,另一个表示直线的结束。利用这些坐标,我们要计算每条线段的斜率和y轴截距。
              然后,我们将收集所有线段的斜率,并将每个线段分为与左线或右线对应的列表(负斜率=左线,正斜率=右线)。
              • 第4行:通过直线数组进行循环。

              • 第5行:从每个线段中提取两个点的(x, y)值。

              • 第6-9行:确定每个线段的斜率和y轴截距。

              • 第10-13行:将负斜率添加到左行列表中,将正斜率添加到右行列表中。

              注意:通常情况下,正斜率=左直线,负斜率=右直线,但在我们的例子中,图像的y轴是反的,这就是为什么斜率是反的(OpenCV中的所有图像都是反的y轴)。
              接下来,我们要从两个表中求斜率和y轴截距的平均值。

                right_avg = np.average(right, axis=0) left_avg = np.average(left, axis=0) left_line = make_points(image, left_avg) right_line = make_points(image, right_avg) return np.array([left_line, right_line])

                • 第1-2行:对两个列表(左边和右边)的所有线段取平均值。

                • 第3-4行:计算每一行的起始点和端点。(我们将在下一节定义make_points函数)

                • 第5行:输出每一行的2个坐标。

                现在我们有了两个列表的平均斜率和y轴截距,让我们定义两个列表的起点和终点。

                  def make_points(image, average): slope, y_int = average y1 = image.shape[0] y2 = int(y1 * (3/5)) x1 = int((y1 — y_int) // slope) x2 = int((y2 — y_int) // slope) return np.array([x1, y1, x2, y2])

                  这个函数有两个参数,一个是带有车道线的图像,另一个是有平均斜率和y_int的列表,输出每条线的起点和终点。
                  • 第1行:定义函数

                  • 第2行:得到平均斜率和y截距

                  • 第3 - 4行:定义的高度线(左右两边都一样)

                  • 第5 - 6行:通过重新排列一条线的方程计算x坐标,从y=mx+b to x = (y-b) / m

                  • 第7行:输出坐标集

                  为了进一步说明,在第一行,我们用y1值作为图像的高度。这是因为在OpenCV中,y轴是倒转的,所以0在顶部,而图像的高度在原点(参考下图)。同样,在第二行,y1乘以3/5,这是因为我们想让直线从原点y1开始,以图像的2/5结束。

                  应用于左线的make_points函数的可视化示例

                  但是,这个函数并不显示这些线,它只计算显示这些线所需的点。接下来,我们要创建一个函数,它取这些点,并用它们来画线。

                    def display_lines(image, lines): lines_image = np.zeros_like(image) if lines is not None: for line in lines: x1, y1, x2, y2 = line cv2.line(lines_image, (x1, y1), (x2, y2), (255, 0, 0), 10) return lines_image

                    这个函数有两个参数:我们想要显示线条的图像以及从平均函数输出的车道线。
                    • 第2行:创建一个与原始图像相同尺寸的黑色图像

                    • 第3行:确保包含线点的列表不是空的

                    • 第4-5行:循环遍历列表,并提取两对(x, y)坐标

                    我们可能会想,为什么我们不把这些线添加到真实图像上,而是黑色图像上。因为原始图像有点太亮了,所以如果我们把它调暗一点,让车道线看得更清楚一点就好了(是的,我们知道,这不是大不了的,但找到改进算法的方法总是很好的)。

                    :直接添加线条到图像。右:使用cv2.addddled函数

                    所以我们要做的就是调用cv2.addWeighted函数:
                    lanes =cv2.addWeighted(copy, 0.8, black_lines, 1, 1)
                    这个函数为实际图像中的每个像素赋予0.8的权重,使它们稍微暗一些(每个像素乘以0.8)。同样地,我们给所有车道线的黑色图像赋予1的权重,这样所有像素都保持相同的强度,使其突出。接下来我们要做的就是调用这些函数:

                      copy = np.copy(image1) grey = grey(copy) gaus = gauss(grey) edges = canny(gaus,50,150) isolated = region(edges)lines = cv2.HoughLinesP(isolated, 2, np.pi/180, 100, np.array([]), minLineLength=40, maxLineGap=5) averaged_lines = average(copy, lines) black_lines = display_lines(copy, averaged_lines)

                      在这里,我们简单地调用前面定义的所有函数,然后在第12行输出结果,cv2.waitKey函数用于告诉程序图像显示需要多长时间。我们将“0”传递给函数,这意味着它将等待,直到按下一个键关闭输出窗口。
                      输出结果:
                      我们也可以把这个算法应用到视频上。

                        video = r”D:\users\new owner\Desktop\TKS\Article Lane Detection\test2_v2_Trim.mp4" cap = cv2.VideoCapture(video) while(cap.isOpened()): ret, frame = cap.read() if ret == True:#----THE PREVIOUS ALGORITHM----# gaus = gauss(frame) edges = cv2.Canny(gaus,50,150) isolated = region(edges) lines = cv2.HoughLinesP(isolated, 2, np.pi/180, 50,) lanes = cv2.ad1dWeighted(frame, 0.8, black_lines, 1, 1) cv2.imshow(“frame”, lanes) #----THE PREVIOUS ALGORITHM----# if cv2.waitKey(10) & 0xFF == ord('q’): break else: break cap.release() cv2.destroyAllWindows()

                        这段代码将我们为图像创建的算法应用到视频中。记住,一个视频就是一串快速出现的图片。
                        • 第1-2行:定义视频的路径。

                        • 第3-4行:捕获视频(使用cv2. videcapture),并循环遍历所有帧。

                        • 第5-6行:读取帧,如果有帧,继续。

                        • 第10-18行:从前面的算法复制代码,并将所有使用Copy的地方替换为frame,因为我们想确保我们操作的是视频的帧,而不是前面函数中的图像。

                        • 第22-23行:显示每一帧10秒,如果按下“q”按钮,退出循环。

                        • 第24-25行:它是第5-6行if语句的延续,但它所做的只是如果没有任何帧,就退出循环。

                        • 第26-27行:关闭视频

                        我们刚刚建立了一个可以检测车道线的算法,希望小伙伴们喜欢构建这个算法,但不要止步于此,这只是一个关于计算机视觉世界的入门项目。

                        关键点:

                        • 使用高斯模糊去除图像中的所有噪声

                        • 使用canny边缘检测来分离图像中的边缘

                        关键字:

                        如果小伙伴们好奇,这里有一些与这个算法相关的关键术语,小伙伴们可以更深入地研究。
                        • 高斯模糊

                        • 位和二进制

                        • 精明的边缘检测

                        • 霍夫变换

                        • 梯度

                        • 极坐标

                        • OpenCV车道线检测

                        其他需要考虑的资源:

                        • youtube视频。

                        Github代码连接:
                        https://github.com/Nushaine/lane-detection/blob/master/Untitled33.ipynb
                        (0)

                        相关推荐

                        • [OpenCV]经典霍夫变换原理

                          本文主要讲述的是霍夫变换的一些内容,并加入一些在生活中的应用,希望能对读者对于霍夫变换的内容有所了解. 首先我先说的是,霍夫变换是一个特征提取技术.其可用于隔离图像中特定形状的特征的技术,应用在图像分 ...

                        • 二值化处理与边缘检测

                          问题:我在提取图像边缘的时候,首先对图像进行灰度变换,之后进行二值处理,最后进行边缘检测得到边缘图像. 但是在查阅资料的过程中我经常发现很多人忽略二值化的步骤,直接进行边缘检测:还有很多人在实现某些功 ...

                        • 一文解读经典霍夫变换(Hough Transform)

                          视觉/图像重磅干货,第一时间送达 新机器视觉 最前沿的机器视觉与计算机视觉技术 206篇原创内容 公众号 来源: Hello AI World 引言 本文讲述霍夫变换的一些内容,并加入一些理解性东西, ...

                        • 31、 霍夫变换 霍夫变换是一种用于在...

                          31. 霍夫变换 霍夫变换是一种用于在图像中查找直线.圆等形状的方法. 霍夫直线变换的基本理论是,二值图像中的任何点都可能是某条直线的一部分.如果通过斜率a和截距b对直线进行参数化,则原始图像中的一个 ...

                        • OpenCV基础知识入门

                          本文旨在让你快速入门opencv. OpenCV OpenCV是计算机视觉中最受欢迎的库,最初由intel使用C和C ++进行开发的,现在也可以在python中使用.该库是一个跨平台的开源库,是免费使 ...

                        • OpenCV探索之路(十一):轮廓查找和多边形包围轮廓

                          Canny一类的边缘检测算法可以根据像素之间的差异,检测出轮廓边界的像素,但它没有将轮廓作为一个整体.所以要将轮廓提起出来,就必须将这些边缘像素组装成轮廓. OpenCV中有一个很强大的函数,它可以从 ...

                        • (三)OpenCV图像处理

                          直接 直接用霍夫直线检测,效果差; 通过图像形态学操作来寻找直线,霍夫获取位置信息与显示. #include <opencv2/opencv.hpp>#include <iostre ...

                        • opencv python智能车道检测,助力无人驾驶

                          近年来,基于人工智能的车道检测算法得到了广泛的研究.与传统的基于特征的方法相比,许多方法表现出了优越的性能.然而,当使用具有挑战性的图像时,其准确率通常仍在低80%或高90%之间,甚至更低. 准确可靠 ...

                        • 手把手写Demo系列之车道线检测

                          快速获得最新干货 文章导读 本文是一篇从零开始做车道线检测Demo的教学式文章,从场景的定义到模型微调的输出,描述车道线Demo式例程中在每个环节需要做的工作,以及中途可能会遇到的各种问题. 1 场景 ...

                        • 自动驾驶汽车急转弯时车道线检测的3种技术

                          重磅干货,第一时间送达 自动驾驶汽车需要感知不同颜色以及在不同的照明条件下的车道线,以便准确地检测车道.除了速度和汽车动力学特性外,还应该知道车道曲率,以确定停留在车道上所需的转向角. 三种技术: 1 ...

                        • 速度最快250fps!实时、高性能车道线检测算法LaneATT

                          CVPR 2021 车道线检测方向论文:Keep your Eyes on the Lane: Real-time Attention-guided Lane Detection . 论文: http ...

                        • 使用OpenCV实现车道线检测

                          重磅干货,第一时间送达 图0 印度泰米尔纳德邦安纳马莱森林公路上的车道检测 本文源码:https://github.com/KushalBKusram/AdvancedLaneDetection 计算 ...

                        • 极市直播回放丨第75期-方浩:车道线检测新SOTA,RESA:循环特征位移聚合器(AAAI2021)

                          车道检测是自动驾驶中的重要的任务,可以帮助系统处理车道保持,轨迹规划,行为预测等场景.但由于路面上的场景非常复杂(如堵车时车道线被严重遮挡,线自身磨损等情况),使用一般的检测器通常效果不佳. 在这次分 ...

                        • CVPR2019| 05-22更新11篇论文及代码合集(含6篇oral,语义分割/车道线检测/视觉导航等)

                          前段时间,计算机视觉顶会CVPR 2019 公布了接收结果,极市也对此做了相关报道:1300篇!CVPR2019接收结果公布,你中了吗?.目前官方已公布了接收论文列表,极市已汇总目前公开的所有论文链接 ...

                        • SUPER车道线检测:异构数据集训练、物理驱动拟合

                          介绍一篇今年的车道线检测论文 SUPER: A Novel Lane Detection System,作者来自密歇根大学和SF Motors 公司. 论文:https://arxiv.org/abs ...

                        • 「车道线检测」一种基于神经网络 结构约束的车道线检测方法

                          作者:小魔方 「3D视觉工坊」技术交流群已经成立,目前大约有12000人,方向主要涉及3D视觉.CV&深度学习.SLAM.三维重建.点云后处理.自动驾驶.CV入门.三维测量.VR/AR.3D人 ...

                        • 车道线检测技术分析

                          针对车道线检测的任务,我们需要弄清楚几个问题: 1.车道线的表示形式? 输出类型:掩码/点集/矢量线条 实例化:每个车道线是否形成实例 分类:是否对车道线进行了分类(单白.双黄等) 提前定义的参数:是 ...