YOLOv8+MMAction2行人动作检测实战:两阶段方案与避坑指南
发布时间:2026/9/25 23:29:00来源:尧图网络
简介面向计算机视觉、行为识别与深度学习方向的研究者、工程师及高年级学生提供一套将YOLOv8目标检测与MMAction2时序模型相结合的行人动作检测可运行源码适合在智能监控、行为分析等场景中快速定位行人并识别动作读者需具备基本的深度学习与Python基础。压缩包共11个文件包含Python脚本、PTH权重、MP4演示视频、TXT配置、HTML页面、MD文档等整体仅14KB结构精简便于本地直接运行和二次开发。目前已有122人学习下载。资源完整覆盖MMAction2环境配置、数据预处理与预训练模型选择如时序平移模块TSM在小数据集上的优势、数据集划分与配置文件修改并给出YOLOv8行人提取、动作识别与结果融合的实现代码配合演示视频可直观验证训练、测试与推理流程。源码还附带轻量级权重与可视化页面能够帮助读者快速复现实验结果、理解关键参数并为进一步改进算法提供参考。1. 行人动作检测为什么是 YOLOv8 MMAction2 的组合监控画面里一个人摔倒在地YOLOv8 能在几十毫秒内圈出他的位置但这个框本身不会告诉你他到底是弯腰系鞋带还是晕倒。要在连续帧里判断动作语义常规路线是再叠一层时序识别模型——MMAction2 就是这个角色。把 YOLOv8 的检测框按时间序列裁剪出来交给 MMAction2 的 TSN/TSM 分类器输出“行走、奔跑、跌倒、挥手”这类标签这就是行人动作检测里最稳的两阶段方案。这套可运行源码的核心就是把这个串联链路做成能直接跑的脚本适合手里有视频数据、想快速验证动作识别效果的工程师。2. 两阶段方案的模型分工YOLOv8 画框MMAction2 定动作这一章先说清楚为什么拆成两步再分别讲两个模型各自管哪一段。我尽量用实际需求来展开不堆原理。2.1 为什么不是单阶段3D 卷积的边界在哪第一次接触行人动作检测的人都会问能不能用 SlowFast 或 C3D 这类 3D 卷积网络把检测和动作识别一步做完我在项目里试过这条路结论是能做但代价很大。3D 卷积网络直接吃整段视频输出的是“视频里发生了某个动作”这种视频级标签拿不到“画面里第几个行人正在做动作”这种空间信息。你把一段双人对话的视频喂进去模型能告诉你“有人在说话”但说不出是左边那个还是右边那个。要拿到行人级别的动作标签还得外挂一个检测器去匹配时空位置绕了一圈又回到两阶段。另一个现实问题是训练成本。3D 卷积网络的参数量是 2D 网络的数倍训练需要更长的视频片段和更大的 batch显存开销直线上升。在 GTX 1660 Ti 这类卡上训练一轮 30 个 epoch 的 TSN 大概需要几个小时同样的数据换 SlowFast时间直接翻三倍不止。所以我在行人动作检测这个场景里很少推荐单阶段方案除非你的需求本身就是“整段视频只判断一个动作”不需要定位到具体行人。两阶段的好处在于两个模型可以各自迭代。检测器漏检了单独调 YOLOv8 的阈值和数据增强动作识别不准单独换 MMAction2 的骨干网络或者调采样策略。这种解耦让排查问题时的搜索空间小了很多。2.2 YOLOv8 负责的行人定位与参数选择YOLOv8 在 COCO 上训练过的权重本来就能输出 person 这个类别所以行人检测基本不需要从头训练。我一般直接加载 yolov8n.pt把检测类别过滤成 personCOCO 里 person 的类别 ID 是 0。要注意的是YOLOv8 的输出有两样东西置信度分数和边界框坐标。后续动作识别只关心框的裁剪内容检测的召回率比精确率重要——宁可多圈几个虚框也不能漏掉真正发生动作的人。置信度阈值我习惯调到 0.350.45而不是默认的 0.5。阈值调低之后画面远处的小目标也能被框住但代价是偶尔把柱子的影子和人像海报也框进来。针对动作识别任务框的噪声可以被后续的时序模型吃掉一部分因为单帧误检不会在连续帧上形成连贯轨迹滑动窗口里的多数帧是干净的行人框。检测器的输入分辨率也要单独拿出来说。直出 1280 分辨率能显著改善远距离小目标的召回但推理时间几乎翻倍。在 GTX 1660 Ti 这种级别的卡上比较均衡的选择是 640 输入配 yolov8n检测部分能跑到 40 FPS 以上如果部署到 RK3588 这类边缘设备还要再降一档用 544 或 480 分辨率同时把模型从 n 换成更轻的变体。分辨率这一项直接决定下游识别器看到的行人区域大小调参的时候必须和识别器的输入尺寸一起考虑。2.3 MMAction2 的动作分类管道TSN/TSM 怎么选MMAction2 是 OpenMMLab 体系里的动作识别库它不负责找目标只负责对一段时序输入做分类。它支持的模型很多行人动作检测场景里用得最多的是 TSN 和 TSM。TSN 的思路是把一段视频均匀切成若干段每段抽一帧再把这几帧的特征融合。它结构简单适合起步。TSM 则是在 ResNet 的通道维度上做时间偏移用 2D 卷积模拟 3D 卷积的时序建模精度和速度的平衡比 TSN 更好。如果动作类别集中在“跌倒、下蹲、挥手”这种短时动作TSM 通常更容易抓住变化剧烈的帧如果主要识别“行走、站立、跑步”这类持续时间长的动作TSN 就够了没必要上 TSM 增加显存开销。MMAction2 的输入不是整段视频而是 Clip 的帧序列。配置里最关键的三组参数是 clip_len、frame_interval 和 num_clips含义和取值如下表参数作用常见取值clip_len每个样本采多少帧8 或 16frame_interval采样间隔隔几帧取一帧14num_clips测试时取几段做平均训练 1测试 4clip_len 设得越大模型看到的时序上下文越长但对跌倒这种需要快速响应的场景8 帧配合 frame_interval2 就够用了。太长的窗口反而会把动作前后的冗余信息也学进去比如把“跌倒后躺在地上”误学成动作本身的一部分。这个参数是后面所有优化里最值得反复试的一个。2.4 两阶段串联的推理链路检测、跟踪、滑动窗口把检测和识别串起来我通常不直接对每一帧都做完整识别而是采用“检测 → 跟踪 → 滑动窗口 → 分类”的管线。具体流程是这样的视频帧先进入 YOLOv8得到当前帧的行人框接着用 IoU 匹配或者 ByteTrack 给每个行人分配一个跟踪 ID每个跟踪 ID 维护一个固定长度的帧缓冲队列只保存该行人最近 N 帧的裁剪图缓冲队列满了之后才把整个队列作为一次 batch 送到 MMAction2 里做动作分类。为什么要中间插一层跟踪因为动作识别需要同一行人的连续帧如果每帧都重新检测框的位置抖动会让裁剪出的行人区域在空间上飘来飘去时序模型输入的对齐性就会变差。跟踪之后裁剪区域由跟踪框决定框的位置变化被平滑掉动作识别的准确率能提高不少。代价是引入了跟踪模块的延迟和 ID Switch 问题——行人互相遮挡时 ID 会跳变缓冲队列里会混入另一个人的帧。这个坑我在避坑章节单独细说。所以整个链路里YOLOv8 只做“时刻 t 的行人定位”MMAction2 做“时间区间 [t-T, t] 的动作语义”两者各管一段中间的跟踪器负责把它们缝起来。可运行源码里核心的串联脚本做的就是这件事。3. 跑通可运行源码环境版本配对与最小复现命令拿到源码包之后第一步不是看代码是先搭环境。这一章的每一行命令我都在 Ubuntu 20.04 上跑过CPU 版也能跑但速度确实劝退——等 MMCV 编译完基本就有换显卡的冲动了。3.1 环境版本配对Python、PyTorch、MMCV 怎么搭这套源码依赖两条技术栈版本冲突的重灾区在 MMAction2 这边。我的建议是先在干净的虚拟环境里装 PyTorch再通过 mim 工具装配套的 mmcv最后装 mmaction2。直接 pip install mmaction2 而不先装 mmcv 的话pip 会把一堆不兼容的依赖拉进来。conda create -n action python3.8 -y conda activate action pip install torch1.13.1 torchvision0.14.1 --index-url https://download.pytorch.org/whl/cu117 pip install -U openmim mim install mmcv2.1.0 mim install mmengine0.10.0 mmdet3.3.0 pip install ultralytics这里最值得说清的是版本配对逻辑。torch 1.13.1 配 cu117 是我实测最稳的组合mmcv 2.1.0 是 mmaction2 1.x 版本能正常编译的一个可用版本mmdet 3.3.0 是为了让 mmaction2 里依赖检测接口的模块正常工作。如果你把 torch 升到 2.1mmcv 就需要去源码编译一个上午就没了。我刚开始就是不信邪直接装最新版结果 mmcv 在 import 阶段报密密麻麻的 CUDA 算子错误老老实实回退版本才跑通。装完检查一下版本号对不对得上python -c import torch, mmcv, mmengine, mmaction; print(torch.__version__, mmcv.__version__, mmengine.__version__, mmaction.__version__)正常输出大概是1.13.1cu117 2.1.0 0.10.0 1.2.0这种格式。如果 import mmaction 直接报ModuleNotFoundError: No module named mmcv说明 mmcv 没装进当前虚拟环境检查一下是不是有多个 conda 环境混了。如果 mmcv 报的是 CUDA 相关的 undefined symbol说明 mmcv 二进制和 torch 的 CUDA 版本不匹配需要卸载重装。3.2 源码结构与预训练权重放哪这个源码包的组织方式和大多数两阶段检测项目一致打开目录先看到三层结构project/ ├── configs/ # 检测与识别的配置文件 │ ├── yolov8_det.yaml # YOLOv8 检测参数 │ └── tsn_recognizer.py # MMAction2 识别配置 ├── weights/ # 预训练权重 │ ├── yolov8n.pt │ └── tsn_r50.pth ├── data/ │ ├── test.mp4 │ └── frames/ # 从视频裁出的帧序列 ├── detect_then_recognize.py └── requirements.txtweights 目录需要自己补两个权重文件YOLOv8 的可以从 ultralytics 官方发布页拿文件名 yolov8n.ptTSN 的权重在 MMAction2 的 model zoo 里有 ResNet50 骨架的版本下载后按照 configs/tsn_recognizer.py 里 checkpoint 字段写好的路径放进去。注意这两个权重文件加起来可能超过 150 MBGit 仓库一般不会塞进去所以下载这一步不要跳过。configs 目录里的 tsn_recognizer.py 是 MMAction2 的完整训练配置文件它同时被训练脚本和推理脚本共用。改这个文件之前先备份微调的时候很容易把参数改乱。3.3 跑通检测 动作识别的完整命令环境就绪、权重就位之后直接跑串联脚本python detect_then_recognize.py \ --video data/test.mp4 \ --det-weight weights/yolov8n.pt \ --rec-config configs/tsn_recognizer.py \ --rec-weight weights/tsn_r50.pth \ --output output.mp4 \ --det-conf 0.4 \ --clip-len 8 \ --frame-interval 2脚本内部的核心循环长这样import cv2 import numpy as np from ultralytics import YOLO from mmaction.apis import init_recognizer, inference_recognizer detector YOLO(args.det_weight) recognizer init_recognizer(args.rec_config, args.rec_weight, devicecuda:0) cap cv2.VideoCapture(args.video) frame_idx 0 buffer [] # 每个行人一个队列这里假设画面里只有一个人 while cap.isOpened(): ret, frame cap.read() if not ret: break frame_idx 1 # 1. 检测只保留 person 类别0 是 COCO 里 person 的 id results detector(frame, classes[0], confargs.det_conf) boxes results[0].boxes.xyxy.cpu().numpy() if len(boxes) 0: x1, y1, x2, y2 map(int, boxes[0]) crop frame[y1:y2, x1:x2] buffer.append(crop) # 2. 攒够一个窗口后整体送入动作识别 if len(buffer) args.clip_len: result inference_recognizer(recognizer, buffer, fps5) label_id result[pred_label] print(fframe {frame_idx}: action_id{label_id}) # 滑动窗口弹掉最旧的一帧保持队列长度 buffer.pop(0)这段代码的逻辑拆开看。classes[0]这个参数把检测类别锁死在 person 上避免车、猫、狗也进到动作识别里浪费算力。conf0.4是检测置信度阈值前面说过动作识别任务里调低一点比调高好。buffer队列保存的是裁剪后的行人图像不是整帧所以后续识别模型看到的是干净的行人区域背景干扰小。inference_recognizer是 MMAction2 提供的高层推理接口它内部会完成 resize、归一化、reshape 成模型输入张量这一整套预处理。但它要求输入是一个列表列表里每个元素是HWC格式的 uint8 图像数组所以代码里直接传buffer是可行的。fps5是告诉识别器这段帧序列对应的时间间隔如果你的视频帧率是 25 FPSbuffer 里每隔一帧取一次那真实时间跨度比 fps5 算出来的更长。这个参数对时序模型影响不小识别结果不稳定时先检查这里。3.4 先用 CPU 把链路跑通再上 GPU如果服务器上没有显卡或者第一次跑不想立刻烧掉半天时间在 CUDA 环境上可以先用 CPU 版把链路跑通。做法是把device参数改成cpu同时把--video换成一段几十秒的短视频。CPU 版跑起来慢但至少能验证数据路径和脚本逻辑没有问题。python detect_then_recognize.py \ --video data/test_clip.mp4 \ --det-weight weights/yolov8n.pt \ --rec-config configs/tsn_recognizer.py \ --rec-weight weights/tsn_r50.pth \ --device cpu \ --det-conf 0.4跑完一条测试视频后建议再做一次最小验证把--det-conf调成 0.8故意让检测框变小甚至漏检观察输出里动作类别是否变成了背景类或频繁跳变。这样做是为了确认整条链路里时序识别确实在依赖检测质量而不是只输出一个和视频内容无关的固定标签。我见过把识别器跑成了恒等输出却没人发现的案例这个验证能一秒钟戳穿。4. 训练自己的行人动作数据标注格式与微调流程预训练权重只能识别它见过的动作。真实场景里你关心的往往是“跌倒、攀爬、挥手求救”这些特定动作所以微调不可避免。这一章从原始视频走到可训练的帧序列和标注文件。4.1 数据准备从视频到帧序列MMAction2 支持两种数据形式整段视频和帧序列。行人动作检测场景我强烈建议用帧序列原因有两个一是训练时能精确控制采样起点避免视频编解码器在随机裁剪时引入花屏帧二是帧序列可以做在线数据增强比如对每一帧做随机翻转视频输入就没这么灵活。把一段视频转成帧序列常见做法是这样ffmpeg -i raw/fall_01.mp4 -q:v 2 -vf fps25 data/frames/fall_01/img_%05d.jpg-q:v 2控制 JPEG 压缩质量值越小画质越好fps25把输入视频统一到 25 帧每秒避免因为素材帧率不齐导致采样间隔对不上。转换之后检查一下 img_ 序列的编号是否从 1 开始连续递增MMAction2 在解析帧路径时会用固定位数的占位符如果编号断档或者不补零RawFrameDataset 会直接报文件不存在。每一段视频还要同步配一个动作类别标签。标注文件是纯文本每行三个字段帧文件夹路径、帧总数、类别 ID。类别 ID 从 0 开始对应的类别名写在另一个 class_ind.txt 里。例如data/frames/fall_01 120 2 data/frames/walk_05 150 0 data/frames/wave_02 100 3帧数这个字段必须和真实帧数一致写少了会把样本截断写多了会在解码时越界。我通常会写一个小脚本来统计每段视频的帧数而不是手填。import cv2, glob for folder in glob.glob(data/frames/*): imgs sorted(glob.glob(folder /*.jpg)) print(folder, len(imgs))如果你用的是 labelme 标注检测框还要把 labelme 的 JSON 转成 YOLO 的 txt 格式。YOLO 格式每行是一个class x_center y_center width height归一化到 01 之间。这个转换脚本网上很多但要注意 labelme 导出的是多边形坐标直接取外接矩形然后除以图片宽高就能得到 YOLO 格式。别忘记类别 ID 要从 0 开始编号labelme 里用字符串类别名映射关系很容易错位。4.2 修改配置文件类别数、样本策略与优化器微调 MMAction2 模型不用改模型结构只需要改配置文件里的几个关键字段。这里给出一个 TSN 的配置骨架model dict( typeRecognizer2D, backbonedict(typeResNet, depth50, pretrainedtorchvision://resnet50), cls_headdict( typeTSNHead, num_classes4, # 改成你自己的动作类别数 in_channels2048, spatial_typeavg, consensusdict(typeAvgConsensus, dim1), dropout_ratio0.4)) train_pipeline [ dict(typeSampleFrames, clip_len8, frame_interval2, num_clips1), dict(typeRawFrameDecode), dict(typeRandomFlip, flip_prob0.5), # 数据增强随机水平翻转 dict(typeResize, scale(256, 256), keep_ratioFalse), dict(typeNormalize, mean[123.675, 116.28, 103.53], std[58.395, 57.12, 57.375], to_rgbTrue), ] optimizer dict(typeSGD, lr0.01, momentum0.9, weight_decay1e-4) total_epochs 30这里要改的地方很集中num_classes改成动作类别总数clip_len和frame_interval根据动作剧烈程度调整优化器在数据量不大的情况下保持 SGD 就行AdamW 在这个任务上没有明显优势。pretrainedtorchvision://resnet50会从 torchvision 的权重缓存加载初始化如果服务器没有外网访问权限要提前把权重文件放到缓存目录否则这里会卡住。训练命令走标准流程python train_recognizer.py configs/tsn_recognizer.py \ --work-dir work_dirs/tsn_finetune \ --validate --seed 42训练日志里主要盯两个指标loss 曲线和 top1 acc。正常情况下 loss 在前 5 个 epoch 快速下降30 个 epoch 后 top1 acc 能到 80% 以上。如果 loss 一直不降先不要怀疑模型回去检查标注文件里的类别 ID 和帧数对不对——RawFrameDataset 在读文件时如果 label 越界通常不会报错只是模型始终学到随机映射。YOLOv8 的微调相比之下简单很多。把行人框标注按上面说的转成 YOLO 格式 txt再写一个 data.yamlpath: ./datasets/person train: images/train val: images/val nc: 1 names: [person]训练命令yolo detect train datadatasets/person.yaml \ modelyolov8n.pt \ epochs30 imgsz640 batch16如果检测训练数据是从监控视频里截的建议每隔 510 帧采样一张避免相邻帧高度相似导致训练集冗余每帧都采样会加重过拟合。4.3 训练监控与评估指标怎么看微调不是跑完就结束。我习惯在训练结束后做一次跨视频验证准备一段模型没见过的视频让串联脚本输出每一帧的检测框和动作标签然后人工核对两类错误——检测漏检和动作误判。在监控场景里动作识别的准确率不能只看整体 top1。跌倒这类少样本动作的召回率才是关键。我常用混淆矩阵来判断模型是不是把“跌倒”全学成了“蹲下”这两种动作在视觉上非常像唯一的区别在时序变化速度上。如果混淆矩阵里这两个类别互混严重就把frame_interval从 2 改成 1让模型看到更密集的帧反过来如果动作本身很慢比如“行走”密集采样反而引入大量相似帧让模型只学会“过去帧长什么样”而不是“动作怎么变化”。训练结束后也可以用脚本把 loss 曲线直接画出来python tools/analysis/analyze_logs.py plot_curve \ work_dirs/tsn_finetune/vis_data/train_log.json \ --keys loss_cls top1_acc --out loss_curve.png这个工具是 MMAction2 自带的分析脚本能省去手动解析 JSON 日志的功夫。曲线如果出现 loss 下降后反弹说明学习率偏大把 lr 从 0.01 降到 0.005或者加一个 cosine 学习率调度别硬扛到最后。5. 避坑指南5 个现场翻车记录与修复方法从环境搭建到训练再到推理每条坑我都交过学费。这一章把我踩过的几个典型问题按“现象 → 原因 → 解决”整理出来你遇到相似报错时可以直接照着排查。5.1 MMCV 与 PyTorch 版本错位import 直接崩现象conda 环境装好后import mmaction报错错误堆栈指向 mmcv 的_ext模块找不到某个 CUDA 符号或者直接提示undefined symbol。原因mmcv 是预编译扩展库它必须和 PyTorch 的 CUDA 版本严格对应。我是先装了 torch 2.1.0又随手pip install mmcvpip 拉下来的 mmcv 是给 torch 1.x 时代编译的两个二进制库里的 CUDA 算子对不上。解决清理掉错的 mmcv改用 mim 安装匹配版本pip uninstall mmcv -y mim install mmcv2.1.0如果还是失败最后的手段是本地源码编译但要预留 30 分钟以上耐心等编译器跑完。CPU 版的 Ubuntu 20.04 上这一步尤其慢因为要链接全套算子。这里有个玄学经验源码编译时不要开多线程MMCV_WITH_OPS1配合单线程编译成功率比make -j8高很多。5.2 CUDA out of memorybatch 调小也翻车现象训练启动几秒后直接CUDA out of memory把 batch 从 16 调到 4 还是一样。原因跑通训练时用了 batch16换到数据更多的数据集后其实不是 batch 导致单次显存峰值超标而是 MMAction2 的RawFrameDataset在数据加载阶段一次性把 8 帧的原始分辨率读进内存做随机裁剪峰值显存出现在数据增强那一步而不是前向传播。解决把Resize从(256, 256)降到(224, 224)同时在配置里给数据加载设置更小的 prefetchtrain_dataloader dict( batch_size8, num_workers4, persistent_workersTrue, pin_memoryTrue, prefetch_factor2)prefetch_factor 是 PyTorch 2.x 的参数调成 2 能显著降低 DataLoader 在 CPU 和 GPU 之间的数据堆积。配了之后同样 batch8训练峰值显存大概能减少 1.5 GB。如果还崩那就把frame_interval从 1 改成 2显存占用和帧数为线性关系。5.3 检测框与识别窗口的时间对齐错乱现象推理时动作标签在“行走”和“跌倒”之间高频跳变肉眼看上去画面里的人已经平躺在地上了标签还在输出“跌倒”。原因两阶段链路里识别器拿到的帧序列来自滑动窗口但检测器每一帧都独立输出。行人一旦摔倒身体姿态发生剧烈变化检测框有时会从“竖直的人形框”跳成“躺平的大矩形框”裁剪出来的行人区域尺度突变时序模型看到的前后帧一致性被破坏。解决在检测和识别之间加一个简单跟踪器用跟踪框的平滑结果替代每帧检测框。我实现过最简单有效的方案维护每个跟踪 ID 的框指数移动平均当前帧的检测框只贡献 20% 的权重其余 80% 来自历史预测。这样框的尺度变化被压制时序输入稳定了标签跳变基本消失。ema_box 0.8 * ema_box 0.2 * current_box需要注意摔倒瞬间检测框会快速变矮变宽EMA 的更新系数需要根据动作剧烈程度动态调整。摔倒这类瞬态动作用 0.2 的检测权重比较合适走路这种慢动作用 0.1 就够。这个参数的整定没有捷径多试几轮就知道手感了。5.4 跌倒样本太少模型只会输出“行走”现象训练数据里“行走”有 500 段“跌倒”只有 20 段微调后模型在测试集上什么都预测成行走acc 有 90% 但实际没意义。原因类别分布极端不平衡。MMAction2 的 TSNHead 默认用标准交叉熵多数类主导了梯度更新少数类在训练中几乎没有出现过模型学到的是“永远输出多数类”。解决两个手段叠加使用。第一个是类别重加权在 TSNHead 里给每个类别设 loss 权重cls_headdict( typeTSNHead, num_classes4, loss_clsdict(typeCrossEntropyLoss, loss_weight1.0), loss_balancedict(typeClassBalancedLoss, freq[500, 50, 20, 100]))第二个是数据上做视频片段过采样对样本少的类别每段视频采样多个随机起点把跌倒类从 20 段扩成 100 段的等价样本。扩完之后要在配置里的train_pipeline里加一步RandomCrop否则多个片段高度重叠扩了也白扩。这两招配在一起跌倒类的召回率能从 20% 提到 70% 以上代价是行走类的精确率掉几个点换回来的收益绝对值是值得的。5.5 两阶段推理掉到 8 FPS卡成幻灯片现象RTX 3060 上单独跑 YOLOv8 有 60 FPS单独跑 MMAction2 的 TSN 有 200 FPS但两阶段串联之后只能跑到 8 FPS视频肉眼可见地卡。原因串联之后每个行人每 8 帧都要单独inference_recognizer一次识别器是批量推理设计单样本推理效率极低。而且 Python 层的cv2.imread和预处理互相阻塞GPU 一直在空等 CPU。解决把识别间隔拉长检测每帧做识别每 12 帧做一次中间帧沿用上一次的动作标签。同时把预处理的 resize 和归一化挪到 GPU 上用torchvision.transforms的 tensor 管道替代 numpy 实现。调整后同样的硬件能到 25 FPS 左右对监控场景已经够用了。如果还不够就把检测端换成 TensorRT 导出的 engine检测耗时能再砍一半这个放到下一章细说。6. 让方案跑得更快帧采样策略与端到端验证这一章落到两个具体技巧和一种验证方法也是我每次接手这类项目最后都会做的三件事。6.1 跳帧检测 滑动窗口识别的组合监控场景里行人不会瞬移检测不需要每帧都做。我采用的方案是检测间隔 K 帧K 取 35非检测帧用上一帧的检测框配合光流做简单平移修正。这样检测的耗时直接除以 K识别侧保持滑动窗口不变整体吞吐能再提 30% 左右。代价是行人突然加速或转向时框的位置会滞后几帧动作识别输入里会短暂出现错位我的经验是 K 不超过 5 就不会明显影响动作判定的准确率。跳帧之后滑动窗口的采样位置也要跟着改否则窗口里会混入大量重复帧。6.2 端到端验证用一段真实监控视频验收跑完源码和微调后我最后会做一次完整的验收拿一段 5 分钟的监控视频目标是正确检出其中 3 次跌倒事件且不出现连续 3 秒以上的误报。验收时我会重点看两个时间点——检测框第一次出现的时间和动作标签第一次变成“跌倒”的时间两者间隔超过 1.5 秒就说明识别窗口过长需要调小 clip_len 或 frame_interval。这种验证方式比单看准确率更贴近实际部署时用户对系统的体验。建议把验收视频固定下来每次改完配置都跑一遍同一段方便对比前后效果。6.3 我的收尾习惯我最后都会写一个简单的 shell 脚本把训练、评估、推理全串起来参数写死在配置文件里下次换数据集只需要改 config 和 data 目录。坚持这个习惯之后我的项目再也不会出现“三个月后回头跑发现自己忘了当初怎么调用”的尴尬。至于要不要把这两阶段推到更边缘的板子上那是在帧采样、EMA 跟踪框都验证够了之后才考虑的事。希望帮到你。本文还有配套的精品资源点击获取
网站建设高端定制企业官网