转载请注明出处:

https://www.cnblogs.com/darkknightzh/p/11486185.html

论文:

https://arxiv.org/abs/1603.06937

官方torch代码(没具体看):

https://github.com/princeton-vl/pose-hg-demo

第三方pytorch代码(位于models/StackedHourGlass.py):

https://github.com/Naman-ntc/Pytorch-Human-Pose-Estimation

1. 简介

该论文利用多尺度特征来识别姿态,如下图所示,每个子网络称为hourglass Network,是一个沙漏型的结构,多个这种结构堆叠起来,称作stacked hourglass。堆叠的方式,方便每个模块在整个图像上重新估计姿态和特征。如下图所示,输入图像通过全卷积网络fcn后,得到特征,而后通过多个堆叠的hourglass,得到最终的热图。

Hourglass如下图所示。其中每个方块均为下下图的残差模块。

Hourglass采用了中间监督(Intermediate Supervision)。每个hourglass均会有热图(蓝色)。训练阶段,将这些热图和真实热图计算损失MSE,并求和,得到损失;推断阶段,使用的是最后一个hourglass的热图。

2. stacked hourglass

堆叠hourglass结构如下图所示(nChannels=256,nStack=2,nModules=2,numReductions=4, nJoints=17):

代码如下:

 class StackedHourGlass(nn.Module):
"""docstring for StackedHourGlass"""
def __init__(self, nChannels, nStack, nModules, numReductions, nJoints):
super(StackedHourGlass, self).__init__()
self.nChannels = nChannels
self.nStack = nStack
self.nModules = nModules
self.numReductions = numReductions
self.nJoints = nJoints self.start = M.BnReluConv(3, 64, kernelSize = 7, stride = 2, padding = 3) # BN+ReLU+conv self.res1 = M.Residual(64, 128) # 输入和输出不等,输入通过1*1conv结果和3*(BN+ReLU+conv)求和
self.mp = nn.MaxPool2d(2, 2)
self.res2 = M.Residual(128, 128) # 输入和输出相等,为x+3*(BN+ReLU+conv)
self.res3 = M.Residual(128, self.nChannels) # 输入和输出相等,为x+3*(BN+ReLU+conv);否则输入通过1*1conv结果和3*(BN+ReLU+conv)求和。 _hourglass, _Residual, _lin1, _chantojoints, _lin2, _jointstochan = [],[],[],[],[],[] for _ in range(self.nStack): # 堆叠个数
_hourglass.append(Hourglass(self.nChannels, self.numReductions, self.nModules))
_ResidualModules = []
for _ in range(self.nModules):
_ResidualModules.append(M.Residual(self.nChannels, self.nChannels)) # 输入和输出相等,为x+3*(BN+ReLU+conv)
_ResidualModules = nn.Sequential(*_ResidualModules)
_Residual.append(_ResidualModules) # self.nModules 个 3*(BN+ReLU+conv)
_lin1.append(M.BnReluConv(self.nChannels, self.nChannels)) # BN+ReLU+conv
_chantojoints.append(nn.Conv2d(self.nChannels, self.nJoints,1)) # 1*1 conv,维度变换
_lin2.append(nn.Conv2d(self.nChannels, self.nChannels,1)) # 1*1 conv,维度不变
_jointstochan.append(nn.Conv2d(self.nJoints,self.nChannels,1)) # 1*1 conv,维度变换 self.hourglass = nn.ModuleList(_hourglass)
self.Residual = nn.ModuleList(_Residual)
self.lin1 = nn.ModuleList(_lin1)
self.chantojoints = nn.ModuleList(_chantojoints)
self.lin2 = nn.ModuleList(_lin2)
self.jointstochan = nn.ModuleList(_jointstochan) def forward(self, x):
x = self.start(x)
x = self.res1(x)
x = self.mp(x)
x = self.res2(x)
x = self.res3(x)
out = [] for i in range(self.nStack):
x1 = self.hourglass[i](x)
x1 = self.Residual[i](x1)
x1 = self.lin1[i](x1)
out.append(self.chantojoints[i](x1))
x1 = self.lin2[i](x1)
x = x + x1 + self.jointstochan[i](out[i]) # 特征求和 return (out)

