任务:

一共要完成两项任务:

1. 在所提供的公路图片上检测出车道线并标记

2. 在所提供的公路视频上检测出车道线并标记

方案:

要检测出当前车道,就是要检测出左右两条车道直线。由于无人车一直保持在当前车道,那么无人车上的相机拍摄视频中,车道线的位置应该基本固定在某一个范围内:

如果我们手动把这部分ROI区域抠出来,就会排除大部分干扰。接下来检测直线肯定用霍夫变换,但ROI区域内的边缘直线信息还是很多,考虑到只有左右两条车道,一条斜率为正、一条斜率为负,可将所有的线分为两组,每组再通过均值或最小二乘法拟合的方式确定唯一一条线就可以完成检测。具体步骤如下:

1. 灰度化

2. 高斯模糊

3. Canny边缘检测

4. 不规则ROI区域截取

5. 霍夫直线检测

6. 车道计算

对于视频来说,只要能检测出一幅图,后面将图像合成一下即可。

图像预处理

灰度化和滤波操作是大部分图像处理的必要步骤。灰度化是因为不需要色彩信息,可以减少计算量。而滤波会削弱图像噪点,排除干扰信息。另外,边缘提取是基于图像梯度的,梯度对噪声很敏感,所以平滑操作必不可少。

这里我们用分模块来写,方便调用:

import cv2
import numpy as np
# 高斯滤波核大小
blur_ksize =
# Canny边缘检测高低阈值
canny_lth =
canny_hth =
def process_an_image(img):
# . 灰度化、滤波和Canny
gray = cv2.cvtColor(img, cv2.COLOR_RGB2GRAY)
blur_gray = cv2.GaussianBlur(gray, (blur_ksize, blur_ksize), )
edges = cv2.Canny(blur_gray, canny_lth, canny_hth)
if __name__ == "__main__":
img = cv2.imread('test_pictures/lane.jpg')
result = process_an_image(img)
cv2.imshow("lane", np.hstack((img, result)))
cv2.waitKey()

边缘检测结果图

ROI截取

按前面方案中提到的,只需保留边缘图中红线部分区域用于后续的霍夫直线检测,其余的都是无用的信息:

我们可以穿件一个梯形的掩膜,然后与边缘检测结果图混合运算,掩膜中白色部分保留,黑色部分舍弃。梯形的四个坐标需要手动标记:

def process_an_image(img):
# . 灰度化、滤波和Canny
# . 标记四个坐标点用于ROI截取
rows, cols = edges.shape
points = np.array([[(, rows), (, ), (, ), (cols, rows)]])
# [[[ ], [ ], [ ], [ ]]]
roi_edges = roi_mask(edges, points) def roi_mask(img, corner_points):
# 创建掩膜
mask = np.zeros_like(img)
cv2.fillPoly(mask, corner_points, )
masked_img = cv2.bitwise_and(img, mask)
return masked_img

结果图 roi_edges如下:

只保留关键区域的边缘检测图

霍夫直线提取

为了方便后续计算直线的斜率,我们使用统计概率霍夫直线变换(因为它能得到直线的起点和终点坐标):

# 霍夫变换参数
rho =
theta = np.pi /
threshold =
min_line_len =
max_line_gap =
def process_an_image(img):
# . 灰度化、滤波和Canny
# . 标记四个坐标点用于ROI截取
# . 霍夫直线提取
drawing, lines = hough_lines(roi_edges, rho, theta, threshold, min_line_len, max_line_gap)
def hough_lines(img, rho, theta, threshold, min_line_len, max_line_gap):
# 统计概率霍夫直线变换
lines = cv2.HoughLinesP(img, rho, theta, threshold, minLineLength=min_line_len, maxLineGap=max_line_gap)
# 新建一副空白画布
drawing = np.zeros((img.shape[], img.shape[], ), dtype=np.uint8)
# draw_lines(drawing, lines) # 画出直线检测结果
return drawing, lines
def draw_lines(img, lines, color=[, , ], thickness=):
for line in lines:
for x1, y1, x2, y2 in line:
cv2.line(img, (x1, y1), (x2, y2), color, thickness)

draw_lines()用来画直线检测的结果,后面我们会接着处理直线,所以这里注释掉了。可以取消注释看看效果:

霍夫变换结果图

对本例的这张测试图来说,如果打印出直线的条数print(len(lines)),应该是16条

车道计算

前面通过霍夫变换得到了多条直线的地点和终点,我们的目的是通过某种算法只得到左右两条车道线

第一步、 根据斜率正负划分某条线是左车道还是右车道。

其中左车道(斜率 < 0),右车道(斜率 > 0)。原因如下图所示:

左车道与图像坐标系成钝角,斜率为负,右车道与图像坐标系成锐角,斜率为正。

