点击 机器学习算法与Python学习 ,选择加星标

精彩内容不迷路

作者 | 李秋键 ,出品 | AI科技大本营(ID:rgznai100)

引言:近段时间,一个让梦娜丽莎图像动起来的项目火遍了朋友圈。而今天我们就将实现让图片中的人物随着视频人物一起产生动作。

其中通过在静止图像中动画对象产生视频有无数的应用跨越的领域兴趣,包括电影制作、摄影和电子商务。更准确地说,是图像动画指将提取的视频外观结合起来自动合成视频的任务一种源图像与运动模式派生的视频。近年来,深度生成模型作为一种有效的图像动画技术出现了视频重定向。特别是,可生成的对抗网络(GANS)和变分自动编码器(VAES)已被用于在视频中人类受试者之间转换面部表情或运动模式。

根据论文FirstOrder Motion Model for Image Animation可知,在姿态迁移的大任务当中,Monkey-Net首先尝试了通过自监督范式预测关键点来表征姿态信息,测试阶段估计驱动视频的姿态关键点完成迁移工作。在此基础上,FOMM使用了相邻关键点的局部仿射变换来模拟物体运动,还额外考虑了遮挡的部分,遮挡的部分可以使用image inpainting生成。

而今天我们就将借助论文所分享的源代码,构建模型创建自己需要的人物运动。具体流程如下。

实验前的准备

首先我们使用的python版本是3.6.5所用到的模块如下:

  • imageio模块用来控制图像的输入输出等。
  • Matplotlib模块用来绘图。
  • numpy模块用来处理矩阵运算。
  • Pillow库用来加载数据处理。
  • pytorch模块用来创建模型和模型训练等。
  • 完整模块需求参见requirements.txt文件。

模型的加载和调用

通过定义命令行参数来达到加载模型,图片等目的。(1)首先是训练模型的读取,包括模型加载方式:

def load_checkpoints(config_path, checkpoint_path, cpu=False):
    with open(config_path) as f:
        config = yaml.load(f)
    generator = OcclusionAwareGenerator(**config[ model_params ][ generator_params ],
                                        **config[ model_params ][ common_params ])
    if not cpu:
        generator.cuda()
    kp_detector = KPDetector(**config[ model_params ][ kp_detector_params ],
                             **config[ model_params ][ common_params ])
    if not cpu:
        kp_detector.cuda()
    if cpu:
        checkpoint = torch.load(checkpoint_path, map_location=torch.device( cpu ))
    else:
        checkpoint = torch.load(checkpoint_path)
    generator.load_state_dict(checkpoint[ generator ])
    kp_detector.load_state_dict(checkpoint[ kp_detector ])
    if not cpu:
        generator = DataParallelWithCallback(generator)
        kp_detector = DataParallelWithCallback(kp_detector)
    generator.eval()
    kp_detector.eval()
    return generator, kp_detector 

(2)然后是利用模型创建产生的虚拟图像,找到最佳的脸部特征:

def make_animation(source_image, driving_video, generator, kp_detector, relative=True, adapt_movement_scale=True, cpu=False):with torch.no_grad():
        predictions = []
        source = torch.tensor(source_image[np.newaxis].astype(np.float32)).permute(0, 3, 1, 2)if not cpu:
            source = source.cuda()
        driving = torch.tensor(np.array(driving_video)[np.newaxis].astype(np.float32)).permute(0, 4, 1, 2, 3)
        kp_source = kp_detector(source)
        kp_driving_initial = kp_detector(driving[:, :, 0])for frame_idx in tqdm(range(driving.shape[2])):
            driving_frame = driving[:, :, frame_idx]if not cpu:
                driving_frame = driving_frame.cuda()
            kp_driving = kp_detector(driving_frame)
            kp_norm = normalize_kp(kp_source=kp_source, kp_driving=kp_driving,
                                   kp_driving_initial=kp_driving_initial, use_relative_movement=relative,
                                   use_relative_jacobian=relative, adapt_movement_scale=adapt_movement_scale)
            out = generator(source, kp_source=kp_source, kp_driving=kp_norm)           predictions.append(np.transpose(out[ prediction ].data.cpu().numpy(), [0, 2, 3, 1])[0])return predictionsdef find_best_frame(source, driving, cpu=False):import face_alignmentdef normalize_kp(kp):
        kp = kp - kp.mean(axis=0, keepdims=True)
        area = ConvexHull(kp[:, :2]).volume
        area = np.sqrt(area)
        kp[:, :2] = kp[:, :2] / areareturn kp
    fa = face_alignment.FaceAlignment(face_alignment.LandmarksType._2D, flip_input=True,
                                      device= cpu  if cpu else  cuda )
    kp_source = fa.get_landmarks(255 * source)[0]
    kp_source = normalize_kp(kp_source)
    norm  = float( inf )
    frame_num = 0for i, image in tqdm(enumerate(driving)):
        kp_driving = fa.get_landmarks(255 * image)[0]
        kp_driving = normalize_kp(kp_driving)
        new_norm = (np.abs(kp_source - kp_driving) ** 2).sum()if new_norm             norm = new_norm
            frame_num = ireturn frame_num

(3)    接着定义命令行调用参数加载图片、视频等方式:

parser = ArgumentParser()
    parser.add_argument("--config", required=True, help="path to config")
    parser.add_argument("--checkpoint", default= vox-cpk.pth.tar , help="path to checkpoint to restore")
    parser.add_argument("--source_image", default= sup-mat/source.png , help="path to source image")
    parser.add_argument("--driving_video", default= sup-mat/source.png , help="path to driving video")
    parser.add_argument("--result_video", default= result.mp4 , help="path to output")
    parser.add_argument("--relative", dest="relative", action="store_true", help="use relative or absolute keypoint coordinates")
    parser.add_argument("--adapt_scale", dest="adapt_scale", action="store_true", help="adapt movement scale based on convex hull of keypoints")
    parser.add_argument("--find_best_frame", dest="find_best_frame", action="store_true", 
                        help="Generate from the frame that is the most alligned with source. (Only for faces, requires face_aligment lib)")
    parser.add_argument("--best_frame", dest="best_frame", type=int, default=None,  
                        help="Set frame to start from.")
    parser.add_argument("--cpu", dest="cpu", action="store_true", help="cpu mode.")
    parser.set_defaults(relative=False)
    parser.set_defaults(adapt_scale=False)
    opt = parser.parse_args()
    source_image = imageio.imread(opt.source_image)
    reader = imageio.get_reader(opt.driving_video)
    fps = reader.get_meta_data()[ fps ]
    driving_video = []try:for im in reader:
            driving_video.append(im)except RuntimeError:pass
    reader.close()
    source_image = resize(source_image, (256, 256))[..., :3]
    driving_video = [resize(frame, (256, 256))[..., :3] for frame in driving_video]
    generator, kp_detector = load_checkpoints(config_path=opt.config, checkpoint_path=opt.checkpoint, cpu=opt.cpu)if opt.find_best_frame or opt.best_frame is not None:
        i = opt.best_frame if opt.best_frame is not None else find_best_frame(source_image, driving_video, cpu=opt.cpu)print ("Best frame: " + str(i))
        driving_forward = driving_video[i:]
        driving_backward = driving_video[:(i+1)][::-1]
        predictions_forward = make_animation(source_image, driving_forward, generator, kp_detector, relative=opt.relative, adapt_movement_scale=opt.adapt_scale, cpu=opt.cpu)
        predictions_backward = make_animation(source_image, driving_backward, generator, kp_detector, relative=opt.relative, adapt_movement_scale=opt.adapt_scale, cpu=opt.cpu)
        predictions = predictions_backward[::-1] + predictions_forward[1:]else:
        predictions = make_animation(source_image, driving_video, generator, kp_detector, relative=opt.relative, adapt_movement_scale=opt.adapt_scale, cpu=opt.cpu)
