作者 | Joanis Gatopoulos
来源 | Medium
编辑 | 代码医生团队
可以通过Computer Vision逐步将视频流转换为行检测器
动机
完全自动驾驶的乘用车并非“指日可待”。马斯克(Elon Musk)声称,特斯拉到2020年底将具备“完全自动驾驶”功能。特别是,他说,特斯拉的硬件已经为自动驾驶做好了准备,剩下的只是他们当前软件的更新,许多杰出的科学家正在研究它。
作为驾驶员,人类的本能是第一个本能,那就是朝我们前方看,并确定汽车应向何处移动。每辆自动驾驶汽车的前方都装有摄像头,这是一项非常重要的任务,它决定了汽车在其间移动的边界。对于人类,在道路上画线。现在,将教一辆自动驾驶汽车看这些线。保证这会很有趣的:)
议程
将逐步设计井眼管线,并在其中激励这样做的动机。
- 灰度变换
- 高斯模糊
- Canny边缘检测
- 遮盖感兴趣的区域
- Hough线检测器
- 查找道路线
完整的代码:
https://github.com/ioangatop/AutonomousCar
在这里,将逐步介绍它,提供整个代码的快照。但是某些部分被省略了,因为这会使职位变得很沉重,并分散对目标的关注。因此有关其他信息,请参阅上面的存储库。
步骤0:读取图像
在matplotlib帮助下,可以轻松地将Python脚本中的任何图像作为三维张量CHW加载(颜色通道,图像的高度和宽度)
代码语言:javascript复制import matplotlib.image as mpimg
img_path = 'test_images/solidWhiteCurve'
img = mpimg.imread(img_path)
plt.imshow(img)
plt.show()
步骤1:灰度
首先,要使图像成为灰度图像。只有一个颜色通道。这将有助于识别边缘和拐角。
可以很容易地通过opencv
代码语言:javascript复制import cv2
gray_img = cv2.cvtColor(img, cv2.COLOR_RGB2GRAY)
plt.imshow(gray_img, cmap='gray')
plt.show()
步骤2:高斯模糊
将高斯噪声添加到图像中,它非常有用,因为它可以平滑像素之间的插值,并且是超通过噪声和伪梯度的一种方法。越高kernel,结果图像将越模糊。
代码语言:javascript复制kernel_size = 5
gauss_img = cv2.GaussianBlur(gray_img,(kernel_size, kernel_size), 0)
步骤3:Canny边缘检测
Canny Edge Detection提供了一种检测图像边界的方法。这是通过图像的渐变完成的。
后者不过是一个函数,其中每个像素的亮度与渐变的强度相对应。
将通过追踪遵循最强渐变的像素来找到边缘!通常,梯度显示函数变化的速度,像素之间的强烈密度变化将指示边缘。
代码语言:javascript复制low_threshold, high_threshold = [200, 300]
canny_img = cv2.Canny(gauss_img, low_threshold, high_threshold)
如您所见,现在可以清楚地确定路线!(甚至可以看到汽车的形状!)
步骤4:遮盖感兴趣的区域
上图中有一些异常值;道路另一端的一些边缘,从风景(山)等,到边缘。当相机固定好后,可以在图像上放置一个遮罩,并仅保留这些对任务有趣的线条。因此,绘制梯形是很自然的,以便仅保留期望道路线所在的区域。再一次,cv2在这里
代码语言:javascript复制# Setting the corners of the trapezium
vertices = np.array([[(0, img_line.shape[0]), (img_line.shape[1], img_line.shape[0]), (400, 260), (600, 260)]])
# make a blank/white image
mask = np.zeros_like(img)
mask_channels = (255,) * img.shape[2]
# Fill the area of interest with 0 and 255 these
# which lie outside of it, thoughout all color channels
cv2.fillPoly(mask, vertices, mask_channels)
# Keep only the pixels with 0 value of the canny_img
masked_img = cv2.bitwise_and(canny_img, mask)
选定区域
应用遮罩后
步骤5:霍夫线检测器
上面的图像仅代表边缘的点。剩下的就是连接边缘。在这种情况下,正在寻找行,将通过将图像传输到称为Hough Space的参数空间来实现这一点。现在,将处理极坐标(rho和theta),在其中搜索相交的线。
代码语言:javascript复制import math
lines = cv2.HoughLinesP(img, rho=1, theta=math.pi/180,
threshold=15, np.array([]),
minLineLength=30,
maxLineGap=40)
line_img = np.zeros((img.shape[0], img.shape[1], 3), dtype=np.uint8)
上面将返回许多小车道(最小长度是超参数minLineLength)。line_img将是一个列表,其元素是一个列表,并由图像空间{(x1,y1),(x2,y2)}中的两个点组成。众所周知,在2D空间中只有一条线经过两点。
这里自然而然地出现了问题。如何连接这些线路,并导致只有两条,它们将成为道路线路?这似乎是这篇文章中最具挑战性的部分。
步骤6:找到路线
策略如下:
- 相对于x轴将图像分成两半
- 将线性回归模型拟合到这些点,以找到一条平滑的线。
由于存在离群值,需要一种可以有效处理它们的回归模型。将使用HuberRegressor。然后,将图像限制在y轴的某个范围内,并借助它cv2.polylines绘制线。请记住,为了获得一条平滑的线,将通过给定的回归值绘制y给定的预测x 。
代码语言:javascript复制import math
def draw_lines(line_img, lines):
# CODE HERE
pass
lines = cv2.HoughLinesP(img, rho=1, theta=math.pi/180,
threshold=15, np.array([]),
minLineLength=30,
maxLineGap=40)
line_img = np.zeros((img.shape[0], img.shape[1], 3), dtype=np.uint8)
draw_lines(line_img, lines)
plt.imshow(line_img)
plt.show()
这时,鼓励读者对以上功能进行编码。
步骤7:将线条与原始图片连接
通过对两个图像进行加权,可以将它们相加。请记住,黑色区域img_lines具有value 0,因此加法不会更改输出之一。
代码语言:javascript复制out_img = cv2.addWeighted(img, 0.9, img_lines, 1.0, 0.0)
plt.imshow(out_img)
plt.show()
备注
瞧!设法找到了道路!视频只是一系列图像,因此,借助moviepy,可以在每帧中使用上述管线!视频管道如下所示:
代码语言:javascript复制from moviepy.editor import VideoFileClip
in_video = 'test_videos_output/solidWhiteCurve.mp4'
output_video = 'test_videos_output/out.mp4'
clip = VideoFileClip(in_video).subclip(0, 8)
empty_clip = clip.fl_image(YOUR PIPELINE AS A FUNCTION THAT
RETURNS THE WEIGHTED IMAGE)
out_clip.write_videofile(output_video, audio=False)
如前所述,完整的代码可以在此处找到。随意使用参数,并提出检测路线的新方法。
https://github.com/ioangatop/AutonomousCar