ARTICLE DETAIL

资讯详情

深耕网站建设、视觉设计与SEO优化的一线实战洞察。

YOLOv5+OpenCV+PyQt5人员离岗检测告警系统

YOLOv5+OpenCV+PyQt5人员离岗检测告警系统 简介一套基于卷积神经网络、PyQt5与OpenCV的人员离岗检测告警系统完整工程面向毕业设计、课程设计及深度学习视觉应用开发者。系统支持加载本地视频或网络视频流通过自定义危险区域坐标对固定视角下的人员离岗行为进行实时识别与告警并具备完整PyQt交互界面便于适配不同监控场景。压缩包共221个文件约150.88MB涵盖Python源码、yaml配置文件、模型权重(pt)、界面ui/qrc资源、mp4运行演示视频及md使用说明等覆盖模型训练到界面部署全链路。使用说明整理了环境创建、依赖安装与危险区域坐标配置等关键信息另附训练图片和多段测试视频便于比对效果。已有342人学习下载适合需要快速搭建离岗检测系统、研究卷积神经网络应用落地的开发者二次开发与调试。1. 为什么人员离岗检测的关键不在检测而在区域判定固定视角的监控画面里判断一个人是不是真的离开工位难点不在“有没有人”这个分类而在两个容易被忽略的细节一是人形检测框和岗位区域之间到底怎么算“在里面”二是无人的持续时间如何做防抖。这套基于 YOLOv5 v6.1 的卷积神经网络 OpenCV PyQt5 人员离岗检测告警系统核心并不是把目标检测跑通而是在固定背景下去定义危险区域再用检测框中心点判断该区域内是否持续无人。项目结构直白适合毕业设计、课程设计以及安全监管类的演示场景调优空间也都集中在坐标和阈值上。2. 环境搭建与依赖管理Python3.8、PyQt5、OpenCV 的版本约束拿到压缩包之后内部包含main.py、提取背景.py、requirements.txt、模型权重文件、运行视频以及test1.jpg、test2.jpg、train_batch0.jpg等用于快速验证的图片。这种项目通常不是单文件脚本而是 PyQt5 界面 OpenCV 视频流 YOLOv5 推理三层结构。如果直接在系统 Python 环境中安装依赖很容易遇到 opencv-whell 和 PyQt5 的 Qt 插件互相抢占或者 torch 版本过新导致 YOLOv5 v6.1 不兼容。因此第一步不是打开 main.py而是先搭一个干净的 Python 3.8 虚拟环境。2.1 Anaconda 创建虚拟环境与镜像加速摘要里要求的流程是先在 Anaconda 中创建 Python 3.8 环境再在 PyCharm 中导入该环境。这个顺序不能反过来否则 PyCharm 会默认使用基础环境把 site-packages 搞得一团乱。推荐命令如下conda create -n duty python3.8 -y conda activate duty pip install -r requirements.txt -i https://pypi.tuna.tsinghua.edu.cn/simple选择 Python 3.8 的原因是 YOLOv5 v6.1 在自动化测试时覆盖了 3.8而 PyQt5 5.15.x 和 torch 1.10 到 1.13 这一区间内对 Python 3.8 的 ABI 兼容性最稳定。如果直接用 Python 3.10 或 3.11安装 opencv-python 时可能拿到新版预编译包底层依赖的 Qt 版本会和 PyQt5 冲突出现 QThread 崩溃或者cv2.imshow被强行关闭。这里有个常见的安装错误MODULE_NOT_FOUND_ERROR: No module named opencv。这是因为 pip 包名不叫 opencv而是 opencv-python。项目使用import cv2所以 requirements.txt 中必须写opencv-python。如果只跑后台检测不显示窗口可以换成opencv-python-headless但本项目的告警界面需要实时绘制危险区域建议保留完整版 OpenCV。2.2 PyQt5 与 OpenCV 的共存验证安装完成后不要急着跑 main.py先用一条命令验证两个库能否同时导入import cv2 from PyQt5.QtCore import PYQT_VERSION_STR print(cv2.__version__) print(PYQT_VERSION_STR) cap cv2.VideoCapture(0) print(cap.isOpened()) cap.release()这段代码同时加载了 OpenCV 的 VideoCapture 和 PyQt5 的 QtCore。如果版本冲突通常会在这个阶段输出Failed to create Qt platform plugin或直接 segment fault。项目推荐的依赖版本如下实测时可以对照自查依赖包推荐版本区间用途python3.8.x解释器torch1.10.0 ~ 1.13.0YOLOv5 卷积神经网络推理torchvision与 torch 匹配模型前处理opencv-python4.5.5 ~ 4.8.0视频读取、绘制、坐标转换PyQt55.15.2 ~ 5.15.10主界面numpy1.21 ~ 1.23图像数组运算提示不要在import PyQt5之后再调用cv2.imshow()这两个库的事件循环会互相抢占。正式运行时OpenCV 只负责读帧和绘制界面显示全部交给 PyQt5 的 QLabel。2.3 requirements.txt 与 Dockerfile 的存在意义压缩包里有 Dockerfile但这个方法在国内网络环境下并不总是顺畅。Docker 里跑 PyQt5 需要额外配置 X11 转发摄像头设备也要映射对调试不友好。Dockerfile 更适合把 YOLOv5 推理单独做成无界面的后台服务。日常开发还是用 Anaconda PyCharm 更直接。requirements.txt 中常见依赖包括 torch、torchvision、PyYAML、matplotlib、tqdm、PyQt5、opencv-python。matplotlib 和 tqdm 仅用于 YOLOv5 训练可视化如果只做推理可以去掉但为避免 main.py 里隐式引用建议原样保留。路径不要出现中文否则 Windows 下 OpenCV 读取模型和视频都可能返回空对象。3. YOLOv5 v6.1 模型加载与 OpenCV 实时帧处理系统始终在做的是一件事从摄像头或视频文件中取出一帧图像把这一帧交给卷积神经网络推理得到人形检测框再根据检测框和危险区域的相对位置决定是否告警。这一章先把“取帧 推理”这条链路写清楚。3.1 从本地权重加载 YOLOv5 模型YOLOv5 v6.1 的主干网络是 CSPDarknet53它的特征提取过程全部由卷积层完成属于典型的 CNN 目标检测模型。项目根目录的PyQt5-YOLOv5-yolov5_v6.1.iml说明这是一个 PyCharm 工程模型代码大概率以本地目录形式存放在工程内。加载方式建议使用 torch.hubimport torch device torch.device(cuda if torch.cuda.is_available() else cpu) model torch.hub.load(., custom, pathweights/best.pt, sourcelocal) model.conf 0.35 model.iou 0.45 model.classes [0] model.to(device)这里的sourcelocal表示从当前项目目录加载 YOLOv5 源码而不是从 GitHub 下载。pathweights/best.pt指向训练好或预训练好的权重文件如果使用官方预训练权重则替换为yolov5s.pt。model.classes [0]是过滤出 COCO 类别中的 person这一步能让检测结果只留下人员框避免桌子、椅子、显示器造成的干扰。如果运行后发现测试图test1.jpg没有任何输出优先检查权重路径和model.classes。model.conf 0.35表示置信度阈值低于 0.35 的检测框都会被丢弃。对远处小目标可以降到 0.25但误检会增加近景工位可以提高到 0.4。3.2 对视频帧进行检测并提取人员框模型接收的是 BGR 数组YOLOv5 内部会自动处理 letterbox 和颜色空间。推理代码只需要把 OpenCV 读到的帧直接传入results model(frame, size640) det results.pandas().xyxy[0] person_boxes det[det[class] 0]size640是输入到网络的图像尺寸。YOLOv5 会先把帧缩放填充到 640 x 640再交给卷积网络。这里有一个容易忽略的点results.pandas().xyxy[0]的每一行包含xmin, ymin, xmax, ymax, confidence, class, name。过滤条件应该使用class而不是name因为name可能随模型数据集变化。person_boxes里的坐标是相对于原始图像尺寸的不是 640 缩放宽高。这一点很重要绘制矩形时直接用cv2.rectangle(frame, (int(row.xmin), int(row.ymin)), ...)即可不需要再做坐标缩放。3.3 OpenCV 视频读取与跳帧控制系统支持本地视频和网络视频流。OpenCV 读取的核心是cv2.VideoCapture但实时推理时不能每一帧都跑一次 YOLOv5否则帧率会降到个位数。常见做法是做跳帧推理cap cv2.VideoCapture(video_path) cap.set(cv2.CAP_PROP_BUFFERSIZE, 3) frame_skip 2 frame_count 0 while cap.isOpened(): ret, frame cap.read() if not ret: break frame_count 1 if frame_count % frame_skip ! 0: continue person_boxes detect_person(frame, model)frame_skip 2表示每 2 帧推理一次其余帧沿用上一次的检测结果。30fps 的视频经过跳帧后只需要处理 15fps能大幅降低 GPU 占用。CAP_PROP_BUFFERSIZE对 RTSP 流有效减小缓冲可以降低延迟但对本地视频文件没有作用。3.4 OpenCV 帧转换为 PyQt5 QImageOpenCV 读出的颜色空间是 BGR而 Qt 的 QImage 使用 RGB不转换直接显示会让画面偏蓝红。转换函数如下rgb cv2.cvtColor(frame, cv2.COLOR_BGR2RGB) h, w, ch rgb.shape bytes_per_line ch * w qt_img QImage(rgb.data, w, h, bytes_per_line, QImage.Format_RGB888) label.setPixmap(QPixmap.fromImage(qt_img).scaled(label.size(), Qt.KeepAspectRatio))bytes_per_line必须等于ch * w否则图像会出现斜向撕裂。QImage只保存了rgb.data的指针不持有数据所有权所以rgb这个变量必须在整个界面刷新过程中存活。最简单的做法是把rgb保存成类成员变量避免被垃圾回收导致花屏。4. 危险区域标定与离岗判定background.png 和 main.py 第 161 行这一部分是项目最容易踩坑的地方。很多人把背景图功能理解为随便截一帧然后画个框结果人还没离开就触发告警。实际项目中提取背景.py生成background.png再用画图工具读取坐标修改 main.py 第 161 行左右这个流程每一步都有物理意义。4.1 提取背景.py 的原理固定视角场景下视频里每一帧都包含当前时刻的人和物。如果直接取某一帧作为背景画出来的危险区域可能贴着人的轮廓视觉上没问题但运行时人员检测框的中心点很容易落在区域外。因此项目在开始标定前先做背景提取。常见实现是多帧取中值或均值import cv2 import numpy as np cap cv2.VideoCapture(duty_video.mp4) frames [] for _ in range(50): ret, frame cap.read() if not ret: break frame cv2.resize(frame, (1280, 720)) frames.append(frame) bg np.median(np.stack(frames), axis0).astype(np.uint8) cv2.imwrite(background.png, bg)这里取 50 帧画面进行中值合成。中值比均值更抗稀疏前景干扰因为人只在部分帧中出现中值能把这部分像素过滤掉。需要说明的是如果检测场景里有长时间停留的物体比如有人靠在工位上不动它会成为背景的一部分。这样生成的背景图在运行时可能让人形框和背景融为一体出现检测不到人的问题。所以提取背景时要确保画面中人员离开。4.2 从画图工具读取坐标并写入 main.py用 Windows 自带“画图”打开background.png。鼠标移到目标区域时左下角会显示该点的 x 和 y 像素坐标。危险区域用矩形的左上顶点和右下顶点表示例如鼠标在工位左上角读到(420, 260)在右下角读到(760, 530)。修改 main.py 第 161 行附近的代码zone ((420, 260), (760, 530))左上顶点对应xmin, ymin右下顶点对应xmax, ymax。写反后 OpenCV 绘制矩形虽然不报错但判定逻辑中的大小比较会失效。这里还要注意如果运行提取背景.py时对视频做了 resize那么画图读到的坐标也必须对应同一个分辨率。比如背景图是 1280x720输入视频是 640x480需要手动把坐标乘以系数或者在背景提取脚本中不改变尺寸。提示改完坐标后重新运行 main.py应先在测试图上绘制半透明危险区域确认。OpenCV 的cv2.rectangle没有透明度参数可以用cv2.addWeighted把填充层和原图叠加避免绘制后盖住人员。4.3 人员是否在岗的判定逻辑模型检测出所有人形框后系统需要判断这些框是否落在zone内。项目推荐使用中心点而不是检测框与危险区域的 IOU。关键代码逻辑如下def center_in_zone(xyxy, zone): x1, y1, x2, y2 xyxy cx (x1 x2) / 2 cy (y1 y2) / 2 zx1, zy1, zx2, zy2 zone[0][0], zone[0][1], zone[1][0], zone[1][1] return zx1 cx zx2 and zy1 cy zy2 occupied False for _, row in person_boxes.iterrows(): if center_in_zone([row.xmin, row.ymin, row.xmax, row.ymax], zone): occupied True break使用中心点的原因是目标检测框的宽高会随着人的站姿、坐姿、手臂伸展幅度变化。比如一个人坐在工位上把手臂抬起来IOU 可能瞬间降到危险区域之外实际上人并没有离岗。中心点具备更强的稳健性只要躯干位置仍在区域内判定就保持有人。缺点是弯腰严重时中心点可能偏移到区域外此时可以改用检测框底部中心点因为这个位置更接近人脚部适用于有固定工位的场景。4.4 无人持续时长与告警复位离岗不能一秒钟判定否则只要检测框抖动一次就误报。常见的实现是维护两个时间变量empty_start None if occupied: empty_start None else: if empty_start is None: empty_start time.time() elif time.time() - empty_start absence_sec: trigger_alarm(zone_id)absence_sec是无人持续阈值项目里通常设为 5 秒或 10 秒。这里需要处理告警复位的时间差人员回到工位后occupied变为 True状态恢复告警关闭。如果希望告警维持一段时间可以加入冷却时间cooldown_sec允许在人员离开又回来时至少保留 3 秒告警。状态机逻辑放到 PyQt5 线程里按检测帧轮询即可。4.5 多区域扩展的坐标设计有些方案需要同时监管多个座位这时可以把zone改成列表zones [((420, 260), (760, 530)), ((800, 260), (1100, 540))]每个区域单独维护empty_start和告警状态。判定逻辑复用center_in_zone对每个区域分别遍历person_boxes。界面显示时用不同颜色的矩形标识区域红色表示告警绿色表示有人。多区域坐标读取仍然用画图工具逐个记录两个顶点坐标不需要修改背景提取逻辑。5. 告警联动与 PyQt5 界面交互QThread 与状态机如果直接在 PyQt5 主线程里跑 YOLOv5 推理视频画面会卡顿窗口拖动无响应甚至弹出 “QThread: Destroyed while thread is running” 错误。告警系统的可靠性取决于线程分离和状态机设计。5.1 使用 QThread 隔离视频检测循环视频循环应放在 QThread 的run()方法中通过信号将处理后的帧和告警状态传回主线程。一个精简的线程类如下class DetectThread(QThread): frame_signal pyqtSignal(QImage) alarm_signal pyqtSignal(bool, int) def run(self): cap cv2.VideoCapture(self.video_source) while not self.isInterruptionRequested(): ret, frame cap.read() if not ret: break person_boxes detect_person(frame, self.model) occupied, empty_seconds check_zones(person_boxes, self.zones) qt_img convert_to_qimage(frame) self.frame_signal.emit(qt_img) self.alarm_signal.emit(occupied, empty_seconds) cap.release()pyqtSignal是线程安全的Qt 主线程会通过事件队列接收信号并更新界面。不要在子线程里直接调用label.setPixmap()这会绕过 Qt 的事件循环轻则刷新异常重则崩溃。线程退出时一定要释放摄像头否则再次点击开始按钮时摄像头仍被占用报[ WARN:0] Failed to open camera。5.2 告警方式的三级联动项目支持界面告警和声音告警使用方可以根据现场需求扩展为平台通知。简单的 HTTP 告警在内部网络环境中非常实用import requests def send_alarm(zone_id, empty_seconds): payload {zone_id: zone_id, empty_seconds: empty_seconds} try: requests.post(http://192.168.1.100:8080/alert, jsonpayload, timeout2) except requests.exceptions.ConnectionError: passtimeout2是必须的。如果告警服务器不可达默认请求会阻塞几十秒导致检测线程积压大量视频帧。这里应该把网络异常吞掉只记录日志不影响检测主流程。声音告警可以使用 PyQt5 的QSound播放 WAV 文件或者使用winsound.Beep后者在 Windows 上不需要额外资源文件。5.3 检测参数与模型选择YOLOv5 的不同权重对离岗检测的影响非常直接场景推荐权重输入尺寸conf说明单工位近景yolov5s.pt6400.35速度快检测稳定多工位远景yolov5m.pt9600.25小目标召回率更高低性能机器yolov5n.pt4160.4帧率高远距离易漏检conf的调整要依赖现场验证。如果人员形框频繁闪烁表现为同一人在连续帧中被识别和丢失可以把iou阈值提高到 0.6或者在多帧结果之间做检测框平滑。需要注意的是跳帧检测后平滑的意义不大因为两次推理之间帧号并不连续。6. 部署排错与离岗判定参数验证系统进入现场部署后最常遇到的是视频流打开失败和离岗误报。下面给出一套可以直接复用的排查与验证方法。6.1 OpenCV 读取 RTSP 失败的处理顺序当cap.isOpened()返回 False先不要怀疑 YOLOv5问题基本出在 OpenCV 的 ffmpeg 后端。设置超时参数可以减少卡死时间cap cv2.VideoCapture(rtsp_url, cv2.CAP_FFMPEG) cap.set(cv2.CAP_PROP_OPEN_TIMEOUT_MSEC, 5000) cap.set(cv2.CAP_PROP_READ_TIMEOUT_MSEC, 5000) cap.set(cv2.CAP_PROP_BUFFERSIZE, 3)CAP_PROP_BUFFERSIZE3能限制 OpenCV 内部缓冲避免读取到延迟十几秒的旧帧。如果 RTSP 拉流画面正常但仍然无法打开先用 VLC 验证地址。对于海康、大华等设备需要配置 RTSP 路径中的 stream 编码部分设备默认输出 H.265OpenCV 的 ffmpeg 版本可能不支持需要在设备端改成 H.264。6.2 离岗判定阈值的量化验证调整absence_sec时不要靠肉眼观察。从项目运行视频里截取一段十分钟的片段人工记录每一秒是否有人再和算法输出对比。简单的验证脚本可以按时间窗口统计def verify(labels, detections, threshold5): tp fp fn 0 for label, det in zip(labels, detections): if label 1 and det 1: tp 1 elif label 0 and det 0: continue elif label 0 and det 1: fp 1 else: fn 1 print(误报:, fp, 漏报:, fn)运行环境里建议以 1 秒为时间片做统计。当漏报多时降低model.conf或增大检测框中心点判定范围误报多时提高absence_sec并检查是否有路过的外人进入危险区域。模型 conf 和 absence_sec 的作用面不同conf 影响检出能力absence_sec 影响时间稳定性修改参数后要分别验证。启动系统后用手机拍摄工位画面在检测区域内进出来回走动观察告警触发和复位时间是否符合预期只有连续无人超过absence_sec时才进入告警状态这个判定逻辑需要从日志中看到明确的触发和恢复时间戳。本文还有配套的精品资源点击获取
返回列表