热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

python图像识别马路_OpenCVPython:车道检测

任务:一共要完成两项任务:1.在所提供的公路图片上检测出车道线并标记2.在所提供的公路视频上检测出车道线并标记方案:要检测出当前车道&#x

任务:

一共要完成两项任务:

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

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

方案:

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

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

1. 灰度化

2. 高斯模糊

3. Canny边缘检测

4. 不规则ROI区域截取

5. 霍夫直线检测

6. 车道计算

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

图像预处理

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

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

import cv2

import numpyasnp

# 高斯滤波核大小

blur_ksize= 5# Canny边缘检测高低阈值

canny_lth= 50canny_hth= 150def process_an_image(img):

#1. 灰度化、滤波和Canny

gray=cv2.cvtColor(img, cv2.COLOR_RGB2GRAY)

blur_gray= cv2.GaussianBlur(gray, (blur_ksize, blur_ksize), 1)

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(0)

边缘检测结果图

ROI截取

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

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

def process_an_image(img):

#1. 灰度化、滤波和Canny

#2. 标记四个坐标点用于ROI截取

rows, cols=edges.shape

points= 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)

cv2.fillPoly(mask, corner_points,255)

masked_img=cv2.bitwise_and(img, mask)return masked_img

结果图 roi_edges如下:

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

霍夫直线提取

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

# 霍夫变换参数

rho= 1theta= np.pi / 180threshold= 15min_line_len= 40max_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= cv2.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) # 画出直线检测结果returndrawing, lines

def draw_lines(img, lines, color=[0, 0, 255], thickness=1):for line inlines:for x1, y1, x2, y2 inline:

cv2.line(img, (x1, y1), (x2, y2), color, thickness)

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

霍夫变换结果图

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

车道计算

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

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

其中左车道(斜率 <0)&#xff0c;右车道(斜率 > 0)。原因如下图所示&#xff1a;

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

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

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

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

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

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

我们的目标是拟合出这样一条直线&#xff1a;

使得误差平方和最小&#xff1a;

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

def process_an_image(img):

#1. 灰度化、滤波和Canny

#2. 标记四个坐标点用于ROI截取

#3. 霍夫直线提取

#4. 车道拟合计算

draw_lanes(drawing, lines)

#5. 最终将结果合在原图上

result&#61; cv2.addWeighted(img, 0.9, drawing, 0.2, 0)returnresult

