作者|ABHISHEK SHARMA
编译|VK
来源|Analytics Vidhya
大约十年前,当谷歌的人还在试验一辆原型车时,我预见了自己的第一辆自动驾驶汽车。当时我立刻被这个想法迷住了。不可否认,我不得不等待一段时间,直到这些概念向社区开放,现在看来等待确实是值得的!
我最近试验了一些与计算机视觉有关的自动驾驶汽车概念,里面包括车道检测。想想看,它实际上是设计任何一款自主汽车的核心概念。
以下是我们将在本视频中构建的车道检测系统:https://youtu.be/sYhZbhT-Smw
很酷,对吧?在这个教程中,我会使用OpenCV库进行车道检测和自动驾驶汽车。当然,在本教程中,我们还将引入Python代码。
目录
-
理解车道检测的概念
-
理解问题陈述
-
什么是帧掩码?
-
车道检测的图像预处理
-
用OpenCV在Python中实现车道检测
理解车道检测的概念
那么什么是车道检测?以下是百度百科对车道的定义:
车道,又称行车线、车行道,是用在供车辆行经的道路。在一般公路和高速公路都有设置,高速公路对车道使用带有法律上的规则,例如行车道和超车道。
对其进行定义是很重要的,因为它使我们能够继续进行车道检测概念。我们在建立一个系统时不能有任何含糊不清的地方。
正如我前面提到的,车道检测是自动驾驶汽车和自动驾驶汽车的关键组成部分。这是驾驶场景理解的重要研究课题之一。一旦获得车道位置,车辆就知道去哪里,并避免撞上其他车道或离开道路。这样可以防止驾驶员/车辆系统偏离车道。
以下是一些随机道路图像(第一行)及其检测到的车道(第二行):
理解问题陈述
我们希望执行的任务是实时检测视频中的车道。我们可以通过多种方式进行车道检测。我们可以使用基于学习的方法,例如在带注释的视频数据集上训练深度学习模型,或者使用预训练好的模型。
然而,也有更简单的方法来执行车道检测。在本文中,我将向你展示如何在不使用任何深入学习模型的情况下完成此任务。但是我们将使用Python中流行的OpenCV库。
下面是我们将要处理的视频的一个帧:
正如我们在这张图片中看到的,我们有四条车道被白色的车道标线隔开。所以,要检测车道,我们必须检测车道两边的白色标记。这就引出了一个关键问题——我们如何检测车道标线?
除了车道标线之外,场景中还有许多其他对象。道路上有车辆、路侧护栏、路灯等,在视频中,每一帧都会有场景变化。这很好地反映了真实的驾驶情况。
因此,在解决车道检测问题之前,我们必须找到一种方法来忽略驾驶场景中不需要的对象。
我们现在能做的一件事就是缩小感兴趣的领域。与其使用整个帧,不如只使用帧的一部分。在下面的图像中,除了车道的标记之外,其他所有内容都隐藏了。当车辆移动时,车道标线将或多或少地落在该区域内:
在下一节中,我将向你展示如何编辑视频帧以选择特定区域。你还将了解一些必要的图像预处理操作。
什么是帧掩码(Frame Mask)?
在这里,帧掩码只是一个NumPy数组。当我们想对图像应用掩码时,只需将图像中所需区域的像素值更改为0、255或任何其他数字。下面给出了一个图像掩蔽的例子。图像中某个区域的像素值已设置为0:
这是一种非常简单但有效的从图像中去除不需要的区域和对象的方法。
车道检测的图像预处理
我们将首先对输入视频中的所有帧应用掩码。然后,我们将应用图像阈值化和霍夫线变换来检测车道标线。
图像阈值化
在这种方法中,灰度图像的像素值根据阈值被指定为表示黑白颜色的两个值之一。因此,如果一个像素的值大于一个阈值,它被赋予一个值,否则它被赋予另一个值。
如上所示,对蒙版图像应用阈值后,我们只得到输出图像中的车道标线。现在我们可以通过霍夫线变换很容易地检测出这些标记。
霍夫线变换
霍夫线变换是一种检测任何可以用数学方法表示的形状的方法。
例如,它可以检测矩形、圆、三角形或直线等形状。我们感兴趣的是检测可以表示为直线的车道标线。
在执行图像阈值化后对图像应用霍夫线变换将提供以下输出:
我们需要对所有帧执行此过程,然后将生成的帧缝合到新视频中。
用OpenCV在Python中实现车道检测
是时候用Python实现这个车道检测项目了!我推荐使用Google Colab,因为构建车道检测系统需要计算能力。
首先导入所需的库:
import os
import re
import cv2
import numpy as np
from tqdm import tqdm_notebook
import matplotlib.pyplot as plt
读取视频帧
我已经从这个YouTube视频中抽取了一些视频片段。你可以从这个链接下载:https://drive.google.com/file/d/1e4cc4zFFna3Owyym6aq7ZXoquHA2l95O/view?usp=sharing。
# 获取帧的文件名
col_frames = os.listdir('frames/')
col_frames.sort(key=lambda f: int(re.sub('D', '', f)))
# 加载帧
col_images=[]
for i in tqdm_notebook(col_frames):
img = cv2.imread('frames/'+i)
col_images.append(img)
让我们绘制一个帧:
# 指定一个索引
idx = 457
# plot frame
plt.figure(figsize=(10,10))
plt.imshow(col_images[idx][:,:,0], cmap= "gray")
plt.show()
帧掩码创建
我们感兴趣的区域是一个多边形。我们想掩盖除了这个区域以外的一切。因此,我们首先必须指定多边形的坐标,然后使用它来准备帧掩码:
# 创建0矩阵
stencil = np.zeros_like(col_images[idx][:,:,0])
# 指定多边形的坐标
polygon = np.array([[50,270], [220,160], [360,160], [480,270]])
# 用1填充多边形
cv2.fillConvexPoly(stencil, polygon, 1)
# 画出多边形
plt.figure(figsize=(10,10))
plt.imshow(stencil, cmap= "gray")
plt.show()
# 应用该多边形作为掩码
img = cv2.bitwise_and(col_images[idx][:,:,0], col_images[idx][:,:,0], mask=stencil)
# plot masked frame
plt.figure(figsize=(10,10))
plt.imshow(img, cmap= "gray")
plt.show()
图像预处理
我们必须对视频帧执行一些图像预处理操作来检测所需的车道。预处理操作包括:
-
图像阈值化
-
霍夫线变换
1.图像阈值化
# 应用图像阈值化
ret, thresh = cv2.threshold(img, 130, 145, cv2.THRESH_BINARY)
# 画出图像
plt.figure(figsize=(10,10))
plt.imshow(thresh, cmap= "gray")
plt.show()
2.霍夫线变换
lines = cv2.HoughLinesP(thresh, 1, np.pi/180, 30, maxLineGap=200)
# 创建原始帧的副本
dmy = col_images[idx][:,:,0].copy()
# 霍夫线
for line in lines:
x1, y1, x2, y2 = line[0]
cv2.line(dmy, (x1, y1), (x2, y2), (255, 0, 0), 3)
# 画出帧
plt.figure(figsize=(10,10))
plt.imshow(dmy, cmap= "gray")
plt.show()
现在我们将对每个帧应用所有这些操作。我们还将结果帧保存在新目录中:
cnt = 0
for img in tqdm_notebook(col_images):
# 应用帧掩码
masked = cv2.bitwise_and(img[:,:,0], img[:,:,0], mask=stencil)
# 应用图像阈值化
ret, thresh = cv2.threshold(masked, 130, 145, cv2.THRESH_BINARY)
# 应用霍夫线变换
lines = cv2.HoughLinesP(thresh, 1, np.pi/180, 30, maxLineGap=200)
dmy = img.copy()
#画出检测到的线
try:
for line in lines:
x1, y1, x2, y2 = line[0]
cv2.line(dmy, (x1, y1), (x2, y2), (255, 0, 0), 3)
cv2.imwrite('detected/'+str(cnt)+'.png',dmy)
except TypeError:
cv2.imwrite('detected/'+str(cnt)+'.png',img)
cnt+= 1
视频准备
# 输入帧的路径
pathIn= 'detected/'
#输出视频路径
pathOut = 'roads_v2.mp4'
# 视频每秒的帧数
fps = 30.0
from os.path import isfile, join
# 获取帧的文件名
files = [f for f in os.listdir(pathIn) if isfile(join(pathIn, f))]
files.sort(key=lambda f: int(re.sub('D', '', f)))
接下来,我们将把检测到的车道上的所有帧放入一个列表中:
frame_list = []
for i in tqdm_notebook(range(len(files))):
filename=pathIn + files[i]
#读取每一个文件
img = cv2.imread(filename)
height, width, layers = img.shape
size = (width,height)
#将帧插入图像数组
frame_list.append(img)
最后,我们现在可以使用下面的代码将帧合并为视频:
# 写入视频
out = cv2.VideoWriter(pathOut,cv2.VideoWriter_fourcc(*'DIVX'), fps, size)
for i in range(len(frame_array)):
out.write(frame_array[i])
out.release()
这就完成了Python中的车道检测系统。
结尾
在本教程中,我们介绍了一种简单的车道检测技术。我们没有使用任何模型或复杂的图像特征。相反,我们的解决方案完全基于某些图像预处理操作。
但是,在很多情况下,这个解决方案都无法工作。例如,当没有车道标线,或者道路上的车辆太多时,该系统将失败。在车道检测中有更复杂的方法来克服这些问题。
原文链接:https://www.analyticsvidhya.com/blog/2020/05/tutorial-real-time-lane-detection-opencv/
欢迎关注磐创AI博客站:
http://panchuang.net/
sklearn机器学习中文官方文档:
http://sklearn123.com/
欢迎关注磐创博客资源汇总站:
http://docs.panchuang.net/