imageio.mimsave(opt.result_video, [img_as_ubyte(frame) for frame in predictions], fps=fps)

模型的搭建

整个模型训练过程是图像重建的过程,输入是源图像和驱动图像,输出是保留源图像物体信息的含有驱动图像姿态的新图像,其中输入的两张图像来源于同一个视频,即同一个物体信息,那么整个训练过程就是驱动图像的重建过程。大体上来说分成两个模块,一个是motion estimation module,另一个是imagegeneration module。(1)其中通过定义VGG19模型建立网络层作为perceptual损失。其中手动输入数据进行预测需要设置更多的GUI按钮,其中代码如下:

class Vgg19(torch.nn.Module):"""
    Vgg19 network for perceptual loss. See Sec 3.3.
    """def __init__(self, requires_grad=False):super(Vgg19, self).__init__()
        vgg_pretrained_features = models.vgg19(pretrained=True).featuresself.slice1 = torch.nn.Sequential()self.slice2 = torch.nn.Sequential()self.slice3 = torch.nn.Sequential()self.slice4 = torch.nn.Sequential()self.slice5 = torch.nn.Sequential()for x in range(2):self.slice1.add_module(str(x), vgg_pretrained_features[x])for x in range(2, 7):self.slice2.add_module(str(x), vgg_pretrained_features[x])for x in range(7, 12):self.slice3.add_module(str(x), vgg_pretrained_features[x])for x in range(12, 21):self.slice4.add_module(str(x), vgg_pretrained_features[x])for x in range(21, 30):self.slice5.add_module(str(x), vgg_pretrained_features[x])self.mean = torch.nn.Parameter(data=torch.Tensor(np.array([0.485, 0.456, 0.406]).reshape((1, 3, 1, 1))),
                                       requires_grad=False)self.std = torch.nn.Parameter(data=torch.Tensor(np.array([0.229, 0.224, 0.225]).reshape((1, 3, 1, 1))),
                                      requires_grad=False)if not requires_grad:for param in self.parameters():
                param.requires_grad = Falsedef forward(self, X):
        X = (X - self.mean) / self.std
        h_relu1 = self.slice1(X)
        h_relu2 = self.slice2(h_relu1)
        h_relu3 = self.slice3(h_relu2)
        h_relu4 = self.slice4(h_relu3)
        h_relu5 = self.slice5(h_relu4)
        out = [h_relu1, h_relu2, h_relu3, h_relu4, h_relu5]return out

(2)创建图像金字塔计算金字塔感知损失:

class ImagePyramide(torch.nn.Module):"""
    Create image pyramide for computing pyramide perceptual loss. See Sec 3.3
    """def __init__(self, scales, num_channels):
        super(ImagePyramide, self).__init__()
        downs = {}for scale in scales:
            downs[str(scale).replace( . ,  - )] = AntiAliasInterpolation2d(num_channels, scale)
        self.downs = nn.ModuleDict(downs)def forward(self, x):
        out_dict = {}for scale, down_module in self.downs.items():
            out_dict[ prediction_  + str(scale).replace( - ,  . )] = down_module(x)return out_dict

(3)等方差约束的随机tps变换

