☰
YOLOv5+HRnet姿态估计实战:从数据准备到边缘设备部署
2026/10/10 18:43:16 网站建设 项目流程

简介:面向目标检测与姿态估计入门及进阶开发者,该资源以YOLOv5为检测主干,结合HRnet与SimDR实现人体关键点实时识别,适用于图片、视频及摄像头场景。包内为可直接运行的原工程文件,共2000个文件,以1867个Python脚本为主,辅以C/C++源码、txt参数与说明、md文档等,整体压缩包841.53MB,结构覆盖项目克隆、环境配置、SPPF模块添加、yaml文件修改、边界框与关键点绘制等完整链路。已有1749人学习浏览,适合希望跳过配置环节、直接上手实践或参考完整工程结构的同学。资源还包含照片/视频/实时演示结果及五个典型报错分析(路径问题、Upsample属性错误、gbk解码等),可帮助快速完成环境搭建与排错,节省自主摸索时间。

1. YOLOv5姿态估计为什么选“检测+关键点”双网络

在康复训练动作纠正的项目里,摄像头装在墙角,人站在两三米外,只靠目标检测能框出“人”,但哪条腿在抬、胳膊肘弯到多少度,框完全给不了答案。YOLOv5姿态估计解决的就是这个缺口:先让YOLOv5把画面里的每个人框出来,再把框内图像交给HRnet,回归鼻子、肩膀、肘、腕、髋、膝、踝等17个人体关键点,最终输出每个关键点的像素坐标。这条串联链路同时利用了YOLOv5在检测上的稳健召回和HRnet高分辨率特征图在关键点定位上的精度,并且在rk3568这类边缘设备上可以把两个模型分别量化、分别替换,工程上非常灵活。适合要把多人姿态估计跑通并调到可交付状态的工程师参考。

2. 从COCO数据到训练集:人体检测框与17个关键点的对齐

2.1 多人姿态估计的两种主流结构:自顶向下与自底向上

做多人姿态估计,最先要选的就是框架结构。自顶向下路线是“先检测人,再对每个人做关键点回归”,也就是本方案的形态。自底向上路线则是“先回归出所有关键点和肢体连接线,再聚类成个体”,OpenPose 是这条路线的代表。选择自顶向下,最直接的理由是精度好控制:每个检测框裁剪出来后,关键点只负责框里的这一条人,训练收敛稳定,推理时也能直接得到“哪个框配哪组关键点”的配对结构。

自底向上的优势是推理耗时不会随着画面人数线性上涨,在人数极多的场景下有吞吐优势,但它的聚类过程在两人交叉、遮挡时经常翻车,把人手和旁边人的肩膀拼到同一副骨架里去。我实际做过对比,工业现场的摄像头画面里通常只有2到6人,自顶向下的耗时完全可以接受,而精度和调试成本明显更友好。这个选择还带来一个工程红利:YOLOv5检测模型和HRnet关键点模型是解耦的,检测模型可以单独换更小的s版本压缩耗时,关键点模型可以单独提升输入分辨率来改善小尺度人的精度,两者互不牵连。

另一个需要接受的现实是,实时性预算必须分摊到两个模型上。同样处理一帧,检测端消耗一部分毫秒,关键点端按人数成倍消耗。所以后续章节里所有关于输入尺寸、量化、批次合并的讨论,本质上都是在争取这部分预算。

2.2 关键点标注格式:COCO的17个点顺序不能记错

训练数据大多沿用 COCO 数据集的17点定义。这个顺序是行业默认的,模型输出头的通道顺序、heatmap 的可视化顺序、评估脚本的关键点顺序全靠它对齐。顺序一旦错了,训练过程不会报错,但评估结果会非常诡异,比如左肩的 loss 降到很低,实际可视化时左边肩膀的点跑到鼻子上,这是典型的静默错误。

索引关键点索引关键点
0nose9left wrist
1left eye10right wrist
2right eye11left hip
3left ear12right hip
4right ear13left knee
5left shoulder14right knee
6right shoulder15left ankle
7left elbow16right ankle
8right elbow

COCO 的标注文件里还有一个细节容易被忽略:keypoints序列是[x1, y1, v1, x2, y2, v2, ...],每三个数字一组,最后的v表示可见性标志。v=0表示该点不在图内且未标注,v=1表示存在但被遮挡,v=2表示可见。训练时 loss 计算只针对v>0的关键点,如果把不可见点也塞进监督,模型会被迫去预测一个毫无意义的位置,关键点输出会整体偏移。

