骨架行为识别-论文复现
骨架行为识别的定义
骨架行为识别是指通过分析人体骨架的运动轨迹和姿态,来识别和理解人体的行为动作。
以下是一些关于骨架行为识别的相关内容:
- 基本概念:骨架行为识别是计算机视觉和模式识别领域的一个重要研究方向。它通常涉及到从图像或视频中提取人体的关键点信息,然后通过分析这些关键点的运动轨迹和相对位置来识别特定的行为或动作。
- 应用领域:骨架行为识别技术在多个领域都有广泛的应用,包括但不限于人机交互、智能监控、运动分析、虚拟现实、机器人技术等。例如,在智能监控系统中,可以通过骨架行为识别来分析行人的活动,以增强安全性;在人机交互领域,可以通过识别用户的手势来控制游戏或应用程序。
- 技术方法:为了实现骨架行为识别,研究人员采用了多种技术方法,包括传统的机器学习技术和基于深度学习的方法。其中,OpenPose算法是一种著名的基于深度学习的姿态估计算法,它可以从图像或视频中检测和跟踪人体的关键点和姿态信息。
- 数据集与评价指标:在骨架行为识别的研究过程中,通常会使用公开的数据集来训练和测试模型的性能。这些数据集可能包含不同的样本量,从而分为大型数据集和小型数据集。性能评估通常涉及到准确率、召回率等评价指标,以衡量不同方法的效果和适用场景。
- 发展趋势:随着技术的不断进步,骨架行为识别方法也在不断地优化和创新。研究人员正在探索如何提高识别的准确性和实时性,以及如何处理更复杂的场景和行为模式。
论文概述
2021年发表在ICCV的"Channel-wise Topology Refinement Graph Convolution for Skeleton-Based Action Recognition" paper链接:CTR-GCN
几乎成为了近两年顶刊顶会人体骨架行为识别论文的基线模型,例如HD-GCN(2023 ICCV),INFO-GCN(2022 CVPR),GAP(2023 ICCV)。
CTR-GCN相较于上一代基线模型2S-AGCN有何改进呢?2s-agcn链接
1.提出了一种新的通道拓扑优化图卷积(ctr - gc)来动态学习不同的拓扑并有效地聚合不同通道中的联合特征,用于基于骨架的动作识别。
2.提出的ctr - gc通过学习共享拓扑作为所有通道的通用先验,并使用每个通道特定于通道的相关性对其进行细化,从而对通道拓扑进行建模。
3.ctr - gc与时间建模模块相结合,我们开发了一个强大的图形卷积网络
简单总结一下,CTR-GCN的突出贡献有2点:
提出一种通道拓扑细化模块,该模块通过对通道维度的压缩与聚合,对每个通道运用不同的图卷积网络进行特征提取。
ctr-gc与简化后的多尺度时间卷积模块MS-TCN模块结合MS-TCN,构成了CTR-GCN架构,该模型参数量小,同时相较于baseline提升巨大。
骨干网络架构分析
CTR-GCN整体架构由通道细化拓扑建模(蓝色)、特征变换(粉色)、通道维度增强(黄色): 三部分构成如下图,分别对应以下三种:
1. 通道细化拓扑建模(蓝色):
通过激活函数M,这里为tanh激活函数,对原始特征进行拓扑细化,得到三个通道特征不同的特征x1,x2,x3。
# start
self.conv1 = nn.Conv2d(self.in_channels, self.rel_channels, kernel_size=1)
self.conv2 = nn.Conv2d(self.in_channels, self.rel_channels, kernel_size=1)
self.conv3 = nn.Conv2d(self.in_channels, self.out_channels, kernel_size=1)
self.conv4 = nn.Conv2d(self.rel_channels, self.out_channels, kernel_size=1)
x1, x2, x3 = self.conv1(x).mean(-2), self.conv2(x).mean(-2), self.conv3(x)
x1 = self.tanh(x1.unsqueeze(-1) - x2.unsqueeze(-2))
x1 = self.conv4(x1) * alpha + (A.unsqueeze(0).unsqueeze(0) if A is not None else 0) # N,C,V,V
x1 = torch.einsum('ncuv,nctv->nctu', x1, x3)
return x1
2. 特征变换(粉色):
通过对通道进行压缩进行维度变换,进行通道拓扑细化的准备阶段
# start
self.in_channels = in_channels
self.out_channels = out_channels
if in_channels == 3 or in_channels == 9:
self.rel_channels = 8
self.mid_channels = 16
else:
self.rel_channels = in_channels // rel_reduction
self.mid_channels = in_channels // mid_reduction
3. 通道维度增强(橘黄色):
将三个进行通道拓扑细化后的特征向量与对应的超参数a卷积,得到输出y
# start
def forward(self, x):
y = None
if self.adaptive:
A = self.PA
else:
A = self.A.cuda(x.get_device())
# 这里的num_subset为3,3根据图3a中代表CTR-GC的个数
for i in range(self.num_subset):
z = self.convs[i](x, A[i], self.alpha)
y = z + y if y is not None else z
y = self.bn(y)
y += self.down(x)
y = self.relu(y)
return y
建模部分代码
CTR-GC:空间建模的基本单元,代码如下:图a蓝色部分为空间建模,空间建模模块由CTR-GC基本块构成,CTR-GC的结构如图b。图a黄色部分为简化的多尺度时间建模,相对于原本的MS-TCN架构删除了一部分卷积分支。
# start
# rel_reduction和mid_reduction分别表示基于相对位置关系和中间特征的注意力子模块中间使用的通道数缩减比例,用于控制模型的参数量。
class CTRGC(nn.Module):
def __init__(self, in_channels, out_channels, rel_reduction=8, mid_reduction=1):
super(CTRGC, self).__init__()
self.in_channels = in_channels
self.out_channels = out_channels
if in_channels == 3 or in_channels == 9:
self.rel_channels = 8
self.mid_channels = 16
else:
self.rel_channels = in_channels // rel_reduction
self.mid_channels = in_channels // mid_reduction
self.conv1 = nn.Conv2d(self.in_channels, self.rel_channels, kernel_size=1)
self.conv2 = nn.Conv2d(self.in_channels, self.rel_channels, kernel_size=1)
self.conv3 = nn.Conv2d(self.in_channels, self.out_channels, kernel_size=1)
self.conv4 = nn.Conv2d(self.rel_channels, self.out_channels, kernel_size=1)
self.tanh = nn.Tanh()
for m in self.modules():
if isinstance(m, nn.Conv2d):
conv_init(m)
elif isinstance(m, nn.BatchNorm2d):
bn_init(m, 1)
def forward(self, x, A=None, alpha=1):
# x.mean(-2)表示对张量x沿着倒数第二个维度进行求平均值的操作。
x1, x2, x3 = self.conv1(x).mean(-2), self.conv2(x).mean(-2), self.conv3(x)
x1 = self.tanh(x1.unsqueeze(-1) - x2.unsqueeze(-2))
x1 = self.conv4(x1) * alpha + (A.unsqueeze(0).unsqueeze(0) if A is not None else 0) # N,C,V,V
x1 = torch.einsum('ncuv,nctv->nctu', x1, x3)
return x1
spatial modeling:空间建模,由CTR-GC和残差连接组成,残差连接的目的是为了保存部分原始特征。
# start
class unit_gcn(nn.Module):
def __init__(self, in_channels, out_channels, A, coff_embedding=4, adaptive=True, residual=True):
super(unit_gcn, self).__init__()
inter_channels = out_channels // coff_embedding
self.inter_c = inter_channels
self.out_c = out_channels
self.in_c = in_channels
self.adaptive = adaptive
self.num_subset = A.shape[0]
self.convs = nn.ModuleList()
for i in range(self.num_subset):
self.convs.append(CTRGC(in_channels, out_channels))
if residual:
if in_channels != out_channels:
self.down = nn.Sequential(
nn.Conv2d(in_channels, out_channels, 1),
nn.BatchNorm2d(out_channels)
)
else:
self.down = lambda x: x
else:
self.down = lambda x: 0
if self.adaptive:
self.PA = nn.Parameter(torch.from_numpy(A.astype(np.float32)))
else:
self.A = Variable(torch.from_numpy(A.astype(np.float32)), requires_grad=False)
self.alpha = nn.Parameter(torch.zeros(1))
self.bn = nn.BatchNorm2d(out_channels)
self.soft = nn.Softmax(-2)
self.relu = nn.ReLU(inplace=True)
for m in self.modules():
if isinstance(m, nn.Conv2d):
conv_init(m)
elif isinstance(m, nn.BatchNorm2d):
bn_init(m, 1)
bn_init(self.bn, 1e-6)
def forward(self, x):
y = None
if self.adaptive:
A = self.PA
else:
A = self.A.cuda(x.get_device())
# 这里的num_subset为3,3根据图3a中代表CTR-GC的个数
for i in range(self.num_subset):
z = self.convs[i](x, A[i], self.alpha)
y = z + y if y is not None else z
y = self.bn(y)
y += self.down(x)
y = self.relu(y)
return y
temporal modeling:时间建模,是简化版的MS-TCN架构
# start
class MultiScale_TemporalConv(nn.Module):
def __init__(self,
in_channels,
out_channels,
kernel_size=3,
stride=1,
dilations=[1,2,3,4],
residual=True,
residual_kernel_size=1):
super().__init__()
# 检查每一个分支膨胀率+2 是否能整除
assert out_channels % (len(dilations) + 2) == 0, '# out channels should be multiples of # branches'
# Multiple branches of temporal convolution
# 分支的数量=膨胀率+2
self.num_branches = len(dilations) + 2
# 分支的通道数 = 输出通道 / 分支数
# 这个计算的目的是确保每个分支的输出通道数相等,从而使得多分支结构中各个分支的特征映射可以合并在一起。
branch_channels = out_channels // self.num_branches
#
if type(kernel_size) == list:
assert len(kernel_size) == len(dilations)
else:
kernel_size = [kernel_size]*len(dilations)
# Temporal Convolution branches
self.branches = nn.ModuleList([
nn.Sequential(
nn.Conv2d(
in_channels,
branch_channels,
kernel_size=1,
padding=0),
nn.BatchNorm2d(branch_channels),
nn.ReLU(inplace=True),
TemporalConv(
branch_channels,
branch_channels,
kernel_size=ks,
stride=stride,
dilation=dilation),
)
for ks, dilation in zip(kernel_size, dilations)
])
# Additional Max & 1x1 branch
self.branches.append(nn.Sequential(
nn.Conv2d(in_channels, branch_channels, kernel_size=1, padding=0),
nn.BatchNorm2d(branch_channels),
nn.ReLU(inplace=True),
nn.MaxPool2d(kernel_size=(3,1), stride=(stride,1), padding=(1,0)),
nn.BatchNorm2d(branch_channels) # 为什么还要加bn
))
self.branches.append(nn.Sequential(
nn.Conv2d(in_channels, branch_channels, kernel_size=1, padding=0, stride=(stride,1)),
nn.BatchNorm2d(branch_channels)
))
# Residual connection
if not residual:
self.residual = lambda x: 0
elif (in_channels == out_channels) and (stride == 1):
self.residual = lambda x: x
else:
self.residual = TemporalConv(in_channels, out_channels, kernel_size=residual_kernel_size, stride=stride)
# initialize
self.apply(weights_init)
def forward(self, x):
# Input dim: (N,C,T,V)
res = self.residual(x)
branch_outs = []
for tempconv in self.branches:
out = tempconv(x)
branch_outs.append(out)
out = torch.cat(branch_outs, dim=1)
out += res
return out
本文涉及的源码可从骨架行为识别-论文复现该文章下方附件获取
代码部署
数据集下载
生成骨架数据
训练和测试
代码部署详细步骤请参考骨架行为识别第三部分
小结
骨架行为识别技术是一种重要的人工智能技术,它在多个领域具有广泛的应用场景,并且展现出巨大的意义和作用。
- 视频理解:骨架行为识别在视频理解中扮演着关键角色,它能够帮助计算机系统理解和解释人类行为,从而应用于各种视频分析任务,如监控、娱乐和教育等。
- 人机交互:通过识别用户的骨架动作,可以实现更自然的人机交互方式,例如在游戏、虚拟现实和智能家居控制中的应用,提升用户体验。
- 运动分析:在体育训练、健身指导和医疗康复等领域,骨架行为识别可以用于分析运动技巧,帮助用户改进动作或进行康复训练。
- 安全监控:在公共安全领域,骨架行为识别可以帮助监控人员及时发现异常行为,如打斗、跌倒等,从而快速做出响应。
- 研究发展:骨架行为识别技术的发展推动了相关领域的研究,如计算机视觉、深度学习和模式识别等,促进了新技术和新方法的产生。
- 数据驱动:随着大量数据集的收集和标注,如NTU-RGB+D,研究者能够开发出更为精确和鲁棒的行为识别模型,这些模型能够自动学习数据中的时空模式,提高识别的准确性和泛化能力。
- 技术创新:为了克服传统方法的局限性,研究人员提出了许多创新的网络架构,如时空图卷积网络(ST-GCN),这些技术不仅提高了模型的表达能力,还增强了其对新数据的适应能力。
- 多学科融合:骨架行为识别的研究和应用涉及到多个学科的知识融合,包括计算机科学、认知科学、机械工程等,这种跨学科的研究有助于推动整个科技领域的发展。
骨架行为识别技术在多个领域具有广泛的应用场景和重要的意义和作用。随着技术的不断发展和完善,相信它将为我们的生活带来更多的便利和可能性。