当前位置: 首页 > news >正文

第二十四周:OpenPose:使用部分亲和场的实时多人2D姿态估计

OpenPose

  • 摘要
  • Abstract
  • 文章信息
  • 引言
  • 方法
    • 同时进行检测和关联
    • 关键部位检测的置信图
    • PAF
    • 使用PAF进行多人解析
  • 关键代码
  • 实验结果
  • 创新与不足
  • 总结

摘要

本篇博客介绍了一种实时多人2D姿态估计框架——OpenPose,其核心思想是通过自底向上的全局关联策略,解决传统方法在多人场景下面临的计算效率低与关键点误匹配问题。针对多人姿态中肢体拓扑关联的复杂性,提出部分亲和场(PAF)技术,以向量场代表关键点间的空间方向关系,结合双分支卷积网络同步输出关键点置信图与PAF场,通过路径积分与二分图匹配算法实现跨个体的精准聚类;针对小尺度关键点检测精度不足,设计多阶段级联网络,逐层细化预测结果以提升遮挡区域的鲁棒性。其优势在于开源易用性、多部位(身体/手部/面部)联合检测能力及遮挡场景下的稳定表现,但存在计算资源消耗高、密集人群误匹配等局限。未来研究可围绕轻量化模型部署、时序动作连续性建模及三维姿态扩展展开。

Abstract

This blog introduces OpenPose, a real-time multi-person 2D pose estimation framework. Its core innovation lies in a bottom-up global association strategy, which addresses the inefficiency and keypoint mismatch issues of traditional methods in multi-person scenarios. To tackle the complexity of limb topological association, the framework proposes Part Affinity Fields (PAF), a technique that represents spatial directional relationships between keypoints through vector fields. By integrating a dual-branch convolutional network, OpenPose simultaneously generates keypoint confidence maps and PAF fields. These outputs are then processed via path integration and bipartite graph matching algorithms to achieve precise cross-individual clustering. Furthermore, to enhance the accuracy of small-scale keypoint detection under occlusion, the framework employs a multi-stage cascaded network architecture that iteratively refines predictions. OpenPose demonstrates strengths in open-source accessibility, multi-part joint detection (body, hands, and face), and robust performance in occluded scenarios. However, limitations include high computational resource demands and mismatches in dense crowds. Future research directions may focus on lightweight model deployment, temporal motion continuity modeling, and extensions to 3D pose estimation.


文章信息

Title:OpenPose: Realtime Multi-Person 2D Pose Estimation Using Part Affinity Fields
Author: Cao, Z (Cao, Zhe) ; Hidalgo, G (Hidalgo, Gines) ; Simon, T (Simon, Tomas) ; Wei, SE (Wei, Shih-En); Sheikh, Y (Sheikh, Yaser)
Source:https://webofscience.clarivate.cn/wos/alldb/full-record/WOS:000597206900012


引言

人体的2D姿态估计,在定位人体的关键点或者部分关键点,主要集中在寻找个体的身体部位。预测多人的姿态面临着挑战:首先:每张图像中可能包含未知数量的人,且图像中的人可能以任意尺度出现在任何位置;第二:人与人之间的交互使空间推理变得复杂,如接触、遮挡等会使体关键点部分之间的关联变得困难;第三:运行时间复杂度随图像中人数的增加而增加,给实时性带来挑战。

姿态估计分为两种方法:自顶向下和自底向上
自顶向下:先使用目标检测器检测出图像中的人,然后对检测出的每个人分别进行姿态估计,是采用“检测→分割→关键点预测”的串行流程。这种方法的效果依赖目标检测的效果,如果图中出现重叠度较高的两个人,则可能会因非极大值抑制而遗漏其中一个人,后续的姿态估计也会受影响。且这种方法计算成本与人数成正比,实时性较差。
自底向上:先检测图像中所有可能的关键点,再通过分组算法将这些关键点关联到不同的个体,是通过“关键点检测→聚类分组”的并行流程。这种方法对遮挡和多人重叠场景更鲁棒,无需依赖检测框。但这种方法并不直接使用来自其他身体部位和其他人的全局上下文信息,在此论文提出之前,最终的图像解析需要耗费大量时间去做全局推理,实时性差。
openpose这篇论文就是采用自底向上的方法,文中提出了部分亲和场(Part Affinity Fields, PAF)方法,通过向量场建模肢体方向,解决多人场景下关键点的正确关联问题。

方法

在这里插入图片描述
上图展示了整体的工作流程。整个网络以大小为 w × \times × h 的彩色图像作为输入,输出图像中每个人的关键点及连接的信息。
在这里插入图片描述

  1. 将图像输入卷积神经网络提取特征
  2. 利用第一步得到的特征图同时预测关键点热度图和PAF信息
  3. 将关键点进行二分匹配,选择有最大得分的连接方式进行连接
  4. 组合所有连接得到图像中所有人的全身姿势

预测身体关键点位置的网络输出为一组2D置信图S,人体有J个关键点,每个关键点对应一个置信图,则每组有J个置信图,即 S = ( S 1 , S 2 , S 3 . . . , S J ) S=(S_1,S_2,S_3...,S_J) S=(S1,S2,S3...,SJ) S j ∈ R w × h , j ∈ { 1 , 2... , J } S_j \in R^{w\times h},j \in {\{1,2...,J\}} SjRw×h,j{1,2...,J}
预测PAF信息的网络输出一组2D矢量场L,编码各个部位之间的关联度。每个肢体是一种连接,需要一个矢量场,2D矢量分为 x 和 y 方向,所以C个关键点的连接方式(C个肢体)有C个向量场, L = ( L 1 , L 2 . . . , L C ) L = (L_1,L_2...,L_C) L=(L1,L2...,LC) L c ∈ R w × h × 2 , c ∈ { 1 , 2... , C } L_c \in R^{w \times h \times 2},c \in {\{1,2...,C\}} LcRw×h×2,c{1,2...,C}

