news 2026/3/27 2:51:54

毕业设计项目 车道线检测(自动驾驶 机器视觉)

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
毕业设计项目 车道线检测(自动驾驶 机器视觉)

文章目录

  • 0 前言
  • 1 车道线检测
  • 2 目标
  • 3 检测思路
  • 4 代码实现
    • 4.1 视频图像加载
    • 4.2 车道线区域
    • 4.3 区域
    • 4.4 canny 边缘检测
    • 4.5 霍夫变换(Hough transform)
    • 4.6 HoughLinesP 检测原理
      • 4.6.1 定义显示车道线方法
      • 4.6.2 查看探测车道线数据结构
      • 4.6.3 探测车道线
      • 4.6.4 合成
      • 4.6.5 优化

0 前言

无人驾驶技术是机器学习为主的一门前沿领域,在无人驾驶领域中机器学习的各种算法随处可见,今天学长给大家介绍无人驾驶技术中的车道线检测。

🧿选题指导, 项目分享:见文末

1 车道线检测

在无人驾驶领域每一个任务都是相当复杂,看上去无从下手。那么面对这样极其复杂问题,我们解决问题方式从先尝试简化问题,然后由简入难一步一步尝试来一个一个地解决问题。车道线检测在无人驾驶中应该算是比较简单的任务,依赖计算机视觉一些相关技术,通过读取 camera 传入的图像数据进行分析,识别出车道线位置,我想这个对于 lidar 可能是无能为力。所以今天我们就从最简单任务说起,看看有哪些技术可以帮助我们检出车道线。

我们先把问题简化,所谓简化问题就是用一些条件限制来缩小车道线检测的问题。我们先看数据,也就是输入算法是车辆行驶的图像,输出车道线位置。

更多时候我们如何处理一件比较困难任务,可能有时候我们拿到任务时还没有任何思路,不要着急也不用想太多,我们先开始一步一步地做,从最简单的开始做起,随着做就会有思路,同样一些问题也会暴露出来。我们先找一段视频,这段视频是我从网上一个关于车道线检测项目中拿到的,也参考他的思路来做这件事。好现在就开始做这件事,那么最简单的事就是先读取视频,然后将其显示在屏幕以便于调试。

2 目标

检测图像中车道线位置,将车道线信息提供路径规划。

3 检测思路

  • 图像灰度处理
  • 图像高斯平滑处理
  • canny 边缘检测
  • 区域 Mask
  • 霍夫变换
  • 绘制车道线

4 代码实现

4.1 视频图像加载

importcv2importnumpyasnpimportsysimportpygamefrompygame.localsimport*classDisplay(object):def__init__(self,Width,Height):pygame.init()pygame.display.set_caption('Drive Video')self.screen=pygame.display.set_mode((Width,Height),0,32)defpaint(self,draw):self.screen.fill([0,0,0])draw=cv2.transpose(draw)draw=pygame.surfarray.make_surface(draw)self.screen.blit(draw,(0,0))pygame.display.update()if__name__=="__main__":solid_white_right_video_path="test_videos/丹成学长车道线检测.mp4"cap=cv2.VideoCapture(solid_white_right_video_path)Width=int(cap.get(cv2.CAP_PROP_FRAME_WIDTH))Height=int(cap.get(cv2.CAP_PROP_FRAME_HEIGHT))display=Display(Width,Height)whileTrue:ret,draw=cap.read()draw=cv2.cvtColor(draw,cv2.COLOR_BGR2RGB)ifret==False:breakdisplay.paint(draw)foreventinpygame.event.get():ifevent.type==QUIT:sys.exit()

上面代码学长就不多说了,默认大家对 python 是有所了解,关于如何使用 opencv 读取图片网上代码示例也很多,大家一看就懂。这里因为我用的是 mac 有时候显示视频图像可能会有些问题,所以我们用 pygame 来显示 opencv 读取图像。这个大家根据自己实际情况而定吧。值得说一句的是 opencv 读取图像是 BGR 格式,要想在 pygame 中正确显示图像就需要将 BGR 转换为 RGB 格式。

4.2 车道线区域

现在这个区域是我们根据观测图像绘制出来,

