YOLOX+PyQt5交通路口智能监测平台设计与实现

1.概述

交通要道的路口上人车穿行,特别是上下班早高峰,且时常发生交通事故。因此对交通路口的车流量和人流量的监测必不可少。

2.检测模型

使用的检测模型为YOLOX模型,模型权重为训练VOC数据集得来,其中包括了二十个类别,但我们主要针对地面交通路口进行监测,选择了最关键的三个监测要素作为监测目标,分别为人、汽车和自行车。YOLOX神经网络模型介绍如下:
YOLOX模型由旷世科技提出,模型结构如下图所示。
在这里插入图片描述

主要分为两大部分,分别为主干特征提取网络和多尺度特征金字塔融合结构。

2.1 主干特征提取网络

主干特征提取网络由4个ResBlock堆叠组成,以对输入的图像进行多尺度特征提取,在这里会输出3个最终的有效特征层到特征金字塔融合结构中。

class YOLOPAFPN(nn.Module):
    def __init__(self, depth=1.0, width=1.0, in_features=("dark3", "dark4", "dark5"), in_channels=[256, 512, 1024],
                 depthwise=False, act="silu"):
        super().__init__()
        Conv = DWConv if depthwise else BaseConv
        self.backbone = CSPDarknet(depth, width, depthwise=depthwise, act=act)
        self.in_features = in_features

        self.upsample = nn.Upsample(scale_factor=2, mode="nearest")

        # -------------------------------------------#
        #   20, 20, 1024 -> 20, 20, 512
        # -------------------------------------------#
        self.lateral_conv0 = BaseConv(int(in_channels[2] * width), int(in_channels[1] * width), 1, 1, act=act)

        # -------------------------------------------#
        #   40, 40, 1024 -> 40, 40, 512
        # -------------------------------------------#
        self.C3_p4 = CSPLayer(
            int(2 * in_channels[1] * width),
            int(in_channels[1] * width),
            round(3 * depth),
            False,
            depthwise=depthwise,
            act=act,
        )

        # -------------------------------------------#
        #   40, 40, 512 -> 40, 40, 256
        # -------------------------------------------#
        self.reduce_conv1 = BaseConv(int(in_channels[1] * width), int(in_channels[0] * width), 1, 1, act=act)
        # -------------------------------------------#
        #   80, 80, 512 -> 80, 80, 256
        # -------------------------------------------#
        self.C3_p3 = CSPLayer(
            int(2 * in_channels[0] * width),
            int(in_channels[0] * width),
            round(3 * depth),
            False,
            depthwise=depthwise,
            act=act,
        )

        # -------------------------------------------#
        #   80, 80, 256 -> 40, 40, 256
        # -------------------------------------------#
        self.bu_conv2 = Conv(int(in_channels[0] * width), int(in_channels[0] * width), 3, 2, act=act)
        # -------------------------------------------#
        #   40, 40, 256 -> 40, 40, 512
        # -------------------------------------------#
        self.C3_n3 = CSPLayer(
            int(2 * in_channels[0] * width),
            int(in_channels[1] * width),
            round(3 * depth),
            False,
            depthwise=depthwise,
            act=act,
        )

        # -------------------------------------------#
        #   40, 40, 512 -> 20, 20, 512
        # -------------------------------------------#
        self.bu_conv1 = Conv(int(in_channels[1] * width), int(in_channels[1] * width), 3, 2, act=act)
        # -------------------------------------------#
        #   20, 20, 1024 -> 20, 20, 1024
        # -------------------------------------------#
        self.C3_n4 = CSPLayer(
            int(2 * in_channels[1] * width),
            int(in_channels[2] * width),
            round(3 * depth),
            False,
            depthwise=depthwise,
            act=act,
        )

    def forward(self, input):
        out_features = self.backbone.forward(input)
        [feat1, feat2, feat3] = [out_features[f] for f in self.in_features]

        P5 = self.lateral_conv0(feat3)

        P5_upsample = self.upsample(P5)

        P5_upsample = torch.cat([P5_upsample, feat2], 1)
        P5_upsample = self.C3_p4(P5_upsample)

        P4 = self.reduce_conv1(P5_upsample)
        P4_upsample = self.upsample(P4)
        P4_upsample = torch.cat([P4_upsample, feat1], 1)
        P3_out = self.C3_p3(P4_upsample)
        P3_downsample = self.bu_conv2(P3_out)
        P3_downsample = torch.cat([P3_downsample, P4], 1)
        P4_out = self.C3_n3(P3_downsample)
        P4_downsample = self.bu_conv1(P4_out)
        P4_downsample = torch.cat([P4_downsample, P5], 1)
        P5_out = self.C3_n4(P4_downsample)

        return (P3_out, P4_out, P5_out)