同时进行检测和关联

在这里插入图片描述
如上图所示,上部分分支网络(Branch 1)用来预测置信图,下部分分支网络(Branch 2)用来预测亲和场 PAF。每一个分支都是一个迭代的预测结构。这种连续重复的阶段优化了预测结果,每个阶段都会有中继监督。
图像首先通过卷积网络进行分析(如使用VGG-19的前10层来初始化并且微调),生成了一组特征图F,作为stage1的输入。在第一阶段,网络产生一组关键点置信度图 S 1 = ρ 1 ( F ) S^1=\rho ^1(F) S1=ρ1(F)和一组PAF: L 1 = ϕ 1 ( F ) L^1=\phi ^1(F) L1=ϕ1(F),其中 ρ 1 \rho^1 ρ1 ϕ 1 \phi ^1 ϕ1是stage 1的预测网络,在后续的每个阶段中,前一个阶段的两个分支预测与原始图像特征F进行拼接(concat),作为后一阶段的输入,用来产生更好的预测结果。
在这里插入图片描述
其中, ρ t \rho ^t ρt ϕ t \phi ^t ϕt是stage t的预测网络。
在这里插入图片描述
上图展示了置信度图和PAF在不同阶段的优化结果。为了指导迭代预测置信度图和PAF,在每个阶段后都使用损失函数。在预测结果与真实标签之间使用L2损失,并对损失函数进行空间加权,来解决一些数据并不是由所有人的标签的问题。stage t 的两个分支的损失函数描述如下:
在这里插入图片描述
其中, S j ∗ S^*_j Sj是真实关键点置信图, L c ∗ L^*_c Lc是真实亲和向量场。 W W W是二值掩码,当图像位置p没有标注时, W ( p ) = 0 W(p)=0 W(p)=0。这个掩码用于避免在训练时惩罚(降低)真实积极的预测。在每个stage后的中继监督通过周期性的补充梯度,解决了梯度消失的问题。总体损失为:
在这里插入图片描述

关键部位检测的置信图

为了评估训练阶段的 f s f_s fs ,用标注的2D关键点生成真实置信图 S ∗ S^* S。每个置信图是特定身体部位在任意像素位置的置信度的2D表示。每张置信图包含图像中每个人k的可见特定关键部位j的峰。
对于图像中包含多人的情况,为每个人生成置信图 S j , k ∗ S^*_{j,k} Sj,k, x j , k ∈ R 2 x_{j,k}\in R^2 xj,kR2是图中第k个人的第j个关键点的真实位置,则在位置 p ∈ R 2 p\in R^2 pR2处:
在这里插入图片描述
这其实就是一个高斯运算,其中, σ \sigma σ控制峰值的范围。
网络预测的置信图是通过max运算符汇总的各个置信度图:
在这里插入图片描述
即不管图像中有多少人,对于每种关键点都只生成一张置信图,取最大值操作只是在置信图中保留了最高置信度的位置,而不会改变其他关键点的检测结果。
在这里插入图片描述
测试时,预测置信度图,并通过执行非最大抑制来获得候选关键点。

PAF

通过预测置信图可以得到预测的候选关键点,如何将它们组合成未知数量的人的全身姿势是一个难题。
在这里插入图片描述
需要检测出相邻关键点之间联系的置信测量,即 这些关键点是否属于同一个人。
一种可能的方法是,检测肢体上每对部位之间的附加中点,并检查其在候选部位检测之间的发生率,如上图 b所示。但当图像中的人比较聚集时,这些中点很可能支持错误的关联(如上图 b中的绿色线所示)。导致这种错误的原因:1.这种方法只编码了每个肢体的位置,没编码方向。2.这种方法将肢体的支撑区域减少到一个点。
为了解决上述问题,论文提出了部位亲和场的方法,它编码了肢体支撑区域的位置和方向信息,如上图 c所示。对于位于特定肢体区域的每个像素,部分亲和场编码了一个部位到另一个部位的方向,并且每种肢体都有相应的亲和场来连接它关联的两个身体部位。
在这里插入图片描述
以上图中的肢体为例, X j 1 , k X_{j1,k} Xj1,k X j 2 , k X_{j2,k} Xj2,k是图像中人 k 的肢体 c 对应的两个关键点 j 1 j1 j1 j 2 j2 j2的位置。如果点p位于这个肢体上,则点p的PAF标签为从 j 1 j1 j1 j 2 j2 j2的单位向量,如果p不在这个肢体上,则该点处的亲和场值为零向量。公式表示如下:
在这里插入图片描述
其中 v 是单位向量,因为不能连接方式不能与大小有关(图像中的肢体的尺度可能不同),仅与方向有关。
在肢体上的点p定义为在一定距离阈值之内的点集,满足:
在这里插入图片描述
其中 σ l \sigma _l σl是肢体宽度。
肢体长度为:
在这里插入图片描述
部分亲和场是图像中所有人的肢体c的平均值:
在这里插入图片描述
其中 n c ( P ) n_c(P) nc(P)是所有k个人在位置p处的非零向量的个数(即不同人的肢体重叠处的像素平均值)
在预测时,通过沿着连接候选部位的线段计算对应部位亲和场的积分来测量候选部位之间的关联,对于两个候选关键点 d j 1 d_{j1} dj1 d j 2 d_{j2} dj2
在这里插入图片描述
其中 p ( u ) = ( 1 − u ) d j 1 + u d j 2 p(u)=(1-u)d_{j1}+ud_{j2} p(u)=(1u)dj1+udj2。实际中,通过取样u的等间距值,求和对应L值来计算积分。