defcolor_select(img,red_threshold=200,green_threshold=200,blue_threshold=200):ysize,xsize=img.shape[:2]color_select=np.copy(img)rgb_threshold=[red_threshold,green_threshold,blue_threshold]thresholds=(img[:,:,0]<rgb_threshold[0])\|(img[:,:,1]<rgb_threshold[1])\|(img[:,:,2]<rgb_threshold[2])color_select[thresholds]=[0,0,0]returncolor_select

效果如下:

4.3 区域

我们要检测车道线位置相对比较固定,通常出现车的前方,所以我们通过绘制,也就是仅检测我们关心区域。通过创建 mask 来过滤掉那些不关心的区域保留关心区域。

4.4 canny 边缘检测

有关边缘检测也是计算机视觉。首先利用梯度变化来检测图像中的边,如何识别图像的梯度变化呢,答案是卷积核。卷积核是就是不连续的像素上找到梯度变化较大位置。我们知道 sobal 核可以很好检测边缘,那么 canny 就是 sobal 核检测上进行优化。

# 示例代码,作者丹成学长:Q746876041defcanny_edge_detect(img):gray=cv2.cvtColor(img,cv2.COLOR_RGB2GRAY)kernel_size=5blur_gray=cv2.GaussianBlur(gray,(kernel_size,kernel_size),0)low_threshold=180high_threshold=240edges=cv2.Canny(blur_gray,low_threshold,high_threshold)returnedges

4.5 霍夫变换(Hough transform)

霍夫变换是将 x 和 y 坐标系中的线映射表示在霍夫空间的点(m,b)。所以霍夫变换实际上一种由繁到简(类似降维)的操作。当使用 canny 进行边缘检测后图像可以交给霍夫变换进行简单图形(线、圆)等的识别。这里用霍夫变换在 canny 边缘检测结果中寻找直线。

# 示例代码,作者丹成学长:Q746876041mask=np.zeros_like(edges)ignore_mask_color=255# 获取图片尺寸imshape=img.shape# 定义 mask 顶点vertices=np.array([[(0,imshape[0]),(450,290),(490,290),(imshape[1],imshape[0])]],dtype=np.int32)# 使用 fillpoly 来绘制 maskcv2.fillPoly(mask,vertices,ignore_mask_color)masked_edges=cv2.bitwise_and(edges,mask)# 定义Hough 变换的参数rho=1theta=np.pi/180threshold=2min_line_length=4# 组成一条线的最小像素数max_line_gap=5# 可连接线段之间的最大像素间距# 创建一个用于绘制车道线的图片line_image=np.copy(img)*0# 对于 canny 边缘检测结果应用 Hough 变换# 输出“线”是一个数组,其中包含检测到的线段的端点lines=cv2.HoughLinesP(masked_edges,rho,theta,threshold,np.array([]),min_line_length,max_line_gap)# 遍历“线”的数组来在 line_image 上绘制forlineinlines:forx1,y1,x2,y2inline:cv2.line(line_image,(x1,y1),(x2,y2),(255,0,0),10)color_edges=np.dstack((edges,edges,edges))importmathimportcv2importnumpyasnp""" Gray Scale Gaussian Smoothing Canny Edge Detection Region Masking Hough Transform Draw Lines [Mark Lane Lines with different Color] """classSimpleLaneLineDetector(object):def__init__(self):passdefdetect(self,img):# 图像灰度处理gray_img=self.grayscale(img)print(gray_img)#图像高斯平滑处理smoothed_img=self.gaussian_blur(img=gray_img,kernel_size=5)#canny 边缘检测canny_img=self.canny(img=smoothed_img,low_threshold=180,high_threshold=240)#区域 Maskmasked_img=self.region_of_interest(img=canny_img,vertices=self.get_vertices(img))#霍夫变换houghed_lines=self.hough_lines(img=masked_img,rho=1,theta=np.pi/180,threshold=20,min_line_len=20,max_line_gap=180)# 绘制车道线output=self.weighted_img(img=houghed_lines,initial_img=img,alpha=0.8,beta=1.,gamma=0.)returnoutputdefgrayscale(self,img):returncv2.cvtColor(img,cv2.COLOR_RGB2GRAY)defcanny(self,img,low_threshold,high_threshold):returncv2.Canny(img,low_threshold,high_threshold)defgaussian_blur(self,img,kernel_size):returncv2.GaussianBlur(img,(kernel_size,kernel_size),0)defregion_of_interest(self,img,vertices):mask=np.zeros_like(img)iflen(img.shape)>2:channel_count=img.shape[2]ignore_mask_color=(255,)*channel_countelse:ignore_mask_color=255cv2.fillPoly(mask,vertices,ignore_mask_color)masked_image=cv2.bitwise_and(img,mask)returnmasked_imagedefdraw_lines(self,img,lines,color=[255,0,0],thickness=10):forlineinlines:forx1,y1,x2,y2inline:cv2.line(img,(x1,y1),(x2,y2),color,thickness)defslope_lines(self,image,lines):img=image.copy()poly_vertices=[]order=[0,1,3,2]left_lines=[]right_lines=[]forlineinlines:forx1,y1,x2,y2inline:ifx1==x2:passelse:m=(y2-y1)/(x2-x1)c=y1-m*x1ifm<0:left_lines.append((m,c))elifm>=0:right_lines.append((m,c))left_line=np.mean(left_lines,axis=0)right_line=np.mean(right_lines,axis=0)forslope,interceptin[left_line,right_line]:rows,cols=image.shape[:2]y1=int(rows)y2=int(rows*0.6)x1=int((y1-intercept)/slope)x2=int((y2-intercept)/slope)poly_vertices.append((x1,y1))poly_vertices.append((x2,y2))self.draw_lines(img,np.array([[[x1,y1,x2,y2]]]))poly_vertices=[poly_vertices[i]foriinorder]cv2.fillPoly(img,pts=np.array([poly_vertices],'int32'),color=(0,255,0))returncv2.addWeighted(image,0.7,img,0.4,0.)defhough_lines(self,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)line_img=self.slope_lines(line_img,lines)returnline_imgdefweighted_img(self,img,initial_img,alpha=0.1,beta=1.,gamma=0.):lines_edges=cv2.addWeighted(initial_img,alpha,img,beta,gamma)returnlines_edgesdefget_vertices(self,image):rows,cols=image.shape[:2]bottom_left=[cols*0.15,rows]top_left=[cols*0.45,rows*0.6]bottom_right=[cols*0.95,rows]top_right=[cols*0.55,rows*0.6]ver=np.array([[bottom_left,top_left,top_right,bottom_right]],dtype=np.int32)returnver