class Transform:"""
    Random tps transformation for equivariance constraints. See Sec 3.3
    """def __init__(self, bs, **kwargs):
        noise = torch.normal(mean=0, std=kwargs[ sigma_affine ] * torch.ones([bs, 2, 3]))self.theta = noise + torch.eye(2, 3).view(1, 2, 3)self.bs = bsif ( sigma_tps  in kwargs) and ( points_tps  in kwargs):self.tps = Trueself.control_points = make_coordinate_grid((kwargs[ points_tps ], kwargs[ points_tps ]), type=noise.type())self.control_points = self.control_points.unsqueeze(0)self.control_params = torch.normal(mean=0,
                                               std=kwargs[ sigma_tps ] * torch.ones([bs, 1, kwargs[ points_tps ] ** 2]))else:self.tps = Falsedef transform_frame(self, frame):
        grid = make_coordinate_grid(frame.shape[2:], type=frame.type()).unsqueeze(0)
        grid = grid.view(1, frame.shape[2] * frame.shape[3], 2)
        grid = self.warp_coordinates(grid).view(self.bs, frame.shape[2], frame.shape[3], 2)return F.grid_sample(frame, grid, padding_mode="reflection")def warp_coordinates(self, coordinates):
        theta = self.theta.type(coordinates.type())
        theta = theta.unsqueeze(1)
        transformed = torch.matmul(theta[:, :, :, :2], coordinates.unsqueeze(-1)) + theta[:, :, :, 2:]
        transformed = transformed.squeeze(-1)if self.tps:
            control_points = self.control_points.type(coordinates.type())
            control_params = self.control_params.type(coordinates.type())
            distances = coordinates.view(coordinates.shape[0], -1, 1, 2) - control_points.view(1, 1, -1, 2)
            distances = torch.abs(distances).sum(-1)
            result = distances ** 2
            result = result * torch.log(distances + 1e-6)
            result = result * control_params
            result = result.sum(dim=2).view(self.bs, coordinates.shape[1], 1)
            transformed = transformed + resultreturn transformeddef jacobian(self, coordinates):
        new_coordinates = self.warp_coordinates(coordinates)
        grad_x = grad(new_coordinates[..., 0].sum(), coordinates, create_graph=True)
        grad_y = grad(new_coordinates[..., 1].sum(), coordinates, create_graph=True)
        jacobian = torch.cat([grad_x[0].unsqueeze(-2), grad_y[0].unsqueeze(-2)], dim=-2)return jacobian    

(4)生成器的定义:生成器,给定的源图像和和关键点尝试转换图像根据运动轨迹引起要点。部分代码如下:

class OcclusionAwareGenerator(nn.Module):def __init__(self, num_channels, num_kp, block_expansion, max_features, num_down_blocks,
                 num_bottleneck_blocks, estimate_occlusion_map=False, dense_motion_params=None, estimate_jacobian=False):super(OcclusionAwareGenerator, self).__init__()if dense_motion_params is not None:self.dense_motion_network = DenseMotionNetwork(num_kp=num_kp, num_channels=num_channels,
                                                           estimate_occlusion_map=estimate_occlusion_map,
                                                           **dense_motion_params)else:self.dense_motion_network = Noneself.first = SameBlock2d(num_channels, block_expansion, kernel_size=(7, 7), padding=(3, 3))
        down_blocks = []for i in range(num_down_blocks):
            in_features = min(max_features, block_expansion * (2 ** i))
            out_features = min(max_features, block_expansion * (2 ** (i + 1)))
            down_blocks.append(DownBlock2d(in_features, out_features, kernel_size=(3, 3), padding=(1, 1)))self.down_blocks = nn.ModuleList(down_blocks)
        up_blocks = []for i in range(num_down_blocks):
            in_features = min(max_features, block_expansion * (2 ** (num_down_blocks - i)))
            out_features = min(max_features, block_expansion * (2 ** (num_down_blocks - i - 1)))
            up_blocks.append(UpBlock2d(in_features, out_features, kernel_size=(3, 3), padding=(1, 1)))self.up_blocks = nn.ModuleList(up_blocks)self.bottleneck = torch.nn.Sequential()
        in_features = min(max_features, block_expansion * (2 ** num_down_blocks))for i in range(num_bottleneck_blocks):self.bottleneck.add_module( r  + str(i), ResBlock2d(in_features, kernel_size=(3, 3), padding=(1, 1)))self.final = nn.Conv2d(block_expansion, num_channels, kernel_size=(7, 7), padding=(3, 3))self.estimate_occlusion_map = estimate_occlusion_mapself.num_channels = num_channels

