OpenCV-Python:车道检测
任务:
一共要完成两项任务:
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:车道检测的更多相关文章
- OpenCV—Python 轮廓检测 绘出矩形框(findContours\ boundingRect\rectangle
千万注意opencv的轮廓检测和边缘检测是两码事 本文链接:https://blog.csdn.net/wsp_1138886114/article/details/82945328 1 获取轮廓 O ...
- OpenCV + Python 人脸检测
必备知识 Haar-like opencv api 读取图片 灰度转换 画图 显示图像 获取人脸识别训练数据 探测人脸 处理人脸探测的结果 实例 图片素材 人脸检测代码 人脸检测结果 总结 下午的时候 ...
- 14、OpenCV Python 直线检测
__author__ = "WSX" import cv2 as cv import numpy as np #-----------------霍夫变换------------- ...
- OpenCV + python 实现人脸检测(基于照片和视频进行检测)
OpenCV + python 实现人脸检测(基于照片和视频进行检测) Haar-like 通俗的来讲,就是作为人脸特征即可. Haar特征值反映了图像的灰度变化情况.例如:脸部的一些特征能由矩形特征 ...
- 【python+opencv】直线检测+圆检测
Python+OpenCV图像处理—— 直线检测 直线检测理论知识: 1.霍夫变换(Hough Transform) 霍夫变换是图像处理中从图像中识别几何形状的基本方法之一,应用很广泛,也有很多改进 ...
- Opencv+Python实现缺陷检测
实验七.缺陷检测 一. 题目描述 对下面的图片进行缺陷检测操作,请详细地记录每一步操作的步骤. 第一站图片是标准样品,后面几张图中有几个样品有瑕疵,需要你通过计算在图片上显示出哪张是合格,哪张 ...
- [PyImageSearch] Ubuntu16.04 使用深度学习和OpenCV实现物体检测
上一篇博文中讲到如何用OpenCV实现物体分类,但是接下来这篇博文将会告诉你图片中物体的位置具体在哪里. 我们将会知道如何使用OpenCV‘s的dnn模块去加载一个预训练的物体检测网络,它能使得我们将 ...
- Yolo车辆检测+LaneNet车道检测
Yolo车辆检测+LaneNet车道检测 源代码:https://github.com/Dalaska/Driving-Scene-Understanding/blob/master/README.m ...
- OpenCV特征点检测------ORB特征
OpenCV特征点检测------ORB特征 ORB是是ORiented Brief的简称.ORB的描述在下面文章中: Ethan Rublee and Vincent Rabaud and Kurt ...
- OpenCV Python教程(3、直方图的计算与显示)
转载请详细注明原作者及出处,谢谢! 本篇文章介绍如何用OpenCV Python来计算直方图,并简略介绍用NumPy和Matplotlib计算和绘制直方图 直方图的背景知识.用途什么的就直接略过去了. ...
随机推荐
- java 数字左补齐0
NumberFormat nf = NumberFormat.getInstance(); //设置是否使用分组 nf.setGroupingUsed(false); ...
- 关于4A系统(我对4A系统的维护的理解)
4A系统 4A系统是统一安全管理平台解决方案,指认证Authentication.账号Account.授权Authorization.审计Audit,中文名称为统一安全管理平台解决方案.即将身份认证. ...
- python之路(5)文件操作(open)
目录 前言 文件的打开模式 文件句柄的方法 seek()方法介绍 前言 打开文件,得到文件句柄并赋值给一个变量 通过句柄对文件进行操作 关闭文件 f = open('demo.txt','r',e ...
- Aras SP9里打开自己写的网页。
首先把自己写的网页挂在IIS里或者网站挂到IIS里面. 然后再Aras里新增method //网页参数 var dialogArguments = new Array(); //窗体参数 var op ...
- 一道Python面试题:给出d = [True, False, True, False, True],请利用列表d,只用一句话返回列表[0,2,4]
看题:给出d = [True, False, True, False, True],请利用列表d,只用一句话返回列表[0,2,4] 这道题的关键是拿到True的索引值,最初我是用list的index方 ...
- hdu5974 A Simple Math Problem(数学)
题目链接 大意:给你两个数X,YX,YX,Y,让你找两个数a,ba,ba,b,满足a+b=X,lcm(a,b)=Ya+b=X,lcm(a,b)=Ya+b=X,lcm(a,b)=Y. 思路:枚举gcd( ...
- 浏览器将URL变成一个屏幕上显示的网页的过程?
前言 一个浏览器是怎么工作的? 正文 URL变网页过程: 1.浏览器通过http或https协议,向服务端请求页面 2.将请求过来的HEML代码通过解析,构建DOM树 3.计算DOM树上的CSS属性 ...
- django中的分页标签
class Pagination: def __init__(self, page, page_total, per_one_page=15, max_page=11): ''' :param pag ...
- java连接3种数据库 JdbcLinkDB --201801
先看这篇记录 java连接3种数据库 JdbcLinkDB 测试 --201801 配置文件放在jar外面 读取,遇到的问题 - 海蓝steven - 博客园https://www.cnblogs.c ...
- 关于操作HDFS的一个问题
近日写程序定时任务调Hadoop MR程序,然后生成报表,发送邮件,当时起了两个任务A和B,调MR程序之前,会操作hdfs(读写都有),任务A每天一点跑,任务B每十分钟跑一次,B任务不会调用MR程序, ...