使用PAF进行多人解析

上一小节提到的部分亲和场是计算两个关键点间联系的得分的,并没有给出解决多个候选部位之间如何连接以形成肢体的问题。
对于每个关键点,可以有多个候选点,这是由于图片中存在多人或者错误预测情况,如下图中 (b)。这些候选关键点定义了一个有可能为肢体的集合。用在PAF上线积分计算的结果来给每个候选肢体打分。找出最优划分的问题,这是一个k维匹配问题,即一个NP-Hard问题。
在这里插入图片描述
论文中提出贪婪松弛,可以不断产生更准确的匹配。论文中猜测这是因为由于PAF网络更大的感受野,成对关联的得分隐式的编码了全局上下文。
首先获取图中多人的人体检测的候选关键点 D j D_j Dj D j = { d j m : f o r j ∈ { 1 , 2... J } , m ∈ { 1 , 2... N j } } D_j=\{d^m_j :for j\in {\{1,2...J\}},m\in {\{1,2...N_j\}}\} Dj={djm:forj{1,2...J},m{1,2...Nj}},其中 N j N_j Nj是关键点 j j j的候选数量, d j m ∈ R 2 d^m_j \in R^2 djmR2是关键点 j j j的第 m m m个检测框位置。
需要找出正确的每对的肢体连接,图的节点是检测的身体关键点的候选点 D j 1 D_{j1} Dj1 D j 2 D_{j2} Dj2,它的边是检测点之间所有可能的成对连接方式。每一个边的权重由公式求 E EE 来确定,即关键点的亲和的总和。二分图的匹配是通过两边没有共享一个节点的方式,来选出边的子集。我们的目标是在边中找到一个有最大权重的匹配,
在这里插入图片描述
其中, E c E_c Ec是c类肢体的的匹配的总体权重, Z c Z_c Zc是 c类肢体的 Z的子集, E m n E_{mn} Emn是关键点 d j 1 m d^m_{j1} dj1m d j 2 m d^m_{j2} dj2m之间的亲和度。
当需要找到多个人完整的姿态时,确定 Z 是一个 k 维的匹配问题。
首先,选择一个最小边数来获取人体姿势的一块棵生成树的骨架,而不是使用完整的图;然后,进一步的将这个匹配问题分解成二分图匹配的集合,同时分别确定相邻树节点的匹配。这两个“松弛”被简单的分解为:
在这里插入图片描述
对于所有肢体连接的候选,组合这些共享同一个关键点的连接,来得到多人完整的姿势。

关键代码

以下是参考pytorch中openpose的搭建:

