• 基于YOLOv8深度学习+Pyqt5的电动车头盔佩戴检测系统


    wx供重浩:创享日记
    对话框发送:225头盔
    获取完整源码源文件+已标注的数据集(1463张)+源码各文件说明+配置跑通说明文档
    若需要一对一远程操作在你电脑跑通,有偿59yuan


    效果展示

    基于YOLOv8深度学习+PyQT5的电动车头盔佩戴检测系统

    在这里插入图片描述

    各文件说明

    在这里插入图片描述


    随着电动车的普及,交通安全问题日益凸显。头盔作为保护骑行者头部安全的重要装备,其佩戴情况的监测对于减少交通事故伤害具有重要意义。本文提出了一种基于YOLOv8(You Only Look Once version 8)的电动车头盔佩戴检测系统,旨在实时监测并提醒骑行者佩戴头盔。该系统利用深度学习技术,通过训练YOLOv8模型来识别电动车骑行者是否佩戴头盔,并在检测到未佩戴头盔的情况下发出警报。系统设计考虑了实时性、准确性和用户友好性,为提升电动车骑行安全提供了一种有效的技术解决方案。

    电动车作为一种便捷的交通工具,在城市交通中扮演着越来越重要的角色。然而,由于缺乏必要的安全措施,电动车事故频发,尤其是头部伤害,往往导致严重后果。头盔的正确佩戴可以有效降低头部受伤的风险。因此,开发一种能够实时监测头盔佩戴情况的系统,对于提高骑行者的安全意识和减少交通事故具有重要作用。

    YOLOv8是YOLO系列目标检测模型的最新版本,它在前代模型的基础上进行了优化,提高了检测速度和准确性。YOLOv8采用了多尺度特征图的融合、更精细的锚点框设计以及更有效的损失函数,使得模型在处理复杂场景时表现出色。

    系统设计
    数据集准备
    为了训练YOLOv8模型,首先需要收集和标注一个包含大量电动车骑行者图像的数据集。数据集应包含各种天气条件、不同光照环境以及多种头盔类型。图像标注需要指出头盔的位置和类别,以便模型学习区分佩戴头盔与否。

    模型训练
    使用准备好的数据集,对YOLOv8模型进行训练。训练过程中,需要调整学习率、批大小、训练周期等超参数,以获得最佳的检测效果。同时,可以采用数据增强技术,如随机裁剪、旋转、颜色变换等,以提高模型的泛化能力。

    系统实现
    系统的核心是YOLOv8模型,它负责从输入的实时视频流中检测头盔。系统还包括一个用户界面,用于显示检测结果和发出警报。当模型检测到未佩戴头盔的骑行者时,系统会通过声音或视觉信号提醒用户。

    实时检测
    为了实现实时检测,系统需要能够在低延迟的情况下处理视频流。这要求模型不仅要准确,还要高效。在实际部署中,可能需要在边缘设备上进行模型推理,以减少对中心服务器的依赖。

    结果与讨论
    在测试集上,YOLOv8模型展现出了较高的头盔检测准确率。在不同的场景和光照条件下,模型均能稳定地识别出佩戴和未佩戴头盔的骑行者。然而,模型在处理遮挡严重或头盔与骑行者头部颜色相近的情况下仍有改进空间。

    结论
    本文提出的基于YOLOv8的电动车头盔佩戴检测系统,能够有效地提高电动车骑行者的安全意识。通过实时监测和提醒,该系统有助于减少因未佩戴头盔导致的交通事故伤害。未来的工作将集中在进一步提高模型的鲁棒性,以及探索更高效的模型部署方案。


    软件系统部分源码

    # -*- coding: utf-8 -*-
    import time
    from PyQt5.QtWidgets import QApplication , QMainWindow, QFileDialog, \
        QMessageBox,QWidget,QHeaderView,QTableWidgetItem, QAbstractItemView
    import sys
    import os
    from PIL import ImageFont
    from ultralytics import YOLO
    sys.path.append('UIProgram')
    from UIProgram.UiMain import Ui_MainWindow
    import sys
    from PyQt5.QtCore import QTimer, Qt, QThread, pyqtSignal,QCoreApplication
    import detect_tools as tools
    import cv2
    import Config
    from UIProgram.QssLoader import QSSLoader
    from UIProgram.precess_bar import ProgressBar
    import numpy as np
    # import torch
    
    class MainWindow(QMainWindow):
        def __init__(self, parent=None):
            super(QMainWindow, self).__init__(parent)
            self.ui = Ui_MainWindow()
            self.ui.setupUi(self)
            self.initMain()
            self.signalconnect()
    
            # 加载css渲染效果
            style_file = 'UIProgram/style.css'
            qssStyleSheet = QSSLoader.read_qss_file(style_file)
            self.setStyleSheet(qssStyleSheet)
    
        def signalconnect(self):
            self.ui.PicBtn.clicked.connect(self.open_img)
            self.ui.comboBox.activated.connect(self.combox_change)
            self.ui.VideoBtn.clicked.connect(self.vedio_show)
            self.ui.CapBtn.clicked.connect(self.camera_show)
            self.ui.SaveBtn.clicked.connect(self.save_detect_video)
            self.ui.ExitBtn.clicked.connect(QCoreApplication.quit)
            self.ui.FilesBtn.clicked.connect(self.detact_batch_imgs)
    
        def initMain(self):
            self.show_width = 770
            self.show_height = 480
    
            self.org_path = None
    
            self.is_camera_open = False
            self.cap = None
    
            # self.device = 0 if torch.cuda.is_available() else 'cpu'
    
            # 加载检测模型
            self.model = YOLO(Config.model_path, task='detect')
            self.model(np.zeros((48, 48, 3)))  #预先加载推理模型
            self.fontC = ImageFont.truetype("Font/platech.ttf", 25, 0)
    
            # 用于绘制不同颜色矩形框
            self.colors = tools.Colors()
    
            # 更新视频图像
            self.timer_camera = QTimer()
    
            # 更新检测信息表格
            # self.timer_info = QTimer()
            # 保存视频
            self.timer_save_video = QTimer()
    
            # 表格
            self.ui.tableWidget.verticalHeader().setSectionResizeMode(QHeaderView.Fixed)
            self.ui.tableWidget.verticalHeader().setDefaultSectionSize(40)
            self.ui.tableWidget.setColumnWidth(0, 80)  # 设置列宽
            self.ui.tableWidget.setColumnWidth(1, 200)
            self.ui.tableWidget.setColumnWidth(2, 150)
            self.ui.tableWidget.setColumnWidth(3, 90)
            self.ui.tableWidget.setColumnWidth(4, 230)
            # self.ui.tableWidget.horizontalHeader().setSectionResizeMode(QHeaderView.Stretch)  # 表格铺满
            # self.ui.tableWidget.horizontalHeader().setSectionResizeMode(0, QHeaderView.Interactive)
            # self.ui.tableWidget.setEditTriggers(QAbstractItemView.NoEditTriggers)  # 设置表格不可编辑
            self.ui.tableWidget.setSelectionBehavior(QAbstractItemView.SelectRows)  # 设置表格整行选中
            self.ui.tableWidget.verticalHeader().setVisible(False)  # 隐藏列标题
            self.ui.tableWidget.setAlternatingRowColors(True)  # 表格背景交替
    
            # 设置主页背景图片border-image: url(:/icons/ui_imgs/icons/camera.png)
            # self.setStyleSheet("#MainWindow{background-image:url(:/bgs/ui_imgs/bg3.jpg)}")
    
        def open_img(self):
            if self.cap:
                # 打开图片前关闭摄像头
                self.video_stop()
                self.is_camera_open = False
                self.ui.CaplineEdit.setText('摄像头未开启')
                self.cap = None
    
            # 弹出的窗口名称:'打开图片'
            # 默认打开的目录:'./'
            # 只能打开.jpg与.gif结尾的图片文件
            # file_path, _ = QFileDialog.getOpenFileName(self.ui.centralwidget, '打开图片', './', "Image files (*.jpg *.gif)")
            file_path, _ = QFileDialog.getOpenFileName(None, '打开图片', './', "Image files (*.jpg *.jepg *.png)")
            if not file_path:
                return
    
            self.ui.comboBox.setDisabled(False)
            self.org_path = file_path
            self.org_img = tools.img_cvread(self.org_path)
    
            # 目标检测
            t1 = time.time()
            self.results = self.model(self.org_path)[0]
            t2 = time.time()
            take_time_str = '{:.3f} s'.format(t2 - t1)
            self.ui.time_lb.setText(take_time_str)
    
            location_list = self.results.boxes.xyxy.tolist()
            self.location_list = [list(map(int, e)) for e in location_list]
            cls_list = self.results.boxes.cls.tolist()
            self.cls_list = [int(i) for i in cls_list]
            self.conf_list = self.results.boxes.conf.tolist()
            self.conf_list = ['%.2f %%' % (each*100) for each in self.conf_list]
    
            # now_img = self.cv_img.copy()
            # for loacation, type_id, conf in zip(self.location_list, self.cls_list, self.conf_list):
            #     type_id = int(type_id)
            #     color = self.colors(int(type_id), True)
            #     # cv2.rectangle(now_img, (int(x1), int(y1)), (int(x2), int(y2)), colors(int(type_id), True), 3)
            #     now_img = tools.drawRectBox(now_img, loacation, Config.CH_names[type_id], self.fontC, color)
            now_img = self.results.plot()
            self.draw_img = now_img
            # 获取缩放后的图片尺寸
            self.img_width, self.img_height = self.get_resize_size(now_img)
            resize_cvimg = cv2.resize(now_img,(self.img_width, self.img_height))
            pix_img = tools.cvimg_to_qpiximg(resize_cvimg)
            self.ui.label_show.setPixmap(pix_img)
            self.ui.label_show.setAlignment(Qt.AlignCenter)
            # 设置路径显示
            self.ui.PiclineEdit.setText(self.org_path)
    
            # 目标数目
            target_nums = len(self.cls_list)
            self.ui.label_nums.setText(str(target_nums))
    
            # 设置目标选择下拉框
            choose_list = ['全部']
            target_names = [Config.names[id]+ '_'+ str(index) for index,id in enumerate(self.cls_list)]
            # object_list = sorted(set(self.cls_list))
            # for each in object_list:
            #     choose_list.append(Config.CH_names[each])
            choose_list = choose_list + target_names
    
            self.ui.comboBox.clear()
            self.ui.comboBox.addItems(choose_list)
    
            if target_nums >= 1:
                self.ui.type_lb.setText(Config.CH_names[self.cls_list[0]])
                self.ui.label_conf.setText(str(self.conf_list[0]))
            #   默认显示第一个目标框坐标
            #   设置坐标位置值
                self.ui.label_xmin.setText(str(self.location_list[0][0]))
                self.ui.label_ymin.setText(str(self.location_list[0][1]))
                self.ui.label_xmax.setText(str(self.location_list[0][2]))
                self.ui.label_ymax.setText(str(self.location_list[0][3]))
            else:
                self.ui.type_lb.setText('')
                self.ui.label_conf.setText('')
                self.ui.label_xmin.setText('')
                self.ui.label_ymin.setText('')
                self.ui.label_xmax.setText('')
                self.ui.label_ymax.setText('')
    
            # # 删除表格所有行
            self.ui.tableWidget.setRowCount(0)
            self.ui.tableWidget.clearContents()
            self.tabel_info_show(self.location_list, self.cls_list, self.conf_list,path=self.org_path)
    
    
        def detact_batch_imgs(self):
            if self.cap:
                # 打开图片前关闭摄像头
                self.video_stop()
                self.is_camera_open = False
                self.ui.CaplineEdit.setText('摄像头未开启')
                self.cap = None
            directory = QFileDialog.getExistingDirectory(self,
                                                          "选取文件夹",
                                                          "./")  # 起始路径
            if not  directory:
                return
            self.org_path = directory
            img_suffix = ['jpg','png','jpeg','bmp']
            for file_name in os.listdir(directory):
                full_path = os.path.join(directory,file_name)
                if os.path.isfile(full_path) and file_name.split('.')[-1].lower() in img_suffix:
                    # self.ui.comboBox.setDisabled(False)
                    img_path = full_path
                    self.org_img = tools.img_cvread(img_path)
                    # 目标检测
                    t1 = time.time()
                    self.results = self.model(img_path)[0]
                    t2 = time.time()
                    take_time_str = '{:.3f} s'.format(t2 - t1)
                    self.ui.time_lb.setText(take_time_str)
    
                    location_list = self.results.boxes.xyxy.tolist()
                    self.location_list = [list(map(int, e)) for e in location_list]
                    cls_list = self.results.boxes.cls.tolist()
                    self.cls_list = [int(i) for i in cls_list]
                    self.conf_list = self.results.boxes.conf.tolist()
                    self.conf_list = ['%.2f %%' % (each * 100) for each in self.conf_list]
    
                    now_img = self.results.plot()
    
                    self.draw_img = now_img
                    # 获取缩放后的图片尺寸
                    self.img_width, self.img_height = self.get_resize_size(now_img)
                    resize_cvimg = cv2.resize(now_img, (self.img_width, self.img_height))
                    pix_img = tools.cvimg_to_qpiximg(resize_cvimg)
                    self.ui.label_show.setPixmap(pix_img)
                    self.ui.label_show.setAlignment(Qt.AlignCenter)
                    # 设置路径显示
                    self.ui.PiclineEdit.setText(img_path)
    
                    # 目标数目
                    target_nums = len(self.cls_list)
                    self.ui.label_nums.setText(str(target_nums))
    
                    # 设置目标选择下拉框
                    choose_list = ['全部']
                    target_names = [Config.names[id] + '_' + str(index) for index, id in enumerate(self.cls_list)]
                    choose_list = choose_list + target_names
    
                    self.ui.comboBox.clear()
                    self.ui.comboBox.addItems(choose_list)
    
                    if target_nums >= 1:
                        self.ui.type_lb.setText(Config.CH_names[self.cls_list[0]])
                        self.ui.label_conf.setText(str(self.conf_list[0]))
                        #   默认显示第一个目标框坐标
                        #   设置坐标位置值
                        self.ui.label_xmin.setText(str(self.location_list[0][0]))
                        self.ui.label_ymin.setText(str(self.location_list[0][1]))
                        self.ui.label_xmax.setText(str(self.location_list[0][2]))
                        self.ui.label_ymax.setText(str(self.location_list[0][3]))
                    else:
                        self.ui.type_lb.setText('')
                        self.ui.label_conf.setText('')
                        self.ui.label_xmin.setText('')
                        self.ui.label_ymin.setText('')
                        self.ui.label_xmax.setText('')
                        self.ui.label_ymax.setText('')
    
                    # # 删除表格所有行
                    # self.ui.tableWidget.setRowCount(0)
                    # self.ui.tableWidget.clearContents()
                    self.tabel_info_show(self.location_list, self.cls_list, self.conf_list, path=img_path)
                    self.ui.tableWidget.scrollToBottom()
                    QApplication.processEvents()  #刷新页面
    
        def draw_rect_and_tabel(self, results, img):
            now_img = img.copy()
            location_list = results.boxes.xyxy.tolist()
            self.location_list = [list(map(int, e)) for e in location_list]
            cls_list = results.boxes.cls.tolist()
            self.cls_list = [int(i) for i in cls_list]
            self.conf_list = results.boxes.conf.tolist()
            self.conf_list = ['%.2f %%' % (each * 100) for each in self.conf_list]
    
            for loacation, type_id, conf in zip(self.location_list, self.cls_list, self.conf_list):
                type_id = int(type_id)
                color = self.colors(int(type_id), True)
                # cv2.rectangle(now_img, (int(x1), int(y1)), (int(x2), int(y2)), colors(int(type_id), True), 3)
                now_img = tools.drawRectBox(now_img, loacation, Config.CH_names[type_id], self.fontC, color)
    
            # 获取缩放后的图片尺寸
            self.img_width, self.img_height = self.get_resize_size(now_img)
            resize_cvimg = cv2.resize(now_img, (self.img_width, self.img_height))
            pix_img = tools.cvimg_to_qpiximg(resize_cvimg)
            self.ui.label_show.setPixmap(pix_img)
            self.ui.label_show.setAlignment(Qt.AlignCenter)
            # 设置路径显示
            self.ui.PiclineEdit.setText(self.org_path)
    
            # 目标数目
            target_nums = len(self.cls_list)
            self.ui.label_nums.setText(str(target_nums))
            if target_nums >= 1:
                self.ui.type_lb.setText(Config.CH_names[self.cls_list[0]])
                self.ui.label_conf.setText(str(self.conf_list[0]))
                self.ui.label_xmin.setText(str(self.location_list[0][0]))
                self.ui.label_ymin.setText(str(self.location_list[0][1]))
                self.ui.label_xmax.setText(str(self.location_list[0][2]))
                self.ui.label_ymax.setText(str(self.location_list[0][3]))
            else:
                self.ui.type_lb.setText('')
                self.ui.label_conf.setText('')
                self.ui.label_xmin.setText('')
                self.ui.label_ymin.setText('')
                self.ui.label_xmax.setText('')
                self.ui.label_ymax.setText('')
    
            # 删除表格所有行
            self.ui.tableWidget.setRowCount(0)
            self.ui.tableWidget.clearContents()
            self.tabel_info_show(self.location_list, self.cls_list, self.conf_list, path=self.org_path)
            return now_img
    
        def combox_change(self):
            com_text = self.ui.comboBox.currentText()
            if com_text == '全部':
                cur_box = self.location_list
                cur_img = self.results.plot()
                self.ui.type_lb.setText(Config.CH_names[self.cls_list[0]])
                self.ui.label_conf.setText(str(self.conf_list[0]))
            else:
                index = int(com_text.split('_')[-1])
                cur_box = [self.location_list[index]]
                cur_img = self.results[index].plot()
                self.ui.type_lb.setText(Config.CH_names[self.cls_list[index]])
                self.ui.label_conf.setText(str(self.conf_list[index]))
    
            # 设置坐标位置值
            self.ui.label_xmin.setText(str(cur_box[0][0]))
            self.ui.label_ymin.setText(str(cur_box[0][1]))
            self.ui.label_xmax.setText(str(cur_box[0][2]))
            self.ui.label_ymax.setText(str(cur_box[0][3]))
    
            resize_cvimg = cv2.resize(cur_img, (self.img_width, self.img_height))
            pix_img = tools.cvimg_to_qpiximg(resize_cvimg)
            self.ui.label_show.clear()
            self.ui.label_show.setPixmap(pix_img)
            self.ui.label_show.setAlignment(Qt.AlignCenter)
    
    
        def get_video_path(self):
            file_path, _ = QFileDialog.getOpenFileName(None, '打开视频', './', "Image files (*.avi *.mp4 *.jepg *.png)")
            if not file_path:
                return None
            self.org_path = file_path
            self.ui.VideolineEdit.setText(file_path)
            return file_path
    
        def video_start(self):
            # 删除表格所有行
            self.ui.tableWidget.setRowCount(0)
            self.ui.tableWidget.clearContents()
    
            # 清空下拉框
            self.ui.comboBox.clear()
    
            # 定时器开启,每隔一段时间,读取一帧
            self.timer_camera.start(1)
            self.timer_camera.timeout.connect(self.open_frame)
    
        def tabel_info_show(self, locations, clses, confs, path=None):
            path = path
            for location, cls, conf in zip(locations, clses, confs):
                row_count = self.ui.tableWidget.rowCount()  # 返回当前行数(尾部)
                self.ui.tableWidget.insertRow(row_count)  # 尾部插入一行
                item_id = QTableWidgetItem(str(row_count+1))  # 序号
                item_id.setTextAlignment(Qt.AlignHCenter | Qt.AlignVCenter)  # 设置文本居中
                item_path = QTableWidgetItem(str(path))  # 路径
                # item_path.setTextAlignment(Qt.AlignHCenter | Qt.AlignVCenter)
    
                item_cls = QTableWidgetItem(str(Config.CH_names[cls]))
                item_cls.setTextAlignment(Qt.AlignHCenter | Qt.AlignVCenter)  # 设置文本居中
    
                item_conf = QTableWidgetItem(str(conf))
                item_conf.setTextAlignment(Qt.AlignHCenter | Qt.AlignVCenter)  # 设置文本居中
    
                item_location = QTableWidgetItem(str(location)) # 目标框位置
                # item_location.setTextAlignment(Qt.AlignHCenter | Qt.AlignVCenter)  # 设置文本居中
    
                self.ui.tableWidget.setItem(row_count, 0, item_id)
                self.ui.tableWidget.setItem(row_count, 1, item_path)
                self.ui.tableWidget.setItem(row_count, 2, item_cls)
                self.ui.tableWidget.setItem(row_count, 3, item_conf)
                self.ui.tableWidget.setItem(row_count, 4, item_location)
            self.ui.tableWidget.scrollToBottom()
    
        def video_stop(self):
            self.cap.release()
            self.timer_camera.stop()
            # self.timer_info.stop()
    
        def open_frame(self):
            ret, now_img = self.cap.read()
            if ret:
                # 目标检测
                t1 = time.time()
                results = self.model(now_img)[0]
                t2 = time.time()
                take_time_str = '{:.3f} s'.format(t2 - t1)
                self.ui.time_lb.setText(take_time_str)
    
                location_list = results.boxes.xyxy.tolist()
                self.location_list = [list(map(int, e)) for e in location_list]
                cls_list = results.boxes.cls.tolist()
                self.cls_list = [int(i) for i in cls_list]
                self.conf_list = results.boxes.conf.tolist()
                self.conf_list = ['%.2f %%' % (each * 100) for each in self.conf_list]
    
                now_img = results.plot()
    
                # 获取缩放后的图片尺寸
                self.img_width, self.img_height = self.get_resize_size(now_img)
                resize_cvimg = cv2.resize(now_img, (self.img_width, self.img_height))
                pix_img = tools.cvimg_to_qpiximg(resize_cvimg)
                self.ui.label_show.setPixmap(pix_img)
                self.ui.label_show.setAlignment(Qt.AlignCenter)
    
                # 目标数目
                target_nums = len(self.cls_list)
                self.ui.label_nums.setText(str(target_nums))
    
                # 设置目标选择下拉框
                choose_list = ['全部']
                target_names = [Config.names[id] + '_' + str(index) for index, id in enumerate(self.cls_list)]
                # object_list = sorted(set(self.cls_list))
                # for each in object_list:
                #     choose_list.append(Config.CH_names[each])
                choose_list = choose_list + target_names
    
                self.ui.comboBox.clear()
                self.ui.comboBox.addItems(choose_list)
    
                if target_nums >= 1:
                    self.ui.type_lb.setText(Config.CH_names[self.cls_list[0]])
                    self.ui.label_conf.setText(str(self.conf_list[0]))
                    #   默认显示第一个目标框坐标
                    #   设置坐标位置值
                    self.ui.label_xmin.setText(str(self.location_list[0][0]))
                    self.ui.label_ymin.setText(str(self.location_list[0][1]))
                    self.ui.label_xmax.setText(str(self.location_list[0][2]))
                    self.ui.label_ymax.setText(str(self.location_list[0][3]))
                else:
                    self.ui.type_lb.setText('')
                    self.ui.label_conf.setText('')
                    self.ui.label_xmin.setText('')
                    self.ui.label_ymin.setText('')
                    self.ui.label_xmax.setText('')
                    self.ui.label_ymax.setText('')
    
    
                # 删除表格所有行
                # self.ui.tableWidget.setRowCount(0)
                # self.ui.tableWidget.clearContents()
                self.tabel_info_show(self.location_list, self.cls_list, self.conf_list, path=self.org_path)
    
            else:
                self.cap.release()
                self.timer_camera.stop()
    
        def vedio_show(self):
            if self.is_camera_open:
                self.is_camera_open = False
                self.ui.CaplineEdit.setText('摄像头未开启')
    
            video_path = self.get_video_path()
            if not video_path:
                return None
            self.cap = cv2.VideoCapture(video_path)
            self.video_start()
            self.ui.comboBox.setDisabled(True)
    
        def camera_show(self):
            self.is_camera_open = not self.is_camera_open
            if self.is_camera_open:
                self.ui.CaplineEdit.setText('摄像头开启')
                self.cap = cv2.VideoCapture(0)
                self.video_start()
                self.ui.comboBox.setDisabled(True)
            else:
                self.ui.CaplineEdit.setText('摄像头未开启')
                self.ui.label_show.setText('')
                if self.cap:
                    self.cap.release()
                    cv2.destroyAllWindows()
                self.ui.label_show.clear()
    
        def get_resize_size(self, img):
            _img = img.copy()
            img_height, img_width , depth= _img.shape
            ratio = img_width / img_height
            if ratio >= self.show_width / self.show_height:
                self.img_width = self.show_width
                self.img_height = int(self.img_width / ratio)
            else:
                self.img_height = self.show_height
                self.img_width = int(self.img_height * ratio)
            return self.img_width, self.img_height
    
        def save_detect_video(self):
            if self.cap is None and not self.org_path:
                QMessageBox.about(self, '提示', '当前没有可保存信息,请先打开图片或视频!')
                return
    
            if self.is_camera_open:
                QMessageBox.about(self, '提示', '摄像头视频无法保存!')
                return
    
            if self.cap:
                res = QMessageBox.information(self, '提示', '保存视频检测结果可能需要较长时间,请确认是否继续保存?',QMessageBox.Yes | QMessageBox.No ,  QMessageBox.Yes)
                if res == QMessageBox.Yes:
                    self.video_stop()
                    com_text = self.ui.comboBox.currentText()
                    self.btn2Thread_object = btn2Thread(self.org_path, self.model, com_text)
                    self.btn2Thread_object.start()
                    self.btn2Thread_object.update_ui_signal.connect(self.update_process_bar)
                else:
                    return
            else:
                if os.path.isfile(self.org_path):
                    fileName = os.path.basename(self.org_path)
                    name , end_name= fileName.rsplit(".",1)
                    save_name = name + '_detect_result.' + end_name
                    save_img_path = os.path.join(Config.save_path, save_name)
                    # 保存图片
                    cv2.imwrite(save_img_path, self.draw_img)
                    QMessageBox.about(self, '提示', '图片保存成功!\n文件路径:{}'.format(save_img_path))
                else:
                    img_suffix = ['jpg', 'png', 'jpeg', 'bmp']
                    for file_name in os.listdir(self.org_path):
                        full_path = os.path.join(self.org_path, file_name)
                        if os.path.isfile(full_path) and file_name.split('.')[-1].lower() in img_suffix:
                            name, end_name = file_name.rsplit(".",1)
                            save_name = name + '_detect_result.' + end_name
                            save_img_path = os.path.join(Config.save_path, save_name)
                            results = self.model(full_path)[0]
                            now_img = results.plot()
                            # 保存图片
                            cv2.imwrite(save_img_path, now_img)
    
                    QMessageBox.about(self, '提示', '图片保存成功!\n文件路径:{}'.format(Config.save_path))
    
    
        def update_process_bar(self,cur_num, total):
            if cur_num == 1:
                self.progress_bar = ProgressBar(self)
                self.progress_bar.show()
            if cur_num >= total:
                self.progress_bar.close()
                QMessageBox.about(self, '提示', '视频保存成功!\n文件在{}目录下'.format(Config.save_path))
                return
            if self.progress_bar.isVisible() is False:
                # 点击取消保存时,终止进程
                self.btn2Thread_object.stop()
                return
            value = int(cur_num / total *100)
            self.progress_bar.setValue(cur_num, total, value)
            QApplication.processEvents()
    
    
    class btn2Thread(QThread):
        """
        进行检测后的视频保存
        """
        # 声明一个信号
        update_ui_signal = pyqtSignal(int,int)
    
        def __init__(self, path, model, com_text):
            super(btn2Thread, self).__init__()
            self.org_path = path
            self.model = model
            self.com_text = com_text
            # 用于绘制不同颜色矩形框
            self.colors = tools.Colors()
            self.is_running = True  # 标志位,表示线程是否正在运行
    
        def run(self):
            # VideoCapture方法是cv2库提供的读取视频方法
            cap = cv2.VideoCapture(self.org_path)
            # 设置需要保存视频的格式“xvid”
            # 该参数是MPEG-4编码类型,文件名后缀为.avi
            fourcc = cv2.VideoWriter_fourcc(*'XVID')
            # 设置视频帧频
            fps = cap.get(cv2.CAP_PROP_FPS)
            # 设置视频大小
            size = (int(cap.get(cv2.CAP_PROP_FRAME_WIDTH)), int(cap.get(cv2.CAP_PROP_FRAME_HEIGHT)))
            # VideoWriter方法是cv2库提供的保存视频方法
            # 按照设置的格式来out输出
            fileName = os.path.basename(self.org_path)
            name, end_name = fileName.split('.')
            save_name = name + '_detect_result.avi'
            save_video_path = os.path.join(Config.save_path, save_name)
            out = cv2.VideoWriter(save_video_path, fourcc, fps, size)
    
            prop = cv2.CAP_PROP_FRAME_COUNT
            total = int(cap.get(prop))
            print("[INFO] 视频总帧数:{}".format(total))
            cur_num = 0
    
            # 确定视频打开并循环读取
            while (cap.isOpened() and self.is_running):
                cur_num += 1
                print('当前第{}帧,总帧数{}'.format(cur_num, total))
                # 逐帧读取,ret返回布尔值
                # 参数ret为True 或者False,代表有没有读取到图片
                # frame表示截取到一帧的图片
                ret, frame = cap.read()
                if ret == True:
                    # 检测
                    results = self.model(frame)[0]
                    frame = results.plot()
                    out.write(frame)
                    self.update_ui_signal.emit(cur_num, total)
                else:
                    break
            # 释放资源
            cap.release()
            out.release()
    
        def stop(self):
            self.is_running = False
    
    
    if __name__ == "__main__":
        app = QApplication(sys.argv)
        win = MainWindow()
        win.show()
        sys.exit(app.exec_())
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121
    • 122
    • 123
    • 124
    • 125
    • 126
    • 127
    • 128
    • 129
    • 130
    • 131
    • 132
    • 133
    • 134
    • 135
    • 136
    • 137
    • 138
    • 139
    • 140
    • 141
    • 142
    • 143
    • 144
    • 145
    • 146
    • 147
    • 148
    • 149
    • 150
    • 151
    • 152
    • 153
    • 154
    • 155
    • 156
    • 157
    • 158
    • 159
    • 160
    • 161
    • 162
    • 163
    • 164
    • 165
    • 166
    • 167
    • 168
    • 169
    • 170
    • 171
    • 172
    • 173
    • 174
    • 175
    • 176
    • 177
    • 178
    • 179
    • 180
    • 181
    • 182
    • 183
    • 184
    • 185
    • 186
    • 187
    • 188
    • 189
    • 190
    • 191
    • 192
    • 193
    • 194
    • 195
    • 196
    • 197
    • 198
    • 199
    • 200
    • 201
    • 202
    • 203
    • 204
    • 205
    • 206
    • 207
    • 208
    • 209
    • 210
    • 211
    • 212
    • 213
    • 214
    • 215
    • 216
    • 217
    • 218
    • 219
    • 220
    • 221
    • 222
    • 223
    • 224
    • 225
    • 226
    • 227
    • 228
    • 229
    • 230
    • 231
    • 232
    • 233
    • 234
    • 235
    • 236
    • 237
    • 238
    • 239
    • 240
    • 241
    • 242
    • 243
    • 244
    • 245
    • 246
    • 247
    • 248
    • 249
    • 250
    • 251
    • 252
    • 253
    • 254
    • 255
    • 256
    • 257
    • 258
    • 259
    • 260
    • 261
    • 262
    • 263
    • 264
    • 265
    • 266
    • 267
    • 268
    • 269
    • 270
    • 271
    • 272
    • 273
    • 274
    • 275
    • 276
    • 277
    • 278
    • 279
    • 280
    • 281
    • 282
    • 283
    • 284
    • 285
    • 286
    • 287
    • 288
    • 289
    • 290
    • 291
    • 292
    • 293
    • 294
    • 295
    • 296
    • 297
    • 298
    • 299
    • 300
    • 301
    • 302
    • 303
    • 304
    • 305
    • 306
    • 307
    • 308
    • 309
    • 310
    • 311
    • 312
    • 313
    • 314
    • 315
    • 316
    • 317
    • 318
    • 319
    • 320
    • 321
    • 322
    • 323
    • 324
    • 325
    • 326
    • 327
    • 328
    • 329
    • 330
    • 331
    • 332
    • 333
    • 334
    • 335
    • 336
    • 337
    • 338
    • 339
    • 340
    • 341
    • 342
    • 343
    • 344
    • 345
    • 346
    • 347
    • 348
    • 349
    • 350
    • 351
    • 352
    • 353
    • 354
    • 355
    • 356
    • 357
    • 358
    • 359
    • 360
    • 361
    • 362
    • 363
    • 364
    • 365
    • 366
    • 367
    • 368
    • 369
    • 370
    • 371
    • 372
    • 373
    • 374
    • 375
    • 376
    • 377
    • 378
    • 379
    • 380
    • 381
    • 382
    • 383
    • 384
    • 385
    • 386
    • 387
    • 388
    • 389
    • 390
    • 391
    • 392
    • 393
    • 394
    • 395
    • 396
    • 397
    • 398
    • 399
    • 400
    • 401
    • 402
    • 403
    • 404
    • 405
    • 406
    • 407
    • 408
    • 409
    • 410
    • 411
    • 412
    • 413
    • 414
    • 415
    • 416
    • 417
    • 418
    • 419
    • 420
    • 421
    • 422
    • 423
    • 424
    • 425
    • 426
    • 427
    • 428
    • 429
    • 430
    • 431
    • 432
    • 433
    • 434
    • 435
    • 436
    • 437
    • 438
    • 439
    • 440
    • 441
    • 442
    • 443
    • 444
    • 445
    • 446
    • 447
    • 448
    • 449
    • 450
    • 451
    • 452
    • 453
    • 454
    • 455
    • 456
    • 457
    • 458
    • 459
    • 460
    • 461
    • 462
    • 463
    • 464
    • 465
    • 466
    • 467
    • 468
    • 469
    • 470
    • 471
    • 472
    • 473
    • 474
    • 475
    • 476
    • 477
    • 478
    • 479
    • 480
    • 481
    • 482
    • 483
    • 484
    • 485
    • 486
    • 487
    • 488
    • 489
    • 490
    • 491
    • 492
    • 493
    • 494
    • 495
    • 496
    • 497
    • 498
    • 499
    • 500
    • 501
    • 502
    • 503
    • 504
    • 505
    • 506
    • 507
    • 508
    • 509
    • 510
    • 511
    • 512
    • 513
    • 514
    • 515
    • 516
    • 517
    • 518
    • 519
    • 520
    • 521
    • 522
    • 523
    • 524
    • 525
    • 526
    • 527
    • 528
    • 529
    • 530
    • 531
    • 532
    • 533
    • 534
    • 535
    • 536
    • 537
    • 538
    • 539
    • 540
    • 541
    • 542
    • 543
    • 544
    • 545
    • 546
    • 547
    • 548
    • 549
    • 550
    • 551
    • 552
    • 553
    • 554
    • 555
    • 556
    • 557
    • 558
    • 559
    • 560
    • 561
    • 562
    • 563
    • 564
    • 565
    • 566
    • 567
    • 568
    • 569
    • 570
    • 571
    • 572
    • 573
    • 574
    • 575
    • 576
    • 577
    • 578
    • 579
    • 580
    • 581
    • 582
    • 583
    • 584
    • 585
    • 586
    • 587
    • 588
    • 589
    • 590
    • 591
    • 592
    • 593
    • 594
    • 595
    • 596
    • 597
    • 598
    • 599
    • 600
    • 601
    • 602
    • 603
    • 604
    • 605
    • 606
    • 607
    • 608
    • 609
    • 610
    • 611
    • 612
    • 613
    • 614
    • 615
    • 616
    • 617
    • 618
    • 619
    • 620
  • 相关阅读:
    VBA技术资料MF51:VBA_在Excel中突出显示唯一值
    【排序算法】Leetcode刷题心得
    杰理701N可视化SDK之LED的配置和代码浅析
    521. 最长特殊序列 Ⅰ
    zabbix的rpm包部署
    高并发情况下保证高可用性
    mysql 5.7 修改密码
    LTE系统TDD无线帧结构特点
    数据结构和算法——线性结构
    JavaWeb整合Mybatis更新删除操作后查询返回数据相同的问题解决
  • 原文地址:https://blog.csdn.net/m0_46653805/article/details/136286933