基于YOLOv5和SlowFast的人体动作识别系统设计与实现
2026/9/13 14:36:25 网站建设 项目流程

简介:一份面向毕业设计、课程设计与期末大作业场景的人体动作识别完整项目,基于YOLOv5与SlowFast双模型架构,并在PyQt5框架下封装为可视化界面,适合计算机相关专业本科生或对动作识别感兴趣的新手学习参考。资源共150个文件,涵盖55个Python源码、5个UI界面文件、2个PT模型权重,以及PNG/JPG图片、MP4演示视频、TXT说明、INI配置、ICO图标等,压缩包整体约153.4MB。其中Python与UI文件对应检测、识别、界面交互等核心模块,视频与图片有助于直观理解运行效果,文档与配置文件可辅助环境配置与参数调优,模型权重可直接加载使用。已有326人学习浏览,项目经过严格调试,支持简单部署,功能较完善、界面美观,适合用作高分毕设的参考模板。代码中包含注释,即便基础薄弱也能对照文档快速上手,便于二次开发与答辩讲解,能为读者节省大量开发与调试时间。

1. 为什么毕设选 YOLOv5 + SlowFast + PyQt5 做人体动作识别

人体动作识别不是单一模型能干净解决的活儿,常见做法是“先检测、后识别”:用 YOLOv5 从画面里把人框出来,再把这一小片人体序列交给 SlowFast 判断动作,最后用 PyQt5 做成带界面的桌面程序。这个链路的好处是检测和识别解耦,哪个模块想替换都方便,而且每个环节都有成熟的预训练权重兜底,哪怕自己只训练动作分类器,效果也不会太差。项目源码里已经带好了检测、识别、界面三块代码和文档说明,部署时只需要把环境和权重对齐,所以特别适合拿去改造成毕业设计、期末大作业或课程设计。下面按我的拆解顺序,把每个模块的接线逻辑和容易掉坑的地方讲清楚。

2. YOLOv5 人体检测:从权重选择到人体框裁剪

2.1 检测在动作识别链路里的准确位置

很多人第一次拿 SlowFast 直接喂整段视频,结果发现识别率飘忽不定,原因在于 SlowFast 输入需要的是“主体区域”。如果画面里有多个人、背景复杂,动作分类器会被无关像素干扰。YOLOv5 在这里扮演的是区域提议器,先把每一帧的 person 类目标框出来。

这一步的输出不是给人看的,而是要按时间顺序存进一个“人体序列缓冲区”。比如设定一个 32 帧的队列,检测到人之后,按框坐标把原图裁剪成正方形再缩放到固定尺寸,存进队列。慢路径和快路径会分别从这个缓冲区采样,而不是对全图做动作识别。这个顺序一旦反了,后面怎么调参都别扭。

2.2 模型选型:yolov5s 还是自定义权重

官方仓库里 yolov5s 权重文件大约 14MB,单帧推理在 GTX 1660 上能到 30ms 左右,做实时桌面演示完全够用。如果机器是集显或者要跑视频文件,更推荐 yolov5s,因为动作识别本身已经占掉不少算力,检测端没必要上 yolov5x。若你希望重点检测特定场景的人体,比如教室、工地,建议用自己的数据微调,但毕设场景下直接用 COCO 预训练权重即可。

代码里加载权重的常见写法如下:

import torch # 加载检测模型,这里以 ultralytics 的 YOLOv5 为例 model = torch.hub.load('ultralytics/yolov5', 'yolov5s', pretrained=True) model.conf = 0.4 # 置信度阈值,过滤低置信度检测框 model.iou = 0.45 # NMS 的 IoU 阈值,控制重叠框的合并 model.classes = [0] # 只保留 person 类别,COCO 中 0 对应 person

上面的model.confmodel.iou是 YOLOv5 推理阶段的常用参数,model.classes指定只输出类别 0,也就是 person。这样既减少计算量,也避免误检把猫狗当成目标。torch.hub.load会从 GitHub 拉取代码,第一次运行需要联网,之后会缓存到本地。

2.3 检测推理与人体框裁剪

拿到检测坐标后,要做的不是直接画框,而是把每个目标框对应的图像区域提取出来,组成一个序列。这里我一般维护一个deque缓冲区,每个元素是一帧里某个人的裁剪图。

