传统方法(OpenCV)_车道线识别

2024-04-10 13:20

本文主要是介绍传统方法(OpenCV)_车道线识别,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

一、思路

基于OpenCV的库:对视频中的车道线进行识别

1、视频处理:视频读取

2、图像转换:图像转换为灰度图

3、噪声去除:高斯模糊对图像进行去噪,提高边缘检测的准确性

4、边缘检测:Canny算法进行边缘检测,找出图像中边缘

5、区域裁剪:定义ROI(Region of Interest,感兴趣区域),裁剪出这个区域的边缘检测结果

6、直线检测:霍夫变换对ROI区域进行直线检测,找出车道线

7、结果展示:将检测到的车道线画在原图/视频上

二、实施流程:

1. 高斯模糊、Canny边缘检测、霍夫变换

import numpy as np
import cv2blur_ksize = 5  # 高斯模糊核大小
canny_lthreshold = 50  # Canny边缘检测低阈值
canny_hthreshold = 150  # Canny边缘检测高阈值
# 霍夫变换参数
rho = 1     #rho的步长,即直线到图像原点(0,0)点的距离
theta = np.pi / 180     #theta的范围
threshold = 15      #累加器中的值高于它时才认为是一条直线
min_line_length = 40    #线的最短长度,比这个短的都被忽略
max_line_gap = 20      #两条直线之间的最大间隔,小于此值,认为是一条直线

2、定义roi_mask函数,用于保留感兴趣区域,屏蔽掉图像中不需要处理的部分,例如天空、树木等,只保留路面部分,从而提高后续处理的效率和准确性。

#img是输入的图像,verticess是兴趣区的四个点的坐标(三维的数组)
def roi_mask(img, vertices):mask = np.zeros_like(img)   #生成与输入图像相同大小的图像,并使用0填充,图像为黑色mask_color = 255cv2.fillPoly(mask, vertices, mask_color)    #使用白色填充多边形,形成蒙板masked_img = cv2.bitwise_and(img, mask) #img&mask,经过此操作后,兴趣区域以外的部分被蒙住了,只留下兴趣区域的图像return masked_img

3、定义draw_lines函数,用于后续对检测到的车道线进行绘制图线。

# 对图像进行画线
def draw_lines(img, lines, color=[255, 255, 0], thickness=2):for line in lines:for x1, y1, x2, y2 in line:cv2.line(img, (x1, y1), (x2, y2), color, thickness)

4、定义hough_lines函数,用于通过霍夫变换检测出图像中的直线,然后根据这些直线执行draw_lines函数画出车道线

def hough_lines(img, rho, theta, threshold,min_line_len, max_line_gap):lines = cv2.HoughLinesP(img, rho, theta, threshold, np.array([]),minLineLength=min_line_len,maxLineGap=max_line_gap)line_img = np.zeros((img.shape[0], img.shape[1], 3), dtype=np.uint8) #生成绘制直线的绘图板,黑底# draw_lines(line_img, lines)draw_lanes(line_img, lines)return line_img

5、定义draw_lanes函数,用于根据霍夫变换检测到的直线,分类、清理、拟合、绘制出车道线

def draw_lanes(img, lines, color=[255, 255, 0], thickness=8):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 imgclean_lines(left_lines, 0.1)    #弹出左侧不满足斜率要求的直线clean_lines(right_lines, 0.1)   #弹出右侧不满足斜率要求的直线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_vtx = calc_lane_vertices(left_points, 325, img.shape[0])   #拟合点集,生成直线表达式,并计算左侧直线在图像中的两个端点的坐标right_vtx = calc_lane_vertices(right_points, 325, img.shape[0]) #拟合点集,生成直线表达式,并计算右侧直线在图像中的两个端点的坐标cv2.line(img, left_vtx[0], left_vtx[1], color, thickness)   #画出左侧直线cv2.line(img, right_vtx[0], right_vtx[1], color, thickness) #画出右侧直线

