尧图网络 高端网站定制 · 原创设计
免费咨询热线
400-888-6620
免费获取方案
YOLOv8+MMAction2两段式行人动作检测:从空间定位到时序分类的工程实践
简介面向智能视频监控与行为分析场景这里提供一套基于YOLOv8目标检测和MMAction2时序模型的行人动作检测源码。它将YOLOv8的行人定位能力与MMAction2的动作分类能力结合在有限数据集上即可完成训练、测试与推理适合计算机视觉、行为识别方向的开发者与研究人员参考。压缩包共11个文件包含Python源码、Temporal Shift Module预训练权重、操作说明文档、HTML演示页面以及3段演示视频整体仅14KB轻量易部署。已有123人学习过该资源。内容涵盖MMAction2环境配置、预训练模型选型例如TSM在小数据集上的优势、数据集预处理与划分、配置文件修改、训练测试推理全流程以及YOLOv8行人提取与结果融合的关键代码。下载者可获得可直接运行的工程框架、代码注释与实验输出便于快速复现和二次开发。1. 行人动作检测为什么必须拆成“YOLOv8定位 MMAction2分类”两段式管线做行人动作检测的人第一反应通常是在YOLOv8里加一个动作类别直接练结果翻车率很高——检测器能告诉你“框里是个人”但同一个框里的人是“打电话”“跌倒”还是“挥手”单帧图像根本给不出足够证据。反过来直接上MMAction2做整段视频分类模型又不知道该看画面里哪个人多人场景下准确率和可解释性双双失控。反直觉的结论是把任务拆成“YOLOv8负责空间定位、MMAction2负责时间上下文”的两段式管线单人的动作识别精度、告警可解释性和后续扩展能力反而全部提升。这篇笔记会从选型理由、环境搭配、数据流打通到五个最容易出问题的环节一次讲清适合已经在用YOLO做检测、想把动作识别补上的团队也适合评估这个方向值不值得投入的从业者。2. 管线选型YOLOv8管住空间MMAction2管住时间2.1 为什么是YOLOv8而不是更轻的检测器速度、权重生态和部署成熟度行人动作检测的第一步是稳定拿到“画面里有哪些人、位置在哪”。这一步做不好后面所有动作分类都是空中楼阁。我在选型时对比过若干轻量检测方案最终回到YOLOv8理由有三条。第一条是速度与精度的平衡点最舒服。YOLOv8s在常见的监控分辨率和边缘设备上单帧推理稳定在十几毫秒到几十毫秒量级而检测精度足以支撑后续分类任务。比它更轻的检测器虽然帧率更高但小目标行人或遮挡场景下检测框抖动明显反而会给动作分类器喂进去大量噪声样本。第二条是权重生态。YOLOv8的COCO预训练权重直接覆盖person类意味着做行人动作检测时你不需要为了“找到人”这一步额外标注和训练数据集开箱即用。第三条是部署成熟度官方推理库封装了导出、量化、TensorRT转换的能力后期从原型到产线上会少走很多弯路。需要强调的是检测器在这里的角色是“候选框生成器”不是“最终决策器”。所以选型时不用追求最极致的检测精度而是要看它在目标场景下漏检率和误检率是否可控。如果实际场景中有大量远距离小目标行人建议直接把输入分辨率从640提到960比换一个更大的检测模型更划算。2.2 MMAction2的识别方案选哪个RGB分类、骨骼识别还是时空检测MMAction2本身不是单一模型而是一整套视频理解工具箱进去之后第一个选择困难就是“到底用哪个方案”。针对行人动作检测实际中可落地的有三条路线RGB时序分类、骨骼动作识别、时空动作检测。RGB时序分类是最常用也是性价比最高的路线代表模型如TSM、TSN、TANet。它直接把连续帧的裁剪图序列送给网络模型在时间维度上学习动作变化。优点是链条最短依赖最少YOLOv8裁剪出什么就吃什么。缺点是背景干扰会跟着裁剪框一起进模型所以裁剪框的扩边策略很关键这个放到第5章细说。骨骼动作识别走的是“姿态估计 骨骼序列分类”路线代表方案是ST-GCN和PoseC3D。优点是对外观干扰不敏感识别“跌倒”“挥手”这类肢体动作更纯粹缺点是前置姿态估计器一旦在俯视角度、远距离小目标或严重遮挡下崩了后续分类就全线崩溃而且调试链路更长。时空检测方案精度上限最高但训练成本和推理开销都大早期项目不建议直接上。我的建议是第一版老老实实选RGB时序分类以TSM作为基线。等把整个管线跑通、数据积累到一定程度再根据失败样本判断要不要引入骨骼分支做二次融合。2.3 两段式数据流检测结果怎么变成动作识别的输入很多人在这一步栽跟头根源是没想明白动作识别模型的输入究竟是什么。YOLOv8的输出是“单帧里的位置”而MMAction2里的动作分类器吃的是“一段连续帧序列”这两个数据形态之间存在一个必须显式实现的转换层。完整的管线应该是这样的视频流逐帧送入YOLOv8得到每一帧的行人检测框接着按交并比和位置连续性把不同帧里的框关联成同一个人的轨迹然后沿着轨迹从视频里切出这个人从出现到离开的连续画面序列再按动作分类器要求的采样数量常见的是8帧或16帧从序列里均匀采样缩放到统一尺寸后送入MMAction2分类器最后对分类结果做时序平滑输出这个人当前的动作标签。这个数据流里最关键的一个认知是动作识别的输入永远不是单张图而是帧序列。有人把裁剪后的单帧直接丢进动作分类器得到的结果自然忽高忽低然后跑来问“是不是模型没训练好”——其实模型是无辜的时序上下文根本没被喂进去。3. 环境搭好与最小管线跑通从单帧检测到动作分类3.1 版本匹配是第一道坎torch、mmcv与MMAction2的兼容关系MMAction2的环境安装是这个项目里第一个能劝退新手的环节。它不是pip install就能完事的因为MMAction2依赖mmcv而mmcv编译时需要和torch、CUDA版本严格对应版本错位会直接报出各种令人挠头的底层错误。常见做法是先建一个干净的conda环境把Python版本定在3.8到3.10之间然后安装torch并用mim安装与当前torch匹配的mmcv。装完之后不要急着装mmaction2先写几行代码验证mmcv能被正常导入。我一般会这样验证import torch print(torch.__version__) print(torch.cuda.is_available()) import mmcv print(mmcv.__version__)如果torch能正常打印版本且cuda可用但import mmcv时报了类似undefined symbol或GLIBC相关的错误基本可以断定是mmcv与torch版本不匹配直接换用对应版本的预编译包不要尝试源码编译。等这三行代码全部跑通再去安装MMAction2并执行它的自检脚本。这里要特别提醒不要图省事把mmcv全家桶直接装上又或者用pip随意指定一个版本号。最靠谱的方式是到MMAction2官方安装文档里找到它的依赖声明按照声明里的版本区间逐一安装。3.2 先用YOLOv8把视频帧变成“有人”的候选框环境就绪后第一步是把YOLOv8的检测能力接进来。假设你的输入是监控视频我先写一个最简版本逐帧读取视频检测行人把检测框信息位置、置信度、帧号存下来。from ultralytics import YOLO import cv2 model YOLO(yolov8s.pt) cap cv2.VideoCapture(demo.mp4) frame_id 0 detections [] # 每项为 (frame_id, x1, y1, x2, y2, conf) while cap.isOpened(): ret, frame cap.read() if not ret: break results model( frame, conf0.45, # 检测置信度阈值过低会引入大量误检 iou0.5, # NMS 的 IoU 阈值 classes[0], # COCO 里 0 是 person 类只保留行人 imgsz640, # 输入尺寸远距离小目标建议调大到 960 verboseFalse # 关闭逐帧打印 ) boxes results[0].boxes for box in boxes: x1, y1, x2, y2 box.xyxy[0].tolist() conf box.conf[0].item() detections.append((frame_id, x1, y1, x2, y2, conf)) frame_id 1逻辑是每一帧只保留person类别的框配合置信度阈值滤掉低质量检测。参数调用看起来简单但这两项直接影响后续轨迹关联稳定性检测置信度建议保持在0.4到0.5区间低于0.35会让误检框进入轨迹关联高于0.6又容易在遮挡场景漏人。输入分辨率640在一般监控视角够用但如果你要检测的画面里行人普遍偏小优先调imgsz而不是换模型。3.3 候选框送入MMAction2滑窗裁剪与动作分类检测框落库之后接下来就是把轨迹对应的帧序列送给MMAction2。这里需要做一个关键操作把某个人连续出现的帧号提取出来按动作时长切段再从每段里均匀采样出模型要求的帧数。import numpy as np import mmcv from mmaction.apis import init_recognizer, inference_recognizer # 假设 config 和 checkpoint 已按官方文档准备好 config tsm_imagenet_pretrained.py checkpoint tsm_r50_1x1x8.pth model init_recognizer(config, checkpoint, devicecuda:0) def frames_to_clip(video_path, track_segment, num_frames8): # track_segment: [(frame_id, x1, y1, x2, y2), ...] step max(1, len(track_segment) // num_frames) sampled track_segment[::step][:num_frames] clip [] for item in sampled: frame_id item[0] frame mmcv.VideoReader(video_path)[frame_id] x1, y1, x2, y2 map(int, item[1:]) # 扩边 20%避免动作超出检测框 h, w frame.shape[:2] cx, cy (x1 x2) / 2, (y1 y2) / 2 bw, bh (x2 - x1) * 1.2, (y2 - y1) * 1.2 x1_, y1_ int(max(0, cx - bw / 2)), int(max(0, cy - bh / 2)) x2_, y2_ int(min(w, cx bw / 2)), int(min(h, cy bh / 2)) clip.append(frame[y1_:y2_, x1_:x2_]) return clip clip frames_to_clip(demo.mp4, track_segment) result inference_recognizer(model, clip, cuda:0) print(result) # 输出包含每个动作类别的得分核心逻辑是按轨迹采样出固定帧数的裁剪图序列并对每帧做扩边裁剪然后再交给动作分类器。扩边比例1.2是经验值太小会把抬手或挥臂的动作切掉太大会让背景噪声淹没人体姿态。这里还要统一裁剪图的缩放和均值方差归一化MMAction2的config里通常已经配好了直接沿用即可。4. 可运行源码的工程化拆解你真正要改的五个地方4.1 检测结果的跨帧缓存与轨迹关联让每一帧的框变成“某一个人”检测器是单帧工作的它并不知道这一帧的张三和上一帧的张三是同一个人。如果不做轨迹关联每一个行人帧都会变成一个独立的“新对象”动作分类也就无从谈起连贯性。最简单的做法是用IoU贪心匹配把当前帧的检测框和上一帧已存在的轨迹末尾做交并比计算交并比超过阈值就认为是同一人否则开新轨迹。这个方案虽然朴素但在行人动作检测的早期版本里完全够用。代码上需要一个缓存字典tracks {} # track_id - 最近 N 帧的检测框 next_track_id 0 def associate(detections, tracks, iou_threshold0.3): global next_track_id for det in detections: best_tid, best_iou None, 0 for tid, track in tracks.items(): last_box track[-1] iou compute_iou(last_box, det) if iou best_iou: best_iou, best_tid iou, tid if best_tid is not None and best_iou iou_threshold: tracks[best_tid].append(det) else: tracks[next_track_id] [det] next_track_id 1判断两个框是不是同一个人的依据只有交并比所以检测框的稳定性在此处极为关键。阈值0.3意味着相邻帧间检测框只要有30%的重叠就视为同一人对帧率异常或行人快速移动的视频这个阈值要配合检测置信度一起调整。更复杂的场景可以考虑引入ByteTrack式的低分框补匹配但前期项目没必要。4.2 动作分类的时序聚合多数投票还是置信度加权MMAction2对一段滑窗输出一个分类结果但这个结果在真实视频流里是逐段变化的。直接把每段的结果当作最终输出会看到动作标签频繁跳变上一秒是“行走”下一秒变成“站立”再下一秒又变回“行走”。解决办法是对时间上相邻的若干次分类结果做聚合。多数投票实现最简单但动作切换的边缘会被拉平置信度加权对切换更敏感但需要模型输出的原始分数稳定。我一般用的是滑动窗口内的置信度加权投票类别得分为该类别在窗口内所有样本的软分数之和取最高分类别作为该时间点的最终标签。from collections import defaultdict class ActionSmoother: def __init__(self, window_size7): self.window [] self.window_size window_size def update(self, scores): # scores: {class_name: softmax_score} self.window.append(scores) if len(self.window) self.window_size: self.window.pop(0) acc defaultdict(float) for s in self.window: for k, v in s.items(): acc[k] v return max(acc, keyacc.get)窗口长度7意味着大约需要7个滑窗结果才能稳定输出在实时视频里对应1到2秒的决策延迟这是可以接受的。如果对实时性要求更高把窗口压到3或5代价是动作切换点附近会出现短暂毛刺。4.3 阈值、帧率与检测置信度三个参数的联动这套管线里至少有四个阈值在互相影响检测置信度、轨迹匹配IoU、分类置信度、平滑窗口长度。最常犯的错误是把它们当成彼此独立的参数单独调某一个然后发现另一个环节崩了。检测置信度调低会引入更多误检框轨迹关联时误关联概率随之上升最终动作分类器被喂入大量非人体内容。分类置信度阈值调低原本低分的“不确定”动作会被当作真实动作输出告警量瞬间爆炸。帧率变化则影响全局同样一段动作视频15帧率和我们的推理帧率不同滑窗覆盖的物理时长完全不同。我习惯先固定检测置信度在0.45、轨迹IoU在0.3用一段离线视频跑动作分类看softmax分数分布再去定分类阈值最后根据业务对延迟的容忍度调平滑窗口。从上到下按依赖顺序调参而不是从下往上瞎试。4.4 视频流输入与批处理别让单帧检测拖垮整体吞吐如果直接用同步方式逐帧读取视频、逐帧跑YOLOv8、攒够帧再跑动作分类整体吞吐会非常难看因为YOLOv8推理期间视频读取在空等反之亦然。常见做法是把读帧和推理解耦成两个线程中间放一个带最大长度的队列。读帧线程持续从视频源读帧放入队列推理线程从队列取帧做检测和后续处理。队列有界是为了防止视频源快过推理能力时内存无限增长。import threading import queue frame_queue queue.Queue(maxsize30) def reader(cap): while True: ret, frame cap.read() if not ret: frame_queue.put(None) break frame_queue.put(frame) def worker(model): while True: frame frame_queue.get() if frame is None: break # 检测 轨迹关联 动作分类 process_frame(model, frame)这里的队列长度30意味着最多缓存约1秒的帧超出的帧会被丢弃。对实时监控而言丢帧比延迟累积更健康因为动作识别本身依赖的是连续观察而非每一帧。后期如果单路视频都跑不动优先考虑把检测输入尺寸降回640或换更小的YOLOv8n。4.5 日志与结果落盘可回溯比实时准确更重要动作检测项目做久了你会发现最耗时间的不是调试模型而是出问题后无法复现。模型在某一帧输出了一个“跌倒”告警但现场已经过去没有任何记录能回答“当时检测框在哪、置信度多少、前后帧是什么样”。所以从第一天起就要把结构化日志落盘。每个动作片段的开始帧、结束帧、行人轨迹ID、检测框平均置信度、动作类别Top3分数全部写成JSON。这样后续无论是调参还是复盘误报都有据可查。import json def save_log(segment): entry { track_id: segment.track_id, start_frame: segment.start_frame, end_frame: segment.end_frame, action: segment.action, top3_scores: segment.top3_scores, avg_det_conf: segment.avg_det_conf, } with open(inference_log.jsonl, a) as f: f.write(json.dumps(entry) \n)JSON的行式存储是为了方便按时间范围检索也方便直接导入到数据分析工具里画混淆矩阵。如果你在一个项目里没有留这种回溯能力后期每一份“抽检效果不佳”的报告都很难定位到底是检测、跟踪还是分类的问题。5. 踩坑记录这套方案里最容易翻车的五个“玄学”问题5.1 动作分类准确率断崖式下跌原因只是检测框把人切得太紧现象YOLOv8检测框看得很准紧贴人体边界但送入MMAction2后动作分类准确率比预期低了将近十个百分点。原因检测框为检测任务优化框住的是“人身体的可见部分”而动作识别需要的是“动作所涉及的空间范围”。人抬起手臂时手往往在检测框边缘甚至框外被扩边不足的裁剪直接切掉。模型等于在看一个缺胳膊的动作片段。解决裁剪前按1.2到1.5的系数扩展检测框并以人体中心为基准保证扩展后的框不偏离主体。5.2 换了一台机器后mmcv 编译失败让人想砸电脑现象在开发机上跑得好好的工程换到一台新机器上重装环境mmcv导入时报底层符号错误源码编译又卡在某个很久不更新的依赖上。原因新机器的CUDA、torch版本和开发机不一致而mmcv的预编译包是按torch和CUDA组合分发的。抄开发机的requirements.txt不等于抄了正确的mmcv包。解决先查阅MMAction2官方安装页里的版本匹配表确认当前torch版本对应的mmcv预编译包存在再安装。优先用mim安装让它自动解析合适版本。只要预编译包可用绝对不要走源码编译那是在给自己挖坑。5.3 行人短暂遮挡后动作被误判成“无动作”或者被拆成两次现象一个人从画面中走过中间被树干挡了十几帧输出结果里动作出现断裂变成了“行走—无动作—行走”甚至被当作两个不同的人。原因轨迹关联要求相邻帧间必须持续有IoU重叠遮挡期间没有检测框轨迹被判定为结束重新出现的框被当成了新对象。解决给轨迹关联加一段丢失容忍窗口短于15帧的断档不结束轨迹而是用上一帧位置做位置预测继续跟踪。注意预测的是位置变化不要做外观重识别后者在行人动作场景里通常比丢帧问题更麻烦。5.4 把检测置信度从0.5调到0.3之后告警量直接失控现象为了提升低光环境下的召回率把检测置信度下调到0.3结果行人动作告警暴涨大量画面角落的模糊人影也会触发动作分类。原因检测阈值下调后误检框进入轨迹关联当误检的目标包含了类似人体的纹理特征任意动作分类都可能被激活而且置信度加权投票在低分样本搅局时不够稳定。解决检测置信度保持在0.45以上单独处理低光场景而不是全局压低阈值。如果确实需要低阈值场景将分类决策改为“动作得分超过某个高判断基准才告警”用二次确认挡住误检。5.5 换用骨骼方案后训练好的模型在自采数据上完全失效现象看到论文里骨骼动作识别效果好把方案换成ST-GCN加姿态估计器结果在自采的监控视角数据上精度惨不忍睹还不如最初的RGB方案。原因骨骼方案的精度上限取决于前置姿态估计器。监控场景常见俯视角度、远距离小目标和侧面遮挡姿态估计器在这些视角下给出的关键点噪声极大动作分类器等于在噪声上跳舞效果自然崩。解决在自采数据上先用RGB时序分类做基线跑通全链路之后再用姿态估计结果做对比实验。只有当你确认姿态估计器在自己的实际拍摄角度下关键点定位足够稳定骨骼方案才有意义。6. 让模型在你自己场景里可用的验证闭环与细节习惯项目收尾阶段最容易出现的问题不是模型效果差而是“不知道效果到底行不行”。跑通源码只是起点要让它在你自己的摄像头角度、光线和行人尺度下可用需要一个系统性的验证闭环。我建议的验证流程是录制三段不同视角的视频分别对应正视角、侧视角和逆光场景每段60到90秒覆盖至少三种动作类别。先手工记录每个动作起止的时间点然后让管线跑一遍把输出的动作时间边界和真实时间边界做Temporal IoU统计。仅看准确率会骗人因为模型可能在某段动作的中间帧分类正确但起止边界偏差极大这直接关系到告警系统的响应时机。针对边界问题在输出端做一个后处理把连续超过0.5秒的相同动作标签合并成段丢弃小于0.3秒的孤立片段这个简单操作就能让时间边界明显干净。我最想强调的习惯是把每次调参都留档。在这个方向上各算法工程师的流程差异不在模型能力上而在是否坚持同一段视频反复验证。我的做法是每次改动后把旧权重和新权重的输出JSON做diff。动作类别变动超过5%的地方一张张找帧看区分到底是分类错还是上游检测错。处理完记录原因标注这是第几次改动、改了什么、为什么改。这套回归机制能帮你在项目后期省下大量重复调试的时间。我早期做过一个某公司的监控项目A同学负责调参连续两周都在凭感觉改检测阈值结果每天都觉得效果比昨天好但一直没跑数据量化对比。后来把两周前和当天的权重放到同一段视频上盲测发现准确率根本没有实质提升甚至还在某些场景退步了。从那以后我强制自己在每次改参数前先跑一次完整的评测脚本生成基线再动配置。盲调和凭感觉调补不出系统的可靠性。希望帮到你。本文还有配套的精品资源点击获取
RELATED