2.3 数据划分与预处理:裁框、缩放、归一化的三件事

拿到标注后,第一件事是划分训练验证集,我一般按 8:1:1 处理,并且按视频序列划分而不是按单帧随机划分。理由很直白:同一段视频的相邻帧高度相似,如果它们同时出现在训练集和验证集里,验证指标会虚高,部署时一跑真实视频就露馅。

预处理阶段有三个动作必须做对。第一是裁框,以检测框为中心向外扩展1.25倍,把肘部、手腕这些经常探出框外的点包进来,扩展比例太小会让关键点被迫依赖图像边缘的信息。第二是缩放,HRnet 常见输入是256x192,操作时保持长宽比缩放到短边192,剩余区域用边缘像素填充,而不是直接拉伸变形,因为拉伸会改变肢体的比例关系。第三是归一化,使用 ImageNet 的均值和标准差,这一步与预训练权重匹配,很多复现失败都是因为自己改了 mean 和 std 却没有同步训练预训练模型。

训练集增强也直接影响姿态估计的泛化。随机旋转在正负40度之间,缩放系数0.7到1.3,水平翻转时注意关键点标签要做左右交换,鼻子与双眼这类中轴点维持原索引。还有一个不常提但很有用的增强:随机遮挡,用黑色矩形块盖住图像中部区域,逼模型在部分肢体不可见时依靠上下文推测位置,这对后续检测框不准时的推理非常有效。

3. 在YOLOv5检测框上接HRnet:模型结构与最小推理代码

3.1 HRnet为什么比简单U-Net适合关键点:高分辨率分支的保持

很多人问为什么不直接用 U-Net 当关键点回归网络。U-Net 的结构是先下采样再上采样,降维过程把空间位置信息压进语义特征,上采样时再尝试补回来,这个过程会有信息损耗。部位遮挡时,关键点的定位只依赖低分辨率的语义特征,精度会明显下降。HRnet 的思路完全不同,它从头到尾保持一条高分辨率分支,同时并行做低分辨率分支,并且在不同分辨率之间反复融合信息,高分辨率支路始终握着精确的空间位置。

这个差异在实际效果上很直观:肩、髋这类大关节两者差距不大,但手腕、脚踝这类细小关节,HRnet 的定位误差通常更小。另外 HRnet 可以通过控制并行分支的宽度得到多个规格,工程上常用 HRnet-W32 作为速度与精度的平衡点,W48 精度更好但推理耗时大幅增加,边缘设备一般不选。

主流的落地方式有两种:第一种直接使用公开的 HRnet 预训练权重,从头训练关键点分支;第二种借助 MMPose 这类工具箱把 HRnet 导出成 ONNX,再接入自己的推理工程。两种方式我都用过,如果项目只需要做单人姿态估计或人数可控的多人估计,直接加载预训练权重更省事,因为骨干网络的特征提取能力已经足够,真正需要训练的是输出头部分的微调。

3.2 从框坐标到HRnet输入:仿射变换与heatmap解码

YOLOv5 给出的坐标是原图中的矩形框,而 HRnet 期望的输入是一张裁剪修正后的单人图。这个转换看起来简单,实际有三个坑。第一,必须按检测框的中心点做外扩,而不是直接在框的四个边界外扩像素,否则框的偏移会直接带偏关键点的相对位置。第二,缩放时保持长宽比,用仿射变换把裁剪区域变成256x192,变换矩阵里的 scale 因子需要在解码关键点坐标时逆向使用。第三,如果只做简单cv2.resize,同一个人的不同帧可能因为检测框轻微抖动,造成输入图像内容不断变化,最终关键点就会抖动。

heatmap 解码是另一个重点。模型输出的不是坐标,而是17张热力图,每张热力图上的峰值位置对应一个关键点。最简单做法是取argmax定位,但 argmax 只能给出整数值像素坐标,在视频中会产生明显的逐帧跳动。更稳的做法是在峰值附近做亚像素偏移,用热力图峰值点上下左右相邻位置的响应值计算偏移量,把坐标推到一个连续值。这一步不需要额外训练,是纯后处理,但效果非常明显,连续帧的腕点平滑度能上一个台阶。

3.3 一条YOLOv5+HRnet串联的最小推理链路(代码)

下面这段代码展示了从一帧原始图像到“检测框 + 17个关键点”的最小闭环。加载检测模型和 HRnet 模型的部分,不同框架封装差异很大,我写成函数占位,你按自己手头的权重格式替换即可。

