目标:实际公路的车道线检测


素材中车道保持不变,车道线清晰明确,易于检测,是车道检测的基础版本,网上也有很多针对复杂场景的高级实现,感兴趣的朋友可以自行了解。

如果我们手动把这部分ROI区域抠出来,就会排除掉大部分干扰。接下来检测直线肯定是用霍夫变换,但ROI区域内的边缘直线信息还是很多,考虑到只有左右两条车道线,一条斜率为正,一条为负,可将所有的线分为两组,每组再通过均值或最小二乘法拟合的方式确定唯一一条线就可以完成检测。总体步骤如下:
1. 灰度化
2. 高斯模糊
3. Canny边缘检测
4. 不规则ROI区域截取
5. 霍夫直线检测
6. 车道计算
7. 对于视频来说,只要一幅图能检查出来,合成下就可以了,问题不大。

图像预处理

灰度化和滤波操作是大部分图像处理的必要步骤。灰度化不必多说,因为不是基于色彩信息识别的任务,所以没有必要用彩色图,可以大大减少计算量。而滤波会削弱图像噪点,排除干扰信息。另外,根据前面学习的知识,边缘提取是基于图像梯度的,梯度对噪声很敏感,所以平滑滤波操作必不可少。

这次的代码我们分模块来写,规范一点。其中process_an_image()是主要的图像处理流程:

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

ROI获取

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

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

def process_an_image(img):# 1. 灰度化、滤波和Canny# 2. 标记四个坐标点用于ROI截取rows, cols = edges.shapepoints = np.array([[(0, rows), (460, 325), (520, 325), (cols, rows)]])# [[[0 540], [460 325], [520 325], [960 540]]]roi_edges = roi_mask(edges, points)def roi_mask(img, corner_points):# 创建掩膜mask = np.zeros_like(img)cv.fillPoly(mask, corner_points, 255)masked_img = cv.bitwise_and(img, mask)return masked_img

这样,结果图”roi_edges”应该是:

霍夫直线提取

为了方便后续计算直线的斜率,我们使用统计概率霍夫直线变换(因为它能直接得到直线的起点和终点坐标)。霍夫变换的参数比较多,可以放在代码开头,便于修改:

# 霍夫变换参数
rho = 1
theta = np.pi / 180
threshold = 15
min_line_len = 40
max_line_gap = 20def process_an_image(img):# 1. 灰度化、滤波和Canny# 2. 标记四个坐标点用于ROI截取# 3. 霍夫直线提取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 = cv.HoughLinesP(img, rho, theta, threshold, minLineLength=min_line_len, maxLineGap=max_line_gap)# 新建一副空白画布drawing = np.zeros((img.shape[0], img.shape[1], 3), dtype=np.uint8)# draw_lines(drawing, lines)     # 画出直线检测结果return drawing, linesdef draw_lines(img, lines, color=[0, 0, 255], thickness=1):for line in lines:for x1, y1, x2, y2 in line:cv.line(img, (x1, y1), (x2, y2), color, thickness)

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

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

车道计算

这部分应该算是本实验的核心内容了:前面通过霍夫变换得到了多条直线的起点和终点,我们的目的是通过某种算法只得到左右两条车道线。
第一步:根据斜率正负划分某条线是左车道还是右车道。

再次强调,斜率计算是在图像坐标系下,所以斜率正负/左右跟平面坐标有区别。
第二步、迭代计算各直线斜率与斜率均值的差,排除掉差值过大的异常数据。

注意这里迭代的含义,意思是第一次计算完斜率均值并排除掉异常值后,再在剩余的斜率中取均值,继续排除……这样迭代下去。

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

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

具体来说,假设目前可能的左车道线有6条,也就是12个坐标点,包括12个x和12个y,我们的目的是拟合出这样一条直线:

使得误差平方和最小:

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

def process_an_image(img):# 1. 灰度化、滤波和Canny# 2. 标记四个坐标点用于ROI截取# 3. 霍夫直线提取# 4. 车道拟合计算draw_lanes(drawing, lines)# 5. 最终将结果合在原图上result = cv.addWeighted(img, 0.9, drawing, 0.2, 0)return resultdef draw_lanes(img, lines, color=[255, 0, 0], thickness=8):# a. 划分左右车道left_lines, right_lines = [], []for line in lines:for x1, y1, x2, y2 in line:k = (y2 - y1) / (x2 - x1)if k < 0:left_lines.append(line)else:right_lines.append(line)if (len(left_lines) <= 0 or len(right_lines) <= 0):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, 325, img.shape[0])right_results = least_squares_fit(right_points, 325, img.shape[0])# 注意这里点的顺序,从左下角开始按照顺序构造梯形vtxs = np.array([[left_results[1], left_results[0], right_results[0], right_results[1]]])# d. 填充车道区域cv.fillPoly(img, vtxs, (0, 255, 0))# 或者只画车道线# cv.line(img, left_results[0], left_results[1], (0, 255, 0), thickness)# cv.line(img, right_results[0], right_results[1], (0, 255, 0), 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) > 0: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:breakdef least_squares_fit(point_list, ymin, ymax):# 最小二乘法拟合x = [p[0] for p in point_list]y = [p[1] for p in point_list]# polyfit第三个参数为拟合多项式的阶数,所以1代表线性fit = np.polyfit(y, x, 1)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时会自动下载,也可手动下载。