3. hourglass

hourglass在numReductions>1时,递归调用自己,结构如下:

代码如下:

 class Hourglass(nn.Module):
"""docstring for Hourglass"""
def __init__(self, nChannels = 256, numReductions = 4, nModules = 2, poolKernel = (2,2), poolStride = (2,2), upSampleKernel = 2):
super(Hourglass, self).__init__()
self.numReductions = numReductions
self.nModules = nModules
self.nChannels = nChannels
self.poolKernel = poolKernel
self.poolStride = poolStride
self.upSampleKernel = upSampleKernel """For the skip connection, a residual module (or sequence of residuaql modules) """
_skip = []
for _ in range(self.nModules):
_skip.append(M.Residual(self.nChannels, self.nChannels)) # 输入和输出相等,为x+3*(BN+ReLU+conv)
self.skip = nn.Sequential(*_skip) """First pooling to go to smaller dimension then pass input through
Residual Module or sequence of Modules then and subsequent cases:
either pass through Hourglass of numReductions-1 or pass through M.Residual Module or sequence of Modules """
self.mp = nn.MaxPool2d(self.poolKernel, self.poolStride) _afterpool = []
for _ in range(self.nModules):
_afterpool.append(M.Residual(self.nChannels, self.nChannels)) # 输入和输出相等,为x+3*(BN+ReLU+conv)
self.afterpool = nn.Sequential(*_afterpool) if (numReductions > 1):
self.hg = Hourglass(self.nChannels, self.numReductions-1, self.nModules, self.poolKernel, self.poolStride) # 嵌套调用本身
else:
_num1res = []
for _ in range(self.nModules):
_num1res.append(M.Residual(self.nChannels,self.nChannels)) # 输入和输出相等,为x+3*(BN+ReLU+conv)
self.num1res = nn.Sequential(*_num1res) # doesnt seem that important ? """ Now another M.Residual Module or sequence of M.Residual Modules """
_lowres = []
for _ in range(self.nModules):
_lowres.append(M.Residual(self.nChannels,self.nChannels)) # 输入和输出相等,为x+3*(BN+ReLU+conv)
self.lowres = nn.Sequential(*_lowres) """ Upsampling Layer (Can we change this??????) As per Newell's paper upsamping recommended """
self.up = myUpsample()#nn.Upsample(scale_factor = self.upSampleKernel) # 将高和宽扩充为原来2倍,实现上采样 def forward(self, x):
out1 = x
out1 = self.skip(out1) # 输入和输出相等,为x+3*(BN+ReLU+conv)
out2 = x
out2 = self.mp(out2) # 降维
out2 = self.afterpool(out2) # 输入和输出相等,为x+3*(BN+ReLU+conv)
if self.numReductions>1:
out2 = self.hg(out2) # 嵌套调用本身
else:
out2 = self.num1res(out2) # 输入和输出相等,为x+3*(BN+ReLU+conv)
out2 = self.lowres(out2) # 输入和输出相等,为x+3*(BN+ReLU+conv)
out2 = self.up(out2) # 升维 return out2 + out1 # 求和

4. 上采样myUpsample

上采样代码如下:

 class myUpsample(nn.Module):
def __init__(self):
super(myUpsample, self).__init__()
pass
def forward(self, x): # 将高和宽扩充为原来2倍,实现上采样
return x[:, :, :, None, :, None].expand(-1, -1, -1, 2, -1, 2).reshape(x.size(0), x.size(1), x.size(2)*2, x.size(3)*2)

其中x为(N)(C)(H)(W)的矩阵,x[:, :, :, None, :, None]为(N)(C)(H)(1)(W)(1)的矩阵,expand之后变成(N)(C)(H)(2)(W)(2)的矩阵,最终reshape之后变成(N)(C)(2H) (2W)的矩阵,实现了将1个像素水平和垂直方向各扩充2倍,变成4个像素(4个像素值相同),完成了上采样。

5. 残差模块

残差模块结构如下:

代码如下:

 class Residual(nn.Module):
"""docstring for Residual""" # 输入和输出相等,为x+3*(BN+ReLU+conv);否则输入通过1*1conv结果和3*(BN+ReLU+conv)求和
def __init__(self, inChannels, outChannels):
super(Residual, self).__init__()
self.inChannels = inChannels
self.outChannels = outChannels
self.cb = ConvBlock(inChannels, outChannels) # 3 * (BN+ReLU+conv) 其中第一组降维,第二组不变,第三组升维
self.skip = SkipLayer(inChannels, outChannels) # 输入和输出通道相等,则输出=输入,否则为1*1 conv def forward(self, x):
out = 0
out = out + self.cb(x)
out = out + self.skip(x)
return out

其中skiplayer代码如下:

 class SkipLayer(nn.Module):
"""docstring for SkipLayer""" # 输入和输出通道相等,则输出=输入,否则为1*1 conv
def __init__(self, inChannels, outChannels):
super(SkipLayer, self).__init__()
self.inChannels = inChannels
self.outChannels = outChannels
if (self.inChannels == self.outChannels):
self.conv = None
else:
self.conv = nn.Conv2d(self.inChannels, self.outChannels, 1) def forward(self, x):
if self.conv is not None:
x = self.conv(x)
return x

6. conv

 class BnReluConv(nn.Module):
"""docstring for BnReluConv""" # BN+ReLU+conv
def __init__(self, inChannels, outChannels, kernelSize = 1, stride = 1, padding = 0):
super(BnReluConv, self).__init__()
self.inChannels = inChannels
self.outChannels = outChannels
self.kernelSize = kernelSize
self.stride = stride
self.padding = padding self.bn = nn.BatchNorm2d(self.inChannels)
self.conv = nn.Conv2d(self.inChannels, self.outChannels, self.kernelSize, self.stride, self.padding)
self.relu = nn.ReLU() def forward(self, x):
x = self.bn(x)
x = self.relu(x)
x = self.conv(x)
return x

7. ConvBlock

 class ConvBlock(nn.Module):
"""docstring for ConvBlock""" # 3 * (BN+ReLU+conv) 其中第一组降维,第二组不变,第三组升维
def __init__(self, inChannels, outChannels):
super(ConvBlock, self).__init__()
self.inChannels = inChannels
self.outChannels = outChannels
self.outChannelsby2 = outChannels//2 self.cbr1 = BnReluConv(self.inChannels, self.outChannelsby2, 1, 1, 0) # BN+ReLU+conv
self.cbr2 = BnReluConv(self.outChannelsby2, self.outChannelsby2, 3, 1, 1) # BN+ReLU+conv
self.cbr3 = BnReluConv(self.outChannelsby2, self.outChannels, 1, 1, 0) # BN+ReLU+conv def forward(self, x):
x = self.cbr1(x)
x = self.cbr2(x)
x = self.cbr3(x)
return x