import cv2 import numpy as np import torch class PoseEstimator: def __init__(self, det_weights, hrnet_weights): # 常见做法:torch.hub.load或YOLOv5的DetectMultiBackend,根据代码版本自选 self.detector = load_yolov5_detector(det_weights) # 常见做法:直接加载HRnet-W32预训练模型,或从ONNX导入 self.hrnet = load_hrnet(hrnet_weights) self.input_h, self.input_w = 256, 192 # HRnet输入尺寸 self.expand_ratio = 1.25 # 检测框外扩比例 def estimate(self, frame): results = self.detector(frame) # YOLOv5推理 boxes = results.pred[0][:, :4].cpu().numpy() # xyxy格式 scores = results.pred[0][:, 4].cpu().numpy() classes = results.pred[0][:, 5].cpu().numpy() all_keypoints = [] for box, score, cls in zip(boxes, scores, classes): if cls != 0 or score < 0.5: # 只处理person类 continue x1, y1, x2, y2 = box.astype(int) cx, cy = (x1 + x2) / 2, (y1 + y2) / 2 w = (x2 - x1) * self.expand_ratio h = (y2 - y1) * self.expand_ratio # 裁剪并限制在图像范围内 sx1, sy1 = int(max(0, cx - w / 2)), int(max(0, cy - h / 2)) sx2, sy2 = int(min(frame.shape[1], cx + w / 2)), int(min(frame.shape[0], cy + h / 2)) crop = frame[sy1:sy2, sx1:sx2] # 保持长宽比缩放到HRnet输入大小 crop_h, crop_w = crop.shape[:2] scale = min(self.input_h / crop_h, self.input_w / crop_w) nh, nw = int(crop_h * scale), int(crop_w * scale) resized = cv2.resize(crop, (nw, nh)) canvas = np.zeros((self.input_h, self.input_w, 3), dtype=np.float32) canvas[:nh, :nw] = resized # 归一化:ImageNet均值标准差 canvas = (canvas / 255.0 - np.array([0.485, 0.456, 0.406])) / np.array([0.229, 0.224, 0.225]) tensor = torch.from_numpy(canvas.transpose(2, 0, 1)).unsqueeze(0).float() with torch.no_grad(): heatmaps = self.hrnet(tensor)[0].cpu().numpy() # (17, H, W) kps = self._decode_heatmaps(heatmaps) # 把相对坐标映射回原图 for kp in kps: kp[0] = kp[0] / self.input_w * nw / crop_w * (sx2 - sx1) + sx1 kp[1] = kp[1] / self.input_h * nh / crop_h * (sy2 - sy1) + sy1 all_keypoints.append((box, kps)) return all_keypoints

这段代码的关键在坐标还原环节。HRnet 输出的是输入尺寸下的相对坐标,我们先把它乘回nw/nh,再乘回裁剪缩放比例,最后加上裁剪区域的偏移量sx1/sy1,才得到原图像坐标。这里最容易出错的点是:如果直接把相对坐标当作原图坐标,远距离目标的关键点会整体偏向裁剪框左上角。

3.4 heatmap解码的亚像素偏移实现

下面是_decode_heatmaps的常见实现,放在这里一并说明。

def _decode_heatmaps(self, heatmaps): decoded = [] for hm in heatmaps: # hm: (H, W) h, w = hm.shape idx = np.argmax(hm) y, x = np.unravel_index(idx, (h, w)) # 亚像素偏移:用一阶差分估算峰值偏移方向 if 0 < y < h - 1 and hm[y, x] > 1e-8: dy = (hm[y + 1, x] - hm[y - 1, x]) / (2 * hm[y, x]) y = np.clip(y + dy * 0.25, 0, h - 1) if 0 < x < w - 1 and hm[y, x] > 1e-8: dx = (hm[y, x + 1] - hm[y, x - 1]) / (2 * hm[y, x]) x = np.clip(x + dx * 0.25, 0, w - 1) decoded.append([x / w * self.input_w, y / h * self.input_h]) return decoded

dy和dx是热力图峰值位置两侧的响应差值,0.25 表示最大偏移不超过四分之一的像素间距,这个经验值在多数 HRnet 权重上表现稳定。如果你觉得关键点仍然抖动,可以把这个系数降到 0.15,代价是定位精度略微下降。这个参数属于典型的“玄学”范畴,没有理论上的最优值,必须针对自己的视频帧率做实验。

