骨架行为识别-论文复现(论文复现)

news2024/11/14 21:15:50

骨架行为识别-论文复现(论文复现)

本文所涉及所有资源均在传知代码平台可获取

序言

骨架行为识别的定义

骨架行为识别是指通过分析人体骨架的运动轨迹和姿态,来识别和理解人体的行为动作。它是计算机视觉和模式识别领域的一个重要研究方向,具有广泛的应用前景,如人机交互、智能监控、运动分析等。

谈到骨架行为识别,不得不提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链接

  • 提出了一种新的通道拓扑优化图卷积(ctr - gc)来动态学习不同的拓扑并有效地聚合不同通道中的联合特征,用于基于骨架的动作识别。

  • 提出的ctr - gc通过学习共享拓扑作为所有通道的通用先验,并使用每个通道特定于通道的相关性对其进行细化,从而对通道拓扑进行建模。

  • ctr - gc与时间建模模块相结合,我们开发了一个强大的图形卷积网络

简单总结一下,CTR-GCN的突出贡献有2点:

  • 第一,提出一种通道拓扑细化模块,该模块通过对通道维度的压缩与聚合,对每个通道运用不同的图卷积网络进行特征提取

  • 第二,ctr-gc与简化后的多尺度时间卷积模块MS-TCN模块结合MS-TCN,构成了CTR-GCN架构,该模型参数量小,同时相较于baseline提升巨大

骨干网络架构分析及代码详解

整体架构

CTR-GCN整体架构由三部分构成如下图,分别对应以下三种:

在这里插入图片描述

通道细化拓扑建模(蓝色)

通过激活函数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

特征变换(粉色)

通过对通道进行压缩进行维度变换,进行通道拓扑细化的准备阶段

# 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

通道维度增强(黄色)

将三个进行通道拓扑细化后的特征向量与对应的超参数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
模块介绍

图a蓝色部分为空间建模,空间建模模块由CTR-GC基本块构成,CTR-GC的结构如图b。图a黄色部分为简化的多尺度时间建模,相对于原本的MS-TCN架构删除了一部分卷积分支

在这里插入图片描述

CTR-GC:空间建模的基本单元,代码如下:

# 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

代码部署详解

接下来描述代码如何部署,也可以参看readme文件,这里结合图例进行说明

数据集下载

所需要的数据集链接: NTU-RGB-D,下载骨架数据集:

  • nturgbd_skeletons_s001_to_s017.zip (NTU RGB+D 60)
  • nturgbd_skeletons_s018_to_s032.zip (NTU RGB+D 120)
  • 将下载的数据解压到如下目录: ./data/nturgbd_raw

NW-UCLA

  • 所需要的数据集链接 NW-UCLA
  • 将 all_sqe 解压并移动到 ./data/NW-UCLA

将下载的数据按照以下目录结构放置

- data/
  - NW-UCLA/
    - all_sqe
      ... # raw data of NW-UCLA
  - ntu/
  - ntu120/
  - nturgbd_raw/
    - nturgb+d_skeletons/     # from `nturgbd_skeletons_s001_to_s017.zip`
      ...
    - nturgb+d_skeletons120/  # from `nturgbd_skeletons_s018_to_s032.zip`
      ...
生成骨架数据

生成 NTU RGB+D 60 or NTU RGB+D 120 数据集:
注意:每一个文件都需要改写路径,换成你数据集存放的路径,可以直接使用绝对路径

在这里插入图片描述

 cd ./data/ntu # or cd ./data/ntu120
 # Get skeleton of each performer
 python get_raw_skes_data.py
 # Remove the bad skeleton 
 python get_raw_denoised_data.py
 # Transform the skeleton to the center of the first frame
 python seq_transformation.py

(注意)正常生成所有数据的目录如下:

在这里插入图片描述

训练和测试
训练

修改配置文件(重要)

在这里插入图片描述

在这里插入图片描述