return (P3_out,P4_out,P5_out)表示主干特征提取网络输出的3个最终特征层。

2.2 多尺度特征金字塔融合结构

在特征金字塔结构中,来自主干特征提取网络的3个有效特征层会进行多尺度的特征融合。因为在高分辨率的特征层具有的细节信息较多,而低分辨率的特征层具有的语义信息较多。首先会通过对低分辨率特征层P5_out进行上采样操作,以便能够和P4_out进行拼接融合。经过多尺度特征金字塔融合结构输出的特征层有三个,以能够实现不同尺寸目标的检测。
多尺度特征金子塔融合结构的代码如下:

class YOLOXHead(nn.Module):
    def __init__(self, num_classes, width=1.0, in_channels=[256, 512, 1024], act="silu", depthwise=False, ):
        super().__init__()
        Conv = DWConv if depthwise else BaseConv

        self.cls_convs = nn.ModuleList()
        self.reg_convs = nn.ModuleList()
        self.cls_preds = nn.ModuleList()
        self.reg_preds = nn.ModuleList()
        self.obj_preds = nn.ModuleList()
        self.stems = nn.ModuleList()

        for i in range(len(in_channels)):
            self.stems.append(
                BaseConv(in_channels=int(in_channels[i] * width), out_channels=int(256 * width), ksize=1, stride=1,
                         act=act))
            self.cls_convs.append(nn.Sequential(*[
                Conv(in_channels=int(256 * width), out_channels=int(256 * width), ksize=3, stride=1, act=act),
                Conv(in_channels=int(256 * width), out_channels=int(256 * width), ksize=3, stride=1, act=act),
            ]))
            self.cls_preds.append(
                nn.Conv2d(in_channels=int(256 * width), out_channels=num_classes, kernel_size=1, stride=1, padding=0)
            )

            self.reg_convs.append(nn.Sequential(*[
                Conv(in_channels=int(256 * width), out_channels=int(256 * width), ksize=3, stride=1, act=act),
                Conv(in_channels=int(256 * width), out_channels=int(256 * width), ksize=3, stride=1, act=act)
            ]))
            self.reg_preds.append(
                nn.Conv2d(in_channels=int(256 * width), out_channels=4, kernel_size=1, stride=1, padding=0)
            )
            self.obj_preds.append(
                nn.Conv2d(in_channels=int(256 * width), out_channels=1, kernel_size=1, stride=1, padding=0)
            )

    def forward(self, inputs):
        outputs = []
        for k, x in enumerate(inputs):
            x = self.stems[k](x)

            cls_feat = self.cls_convs[k](x)
            cls_output = self.cls_preds[k](cls_feat)

            reg_feat = self.reg_convs[k](x)
            reg_output = self.reg_preds[k](reg_feat)

            obj_output = self.obj_preds[k](reg_feat)

            output = torch.cat([reg_output, obj_output, cls_output], 1)
            outputs.append(output)
        return outputs

这里return返回的outputs是一个列表,长度为3,为多尺度特征金字塔融合结构的输出结果。

3.系统可视化

系统使用了PyQt5作为可视化工具,PyQt和C++中的qt类似,具有良好的交互性,包含了日常开发常用的控件,像显示提示控件QLabel,按钮QPushbotton,输入框控件TextBrowser等,并且实现了信号槽机制,能够简单快速的获取页面控件和响应事件。系统整体效果如下:
在这里插入图片描述

系统整体布局为线性垂直布局,从上至下依次为系统标题、功能区和显示区。显示区为监测的实时画面显示,这里通过QLabel控件来作为一个容器,来接受opencv库中的视频流。
系统实现了实时摄像头监测功能和上传视频监测功能,

3.1 打开摄像头

当用户点击打开摄像头后,系统将打开电脑的默认摄像头进行画面获取,并将获取的画面进行监测,这里最重要的代码就是定时器函数,因为用户点击打开摄像头后,只是一个瞬间事件,而系统需要将摄像头拍摄的画面进行实时检测,这是一个连续性事件,而下面这两行代码很重要