4. 训练自己的关键点数据集:yolov5超参数、损失函数与验证指标

4.1 YOLOv5只做人检测:改类别数还是用现成person权重

YOLOv5 姿态估计里的 YOLOv5 部分只承担“检测出人”这一件事,所以不要急着改检测头去直接回归关键点,那是另一种方案(类似 YOLOv8-pose 的思路),和标题中 HRnet 组合的设计并不相同。检测端的常见做法有两种:一是直接用 COCO 预训练的yolov5s.pt,推理时过滤其他类别,只保留 person 类;二是用自己场景的数据微调一个单类检测模型。

如果场景固定,比如摄像头位置不动、拍摄范围基本不变,强烈建议微调单类模型。原因很实际:通用 person 权重在标准视角下效果不错,但在俯拍、远距离、人穿深色衣服等场景会漏检,而漏检意味着整个姿态估计管道对这个人的关键点输出完全丢失。微调成本很低,数据量几百张就够,关键是这些图要覆盖你实际场景的站位、角度和光照范围。

# person.yaml train: /data/person/images/train val: /data/person/images/val nc: 1 names: ['person']

训练命令注意把--classes参数使用起来,只对 person 类别计算 loss:

python train.py --data person.yaml --weights yolov5s.pt --epochs 100 --img 640 --batch 16

微调后的检测模型比通用模型更能容忍场景内的背景干扰,也更容易在 640 输入下跑出更干净的框,这几个边界场景决定了关键点质量的基线。

4.2 调yolov5超参数:前三必调项

在 yaml 超参数文件里,lr0、anchor和mosaic是影响 person 检测收敛速度的三个关键项。初始学习率lr0建议从 0.01 起步,如果你的数据只有几百张,降到 0.001 更稳,避免前期 batch 梯度震荡太大把预训练权重洗掉。很多人直接沿用 COCO 训练时的默认 0.01,小数据场景下训练到中期 mAP 容易反复跳。

anchor 适合用遗传算法重新计算。person 框的宽高比明显比 COCO 全体目标瘦长,默认锚框里大量是适合汽车和行人的方框。在train.py加--noautoanchor可以锁定自动锚框策略,或者直接让 YOLOv5 在训练前重新聚类一次,因为腰包不匹配会直接导致小目标召回率上不去,而这个现象从 loss 曲线里根本看不出来。

mosaic增强在纯 person 场景建议降低到 0.5 以下。Mosaic 把四张图拼接在一起,人物经常被切断,检测模型被迫学习残缺目标的特征,这对提升通用检测有帮助,但会伤害姿态估计场景下“框住完整人”的要求。完整人框被切开,意味着关键点裁剪也跟着出错。

4.3 HRnet监督头与损失:MSE在heatmap上的加减法

HRnet 的训练通常使用 heatmap 回归的 MSE 损失。做法是把每个关键点坐标展开成一个以该点为中心、半径为sigma的二维高斯分布,模型的任务不是直接预测坐标,而是预测这个热图的分布形态。MSE 损失比较温和,不会因为个别点的误差产生剧烈梯度,这对遮挡部位尤其友好。

生成热图时有两个细节影响训练效果。第一,sigma的值一般取 2 到 3,小目标居多的数据集建议调到 1.5,否则热图峰值区域过大,多个相邻关键点的热图互相重叠,优化目标变模糊。第二,生成热图时使用np.maximum而不是赋值,这样重叠区域取最大响应值,不会因为后一个关键点的写入把前一个点的峰值冲掉。

def generate_heatmap(keypoints, img_size=(256, 192), sigma=2, num_kpts=17): heatmap = np.zeros((num_kpts, *img_size), dtype=np.float32) y_grid, x_grid = np.mgrid[0:img_size[0], 0:img_size[1]] for i, (x, y) in enumerate(keypoints): if x < 0 or y < 0: continue r = int(round(sigma * 3)) y0, y1 = max(0, int(y) - r), min(img_size[0], int(y) + r + 1) x0, x1 = max(0, int(x) - r), min(img_size[1], int(x) + r + 1) gauss = np.exp(-((y_grid[y0:y1, x0:x1] - y) ** 2 + (x_grid[y0:y1, x0:x1] - x) ** 2) / (2 * sigma ** 2)) heatmap[i, y0:y1, x0:x1] = np.maximum(heatmap[i, y0:y1, x0:x1], gauss) return heatmap