方法1:按照如下配置修修改好配置文件,即可运行以下命令

python main.py --config ''你刚刚修改配置文件的绝对路径'

方法2:直接在命令里设置参数,方法如下:

# Example: training CTRGCN on NTU RGB+D 120 cross subject with GPU 0
python main.py --config config/nturgbd120-cross-subject/default.yaml --work-dir work_dir/ntu120/csub/ctrgcn --device 0
# Example: training provided baseline on NTU RGB+D 120 cross subject
python main.py --config config/nturgbd120-cross-subject/default.yaml --model model.baseline.Model--work-dir work_dir/ntu120/csub/baseline --device 0
# Example: training CTRGCN on NTU RGB+D 120 cross subject under bone modality
python main.py --config config/nturgbd120-cross-subject/default.yaml --train_feeder_args bone=True --test_feeder_args bone=True --work-dir work_dir/ntu120/csub/ctrgcn_bone --device 0

在 NW-UCLA 数据集上训练模型, 你需要在配置文件里修改

train_feeder_args and test_feeder_args 里修改data_path为"bone" or “motion” or "bone motion"去变换模态,然后运行

python main.py --config config/ucla/default.yaml --work-dir work_dir/ucla/ctrgcn_xxx --device 0

训练你自己的模型将你的模型文件 your_model.py放入 ./model 目录下然后运行:

# Example: training your own model on NTU RGB+D 120 cross subject
python main.py --config config/nturgbd120-cross-subject/default.yaml --model model.your_model.Model --work-dir work_dir/ntu120/csub/your_model --device 0
测试

测试保存在 <work_dir>的训练模型, 运行以下命令:

python main.py --config <work_dir>/config.yaml --work-dir <work_dir> --phase test --save-score True --weights <work_dir>/xxx.pt --device 0

将不同模态的数据进行融合,请在项目目录下运行:

# Example: ensemble four modalities of CTRGCN on NTU RGB+D 120 cross subject
python ensemble.py --datasets ntu120/xsub --joint-dir work_dir/ntu120/csub/ctrgcn --bone-dir work_dir/ntu120/csub/ctrgcn_bone --joint-motion-dir work_dir/ntu120/csub/ctrgcn_motion --bone-motion-dir work_dir/ntu120/csub/ctrgcn_bone_motion

模型优化&&创新

./model/ctr-gcn.py里的class类进行修改,由于CTR-GCN的架构和2s-AGCN如出一辙。所以在层与层之间或者空间、时间建模可加入注意力模块或者即插即用的卷积模块,例如空洞卷积、大核卷积。详细的源码注释已放附件

# 定义自注意力层
class SelfAttention(nn.Module):
    def __init__(self,in_channels,out_channels):
        super(SelfAttention, self).__init__()
        # q,k,v的kernel_size只能是1
        self.conv_q = nn.Conv2d(in_channels,out_channels,1)
        self.conv_k = nn.Conv2d(in_channels,out_channels,1)
        self.conv_v = nn.Conv2d(in_channels,out_channels,1)

# 空间建模部分加入自注意力层
# 这里对应的是 figure3 图a的 Temporal modeling
    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)
        # 这里的是所有的结果concat,dim=1
        out = torch.cat(branch_outs, dim=1)
        # 这里尝试在多尺度时间卷积上加入自注意力机制效果
        out = self.selfattention(out) + out
        out += res
        return out

文章代码资源点击附件获取

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2157966.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

力扣上刷题之C语言实现-Days1

一. 简介 本文记录一下力扣的逻辑题。主要是数组方面的&#xff0c;使用 C语言实现。 二. 涉及数组的 C语言逻辑题 1. 两数之和 给定一个整数数组 nums 和一个整数目标值 target&#xff0c;请你在该数组中找出 和为目标值 target的那 两个 整数&#xff0c;并返回它们的…

C++笔试强训15、16、17

