基于YOLOv5+pyqt5的跌倒检测系统(含pyqt页面、训练好的模型)

news2025/1/18 8:59:32

image-20240623154808782

简介

跌倒是老年人和身体不便者常见的意外事故,及时检测和处理跌倒事件对于保障他们的安全至关重要。为了提高对跌倒事件的监控效率,我们开发了一种基于YOLOv5目标检测模型的跌倒检测系统。本报告将详细介绍该系统的实际应用与实现,包括系统架构、功能实现、使用说明、检测示例、数据集获取与介绍、YOLOv5模型介绍及其训练过程。

系统架构

系统组成

  1. 用户界面(GUI):基于PyQt5开发,支持图像、视频和实时摄像头检测功能。
  2. 检测模型:基于YOLOv5的目标检测模型,用于识别跌倒事件。
  3. 视频处理模块:处理视频流,实现实时跌倒检测。
  4. 数据管理模块:负责数据的加载、保存及标注。

工作流程

  1. 用户加载图像/视频或启动摄像头
  2. 系统调用YOLOv5模型进行跌倒检测
  3. 检测结果显示在GUI上,包括跌倒事件的位置和时间
  4. 用户可以保存检测结果

功能实现

import sys
import cv2
import torch
import numpy as np
import time
import os
from PyQt5.QtWidgets import QApplication, QMainWindow, QPushButton, QFileDialog, QLabel, QVBoxLayout, QWidget, \
    QHBoxLayout, QSlider, QMessageBox
from PyQt5.QtGui import QImage, QPixmap, QFont
from PyQt5.QtCore import QTimer, QThread, pyqtSignal, Qt, QMutex, QWaitCondition


class YoloV5Detector:
    def __init__(self):
        self.model = torch.hub.load('ultralytics/yolov5', 'custom', path="./yolov5_fall.pt")

    def detect_image(self, image_path):
        image = cv2.imread(image_path)
        results = self.model(image)
        return results

    def detect_frame(self, frame):
        results = self.model(frame)
        return results


class VideoProcessor(QThread):
    frame_processed = pyqtSignal(np.ndarray)
    fps_signal = pyqtSignal(float)
    progress_signal = pyqtSignal(str)
    object_count_signal = pyqtSignal(int)
    category_count_signal = pyqtSignal(dict)

    def __init__(self, video_path=None, use_camera=False, output_dir='output'):
        super().__init__()
        self.video_path = video_path
        self.use_camera = use_camera
        self.running = False
        self.paused = False
        self.mutex = QMutex()
        self.condition = QWaitCondition()
        self.detector = YoloV5Detector()
        self.out = None
        self.output_dir = output_dir

    def run(self):
        self.running = True
        if self.use_camera:
            cap = cv2.VideoCapture(0)
        else:
            cap = cv2.VideoCapture(self.video_path)

        if not cap.isOpened():
            return

        # Create unique filename based on current time
        current_time = time.strftime("%Y%m%d_%H%M%S")
        os.makedirs(self.output_dir, exist_ok=True)
        output_file = os.path.join(self.output_dir, f'output_{current_time}.mp4')

        fourcc = cv2.VideoWriter_fourcc(*'mp4v')
        fps = cap.get(cv2.CAP_PROP_FPS)
        if fps == 0:  # If fps cannot be obtained, set a default value
            fps = 30
        width = int(cap.get(cv2.CAP_PROP_FRAME_WIDTH))
        height = int(cap.get(cv2.CAP_PROP_FRAME_HEIGHT))
        self.out = cv2.VideoWriter(output_file, fourcc, fps, (width, height))

        total_frames = int(cap.get(cv2.CAP_PROP_FRAME_COUNT))
        duration = total_frames / fps

        last_time = time.time()
        frame_count = 0

        while self.running:
            self.mutex.lock()
            if self.paused:
                self.condition.wait(self.mutex)
            self.mutex.unlock()

            ret, frame = cap.read()
            if not ret:
                break
            #frame = cv2.resize(frame, (640, 640))
            results = self.detector.detect_frame(frame)
            annotated_frame = results.render()[0]
            self.frame_processed.emit(annotated_frame)

            object_count = len(results.xyxy[0])  # Number of detected objects
            categories = results.names  # Object category names
            category_counts = {category: (results.pred[0][:, -1] == i).sum().item() for i, category in categories.items()}
            non_zero_category_counts = {cat: count for cat, count in category_counts.items() if count > 0}
            self.object_count_signal.emit(object_count)
            self.category_count_signal.emit(non_zero_category_counts)

            if self.out:
                self.out.write(annotated_frame)

            frame_count += 1
            if frame_count % 10 == 0:
                current_time = frame_count / fps
                progress_text = f'{self.format_time(current_time)} / {self.format_time(duration)}'
                self.progress_signal.emit(progress_text)

            current_time = time.time()
            show_fps = 1.0 / (current_time - last_time)
            last_time = current_time
            self.fps_signal.emit(show_fps)

        cap.release()
        if self.out:
            self.out.release()

    def format_time(self, seconds):
        mins, secs = divmod(seconds, 60)
        return f'{int(mins):02}:{int(secs):02}'

    def pause(self):
        self.paused = True

    def resume(self):
        self.paused = False
        self.condition.wakeAll()

    def stop(self):
        self.running = False
        self.resume()
        self.wait()


