首页 > 其他分享 >Opencv项目实战:25 车道线检测

Opencv项目实战:25 车道线检测

时间:2024-12-26 23:01:29浏览次数:6  
标签:25 车道 img Laneline self cv2 Opencv line

1、项目介绍

本项目的主要目标是通过视频输入流实时检测并显示车道线。通过在GUI界面中集成OpenCV图像处理功能,用户可以加载视频,启动检测过程,并在窗口中查看每帧图像的处理结果。

2、效果演示

3、项目搭建

文件夹videofiles放置视频检测文件,lane.ui为PyQt设计的ui文件,lane.py为lane.ui转换后的代码文件,lanedet.py为车道线检测的逻辑主代码,run.py为继承自lane.py的界面代码。

4、原理讲解

车道线检测模块的核心目标是从输入的视频流或图像中准确地检测出车道线,并将其实时标记在视频中。整个检测过程涉及多个步骤,包括图像预处理、区域选择、车道线检测及结果绘制。

图像预处理

车道线检测的第一步是图像的预处理。通过高斯模糊对图像进行去噪,减少图像中的噪声对后续处理的影响。之后,将图像转换为灰度图,以便于后续的边缘检测。边缘检测采用了Canny算法,这是一个经典的边缘检测方法,能够有效提取出图像中显著的边缘信息。通过设置适当的低、高阈值,Canny算法可以有效地提取出车道线的轮廓,作为后续处理的输入。

def Preprocess_images(image, ksize, sigma=None, low_threshold=50, high_threshold=250):
    if sigma is None:
        sigma = 0.3 * ((ksize - 1) * 0.5 - 1) + 0.8
    gaussianblur_img = cv2.GaussianBlur(image, ksize=(ksize, ksize), sigmaX=sigma)
    gray_img = cv2.cvtColor(gaussianblur_img, cv2.COLOR_BGR2GRAY)
    canny_img = cv2.Canny(gray_img, low_threshold, high_threshold)

    return canny_img

区域选择

车道线通常出现在图像的特定区域,为了提高检测效率和准确性,采用了感兴趣区域(ROI)的技术。通过设定一个多边形区域,我们限制了车道线检测只在图像的下半部分进行,从而减少了不相关区域的干扰。在此区域内,只对车道线进行进一步处理,提升了处理速度。

def Roi_Mask(gray_image):
    mask = np.zeros_like(gray_image)
    height, width = gray_image.shape
    # 如果效果不是很好, 可以调整此处的比例
    left_bottom = [0, height]
    right_bottom = [width, height]
    left_top = [width / 3, height / 1.5]
    right_top = [width / 3 * 2, height / 1.5]

    vertices = np.array([left_top,
                        right_top,
                        right_bottom,
                        left_bottom
                         ], np.int32)

    cv2.fillPoly(mask, [vertices], (255, ))
    masked_image = cv2.bitwise_and(gray_image, mask)
    return masked_image

如果你发现在你自己的视频文件的效果不是很好,可以调制vertices这里的比例,或者修改canny阈值。

车道线检测

车道线检测采用了霍夫变换的方法,这是检测直线的一种经典算法。在此过程中,首先对ROI区域的图像进行霍夫变换,提取出图像中的直线。然后,根据直线的斜率对其进行分类:斜率较小的直线为右车道线,斜率较大的直线为左车道线。接下来,使用最小二乘法对每个车道线的端点进行拟合,从而得到更加平滑且准确的车道线。

结果绘制与显示

通过拟合得到的左右车道线在图像中进行绘制。为便于用户查看,车道线用不同颜色的线条标注在视频帧上。此外,为了进一步增强视觉效果,可以在车道线的区域填充绿色透明多边形,以突出显示车道线区域。检测到的车道线在实时视频中显示,提供了实时反馈,方便用户查看车道线检测的效果。

lanedet.py

import cv2
import time
import numpy as np