文章目录 笔试强训15一、选择题1-5题6-10题 二、编程题题目一题目二 笔试强训16一、选择题1-5题6-10题 二、编程题题目一题目二 笔试强训17一、选择题1-5题6-10题 二、编程题题目一题目二 笔试强训15 一、选择题 1-5题 共有派生下&#xff0c;派生类的成员函数只能访问基类的…

大模型训练不难,三步即可实现

前言 初步认识了大模型长什么样了&#xff0c;接下来一起来看看如何训练出一个大模型。 训练方式&#xff0c;这里主要参考OpenAI发表的关于InstructGPT的相关训练步骤&#xff0c;主流的大模型训练基本形式大多也是类似的&#xff1a; 1、预训练&#xff08;Pretraining&a…

安卓13设置动态修改设置显示版本号 版本号增加信息显示 android13增加序列号

总纲 android13 rom 开发总纲说明 文章目录 1.前言2.问题分析3.代码分析4.代码修改5.编译6.彩蛋1.前言 设置 =》关于平板电脑 =》版本号 在这里显示了系统的一些信息,但是这里面的信息并不包含序列号之类的信息,我们修改下系统设置,在这里增加上相关的序列号。 2.问题分析…

C语言 使用scanf函数时出现错误代码C4996

文章目录 错误样式解决方法方法一&#xff1a;使用安全的函数替代方法二&#xff1a;禁用警告方法三&#xff1a;检查并修改编译器设置 错误样式 C4996 ‘scanf’: This function or variable may be unsafe. Consider using scanf_s instead. To disable deprecation, use _C…

《算法岗面试宝典》正式发布

大家好&#xff0c;历时半年完善&#xff0c;《算法岗面试宝典》 终于可以跟大家见面了。 最近 ChatGPT 爆火&#xff0c;推动了技术圈对大模型算法场景落地的热情&#xff0c;就业市场招聘人数越来越多&#xff0c;算法岗一跃成为竞争难度第一的岗位。 岗位方向 从细分方向…

K8s Calico替换为Cilium,以及安装Cilium过程

一、删除Calico kubectl delete daemonset calico-node -n kube-systemkubectl delete deployment calico-kube-controllers -n kube-system kubectl delete ds kube-flannel-ds -n kube-system kubectl delete cm calico-config -n kube-system kubectl delete secret calico…

YOLOv5训练COCO2017数据集

网上没找到适合新手小白的教程,看了些教程,但还是没法解决自己遇到的问题。记录下自己的过程,希望能提供点帮助。 默认已经部署好了yolov5。 安装部署yolov5可参考以下: ubuntu20.04配置YOLOV5(非虚拟机)_ubuntu系统实现yolov5没有显卡-CSDN博客 目录 一、数据集下载…

Java基础-零拷贝

文章目录 什么是零拷贝&#xff1f;传统IO执行过程零拷贝的意义零拷贝的主要实现方式实际应用场景零拷贝的优势零拷贝的局限性 Java 中的零拷贝实现FileChannel.transferTo()FileChannel.transferFrom() 相关知识点解释什么是DMA内核空间和用户空间什么是用户态、内核态什么是上…

2012年408考研真题-数据结构

8.【2012统考真题】求整数n(n≥0)的阶乘的算法如下&#xff0c;其时间复杂度是(&#xff09;。 int fact(int n){ if(n<1) return 1; return n*fact (n-1); } A. O(log2n) B. O(n) C. O(nlog2n) D. O(n^2) 解析&#xff1a; 观察代码&#xff0c;我们不…

如何在openKylin中配置ssh服务并实现远程连接开放麒麟系统(1)

文章目录 前言1. 安装SSH服务2. 本地SSH连接测试3. openKylin安装Cpolar4. 配置 SSH公网地址5. 公网远程SSH连接6. 固定SSH公网地址7. SSH固定地址连接 前言 本文主要介绍如何在openKlyin系统中设置ssh连接&#xff0c;并结合cpolar内网穿透工具实现远程也可以ssh连接本地局域…