6、定义clean_lines函数,用于将斜率不满足要求的直线去除,即不进行绘制

#将不满足斜率要求的直线弹出
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:break

7、定义calc_lane_vertices函数,用于根据给定的点集拟合一条直线,并计算这条直线在图像中的两个端点的坐标

#拟合点集,生成直线表达式,并计算直线在图像中的两个端点的坐标
def calc_lane_vertices(point_list, ymin, ymax):x = [p[0] for p in point_list]  #提取xy = [p[1] for p in point_list]  #提取yfit = np.polyfit(y, x, 1)   #用一次多项式x=a*y+b拟合这些点,fit是(a,b)fit_fn = np.poly1d(fit) #生成多项式对象a*y+bxmin = int(fit_fn(ymin))    #计算这条直线在图像中最左侧的横坐标xmax = int(fit_fn(ymax))     #计算这条直线在图像中最右侧的横坐标return [(xmin, ymin), (xmax, ymax)]

8、编写主函数。首先读取视频并获取每一帧,如果读取帧失败(即视频已经播放完毕),则跳出循环;接着对读取到的帧进行一系列处理,包括转换为灰度图、高斯模糊、Canny边缘检测、生成ROI掩膜、霍夫直线检测等;然后将处理后的图像与原图融合,得到最终的结果;最后显示结果图像,如果按下Esc键,则跳出循环,即关闭所有窗口

if __name__ == '__main__':try:cap = cv2.VideoCapture('./video_1.mp4')if (cap.isOpened()):  # 视频打开成功flag = 1else:flag = 0num = 0if (flag):while (True):ret,frame = cap.read()  # 读取一帧if ret == False:  # 读取帧失败breakgray = cv2.cvtColor(frame, cv2.COLOR_RGB2GRAY)  #图像转换为灰度图blur_gray = cv2.GaussianBlur(gray, (blur_ksize, blur_ksize), 0, 0)  #使用高斯模糊去噪声edges = cv2.Canny(blur_gray, canny_lthreshold, canny_hthreshold)    #使用Canny进行边缘检测roi_vtx = np.array([[(0, frame.shape[0]), (460, 325),(520, 325), (frame.shape[1], frame.shape[0])]]) ##目标区域的四个点坐标,roi_vtx是一个三维的数组roi_edges = roi_mask(edges, roi_vtx)    #对边缘检测的图像生成图像蒙板,去掉不感兴趣的区域,保留兴趣区line_img = hough_lines(roi_edges, rho, theta, threshold,min_line_length, max_line_gap)   #使用霍夫直线检测,并且绘制直线res_img = cv2.addWeighted(frame, 0.8, line_img, 1, 0)   #将处理后的图像与原图做融合cv2.imshow('meet',res_img)if cv2.waitKey(30) & 0xFF == 27:breakcv2.waitKey(0)cv2.destroyAllWindows()except:pass