(5)判别器类似于Pix2PixGenerator。

def __init__(self, num_channels=3, block_expansion=64, num_blocks=4, max_features=512,
                 sn=False, use_kp=False, num_kp=10, kp_variance=0.01, **kwargs):super(Discriminator, self).__init__()
        down_blocks = []for i in range(num_blocks):
            down_blocks.append(
                DownBlock2d(num_channels + num_kp * use_kp if i == 0 else min(max_features, block_expansion * (2 ** i)),
                            min(max_features, block_expansion * (2 ** (i + 1))),
                            norm=(i != 0), kernel_size=4, pool=(i != num_blocks - 1), sn=sn))self.down_blocks = nn.ModuleList(down_blocks)self.conv = nn.Conv2d(self.down_blocks[-1].conv.out_channels, out_channels=1, kernel_size=1)if sn:self.conv = nn.utils.spectral_norm(self.conv)self.use_kp = use_kpself.kp_variance = kp_variance
    def forward(self, x, kp=None):
        feature_maps = []out = xif self.use_kp:
            heatmap = kp2gaussian(kp, x.shape[2:], self.kp_variance)out = torch.cat([out, heatmap], dim=1)for down_block in self.down_blocks:
            feature_maps.append(down_block(out))out = feature_maps[-1]
        prediction_map = self.conv(out)return feature_maps, prediction_map

最终通过以下代码调用模型训练“python demo.py--config config/vox-adv-256.yaml --driving_video path/to/driving/1.mp4--source_image path/to/source/7.jpg --checkpointpath/to/checkpoint/vox-adv-cpk.pth.tar --relative --adapt_scale”效果如下:

完整代码:https://pan.baidu.com/s/1nPE13oI1qOerN0ANQSH92g提取码:e4kx

喜欢就点击“在看”吧!

创建图片mat_Python骚操作,让图片人物动起来!相关推荐

  1. 骚操作!图片缩小后 内容竟变得完全不同?

    点击上方"AI算法与图像处理",选择加"星标"或"置顶" 重磅干货,第一时间送达 编辑:AI算法与图像处理 仅作为学习交流分享,如有侵权请联 ...

  2. python 将图片转换成像素画_Python 骚操作之图片转字符画

    今天给大家介绍一个 Python 的骚操作,将图片转化为字符画,又是一个装逼的好技巧,大家赶紧 get 一下吧! 实现原理: 字符画是一系列字符的组合,我们可以把字符看作是比较大块的像素,一个字符能表 ...

  3. js控制台 console 骚操作-打印图片-自定义样式-字符画

    目录 控制台打印骚操作 自定义样式 打印(输出)图片 借助平台打印字符(符号组成) Lunicode 平台操作 图片转字符打印 控制台打印骚操作二 效果图 代码实现 错误案例 方式一(感觉有点乱) 方 ...

  4. 图片翻译如何操作?图片翻译的方法分享.

    随着全球化的发展,图片翻译已成为越来越重要的工具,特别是在工作场景中,图片翻译对于企业和个人来说都是一种非常有效的解决方案,图片翻译可以帮助我们更好地理解文件的内容,在工作中,我们经常需要阅读来自国外 ...

  5. python学习之手把手教你将图片变成黑白或彩色字符画(骚操作)

    文章目录 前言 一.字符画的实现原理 二.黑白字符画实现代码 三.彩色字符画生成 代码实现: 总结 前言 字符画这个话题,似乎早在贴吧时代就已经被玩烂了.在百度图片随便一搜索,就能够看到非常多.然后在 ...

  6. 音视频骚操作,FFmpeg 如何播放带「图片」的 M3U8 视频,IJKPlyaer 适配非标 TS 文件

    如果看到一个需要播放的视频链接显示是一张图片,你会不会感觉有点懵?如果这张图片写着 png,然后实际格式是 bmp ,你会不会更懵了?如果这个 bmp 还做了加密篡改呢?今天我们要聊的就是这样一个充满 ...

  7. 《Python自动化》学习笔记:百度云智能实现黑白图片上色,人物动漫化(代码干货)

    基本要求 要注册百度的账号,并且创建应用,方法如下: 百度智能云新手入门教程:https://cloud.baidu.com/doc/OCR/s/dk3iqnq51 目的是为了获得API_Key和Se ...

  8. 【愚公系列】2023年01月 .NET CORE工具案例- Magick.NET神级图片和视频操作库

    文章目录 前言 一.Magick.NET的使用 1.安装包 2.图像的操作 2.1 图像读取 2.1 图像大小改变 2.2 图像格式转换 2.3 PDF转换 2.4 添加水印.文本 2.5 图片合并 ...

  9. ArcGIS_Online——如何创建地图故事模板以及插入图片、音乐、视频

    ArcGIS Online--如何创建地图故事模板以及插入图片.音乐.视频   在介绍如何插入图片.音乐.视频前,我先来介绍一下怎么创建故事的"环境",也就是如何使用故事模板.第一 ...

  10. 【ps操作】图片裁切、校色、修复以及蒙版的使用

    文章目录 基本操作 图层面板介绍 图片的裁切 图像校色 图像修复 红眼 修瑕疵 去水印 蒙版的作用 钢笔工具 基本操作 工具栏设置 菜单中窗口(设置右边的工具栏)--添加导航器,画笔,字符这些常用工具 ...