功能 接口测试,详解从抓包 +linux 日志 + 数据库的 bug 定位!

我在跟很多测试人员交流中发现&#xff0c;很大一部分测试工程师在进行功能和接口测试过程中&#xff0c;对于发现的bug很少去进行定位&#xff0c;只是将bug基于业务操作上如何出现的&#xff0c;进行描述&#xff1b;至于bug产生的原因&#xff0c;开发自己排查去吧。本文中&…

多语言文本 AI 纠错格式化 API 数据接口

多语言文本 AI 纠错格式化 API 数据接口 AI / 文本处理 AI 模型智能纠正 语法纠错 / 文本格式化。 1. 产品功能 支持多语言文本的语法纠错&#xff1b;自动识别并纠正拼写错误、语法错误和标点符号使用不当&#xff1b;优化文本格式&#xff0c;提高可读性&#xff1b;基于AI…

《李·斯莫林讲量子引力》:在不断运动的宇宙中探究离散的时空

可能是斯莫林的书读得并不多&#xff0c;感觉他讲故事的能力不如讲物理定律的能力。前半部分纯知识的可读性要好于后面讲述理论的创造过程的故事。如作者所说现代科学没有任何领域是单打独斗&#xff0c;而是不断探索&#xff0c;在团队中&#xff0c;前人和其他专业领域专家合…

vue使用PDF.JS踩的坑--部署到服务器上显示pdf.mjs viewer.mjs找不到资源

之前项目使用的pdf.js 是2.15.349版本&#xff0c;最近换了一个4.6.82的版本&#xff0c;在本地上浏览文件运行的好好的&#xff0c;但是发布到服务器&#xff08;IIS&#xff09;上打不开文件&#xff0c;控制台提示找不到pdf.mjs viewer.mjs。 之前使用的2.15.349pdf和viewer…

76、Python之函数式编程:柯里化都不懂,别说你会函数式编程

引言 很多时候&#xff0c;我们在定义函数处理比较复杂的业务逻辑时&#xff0c;首先是想着遵照“单一职能原则&#xff08;SRP&#xff09;”&#xff0c;尽量拆分为功能单一、足够精简的函数&#xff0c;以便保证代码的可读性和可扩展性。但是&#xff0c;有些逻辑就是没法拆…

2024年双十一有什么好物值得买呢?双十一必买好物清单

双十一买什么犒劳自己既不会浪费钱又可以增添生活的幸福感&#xff1f;以下就整理了五款更适合与秋冬独自生活相伴的好物&#xff0c;精致增加生活氛围感&#xff0c;热爱生活的同时更好的爱自己&#xff01;努力工作和生活当然也要更好的享受生活&#xff0c;给生活创造更多美…

Vue(14)——组合式API①

setup 特点&#xff1a;执行实际比beforeCreate还要早&#xff0c;并且获取不到this <script> export default{setup(){console.log(setup函数);},beforeCreate(){console.log(beforeCreate函数);} } </script> 在setup函数中提供的数据和方法&#xff0c;想要在…

数据结构和算法之树形结构(2)

文章出处&#xff1a;数据结构和算法之树形结构(2) 关注码农爱刷题&#xff0c;看更多技术文章&#xff01;&#xff01; 三、二叉查找树(接前篇) 二叉查找树&#xff0c;又称二叉搜索树或二叉排序树&#xff0c;是在普通二叉树基础上为了实现快速查找而设计出来的一种树形结…

Fyne ( go跨平台GUI )中文文档-绘图和动画(三)

本文档注意参考官网(developer.fyne.io/) 编写, 只保留基本用法 go代码展示为Go 1.16 及更高版本, ide为goland2021.2 这是一个系列文章&#xff1a; Fyne ( go跨平台GUI )中文文档-入门(一)-CSDN博客 Fyne ( go跨平台GUI )中文文档-Fyne总览(二)-CSDN博客 Fyne ( go跨平台GUI…