# 使用环境dlcv/001#1、
import numpy as np
import cv2blur_ksize = 5  # 高斯模糊核大小
canny_lthreshold = 50  # Canny边缘检测低阈值
canny_hthreshold = 150  # Canny边缘检测高阈值
# 霍夫变换参数
rho = 1  # rho的步长,即直线到图像原点(0,0)点的距离
theta = np.pi / 180  # theta的范围
threshold = 15  # 累加器中的值高于它时才认为是一条直线
min_line_length = 40  # 线的最短长度,比这个短的都被忽略
max_line_gap = 20  # 两条直线之间的最大间隔,小于此值,认为是一条直线#2、
#img是输入的图像,verticess是兴趣区的四个点的坐标(三维的数组)
def roi_mask(img, vertices):mask = np.zeros_like(img)   #生成与输入图像相同大小的图像,并使用0填充,图像为黑色mask_color = 255cv2.fillPoly(mask, vertices, mask_color)    #使用白色填充多边形,形成蒙板masked_img = cv2.bitwise_and(img, mask) #img&mask,经过此操作后,兴趣区域以外的部分被蒙住了,只留下兴趣区域的图像return masked_img#3、
# 对图像进行画线
def draw_lines(img, lines, color=[255, 255, 0], thickness=2):for line in lines:for x1, y1, x2, y2 in line:cv2.line(img, (x1, y1), (x2, y2), color, thickness)#4、
def hough_lines(img, rho, theta, threshold,min_line_len, max_line_gap):lines = cv2.HoughLinesP(img, rho, theta, threshold, np.array([]),minLineLength=min_line_len,maxLineGap=max_line_gap)line_img = np.zeros((img.shape[0], img.shape[1], 3), dtype=np.uint8) #生成绘制直线的绘图板,黑底# draw_lines(line_img, lines)draw_lanes(line_img, lines)return line_img#5、
def draw_lanes(img, lines, color=[255, 255, 0], thickness=8):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 imgclean_lines(left_lines, 0.1)  # 弹出左侧不满足斜率要求的直线clean_lines(right_lines, 0.1)  # 弹出右侧不满足斜率要求的直线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_vtx = calc_lane_vertices(left_points, 325, img.shape[0])  # 拟合点集,生成直线表达式,并计算左侧直线在图像中的两个端点的坐标right_vtx = calc_lane_vertices(right_points, 325, img.shape[0])  # 拟合点集,生成直线表达式,并计算右侧直线在图像中的两个端点的坐标cv2.line(img, left_vtx[0], left_vtx[1], color, thickness)  # 画出左侧直线cv2.line(img, right_vtx[0], right_vtx[1], color, thickness)  # 画出右侧直线#6、
#将不满足斜率要求的直线弹出
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:break#7、
#拟合点集,生成直线表达式,并计算直线在图像中的两个端点的坐标
def calc_lane_vertices(point_list, ymin, ymax):x = [p[0] for p in point_list]  #提取xy = [p[1] for p in point_list]  #提取yfit = np.polyfit(y, x, 1)   #用一次多项式x=a*y+b拟合这些点,fit是(a,b)fit_fn = np.poly1d(fit) #生成多项式对象a*y+bxmin = int(fit_fn(ymin))    #计算这条直线在图像中最左侧的横坐标xmax = int(fit_fn(ymax))     #计算这条直线在图像中最右侧的横坐标return [(xmin, ymin), (xmax, ymax)]#8、
if __name__ == '__main__':try:cap = cv2.VideoCapture('1.mp4')if (cap.isOpened()):  # 视频打开成功flag = 1else:flag = 0num = 0if (flag):while (True):ret,frame = cap.read()  # 读取一帧if ret == False:  # 读取帧失败breakgray = cv2.cvtColor(frame, cv2.COLOR_RGB2GRAY)  #图像转换为灰度图blur_gray = cv2.GaussianBlur(gray, (blur_ksize, blur_ksize), 0, 0)  #使用高斯模糊去噪声edges = cv2.Canny(blur_gray, canny_lthreshold, canny_hthreshold)    #使用Canny进行边缘检测roi_vtx = np.array([[(0, frame.shape[0]), (460, 325),(520, 325), (frame.shape[1], frame.shape[0])]]) ##目标区域的四个点坐标,roi_vtx是一个三维的数组roi_edges = roi_mask(edges, roi_vtx)    #对边缘检测的图像生成图像蒙板,去掉不感兴趣的区域,保留兴趣区line_img = hough_lines(roi_edges, rho, theta, threshold,min_line_length, max_line_gap)   #使用霍夫直线检测,并且绘制直线res_img = cv2.addWeighted(frame, 0.8, line_img, 1, 0)   #将处理后的图像与原图做融合cv2.imshow('meet',res_img)if cv2.waitKey(30) & 0xFF == 27:breakcv2.waitKey(0)cv2.destroyAllWindows()except:pass

这篇关于传统方法(OpenCV)_车道线识别的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/891179

相关文章

Nginx设置连接超时并进行测试的方法步骤