import cv2 from collections import deque buffer = deque(maxlen=32) # 保存最近 32 帧的人体裁剪图 def process_frame(frame): results = model(frame) # 执行检测 boxes = results.xyxy[0].cpu().numpy() # 得到 [x1, y1, x2, y2, conf, cls] persons = [] for box in boxes: x1, y1, x2, y2 = map(int, box[:4]) crop = frame[y1:y2, x1:x2] # 裁剪人体区域 crop = cv2.resize(crop, (224, 224)) # 统一输入尺寸 persons.append(crop) return persons

这里把每个目标单独裁剪后统一缩放为 224×224,是为了匹配后续 SlowFast 的输入尺寸。注意results.xyxy[0]是 YOLOv5 输出的 xyxy 格式,列顺序是左上角和右下角坐标、置信度、类别编号。map(int, box[:4])把坐标转成整数,避免 OpenCV 切片时报类型错误。

实际项目中,一个画面可能同时有多个人,那么每个目标都应该维护自己的独立缓冲区。常见做法是用一个字典,键是跟踪 ID,值是 deque。如果没有跟踪模块,也可以保守一点:只取置信度最高的人做动作识别,其余人只画框不识别。这样对于毕设演示最稳定。

2.4 检测参数调整和常见误用

参数推荐值作用调高时的效果
conf0.4 ~ 0.5置信度阈值误检减少,但可能漏检远距离小目标
iou0.45 ~ 0.6NMS 合并重叠框调高会让重叠目标保留更多
imgsz640输入分辨率调高到 1280 提升小目标检测,但推理变慢

一个常见的误用是直接用model(frame)得到的结果去匹配动作,忽略了检测框的抖动。实际从摄像头取帧时,YOLOv5 对同一段动作的检测框坐标会有几个像素的波动,直接裁剪会导致输入序列抖动明显。更稳妥的做法是对检测框做轻量平滑:把当前帧的框和前一帧的框按 0.7:0.3 加权平均,或者记录最近 5 帧的框中心点做均值。这个预处理不会影响动作识别的语义,却能显著提高 SlowFast 输入序列的稳定性。

3. SlowFast 动作识别:双路径网络如何工作

3.1 慢路径与快路径的互补逻辑

SlowFast 的核心思想来自人体视觉的双通道机制:慢路径以低帧率提取空间结构和颜色纹理,快路径以高帧率捕捉运动变化。慢路径的通道数更多、时间步长更大(比如每秒 4 帧),而快路径通道数更少、时间步长更小(每秒 32 帧),两条路径通过侧向连接融合特征。和 TSN 那种纯 2D CNN 加时间聚合的思路相比,SlowFast 不需要光流输入,模型内部就能学习运动特征。

在动作识别领域,SlowFast 的优势在于对“动作幅度小但持续时间长”的行为更敏感,比如挥手指、缓慢站起。视频里的人体动作往往同时包含快速肢体运动和缓慢姿态变化,单一路径很难兼顾。这也是毕设答辩时最有讲头的点。

3.2 输入预处理:帧采样和尺寸对齐

SlowFast 的标准输入是一个视频片段,而不是单帧。典型配置是采样 32 帧,每帧经过中心裁剪到 224×224。视频帧率屠夫,但表里直接给推荐值更好。项目中,我常用 16 帧作为折中,因为实时性更好,识别准确率下降幅度可以接受。

import torch import torchvision.transforms as T transform = T.Compose([ T.ToPILImage(), T.Resize((256, 256)), T.CenterCrop(224), T.ToTensor(), T.Normalize(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225]), ]) def build_clip(person_buffer): frames = list(person_buffer)[-16:] # 取最近 16 帧 clip = torch.stack([transform(f) for f in frames], dim=1) # C, T, H, W return clip.unsqueeze(0) # 加 batch 维度

这里把裁剪图变成C, T, H, W的张量,T是时间维。transform里的 Resize 和 CenterCrop 是模仿 SlowFast 预训练时的预处理流程,均值方差是 ImageNet 的标准值。如果你的自定义动作数据是灰度图,记得先转成三通道。

3.3 SlowFast 推理和类别映射