第二步、迭代计算各直线斜率与斜率均值的差,排除掉差值过大的异常数据

第一次计算完斜率均值并排除掉异常值后,再在剩余的斜率中取均值,继续排除。一直迭代下去。

第三步、最小二乘法拟合左右车道线

经过第二步的筛选,就只剩下可能的左右车道线了。我们需要从多条直线中拟合出一条。使用最小二乘法,它通过最小化误差的平方和来寻找数据的最佳匹配函数。

假设目前可能的左车道线有6条,也就是12个坐标点。

我们的目标是拟合出这样一条直线:

使得误差平方和最小:

Python中可以直接使用 np.polyfit() 进行最小二乘法拟合

def process_an_image(img):
# . 灰度化、滤波和Canny
# . 标记四个坐标点用于ROI截取
# . 霍夫直线提取
# . 车道拟合计算
draw_lanes(drawing, lines)
# . 最终将结果合在原图上
result = cv2.addWeighted(img, 0.9, drawing, 0.2, )
return result
def draw_lanes(img, lines, color=[, , ], thickness=):
# a. 划分左右车道
left_lines, right_lines = [], []
for line in lines:
for x1, y1, x2, y2 in line:
k = (y2 - y1) / (x2 - x1)
if k < :
left_lines.append(line)
else:
right_lines.append(line)
if (len(left_lines) <= or len(right_lines) <= ):
return
# b. 清理异常数据
clean_lines(left_lines, 0.1)
clean_lines(right_lines, 0.1)
# c. 得到左右车道线点的集合,拟合直线
left_points = [(x1, y1) for line in left_lines for x1, y1, x2, y2 in line]
left_points = left_points + [(x2, y2) for line in left_lines for x1, y1, x2, y2 in line]
right_points = [(x1, y1) for line in right_lines for x1, y1, x2, y2 in line]
right_points = right_points + [(x2, y2) for line in right_lines for x1, y1, x2, y2 in line]
left_results = least_squares_fit(left_points, , img.shape[])
right_results = least_squares_fit(right_points, , img.shape[])
# 注意这里点的顺序
vtxs = np.array([[left_results[], left_results[], right_results[], right_results[]]])
# d. 填充车道区域
cv2.fillPoly(img, vtxs, (, , ))
# 或者只画车道线
# cv2.line(img, left_results[], left_results[], (, 0, 255), thickness)
# cv2.line(img, right_results[], right_results[], (, 0, 255), thickness) def clean_lines(lines, threshold):
# 迭代计算斜率均值,排除掉与差值差异较大的数据
slope = [(y2 - y1) / (x2 - x1) for line in lines for x1, y1, x2, y2 in line]
while len(lines) > :
mean = np.mean(slope)
diff = [abs(s - mean) for s in slope]
idx = np.argmax(diff)
if diff[idx] > threshold:
slope.pop(idx)
lines.pop(idx)
else:
break def least_squares_fit(point_list, ymin, ymax):
# 最小二乘法拟合
x = [p[] for p in point_list]
y = [p[] for p in point_list]
# polyfit第三个参数为拟合多项式的阶数,所以1代表线性
fit = np.polyfit(y, x, )
fit_fn = np.poly1d(fit) # 获取拟合的结果
xmin = int(fit_fn(ymin))
xmax = int(fit_fn(ymax))
return [(xmin, ymin), (xmax, ymax)]

最后得到的是左右两条车道线的起点和终点坐标。可以选择画出车道线,也可以填充整个区域:

画出车道线的效果不是很好,还是选用填充比较直观。

视频处理

搞定了一张图,视频的话也就没什么难度了。关键是视频帧的提取和合成,为此我们需要用到Python的视频编辑包 moviepy

pip install moviepy

另外还需要 ffmpeg,首次运行moviepy时会自动下载。也可手动下载。建议手动下载,不知为什么,博主自动下载老是失败。ffmpeg-win32-v3.2.4.exe

只需要在开头导入 moviepy,然后主函数改掉就可以了,其余代码不需要修改:

# 开头导入moviepy
from moviepy.editor import VideoFileClip
# 主函数更改为:
if __name__ == "__main__":
output = 'test_videos/output.mp4'
clip = VideoFileClip("test_videos/cv2_white_lane.mp4")
out_clip = clip.fl_image(process_an_image)
out_clip.write_videofile(output, audio=False)