self.timer_camera = QtCore.QTimer()
self.timer_camera.timeout.connect(self.show_camera)  # 将timeout绑定槽函数show_camera

self.timer_camera = QtCore.QTimer()定义了定时器,并通过信号槽self.timer_camera.timeout.connect(self.show_camera)进行了检测事件绑定。

打开摄像头按钮代码如下:

    def open_camera_btn(self):
        if not self.timer_camera.isActive():  # 定时器未启动
            flag = self.cap.open(self.CAM_NUM)
            if flag == False:
                msg = QtWidgets.QMessageBox.warning(self.window, '警告!', "请检查摄像头是否连接正确",
                                                    buttons=QtWidgets.QMessageBox.Ok)
            else:
                self.timer_camera.start()  # 设置30毫秒后,定时器将每隔30毫秒调用timeout函数
                self.open_camera.setText('关闭监测')

        # 关闭检测按钮事件
        else:
            self.timer_camera.stop()
            self.cap.release()
            self.label_show_camera.clear()  # 清空视频显示区域
            self.open_camera.setText('开始监测')

这里就比较简单了,只需判断当前定时器是否被打开,如果没有就去打开摄像头,并启动定时器,再把摄像头的提示文字修改一下,改为“关闭摄像头”,当用户再次点击按钮时,就可以关闭摄像头,并将定时器暂定,一个按钮实现启动和关闭功能。

3.2 上传视频监测

上传视频监测按钮整体功能与打开摄像头类似,只需要将视频流进行更改,这里通过QFileDialog.getOpenFileName()函数来实现打开资源文件窗口进行视频选择,并将选择的视频文件的绝对路径进行返回,这样就能将视频的绝对路径传给opencv进行视频流读取:

上传视频文件监测按钮代码如下:

    def video_detect_btn(self):
        fileUrl, _ = QFileDialog.getOpenFileName(self, "Open Video File", QDir.currentPath(),
                                                  "Video Files (*.mp4 *.avi *.mov *.wmv);;")
        # 视频选择成功
        if fileUrl:
            print(fileUrl)
            self.label_video_url.setText(fileUrl)
            if not self.timer_camera.isActive():  # 定时器未启动

                flag = self.cap.open(fileUrl)
                if flag == False:
                    msg = QtWidgets.QMessageBox.warning(self.window, '警告!', "请检查摄像头是否连接正确",
                                                        buttons=QtWidgets.QMessageBox.Ok)
                else:
                    self.timer_camera.start()  # 设置30毫秒后,定时器将每隔30毫秒调用timeout函数

点击上传监测视频后,将打开资源文件窗口进行视频文件选择,效果如下:
在这里插入图片描述

用户选择完成后,将开始逐帧检测。

3.3 统计结果显示

页面中的结果显示也采用了垂直线性布局的方式,效果如下:
在这里插入图片描述

目标种类和统计结果两个垂直线性布局包裹在一个QWidget控件中,QWidget控件使用水平线性布局的方式。行人、汽车和自行车统计结果在show_res_num()中实现,dict是一个字典类型的变量,key为目标类型,value为目标类别对应的个数。
代码为:

        label_list = [*dict]
        all = 0
        # for i in label_list:
        #     all = all + int(dict.get(i))
        # self.all_result.setText(str(all))
        if 'person' in label_list:
            self.person_num.setText(str(dict['person']))
            all += int(dict['person'])
        else:
            self.person_num.setText('0')
        if 'car' in label_list:
            self.car_num.setText(str(dict['car']))
            all += int(dict['car'])
        else:
            self.car_num.setText('0')
        if 'bicycle' in label_list:
            self.light_num.setText(str(dict['bicycle']))
            all += int(dict['bicycle'])
        else:
            self.light_num.setText('0')
        self.all_result.setText(str(all))

4.效果

在这里插入图片描述

5.总结

