基于YOLOv8-pose的游泳动作识别实战:数据标注、训练与Gradio部署 简介面向计算机视觉方向学生与毕业设计开发者这份资源提供一套基于YOLOv8的游泳动作识别系统包含完整可运行的源码、配套数据集、可视化界面与部署说明适合快速复现并用于毕设、课程设计或初期立项演示。资源共97个文件以Python源码70个py为主辅以预训练权重pt、配置文件xml、说明文档txt及演示视频mp4等其中py覆盖模型训练、检测服务、可视化页面等核心模块整体压缩包仅24.21MB轻量易部署。目前已有40人学习下载。除基础识别功能外项目还支持输出核心指标曲线图、混淆矩阵、F1分数曲线、精确率-召回率曲线、验证集预测结果与标签分布图便于在答辩或汇报中直观展示模型效果同时提供README指引与多种配置文件便于二次修改以适配其他动作识别场景。资源包内代码均经运行测试适合希望直接获得可演示系统的学生或入门开发者参考学习。1. 用YOLOv8做游泳动作识别先要搞清楚这套系统识别的是“泳姿”而不是“是否在游泳”实际项目里光靠检测框根本区分不了自由泳和蛙泳需要把YOLOv8的检测能力与姿态关键点结合起来。这套系统在检测到游泳者的同时输出头部、手腕、肩膀、髋、膝、踝等关键点坐标再依据关节角度和时序变化去判别泳姿类别。相比直接对整张图分类它能应对画面中多人、遮挡和水花干扰。本文以毕设和课程设计最常见的落地方式来讲基于YOLOv8-pose做人物关键点提取再结合简单分类器完成动作识别最后用Gradio搭建可视化界面并给出训练、部署和调优的完整路径。这个场景适合谁如果你是计算机视觉方向的在校生需要一周内把系统跑起来或者想把模型部署到自己的笔记本上可以照这套方案走。整个链路里我会把数据标注、训练参数、界面封装和导出细节全部列出遇到坑也能看日志定位。2. 识别框架的底层逻辑YOLOv8检测目标位置姿态分支负责动作建模2.1 为什么选YOLOv8网络结构里C2F更适合游泳这种动态目标YOLOv8从2023年初发布后几乎是目前工业界落地最快的检测架构。它的C2F模块替换了CSPNet在保持轻量的前提下提升了梯度流通对游泳者这种频繁遮挡、身体弯曲变形的目标小目标和中目标召回都更好。相比YOLOv5YOLOv8不需要再手工设置anchor去掉了NMS阈值与anchor比例的耦合训练时少一撮需要调的超参。而且Ultralytics将检测、分类、姿态估计统一在一个框架里一行代码就能切换训练任务这恰好适合毕设需求——既要用检测框定位人又要用姿态关键点去描述划臂和打腿。实际选择型号时我一般建议先用yolov8n-pose跑通流程再根据显存升级到yolov8s-pose或yolov8m-pose。GTX 1660 Ti这种6GB显存的卡能够跑yolov8s-pose的batch size为16再大就会OOM。如果你只有CPU环境yolov8n-pose在640分辨率下推理一帧大约120-180ms做离线视频分析可以接受实时预览会卡顿。2.2 游泳动作识别的两步方案检测关键点序列分类要区分自由泳、蛙泳、蝶泳、仰泳不能只看静态的一帧。常见做法是用YOLOv8-pose先输出每个人17个关键点然后按关键点计算两个核心特征肩关节与肘关节的夹角、髋关节和膝关节的屈伸速度。接着把连续30帧的特征组成一个时间窗口送入一个轻量分类器比如LSTM或随机森林。有些毕设直接将姿态关键点序列输入Transformer但数据量少时容易过拟合。下面用一个表格把四种泳姿的关键特征和容易混淆的点列出来泳姿观察周期关键夹角特征常见错判原因自由泳手臂交替各一次肩关节角度变化180°左右肘关节逐渐屈伸与仰泳混淆需看身体朝向蛙泳双腿同步蹬夹膝关节屈曲角度峰值超过130°与自由泳打腿节奏不同需看双腿是否同时蝶泳双臂同步前扑肩部与髋部出现波浪状位移与蛙泳混淆需看双腿并拢状态仰泳身体背部朝上肩关节旋转但肩部位置稳定自由泳翻转身体的瞬间会被误判这个表就是后续特征工程的依据。如果你只有检测框没有关键点那么建议直接给检测框做光流不过稳定性很差所以项目里包含的关键点数据集才是能跑通动作识别的核心。2.3 数据标签怎么设计检测框关键点泳姿类别YOLOv8-pose要求标签格式为class_id, x_center, y_center, width, height, kpt_x1, kpt_y1, visibility1, ...其中visibility为0表示该点未标注。对于游泳动作识别还要额外增加一个泳姿类别字段这个可以放在文件名所在的集合名中或者在训练分类器时单独打标签。下面是一个数据组织的建议结构swim_dataset/ ├── images/ │ ├── train/ # 视频切帧 │ └── val/ ├── labels/ │ ├── train/ # 与images同名的txt │ └── val/ └── action_labels/ ├── train.txt # 每行帧号 泳姿标签 └── val.txtlabel_images直接用labelme或labelImg标注人的检测框然后用数据增强脚本把关键点按COCO顺序写入txt。现实中毕设项目很大概率使用公开泳池数据集或者自己录一段游泳视频用“Roboflow”自动标注再人工精修。若数据集与目标场景差异大比如比赛视频低角度你家摄像头高角度需要手动加翻转向增广。然后写一个转换脚本把标注文件同步生成到规范格式。核心代码如下import json import numpy as np def convert_labelme_to_yolo(json_path, out_txt, image_shape, class_nameswimmer): data json.load(open(json_path)) h, w image_shape[:2] shapes [s for s in data[shapes] if s[label] class_name] with open(out_txt, w) as f: for s in shapes: pts np.array(s[points], dtypenp.float32) x_min, y_min pts.min(axis0) x_max, y_max pts.max(axis0) box_w (x_max - x_min) / w box_h (y_max - y_min) / h cx ((x_min x_max) / 2) / w cy ((y_min y_max) / 2) / h f.write(f0 {cx:.6f} {cy:.6f} {box_w:.6f} {box_h:.6f}\n)这段代码只把矩形框转成YOLO格式。对于关键点通常不在这一步处理而是先用一个大模型生成伪关键点再人工检查。这个技巧在数据量不够时很管用——用yolov8m-pose.pt在目标视频上跑一遍将输出结果作为预标注导入标注软件修正。3. 从零跑通训练环境配置、数据集整理和最小可复现命令3.1 用conda装配YOLOv8运行环境先装好GPU版PyTorch再安装ultralytics。不要用pip直接装PyTorch容易装成CPU版本。下面命令在Anaconda中执行conda create -n swim python3.10 -y conda activate swim conda install pytorch torchvision pytorch-cuda12.1 -c pytorch -c nvidia pip install ultralytics opencv-python pandas gradio flask onnxruntime说明pytorch-cuda版本要与驱动匹配如果驱动只支持CUDA 11.8将上面的12.1改成11.8。装完用python -c import torch; print(torch.cuda.is_available())验证。GTX 1660 Ti驱动通常支持12.1但老驱动请先升级。yolov8环境配置最容易踩的坑是OpenCV与ultralytics版本冲突装完后先跑一次yolo predict sourcebus.jpg modelyolov8n.pt确认基础链路通顺。3.2 数据集整理视频抽帧和标签清洗把视频按2-4帧提取一张图像能够避免连续帧过度相似导致过拟合。我用下面的命令做抽帧ffmpeg -i swim.mp4 -vf fps2 images/%06d.jpg抽帧后需要清洗质量差的图像有水花遮挡、运动员离场。清洗用人工看一眼不要只写自动化脚本因为模糊样本会直接拉低mAP。然后通过labelimg画框再把画出的box转成YOLOv8-pose的txt步骤很容易出错我建议完成转换后用yolo check-data检查标注分布。一个干净的数据集结构要包含data.yamlpath: swim_dataset train: images/train val: images/val nc: 1 names: [swimmer] kpt_shape: [17, 3]注意kpt_shape中的3代表x, y, visibility。如果你的数据集中有大量游泳者只露出半身比如肩膀以下被水遮挡那关键点缺失会很多建议把visibility阈值改为2训练时忽略这些点。3.3 训练自己的数据集关键参数与最小命令训练命令非常短但参数影响很大。下面命令用于训练一个姿态模型yolo pose train \ modelyolov8n-pose.pt \ dataswim.yaml \ epochs120 \ imgsz640 \ batch16 \ device0 \ patience20 \ projectswim_results \ namepose_baseline参数说明epochs不是越大越好120轮足够超过200轮通常开始过拟合尤其是模拟数据。imgsz如果动作区域小用800或960可以提高小目标关键点精度但显存占用翻倍。batch6GB显存跑n模型可以用16如果换成s模型要降到8。patience验证集持续20轮不涨就早停避免占用时间。device多卡可以写0,1CPU不指定即可。训练完成后会在swim_results/pose_baseline/weights/下生成best.pt和last.pt。接着用yolo pose val验证观察metrics/mAP50-95以及关键点的PCK指标。yolo pose train记录下来的results.csv能画损失函数曲线图直接通过pandas读取画图即可不需要再用TensorBoard。import pandas as pd import matplotlib.pyplot as plt df pd.read_csv(swim_results/pose_baseline/results.csv) plt.plot(df[epoch], df[train/pose_loss], labeltrain) plt.plot(df[epoch], df[val/pose_loss], labelval) plt.legend() plt.show()3.4 训练动作分类器基于关键点序列关键点模型只能给骨骼点不能直接输出泳姿。需要把best.pt作为特征提取器对每30帧提取一次关键点矩阵组成样本。随机森林等经典分类器就能达到95%的准确率。给出特征提取代码import numpy as np from sklearn.ensemble import RandomForestClassifier def extract_features(kpts_seq): angles [] for frame in kpts_seq: left_shoulder frame[5] left_elbow frame[7] left_hand frame[9] v1 left_shoulder - left_elbow v2 left_hand - left_elbow cos_angle np.dot(v1, v2) / (np.linalg.norm(v1)*np.linalg.norm(v2)1e-6) angles.append(np.arccos(np.clip(cos_angle, -1, 1))) return np.array(angles).flatten()说明如果关键点缺失需要做插值不能直接丢弃否则时域长度不足。随机森林并不在乎特征缩放但LSTM需要将时间窗口打乱因为不同泳姿的时间周期长度不一致直接用LSTM反而容易欠拟合。分类器训练完成后用joblib保存供后续可视化界面调用。4. 把模型封装成可视化和可部署服务Gradio界面 Flask API4.1 使用Ultralytics的predict接口实现视频识别在封装之前先用pipeline跑通一个视频。下面代码加载训练好的best.pt并输出带关键点和标注框的视频from ultralytics import YOLO model YOLO(swim_results/pose_baseline/weights/best.pt) results model.predict(swim.mp4, saveTrue, conf0.25, imgsz640)predict参数说明conf是置信度阈值0.25在游泳场景下相对平衡如果误检多可以调到0.4。saveTrue会把结果视频写到runs/pose/predict/目录下。如果你只需要关键点数组可以遍历results[i].keypoints.xy。4.2 用Gradio快速搭建可视化界面Gradio的Video组件能处理视频上传处理函数返回视频路径和动作统计表界面代码只需十几行import gradio as gr def process_video(video_path): out_path infer(video_path) # 封装好的推理函数 stats collect_action_stats(out_path) return out_path, stats gr.Interface( fnprocess_video, inputsgr.Video(label上传游泳视频), outputs[gr.Video(label识别结果), gr.Dataframe(headers[动作, 次数])], titleYOLOv8游泳动作识别系统 ).launch(server_name0.0.0.0, server_port7860)说明gr.Video组件会先转码处理较大视频时建议把文件缓存到临时目录。process_video函数中需要调用之前的模型进行帧处理并返回mp4路径。如果要在本地演示不需要公网launch时不要开share。queue必须显式设置避免并发上传时内存膨胀。4.3 将模型导出为ONNX并部署成Flask服务要在产品环境或RK3588上部署YOLOv8不能依赖PyTorch。导出ONNX命令yolo export modelswim_results/pose_baseline/weights/best.pt formatonnx dynamicFalse opset12导出过程是否包含NMSUltralytics导出时不会自动带NMS所以ONNX直接输出候选框和分数。如果不想处理这些底层逻辑可以继续在服务器上用ultralytics Python API但单帧吞吐会下降。这里给一个Flask最小示例from flask import Flask, request, jsonify import onnxruntime as ort import numpy as np import cv2 sess ort.InferenceSession(best.onnx) app.route(/predict, methods[POST]) def predict(): file request.files[file] img cv2.imdecode(np.frombuffer(file.read(), np.uint8), cv2.IMREAD_COLOR) # 预处理 后处理省略 boxes, keypoints decode_output(sess, img) return jsonify({keypoints: keypoints.tolist()})注意由于不同版本ONNX输出格式有差异实际部署时要打印输出层形状再写对应的候选框解码。如果你需要在RK3588上用NPU加速则要把ONNX转成RKNN格式转换工具链是RKNN-Toolkit2。5. 直接能用的三个进阶技巧长视频切段、帧间平滑与TensorRT加速5.1 长视频先切段再识别避免内存溢出游泳视频动辄几十分钟直接喂给模型会把内存占满。常见做法是先按场景切换切段再用FFmpeg将原视频切成300帧一段ffmpeg -i swim.mp4 -filter:v selectgt(scene,0.4) -vsync vfr scene_%04d.jpg ffmpeg -i swim.mp4 -c copy -f segment -segment_time 10 -reset_timestamps 1 seg_%04d.mp4第一行用于检测场景变化第二行按时间切片。每段独立推理后合并结果这样即使某一段异常中断也不会影响整体流程。5.2 帧间平滑滑动窗口对动作概率做均值逐帧输出的泳姿标签会抖动特别是在划臂的临界帧。对每个窗口求softmax概率平均后取argmax效果立刻稳定。窗口长度一般为15帧约0.5秒。用numpy实现def smooth_labels(logits, window15): kernel np.ones(window) / window sm np.apply_along_axis(lambda m: np.convolve(m, kernel, modesame), axis0, arrlogits) return sm.argmax(axis1)注意convolve的same模式会让首尾窗口覆盖不完整可以忽略前5帧标签。统计动作次数时按局部峰值计数不要直接数标签切换否则一个划臂周期会被多算。5.3 用TensorRT打包推理低配置设备也能实时若部署到Jetson或RK3588建议把ONNX转成TensorRT enginetrtexec --onnxbest.onnx --saveEnginebest.engine --fp16在Jetson上FP16精度损失极小但推理速度能提升3-5倍。如果要对关键点性能做验证可在val集上对比PyTorch和TensorRT输出的平均欧氏距离。该距离一般应小于2像素如果出现大偏移回退到FP32。这些技巧可以直接组合进现有代码。视频上线前你可以用手持手机拍摄一段泳池画面验证系统对曝光不足和水花遮挡的鲁棒性。本文还有配套的精品资源点击获取