OpenCV-Python:车道检测的更多相关文章

  1. OpenCV—Python 轮廓检测 绘出矩形框(findContours\ boundingRect\rectangle

    千万注意opencv的轮廓检测和边缘检测是两码事 本文链接:https://blog.csdn.net/wsp_1138886114/article/details/82945328 1 获取轮廓 O ...

  2. OpenCV + Python 人脸检测

    必备知识 Haar-like opencv api 读取图片 灰度转换 画图 显示图像 获取人脸识别训练数据 探测人脸 处理人脸探测的结果 实例 图片素材 人脸检测代码 人脸检测结果 总结 下午的时候 ...

  3. 14、OpenCV Python 直线检测

    __author__ = "WSX" import cv2 as cv import numpy as np #-----------------霍夫变换------------- ...

  4. OpenCV + python 实现人脸检测(基于照片和视频进行检测)

    OpenCV + python 实现人脸检测(基于照片和视频进行检测) Haar-like 通俗的来讲,就是作为人脸特征即可. Haar特征值反映了图像的灰度变化情况.例如:脸部的一些特征能由矩形特征 ...

  5. 【python+opencv】直线检测+圆检测

     Python+OpenCV图像处理—— 直线检测 直线检测理论知识: 1.霍夫变换(Hough Transform) 霍夫变换是图像处理中从图像中识别几何形状的基本方法之一,应用很广泛,也有很多改进 ...

  6. Opencv+Python实现缺陷检测

    实验七.缺陷检测 一. 题目描述 ​ 对下面的图片进行缺陷检测操作,请详细地记录每一步操作的步骤. ​ 第一站图片是标准样品,后面几张图中有几个样品有瑕疵,需要你通过计算在图片上显示出哪张是合格,哪张 ...

  7. [PyImageSearch] Ubuntu16.04 使用深度学习和OpenCV实现物体检测

    上一篇博文中讲到如何用OpenCV实现物体分类,但是接下来这篇博文将会告诉你图片中物体的位置具体在哪里. 我们将会知道如何使用OpenCV‘s的dnn模块去加载一个预训练的物体检测网络,它能使得我们将 ...

  8. Yolo车辆检测+LaneNet车道检测

    Yolo车辆检测+LaneNet车道检测 源代码:https://github.com/Dalaska/Driving-Scene-Understanding/blob/master/README.m ...

  9. OpenCV特征点检测------ORB特征

    OpenCV特征点检测------ORB特征 ORB是是ORiented Brief的简称.ORB的描述在下面文章中: Ethan Rublee and Vincent Rabaud and Kurt ...

  10. OpenCV Python教程(3、直方图的计算与显示)

    转载请详细注明原作者及出处,谢谢! 本篇文章介绍如何用OpenCV Python来计算直方图,并简略介绍用NumPy和Matplotlib计算和绘制直方图 直方图的背景知识.用途什么的就直接略过去了. ...

随机推荐

  1. [luogu1600]NOIp2016D1T2 天天爱跑步

    题目链接: luogu1600 谨以此题纪念那段年少无知但充满趣味的恬淡时光 附上一位dalao的博客链接:https://www.luogu.org/blog/user26242/ke-pa-di- ...

  2. bzoj3168 钙铁锌硒维生素 (矩阵求逆+二分图最小字典序匹配)

    设第一套为A,第二套为B 先对于每个B[i]判断他能否替代A[j],即B[i]与其他的A线性无关 设$B[i]=\sum\limits_{k}{c[k]*A[k]}$,那么只要看c[j]是否等于零即可 ...

  3. 【学习笔记】python

    1.  len( s )  返回对象(字符.列表.元祖等)的长度或项目个数. >>>str = "runoob" >>> len(str) # ...

  4. (二叉树 递归) leetcode 889. Construct Binary Tree from Preorder and Postorder Traversal

    Return any binary tree that matches the given preorder and postorder traversals. Values in the trave ...

  5. 第一周java学习总结

    学号 20175206 <Java程序设计>第一周学习总结 教材学习内容总结 第一章是关于JAVA入门的注意事项: 第一章主要按照顺序讲了JAVA的地位,诞生,特点,JDK的安装,一些ja ...

  6. Httpclient发送json请求

    一.Httpclient发送json请求 public String RequestJsonPost(String url){    String strresponse = null;    try ...

  7. Exp2 后门原理与实践

    一.基础问题回答 (注:本实验中在win7上使用的ncat.exe和socat.exe均可在实验指导的附件中下载) 1.例举你能想到的一个后门进入到你系统中的可能方式? 答:非官方网站下载资源时,可能 ...

  8. Hive 锁处理

    hive有两个锁,共享索(s) 和排它锁(x) 在进行ddl操作时,排他锁会阻止 ddl 操作.drop.alter table 如果一个hive查询使用到了表A,执行时间10分钟.在这10分钟内要d ...

  9. 使用onblur+alert+focus导致的死循环解决

    <input type="text" id="loginName" onblur="checkLoginName()"/> fu ...

  10. Supervisor的安装以及使用

    1.supervisor的介绍 Supervisor 是用Python开发的一个client/server服务,是Linux/Unix系统下的一个进程管理工具,不支持Windows系统,通过配置可以实 ...