#!/usr/bin/env python
# -*- coding:utf-8 -*-
# Author: Donny You(youansheng@gmail.com)import torch
import torch.nn as nnfrom model.pose.loss.loss import BASE_LOSS_DICT
from lib.model.module_helper import ModuleHelper# 定义 OpenPose 网络类,继承自 nn.Module
class OpenPose(nn.Module):def __init__(self, configer):# 调用父类的构造函数super(OpenPose, self).__init__()# 保存配置信息self.configer = configer# 根据配置信息获取骨干网络self.backbone = ModuleHelper.get_backbone(backbone=self.configer.get('network.backbone'),pretrained=self.configer.get('network.pretrained'))# 初始化 PoseModel 模型,输入通道数为骨干网络的特征数self.pose_model = PoseModel(configer, self.backbone.get_num_features())# 获取有效的损失字典self.valid_loss_dict = configer.get('loss', 'loss_weights', configer.get('loss.loss_type'))def forward(self, data_dict):# 将输入图像数据通过骨干网络x = self.backbone(data_dict['img'])# 将骨干网络的输出通过 PoseModel 模型,得到 paf_out 和 heatmap_outpaf_out, heatmap_out = self.pose_model(x)# 构建输出字典,包含最后一层的 paf 和 heatmap 输出out_dict = dict(paf=paf_out[-1], heatmap=heatmap_out[-1])# 如果是测试阶段,直接返回输出字典if self.configer.get('phase') == 'test':return out_dict# 初始化损失字典loss_dict = dict()# 遍历 paf_out 的每个输出for i in range(len(paf_out)):# 如果对应的 paf_loss 在有效损失字典中if 'paf_loss{}'.format(i) in self.valid_loss_dict:# 计算 paf 损失,包括参数、损失类型和权重loss_dict['paf_loss{}'.format(i)] = dict(params=[paf_out[i]*data_dict['maskmap'], data_dict['vecmap']*data_dict['maskmap']],type=torch.cuda.LongTensor([BASE_LOSS_DICT['mse_loss']]),weight=torch.cuda.FloatTensor([self.valid_loss_dict['paf_loss{}'.format(i)]]))# 遍历 heatmap_out 的每个输出for i in range(len(heatmap_out)):# 如果对应的 heatmap_loss 在有效损失字典中if 'heatmap_loss{}'.format(i) in self.valid_loss_dict:# 计算 heatmap 损失,包括参数、损失类型和权重loss_dict['heatmap_loss{}'.format(i)] = dict(params=[heatmap_out[i]*data_dict['maskmap'], data_dict['heatmap']*data_dict['maskmap']],type=torch.cuda.LongTensor([BASE_LOSS_DICT['mse_loss']]),weight=torch.cuda.FloatTensor([self.valid_loss_dict['heatmap_loss{}'.format(i)]]))return out_dict, loss_dict# 定义 PoseModel 类,继承自 nn.Module
class PoseModel(nn.Module):def __init__(self, configer, in_channels):# 调用父类的构造函数super(PoseModel, self).__init__()# 保存配置信息和输入通道数self.configer = configerself.in_channels = in_channels# 获取模型字典model_dict = self._get_model_dict(self.configer, in_channels)# 从模型字典中获取各个模块self.model0 = model_dict['block_0']self.model1_1 = model_dict['block1_1']self.model2_1 = model_dict['block2_1']self.model3_1 = model_dict['block3_1']self.model4_1 = model_dict['block4_1']self.model5_1 = model_dict['block5_1']self.model6_1 = model_dict['block6_1']self.model1_2 = model_dict['block1_2']self.model2_2 = model_dict['block2_2']self.model3_2 = model_dict['block3_2']self.model4_2 = model_dict['block4_2']self.model5_2 = model_dict['block5_2']self.model6_2 = model_dict['block6_2']# 初始化卷积层的权重和偏置for m in self.modules():if isinstance(m, nn.Conv2d):m.weight.data.normal_(0, 0.01)if m.bias is not None:m.bias.data.zero_()@staticmethoddef _make_layers(layer_dict):# 初始化层列表layers = []# 遍历除最后一层外的所有层for i in range(len(layer_dict) - 1):layer = layer_dict[i]for k in layer:v = layer[k]# 如果是池化层if 'pool' in k:layers += [nn.MaxPool2d(kernel_size=v[0], stride=v[1], padding=v[2])]else:# 构建卷积层和 ReLU 激活函数conv2d = nn.Conv2d(in_channels=v[0], out_channels=v[1], kernel_size=v[2], stride=v[3], padding=v[4])layers += [conv2d, nn.ReLU(inplace=True)]# 处理最后一层layer = list(layer_dict[-1].keys())k = layer[0]v = layer_dict[-1][k]conv2d = nn.Conv2d(in_channels=v[0], out_channels=v[1], kernel_size=v[2], stride=v[3], padding=v[4])layers += [conv2d]return nn.Sequential(*layers)@staticmethoddef _get_model_dict(configer, in_channels):# 获取 paf 和 heatmap 的输出通道数paf_out = configer.get('network', 'paf_out')heatmap_out = configer.get('network', 'heatmap_out')# 初始化块字典blocks = {}# 定义 block_0block_0 = [{'conv4_3_CPM': [in_channels, 256, 3, 1, 1]}, {'conv4_4_CPM': [256, 128, 3, 1, 1]}]# 定义 block1_1 和 block1_2blocks['block1_1'] = [{'conv5_1_CPM_L1': [128, 128, 3, 1, 1]}, {'conv5_2_CPM_L1': [128, 128, 3, 1, 1]},{'conv5_3_CPM_L1': [128, 128, 3, 1, 1]}, {'conv5_4_CPM_L1': [128, 512, 1, 1, 0]},{'conv5_5_CPM_L1': [512, paf_out, 1, 1, 0]}]blocks['block1_2'] = [{'conv5_1_CPM_L2': [128, 128, 3, 1, 1]}, {'conv5_2_CPM_L2': [128, 128, 3, 1, 1]},{'conv5_3_CPM_L2': [128, 128, 3, 1, 1]}, {'conv5_4_CPM_L2': [128, 512, 1, 1, 0]},{'conv5_5_CPM_L2': [512, heatmap_out, 1, 1, 0]}]# 定义 block2 到 block6for i in range(2, 7):blocks['block%d_1' % i] = [{'Mconv1_stage%d_L1' % i: [128 + paf_out + heatmap_out, 128, 7, 1, 3]},{'Mconv2_stage%d_L1' % i: [128, 128, 7, 1, 3]},{'Mconv3_stage%d_L1' % i: [128, 128, 7, 1, 3]},{'Mconv4_stage%d_L1' % i: [128, 128, 7, 1, 3]},{'Mconv5_stage%d_L1' % i: [128, 128, 7, 1, 3]},{'Mconv6_stage%d_L1' % i: [128, 128, 1, 1, 0]},{'Mconv7_stage%d_L1' % i: [128, paf_out, 1, 1, 0]}]blocks['block%d_2' % i] = [{'Mconv1_stage%d_L2' % i: [128 + paf_out + heatmap_out, 128, 7, 1, 3]},{'Mconv2_stage%d_L2' % i: [128, 128, 7, 1, 3]},{'Mconv3_stage%d_L2' % i: [128, 128, 7, 1, 3]},{'Mconv4_stage%d_L2' % i: [128, 128, 7, 1, 3]},{'Mconv5_stage%d_L2' % i: [128, 128, 7, 1, 3]},{'Mconv6_stage%d_L2' % i: [128, 128, 1, 1, 0]},{'Mconv7_stage%d_L2' % i: [128, heatmap_out, 1, 1, 0]}]layers = []for block in block_0:for key in block:v = block[key]if 'pool' in key:layers += [nn.MaxPool2d(kernel_size=v[0], stride=v[1], padding=v[2])]else:conv2d = nn.Conv2d(in_channels=v[0], out_channels=v[1], kernel_size=v[2], stride=v[3], padding=v[4])layers += [conv2d, nn.ReLU(inplace=True)]models = {'block_0': nn.Sequential(*layers)}# 构建其他模块for k in blocks:v = blocks[k]models[k] = PoseModel._make_layers(v)return modelsdef forward(self, x):# 通过 model0 进行前向传播out1 = self.model0(x)out1_1 = self.model1_1(out1)out1_2 = self.model1_2(out1)# 拼接输出out2 = torch.cat([out1_1, out1_2, out1], 1)out2_1 = self.model2_1(out2)out2_2 = self.model2_2(out2)out3 = torch.cat([out2_1, out2_2, out1], 1)out3_1 = self.model3_1(out3)out3_2 = self.model3_2(out3)out4 = torch.cat([out3_1, out3_2, out1], 1)out4_1 = self.model4_1(out4)out4_2 = self.model4_2(out4)out5 = torch.cat([out4_1, out4_2, out1], 1)out5_1 = self.model5_1(out5)out5_2 = self.model5_2(out5)out6 = torch.cat([out5_1, out5_2, out1], 1)out6_1 = self.model6_1(out6)out6_2 = self.model6_2(out6)# 收集 paf 和 heatmap 的输出paf_out = [out1_1, out2_1, out3_1, out4_1, out5_1, out6_1]heatmap_out = [out1_2, out2_2, out3_2, out4_2, out5_2, out6_2]return paf_out, heatmap_outif __name__ == "__main__":print(OpenPose(1))