class MainWindow(QMainWindow):
    def __init__(self):
        super().__init__()
        self.setWindowTitle('跌倒检测系统')
        self.setGeometry(100, 100, 1000, 700)

        self.layout = QVBoxLayout()

        self.label = QLabel(self)
        self.layout.addWidget(self.label)

        self.fps_label = QLabel(self)
        self.fps_label.setFont(QFont('Arial', 14))
        self.layout.addWidget(self.fps_label)

        self.object_count_label = QLabel(self)
        self.object_count_label.setFont(QFont('Arial', 14))
        self.layout.addWidget(self.object_count_label)

        self.category_count_label = QLabel(self)
        self.category_count_label.setFont(QFont('Arial', 14))
        self.layout.addWidget(self.category_count_label)

        self.progress_label = QLabel(self)
        self.progress_label.setFont(QFont('Arial', 14))
        self.layout.addWidget(self.progress_label)

        self.slider = QSlider(Qt.Horizontal)
        self.slider.setRange(0, 100)
        self.slider.setValue(0)
        self.slider.sliderPressed.connect(self.pause_video)
        self.slider.sliderReleased.connect(self.resume_video)
        self.layout.addWidget(self.slider)

        button_layout = QHBoxLayout()

        self.btn_image = QPushButton('Open Image', self)
        self.btn_image.setFont(QFont('Arial', 14))
        self.btn_image.clicked.connect(self.open_image)
        button_layout.addWidget(self.btn_image)

        self.btn_video = QPushButton('Open Video', self)
        self.btn_video.setFont(QFont('Arial', 14))
        self.btn_video.clicked.connect(self.open_video)
        button_layout.addWidget(self.btn_video)

        self.btn_camera = QPushButton('Open Camera', self)
        self.btn_camera.setFont(QFont('Arial', 14))
        self.btn_camera.clicked.connect(self.open_camera)
        button_layout.addWidget(self.btn_camera)

        self.btn_save = QPushButton('Save Result', self)
        self.btn_save.setFont(QFont('Arial', 14))
        self.btn_save.clicked.connect(self.save_result)
        button_layout.addWidget(self.btn_save)

        self.layout.addLayout(button_layout)

        self.container = QWidget()
        self.container.setLayout(self.layout)
        self.setCentralWidget(self.container)

        self.cap = None
        self.current_frame = None
        self.video_processor = None

    def open_image(self):
        self.stop_video_processing()
        file_name, _ = QFileDialog.getOpenFileName(self, 'Open Image', '', 'Images (*.png *.xpm *.jpg *.jpeg *.bmp)')
        if file_name:
            self.show_loading_message(True)
            results = YoloV5Detector().detect_image(file_name)
            self.current_frame = results.render()[0]
            self.display_image(self.current_frame)
            self.fps_label.setText('')
            self.progress_label.setText('')
            self.object_count_label.setText('')
            self.category_count_label.setText('')
            self.show_loading_message(False)

    def open_video(self):
        self.stop_video_processing()
        file_name, _ = QFileDialog.getOpenFileName(self, 'Open Video', '', 'Videos (*.mp4 *.avi *.mov *.mkv)')
        if file_name:
            self.show_loading_message(True)
            self.start_video_processing(file_name)

    def open_camera(self):
        self.stop_video_processing()
        self.show_loading_message(True)
        self.start_video_processing(use_camera=True)

    def show_loading_message(self, show):
        self.btn_image.setEnabled(not show)
        self.btn_video.setEnabled(not show)
        self.btn_camera.setEnabled(not show)
        self.btn_save.setEnabled(not show)
        if show:
            QApplication.setOverrideCursor(Qt.WaitCursor)
        else:
            QApplication.restoreOverrideCursor()

    def start_video_processing(self, video_path=None, use_camera=False):
        self.video_processor = VideoProcessor(video_path=video_path, use_camera=use_camera)
        self.video_processor.frame_processed.connect(self.display_image)
        self.video_processor.fps_signal.connect(self.update_fps)
        self.video_processor.progress_signal.connect(self.update_progress)
        self.video_processor.object_count_signal.connect(self.update_object_count)
        self.video_processor.category_count_signal.connect(self.update_category_count)
        self.video_processor.start()
        self.show_loading_message(False)

    def stop_video_processing(self):
        if self.video_processor is not None:
            self.video_processor.stop()
            self.video_processor = None

    def pause_video(self):
        if self.video_processor is not None:
            self.video_processor.pause()

    def resume_video(self):
        if self.video_processor is not None:
            self.video_processor.resume()

    def update_fps(self, fps):
        self.fps_label.setText(f'FPS: {fps:.2f}')

    def update_progress(self, progress):
        self.progress_label.setText(progress)

    def update_object_count(self, count):
        self.object_count_label.setText(f'Object Count: {count}')

    def update_category_count(self, category_counts):
        category_count_text = 'Category Counts: ' + ', '.join([f'{cat}: {count}' for cat, count in category_counts.items()])
        self.category_count_label.setText(category_count_text)

    def display_image(self, frame):
        # 将当前帧存储在实例变量中
        self.current_frame = frame

        # 将帧从 BGR(OpenCV 格式)转换为 RGB 格式
        # OpenCV 默认使用 BGR 格式,而 QImage 期望的是 RGB 格式
        rgb_frame = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)

        # 获取帧的尺寸
        height, width, channel = frame.shape

        # 计算 QImage 构造函数所需的每行字节数
        # 这等于图像的宽度乘以通道数(RGB 为 3)
        bytes_per_line = 3 * width

        # 从 RGB 帧数据创建一个 QImage
        # 使用数据、宽度、高度、每行字节数和图像格式来创建 QImage
        convert_to_Qt_format = QImage(rgb_frame.data, width, height, bytes_per_line, QImage.Format_RGB888)

        scaled_image = convert_to_Qt_format.scaled(1000, 600, aspectRatioMode=1)
        # 将缩放后的 QImage 设置为 QLabel 的图像
        self.label.setPixmap(QPixmap.fromImage(scaled_image))

        # 确保图像在 QLabel 中居中
        self.label.setAlignment(Qt.AlignCenter)

    def save_result(self):
        if self.current_frame is not None:
            file_name, _ = QFileDialog.getSaveFileName(self, 'Save Image', '', 'Images (*.png *.jpg *.jpeg *.bmp)')
            if file_name:
                cv2.imwrite(file_name, self.current_frame)
        elif self.video_processor:
            self.video_processor.stop()
            self.video_processor.wait()
            self.video_processor = None