模型输出的是每个动作类别的 logits,需要经过 Softmax 转成概率。项目里动作类别通常是自定义的,比如站立、行走、挥手、蹲下等。代码会读取一个标签文件,把类别索引映射到中文标签。

model_sf = torch.load('slowfast.pth', map_location='cpu') model_sf.eval() with torch.no_grad(): out = model_sf(clip) # 输出形状 [1, num_classes] probs = torch.softmax(out, dim=1) top1 = torch.argmax(probs, dim=1).item() label = label_list[top1]

model_sf的加载方式取决于你保存权重时是否包含整个模型结构。如果只保存了state_dict,需要先实例化模型再load_state_dict。注意model_sf.eval()必须调用,否则 BN 层和 Dropout 在推理阶段行为不对。

实际部署中,SlowFast 的输入帧数和模型训练时的num_frames必须一致。如果训练时是 32 帧,你推理只给 16 帧,PyTorch 不会报错,但结果会是乱七八糟的类别概率。这一点很多新手容易忽略。

3.4 和传统方法的差别以及调参方向

不少人会把 SlowFast 和 3D CNN 混在一起说。两者本质都是 3D 卷积,但 SlowFast 引入了“快慢双路径侧向连接”,而不是简单地把所有帧堆进一个 3D ResNet。如果你的视频里动作持续时间很短,快路径的帧率就格外重要,tau参数控制慢路径的帧步长,调大会让慢路径看到更稀疏的帧,运动感知会转移到快路径上。

调参时先看类别文件对不对,再看帧数是否匹配,最后才是学习率。实际用起来,最容易出问题的是输入像素值没有归一化,导致 logits 全是同一个值。我的习惯是先打印probs看分布,如果所有类别概率接近 1/N,多半是预处理问题。

4. PyQt5 界面整合:线程设计、视频输入与状态显示

4.1 界面功能布局

PyQt5 在这里负责三件事:显示视频画面、实时更新识别标签、控制开始停止。项目里的posters.jpg是界面背景图,icon.ico是程序图标,这些静态资源在 PyQt5 里通过资源文件或相对路径加载。界面主体是一个QLabel用来显示图像,旁边放识别结果QLineEditQLabel,底部是“打开视频”、“开始识别”、“停止”三个按钮。

4.2 QThread 避免界面卡顿

把检测识别放到主线程是很多人第一次写 PyQt5 视觉项目会踩的坑。如果摄像头帧率是 30fps,YOLOv5 推理一次要 30ms,加上 SlowFast 的 100ms,主线程卡死是必然的。正确做法是把视频处理循环放进QThread,通过信号把结果传回主线程。

from PyQt5.QtCore import QThread, pyqtSignal import cv2 class VideoThread(QThread): change_frame = pyqtSignal(object) update_action = pyqtSignal(str) def __init__(self, source=0): super().__init__() self.source = source self.running = True def run(self): cap = cv2.VideoCapture(self.source) while self.running: ret, frame = cap.read() if not ret: break persons = process_frame(frame) if persons: # 更新缓冲区并识别动作 buffer.append(persons[0]) if len(buffer) >= 16: clip = build_clip(buffer) label = infer_action(clip) self.update_action.emit(label) self.change_frame.emit(frame) cap.release() def stop(self): self.running = False

run方法里不断读帧、检测、识别,然后通过emit把画面和结果发出去。change_frame信号带的是 OpenCV 的 BGR 图像,主线程槽函数里要做 BGR 到 RGB 的转换,再转成 QImage 显示。这一步容易出颜色偏差,原因就是 OpenCV 和 PyQt5 的颜色通道顺序不一致。

4.3 QImage 转换和槽函数

def update_frame(self, frame): rgb_image = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB) h, w, ch = rgb_image.shape bytes_per_line = ch * w qt_image = QImage(rgb_image.data, w, h, bytes_per_line, QImage.Format_RGB888) self.video_label.setPixmap(QPixmap.fromImage(qt_image))

这里QImage的构造函数需要传入data指针,rgb_image.data必须保证在setPixmap之前该数组仍然存活。如果rgb_image是局部变量,赋值给qt_image后,Python 的引用计数可能让它被释放,显示会花屏。稳妥做法是把rgb_image保存为当前对象的成员变量,或者直接使用QImage(qt_image)复制一份。