4.6 HoughLinesP 检测原理

接下来进入代码环节,学长详细给大家解释一下 HoughLinesP 参数的含义以及如何使用。

lines=cv2.HoughLinesP(cropped_image,2,np.pi/180,100,np.array([]),minLineLength=40,maxLineGap=5)
  • 第一参数是我们要检查的图片 Hough accumulator 数组
  • 第二个和第三个参数用于定义我们 Hough 坐标如何划分 bin,也就是小格的精度。我们通过曲线穿过 bin 格子来进行投票,我们根据投票数量来决定 p 和 theta 的值。2 表示我们小格宽度以像素为单位 。


我们可以通过下图划分小格,只要曲线穿过就会对小格进行投票,我们记录投票数量,记录最多的作为参数


  • 如果定义尺寸过大也就失去精度,如果定义格子尺寸过小虽然精度上来了,这样也会打来增长计算时间。
  • 接下来参数 100 表示我们投票为 100 以上的线才是符合要求是我们要找的线。也就是在 bin 小格子需要有 100 以上线相交于此才是我们要找的参数。
  • minLineLength 给 40 表示我们检查线长度不能小于 40 pixel
  • maxLineGap=5 作为线间断不能大于 5 pixel

4.6.1 定义显示车道线方法

defdisply_lines(image,lines):pass

通过定义函数将找到的车道线显示出来。

line_image=disply_lines(lane_image,lines)

4.6.2 查看探测车道线数据结构

defdisply_lines(image,lines):line_image=np.zeros_like(image)iflinesisnotNone:forlineinlines:print(line)

先定义一个尺寸大小和原图一样的矩阵用于绘制查找到车道线,我们先判断一下是否已经找到车道线,lines 返回值应该不为 None 是一个矩阵,我们可以简单地打印一下看一下效果

[[704418927641]][[704426791516]][[320703445494]][[585301663381]][[630341670383]]

4.6.3 探测车道线

看数据结构[[x1,y1,x2,y2]] 的二维数组,这就需要我们转换一下为一维数据[x1,y1,x2,y2]