训练 HRnet 时,初始学习率设到 1e-3,用 Adam 优化器,batch size 在 32 到 64 之间。如果发现 loss 卡在 0.02 附近降不下去,优先检查 heatmap 生成代码里坐标和图像尺寸的对应关系,我遇到过多次“训练的输入是 256x192,但生成的 heatmap 是 192x256”,模型根本学不到东西,loss 不下来反而正常。

4.4 评估用PCK还是OKS,别拿mAP一通乱用

目标检测那套 mAP 评估在关键点上适用性有限。关键点任务常用的两个指标是 PCK 和 OKS。PCK 计算预测点落在以真实点为中心、以躯干直径为尺度的一个阈值内的百分比,直观理解是“预测点和真实点的距离是否小于人体尺寸的某个比例”。PCK@0.2 是边缘部署验收时我习惯看的指标,它比 OKS 更贴近“肉眼看不出来错位”的体验。

OKS 是 COCO 关键点评估使用的指标,它给每个关键点分配了不同的标准差,鼻子和大关节的标准差不同,更精细地反映标注质量差异。如果你要和其他公开模型对比,用 OKS;如果你只关心自己的视频跑出来效果如何,PCK 更直接。建议训练时每隔 5 个 epoch 在验证集上计算一次 PCK@0.5 和 PCK@0.2,而不是只看 loss。有几次 loss 稳定下降但 PCK 不动,说明模型在学习热图的整体分布却忽略了峰值位置的精确度,这时候需要检查 sigma 和亚像素偏移的解码方式。

5. HRnet落地避坑:关键点抖动、坐标错位的定位与修复

5.1 现象:多人场景,A人的肘接到B人的肩

两人交叉或距离较近时,裁剪框里除了目标人,往往还带着旁边人的手臂或肩膀。HRnet 只看框内图像,不会区分是不是同一个人,于是容易出现 A 人的肘部和 B 人的肩膀同时被激活,解码出来关键点像是长在另一个人身上。

原因有两层,一是检测框外扩比例不够,框边缘把另一个人的肢体切进来;二是训练裁剪阶段没有模拟这种交叉干扰。解决方法是先把外扩比例调到 1.3,同时过滤掉面积过小的检测框,这些框大多是远距离误检或遮挡碎块。训练集增强里加入“随机把另一张图的肢体区域拼贴进当前裁剪图”的干扰策略,让模型见过这种混乱输入,推理时抗干扰能力会显著提升。

5.2 现象:静态图片正常,视频里关键点疯狂抖动

单帧推理结果完全正确,但逐帧跑视频时,手腕和脚踝的坐标在相邻帧之间跳变好几个像素,看起来像是人在抽搐。这种问题在关键点落地场景中最常见,也是最难接受的一类缺陷。

主要原因是检测框本身在抖动。YOLOv5 每一帧给出的框并不完全一致,框的中心点、宽高在像素级别波动,这种波动经过裁剪、缩放、坐标还原三步放大后,关键点位置的稳定性自然被破坏。解决路径分两级:先对检测框做时间维度的平滑,再对关键点输出做后滤波。检测框平滑通常用指数移动平均,alpha 设为 0.6 到 0.8,帧率越高可以越接近 0.8。关键点后处理可以用一维中值滤波,窗口取 3 到 5 帧,这个操作会带来几十毫秒的延迟,但能极大提升视频观感。

def smooth_boxes(prev_box, cur_box, alpha=0.7): if prev_box is None: return cur_box return alpha * prev_box + (1 - alpha) * cur_box

注意平滑时要对四个坐标同时做,而不是只平滑中心点,否则框的宽高会畸变。还有一点经验:关键点后滤波只对连续帧同一 ID 的目标做,目标跟踪 ID 切换的瞬间滤波窗口必须重置,否则会出现前一个人和后一个人的关键点混叠。

5.3 现象:近处好远端差,小目标人的脖子被画到肩膀上

画面中近处的人关键点正常,远端小目标的关键点错得离谱,最典型的是脖子点偏到肩膀位置。原因是 HRnet 的输入分辨率固定为 256x192,小目标在整图里只占很小的像素区域,裁出来后对应到 ROI 内部,关键点的空间差异可能只有 2 到 3 个像素。热图在这个尺度下峰值重叠严重,亚像素解码再准也无济于事。