相关推荐

2.skill实战:用AI技能包构建自动周报生成器

2.skill实战:用AI技能包构建自动周报生成器

如果你也跟我一样,经常被同一类杂事反复磨时间,那你一定会理解为什么我电脑里躺着一排以 .skill 结尾的文件夹。今天文章的主角是其中的 2.skill,它不是我第二个孩子,也不是什么玄学编号,而是我给自己正式入库的“第二…

📅 2026/10/12 15:29:24
Python+TuShare构建A股自动选股程序:数据、规则与避坑指南

Python+TuShare构建A股自动选股程序:数据、规则与避坑指南

简介:面向量化投资入门者与Python开发者的A股自动选股实战项目,基于TuShare金融数据接口,覆盖行情获取、数据清洗、策略制定与回测分析全流程。压缩包共25个文件,以17个Python脚本为主体,包含数据抓取、策略模块、工作…

📅 2026/10/12 15:29:24
数学建模种植策略可视化:从决策变量到论文级图表

数学建模种植策略可视化:从决策变量到论文级图表

简介:这份资源面向参加2024年高教社杯全国大学生数学建模竞赛C题的学生与指导教师,聚焦农作物种植策略的可视化实现,帮助读者把线性规划、统计分析等建模结果转化为直观图表,适合具备一定Python基础、需要完善论文配图与数据探索环…