if __name__ == '__main__':

    app = QApplication(sys.argv)
    window = MainWindow()
    window.show()
    sys.exit(app.exec_())

使用说明

  1. 加载图像/视频
    • 点击“Open Image”按钮加载待检测的图像。
    • 点击“Open Video”按钮加载待检测的视频文件。
    • 点击“Open Camera”按钮启动摄像头进行实时跌倒检测。
  2. 显示结果
    • 界面实时显示检测到的跌倒事件及其位置和时间。
    • 显示实时帧率(FPS)、进度、检测统计信息等。
  3. 保存结果
    • 点击“Save Result”按钮保存当前检测结果。

检测示例

系统能够准确检测和标注出图像或视频中的跌倒事件,如下图所示:

image-20240623154740788

image-20240623154855554

数据集获取与介绍

数据集介绍

为了训练和评估模型,我们使用了一个包含大量跌倒事件图像和视频的公开数据集。数据集包含以下内容:

  1. 图像和视频数量:数据集中包含2000个标注好的视频片段和5000张图像。
  2. 标注信息:每个视频和图像都包含详细的跌倒事件标注,包括跌倒的起始和结束时间、位置等。

数据集获取

数据集可以通过以下链接获取:

  • 公开数据集下载链接

下载后,将数据集解压到指定目录,并根据YOLOv5格式进行组织。