这里的监测模型使用的是旷视科技提出的YOLOX检测模型,并且权重文件也使用的是官方提供的s版,能够检测的类别有二十种,这里只选取了三种监测模型进行了统计结果显示。程序的入口为main函数,并提供了源码。并且较容易对源码进行修改,以训练自己的数据集,针对自己的应用领域。
YOLOX模型github地址:https://github.com/Megvii-BaseDetection/YOLOX
VOC2012数据集地址:https://host.robots.ox.ac.uk/pascal/VOC/voc2012/VOCtrainval_11-May-2012.tar 官方工具文档:http://host.robots.ox.ac.uk/pascal/VOC/voc2012/htmldoc/index.html

6.环境和exe文件运行说明

6.1 源码运行的python环境

python == 3.8
PyQt5 == 5.15.2
torch == 1.9.1+cu111

6.2 exe文件运行说明

在这里插入图片描述
在这里插入图片描述

main.exe可执行文件较大,因为其中包含了torch库文件,main.py文件下载链接:main.exe下载地址
提取码:8cfk
将main.py文件下载后需要放到output文件夹中,output文件下还有两个文件夹分别为ui和model_data。其中ui文件夹中包含了系统运行所需的页面ui文件;model_data文件夹中包含了检测模型的权重文件和类别文件。双击main.exe文件运行时,可能需要等待一点时间等待系统启动。
参考文章: https://blog.csdn.net/weixin_44791964/article/details/120476949

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

前端:Vue学习 - 购物车项目

前端:Vue学习 - 购物车项目 1. json-server,生成后端接口2. 购物车项目 - 实现效果3. 参考代码 - Vuex 1. json-server,生成后端接口 全局安装json-server,json-server官网为:json-server npm install json-server -…

【运算放大器】输入失调电压和输入偏置电流(2)实例计算

概述 根据上一篇文章的理论,分别计算没有输入电阻和有输入电阻两种情况下的运放总输出误差。例题来自于TI高精度实验室系列课程。 目录 概述实例计算 1:没有输入电阻实例计算 2:有输入电阻总结 实例计算 1:没有输入电阻 要求&am…

antdesgin table 组件下载成excel

文章目录 发现宝藏一、需求二、报错 发现宝藏 前些天发现了一个巨牛的人工智能学习网站,通俗易懂,风趣幽默,忍不住分享一下给大家。【宝藏入口】。 一、需求 原组件如下,需要添加下载功能 import React, { useState } from rea…

React间的组件通信

一、父传子&#xff08;props&#xff09; 步骤 父组件传递数据&#xff0c;子组件标签身上绑定属性子组件接收数据&#xff0c;props的参数 // 子组件 function Son(props) {return (<div>this is Son, {props.name}</div>) }// 父组件 function App() {const n…

通信原理-实验六:实验测验

实验六 实验测验 一&#xff1a;测验内容和要求 测试需要完成以下几个步骤&#xff1a; 配置好以下网络图&#xff1b;占总分10%&#xff08;缺少一个扣一分&#xff09;根据下面图配置好对应的IP和网关以及路由等相关配置&#xff0c;保证设备之间连通正常&#xff1b;占总…

谷粒商城实战笔记-60-商品服务-API-品牌管理-效果优化与快速显示开关

文章目录 一&#xff0c;显示状态列改为switch开关二&#xff0c;监听状态改变 首先&#xff0c;把ESLint语法检查关掉&#xff0c;因为这个语法检查过于严格&#xff0c;在控制台输出很多错误信息&#xff0c;干扰开发。 在build目录下下webpack.base.conf.js中&#xff0c;把…

matlab实验:实验六MATLAB 数值计算与符号运算

题目1&#xff1a;&#xff08;线性方程组数值求解&#xff09; 1&#xff0e; 用不同的方法求解下面方程&#xff1a;&#xff08;方程原式参考 P369 实验 10&#xff0c;第 1 题&#xff09; 第 1 种&#xff0c;左除和求逆函数(inv) 第 2 种 &#xff0c; 用 符 号 运 算 的…

鸿蒙(HarmonyOS)自定义Dialog实现时间选择控件

一、操作环境 操作系统: Windows 11 专业版、IDE:DevEco Studio 3.1.1 Release、SDK:HarmonyOS 3.1.0&#xff08;API 9&#xff09; 二、效果图 三、代码 SelectedDateDialog.ets文件/*** 时间选择*/ CustomDialog export struct SelectedDateDialog {State selectedDate:…

数据结构和算法入门