其中,OpenPose 类是整个 OpenPose 网络模型的封装,主要负责整合骨干网络、姿态模型以及损失计算,PoseModel 类是 OpenPose 网络中具体的姿态估计模型,负责构建和执行从特征图到 PAF 和热图输出的计算过程。

实验结果

在这里插入图片描述
与其他方法比较,openpose有最快的速度和相对好的mAP。
在这里插入图片描述
同时,也展现了其运行时间随着图像中人数的增加的变化很小,意味着openpose在多人姿态估计上的表现优异。

创新与不足

OpenPose采用了自底向上的方法来评估人体姿态。文中提出部分亲和场,通过向量场建模肢体方向,解决多人场景下关键点的正确关联问题。PAF为每个肢体定义了一个方向向量场,不仅指示关键点之间的空间关系,还编码了肢体方向信息。相比传统的中点匹配或几何约束方法,PAF能有效处理肢体重叠、遮挡和复杂背景,显著提升多人姿态估计的鲁棒性。传统方法(如Mask R-CNN)处理时间随人数线性增长,而OpenPose在多人场景下仍保持高效。通过多阶段(Stage)迭代逐步细化预测结果。每个阶段将前一阶段的置信图、PAF场与原始特征拼接,通过中间监督(Intermediate Supervision)补充梯度,缓解梯度消失问题,确保预测精度逐级提升。
但在面对遮挡与极端姿势时效果不佳,对严重遮挡、快速动作或非常规姿势(如舞蹈动作)的检测鲁棒性不足,易导致关键点误关联或漏检。且OpenPose对复杂手部姿态(如手指弯曲)和脚部细节(如鞋型)的检测常出现模糊或错误。

总结

OpenPose作为首个实现实时多人全身姿态估计的自底向上的方法,其工作流程围绕全局特征解析→关键点关联→拓扑合成展开:首先通过VGG-19骨干网络提取图像特征,并采用多阶段级联的双分支网络同步预测关键点置信图(Confidence Maps)和部分亲和场(PAF),其中置信图定位所有人体关节点的概率分布,PAF则以向量场形式编码相邻关键点的空间关联性;随后对置信图的峰值进行非极大值抑制提取候选关键点,并基于PAF对每对候选点沿连接路径积分计算亲和度得分,利用二分图匹配算法(如匈牙利算法)将离散关键点聚类为完整的个体姿态;最后结合预定义的肢体拓扑规则验证连接合理性,输出涵盖身体、手部及面部的多粒度姿态数据。其核心价值在于兼顾实时性与多尺度检测能力,既能实现高效推理,又能联合输出身体、手部及面部的精细化运动特征。然而,该框架仍受限于计算资源消耗较高、复杂遮挡下关键点分组易出错等缺陷,未来研究可围绕轻量化模型压缩、多模态时序信息融合以及抗遮挡关联算法展开优化。

相关文章:

第二十四周:OpenPose:使用部分亲和场的实时多人2D姿态估计

OpenPose 摘要Abstract文章信息引言方法同时进行检测和关联关键部位检测的置信图PAF使用PAF进行多人解析 关键代码实验结果创新与不足总结 摘要 本篇博客介绍了一种实时多人2D姿态估计框架——OpenPose,其核心思想是通过自底向上的全局关联策略,解决传统…...

ReACT agent和FC agent

rag系列文章目录 文章目录 rag系列文章目录前言一、简介二、示例说明三、对比总结 前言 大模型时代llm正在改变我们的生活,它可以帮助人类写作,写代码,翻译等等,但是llm的知识在训练时被冻结,无法直接使用api接入外部…...

大数据之常用Linux操作

一、 修改文件夹的所有者和所属组均为test用户 chown test:test /opt/文件夹名称二、使用rsync同步文件 rsync主要用于备份和镜像。具有速度快、避免复制相同内容和支持符号链接的优点。 rsync -av $pdir/$fname $user$host:$pdir/$fname三、配置环境变…...

计算机视觉行业洞察--影像行业系列第一期

计算机视觉行业产业链的上下游构成相对清晰,从基础技术研发到具体应用场景的多个环节相对成熟。 以下是我结合VisionChina经历和行业龙头企业对计算机视觉行业产业链上下游的拆解总结。 上下游总结 上游产业链分为软硬件两类,视觉的硬件主要指芯片、…...

自定义实现简版状态机

状态机(State Machine)是一种用于描述系统行为的数学模型,广泛应用于计算机科学、工程和自动化等领域。它通过定义系统的状态、事件和转移来模拟系统的动态行为。 基本概念 状态(State):系统在某一时刻的特…...

【Deepseek】Linux 本地部署 Deepseek

前言 本文介绍在 Linux 系统上部署 Deepseek AI。本文教程是面向所有想体验 AI 玩家的一个简易教程,因此即使是小白也可以轻松完成体验,话不多说立马着手去干。 [注]:笔者使用的系统为 Ubuntu 24.10 1. 关于 ollama Ollama 是一款开源应用…...

JavaScript系列(83)--正则表达式高级详解

JavaScript 正则表达式高级详解 🎯 正则表达式是处理文本的强大工具,掌握其高级特性可以让我们更高效地处理复杂的文本匹配和处理任务。让我们深入探讨JavaScript中正则表达式的高级应用。 正则表达式基础回顾 🌟 💡 小知识&…...