YOLOv5介绍

YOLOv5简介

YOLOv5(You Only Look Once Version 5)是一个先进的目标检测模型,由Ultralytics团队开发。与其前身相比,YOLOv5在速度和精度上有了显著提升,能够在实时应用中表现出色。

官方模型

YOLOv5提供了多种预训练模型,分别适用于不同的检测任务和需求。我们选择了适合跌倒检测的YOLOv5s模型,并在此基础上进行了微调,以适应具体的检测需求。

训练过程

数据准备

  1. 数据标注:使用LabelImg等工具对数据集进行标注,生成YOLOv5格式的标注文件。
  2. 数据划分:将数据集划分为训练集、验证集和测试集,比例为7:2:1。

模型训练

  1. 环境配置:在训练前,配置好所需的深度学习环境,包括安装PyTorch、YOLOv5等依赖项。

  2. 训练配置:设置训练参数,如学习率、批量大小、训练轮数等。

  3. 模型训练

    :使用以下命令启动训练:

    bash
    复制代码
    python train.py --img 640 --batch 16 --epochs 50 --data data.yaml --weights yolov5s.pt --cache
    
  4. 模型评估:在验证集上评估模型性能,记录精度、召回率、mAP等指标。

模型优化

  1. 超参数调整:根据评估结果调整学习率、批量大小、数据增强策略等超参数。
  2. 迁移学习:使用预训练模型进行迁移学习,提高模型在特定任务上的表现。

模型部署

  1. 模型保存:将训练好的模型保存为权重文件(如yolov5s_fall.pt)。
  2. 集成到系统:将模型集成到跌倒检测系统中,实现实时检测功能。

结论

本报告详细介绍了基于YOLOv5的跌倒检测系统,包括系统架构、功能实现、使用说明、检测示例、数据集获取与介绍、YOLOv5模型介绍以及训练过程。该系统通过PyQt5提供了友好的用户界面,支持图像、视频和实时摄像头的跌倒检测,具有较高的检测精度和实时性。未来可以进一步优化模型和系统,以提升跌倒检测的准确性和应用范围。

完整资料地址

  • yolov5预训练模型地址:https://github.com/ultralytics/yolov5
  • 本文项目完整链接:链接:链接:https://pan.baidu.com/s/1cd-IAjNpHefD92LQDgU93A?pwd=7j5z
    • 包含: PyQT用户交互页面、训练好的检测模型(yolov5s)、几张测试图片、一段测试视频
    • 提取码:7j5z
  • 数据集:转载链接:https://blog.csdn.net/guyuealian/article/details/130250738

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1853281.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