def Preprocess_images(image, ksize, sigma=None, low_threshold=50, high_threshold=250):
    if sigma is None:
        sigma = 0.3 * ((ksize - 1) * 0.5 - 1) + 0.8
    gaussianblur_img = cv2.GaussianBlur(image, ksize=(ksize, ksize), sigmaX=sigma)
    gray_img = cv2.cvtColor(gaussianblur_img, cv2.COLOR_BGR2GRAY)
    canny_img = cv2.Canny(gray_img, low_threshold, high_threshold)

    return canny_img

def Roi_Mask(gray_image):
    mask = np.zeros_like(gray_image)
    height, width = gray_image.shape
    # 如果效果不是很好, 可以调整此处的比例
    left_bottom = [0, height]
    right_bottom = [width, height]
    left_top = [width / 3, height / 1.5]
    right_top = [width / 3 * 2, height / 1.5]

    vertices = np.array([left_top,
                        right_top,
                        right_bottom,
                        left_bottom
                         ], np.int32)

    cv2.fillPoly(mask, [vertices], (255, ))
    masked_image = cv2.bitwise_and(gray_image, mask)
    return masked_image

def draw_lines(img, lines, color=(0, 255, 255), thickness=2, is_draw_polygon=True):
    left_lines_x = []
    left_lines_y = []
    right_lines_x = []
    right_lines_y = []
    line_y_max = 0
    line_y_min = 999

    try:
        for line in lines:
            for x1, y1, x2, y2 in line:
                if y1 > line_y_max:
                    line_y_max = y1
                if y2 > line_y_max:
                    line_y_max = y2
                if y1 < line_y_min:
                    line_y_min = y1
                if y2 < line_y_min:
                    line_y_min = y2

                k = ( (y2 - y1) / (1e-6 + x2 - x1))

                if k < -0.3:
                    left_lines_x.append(x1)
                    left_lines_y.append(y1)
                    left_lines_x.append(x2)
                    left_lines_y.append(y2)
                elif k > 0.3:
                    right_lines_x.append(x1)
                    right_lines_y.append(y1)
                    right_lines_x.append(x2)
                    right_lines_y.append(y2)
        # 最小二乘直线拟合
        left_line_k, left_line_b = np.polyfit(left_lines_x, left_lines_y, 1)
        right_line_k, right_line_b = np.polyfit(right_lines_x, right_lines_y, 1)

        cv2.line(img,
                 (int((line_y_max - left_line_b) / left_line_k), line_y_max),
                 (int((line_y_min - left_line_b) / left_line_k), line_y_min),
                 color, thickness)
        cv2.line(img,
                 (int((line_y_max - right_line_b) / right_line_k), line_y_max),
                 (int((line_y_min - right_line_b) / right_line_k), line_y_min),
                 color, thickness)
        zero_img = np.zeros((img.shape), dtype=np.uint8)
        if is_draw_polygon:
            polygon = np.array([
                [int((line_y_max - left_line_b) / left_line_k), line_y_max],
                [int((line_y_max - right_line_b) / right_line_k), line_y_max],
                [int((line_y_min - right_line_b) / right_line_k), line_y_min],
                [int((line_y_min - left_line_b) / left_line_k), line_y_min]
            ])
            cv2.fillConvexPoly(zero_img, polygon, color=(0, 255, 0))

        img = cv2.addWeighted(img, 1, zero_img, beta = 0.2, gamma=0)

    except Exception:
        pass

    return img


class FPS:
    def __init__(self):
        self.pTime = time.time()

    def update(self, img=None, pos=(20, 50), color=(255, 0, 0), scale=3, thickness=3):
        cTime = time.time()
        try:
            fps = 1 / (cTime - self.pTime)
            self.pTime = cTime
            if img is None:
                return fps
            else:
                cv2.putText(img, f'FPS: {int(fps)}', pos, cv2.FONT_HERSHEY_PLAIN,
                            scale, color, thickness)
                return fps, img
        except:
            return 0

