前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >使用Python+OpenCV实现自动驾驶汽车的车道线检测

使用Python+OpenCV实现自动驾驶汽车的车道线检测

作者头像
磐创AI
发布2021-05-07 16:10:55
5.3K0
发布2021-05-07 16:10:55
举报
文章被收录于专栏:磐创AI技术团队的专栏


磐创AI分享

来源 | 深度学习与计算机视觉

编辑 | 磐怼怼

对于所有想知道如何在一篇文章中涵盖这一概念的人,我想说,在你深入探索之前,事情听起来很复杂。我不会说这篇文章非常简单,但是它的确是建立在非常基础的计算机视觉概念之上的。

先决条件是什么?具备一些基本的OpenCV知识会很好。如果没有,请不要担心,我将尝试解释我将使用的OpenCV函数,并为你提供参考,以更详细地检查它们。

本文的每一节将介绍一个最终将在程序的主要部分中使用的函数。此外,在本文中,我将使用图像演示所有内容。你可以重用相同的代码来使用视频(因为视频只是图像的集合)。

步骤1:边缘检测

我们将使用Canny边缘检测。如果你不确定这是什么,看看我之前的文章,它以实用的方式解释了这一点。

  • https://medium.com/analytics-vidhya/image-simplification-through-binarization-in-opencv-1292d91cae12
代码语言:javascript
复制
def canyEdgeDetector(image):
    edged = cv2.Canny(image, 50, 150)
    return edged

这是我们应用Canny边缘检测后的输出结果

检测Canny边缘后输出

步骤2:定义ROI(感兴趣区域)

驾驶时,为了让汽车保持在车道上,你只关注当前道路的下一个100米。而且,你也不关心护栏另一边的路。这就是我们感兴趣的区域。我们从图像中隐藏不必要的细节,只显示能帮助我们找到车道的区域。

红色的三角形表示我们感兴趣的区域

代码语言:javascript
复制
def getROI(image):
    height = image.shape[0]
    width = image.shape[1]
    # Defining Triangular ROI: The values will change as per your camera mounts
    triangle = np.array([[(100, height), (width, height), (width-500, int(height/1.9))]])
    # creating black image same as that of input image
    black_image = np.zeros_like(image)
    # Put the Triangular shape on top of our Black image to create a mask
    mask = cv2.fillPoly(black_image, triangle, 255)
    # applying mask on original image
    masked_image = cv2.bitwise_and(image, mask)
    return masked_image
  1. 我们已经定义了三角形ROI,它的坐标将根据你安装在你的汽车上的摄像头的位置而变化(尝试只拥有那部分图像,这将实际有助于车道检测)。
  2. 我们创建了一个与原始图像相同形状的黑色图像:

创建一个与原始图像相同形状的黑色图像

  1. 创建蒙版:然后使用cv2.fillPoly()将我们的三角形(带白色线条)放在我们的黑色图像的顶部,创建一个蒙版。

创建一个面具

  1. 在我们的原始图像上应用蒙版,得到只有我们的ROI的裁剪图像。

原始图像+蒙版=具有ROI的最终图像

这一步的输出类似于:

getROI ()之后的输出

在得到感兴趣区域之前进行边缘检测是很重要的,否则边缘检测也会检测出我们感兴趣区域的边界。

步骤3:获取图像中的所有直线

下一步是通过ROI得到图像中的所有直线。houghlinesp()可以帮助你实现这一点。这个函数返回它能在输入图像中找到的所有直线的列表。每一行用[x1, y1, x2, y2]表示。