OpenCv形态学(一)

目录 形态学转换 结构元素 腐蚀 膨胀 开运算 闭运算 形态学梯度 顶帽 黑帽 图像轮廓 查找轮廓 绘制轮廓 形态学转换 形态变换是一些基于图像形状的简单操作。通常在二值图像上执行。它需要两个输入,一个是我们的原始图像,第二个是决定操作性…

Nginx Proxy Manager反向代理Jackett

1 说明 最近折腾nas,发现npm反向代理Jackett后出现无法访问的问题,是因为外网访问jackett (例如https://domain.com:7373/jackett/UI/Dashboard)时,url会被重定向到https://domain.com/jackett/UI/Login?ReturnUrl%2Fjackett%2FUI%2FDashbo…

基于matlab的K-means聚类图像分割

1 原理 K-means聚类算法在图像分割中的应用是基于一种无监督的学习方法,它将图像中的像素点或特征区域划分为K个不同的簇或类别。以下是K-means聚类算法用于图像分割的原理,包括步骤和公式: 1.1 原理概述 选择簇的数量(K): 首先…

《数字图像处理与机器视觉》案例一(库尔勒香梨果梗提取和测量)

一、引言 果梗是判断水果新鲜程度的重要标志,对水果的贮藏和保鲜也具有重要的参考价值。库尔勒香梨分级标准中对果梗有明确要求,要求果梗完整,但由于库尔勒香梨果梗颜色与果实接近,用传统的简单阈值分割方法难以提取。因此&#…

双指针算法专题(移动零 复写零 快乐数)

目录 前言 1. 移动零 (1)题目及示例 (2)一般思路 (3)双指针解法 2. 复写零 (1)题目及示例 (2)一般解法 (3)双指针解法 3. 快…

Kubernetes相关生态

1、Prometheus、Metrics Server与Kubernetes监控体系 简介: Prometheus 项目与 Kubernetes 项目一样,也来自于 Google 的 Borg 体系,它的原型系统,叫作 BorgMon,是一个几乎与 Borg 同时诞生的内部监控系统 Pro…

AG32 MCU Start Kit 开发板快速入门及 21天体验活动

AG32 IDE开发环境搭建-完整版 海振远科技 2024-6-18 AG32 MCU开发板的使用 使用准备 在使用开发板前,请确认已经安装好开发环境。 安装环境过程,请参考文档《AG32 开发环境搭建.pdf》 上电: 给开发板5V 供电,打开开关&#…

平面设计软件PS/AI/ID/CDR怎么选怎么下载(附教程)

随着设计行业的普遍化,平面设计软件也越来越多且功能越来越强大。平面设计软件需要在电脑上运行使用,来进行平面画面、平面文字的设计工作。如大家所了解的,Adobe Photoshop、Adobe Illustrator、CorelDRAW、Adobe InDesign是平面设计中最常用…

PostgreSQL计算 queryid 原理

数据库版本 PG 16.1 queryid 是什么 queryid 是将 sql 规范化 (normalization) 后&#xff0c;通过哈希函数计算出来的 64 位整数。 以 SELECT id, data FROM tbl_a WHERE id < 300 ORDER BY data; 这条 SQL 为例。当我们在 PG 中执行这条 sql 时&#xff0c;内核在语义…

技术性屏蔽百度爬虫已经一周了!

很久前明月就发现百度爬虫只抓取、只收录就是不给流量了&#xff0c;加上百度搜索体验越来越差&#xff0c;反正明月已经很久没有用过百度搜索&#xff0c;目前使用的浏览器几乎默认搜索都已经修改成其他搜索引擎了&#xff0c;真要搜索什么&#xff0c;一般都是必应谷歌结合着…

【设计模式深度剖析】【11】【行为型】【解释器模式】| 以算术表达式求值为例加深理解

&#x1f448;️上一篇:状态模式 设计模式-专栏&#x1f448;️ 文章目录 解释器模式定义英文原话直译 解释器模式中的角色1. 抽象表达式&#xff08;AbstractExpression&#xff09;2. 终端表达式&#xff08;TerminalExpression&#xff09;3. 非终端表达式&#xff08;Non…

Linux 特殊变量 $?

一. 说明 在 Linux 和其他类 Unix 系统中&#xff0c;$? 是一个特殊的变量&#xff0c;用于获取上一个命令的退出状态码。 退出状态码是一个整数值&#xff0c;通常用来表示命令的执行结果。 ⏹退出状态码的含义 0&#xff1a;命令成功执行。0以外的数字&#xff1a;命令执…

[SAP ABAP] 插入内表数据

语法格式 INSERT <wa> INTO <itab> INDEX <idx>. <wa>&#xff1a;代表工作区 <itab>&#xff1a;代表内表 <idx>&#xff1a;代表索引值 示例1 结果显示&#xff1a; 语法格式 INSERT <wa> INTO TABLE <itab>. <wa>&…

文件创建与查看

touch touch命令用于创建一个新的文件。 语法&#xff1a;touch Linux路径 其中路径可以是相对路径、绝对路径或者特殊路径符都可以。 改图展示了通过 touch test.txt 命令创建了一个 test.txt文件&#xff0c;其中深色的代表文件夹&#xff0c;白色的代表文件。 使用 ls -lh…

昇思25天学习打卡营第5天|模型训练|保存与加载

学AI还能赢奖品&#xff1f;每天30分钟&#xff0c;25天打通AI任督二脉 (qq.com) 模型训练 模型训练一般分为四个步骤&#xff1a; 构建数据集。定义神经网络模型。定义超参、损失函数及优化器。输入数据集进行训练与评估。 现在我们有了数据集和模型后&#xff0c;可以进行…

QTreeView双击任意列展开

一.效果 二.原理 重点是如何通过其他列的QModelIndex(假设为index),获取第一列的QModelIndex(假设为firstColumnIndex)。代码如下所示: QModelIndex firstColumnIndex = model->index(index.row(), 0, index.parent()); 这里要注意index函数的第三个参数,第三个参…

切换国内yum源

切换国内yum源 一、备份现有YUM源二、下载新的YUM源配置文件三、先清理YUM缓存再生成四、测试新的YUM源五、重启系统服务(生效可以不重启)可选 首先&#xff0c;切换国内YUM源的具体步骤取决于您使用的Linux发行版和当前的YUM源配置。以下是一般步骤&#xff0c;适用于大多数基…

学会python——对目录的操作(python实例十)

目录 1、认识Python 2、环境与工具 2.1 python环境 2.2 Visual Studio Code编译 3、遍历当前目录 3.1 代码构思 3.2 代码示例 3.3 运行结果 4、删除目录中的文件 4.1 代码构思 4.2 代码示例 4.3 运行结果 5、总计 1、认识Python Python 是一个高层次的结合了解释性…

CTF-蓝帽杯 2022 初赛Misc计算机取证题目详解

使用工具&#xff1a;Volatility、Passware Kit、Arsenal Image Mounter、DiskGenius 题目文件如下&#xff1a; 首先要知道这些文件是什么&#xff1a; dmp后缀指Dump文件&#xff0c;是windows系统中的错误转储文件。包含计算机程序运行时的内存信息的文件。通常操作系统或…

基于SpringBoot+Vue二手交易平台设计和实现(源码+LW+调试文档+讲解等)

&#x1f497;博主介绍&#xff1a;✌全网粉丝1W,CSDN作者、博客专家、全栈领域优质创作者&#xff0c;博客之星、平台优质作者、专注于Java、小程序技术领域和毕业项目实战✌&#x1f497; &#x1f31f;文末获取源码数据库&#x1f31f; 感兴趣的可以先收藏起来&#xff0c;还…