📅 2026/10/12 15:29:24
MORE NEWS

更多资讯

📰

2026年FDE银行信贷审批 + 反洗钱智能体生产级落地方案:把settings改到TaoToken

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

📰

Python网络舆情分析系统实战:从数据采集到情感可视化全链路

简介:这是一套面向高校计算机相关专业学生的网络舆情分析系统完整项目,可直接用于Python毕业设计或课程设计。项目采用Python 3.7/3.8开发,配合PyCharm、Navicat与MySQL数据库,前后端代码齐备,功能完善、界面美观、操作…

📰

手把手编写儿童手机远程监控App之MQTT嘟宝绑定流程:TaoToken统一Key接入与联调验证

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

📰

Python酒店评论情感分析:从TF-IDF到BERT的完整源码实战

简介:这是一份面向计算机相关专业学生与项目实战学习者的酒店评论情感分析完整项目包,由导师指导并通过评审,评审分98分,适合用作毕业设计、课程设计或期末大作业参考。资源共23个文件,压缩包约4.36MB,以14…

📰

从ANN.rar到可用预测模型:神经网络预测工程完整实操指南

简介:这是一份面向机器学习初学者的ANN预测分析示例,压缩包内包含一个完整的MATLAB程序文件(ANN.m),用于构建人工神经网络模型并执行数据预测。程序涵盖网络结构定义、训练参数设置、数据加载与预处理、反向传播训练及…

📰

从培训到赋能:AI落地项目的真实拆解与操作框架

我在多家公司带过 AI 落地项目,见过太多人花了大几千块钱报班、熬夜刷完几十节视频课,回到工位上第二天还是该怎么做就怎么做。问题不是他们不努力,而是他们把“培训”当成了“赋能”。这两个词看着像近义词,实际上一套是隔靴搔痒…

TODAY

今日更新

THIS WEEK

本周精选

THIS MONTH

本月热门

读完文章,想聊聊您的网站?

告诉我们您的行业与需求,资深顾问一对一梳理方案与报价,全程免费。

📞 💬