def dectet_lane_line(video_path, ksize=3, end_k=27, is_draw_polygon=True):
    cap = cv2.VideoCapture(video_path)
    fps_counter = FPS()
    while True:
        ret, img = cap.read()
        video = Preprocess_images(img, ksize=ksize)
        roi_image = Roi_Mask(video)
        line_img = cv2.HoughLinesP(roi_image,
                                   rho=1,
                                   theta=np.pi / 180,
                                   threshold=15,
                                   minLineLength=40,
                                   maxLineGap=20)

        img = draw_lines(img, line_img, is_draw_polygon=is_draw_polygon)
        fps, img = fps_counter.update(img)
        cv2.imshow('frame', img)

        k = cv2.waitKey(1)
        if k == end_k:
            break

    cap.release()
    cv2.destroyAllWindows()

def dectet_lane_line_ui(
        img,
        ksize,
        minVotesForLine=15,
        minLineLength=40,
        maxLineGap=20,
        is_draw_polygon=True):
    video = Preprocess_images(img, ksize=ksize)
    roi_image = Roi_Mask(video)
    line_img = cv2.HoughLinesP(roi_image,
                               rho=1,
                               theta=np.pi / 180,
                               threshold=minVotesForLine,
                               minLineLength=minLineLength,
                               maxLineGap=maxLineGap)
    img = draw_lines(img, line_img, is_draw_polygon=is_draw_polygon)

    return img

if __name__=="__main__":
    video_path = r"videofiles/video1.mp4"
    dectet_lane_line(video_path)

lane.py 

# -*- coding: utf-8 -*-

# Form implementation generated from reading ui file 'lane.ui'
#
# Created by: PyQt5 UI code generator 5.15.9
#
# WARNING: Any manual changes made to this file will be lost when pyuic5 is
# run again.  Do not edit this file unless you know what you are doing.


from PyQt5 import QtCore, QtGui, QtWidgets