1.了解数据结构和算法 1.1 二分查找 二分查找&#xff08;Binary Search&#xff09;是一种在有序数组中查找特定元素的搜索算法。它的基本思想是将数组分成两半&#xff0c;然后比较目标值与中间元素的大小关系&#xff0c;从而确定应该在左半部分还是右半部分继续查找。这个…

Hyperledger Fabric 网络体验 - 网络启动过程概览

进入fabric-samples/test-network目录&#xff0c;执行指令&#xff1a; ./network.sh up -i 2.5执行完指令能看到fabric已经启动。 作为第一次Fabric网络体验&#xff0c;网络启动主要包含三个操作&#xff0c;分别是生成配置文件、启动网络和操作网络。 配置文件 使用cr…

数驭未来,景联文科技构建高质大模型数据库

国内应用层面的需求推动AI产业的加速发展。根据IDC数据预测&#xff0c;预计2026年中国人工智能软件及应用市场规模会达到211亿美元。 数据、算法、算力是AI发展的驱动力&#xff0c;其中数据是AI发展的基石&#xff0c;中国的数据规模增长速度预期将领跑全球。 2024年《政府工…

部署jar包遇到“zip file closed”和“ JCE cannot authenticate the provider BC”

一&#xff1a;导致原因 1、是因为自己打包时使用的jdk8而后运行时使用的是jdk17&#xff0c;jdk版本不一致导致的文件类型异常 二&#xff1a;报错截图 三&#xff1a;解决问题 1、使用jdk几打包就使用jdk几运行&#xff0c;列如我使用的是jdk8打包&#xff0c;使用jdk8运行…

甲方怒斥!!!为什么媒体不按原稿发布?

传媒如春雨&#xff0c;润物细无声&#xff0c;大家好&#xff0c;我是51媒体网胡老师。 前几天执行了一个媒体邀约的项目&#xff0c;邀约媒体参会&#xff0c;以及活动现场一切都很顺利&#xff0c;稿件同步的很晚&#xff0c;但还是让几个媒体连夜进行了刊登报道&#xff0…

MySQL作业五

1. 创建表goods&#xff0c;orders 2. 向商品表中插入商品记录 3. 触发器操作 3.1 建立触发器&#xff0c;订单表中增加订单数量后&#xff0c;商品表商品数量同步减少对应的商品订单出数量,并测试 3.2 建立触发器&#xff0c;实现功能:客户取消订单&#xff0c;恢复商品表对应…

五、工厂方法模式

文章目录 1 基本介绍2 案例2.1 Drink 抽象类2.2 Tea 类2.3 Coffee 类2.4 DrinkFactory 抽象类2.5 TeaFactory 类2.6 CoffeeFactory 类2.7 Client 类2.8 Client 类运行结果2.9 总结 3 各角色之间的关系3.1 角色3.1.1 Product ( 抽象产品 )3.1.2 ConcreteProduct ( 具体产品 )3.1…

如何开启或者关闭 Windows 安全登录?

什么是安全登录 什么是 Windows 安全登录呢&#xff1f;安全登录是 Windows 附加的一个组件&#xff0c;它可以在用户需要登录的之前先将登录界面隐藏&#xff0c;只有当用户按下 CtrlAltDelete 之后才出现登录屏幕&#xff0c;这样可以防止那些模拟登录界面的程序获取密码信息…

【Apache Doris】数据副本问题排查指南

【Apache Doris】数据副本问题排查指南 一、问题现象二、问题定位三、问题处理 本文主要分享Doris中数据副本异常的问题现象、问题定位以及如何处理此类问题。 一、问题现象 问题日志 查询报错 Failed to initialize storage reader, tablet{tablet_id}.xxx.xxx问题说明 查…

SSD基本架构与工作原理

SSD的核心由一个或多核心的CPU控制器、DRAM缓存以及多个NAND闪存芯片组成。CPU控制器负责管理所有读写操作&#xff0c;并通过DRAM缓存存储映射表等元数据&#xff0c;以加速寻址过程。 NAND闪存则是数据存储的实际介质&#xff0c;其组织结构从大到小依次为通道&#xff08;包…

Springboot项目远程部署gitee仓库(docker+Jenkins+maven+git)

创建仓库 创建一个Springboot项目&#xff0c;勾选web将该项目创建git本地仓库&#xff0c;再创建远程仓库推送上去 创建TestController RestControllerRequestMapping("/test")public class TestController {GetMapping("/hello")public String sayHell…