简介
Udacity是硅谷的一个在线教育网站,主要以介绍AI技术为主,除了基本的课程录像还会有专业老师进行代码review,通过课程会颁发nano degree。其中最为火爆的一门课程就是自动驾驶课程CarNd,这门课程由简入繁分了三个term,第一个term主要是以机器视觉识别道路、交通标志为主,需要掌握基本的深度学习和机器视觉知识;第二个term是介绍传感器以及使用卡尔曼滤波进行定位和速度控制;第三个term主要是路径规划以及系统集成,据说最后会把学员的代码放入真车进行实际测试。Ucacity的课程质量很高,但是有些小贵,出于无奈只好从网上搜集资料,开源的资料包含了前两个term,学完可以对无人驾驶有基本的了解,感兴趣的同学可以试试。
处理流程
无人驾驶车辆的基本功能之一就是检测车道,之前参加过飞思卡尔智能车竞赛的同学对这个项目应该并不陌生,与摄像头组识别赛道的原理类似,可惜当时我在红外组,没有深入了解相关原理。
先简单说下利用图像处理技术识别车道的基本流程
p1相对来说比较简单,使用opencv的一些操作函数即可实现检测功能。下面详细介绍每个步骤的实现
读入图像
首先读入图像
1 | //导入opencv |
效果如下
二值化处理
opencv对于彩色图像的存储使用GBR格式,与一般RGB格式顺序不太一样,据说是为了做硬件兼容。二值化处理是把读入的彩色图像转成灰度图,方便后续计算。
1 | //彩色图像转化为灰度图 |
效果如下
高斯模糊
高斯模糊是一种图像平滑技术,基本原理是在以中心像素为原点取一定半径内像素点值求加权平均值,权重根据高斯函数求得,结果就是高斯模糊处理后的图片。从数值上看,整体上更加平缓,从图片效果上看变得模糊了。
1 | blured_image = cv2.GaussianBlur(gray_image,(5,5),0) //第二个参数表示模糊半径,第三个参数代表高斯函数标准差,半径越大标准差越大,图片越模糊 |
Canny边缘检测
由于车道具有明显的边缘,所以使用边缘检测可以提取车道信息,opencv提供了canny边缘检测函数可以实现该功能。
1 | canny = cv2.cv2.Canny(blur,250,300) |
效果如下
ROI区域提取
在理想情况下(直路且行驶平稳),车道的位置在拍摄的图片中的相对位置不变,我们并不关心除车道以外的其他位置,所以可以通过ROI区域提取获取车道所在区域。针对p1所用到的图片,其ROI区域顶点坐标分别为(0,540),(465,320),(475,320),(960,320),其所形成的不规则区域如下所示
与边缘检测结果进行叠加
1 | roi_range = np.array([[(0,540),(465,320),(475,320),(960,320)]],dtype = np.int32) |
效果如下
Hough直线检测
现在经过处理后的图像基本只包含ROI区域内的车道信息以及其他干扰信息,这两种信息的区别在于车道由多条直线构成,而干扰信息则不具备这样明显的条件,所以直线检测可以提取ROI区域内的车道。
opencv提供了霍夫变换函数HoughLines和HoughLinesP用于直线检测,目前先不去深究其原理,后续我再补充。其函数定义如下:
HoughLinesP(image, rho, theta, threshold, minLineLength=None, maxLineGap=None)
- image:必须是二值图像,推荐使用canny边缘检测的结果图像;
- rho: 线段以像素为单位的距离精度,double类型的,推荐用1.0
- theta: 线段以弧度为单位的角度精度,推荐用numpy.pi/180
- threshod: 累加平面的阈值参数,int类型,超过设定阈值才被检测出线段,值越大,基本上意味着检出的线段越长,检出的线段个数越少。根据情况推荐先用100试试
- minLineLength:线段以像素为单位的最小长度,根据应用场景设置
maxLineGap:同一方向上两条线段判定为一条线段的最大允许间隔(断裂),超过了设定值,则把两条线段当成一条线段,值越大,允许线段上的断裂越大,越有可能检出潜在的直线段
1
2
3
4
5
6
7lines = cv2.HoughLinesP(img_mashed,1,np.pi/180,15,25,20)
for line in lines:
for x1,y1,x2,y2 in line:
cv2.line(img_masked,(x1,y1),(x2,y2),255,12)
cv2.imshow('img_masked',img_masked)
cv2.waitKey()
cv2.destroyAllWindows()
效果如下
车道标记
直线检测完成后,需要对检测后的直线进行过滤,把误差较大的点移除,并且还要将这些点连成直线,进行车道标记。
车道左右边线检测
首先需要区分车道左边线和右边线,我们已经拿到了直线检测后的直线端点,可以利用斜率进行区分。由于opencv默认原点位于图像左上角顶点,所以左边线斜率为负,右边线斜率为正,并计算每段线段的斜率和截距。
1 | positive_slop_intercept = [] //左边线点构成直线的斜率和截距 |
计算左右边线斜率和截距
上一步完成了斜率和截距的计算,还要对其进行筛选,把误差较大的线段移除
1 | legal_slop=[] |
计算车道两端端点
计算除了车道的斜率和截距,还需要两个端点才能确定车道的范围。由于我们只关注ROI区域范围内的车道,所以车道左右边线的上顶点的值为ymin=320、下顶点的值为ymax=540,从而求得每条边线的两端顶点。
1 | xmin = int((ymin - intercept)/slop) |
标记车道
把标记过车道的图片与原始图片叠加,实现车道的标记。图像叠加通过opencv的addWeight函数实现,该函数定义如下:
cv2.addWeighted(src1, alpha, src2, beta, gamma[, dst[, dtype]])
- alpha : 第一幅图片中元素的权重
- beta : 是第二个的权重
- gamma : 加到最后结果上的值
1 | //imgae为原始图像,line_image为车道标记图像 |
最终结果如下
处理视频
处理视频需要使用moviepy的VideoFileClip函数
1 | video = VideoFileClip(path) //待处理图像路径 |
效果如下
- 原始视频
- 处理后视频