class Ui_Laneline(object):
    def setupUi(self, Laneline):
        Laneline.setObjectName("Laneline")
        Laneline.resize(1043, 708)
        self.centralwidget = QtWidgets.QWidget(Laneline)
        self.centralwidget.setObjectName("centralwidget")
        self.layoutWidget = QtWidgets.QWidget(self.centralwidget)
        self.layoutWidget.setGeometry(QtCore.QRect(30, 90, 991, 561))
        self.layoutWidget.setObjectName("layoutWidget")
        self.gridLayout_2 = QtWidgets.QGridLayout(self.layoutWidget)
        self.gridLayout_2.setContentsMargins(0, 0, 0, 0)
        self.gridLayout_2.setObjectName("gridLayout_2")
        self.RSettingFm = QtWidgets.QFrame(self.layoutWidget)
        self.RSettingFm.setMaximumSize(QtCore.QSize(16777215, 200))
        self.RSettingFm.setFrameShape(QtWidgets.QFrame.Box)
        self.RSettingFm.setFrameShadow(QtWidgets.QFrame.Plain)
        self.RSettingFm.setObjectName("RSettingFm")
        self.RunBt = QtWidgets.QPushButton(self.RSettingFm)
        self.RunBt.setGeometry(QtCore.QRect(120, 80, 93, 28))
        self.RunBt.setObjectName("RunBt")
        self.gridLayout_2.addWidget(self.RSettingFm, 1, 2, 1, 1)
        self.DispLb = QtWidgets.QLabel(self.layoutWidget)
        self.DispLb.setEnabled(True)
        self.DispLb.setMaximumSize(QtCore.QSize(800, 800))
        self.DispLb.setFrameShape(QtWidgets.QFrame.Box)
        self.DispLb.setFrameShadow(QtWidgets.QFrame.Plain)
        self.DispLb.setText("")
        self.DispLb.setObjectName("DispLb")
        self.gridLayout_2.addWidget(self.DispLb, 0, 1, 2, 1)
        self.SettingFm = QtWidgets.QFrame(self.layoutWidget)
        self.SettingFm.setMaximumSize(QtCore.QSize(350, 500))
        self.SettingFm.setFrameShape(QtWidgets.QFrame.Box)
        self.SettingFm.setFrameShadow(QtWidgets.QFrame.Plain)
        self.SettingFm.setObjectName("SettingFm")
        self.PFilePathLiEd = QtWidgets.QLineEdit(self.SettingFm)
        self.PFilePathLiEd.setGeometry(QtCore.QRect(100, 30, 161, 31))
        self.PFilePathLiEd.setObjectName("PFilePathLiEd")
        self.PFilePathLab = QtWidgets.QLabel(self.SettingFm)
        self.PFilePathLab.setGeometry(QtCore.QRect(20, 40, 81, 16))
        font = QtGui.QFont()
        font.setFamily("宋体")
        font.setPointSize(10)
        self.PFilePathLab.setFont(font)
        self.PFilePathLab.setObjectName("PFilePathLab")
        self.PFilePathBt = QtWidgets.QPushButton(self.SettingFm)
        self.PFilePathBt.setGeometry(QtCore.QRect(270, 30, 71, 28))
        self.PFilePathBt.setObjectName("PFilePathBt")
        self.gridLayout_2.addWidget(self.SettingFm, 0, 2, 1, 1)
        self.TitleLab = QtWidgets.QLabel(self.centralwidget)
        self.TitleLab.setGeometry(QtCore.QRect(310, 20, 451, 61))
        self.TitleLab.setMaximumSize(QtCore.QSize(500, 100))
        font = QtGui.QFont()
        font.setFamily("宋体")
        font.setPointSize(24)
        self.TitleLab.setFont(font)
        self.TitleLab.setObjectName("TitleLab")
        Laneline.setCentralWidget(self.centralwidget)
        self.menubar = QtWidgets.QMenuBar(Laneline)
        self.menubar.setGeometry(QtCore.QRect(0, 0, 1043, 26))
        self.menubar.setObjectName("menubar")
        self.menu = QtWidgets.QMenu(self.menubar)
        self.menu.setObjectName("menu")
        Laneline.setMenuBar(self.menubar)
        self.statusbar = QtWidgets.QStatusBar(Laneline)
        self.statusbar.setObjectName("statusbar")
        Laneline.setStatusBar(self.statusbar)
        self.laneaction = QtWidgets.QAction(Laneline)
        self.laneaction.setObjectName("laneaction")
        self.yinaction = QtWidgets.QAction(Laneline)
        self.yinaction.setObjectName("yinaction")
        self.menu.addAction(self.laneaction)
        self.menu.addAction(self.yinaction)
        self.menubar.addAction(self.menu.menuAction())

        self.retranslateUi(Laneline)
        QtCore.QMetaObject.connectSlotsByName(Laneline)

    def retranslateUi(self, Laneline):
        _translate = QtCore.QCoreApplication.translate
        Laneline.setWindowTitle(_translate("Laneline", "MainWindow"))
        self.RunBt.setText(_translate("Laneline", "运行"))
        self.PFilePathLab.setText(_translate("Laneline", "文件路径:"))
        self.PFilePathBt.setText(_translate("Laneline", "..."))
        self.TitleLab.setText(_translate("Laneline", "基于Opencv的车道线检测"))
        self.menu.setTitle(_translate("Laneline", "选择"))
        self.laneaction.setText(_translate("Laneline", "车道检测"))
        self.yinaction.setText(_translate("Laneline", "音量控制"))

run.py

from lane import Ui_Laneline
from lanedet import dectet_lane_line_ui
import sys
import cv2
from PyQt5.QtGui import QPixmap
from PyQt5.QtWidgets import QApplication, QMainWindow, QFileDialog
from PyQt5.QtCore import QTimer, QCoreApplication
import qimage2ndarray