def draw_lanes(img, lines, color&#61;[255, 0, 0], thickness&#61;8):

# a. 划分左右车道

left_lines, right_lines&#61;[], []for line inlines:for x1, y1, x2, y2 inline:

k&#61; (y2 - y1) / (x2 -x1)if k <0:

left_lines.append(line)else:

right_lines.append(line)if (len(left_lines) <&#61; 0 or len(right_lines) <&#61; 0):return# b. 清理异常数据

clean_lines(left_lines,0.1)

clean_lines(right_lines,0.1)

# c. 得到左右车道线点的集合&#xff0c;拟合直线

left_points&#61; [(x1, y1) for line in left_lines for x1, y1, x2, y2 inline]

left_points&#61; left_points &#43; [(x2, y2) for line in left_lines for x1, y1, x2, y2 inline]

right_points&#61; [(x1, y1) for line in right_lines for x1, y1, x2, y2 inline]

right_points&#61; right_points &#43; [(x2, y2) for line in right_lines for x1, y1, x2, y2 inline]

left_results&#61; least_squares_fit(left_points, 325, img.shape[0])

right_results&#61; least_squares_fit(right_points, 325, img.shape[0])

# 注意这里点的顺序

vtxs&#61; np.array([[left_results[1], left_results[0], right_results[0], right_results[1]]])

# d. 填充车道区域

cv2.fillPoly(img, vtxs, (0, 255, 0))

# 或者只画车道线

# cv2.line(img, left_results[0], left_results[1], (0, 0, 255), thickness)

# cv2.line(img, right_results[0], right_results[1], (0, 0, 255), thickness)

def clean_lines(lines, threshold):

# 迭代计算斜率均值&#xff0c;排除掉与差值差异较大的数据

slope&#61; [(y2 - y1) / (x2 - x1) for line in lines for x1, y1, x2, y2 inline]while len(lines) > 0:

mean&#61;np.mean(slope)

diff&#61; [abs(s - mean) for s inslope]

idx&#61;np.argmax(diff)if diff[idx] >threshold:

slope.pop(idx)

lines.pop(idx)else:breakdef least_squares_fit(point_list, ymin, ymax):

# 最小二乘法拟合

x&#61; [p[0] for p inpoint_list]

y&#61; [p[1] for p inpoint_list]

# polyfit第三个参数为拟合多项式的阶数&#xff0c;所以1代表线性

fit&#61; np.polyfit(y, x, 1)

fit_fn&#61;np.poly1d(fit) # 获取拟合的结果

xmin&#61; int(fit_fn(ymin))

xmax&#61; int(fit_fn(ymax))return [(xmin, ymin), (xmax, ymax)]

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

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

视频处理

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

pip install moviepy

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

只需要在开头导入 moviepy&#xff0c;然后主函数改掉就可以了&#xff0c;其余代码不需要修改&#xff1a;

# 开头导入moviepyfrommoviepy.editor import VideoFileClip

# 主函数更改为&#xff1a;if __name__ &#61;&#61; "__main__":

output&#61; &#39;test_videos/output.mp4&#39;clip&#61; VideoFileClip("test_videos/cv2_white_lane.mp4")

out_clip&#61;clip.fl_image(process_an_image)

out_clip.write_videofile(output, audio&#61;False)



推荐阅读
  • Ihavetwomethodsofgeneratingmdistinctrandomnumbersintherange[0..n-1]我有两种方法在范围[0.n-1]中生 ... [详细]
  • 微信公众号推送模板40036问题
    返回码错误码描述说明40001invalidcredential不合法的调用凭证40002invalidgrant_type不合法的grant_type40003invalidop ... [详细]
  • 在对WordPress Duplicator插件0.4.4版本的安全评估中,发现其存在跨站脚本(XSS)攻击漏洞。此漏洞可能被利用进行恶意操作,建议用户及时更新至最新版本以确保系统安全。测试方法仅限于安全研究和教学目的,使用时需自行承担风险。漏洞编号:HTB23162。 ... [详细]
  • 服务器部署中的安全策略实践与优化
    服务器部署中的安全策略实践与优化 ... [详细]
  • 本文详细介绍了在 CentOS 7 系统中配置 fstab 文件以实现开机自动挂载 NFS 共享目录的方法,并解决了常见的配置失败问题。 ... [详细]
  • 本文介绍了在 Java 编程中遇到的一个常见错误:对象无法转换为 long 类型,并提供了详细的解决方案。 ... [详细]
  • 本文回顾了作者初次接触Unicode编码时的经历,并详细探讨了ASCII、ANSI、GB2312、UNICODE以及UTF-8和UTF-16编码的区别和应用场景。通过实例分析,帮助读者更好地理解和使用这些编码。 ... [详细]
  • 单片微机原理P3:80C51外部拓展系统
      外部拓展其实是个相对来说很好玩的章节,可以真正开始用单片机写程序了,比较重要的是外部存储器拓展,81C55拓展,矩阵键盘,动态显示,DAC和ADC。0.IO接口电路概念与存 ... [详细]
  • window下的python安装插件,Go语言社区,Golang程序员人脉社 ... [详细]
  • javascript分页类支持页码格式
    前端时间因为项目需要,要对一个产品下所有的附属图片进行分页显示,没考虑ajax一张张请求,所以干脆一次性全部把图片out,然 ... [详细]
  • 如何在Java中使用DButils类
    这期内容当中小编将会给大家带来有关如何在Java中使用DButils类,文章内容丰富且以专业的角度为大家分析和叙述,阅读完这篇文章希望大家可以有所收获。D ... [详细]
  • Python 3 Scrapy 框架执行流程详解
    本文详细介绍了如何在 Python 3 环境下安装和使用 Scrapy 框架,包括常用命令和执行流程。Scrapy 是一个强大的 Web 抓取框架,适用于数据挖掘、监控和自动化测试等多种场景。 ... [详细]
  • 本文将详细介绍如何在Mac上安装Jupyter Notebook,并提供一些常见的问题解决方法。通过这些步骤,您将能够顺利地在Mac上运行Jupyter Notebook。 ... [详细]
  • 在2019年寒假强化训练中,我们深入探讨了二分算法的理论与实践应用。问题A聚焦于使用递归方法实现二分查找。具体而言,给定一个已按升序排列且无重复元素的数组,用户需从键盘输入一个数值X,通过二分查找法判断该数值是否存在于数组中。输入的第一行为一个正整数,表示数组的长度。这一训练不仅强化了对递归算法的理解,还提升了实际编程能力。 ... [详细]
  • 在 Linux 环境下,多线程编程是实现高效并发处理的重要技术。本文通过具体的实战案例,详细分析了多线程编程的关键技术和常见问题。文章首先介绍了多线程的基本概念和创建方法,然后通过实例代码展示了如何使用 pthreads 库进行线程同步和通信。此外,还探讨了多线程程序中的性能优化技巧和调试方法,为开发者提供了宝贵的实践经验。 ... [详细]
author-avatar
罗丝012
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有