《Nginx设置连接超时并进行测试的方法步骤》在高并发场景下,如果客户端与服务器的连接长时间未响应,会占用大量的系统资源,影响其他正常请求的处理效率,为了解决这个问题,可以通过设置Nginx的连接... 目录设置连接超时目的操作步骤测试连接超时测试方法:总结:设置连接超时目的设置客户端与服务器之间的连接

Java判断多个时间段是否重合的方法小结

《Java判断多个时间段是否重合的方法小结》这篇文章主要为大家详细介绍了Java中判断多个时间段是否重合的方法,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下... 目录判断多个时间段是否有间隔判断时间段集合是否与某时间段重合判断多个时间段是否有间隔实体类内容public class D

Python使用国内镜像加速pip安装的方法讲解

《Python使用国内镜像加速pip安装的方法讲解》在Python开发中,pip是一个非常重要的工具,用于安装和管理Python的第三方库,然而,在国内使用pip安装依赖时,往往会因为网络问题而导致速... 目录一、pip 工具简介1. 什么是 pip?2. 什么是 -i 参数?二、国内镜像源的选择三、如何

IDEA编译报错“java: 常量字符串过长”的原因及解决方法

《IDEA编译报错“java:常量字符串过长”的原因及解决方法》今天在开发过程中,由于尝试将一个文件的Base64字符串设置为常量,结果导致IDEA编译的时候出现了如下报错java:常量字符串过长,... 目录一、问题描述二、问题原因2.1 理论角度2.2 源码角度三、解决方案解决方案①:StringBui

Linux使用nload监控网络流量的方法

《Linux使用nload监控网络流量的方法》Linux中的nload命令是一个用于实时监控网络流量的工具,它提供了传入和传出流量的可视化表示,帮助用户一目了然地了解网络活动,本文给大家介绍了Linu... 目录简介安装示例用法基础用法指定网络接口限制显示特定流量类型指定刷新率设置流量速率的显示单位监控多个

Java覆盖第三方jar包中的某一个类的实现方法

《Java覆盖第三方jar包中的某一个类的实现方法》在我们日常的开发中,经常需要使用第三方的jar包,有时候我们会发现第三方的jar包中的某一个类有问题,或者我们需要定制化修改其中的逻辑,那么应该如何... 目录一、需求描述二、示例描述三、操作步骤四、验证结果五、实现原理一、需求描述需求描述如下:需要在

JavaScript中的reduce方法执行过程、使用场景及进阶用法

《JavaScript中的reduce方法执行过程、使用场景及进阶用法》:本文主要介绍JavaScript中的reduce方法执行过程、使用场景及进阶用法的相关资料,reduce是JavaScri... 目录1. 什么是reduce2. reduce语法2.1 语法2.2 参数说明3. reduce执行过程

C#中读取XML文件的四种常用方法

《C#中读取XML文件的四种常用方法》Xml是Internet环境中跨平台的,依赖于内容的技术,是当前处理结构化文档信息的有力工具,下面我们就来看看C#中读取XML文件的方法都有哪些吧... 目录XML简介格式C#读取XML文件方法使用XmlDocument使用XmlTextReader/XmlTextWr

C++初始化数组的几种常见方法(简单易懂)

《C++初始化数组的几种常见方法(简单易懂)》本文介绍了C++中数组的初始化方法,包括一维数组和二维数组的初始化,以及用new动态初始化数组,在C++11及以上版本中,还提供了使用std::array... 目录1、初始化一维数组1.1、使用列表初始化(推荐方式)1.2、初始化部分列表1.3、使用std::

oracle DBMS_SQL.PARSE的使用方法和示例

《oracleDBMS_SQL.PARSE的使用方法和示例》DBMS_SQL是Oracle数据库中的一个强大包,用于动态构建和执行SQL语句,DBMS_SQL.PARSE过程解析SQL语句或PL/S... 目录语法示例注意事项DBMS_SQL 是 oracle 数据库中的一个强大包,它允许动态地构建和执行