class LaneDetectionShow(QMainWindow, Ui_Laneline):
    def __init__(self, parent=None):
        super(LaneDetectionShow, self).__init__(parent)
        self.setupUi(self)
        self.PrepParameters()
        self.CallBackFunctions()
        self.InitUI()  # 初始化UI组件的额外设置

        self.ksize = 3
        # self.min_votes_for_line = 15
        # self.min_line_length=40,
        # self.max_line_gap=20,
        # self.is_draw_polygon=True,

    def PrepParameters(self):
        self.PFilePath = r'./videofiles\video1.mp4'
        self.PFilePathLiEd.setText(self.PFilePath)
        video_path = self.PFilePath
        self.camera = cv2.VideoCapture(video_path)
        if not self.camera.isOpened():
            print("Error: Could not open video.")

    def CallBackFunctions(self):
        """将lineedit与button连接"""
        self.PFilePathBt.clicked.connect(self.SetPFilePath)
        self.RunBt.clicked.connect(self.StartVideo)

    def SetPFilePath(self):
        file_filter = "Video Files (*.mp4 *.avi *.mov *.mkv)"
        options = QFileDialog.Options()
        start_dir = '.'
        file_name, _ = QFileDialog.getOpenFileName(self, "Select Video File", start_dir, file_filter, options=options)
        if file_name:
            self.PFilePathLiEd.setText(file_name)
            self.PFilePath = file_name
            self.camera.release()
            self.camera = cv2.VideoCapture(file_name)
            if not self.camera.isOpened():
                print("Error: Could not open selected video.")

    def DispImg(self):
        if self.Image is not None:
            self.Image = dectet_lane_line_ui(
                self.Image,
                self.ksize,
                # minVotesForLine=,
                # minLineLength=,
                # maxLineGap=,
                # is_draw_polygon=,
            )
            self.Image = cv2.cvtColor(self.Image, cv2.COLOR_BGR2RGB)
            qimg = qimage2ndarray.array2qimage(self.Image)
            self.DispLb.setPixmap(QPixmap.fromImage(qimg))
            self.DispLb.setScaledContents(True)

    def StartVideo(self):
        self.Timer = QTimer()
        self.Timer.timeout.connect(self.TimerOutFun)
        self.Timer.start(30)  # 启动定时器,每30毫秒触发一次

    def TimerOutFun(self):
        success, self.Image = self.camera.read()
        if success:
            self.DispImg()
        else:
            self.Timer.stop()
            print("Video playback finished.")

    def InitUI(self):
        self.RunBt.setEnabled(True)

    def ExitApp(self):
        self.Timer.Stop()
        self.camera.release()
        QCoreApplication.quit()

if __name__ == '__main__':
    app = QApplication(sys.argv)
    ui = LaneDetectionShow()
    ui.show()
    sys.exit(app.exec_())

5、项目总结

本项目利用 PyQt5 作为图形用户界面框架,并结合 OpenCV 进行图像处理,开发了一个车道线检测系统。该系统能够从视频文件中实时读取每一帧图像,并通过车道线检测算法对视频进行处理,最终在界面中展示车道线检测结果。用户可以选择视频文件并查看车道线检测的实时效果。

6、参考文章

OpenCV实战案例——车道线识别-CSDN博客

opencv入门项目——车道线检测_opencv车道线检测-CSDN博客

基于OpenCv的车道线检测_哔哩哔哩_bilibili

车道线检测1.0(Python+Opencv基础版)-CSDN博客

基于OpenCV的车道线检测-CSDN博客

车道线检测(传统基于Opencv的方法)_基于opencv的车道线检测-CSDN博客

标签:25,车道,img,Laneline,self,cv2,Opencv,line
From: https://blog.csdn.net/m0_62919535/article/details/144691213