界面线程和识别线程之间不要直接传递 list 或 dict,而是通过信号传送对象或字符串,这样避免线程锁竞争。视频输入方面,source既可以传摄像头编号0,也可以传视频文件路径,只要在VideoCapture阶段兼容就行。

4.4 PyQt5 环境配置与版本坑

PyQt5 的新装方式已经变了,早期pip install pyqt5直接可用,现在有些镜像源需要装pyqt5-qt5pyqt5-sip两个库,否则运行时会报缺少 QtCore。环境配置一般用:

pip install pyqt5 pyqt5-sip opencv-python torch torchvision

如果遇到ImportError: DLL load failed while importing QtCore,很可能是系统缺少 VC++ 运行库,或者 Python 版本和 PyQt5 位数不匹配。建议直接用 Python 3.8 或 3.10,避开 3.12 的兼容问题。PyQt5 和 PySide6 的 API 大部分兼容,但信号定义方式不同,项目用的是 PyQt5,不要混着装。

5. 让项目跑起来:环境校验、超参调整和常见坑位

5.1 部署后的快速自检

拿到源码后不要急着打开主界面,先跑确认环境是通的。在项目根目录执行:

pip install -r requirements.txt python -c "import torch, cv2, PyQt5; print(torch.__version__, cv2.__version__)"

如果 import 报错,优先检查是不是 Python 路径混了多个环境。YOLOv5 的torch.hub.load依赖git和网络,离线环境可以先把权重文件下载到本地,再通过torch.hub.load('local/yolov5', 'custom', path='best.pt')加载。启动项目后,如果界面打开但摄像头黑屏,第一件事是检查cv2.VideoCapture(0)的返回值和摄像头驱动。

5.2 动作识别超参调整方向

SlowFast 的alpha参数控制快路径的帧率倍率,默认是 8,意思是快路径帧率是慢路径的 8 倍。如果你识别的是“走路”一类动作,慢路径的贡献更大,可以适当增大慢路径的通道数。如果识别的是“跳跃”这种瞬时动作,调大快路径的帧率更有效。

帧缓冲区的长度也很关键。我测试时发现,缓冲区 16 帧时挥手识别最灵敏,蹲下这类慢动作需要 32 帧。缓冲区太短会让慢动作只看到半个过程,太长又会引入两段动作的混合。建议在代码里把这个长度暴露成配置项,方便答辩时现场切换对比。

5.3 训练自己的动作数据集如何改造

项目界面默认加载预训练权重,但如果你想识别自定义动作,需要准备自己的视频片段。常见流程是:把每个动作视频按帧抽成图片,用 YOLOv5 检测并裁剪人体,然后保存成 SlowFast 可读的格式。训练命令通常是:

python tools/run_net.py --cfg configs/MyAction/SLOWFAST_16x8_R50.yaml

配置里要改TRAIN.DATASETTEST.DATASET路径,以及MODEL.NUM_CLASSES为你自己的类别数。这里最容易出错的是类别数不匹配,模型最后一层全连接输出维度会和权重文件不一致,加载时报size mismatch。解决方式是在配置里去掉了预训练权重头部,或者把NUM_CLASSES改回权重训练时的类别数。

5.4 最后检查的三件事

第一,确认视频输入画面和识别结果显示在同一个坐标系,避免检测框和实际人体位置错位。第二,关闭界面时在closeEvent里显式调用线程stop()wait(),否则进程不会退出。第三,把模型权重和标签文件路径写成相对当前文件位置的绝对路径,防止从其他目录启动项目时找不到文件。比如posters.jpg.ini文件存在项目目录下,启动脚本要用os.path.dirname(__file__)定位。项目里的Users.db是界面操作日志或用户配置的 SQLite 数据库,不要删除,否则初始化逻辑会报错。

这套链路里,YOLOv5 负责把人找出来,SlowFast 负责理解动作,PyQt5 负责把过程呈现给老师和评委。毕设答辩时,能讲清楚“为什么要把检测和识别拆开”,比背出模型参数更有说服力。

本文还有配套的精品资源,点击获取

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询