以下链接是个人关于Liquid Warping GAN(Impersonator)-姿态迁移,所有见解,如有错误欢迎大家指出,我会第一时间纠正。有兴趣的朋友可以加微信:a944284742相互讨论技术。若是帮助到了你什么,一定要记得点赞!因为这是对我最大的鼓励。
风格迁移1-00:Liquid Warping GAN(Impersonator)-目录-史上最新无死角讲解
通过前面的博客,网络模型以及loss的总体计算,可以说是有了一定了解,那么现在我们分析下hmr以及render,其实呢,这个本来是不打算讲解的,因为这个其实不属于
Liquid Warping GAN的内容,作者也是直接使用别人训练好的模型,但是为了更加方便的理解论文,这里就带大家简单的过一遍,和论文串接起来。通过train.py,对其中的代码
# 根据模式名字创建模型,默认使用impersonator模式进行训练,self._opt.model= impersonator_trainer
self._model = ModelsFactory.get_by_name(self._opt.model, self._opt)
一路追踪,可以找到models/impersonator_trainer.py中的class Impersonator(BaseModel)类,进入其初始化函数调用的self._init_create_networks(),实现如下(只复制了核心代码):
# body recovery Flow,构建body recovery网络,即hmr以及render
self._bdr = BodyRecoveryFlow(opt=self._opt)
# generator network,构建生成网络
self._G = self._create_generator()
# discriminator network,构建鉴别网络
self._D = self._create_discriminator()
由这3个网络构成的统一模型,就是我们的,Liquid Warping GAN总体架构了,现在我们来看看上面3个部分的第一个部分,即hmr以及render。进入BodyRecoveryFlow。
BodyRecoveryFlow的代码注释如下:
def forward(self, src_img, ref_img, src_smpl, ref_smpl):
# get smpl information,注意,这里的smpl来自于数据集的smpls中的pkl文件
# 在训练的时候,是直接中获得得,也就是src_info以及ref_info的SMPL不是通过hmr估算出来的
# 在测试的时候会使用hmr模型估算出来的结果
# 其包含了theta[b,85] = pose[b,72] + shape[b,10] + cam[b,3]
# cam[b,3],对应论文中的K, pose[b,72]对应论文的θ, shape[b,10]对应论文中的β
# verts[b,6890,3]对应论文中的Nv
# 最后还有j2d[b,19,2]与j3d[b,19,3]大概表示3D与2D视觉下,人体19个关键的空间坐标吧
src_info = self._hmr.get_details(src_smpl)
ref_info = self._hmr.get_details(ref_smpl)
# process source inputs,在src_info['cam']的视角下,对src_info['verts']进行渲染
# src_f2verts[b, 13776, 3, 2]对应论文中的Nf,对应于13776个坐标位置的mask
src_f2verts, src_fim, _ = self._render.render_fim_wim(src_info['cam'], src_info['verts'])
# 只取得原图的verts
src_f2verts = src_f2verts[:, :, :, 0:2]
# 把所有的y坐标乘以-1,应该是为了后续计算矩阵T的方便
src_f2verts[:, :, :, 1] *= -1
# src_cond[b,3,256,256],获得src img对应的verts所在位置坐标的mask
src_cond, _ = self._render.encode_fim(src_info['cam'], src_info['verts'], fim=src_fim, transpose=True)
# 获得mask掩码
src_crop_mask = util.morph(src_cond[:, -1:, :, :], ks=3, mode='erode')
#
_, ref_fim, ref_wim = self._render.render_fim_wim(ref_info['cam'], ref_info['verts'])
ref_cond, _ = self._render.encode_fim(ref_info['cam'], ref_info['verts'], fim=ref_fim, transpose=True)
# 计算获得论文中的T矩阵
T = self._render.cal_bc_transform(src_f2verts, ref_fim, ref_wim)
# src_img经过T矩阵变化
syn_img = F.grid_sample(src_img, T)
# src input,input_G_src为论文中的Ift
input_G_src = torch.cat([src_img * (1 - src_crop_mask), src_cond], dim=1)
# tsf input,input_G_tsf为论文中的Isyn
input_G_tsf = torch.cat([syn_img, ref_cond], dim=1)
# bg input
src_bg_mask = util.morph(src_cond[:, -1:, :, :], ks=15, mode='erode')
# input_G_src_bg为论文中的Ibg,
input_G_src_bg = torch.cat([src_img * src_bg_mask, src_bg_mask], dim=1)
if self._opt.bg_both:
ref_bg_mask = util.morph(ref_cond[:, -1:, :, :], ks=15, mode='erode')
input_G_tsf_bg = torch.cat([ref_img * ref_bg_mask, ref_bg_mask], dim=1)
else:
input_G_tsf_bg = None
# masks
tsf_crop_mask = util.morph(ref_cond[:, -1:, :, :], ks=3, mode='erode')
# 根据关键点分别获得头部,身体的box坐标
head_bbox = self.cal_head_bbox(ref_info['j2d'])
body_bbox = self.cal_body_bbox(ref_info['j2d'])
return input_G_src_bg, input_G_tsf_bg, input_G_src, input_G_tsf, \
T, src_crop_mask, tsf_crop_mask, head_bbox, body_bbox
大家观看代码从其前向传播看起,又兴趣的朋友可以去研究一下hmr以及render的细节,因为这是另外一个体系了,简单的介绍就到这里了,总的来说,我们主要是为了获得如下结果:
# input_G_src_bg为论文中的Ibg,
# input_G_src为论文中的Ift
# input_G_tsf为论文中的Isyn
# src_crop_mask为source图像的掩码,即人体部分像素为0,其余为1
# tsf_crop_mask为reference图像的掩码,即人体部分像素为0,其余为1
# T 和论文的T对应
# head_bbox = 头部的bbox坐标
# body_bbox = 身体的bbox坐标
获得了这些之后,就相当于得到了生成网络的输入,接下来要走的网络就是如下两个了:
# generator network,构建生成网络
self._G = self._create_generator()
# discriminator network,构建鉴别网络
self._D = self._create_discriminator()
各位朋友,在这里表示抱歉,我发现此网络,虽然作者的构思很巧妙,但没有落地的价值,网络泛化能力较差,不能达到我预期的效果,还有很多需要改进的地方,所有暂时不打算深究下去了。如果以后本人有空闲时间,会把剩余的篇章都接上。如果需要查看其他的篇章,通过上面的链接即可,感谢各位朋友关注了,有你们一路的陪伴我真的很开心,也很骄傲!再见,其他项目再见。