相关文章

  • OpenCV计算机视觉 03 椒盐噪声的添加与常见的平滑处理方式(均值、方框、高斯、中值)
    上一篇文章:OpenCV计算机视觉02图片修改图像运算边缘填充阈值处理添加椒盐噪声defadd_peppersalt_noise(image,n=10000):  result=image.copy()  h,w=image.shape[:2]  #获取图片的高和宽  foriinrange(n):  #生成n个椒盐噪声  ......
  • # 2024-2025-1 20241310 《计算机基础与程序设计》第十四周学习总结
    2024-2025-120241310《计算机基础与程序设计》第十四周学习总结作业信息这个作业属于哪个课程2024-2025-1-计算机基础与程序设计这个作业要求在哪里2024-2025-1计算机基础与程序设计第一周作业这个作业的目标自学教材《C语言程序设计》第13-14章并完成云班课测......
  • 全是干货!25年春招IT程序员,失业被裁、如何快速找到一份工作(涨薪)?学到即赚到!
    全是干货!25年春招IT程序员,失业被裁、如何快速找到一份工作(涨薪)?学到即赚到!【马士兵】_哔哩哔哩_bilibili2.如何根据招聘公司岗位需求准备技能+简历?_哔哩哔哩_bilibili3.校招如何准备技能找到满意的工作,具备哪些技能可以进大厂?_哔哩哔哩_bilibili4.社招生该如何储备技能找到满......
  • 『联合省选2025集训』『图的连通性进阶』Day3 略解
    前言我们趋行在人生这个亘古的旅途,在坎河中奔跑,在挫折里涅槃,忧愁缠满全身,痛苦飘洒一地。我们累,却无从止歇;我们苦,却无法回避。今天是连通性的进阶题目,重点是耳分解,双极定向,以及边三连通分量。因为调题速度过慢,导致被硬控,所以第二天晚上补的差不多了再来写的。感觉知识点方面......
  • 2024-2025-1 20241401 《计算机基础与程序设计》 第十四周学习总结
    班级链接2024计算机基础与程序设计作业要求第十四周作业教材学习内容总结《C语言程序设计》第13-14章第13章文件操作二进制文件和文本文件:介绍了文件的两种基本类型,二进制文件和文本文件,以及它们的区别。文件的打开和关闭:介绍在C语言中打开和关闭文件的方式......
  • 2025跨境电商逆袭:Temu店铺权重提升全攻略
     在当今的跨境电商领域,Temu迅猛崛起,成为众多卖家眼中的潜力股。自2022年上线以来,它以惊人的速度拓展市场版图,目前已成功登陆全球多个国家和地区,吸引了海量海外消费者的关注与下载,其影响力与日俱增,发展势头极为强劲。Tem创新的“全托管模式“极大地简化了供应链流程,让卖......
  • 2024-2025-1 20241322 《计算机基础与程序设计》第十四周学习总结
    2024-2025-120241322《计算机基础与程序设计》第十四周学习总结作业信息这个作业属于哪个课程https://edu.cnblogs.com/campus/besti/2024-2025-1-CFAP这个作业要求在哪里https://www.cnblogs.com/rocedu/p/9577842.html#WEEK14这个作业的目标自学教材《C语言......
  • 2025年混合云市场趋势分析:结合云上云下
    2025年混合云市场将迎来显著增长,主要受数字化转型、政策法规推动和技术创新带动。2025年混合云市场趋势的分析:市场规模和增长预测全球市场:Gartner预测,2025年全球终端用户在公有云服务上的支出将达到7234亿美元,较2024年增长21.5%。中国市场:中国混合云......
  • 2025年佛山市高新技术企业资质认定材料(佛山市高企申报材料清单)
    随着科技的不断进步和创新战略的深入实施,佛山市高新技术企业资质认定成为了推动地方经济转型升级和增强企业核心竞争力的重要途径。为了帮助企业和相关机构更好地准备和完成高企申报工作,本文将详细介绍2025年佛山市高新技术企业资质认定所需准备的材料清单,确保申报过程顺利、高......
  • 125. 验证回文串
    题目链接解题思路:双指针,一个一个对比,跳过非数字字母,对比时,忽略大小写代码classSolution:defisPalindrome(self,s:str)->bool:#双指针i,j=0,len(s)-1whilei<j:#跳过非字母和数字字符while......