现在,这看起来很简单,但是houghlinesp检测的基本工作原理需要一点时间来解释。所以我不会在本文中介绍它。相反,我建议你看一看此教程(#28,#29,#30应该足以理解霍夫直线原则)。

  • 教程:https://www.youtube.com/watch?v=7m-RVJ6ABsY
代码语言:javascript
复制
def getLines(image):
    lines = cv2.HoughLinesP(image, 0.3, np.pi/180, 100, np.array([]), minLineLength=70, maxLineGap=20)
    return lines

必须根据你的需求调整cv2.HoughLinesP()的参数(尝试更改和调试最适合你的)。但我认为以上这些应该在大多数情况下都适用。这一步的输出是这样的:

在图像中检测到3条线。图像中可能检测到数百条线。因此,调整参数以获得尽可能少的线

步骤4:一些实用函数

下面的实用函数获取图像和线条列表,并在图像上绘制线条。(这个步骤没有从步骤3获取任何输入。相反,这只是一个将从Step5调用的实用程序步骤,因此你首先查看Step5并在需要时访问该步骤)。

代码语言:javascript
复制
def displayLines(image, lines):
    if lines is not None:
        for line in lines:
            x1, y1, x2, y2 = line.reshape(4) #converting to 1d array
            cv2.line(image, (x1, y1), (x2, y2), (255, 0, 0), 10)
    return image

我们定义了另一个实用函数来从它的参数(斜率和截距)得到线坐标。记住,直线用y=mx+c表示,其中m是斜率,c是截距。

代码语言:javascript
复制
def getLineCoordinatesFromParameters(image, line_parameters):
    slope = line_parameters[0]
    intercept = line_parameters[1]
    y1 = image.shape[0]  # since line will always start from bottom of image
    y2 = int(y1 * (3.4 / 5))  # some random point at 3/5
    x1 = int((y1 - intercept) / slope)
    x2 = int((y2 - intercept) / slope)
    return np.array([x1, y1, x2, y2])

注意我们是如何选择y和y的值的

步骤5:平滑线条

一旦我们从步骤3中获得了直线,在这一步中我们将这些直线分成两组(左边和右边)。如果你注意到步骤3的输出图像,那么该步骤将把Line1和line2放到左边的组中,而Line3放到右边的组中。

如何在车道的左侧和右侧获得一条公共线

分组后,我们找到该组的平均斜率(m)和截距(c),并通过调用getLineCoordinatesFromParameters() 并传递平均值m和平均值c来为每个组创建一条线。

下面是完成这一切的函数:

代码语言:javascript
复制
def getSmoothLines(image, lines):
    left_fit = []  # will hold m,c parameters for left side lines
    right_fit = []  # will hold m,c parameters for right side lines

    for line in lines:
        x1, y1, x2, y2 = line.reshape(4)
        parameters = np.polyfit((x1, x2), (y1, y2), 1)
        slope = parameters[0]
        intercept = parameters[1]

        if slope < 0:
            left_fit.append((slope, intercept))
        else:
            right_fit.append((slope, intercept))

    left_fit_average = np.average(left_fit, axis=0)
    right_fit_average = np.average(right_fit, axis=0)

    # now we have got m,c parameters for left and right line, we need to know x1,y1 x2,y2 parameters
    left_line = getLineCoordinatesFromParameters(image, left_fit_average)
    right_line = getLineCoordinatesFromParameters(image, right_fit_average)
    return np.array([left_line, right_line])

这是线条分组后的图像:

线分组后输出

主代码(逐一调用上述步骤)

一旦我们准备好了各个函数,我们只需要在我们的主代码中调用它们,你就会在你的图像中检测到车道。

代码语言:javascript
复制
image = cv2.imread("3.jpg") #Load Image

edged_image = canyEdgeDetector(image)   # Step 1
roi_image = getROI(edged_image)         # Step 2

lines = getLines(roi_image)             # Step 3

smooth_lines = getSmoothLines(image, lines)    # Step 5
image_with_smooth_lines = displayLines(image, smooth_lines) # Step 4

cv2.imshow("Output", image_with_smooth_lines)
cv2.waitKey(0)

输出会像这样:

具有确定车道的最

最后的话

终输出

你一直看到文章的结尾。对所有内容进行排序并使其适合图像后,你便知道如何将其用于视频。你可能已经意识到你可以如何巧妙地使用非常基本的计算机视觉操作来实现如此有用的东西。

我想说的是,不要把这项工作与特斯拉这样的大公司做的比较(他们的基础也是类似的)。把这作为动力,也许在某个时候,你也能取得类似的成就。

在此之前,请继续探索:你可以在这里查看Github源代码的链接。

  • https://github.com/pdhruv93/computer-vision/tree/main/lane-detection-self-driving

检测到视频上的通道后的样本输出(由于录像机的原因,帧速度很慢):

  • https://youtu.be/JH-wQc4pSbI
本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2021-05-05,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 磐创AI 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 主代码(逐一调用上述步骤)
相关产品与服务
图像处理
图像处理基于腾讯云深度学习等人工智能技术,提供综合性的图像优化处理服务,包括图像质量评估、图像清晰度增强、图像智能裁剪等。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档