centernet代码解读-mutipose

本文详细解读了基于CenterNet的人体姿态估计代码,包括数据预处理、模型构建和训练过程。数据预处理涉及读取COCO数据集,使用热力图表示关键点并进行数据增强。模型部分介绍了ResNet101等网络结构的初始化。训练阶段,采用Focal Loss计算heatmap损失,多任务损失函数结合关键点和中心点误差进行优化。
摘要由CSDN通过智能技术生成

  前言:

        centernet可实现目标检测,人体姿态估计,3D检测,本文针对人体姿态估计代码进行解读。文章核心思想,使用热力图表示物体关键点及中心点,及使用中心点到关键点的矢量对检测结果进行双重保障。

1、数据集

        数据集使用coco2017中的person_keypoints_{}2017.json,目标检测使用的是image_info_test-dev2017.json,同一个coco数据集有不同类型的标注文件,根据标注文件中的不同图片id可以在同一个图片文件夹中找到图片。初始化dataloader时会调用coco_hp.py中的init()函数,用于后面制作真值格式所要用的一些值进行一些初始化;在for iter_id, batch in enumerate(data_loader):中调用multi_pose.py的getitem()函数,按照需要的字段及格式从annotation中取出真值

coco_hp.py

        在init函数中定义一些 a、在datasets/sample/multi_pose.py中会用到的变量及值;b、读取标注文件的路径及名称 

multi_pose.py  制作真值

        a、根据图片及标注文件路径进行读取 

        b、在getitem中将标注文件中的真值处理成我们需要的数据存储方式,

        c、做了一些数据增强操作,如翻转仿射变换 及色彩变化(光照、对比度、饱和度)其中色彩变化用到的参数self._data_rng, inp, self._eig_val, self._eig_vec均在coco_hp.py中定义

        d、将图片除255使得图片值变为0到1之间的数值 e、归一化,减均值,除方差

    ret = {'input': inp, 'hm': hm, 'reg_mask': reg_mask, 'ind': ind, 'wh': wh
           'hps': kps, 'hps_mask': kps_mask}
 #图片,热力图,int_float回归mask,index,物体宽高,关键点热力图,关键点

inp:图片通过cv2.imread()读取图片,并对图片进行除255,减均值除方差操作

hm : 建立一个和输入图片同样宽高的数组,里面的数值是物体中心点的热力图。热力图是怎么来的呢?1、通过物体宽高算的高斯半径,根据高斯半径,物体中心点,hm及函数draw_gaussian画出属于这个物体的热力图。

reg_mask:#第k个物体的偏差mask=1,初始值为0 reg为物体中心点int到float的偏差

ind:中心点所在的宽高拉成1维后的索引

wh:物体宽高

kps:中心点到关键点间的距离

kps_mask:初始值为0,在有关键点的地方值为1,作用kps*kps_mask与output[kps]*kps_mask做差值,如果检测结果在应该有关键点的地方没有关键点,那output[kps]*kps_mask会很小,则与kps*kps_mask差值会很大

dense_kps:密集的kps。【17,2,128,128】每一个关键点生成一个[2,128,128]的矩阵。如果当前物体中心点高斯半径大于heatmap当前物体中心点高斯半径范围内的值(heatmap当前物体中心点是在这一步之后画的,所以如果两个物体相距较远,一般是大于,如果两个中心点,相距较近,可能有小于的情况)

大于的部分:

矩阵中物体中心点高斯半径范围内存放 pt-ct_int的x,y减delta(5,-4,-3,-2,-1,0,1,2,3,4,5……17)

小于的部分:

使用原来dense_kps这一中心点附近的值

dense_hps_mask:中心点的高斯热力图

如果有dense_kps,则删除原有hps及hps_mask.del ret['hps'], ret['hps_mask']

reg:中心点float到中心点int的误差

hm_hp:关键点绘画的高斯热力图

hp_offset:关键点float到关键点int的误差

hp_ind:关键点所在的宽高拉成1维后的索引

hp_mask:有关键点的地方值为1

2、模型

        根据opt.py配置文件中配置的--arch,决定本任务使用哪种网络结构作为backbone,共有“res_18 | res_101 | resdcn_18 | resdcn_101 |''dlav0_34 | dla_34 | hourglass'”七种。

        以resnet101为例,在main.py中,调用了createmodel,在createmodel中又调用了_model_factory及getmodel。在_model_factory中传入opt中写的模型名称,通过_model_factory的getmodel调用真正的构建模型方法,如get_pose_net。在get_pose_net中构建PoseResNet类的实例,构建网络模型,同时在get_pose_net中对模型参数进行初始化。

        初始化:如果模型层是卷积,使用正态分布初始化,如果是偏置,使用常数0初始化;如果是BN层,weight使用常数1初始化,bias使用0初始化……