常见处理是提高 HRnet 输入分辨率到 384x288,或者对检测置信度低的框做针对性放大后再送 HRnet。但提高分辨率直接带来推理耗时翻倍,边缘设备上不一定承受得起。我一般建议先检查检测置信度阈值,低于 0.3 的框直接放弃,这类框即使做了关键点也大概率是噪声;高于 0.5 的框再送 HRnet。如果远距离人的业务优先级高,把输入分辨率提高到 384x288 是相对简单的方案,同时配合前面提到的检测框平滑,避免高分辨率下更多细节的抖动被放大。

5.4 现象:单帧耗时正常,整条视频帧率上不去

单人时单帧推理 15 毫秒,五人以上直接掉到 70 多毫秒,帧率完全不可用。这是自顶向下结构逃不开的特性。问题往往不是模型太慢,而是每个人单独送一次网络,batch size 为 1 的多次推理在 GPU 上完全没有发挥并行能力,同时 CPU 与 GPU 之间的拷贝开销被多次放大。

解决方法是批量化推理,把一帧里的所有裁剪图拼接成一个大 batch,一次性送进 HRnet。由于每个人的裁剪尺寸可能不同,需要在 batch 前做尺寸对齐,可以用固定分辨率并填充边缘来完成。batch size 还建议补齐到 4 或 8 的倍数,减少底层计算单元的浪费。另一个思路是设置最大跟踪人数,超过 N 人时只对检测置信度最高的 N 个框做关键点,这个做法在视频会议场景很常用。单帧耗时从 15 毫秒到 70 毫秒,其实大部分时间消耗在小 batch 的调度上,和模型本身的算力关系不大。

6. 把模型跑到rk3568与树莓派4b:量化手段与实时优化

6.1 检测与关键点分开量化,rk3568上INT8的取舍

rk3568 这类边缘盒子上部署,统一转 RKNN 已经是标准路径。我的习惯是两个模型分开做 INT8 量化,而不是打包成一个整体,因为 YOLOv5 对量化误差不敏感,HRnet 却非常容易掉点。关键点模型的 heatmap 输出是连续的置信分布,INT8 量化后峰值周围的细节被抹平,亚像素偏移计算出来的坐标直接影响 PCK。

量化时的校准数据集务必来自实际场景,不要用 COCO 验证集。校准图 100 到 200 张足够,关键是要覆盖不同人数、不同距离的分布。RKNN Toolkit 转出 INT8 后,务必单独验证 HRnet 的 PCK 变化,如果掉点超过 5%,退回混合精度方案,检测端保持 INT8,关键点端保持 FP16。这也是标题里“实时检测”任务下最常见的工程折中。

树莓派4b部署yolov5时,常见做法是先把模型转成 ONNX,再用 ONNX Runtime 或者 NCNN 做推理。树莓派 CPU 上跑 640 输入的 YOLOv5s 加 256x192 的 HRnet,通常只能到每秒 2 到 3 帧,优化空间主要在三处:检测端降到 480 输入、HRnet 降到 192x144、关键点只对画面中最接近摄像头的三个人做。上 NPU 之前,先做这些软件层面的裁剪,比直接换硬件更划算。

6.2 关键点后处理查错顺序与验证脚本

部署之前,我习惯给自己留一个“后悔药”式的工具:一段离线回放脚本,读入录好的视频,叠加检测框、关键点和置信度输出,逐帧保存。这样现场效果不对时,可以回放分析,而不是对着板子上的实时画面猜问题。查错顺序也固定,先看检测框是否稳定,再检查裁剪坐标还原是否出错,最后才怀疑模型精度。检测框不稳定会连带关键点抖动,裁剪还原出错会让关键点在画面上的位置整体偏移,这两种问题如果先去调模型参数,等于白费功夫。

验收时以 PCK@0.2 为标准,在测试视频上人工标注 100 帧,统计各关键点的命中率。如果整体命中率在 85% 以上,观感基本可接受。手腕、脚踝这类末端点命中最容易低于均值,不要只盯着平均分看,单独统计这组点位的命中率,它们的表现决定了用户会不会抱怨。

多年做这类项目下来,最深的教训是别把模型精度看得太重,先把输入到输出的链路按帧对齐。去年一个项目里,关键点模型精度指标很好看,现场视频依然抖动明显,最后排查半天发现是检测框平滑参数在低帧率下取样过少,没起到作用。后来我固定了检测框平滑与关键点滤波双通道,才彻底解决这类观感问题。希望这些经验能帮你少走一段弯路。

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

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

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

立即咨询