【行业解决方案篇九】【DeepSeek能源勘探:地震波数据智能解释】

第一章 先导课:给地球做CT的百年难题 各位老铁,今天咱们要聊的这个话题绝对硬核——给地球做CT还要用人工智能,这事儿到底有多刺激?想象一下你拿着医院CT报告单,但扫描对象换成深埋地下5000米的油气层,扫描仪换成总长300公里的地震波阵列,这操作难度直接飙升到地狱级。…...

密度提升30%!Intel 18A工艺正式开放代工

快科技2月23日消息,Intel官方网站悄然更新了对于18A(1.8nm级)工艺节点的描述,称已经做好了迎接客户项目的准备,将在今年上半年开始流片,有需求的客户可以随时联系。 Intel宣称,这是在北美地区率先量产的2nm以下工艺节…...

ESP32S3:参考官方提供的led_strip组件使用 SPI + DMA 方式驱动WS2812 RGB灯的实现思路 (实现各个平台移植使用该方式)

目录 引言使用SPI + DMA 方式实现思路分析1. 查看WS2812的datasheet手册2. 根据官方的led_strip组件的方式,自己手把手实现一遍3.完整的程序(实现霓虹灯效果)引言 参考官方提供的led_strip组件使用 SPI + DMA 方式驱动WS2812 RGB灯的实现思路,只有明白实现的思路,方能将其…...

java实现多图合成mp4和视频附件下载

java实现多图合成mp4和视频附件下载 在wutool中,封装了视频处理工具类,基于javacv和ffmpeg库,实现多图合成mp4、视频http附件下载等。 关于wutool wutool是一个java代码片段收集库,针对特定场景提供轻量解决方案,只…...

VulnOSv2 靶机渗透测试

春秋蝉鸣少年归~ arp发现靶机ip地址 发现开放80端口那先去访问一下 问题不大,没有什么有用的提示那就上dirb跑一下 这里给了一个版本号 通过searchsploit搜索了一下没有这个版本的poc/exp去网上搜搜看 这个也试了一下也利用不了回到页面上发现有个website可以点 然后…...

【STM32】内存管理