def init_weights(self, num_layers, pretrained=True):
        if pretrained:
            # print('=> init resnet deconv weights from normal distribution')
            for _, m in self.deconv_layers.named_modules():
                if isinstance(m, nn.ConvTranspose2d):
                    # print('=> init {}.weight as normal(0, 0.001)'.format(name))
                    # print('=> init {}.bias as 0'.format(name))
                    nn.init.normal_(m.weight, std=0.001)
                    if self.deconv_with_bias:
                        nn.init.constant_(m.bias, 0)
                elif isinstance(m, nn.BatchNorm2d):
                    # print('=> init {}.weight as 1'.format(name))
                    # print('=> init {}.bias as 0'.format(name))
                    nn.init.constant_(m.weight, 1)
                    nn.init.constant_(m.bias, 0)
           ……

3、训练

        在for epoch中开始循环训练。使用trainer.train(),MutiposeTrainer继承BaseTrainer,BaseTrainer.train.调用runepoch,在runepoch中主要调用model_with_loss(batch)得到模型的检测结果与loss

        得到模型结果与计算loss

        得到模型结果与loss使用这一句话

output, loss, loss_stats = model_with_loss(batch)

loss调用 MultiPoseTrainer中的__get_losses,在该函数中又调用 MultiPoseLoss类完成整个loss的计算。ModelWithLoss中在BaseTrainer中初始化得到模型及配置参数opt,调用ModelWithLoss类,在该类中对模型进行输入,并将得到的结果送入loss得到loss。

class BaseTrainer(object):
  def __init__(
    self, opt, model, optimizer=None):
    self.opt = opt
    self.optimizer = optimizer
    self.loss_stats, self.loss = self._get_losses(opt)
    self.model_with_loss = ModelWithLoss(model, self.loss)
class ModelWithLoss(torch.nn.Module):
  def __init__(self, model, loss):
    super(ModelWithLoss, self).__init__()
    self.model = model
    self.loss = loss
  
  def forward(self, batch):
    outputs = self.model(batch['input'])
    loss, loss_stats = self.loss(outputs, batch)
    return outputs[-1], loss, loss_stats
class MultiPoseLoss(torch.nn.Module):
  def __init__(self, opt):
    super(MultiPoseLoss, self).__init__()
    self.crit = FocalLoss()
    self.crit_hm_hp = torch.nn.MSELoss() if opt.mse_loss else FocalLoss()
    self.crit_kp = RegWeightedL1Loss() if not opt.dense_hp else \
                   torch.nn.L1Loss(reduction='sum')
    self.crit_reg = RegL1Loss() if opt.reg_loss == 'l1' else \
                    RegLoss() if opt.reg_loss == 'sl1' else None
    self.opt = opt
    
  def forward(self, outputs, batch): 
    …………………………………………
    ………………………………
    loss = opt.hm_weight * hm_loss + opt.wh_weight * wh_loss + \
           opt.off_weight * off_loss + opt.hp_weight * hp_loss + \
           opt.hm_hp_weight * hm_hp_loss + opt.off_weight * hp_offset_loss
    loss_stats = {'loss': loss, 'hm_loss': hm_loss, 'hp_loss': hp_loss, 
                  'hm_hp_loss': hm_hp_loss, 'hp_offset_loss': hp_offset_loss,
                  'wh_loss': wh_loss, 'off_loss': off_loss}
    return loss, loss_stats

class MultiPoseTrainer(BaseTrainer):
  def __init__(self, opt, model, optimizer=None):
    super(MultiPoseTrainer, self).__init__(opt, model, optimizer=optimizer)
  
  def _get_losses(self, opt):
    loss_states = ['loss', 'hm_loss', 'hp_loss', 'hm_hp_loss', 
                   'hp_offset_loss', 'wh_loss', 'off_loss']
    loss = MultiPoseLoss(opt)
    return loss_states, loss

计算heatmap使用focalloss 

def _neg_loss(pred, gt):
  ''' Modified focal loss. Exactly the same as CornerNet.
      Runs faster and costs a little bit more memory
    Arguments:
      pred (batch x c x h x w)
      gt_regr (batch x c x h x w)
  '''
  pos_inds = gt.eq(1).float()#gt等于1的index  正样本
  neg_inds = gt.lt(1).float() #less than 小于1的index  副样本

  neg_weights = torch.pow(1 - gt, 4) #(1-y)^4

  loss = 0

  pos_loss = torch.log(pred) * torch.pow(1 - pred, 2) * pos_inds
  neg_loss = torch.log(1 - pred) * torch.pow(pred, 2) * neg_weights * neg_inds

  num_pos  = pos_inds.float().sum()
  pos_loss = pos_loss.sum()
  neg_loss = neg_loss.sum()

  if num_pos == 0:
    loss = loss - neg_loss   #当没有正样本时只取负样本的损失函数
  else:
    loss = loss - (pos_loss + neg_loss) / num_pos # 有正样本时,正负样本损失相加除正样本个数
  return loss

4、decode

评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值