只需在开头导入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)

本文实现了车道检测的基础版本,如果你感兴趣的话,可以自行搜索了解更多。

实现功能完整代码

import cv2 as cv
import numpy as np# 高斯滤波核大小
blur_ksize = 5# Canny边缘检测高低阈值
canny_lth = 50
canny_hth = 150# 霍夫变换参数
rho = 1
theta = np.pi / 180
threshold = 15
min_line_len = 40
max_line_gap = 20def process_an_image(img):# 1. 灰度化、滤波和Cannygray = cv.cvtColor(img, cv.COLOR_RGB2GRAY)blur_gray = cv.GaussianBlur(gray, (blur_ksize, blur_ksize), 1)edges = cv.Canny(blur_gray, canny_lth, canny_hth)# 2. 标记四个坐标点用于ROI截取rows, cols = edges.shapepoints = np.array([[(0, rows), (460, 325), (520, 325), (cols, rows)]])# [[[0 540], [460 325], [520 325], [960 540]]]roi_edges = roi_mask(edges, points)# 3. 霍夫直线提取drawing, lines = hough_lines(roi_edges, rho, theta,threshold, min_line_len, max_line_gap)# 4. 车道拟合计算draw_lanes(drawing, lines)# 5. 最终将结果合在原图上result = cv.addWeighted(img, 0.9, drawing, 0.2, 0)return resultdef roi_mask(img, corner_points):# 创建掩膜mask = np.zeros_like(img)cv.fillPoly(mask, corner_points, 255)masked_img = cv.bitwise_and(img, mask)return masked_imgdef hough_lines(img, rho, theta, threshold, min_line_len, max_line_gap):# 统计概率霍夫直线变换lines = cv.HoughLinesP(img, rho, theta, threshold,minLineLength=min_line_len, maxLineGap=max_line_gap)# 新建一副空白画布drawing = np.zeros((img.shape[0], img.shape[1], 3), dtype=np.uint8)# 画出直线检测结果# draw_lines(drawing, lines)# print(len(lines))return drawing, linesdef draw_lines(img, lines, color=[0, 0, 255], thickness=1):for line in lines:for x1, y1, x2, y2 in line:cv.line(img, (x1, y1), (x2, y2), color, thickness)def draw_lanes(img, lines, color=[255, 0, 0], thickness=8):# a. 划分左右车道left_lines, right_lines = [], []for line in lines:for x1, y1, x2, y2 in line:k = (y2 - y1) / (x2 - x1)if k < 0:left_lines.append(line)else:right_lines.append(line)if (len(left_lines) <= 0 or len(right_lines) <= 0):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, 325, img.shape[0])right_results = least_squares_fit(right_points, 325, img.shape[0])# 注意这里点的顺序vtxs = np.array([[left_results[1], left_results[0], right_results[0], right_results[1]]])# d.填充车道区域cv.fillPoly(img, vtxs, (0, 255, 0))# 或者只画车道线# cv.line(img, left_results[0], left_results[1], (0, 255, 0), thickness)# cv.line(img, right_results[0], right_results[1], (0, 255, 0), 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) > 0: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:breakdef least_squares_fit(point_list, ymin, ymax):# 最小二乘法拟合x = [p[0] for p in point_list]y = [p[1] for p in point_list]# polyfit第三个参数为拟合多项式的阶数,所以1代表线性fit = np.polyfit(y, x, 1)fit_fn = np.poly1d(fit)  # 获取拟合的结果xmin = int(fit_fn(ymin))xmax = int(fit_fn(ymax))return [(xmin, ymin), (xmax, ymax)]if __name__ == "__main__":img = cv.imread('test_pictures/lane2.jpg')result = process_an_image(img)cv.imshow("lane", np.hstack((img, result)))cv.waitKey(0)

