分享一个简单车道识别示例,介绍如何使用 opencv 识别图像中车道线,有助于对车辆进行导航,虽然比较基础,主要目的让大家熟悉 opencv 一些特征提取的方法。我们随后介绍如何应用深度学习来做车道线检测的任务。
读取路况图像
这里我们主要使用 opencv 来做这件事,所以首先引入 opencv 库。
import cv2
使用 imread 方法来读取图片,使用 imshow 将图片显示出来,简单地观测一下图片。路况还是比较好,几乎没有什么车辆、而且没有弯道,大家可能会说是不是过于简单,实际上没有那么简单,做事可以先从简单开始,一步一步来,把简单做好了才能做好复杂的事。
image = cv2.imread('test_image.jpg')
cv2.imshow('result',image)
cv2.waitKey(0)
test_image
去色
在 opencv 中提供一些用于检测(识别)边缘、角点等这些特征点方法通常需要我们输入一张灰度图,这也就是为什么我们需要先对图片进行去色处理。在计算机视觉中提取关键点是大部分任务中不可缺少。关键点可以带来更多关于图片的信息,帮助机器理解图像的语义。而且关键点通常就是在图像灰度有变化的地方。
image = cv2.imread('test_image.jpg')
# copy not reference to
lane_image = np.copy(image)
gray = cv2.cvtColor(lane_image,cv2.COLOR_RGB2GRAY)
cv2.imshow('result',gray)
cv2.waitKey(0)
图
降低噪点
第二项工作还需要对图片进行模糊(blur),通过模糊来减少图片的噪点这样更有利于查找边界。
在灰度图每一个像素都是一个单一值来表示,表示图像灰度值,通过改变深色周围的像素灰度值来实现模糊(或者说是平滑)这里我们会创建一个卷积核。高斯模糊本质上是低通滤波器,输出图像的每个像素点是原图像上对应像素点与周围像素点的加权和,原理并不复杂。如何了解了卷积神经网络这应该就不难懂了。
从图中我们可以看出高斯模糊对图片边缘进行模糊处理。
图图片是由表示灰度的像素值组成的,像素值差距大地方会明显梯度下降感觉会造成图片不够平滑,或者说不连续,这个很好理解,这就需要我们模糊对这些比较锐利的边缘进行处理。看这张图 90 值周围都是 255 所以 90 点在 255 就是感觉不太协调。我们可以通过 90 周围像素值来影响 90 这个值,通过求这些值平均值方式来减少 90 和其周围像素值之间差异。
卷积核这个就是由加权值组成的卷积核,卷积核在图像进行游走,通过将像素值通过计算其周围值的均值或者带有权重的值来计算其值。
图高斯模糊之所以叫高斯模糊,是因为运用了高斯卷积核来进行图片模糊成的密度函数
blur = cv2.GaussianBlur(gray,(5,5),0)
模糊处理图
Canny边缘检测算法
获取图片的边缘信息是底层数字图像处理的基本任务之一。边缘信息对进一步提取高层语义信息有很大的影响。这也有助于我们对图像物体进行识别
图过渡分为两种一个强过渡一种是弱过渡
图
过渡强弱是我们判断是否边缘的依据,我们可以通过阈值来指定那些是我们要找的过渡将这些过渡作为边缘,那些是我们忽略过渡。
激活
这里就需要一个类似神经网激活函数,来确定那些是用于判断边缘那些不是。
图
canny = cv2.Canny(blur,50,150)
cv2.imshow('result',canny)
canny
整理代码
import cv2
import numpy as np
def canny(image):
gray = cv2.cvtColor(image,cv2.COLOR_RGB2GRAY)
blur = cv2.GaussianBlur(gray,(5,5),0)
canny = cv2.Canny(blur,50,150)
return canny
# read image
image = cv2.imread('test_image.jpg')
# copy not reference to
lane_image = np.copy(image)
canny = canny(lane_image)
cv2.imshow('result',canny)
cv2.waitKey(0)
将对图片进行去色、模糊以及边缘检测一系列处理都整理到 canny 这个函数。然后通过调用该函数进行
import matplotlib.pyplot as plt
plt.imshow(canny)
plt.show()
图
应用遮罩
通常这样 camera 都是安装在车前盖上来对车道线进行识别。
def region_of_interest(image):
height = image.shape[0]
ploygons = np.array([[(200,height),(1100,height),(550,250)]])
mask = np.zeros_like(image)
cv2.fillPoly(mask,ploygons,255)
return mask
cv2.imshow('result',region_of_interest(canny))
cv2.waitKey(0)
mask
通过 bitwise_and 对两个图像的每一个像素做 and (与) 运算,来将遮罩应用图像,这个如果大家有 Photoshop 经验应该不难理解这个操作,也就是仅保留我们感兴趣区域。
def region_of_interest(image):
height = image.shape[0]
ploygons = np.array([[(200,height),(1100,height),(550,250)]])
mask = np.zeros_like(image)
cv2.fillPoly(mask,ploygons,255)
masked_image = cv2.bitwise_and(image,mask)
return masked_image
图
霍夫变换(Hough Transform)
从图中看这就是一个再简单不过函数, 表示线性方程的直线。
直线方程在下图中我们可以看到 方程形状是由斜率 m 和截距 b 决定的,所以根据 m 和 b 的取值就线性方程,建立一个坐标系通过点表示出来就是霍夫空间。m 和 b 就是在初中时候就学到的方程的斜率和截距。
那么也就是在霍夫空间中每一个点对应坐标中一条直线。
图m 是表示方程的斜率,然后以点(在霍夫空间中的坐标为 m 和 b) 来对应左侧的直线。
图通过一个例子来说明,在下图(左侧)中 可以在(右侧)霍夫空间中点(3,2)对应该方程 。
知道通过一点的直线是任意多, 通过坐标系中的一点,该方程在霍夫空间对应点为(2,8),大家都已经了解了他们之间对应关系了吧。
图方程 也会通过这一点,然后在将这个方程对应到霍夫空间,依次类推大家可以自己动手尝试一下举出一些列通过一点方程,然后将他们对应表示在霍夫空间。
图大家不难看出通过某一点的方程,看似没有什么规律不过在霍夫方程中我们似乎看出这些经过某一点所有方程在霍夫空间中对应的点都会出现在一条直线上。
图同样我们可以再举一点,然后将通过这一点的直线描绘在霍夫空间表示出来,在让这些方程对应点在霍夫空间连接成线,我们发现这两直线有了一个交点。
图其实在霍夫空间两条直线交点所对应方程就是通过这两个点的直线的表示的方程
图
依次类推
图
这样我们回到我们处理的道路图,说了这么多有关霍夫变换,就是为了通过多个点来找他们共享(也就是穿过这些点)的那条直线。
图 图
实际情况应该没有上面那么理想,我们无法找到一条直线精准地通过所有点,只能模糊地在一定范围内找到一条符合直线。所以我们可以将霍夫空间划分为一些小格子来扩大范围。
网友评论