(原)堆叠hourglass网络的更多相关文章

  1. [原] KVM虚拟机网络闪断分析

    背景 公司云平台的机器时常会发生网络闪断,通常在10s-100s之间. 异常情况 VM出现问题时,表现出来的情况是外部监控系统无法访问,猜测可能是由于系统假死,OVS链路问题等等.但是在出现网络问题的 ...

  2. [原]NYOJ-无线网络覆盖-199

    大学生程序代写 /*无线网络覆盖 时间限制:3000 ms  |  内存限制:65535 KB 难度:3 描述 我们的乐乐同学对于网络可算得上是情有独钟,他有一个计划,那就是用无线网覆盖郑州大学. 现 ...

  3. 论文笔记 Stacked Hourglass Networks for Human Pose Estimation

     Stacked Hourglass Networks for Human Pose Estimation key words:人体姿态估计 Human Pose Estimation 给定单张RGB ...

  4. Linux资源监控命令/工具(网络)

    1.手动/自动设定与启动/关闭IP参数:ifconfig,ifup,ifdown      这三个指令的用途都是在启动网络接口,不过,ifup与ifdown仅能就/etc/sysconfig/netw ...

  5. 5、Docker容器网络

    使用Linux进行IP层网络管理的指     http://linux-ip.net/html/ # yum install iproute http://linux-ip.net/html/tool ...

  6. 基于 Keras 用 LSTM 网络做时间序列预测

    目录 基于 Keras 用 LSTM 网络做时间序列预测 问题描述 长短记忆网络 LSTM 网络回归 LSTM 网络回归结合窗口法 基于时间步的 LSTM 网络回归 在批量训练之间保持 LSTM 的记 ...

  7. ACM-ICPC 2018 徐州赛区网络预赛 I. query 树状数组

    I. query 题目链接: Problem Description Given a permutation \(p\) of length \(n\), you are asked to answe ...

  8. 深度残差网络——ResNet学习笔记

    深度残差网络—ResNet总结 写于:2019.03.15—大连理工大学 论文名称:Deep Residual Learning for Image Recognition 作者:微软亚洲研究院的何凯 ...

  9. NASNet : Google Brain经典作,改造搜索空间,性能全面超越人工网络,继续领跑NAS领域 | CVPR 2018

    论文将搜索空间从整体网络转化为卷积单元(cell),再按照设定堆叠成新的网络家族NASNet.不仅降低了搜索的复杂度,从原来的28天缩小到4天,而且搜索出来的结构具有扩展性,在小模型和大模型场景下都能 ...

随机推荐

  1. web框架--tornado框架之模板引擎继承

    使用模板的继承可以重复使用相同结构的模板, 可以大大减少代码量 入门实例 一.demo目录结构 注解: master.html为模板内容,被index.html,account.html引用 二.各文 ...

  2. 快速调用Android虚拟机

    由于使用Android studio开发flutter 每次打开软件都需要很长时间,所以我自己使用vscode进行开发,没有虚拟机也开发不了,于是就有了这篇博客的作用啦 在本地桌面新建一个xxx.ba ...

  3. [RN] React Native ScrollView去掉自带的间隔

    React Native ScrollView去掉自带的间隔 使用ScrollView时,自带了一个类似marginTop的效果,将其去掉 <ScrollView automaticallyAd ...

  4. [BJOI2019]光线(DP)

    降智了…… 当你走头无路的时候就应该知道瞎搞一个DP: $p[i]$ 表示光射入第 $1$ 块玻璃时,从第 $i$ 块玻璃出去的光量. $q[i]$ 表示光射入第 $i$ 块玻璃时,从第 $i$ 块玻 ...

  5. DevC++中的[Error](不定期更新)

    ld returned 1 exit status 程序是没有问题的,可能的问题最有可能是以下2个 1.是你的程序已经在运行,关闭原来的程序就可以正常了 2.你机器上有杀毒(安全)软件阻止了你程序的生 ...

  6. springmvc单例

    默认情况下springmvc都是单例的,用@Controller注解的web页面,下次请求时,可以拿到controller成员变量的上次运行的信息. 比如:controller类里面有: Linked ...

  7. 使用 go modules 包管理工具(一)

    概述 我想实现一个开箱即用的 API 框架的轮子,这个轮子是基于 Gin 基础上开发的. 为什么是开箱即用,它会集成哪些功能? 以上功能点,都是常用的,后期可能还会增加. 废话不多说,咱们开始吧. 创 ...

  8. RabbitMQ的构架

    初识rabbitMQ RabbitMQ 是一个由 Erlang 语言开发的 AMQP 的开源实现. AMQP :Advanced Message Queue,高级消息队列协议.它是应用层协议的一个开放 ...

  9. Scratch 3.6环境搭建(万江波实战记录)

    1.Scratch官网在线环境 官方网址:scratch.mit.edu 进入后,点击“Create”建立 2. Scratch官网在线环境_简体中文(点击这个地球) 3-选择:创意 4-进入”创意“ ...

  10. 使用JavaScript几种简单的排序

    前几天在工作碰到一个json对象排序的问题,一直认为JavaScript不能进行对象的排序,其实并不是,今天就来总结下常见的几种简单排序: 第一类 纯数字: var arrOld = [4,10,9, ...