defdisply_lines(image,lines):line_image=np.zeros_like(image)iflinessisnotNone:forlineinlines:x1,y1,x2,y2=line.reshape(4)cv2.line(line_image,(x1,y1),(x2,y2),(255,0,0),10)returnline_image line_image=disply_lines(lane_image,lines)cv2.imshow('result',line_image)

4.6.4 合成

有关合成图片我们是将两张图片通过给一定权重进行叠加合成。

4.6.5 优化

探测到的车道线还是不够平滑,我们需要优化,基本思路就是对这些直线的斜率和截距取平均值然后将所有探测出点绘制到一条直线上。

defaverage_slope_intercept(image,lines):left_fit=[]right_fit=[]forlineinlines:x1,y1,x2,y2=line.reshape(4)parameters=np.polyfit((x1,x2),(y1,y2),1)print(parameters)

这里学长定义两个数组 left_fit 和 right_fit 分别用于存放左右两侧车道线的点,我们打印一下 lines 的斜率和截距,通过 numpy 提供 polyfit 方法输入两个点我们就可以得到通过这些点的直线的斜率和截距。

[1.-286.][1.03448276-302.27586207][-1.6721238.04][1.02564103-299.
[1.02564103-299.defaverage_slope_intercept(image,lines):left_fit=[]right_fit=[]forlineinlines:x1,y1,x2,y2=line.reshape(4)parameters=np.polyfit((x1,x2),(y1,y2),1)# print(parameters)slope=parameters[0]intercept=parameters[1]ifslope<0:left_fit.append((slope,intercept))else:right_fit.append((slope,intercept))print(left_fit)print(right_fit)

我们输出一下图片大小,我们图片是以其左上角作为原点 0 ,0 来开始计算的,所以我们直线从图片底部 700 多向上绘制我们无需绘制全部可以截距一部分即可。

defmake_coordinates(image,line_parameters):slope,intercept=line_parameters y1=image.shape[0]y2=int(y1*(3/5))x1=int((y1-intercept)/slope)x2=int((y2-intercept)/slope)# print(image.shape)returnnp.array([x1,y1,x2,y2])

所以直线开始和终止我们给定 y1,y2 然后通过方程的斜率和截距根据y 算出 x。

averaged_lines=average_slope_intercept(lane_image,lines);line_image=disply_lines(lane_image,averaged_lines)combo_image=cv2.addWeighted(lane_image,0.8,line_image,1,1,1)cv2.imshow('result',combo_image)

🧿 项目分享:大家可自取用于参考学习,获取方式见文末!

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/3/27 5:44:54

Dify API接口文档解读:实现外部系统集成

Dify API 接口解读&#xff1a;打通外部系统与 AI 应用的关键桥梁 在企业纷纷拥抱大模型的今天&#xff0c;一个现实问题摆在面前&#xff1a;如何让非 AI 专业的开发团队也能快速为业务系统“注入智能”&#xff1f;直接调用大模型 API 看似简单&#xff0c;但面对提示工程、…

作者头像 李华
网站建设 2026/3/25 11:24:29

LCD12864并行驱动:超详细版时序控制解析

深入LCD12864并行驱动&#xff1a;从时序到实战的完整掌控你有没有遇到过这样的情况&#xff1f;明明代码写得一丝不苟&#xff0c;引脚连接也一一核对无误&#xff0c;可LCD12864就是不亮、乱码、或者只显示半屏。更糟的是&#xff0c;有时候它“偶然”能工作&#xff0c;换个…

作者头像 李华
网站建设 2026/3/23 15:29:23

13、项目商业视角规划:成功的关键要素

项目商业视角规划:成功的关键要素 1. 商业规划的重要性 商业规划是项目规划的首要阶段,此阶段主要探索并明确需要解决的问题。有效的需求是一个约束参数框架,它能指导决策和设计。商业需求和目标是构建框架需求的起点,尽管项目最终会聚焦于用户需求,但满足用户需求始终是…

作者头像 李华
网站建设 2026/3/17 9:46:10

14、产品开发的策略与用户定位

产品开发的策略与用户定位 在产品开发过程中,有许多关键的策略和方法能够帮助我们打造出更具价值、更贴合用户需求的产品。下面将为大家详细介绍这些重要的内容。 1. 帕累托原则的应用 帕累托原则,也就是广为人知的“80/20 规则”,是一个在产品开发中极具价值的认知工具。…

作者头像 李华