opencv 图像处理应用之车道检测相关推荐

  1. 基于OpenCV的视频道路车道检测

    基于OpenCV的视频道路车道检测 基于OpenCV的视频道路车道检测 前言 综述 运行方法 车道检测的实现 路面图像二值化 基于透视变换提取车道区域 基于二次多项式拟合车道线 计算曲率半径与车辆的偏 ...

  2. python+opencv图像处理之边缘检测车道线识别

    python+opencv图像处理之边缘检测车道线识别 1.自行安装python和opencv 2.导入我们要使用的相关库 import cv2 from matplotlib import pypl ...

  3. OpenCV图像处理——iOS端人脸检测

    前言 1.OpenCV有专门的iOS平台的包,可以真接下载导入工程,也可以用cmake把OpenCV源码编成.a文件,以静态库的形式导入工程. 2.我这里用的Xcode11,OpenCV用的是最4.2 ...

  4. Python+Opencv图像处理--基于OTSU+凸包检测的粘连大米分割

    文章目录 前言 1.分割流程图 2.图像预处理 2.1 改进二值化算法 2.2 形态学运算 3.提取轮廓 4.凸包检测 5.标记大米 前言 转载请注明本文出处. 最近一段时间一直在看粘连分割,网上也有 ...

  5. opencv 分割长度_opencv+python智能车道检测,助力无人驾驶

    近年来,基于人工智能的车道检测算法得到了广泛的研究.与传统的基于特征的方法相比,许多方法表现出了优越的性能.然而,当使用具有挑战性的图像时,其准确率通常仍在低80%或高90%之间,甚至更低. 准确可靠 ...

  6. python仿真智能驾驶_opencv+python智能车道检测,助力无人驾驶

    近年来,基于人工智能的车道检测算法得到了广泛的研讨.与传统的基于特征的方法相比,许多方法表现出了优越的功能.但是,当使用具有应战性的图像时,其准确率通常仍在低80%或高90%之间,甚至更低. 准确牢靠 ...

  7. 实战:使用 OpenCV 的自动驾驶汽车车道检测(附代码)

    点击上方"小白学视觉",选择加"星标"或"置顶" 重磅干货,第一时间送达 一.边缘检测 我们将使用 Canny 进行边缘检测.如果你不确定这 ...

  8. 独家 | 无人驾驶项目实战: 使用OpenCV进行实时车道检测

    作者:PRATEEK JOSHI 翻译:张若楠 校对:吴金笛 本文长度为2600字,建议阅读10分钟 本文为一个从图像预处理角度入手的无人驾驶车道识别实战项目. 作者序 大约十年前,我瞥见了第一辆自动 ...

  9. python获取数组中大于某一阈值的那些索引值_使用Python+OpenCV进行实时车道检测...

    大约十年前,当谷歌还在试验一辆原型车的时候,我想到了自己的第一辆自动驾驶汽车,当时我立刻被这个想法迷住了.不可否认的是,我必须等待一段时间,直到这些概念向社区开放,现在看来等待确实是值得的!我最近试验 ...

最新文章

  1. 白话经典算法系列之七 堆与堆排序
  2. XHTML标准下的100%高度问题
  3. 前端将数据转化为弹幕效果的实现方式
  4. 华为鸿蒙二代支持的手机,关于华为鸿蒙,国产厂商中只有2家表示支持
  5. Hlg 1030 排序
  6. 卷积神经网络之迁移学习
  7. 南阳oj-----汉诺塔(三)(stack)
  8. 用74l138实现一个一位全减器_用react实现一个仿ionic button组件
  9. 台达A2/B2伺服电机编码器改功率软件
  10. 服务器系统安装蓝牙驱动,Atheros Bluetooth蓝牙设备驱动
  11. 线上jvm 内存飙高排查
  12. 移动端电商项目开发流程
  13. nb 底特律 外包公司,招人,
  14. 错过了前三次的伟大革命(蒸汽机革命、电气革命、信息革命),GPT-4 你还要错过人工智能AI革命吗!!!
  15. 查找节点下的所有子节点(包括孙节点和隐藏节点)
  16. 收录CTF MISC方向中使用的在线工具网站
  17. 为什么JavaScript中的this如此强大?
  18. 高压电源模块12V升110V/150V/250V/350V稳压直流电源
  19. 超火哔哩哔哩mac客户端你值得拥有
  20. Raw-OS源码分析之软件定时器

热门文章

  1. 用python画八卦图-使用turtle绘制太极八卦图
  2. python能做什么-学了Python都能做什么
  3. java 代码智能提示,如何在Eclipse中设置Java、JavaScript、HTML智能代码提示
  4. vue组件内数值做watch监听,首次监听不到的问题
  5. 【王道计组笔记】储存系统(1):基本概念
  6. 小试ImageMagik——开发篇
  7. 4个常用的计算机应用软件,信息技术应用--常用计算机工具软件4常用工具软件单元四.pdf...
  8. cpu上干硅脂怎么清理_cpu导热硅脂如何涂抹,涂抹的时候需要注意哪些事项
  9. 如何运行wifi服务器,技术:如何通过wifi进行文件传输?
  10. mysql email能索引吗_10、MySQL索引