最新文章

  1. Codeforces Round #643 (Div. 2)题解
  2. 低潮过后,未来医疗可穿戴市场将如何发展?
  3. 【LeetCode】106. Construct Binary Tree from Inorder and Postorder Traversal
  4. 编写优秀Bug报告的艺术 ----转载自CSDN(imlogic的专栏)
  5. 「SDOI2016」储能表(数位dp)
  6. Blazor 组件库开发指南
  7. .NET 6 Preview5+VS2022实战千万并发秒杀项目,帅爆了(附源码)
  8. lamp不解析php,lamp环境无法解析php环境怎么办
  9. 20秋学期计算机应用基础在线作业3,电子科大16秋《计算机应用基础》在线作业3...
  10. 手机号码验证的正则表达式(17......)
  11. 5.5 tensorflow2实现多项式回归与神经网络、未来一个月购买量预测——python实战
  12. fcm模糊聚类matlab实例_智能控制-Fuzzy控制- matlab
  13. Millumin for Mac视频实时编辑软件
  14. 通达oa2017 数据库表结构
  15. mysql电商数据库设计文档_MySQL电商项目数据库设计及架构优化视频课程
  16. 美国加州计算机专业大学排名,USNews美国计算机专业大学排名
  17. [软件人生]耐得住寂寞——积累是低潮时期技术人员的品质
  18. flutter 接入微信客服
  19. 老男孩网络安全2021
  20. linux CPU降频脚本

热门文章

  1. tensorflow stack unstack操作
  2. linux设备实现otg移动盘,Linux下通过OTG把板卡上的一个磁盘或文件映射成移动磁盘...
  3. asp.net 自带 ajax,ASP.NET AJAX
  4. linux 运行菜刀,Linux部署常用命令
  5. 罗斯蒙特电磁流量计8723说明书_罗斯蒙特电磁流量计8732EM变送器信号处理算法说明...
  6. z=rand()%i c语言,C语言关于产生随机数文章转载两篇(一)
  7. yii2 DAO实现增,删,改,查
  8. 为什么开发人员必须要了解数据库锁?
  9. 浅谈找到***点后的处理(清理***)
  10. 宝马发布三款新车,2019年将开启最大规模产品攻势...