资讯详情

资讯详情

OpenPose核心解读:PAF如何解决多人姿态估计的关键点关联难题

姿态估计这两年其实已经进入“柴米油盐”阶段了大部分做视觉的工程师手里都有现成的人体关键点方案但回到那篇让多人实时2D姿态估计真正落地的工作几乎所有人绕不开同一个名字OpenPose。我最早接触它的时候脑子里全是问号——四肢交叉、人物重叠、人数不固定一张下班高峰期的地铁照片里网络凭什么分得清谁是谁的胳膊谁是谁的腿这篇笔记是我重读《Realtime Multi-Person 2D Pose Estimation using Part Affinity Fields》之后的完整梳理重点拆解PAFPart Affinity Fields部位亲和场到底做了什么为什么它能解决“关键点关联”这个核心难题。适合刚入门姿态估计、准备复现论文、或者已经在用OpenPose但没细读原理解的人。读这篇论文之前我建议先把心态调整一下它并没有用特别复杂的数学也没有堆太多花哨模块真正难的是理解“为什么要这么设计”。一旦理解了PAF存在的意义后边的网络结构、训练细节、工程优化全部水到渠成。1. 整体设计思路拆解1.1 为什么多人姿态估计这么难先从一个单人姿态估计说起。单人场景下模型只需要回归出一个人身上所有关键点的位置。一张图里只有一个人不存在“关键点到底属于谁”的问题所有检测出的点直接连起来就是骨架。很多早期的优秀工作比如堆叠沙漏网络都是在这个前提下做的效果也相当不错。但现实场景里几乎不会有“一个人站在纯色背景前朝你张开双臂”这种配合度。真实的摄像头视角下要么是一群人在走动要么是几个人挤在一起交谈甚至一个人被另一个人挡住半边身体。此时姿态估计就变成两件事第一把所有人身上的关键点找出来第二把属于同一个人的关键点正确连起来。第二件事的难度被很多人低估了。两个离得很近的人各自的手肘和手腕在像素上可能贴在一起一个人双手交叉抱在胸前时左肘和右腕的距离看起来比左肘和左腕的距离还近。如果只依赖关键点的空间坐标做聚类这种歧义几乎无法消除。OpenPose这篇论文要解决的核心问题就是这种“关联歧义”。1.2 Top-Down与Bottom-Up两条路线的博弈在OpenPose之前多人姿态估计大致有两种处理思路。一种是Top-Down自上而下先用目标检测模型把画面里的每个人框出来再对每个框分别做单人姿态估计。优势是每个关键点都明确归属于框内的这个人不会搞混劣势也很明显推理时间随人数增加线性上升。十个人的画面就要跑十次单人姿态估计器实时性基本无从谈起。而且一旦人挨得太近检测框互相重叠Top-Down方法会陷入“先有鸡还是先有蛋”的困境——框不好姿态一定不好姿态不好框的质量也无从提升。另一种就是Bottom-Up自下而上先从整张图里一次性检测出所有关键点再把关键点聚合成个体骨架。它的计算量基本不随人数线性增长实时性好难点在于分组。OpenPose选择了Bottom-Up路线并且用PAF把“分组”这个抽象问题转化成了可学习的向量场估计问题。对比维度Top-Down方法Bottom-Up方法OpenPose处理顺序先检测人再检测关键点先检测关键点再关联成骨架推理速度随人数线性下降基本恒定适合实时场景密集场景检测框相互干扰严重对重叠更鲁棒核心难点检测框质量关键点关联匹配1.3 为什么单单靠关键点位置不够假设现在图里有两个人的左肘和左腕四个候选点已经检测出来了。问题是哪两个点应该连成一条左臂最直觉的做法是计算左肘到两个左腕的距离选更近的那个。看起来很有道理但一旦两人站在一起左腕A离左肘B的距离可能比左腕B离左肘B更近。距离最近原则必然连错。另一些方法会试着用“肢体外观”做匹配比如比较两处关键点周围的小图像块颜色、纹理是否一致。这听着也合理万一两人穿着相同颜色的衣服整条逻辑直接失效。而且图像块只能描述局部无法表达肢体空间上的连贯性。PAF的突破口就在于它不仅仅告诉你“这个位置是什么”还告诉你“这个位置上的肢体朝哪个方向延伸”。沿着某条真实的左臂走路径上每个像素都提供一个指向手腕的方向信息如果这条路径确实连接了同一个人那么沿路径收集到的方向信息应该高度一致。这样“两个关键点是否属于同一个人”就变成了“两点之间的连线与路径上每个位置的向量方向是否匹配”。匹配问题变成了积分问题连续且可导自然可以放进网络里学习。这就好比你去快递柜取件只知道柜子里放了一堆包裹关键点位置远远不够。真正有效的办法是看包裹上的快递面单方向向量上面写着它应该被送到哪个地址。只要面单相互匹配包裹自然就归到一起。2. 核心细节解析与实操要点2.1 置信图怎么告诉网络关键点在哪OpenPose的网络输出分两条分支一条负责预测置信图Confidence Maps另一条负责预测PAF。先看置信图。对每个关键点类别鼻子、左肘、右腕等网络会输出一张二维热图每个像素的数值表示该位置存在该类型关键点的置信度。训练时Ground Truth置信图是在每个真实关键点位置生成一个高斯峰。高斯峰不是只把标注点那个单一像素设为1而是让周围区域也有一定响应这样可以容忍标注误差也给网络的回归一个平滑的优化目标。比较关键的一个细节是多人场景下如何融合不同人的高斯峰。如果两个人都被标注了左肘且位置比较接近生成真值热图时应该取两个高斯的元素级最大值而不是平均。平均会把两个峰变成一个相对模糊、峰值下降的“双峰混合体”导致训练目标不明确取最大值则保留了两个峰值信号网络需要学习预测出两个响应高峰对应两个人的左肘。这个细节我第一次读的时候直接跳过了后来在生成训练数据时发现模型在人靠近的场景下热图响应总是糊成一片回来看论文才发现自己把max操作错写成了average。置信图的分辨率通常远低于输入图像。记输入图如果被缩放成368x368那么输出的热图大概是46x46左右后续再从低分辨率热图上采样回原图做关键点坐标提取。低分辨率会牺牲一些定位精度但换来的是计算量的极大节省属于实时性上的必要妥协。2.2 PAF的地面真值到底是怎么生成的PAF的名字里带“Affinity亲和”因为它表达的是两个关键点之间的亲和关系。每一种肢体左臂、右腿、躯干等对应一组PAF通道每个通道里存的是一个二维向量场。训练真值生成方式非常巧妙。对标注好的某一个具体人物的肢体比如从脖子NEck到左髋LHip假设这个人的这两个关键点坐标分别是N和H。对于画面中任意一个像素p先计算它在N到H这条线段上的投影位置。如果投影落在线段范围内且p到线段的垂直距离小于某个阈值简单理解为肢体半宽度就认为p属于这个人的躯干区域。此时p位置上的真值PAF向量就是N指向H的单位方向向量如果p不在任何人的肢体区域内则向量为零向量。对于有多个人重叠覆盖同一个像素的情况PAF真值是所有覆盖该像素的人的向量的平均值。为什么要平均而不是像置信图那样取最大我自己理解是这样的PAF后面要用来做关键点匹配它关心的是“从某个关键点出发沿路径走方向是否一致”。如果位置被多人覆盖取平均方向能够同时保留两种可能的连接方向让匹配阶段有机会把不同的人区分开取最大值在数学上反而不容易定义“哪个最大”因为向量是二维的没有一个天然的标量排序。这个阈值决定了肢体覆盖区域有多宽。太宽PAF会侵占相邻肢体的区域两个肢体方向互相干扰太窄匹配时沿线段采样的点太少积分的统计稳定性变差。论文里的选取依实验而定我复现时习惯把肢体宽度设成身高的约0.02到0.03倍整体影响不是特别敏感但不能极端取值。2.3 网络结构为什么要多个Stage迭代OpenPose的网络骨架分成三段。前端先用VGG的前几层卷积提取图像特征得到一个共享的高层特征表示。这个前端给后续的多阶段推理打底相当于给整幅图做了一次通用理解把大量底层纹理信息转化成更高层级的语义特征。之后是核心的迭代阶段。每个阶段CNN同时吃三路输入VGG输出的共享特征、上一阶段输出的置信图、上一阶段输出的PAF。然后内部再分出两个分支一个分支继续预测置信图一个分支继续预测PAF。这样的循环阶段在论文里设置了多个数量增加会提高精度但计算量也线性增长。最初论文中训练时用了较多阶段后续工程版为了实时性有所裁剪这也是大家在源码里看到阶段数可能对不上的原因。为什么要迭代而不是一次性输出最终结果我的理解是肢体关联本质上有很强的空间上下文依赖。拿到一个局部区域的PAF你可能还确定不了左右手臂怎么配对需要把视野放大到躯干甚至整个人才敢下结论。多阶段叠加让网络通过重复处理逐步扩大有效感知范围实现了从局部细节到全局语义的“渐进式确认”。这个过程有点像人眼认人先看到一只手不放心再看肩膀和脸确认角度和尺度最后才决定手和肩膀属于谁。一次性卷积输出容易在模糊区域草率判断多阶段输出给了网络反悔、修正的机会。2.4 损失函数的几个坑每个阶段的两个分支都会和真值计算L2损失然后所有阶段的损失求和。这里的关键词是“所有阶段都算Loss”。如果不加中间监督只对最后一个阶段算损失前面阶段梯度经过多次循环反向传播之后可能衰减训练初期模型会学得非常慢。中间监督强制前面每一个阶段都输出当前最合理的置信图和PAF梯度路径变短训练便稳定了很多。Ground Truth里还会带一个Mask。很多公开数据集的标注并不是完全稠密的有些人的某些关键点可能被遮挡或未标注。这些位置如果硬算损失会给网络传递错误信号。所以未标注区域对应的热图和PAF的损失权重直接置零。我见过有同学复现时把这层mask漏了结果训练出来的模型在遮挡区域疯狂“脑补”出根本不存在的关键点原因就是没标注区域被当成了“该区域没有关键点”的负样本。损失函数本身没什么花哨的就是朴素的L2距离。但它的梯度在预测靠近0或1的时候依然保持稳定配合多阶段监督在工程上实现起来非常简单这也是OpenPose能够在各种平台被快速复现的原因之一。3. 实操过程与核心环节实现3.1 从头生成训练数据的完整步骤复现OpenPose找个现成预训练模型跑demo其实不难真正实用是把PAF的Ground Truth数据管线搭对。我建议顺序是先准备骨架定义和关键点标注再去实现置信图和PAF的生成最后接入网络训练。骨架定义是一切的基础。以常见的关键点格式为例基本的关节列表包括鼻子、双眼、双耳、双肩、双肘、双腕、双胯、双膝、双踝。需要用肢体连接关系定义所谓的“骨架树”。生成PAF真值时只需要遍历每个肢体生成置信图时只需要遍历每个关键点类型。很多代码错误都出在肢体列表和关键点编号对不上比如左腿连了右胯模型训练再久也学不出合理的腿部PAF。关键点标注通常来自公开数据集格式一般是每个目标的关节点坐标与可见性标记。读取标注后第一步要做的是统一坐标原图和网络输入分辨率之间有个缩放比例真值坐标必须同步缩放否则热图的峰位置和PAF的肢体端点全偏了。这个小坑我曾经在一个新数据集上踩过训练Loss能降到很低但预测关节点总是有恒定偏移排查了两天才发现是坐标缩放时丢了一个浮点精度。下一步生成置信图。对每个人的每个可见关键点在对应的通道上画一个二维高斯。坐标越界、高斯被裁剪的情况要单独处理否则训练数据边界区域会出现半个高斯峰影响边缘人物的检测。再生成PAF。写一个“点到线段”的函数计算像素位置到肢体线段的垂直距离和投影参数。投影在0到1之间且垂直距离小于阈值就说明这个像素属于当前肢体范围把方向向量写进对应通道。多人重叠时累加并归一化注意要除以该像素被覆盖的次数也就是计算平均向量。这一整套代码量其实不大几十行就能跑通但正确性验证很关键。我的习惯是把生成的置信图和PAF可视化叠加在原图上直接肉眼看热图的峰是否立在关节上PAF的向量箭头是否顺着肢体方向排开。如果可视化出了问题训练再好的网络也没用。3.2 网络训练的工程化配置网络训练输入分辨率通常设置为固定的宽高比比如高368。输入图像的宽度可以跟着数据集分布来定如果拍摄场景偏宽固定比例会导致大量目标被挤压变形影响关键点定位精度。数据增强方面随机旋转和随机缩放是最有效的两种。旋转角度控制在这个量级太大容易让肢体出现不自然的弯曲缩放范围根据目标尺寸分布来确定。随机水平翻转时要注意交换左右关节点标签比如把左肘和右肘互换。这个处理如果漏掉模型会对镜像场景产生严重偏见。优化器一般选择带动量的SGD学习率采用阶梯式衰减。批次大小受显存限制多阶段网络的前向和反向占用显存很高如果显存不够用可以降低输入分辨率。但输入分辨率降到256以下后小目标关键点的定位精度会明显下降所以要平衡。多阶段带来的另一个工程问题是显存中的中间变量数量很大。每个阶段都会保留置信图和PAF用于后续计算阶段越多显存开销越大。训练时用完整阶段数如果爆显存可以先在较少的阶段上跑通整个训练流程最后再加大阶段数微调能节省很多调试时间。3.3 从置信图和PAF到最终骨架匹配逻辑拆解推理阶段可以拆成四步。第一步网络输出置信图和PAF。第二步从置信图里提取每个关键点类型的候选坐标通常对热图做非极大值抑制留下局部峰值点同时记录每个候选点的置信度。第三步对所有相邻肢体类型的候选点做匹配也就是用PAF计算每两个候选点之间是否具备足够强的肢体关联。第四步把所有匹配成功的肢体按人体骨架图拼起来输出多人的完整骨架。第三步匹配的具体做法是这样的假设现在要匹配“左肘”和“左腕”这两个关键点左肘的候选点集有E个点左腕的候选点集有W个点。对每一对组合连接这两个候选点并在这条连线上均匀采样若干位置。对每个采样位置利用PAF场读取该位置对应的二维向量然后计算这个向量与“从肘到腕的方向向量”的内积。内积值越大说明PAF在该处指示的方向越倾向于这个连接。将所有采样位置的数值累加平均得到这对候选点的匹配分。如果匹配分超过某个阈值就认为该连接成立。这一步就是PAF论文最体现功力之处。把“两个点应该连一条边”的离散问题变成了连续向量场上的积分问题。我读第一遍时对这个设计最感叹因为它把空间方向信息组织成一个可供网络学习和数值计算的结构让最终的匹配既能利用全局上下文又能保持实时计算。最后一步是多人体骨架组装。因为同一类型的肢体在不同的候选组合之间存在竞争关系比如一个左肘候选可能同时和两个左腕候选都有很高的匹配分但现实中一个人的左肘只能连一个左腕。OpenPose将匹配建模为二分图最大权重匹配问题通过匈牙利算法或者简化的贪婪策略求解。需要注意的是论文在匹配过程中强调了一个先验一个关键点只能被一条肢体使用一次但可以在不同肢体类型中重复出现。比如左肩既参与左臂的构建也参与躯干的构建这是允许的。这种逐肢体类型独立匹配的策略虽然严格意义上不是全局最优但在实测中速度和结果都非常可靠这也是它会成为大规模工程方案的原因。3.4 多尺度推理与实时性取舍论文在实验里提到一个提升精度的手段多尺度测试。推理时把输入图像缩放到若干个不同的尺寸分别送入网络再把各尺度产生的置信图和PAF对齐并做融合。小尺度输入能捕捉整体轮廓大尺度输入能保留关节细节两者叠加通常能提升不少精度尤其对小尺寸人物和动态模糊场景。但多尺度测试的代价是成倍的计算量。我在实际部署时很少用多尺度因为实时场景根本等不起。如果追求精度可以在服务端批量推理时做多尺度如果是嵌入式设备或者手机端直接用单尺度更现实的做法是用轻量级前端替换VGG把推理帧率从十几帧提到三十帧以上。OpenPose的设计让前端特征提取部分和后端迭代部分解耦换一个轻量前端并不需要改动PAF阶段这给工程优化留了很大余地。影响实时性的另一大因素是输入分辨率。实测下来输入分辨率从368降到256单帧耗时能减少将近一半精度下降幅度在近距离场景里基本可接受但远距离小目标会明显吃亏。工业项目里经常要跟产品和客户商量这个度没有绝对最优值只有场景里最合适的值。3.5 我的快速复现检查清单按照我自己的习惯复现一个论文不是把代码跑起来就算结束而是设置几个检查节点层层验证。第一层可视化Ground Truth。必须能清晰地看到热图峰值立在关节标注点上PAF箭头沿着肢体方向整齐排列。第二层训练前期观察中间监督效果。第一个阶段应当能输出结构相对简单的置信图后续阶段逐渐细化如果所有阶段输出基本一样大概率是网络容量或者特征复用出了问题。第三层推理时单独检查置信图分支不接匹配逻辑人工看关键点检测是否准确。如果这一步已经有大面积漏检问题一定出在数据或者前端而不是PAF。只有置信图质量达标的条件下再去调PAF匹配阈值才有意义。第四层找一个重叠严重的测试图比如几个人站在一起伸手拿东西如果骨架连线正确率高说明PAF确实学到了关联信息。这套检查清单帮我在自定义数据集上快速定位过好几个问题也推荐给想真正吃透OpenPose的人。4. 常见问题与排查技巧实录很多读者复现OpenPose之后跟我反馈问题集中在一张表里。我把自己遇到过的现象、原因和排查方向整理成了下面的排查表供参考。现象可能原因排查方式训练Loss收敛后关键点依旧偏移Ground Truth坐标缩放精度丢失或高斯生成时坐标未取整可视化训练数据的置信图叠加原图遮挡区域出现凭空关键点训练mask未正确应用未标注区域被当负样本检查损失计算里的mask维度是否与关键点通道对应多人靠近时骨架连线交叉错乱PAF训练真值里肢体宽度阈值过大向量区域互相污染可视化PAF缩小肢体宽度阈值检测出的关键点抖动剧烈置信图阈值设置过低把噪声峰也当成了候选点或者NMS窗口过小调高阈值检查NMS窗口大小小目标人物关键点全部漏检输入分辨率太低或数据增强里缺少尺度变化扩大随机缩放范围或采用多尺度推理多人匹配时同一人体被拆成好几段二分图匹配阈值过高部分肢体的匹配分不达标降低匹配阈值观察各肢体类型的匹配分分布曲线连线方向明显反了骨架定义里肢体端点的起点和终点搞反PAF训练真值方向错掉强行可视化一对肢体检查箭头方向4.1 PAF阈值到底怎么调PAF推断阶段有两个核心阈值一个是匹配分阈值若两个候选点间的积分分数低于该值则拒绝连接另一个是关键点阈值热图峰值低于该值时不会被选为候选点。匹配分阈值的调整依赖数据分布。照片里的人动作幅度越大、肢体交缠越多阈值就越要放宽否则大量真实的肢体连接会因为局部方向不一致而匹配不上。但阈值太低会把不同人的肢体误连起来。我通常的调整思路是先用一个较宽松的阈值把所有可能的连接全部保留查看输出骨架中有多少是明显错误连接的再逐步提高阈值直到错误率下降到一个可接受范围。论文里推荐的默认值可以作为起点但工程落地时一定要针对自己的场景重新标定。4.2 为什么PAF梯度正常但匹配还是经常错如果在训练过程中确认置信图和PAF的可视化结果都很合理但推理时还是出现连线错误大概率是匹配环节的细节没对齐。一个容易忽略的点是采样点个数和采样方式。两个候选点距离较远时如果只在连线上均匀采几个点PAF向量在中间某个局部异常值可能会直接扭曲积分结果。我建议适当增加采样点数至少几十个以上让积分结果更平滑。另一个点是对齐方向计算PAF与“源点到目标点”方向的内积时必须保证PAF的通道顺序和肢体骨架定义的端点顺序一致。如果网络输出的PAF通道是“左肘到左腕”的方向而匹配代码里读成了“左腕到左肘”的方向内积就变成负值匹配必然失败。4.3 自顶向下和自底向上能结合吗这个问题很多人问过我。OpenPose的自底向上路线在速度和基建上胜出但论单人的精确关键点定位通常不如精心设计的自顶向下方法。工业中一种常见做法是两者结合用轻量目标检测去粗筛候选区域在区域内用单人姿态估计模型得到高精度结果再用OpenPose级别的PAF做全局去重和互相校正。当然这样会牺牲一部分帧率属于精度优先项目的折中方案。论文笔记的价值在这里就体现出来了。只读结论不看上下文的人容易把OpenPose当作一个万能方案实际上它最擅长的是多人、实时、中等精度这三者的平衡点。理解了这个你才能在业务里做出正确的技术选型。4.4 再谈一个数据细节重叠人物的PAF精度最后分享一个我在自定义数据集上反复踩过的坑。当画面中有两个几乎重叠的人时Ground Truth PAF在重叠区域会被平均。考虑这样一种情况两个人的手臂方向完全相反重叠区域的平均向量几乎为零向量。训练时网络会学到“这里没有肢体方向”但推理时恰恰需要在这里区分出两条相反的手臂这就达不到效果。当时我提升重叠场景精度的手段有两个。第一个是训练时大幅度增加重叠人物的样本比例让网络见到足够多“重叠但方向相反”的例子强行把它学出来。第二个是把PAF分支的网络宽度加大一点增大分支自身的容量。实测这两个调整能把重叠场景的错误连接率降低不少但始终做不到完全消除。这也是为什么后来的一些姿态估计工作会引入可学习的分组机制或者Transformer做全局推理。对初学者来说意识到PAF的局限性同样重要它能帮你判断什么时候够用什么时候需要拜托其他方案。我的实际使用体会跑通OpenPose的demo其实并没有多难但真正把PAF理解透之后我才意识到它最了不起的地方不在于网络结构而在于把“关键点关联”这样一个离散排序问题巧妙地转译成了连续向量场上的积分问题。直到现在我遇到多人姿态估计任务时第一反应仍然是先想想能不能用向量场或者类似的连续表示去建模关系这套思路对很多场景都有参考价值。如果你想真正掌握这套方法我的建议非常直接不要只在别人写好的框架里跑测试而是花一个周末把自己关在房间里从Ground Truth生成开始完整写出置信图和PAF的数据管线然后再去接触网络结构和训练代码。等你能从零生成一条正确的人体骨架再去谈部署和优化心里就有底了。后面遇到新的姿态估计论文也能很快就看懂它到底改进了什么、在哪里动了刀。
觉得有用,分享给同行:

为您的企业打造数字门面

稳重轻奢商务风格,端正雅致视觉,长效耐看不易过时。

立即咨询 →