简介:一份结合YOLOv8目标检测与MMAction2时序模型的行人动作检测可运行源码,面向智能视频监控、行为分析等场景的计算机视觉开发者和研究人员,解决视频中行人定位与行为分类的联动问题。资源共11个文件,涵盖py算法源码、mp4演示视频、pth预训练权重、md说明文档、html可视化页面及配置文件等,压缩包约14KB,结构紧凑便于快速部署。源码完整演示了YOLOv8行人提取、MMAction2动作识别与结果融合的关键步骤,并针对Temporal Shift Module等预训练模型在小数据集上的选型做了说明;同时提供数据集预处理与划分、配置文件修改等实操指引,配合可运行脚本与README,可复现环境配置、模型训练、测试和推理的完整链路。目前已有122人学习使用,适合需要在真实监控场景中落地行人动作检测方案的开发者参考。 最近有朋友在做行人动作检测,一上来就问:能不能直接用YOLOv8把动作也分类出来?我一般会回一句:YOLOv8擅长框出目标,动作识别是另一个维度的事。与其硬改检测头,不如把它和MMAction2配合起来,检测负责定位,动作识别负责理解行为。这套组合在工程上很成熟,尤其适合“先找人、再看人在干嘛”的场景,比如安防监控、人机交互、零售行为分析。这次我把整套可运行源码的思路、配置、踩坑点都拆开讲,适合刚接触动作识别,或者想快速搭一套检测+识别pipeline的人。
1. 整体思路与双阶段方案选型
1.1 为什么不直接用单一模型做检测和分类
很多人第一次接触这个需求时,会想:YOLOv8已经能分类目标了,能不能把“走路、跑步、招手、摔倒”当作类别,直接在检测框里输出动作标签?理论上可以,但实际效果会很糟糕。动作识别强依赖时序上下文,单帧检测模型只能看到当前帧的姿态和空间位置,看不到“过去几秒这个人做了什么”。比如“摔倒”和“蹲下”在单帧画面上可能长得一模一样,只有结合连续几帧的变化趋势才能区分。
单阶段方案还有一个绕不开的问题:动作类别通常比目标类别更细,数据标注成本成倍增加。你要标“人”容易,标“这个人从第几帧开始举手,到第几帧放下”就麻烦得多。所以工程上更通用的做法是两阶段:先用目标检测模型把行人的位置框出来,再把连续多帧的检测框区域或骨骼关键点喂给时序模型做动作分类。这就是YOLOv8+MMAction2的典型分工。
1.2 YOLOv8负责“在哪”,MMAction2负责“在做什么”
这套方案里,YOLOv8的角色就是干净利落的目标检测器。它每帧输出行人的边界框和置信度,我用它过滤掉背景干扰,只保留“人”这个前景。MMAction2是OpenMMLab家族里的动作识别库,里面封装了多种时序模型,比如TSN、TSM、SlowFast,还有基于骨骼的ST-GCN。我这里选的是基于RGB帧序列的TSM(Temporal Shift Module),它在精度和速度之间比较均衡,普通GPU也能跑得动。
两者的连接方式也很简单:对视频流的每一帧跑YOLOv8,得到每个人的检测框后,把框内的图像区域按时间顺序截取下来,组成一个短片段,再交给MMAction2的模型判断动作类别。如果多人同时出现,需要先做目标跟踪,给每个人分配一个ID,再按ID组织各自的时序片段。否则识别结果会在多个人之间来回跳。
| 方案 | 优势 | 劣势 |
|---|---|---|
| 单帧检测模型直接分类 | 流程简单、延迟低 | 缺乏时序信息,混淆动作多 |
| YOLOv8+单帧分类头 | 复用检测框 | 仍无法区分同姿不同动 |
| YOLOv8+MMAction2时序识别 | 利用连续帧,动作区分度高 | 多一个时序模型,工程链路稍长 |
2. 核心细节解析:从检测框到动作片段
2.1 YOLOv8行人检测的关键配置
YOLOv8本身不复杂,但用在动作识别场景里,有几个参数值得单独调。首先模型尺寸,我建议优先用yolov8n或yolov8s。动作识别需要连续处理大量帧,检测部分如果太重,整个pipeline的FPS会被拖下来。其次置信度阈值,一般取0.4~0.5,但在监控场景里,行人经常被遮挡或处于画面边缘,阈值可以放宽到0.3,宁可多出几个框,也不能漏掉关键人物。NMS的IoU阈值建议默认0.45,如果画面中行人密集,可以调低到0.4,减少多人框重叠导致的漏检。
还有一个容易忽略的点:检测输入分辨率。YOLOv8默认是640x640,如果原视频是1080p,直接resize到640会损失小目标细节。行人动作检测通常人是主体,尺寸不算太小,我用640基本够;但如果摄像头离得远、行人只占几十个像素,建议把imgsz调到960或1280,代价是推理时间增加。这个需要根据实际摄像头角度去试。
在多人场景里,我还会给检测结果接一个轻量级追踪器,比如ByteTrack。它不需要额外训练,用检测框和IoU匹配就能稳定跟踪。有了track ID,MMAction2才能拿到“同一段时间里同一个人的连续画面”。这是我踩过比较深的一个坑:一开始没做跟踪,画面里两个人交叉走过,动作识别结果直接串台。
2.2 MMAction2动作识别输入构造
MMAction2的输入不是随便截几帧图片就行的。以TSM为例,它通常接收一个clip,也就是连续N帧的堆叠。这个N一般取8或16。实际操作时,我会从某个人的跟踪轨迹里均匀取出最近N帧的检测框区域,resize成模型需要的尺寸(比如224x224),然后按时间顺序排列成一个tensor。
这里有个重要细节:采样帧的方式。不是简单取最近N帧,而是先选定一个时间窗口,比如过去2秒,然后在这个窗口里均匀采样8帧。这样能避免人物动作在时间轴上被压缩或拉伸。MMAction2有现成的SampleFrames模块做这件事,但我调试时发现,如果直接喂原始视频帧,需要先根据检测框做crop,再做resize和归一化。顺序不能乱:先crop,再resize,最后归一化到ImageNet的mean/std,否则模型输出会莫名其妙。
另外,MMAction2支持处理整段视频,也支持在线流式识别。源码里我会用VideoRecognizer.inference直接处理一个clip tensor。但要注意,MMAction2的inference接口默认输入是NCHW格式的原始帧列表,类别映射文件要和你训练/下载时的label顺序完全一致。很多“能跑但结果全错”的问题,80%出在label对齐上。
2.3 数据集与标签对齐的坑
如果你用的是预训练模型,比如在Kinetics-400上训练的TSM,那它的输出标签是400个动作类别,比如“开酒瓶”“刷地板”“打保龄球”。把这些类别直接用在行人动作检测业务里会很别扭,因为你需要的是“行走、奔跑、站立、挥手、跌倒”这种更贴近场景的标签。这时候就必须准备自己的数据集微调。
我建议用已有公开数据集做预训练的起点,比如UCF101或Something-Something,再采集自己的业务数据做微调。标注格式用MMAction2支持的自定义格式:一个视频片段对应一个动作类别标签,时间段划分要标清楚。粒度上,动作的开始和结束帧尽量标精确,TSM这类模型对边界敏感,标得粗会让模型学出“过渡动作”。
还有一个容易出问题的地方:类别不平衡。监控场景里“行走”占了90%,“跌倒”可能只有1%。此时不能只看准确率,要关注每个类别的recall。可以在训练时给标签加class_weight,或者干脆对稀有类别做过采样。这个我在后面训练配置里会提到。
3. 实操过程:搭建可运行环境与跑通源码
3.1 环境配置与版本兼容
这套源码依赖OpenMMLab生态,版本兼容是最大的门槛。我用的稳定组合是:Python 3.8、CUDA 11.7、PyTorch 1.13.1、mmcv 2.0.1(对应mmengine)、mmdet 3.2.0、mmaction2 1.2.0。YOLOv8部分用ultralytics的8.x版本,和OpenMMLab体系分开装,互不干扰。
特别注意:mmcv的安装一定要和你的CUDA、PyTorch版本匹配。用官方命令从源码编译最稳妥,但耗时较长。如果想用预编译wheel,你得精确选择mmcv==2.0.1对应的cu117/torch1.13版本。这里我吃过亏:装了mmcv 2.0.1但PyTorch是2.0,直接导致cuda算子不匹配,整个检测模块跑不起来。所以先定PyTorch,再定mmcv,顺序不能反。
YOLOv8的环境比较简单,pip install ultralytics就能搞定。MMAction2安装则需要克隆仓库然后pip install -e .。建议用虚拟环境隔离,不要和系统Python混装,否则OpenMMLab这套依赖能让你的环境乱成一团。
3.2 源码结构与关键模块说明
我提供的可运行源码基本是这样的结构:
project/ ├── detector/ │ └── yolov8_detector.py # 封装YOLOv8检测 ├── tracker/ │ └── bytetrack.py # 简单跨帧跟踪 ├── recognizer/ │ └── mmaction_recognizer.py # 封装MMAction2 ├── pipeline.py # 串联检测、跟踪、识别 ├── configs/ │ └── tsm_r50_8frames.py # MMAction2模型配置 └── requirements.txt核心的pipeline逻辑并不复杂。我用一个视频帧循环来演示:
# 伪代码,展示主流程 while True: frame = cap.read() dets = detector.detect(frame) # list of [x1,y1,x2,y2,score] tracks = tracker.update(dets) # 给每个框分配track_id for track in tracks: clips[track.id].append(crop_by_box(frame, track.box)) if len(clips[track.id]) >= clip_len: clip = sample_frames(clips[track.id]) action = recognizer.predict(clip) draw(frame, track.box, action)这里关键的模块是tracker.update和recognizer.predict。ByteTrack的update会先做IoU匹配,再处理新出现和消失的轨迹。实际使用中,我会保存每个人的历史crop列表,长度超过clip_len后就按时间均匀采样,而不是直接存最近N帧,这样动作识别更稳定。
3.3 在GPU/CPU上的运行与性能优化
很多人问女孩那类问题:GTX 1660Ti能不能跑?可以。我实测过,YOLOv8s + TSM(8帧)在1660Ti上跑1080p视频,检测部分大约20ms一帧,识别部分每次动作判断约30ms,汇总起来每秒大概能处理15~20帧,基本满足实时监控需求。如果换成YOLOv8n,检测能快一倍,但小目标精度会下降。如果你要用CPU跑,那就得把输入分辨率降到320,识别模型换成TSM的轻量版,速度大概在3~5FPS,只能做离线分析。
优化方面有几个立竿见影的手段。视频帧如果是固定摄像头,可以做一个背景差分预处理,画面中没有任何运动目标时直接跳过YOLOv8检测,能省大量算力。另外,MMAction2的推理可以放到TensorRT上加速,但配置流程略繁琐,纯Python实现先用PyTorch跑通功能,性能优化放在后面。
还有一个容易被忽略的细节:视频帧预处理中,bgr和rgb的顺序一定不要搞反。YOLOv8用OpenCV读进来是BGR,而MMAction2的tsm模型训练时用的是RGB。如果你直接喂BGR帧给识别模型,动作准确率会明显下降。我在pipeline里专门加了一个cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)。
4. 常见问题与排查技巧实录
4.1 依赖冲突与mmcv版本不匹配
这是OpenMMLab全家桶用户最头疼的问题。常见的报错是ModuleNotFoundError: mmcv._ext或者KeyError: 'mmcv' has no attribute 'ops'。这时候先检查版本矩阵:python -c "import mmcv; print(mmcv.__version__)"和python -c "import torch; print(torch.__version__)",确保它们与mmcv官方兼容表一致。如果已经装错,最简单的方法是在虚拟环境里卸载重装,而不是尝试修补二进制的cuda算子。
另一种隐蔽问题:同时装了mmcv-full和mmcv,两个包冲突。OpenMMLab从2.0开始已经不做mmcv-full的区分,统一叫mmcv。老教程里让你装mmcv-full的,在2.x时代是不对的。务必用官方最新安装命令。
4.2 检测结果抖动/误检导致动作识别错误
视频里行人偶尔被遮挡,YOLOv8会短暂漏检,ByteTrack的轨迹会断裂,然后重新分配新ID。动作识别一旦换了ID,前面攒的历史帧就断掉了,识别结果跳变。解决方法是让ByteTrack允许轨迹“存活”一段时间,比如在丢失目标后保留ID 0.5秒,期间如果没有新的框匹配,才判定轨迹结束。这样短暂遮挡不会影响动作识别。
另一个常见问题:误检框把背景砖墙或汽车玻璃当成行人,识别模型在里面提取到的“动作”完全没有意义。我一般会加一个尺寸过滤:检测框高度小于视频高度5%、宽高比大于2.0或小于0.2的框直接丢弃。这个规则在大多数监控视角下都能有效过滤假目标。
4.3 推理速度慢怎么办
先别急着换模型,先看瓶颈在哪。在pipeline里可以对检测和识别分开计时。通常瓶颈出在YOLOv8的输入分辨率太大,或MMAction2的模型太大。我建议的调优顺序是:第一,YOLOv8从s换成n,看精度损失在不在可接受范围内;第二,MMAction2的TSM采样帧数从8降到4,延迟能降低一截;第三,如果还是在实时场景下卡顿,可以把检测和识别放到两个线程里并行,识别不阻塞检测,用队列传递帧。
还有一个性能陷阱:每次识别都对同一个clip重复计算。如果视频帧率是30FPS,而动作识别模型需要8帧,那每帧都做一次推理没意义。我实际代码里设置了一个“识别节流”机制:每5帧才跑一次动作识别,中间帧沿用上一次的结果。这样能有效降低GPU占用,同时动作变化平滑,肉眼几乎感知不到延迟。
下面是一个问题速查表,方便直接对照:
| 现象 | 可能原因 | 处理建议 |
|---|---|---|
| 一运行就报cuda错误 | mmcv与PyTorch版本不匹配 | 核对版本矩阵,重装mmcv |
| 动作类别全是乱的 | label映射文件与模型不一致 | 检查类别索引,逐个对应 |
| 识别结果反复横跳 | 没有跟踪ID或跟踪断裂 | 接入ByteTrack并延长ID保留时间 |
| 检测框闪烁严重 | 置信度阈值低导致误检 | 提高阈值,增加尺寸过滤 |
| 速度只有2FPS | 检测+识别串行且输入太大 | 降低imgsz,并行化,减少识别频率 |
结尾
做这个项目时我发现,真正花时间的不是调模型,而是把“检测-跟踪-识别”三块拼起来,还要处理各种边界情况。踩过几次坑之后,我现在的体会是:动作识别不能在单帧上较劲,要让模型看到足够多的上下文;YOLOv8和MMAction2的配合已经是很稳的工程范式,但你必须清楚每一条数据从摄像头到模型前的每一步发生了什么。这套源码我在自己的监控模拟场景里跑起来很稳定,如果你也想做行人动作检测,建议先从最简单的三件套入手:一个轻量YOLOv8,一个TSM识别器,一个ByteTrack。跑通之后再慢慢迭代精度和速度,你会发现剩下的问题都有迹可循。
本文还有配套的精品资源,点击获取