【STM32】内存管理 文章目录 【STM32】内存管理1、内存管理简介疑问:为啥不用标准的 C 库自带的内存管理算法?2、分块式内存管理(掌握)分配方向分配原理释放原理分块内存管理 管理内存情况 3、内存管理使用(掌握&#…...

【环境配置】maven,mysql,node.js,vue的快速配置与上手

【环境配置】maven,mysql,node.js,vue的快速配置与上手 我们在利用springbootvue来进行全栈项目开发时,需要做很多的准备工作,其中maven,mysql,node,js和vue的配置是必不可少的。 本期我们尽可能精简地介绍它们的配置以及快速上手。 1.maven 1.1.下载…...

前端实现socket 中断重连

前端代码 let ws;let reconnectAttempts 0;const maxReconnectAttempts 5;let reconnectTimer null;// 初始化连接function connect() {ws new WebSocket(ws://localhost:3001);ws.onopen () > {console.log(✅ 连接成功);reconnectAttempts 0; // 重置重连计数器docu…...

【深度学习】Transformer 的常见的位置编码有哪些

Transformer 位置编码(Positional Encoding)主要用于弥补 自注意力机制(Self-Attention) 对位置信息的忽略,常见的方案有以下几种: 1. 绝对位置编码(Absolute Positional Encoding) …...

HybridCLR+Adressable+Springboot热更

本文章会手把手教大家如何搭建HybridCLRAdressableSpringboot热更。 创作不易,动动发财的小手点个赞。 安装华佗 首先我们按照官网的快速上手指南搭建一个简易的项目: 快速上手 | HybridCLR 注意在热更的代码里添加程序集。把用到的工具放到程序集里…...

收到线上服务器出现cpu告警一般怎么排查?

当线上服务器出现CPU告警时,可以按照以下步骤进行系统性排查,逐步定位问题根源: 1. 快速确认CPU使用情况 命令工具:top # 实时查看CPU占用(按P排序进程) htop …...

买股票的最佳时机 - 2

买卖股票的最佳时机 III 题目描述&#xff1a; 提示&#xff1a; 1 < prices.length < 1050 < prices[i] < 105 分析过程&#xff1a; 写动态规划&#xff0c;我们需要考虑一下问题&#xff1a; 定义状态状态转移方程初始条件 遍历顺序 4种状态&#xff1a; …...

pytorch入门级项目--基于卷积神经网络的数字识别

文章目录 前言1.数据集的介绍2.数据集的准备3.数据集的加载4.自定义网络模型4.1卷积操作4.2池化操作4.3模型搭建 5.模型训练5.1选择损失函数和优化器5.2训练 6.模型的保存7.模型的验证结语 前言 本篇博客主要针对pytorch入门级的教程&#xff0c;实现了一个基于卷积神经网络&a…...

【Java】求绝对值

目录 引言基础方法Math.abs()适用类型与语法代码示例 特殊数值处理复数绝对值&#xff08;模&#xff09;大整数与高精度小数 底层实现与性能优化位运算技巧&#xff08;仅限int类型&#xff09;最小值溢出与 Math.absExact()解决方案1&#xff1a;手动判断解决方案2&#xff0…...

简单爬虫:东方财富网股票数据爬取(20231230)

目标网站&#xff1a;https://quote.eastmoney.com/center/gridlist.html#hs_a_board 需求&#xff1a;将东方财富网行情中心不同板块的股票数据爬取下来 目标是将各个选项卡的股票数据全部爬取并以excel文件保存在本地。 查看网页源代码发现并没有目标数据&#xff0c;因此需…...

Vue学习教程-14内置指令

文章目录 前言一、v-text指令二、v-html指令三、v-cloak指令四、v-once指令五、v-pre指令六、其他指令 前言 Vue.js 提供了许多内置指令&#xff08;Directives&#xff09;&#xff0c;这些指令用于在模板中添加特殊功能。内置指令以 v- 前缀开始。 v-text : 更新元素的 tex…...

Java——抽象类

在Java中&#xff0c;抽象类&#xff08;Abstract Class&#xff09; 是一种特殊的类&#xff0c;用于定义部分实现的类结构&#xff0c;同时允许子类提供具体的实现。抽象类通常用于定义通用的行为或属性&#xff0c;而将具体的实现细节留给子类。 1. 抽象类的定义 语法&…...

js数据类型检测

JavaScript的数据类型检测 typeof操作符 适用场景 基本数据类型快速判断&#xff1a;适用于快速判断变量是否为number、string、boolean、undefined、function等基本数据类型。比如在函数参数检查中&#xff0c;若要求传入数字参数&#xff0c;可用typeof来初步判断。函数类型…...

Maven+SSM+SpringBoot+Mybatis-Plus

SSM技术栈&#xff1a;spring6、springmvc、mybatis、springboot3、mybatis-plus、druid&#xff1b; 前端: node、npm、vue 快速掌握&#xff1a;全新SSMSpring BootMyBatis-Plus实战精讲...

【爬虫】request库

文章目录 发送请求响应对象响应数据的方式中文乱码问题响应对象的其他属性或方法 发送带参数的请求headers和查询参数 Requests——发送http请求&#xff0c;获取响应数据 首先&#xff0c;请确保&#xff1a; 已安装 RequestsRequests 是最新的 让我们从一些简单的示例开始…...

Docker内存芭蕾:优雅调整容器内存的极限艺术

title: “&#x1f4be; Docker内存芭蕾&#xff1a;优雅调整容器内存的极限艺术” author: “Cjs” date: “2025-2-23” emoji: “&#x1fa70;&#x1f4a5;&#x1f4ca;” 当你的容器变成内存吸血鬼时… &#x1f680; 完美内存编排示范 &#x1f4dc; 智能内存管家脚本…...

云手机如何进行经纬度修改

云手机如何进行经纬度修改 云手机修改经纬度的方法因不同服务商和操作方式有所差异&#xff0c;以下是综合多个来源的常用方法及注意事项&#xff1a; 通过ADB命令注入GPS数据&#xff08;适用于技术用户&#xff09; 1.连接云手机 使用ADB工具连接云手机服务器&#xff0c;…...

力扣-贪心-53 最大子数组和

思路 先把每一个值都加到当前集合中&#xff0c;记录当前的和&#xff0c;直到当前记录和小于0了&#xff0c;再重置改记录&#xff0c;再次尝试累加 代码 class Solution { public:int maxSubArray(vector<int>& nums) {int res INT32_MIN;int curSum 0;for(in…...

【c语言】函数_作业详解

前言&#xff1a; 对应鹏哥专升本c语言&#xff0c;51集 内容&#xff1a; 找出10个数值中的最大值&#xff0c; #include <stdio.h> //求10个整数中的最大值 int main() {//准备10个整数 //int arr[10] {1,2,3,4,13,6,7,8,9,-2};//用于循环10次int i 0;//也可以自…...

超详细:数据库的基本架构

MySQL基础架构 下面这个图是我给出的一个MySQL基础架构图&#xff0c;可以清楚的了解到SQL语句在MySQL的各个模块进行执行过程。 然后MySQL可以分为两个部分&#xff0c;一个是server层&#xff0c;另一个是存储引擎。 server层 Server层涵盖了MySQL的大多数核心服务功能&am…...

PCL 边界体积层次结构(Boundary Volume Hierarchy, BVH)

文章目录 一、简介二、实现代码三、实现效果参考资料一、简介 边界体积层次结构(Boundary Volume Hierarchy, BVH) 是一种高效的空间数据结构,广泛应用于计算机图形学、计算机视觉、机器人学、物理仿真等领域。它的核心思想是通过将空间递归地划分为层次化的包围体(通常是轴…...

【微服务】深入解析spring aop原理

目录 一、前言 二、AOP 概述 2.1 什么是AOP 2.2 AOP中的一些概念 2.2.1 aop通知类型 2.3 AOP实现原理 2.3.1 aop中的代理实现 2.4 静态代理与动态代理 2.4.1 静态代理实现 三、 jdk动态代理与cglib代理 3.1 jdk动态代理 3.1.1 jdk动态代理模拟实现 3.2 CGLIB 代理…...

Python爬虫系统搭建教程,从0开始搭建爬虫系统(附安装包)

文章目录 前言一、Python环境搭建1.Python安装2.选择Python开发环境3. 安装必要库 二、基础爬虫构建1. 发送请求获取网页2. 解析网页提取数据 三、使用 Scrapy 框架搭建系统1. 创建 Scrapy 项目2. 生成爬虫3. 编写爬虫代码4. 运行爬虫 四、应对反爬虫机制1. 常见反爬虫手段2. 解…...

linux -对文件描述符的操作dup、fcntl有五种

dup #include<unistd.h> int dup(int oldfd);作用&#xff1a;复制一个新的文件描述符fd 3, int fd1 dup(fd);f指向的是a.txt,fd1指向的也是a.txt从空闲的文件描述符表中找一个最小的作为新的拷贝的文件描述符返回&#xff1a;成功返回新的文件描述符&#xff0c;失败…...

人工智能(AI)的不同维度分类

人工智能(AI)的分类 对机器学习进行分类的方式多种多样&#xff0c;可以根据算法的特性、学习方式、任务类型等不同维度进行分类这些分类都不是互斥的&#xff1a; 1、按数据模态不同:图像&#xff0c;文本&#xff0c;语音&#xff0c;多态等 2、按目标函数不同:判别式模型…...

MySQL的Union和OR查询

这里写目录标题 **1. 创建表和索引****2. 编写 UNION 查询****3. 使用 EXPLAIN 分析查询****4. 分析 EXPLAIN 结果****可能的结果分析**&#xff1a; **5. 验证索引合并****总结****1. UNION 操作的分析****为什么使用临时表&#xff1f;** 2. OR 条件的分析为什么使用索引合并…...

金融业的AI革命——量化交易与智能风控(五)

‌第五章 AI驱动的智能投顾与普惠金融服务体系‌ ‌一、智能投顾技术架构的范式革新‌ ‌1. 技术演进三阶段‌ ‌阶段‌‌核心技术‌‌典型应用‌‌局限性‌规则引擎(2015-2020)决策树、专家系统银行理财风险评估自动化(覆盖率98%)无法应对黑天鹅事件机器学习(2020-2023)…...

机动车授权签字人考试题库及答案

一、单选题 11、 资质认定,是指(  )以上质量技术监督部门依据有关法律法规和标准、技术规范的规定,对检验检测机构的基本条件和技术能力是否符合法定要求实施的评价许可。资质认定包括检验检测机构计量认证。 A、县级 B、市级 C、地区 D、省级 答案&#xff1…...

Kafka系列之:记录一次源头数据库刷数据,造成数据丢失的原因

Kafka系列之:记录一次源头数据库刷数据,造成数据丢失的原因 一、背景二、查看topic日志信息三、结论四、解决方法一、背景 源头数据库在很短的时间内刷了大量的数据,部分数据在hdfs丢失了 理论上debezium数据采集不会丢失,就需要排查数据链路某个节点是否有数据丢失。 数据…...

鸿蒙学习-

鸿蒙数据传值 //* 传值 //* State /**State创建一个响应式的数据&#xff0c;但不是所有的更改都会引起刷新&#xff0c;只有被框架观察到的修改才会被刷新UI* 1. 基本数据类型如 number string boolean等值的变化修改* 2. Object类型&#xff0c;只会观察到第一层的数据变化或…...

【多模态处理篇五】【DeepSeek文档解析:PDF/Word智能处理引擎】

你知道吗?全球每天产生的PDF文档超过10亿份,但90%的上班族还在用复制粘贴的笨办法处理文档!DeepSeek文档解析引擎就像给你的电脑装上了"文档翻译官",能把PDF/Word里的文字、表格、公式甚至排版样式都变成AI能理解的"语言"。举个真实场景:法务小姐姐用…...

WPS中如何批量上下居中对齐word表格中的所有文字

大家好&#xff0c;我是小鱼。 在日常制作Word表格时&#xff0c;经常需要对表格中的内容进行排版。经常会把文字设置成左对齐、居中对齐或者是右对齐&#xff0c;这些对齐方式都比较好设置&#xff0c;有时我们制作的表格需要把文字批量上下居中对齐&#xff0c;那么怎么操作…...

【LLM】增强大模型推理能力的四种范式

note 增强大模型推理能力的四种范式&#xff1a;推理时间扩展、纯强化学习(RL)、SFTRL、蒸馏(distillation) 。其实这几种方法本质就是SFTRL&#xff1a; 低成本做事就直接推理时间扩展稍微肯付出成本就蒸馏SFT&#xff0c;顺便搞点高质量COT SFT数据高级点就先用GRPO等RL学习…...

2025年华为手机解锁BL的方法

注&#xff1a;本文是我用老机型测试的&#xff0c;新机型可能不适用 背景 华为官方已经在2018年关闭了申请BL解锁码的通道&#xff0c;所以华为手机已经无法通过官方获取解锁码。最近翻出了一部家里的老手机华为畅玩5X&#xff0c;想着能不能刷个系统玩玩&#xff0c;但是卡…...

鸿蒙状态管理概述 v2

状态管理v2 概述状态管理之v2ObservedV2 和 Trace状态管理V1版本对嵌套类对象属性变化直接观测的局限性ObservedV2 和 Trace 使用场景 Local状态管理V1版本State装饰器的局限性 Param状态管理V1版本接受外部传入的装饰器的局限性 OnceEventComputedComputed 使用场景 TypePersi…...

阿里云上的网站配置HTTPS

1. 获取SSL证书 创建证书 下载证书 下载 上传 .key .pem 文件 到 阿里云服务器 /etc/nginx/ssl nginx.conf 配置 server { listen 443 ssl; server_name yuming; ssl_certificate /etc/nginx/ssl/*.pem; ssl_certificate_key /etc/nginx/ssl/*.key;...

【部署优化篇十四】【十万字全景拆解:GitHub Actions自动化流水线设计圣经(DeepSeek工业级实践大公开)】

一、从手工作坊到智能工厂:CI/CD的革命之路 想象一下,你所在的公司每天要手工组装1000台手机,每个环节都靠老师傅肉眼检查——这就是没有CI/CD的软件开发现状。GitHub Actions的出现,就像给软件交付装上了特斯拉的超级工厂流水线。 DeepSeek的CI/CD演进史就是一部血泪史:…...

Golang | 每日一练 (3)

&#x1f4a2;欢迎来到张胤尘的技术站 &#x1f4a5;技术如江河&#xff0c;汇聚众志成。代码似星辰&#xff0c;照亮行征程。开源精神长&#xff0c;传承永不忘。携手共前行&#xff0c;未来更辉煌&#x1f4a5; 文章目录 Golang | 每日一练 (3)题目参考答案map 实现原理hmapb…...