专栏首页Coding迪斯尼人脸生成黑科技:实现人脸转变特效,让人脸自动戴墨镜

人脸生成黑科技:实现人脸转变特效,让人脸自动戴墨镜

上一节我们通过VAE网络完成了人脸生成效果。VAE网络一个特性是会把人脸编码成一个含有200个分量的向量,反过来说在特定分布范围内的含有200个分量的向量就对应一张人脸。由于向量之间可以进行运算,这就意味着我们把两张不同人脸A,B分布转换成两个不同向量z_A,z_B,然后我们使用向量运算例如z_AB = z_A *(1 - alpha) + z_B *alpha,就能将两个向量以一定比例合成一个新向量,该新向量就会对应一个人脸,而且这个人脸就会同时具有人脸A和B的特点,如果我们增大参数alpha,那么生成向量对应的人脸特征就会更像人脸B,如果我们减少alpha的值,生成向量对应的人脸就更像人脸A.

接下来我们看看如何实现人脸的转变特效,首先我们先出数据图片中选出具有特定特征的人脸图片,例如”戴墨镜“,然后使用编码器得出”戴墨镜“人脸图片的特征向量,然后我们再选取不带墨镜的人脸图片,计算其特征向量,然后使用向量运算将两个向量结合起来,这样我们就能把后面不带墨镜的人脸转换成带墨镜的模样。

我们看看相应代码实现:

def  morph_faces(start_image_file, end_image_file):
    alpha = np.arange(0, 1, 0.1)
    atttribute_specific = att[att['image_id'].isin([start_image_file, end_image_file])]
    att_specific = atttribute_specific.reset_index()
    data_flow_label = imageLoader.build(att_specific, 2) #加载满足给定条件的图片

    example_batch = next(data_flow_label)
    example_images = example_batch[0]
    example_labels = example_batch[1]
    z_vectors = vae.encoder.predict(example_images)#获得人脸图像的关键向量
    fig = plt.figure(figsize = (18, 8))
    counter = 1
    img = example_images[0].squeeze()
    sub = fig.add_subplot(1, len(alpha) + 2, counter)
    sub.axis('off')
    sub.imshow(img)
    counter += 1

    for factor in alpha: #通过变化参数让一个向量滑向另一个向量
        changed_z_vector = z_vectors[0] * (1 - factor) + z_vectors[1] * factor
        changed_image = vae.decoder.predict(np.array([changed_z_vector]))[0] #根据新向量绘制人脸图像
        img = changed_image.squeeze()
        sub = fig.add_subplot(1, len(alpha) + 2, counter)
        sub.axis('off')
        sub.imshow(img)
        counter += 1
    img = example_images[1]
    sub = fig.add_subplot(1, len(alpha) + 2, counter)
    sub.axis('off')
    sub.imshow(img)
    plt.show()

上面函数先将输入的两张图片转换成各自对应的向量,然后使用向量运算,通过变化中间参数的形式让一个向量不断的”滑向“另一个向量,由此形成的效果是,其中一张人脸图片参数”渐变“效果,随着向量不断滑向另一个向量,所生成的人脸图片越来越具备目标向量的特性,我们调用上面函数看看实现效果:

start_image_file = '000238.jpg'
end_image_file = '000193.jpg' #戴墨镜人脸
morph_faces(start_image_file, end_image_file)

上面代码运行后所得结果如下:

处于最左和最右边的图像时我们输入的两张人脸图片,中间人脸是将一边人脸图片对应的向量滑向另一边时所产生的人脸,我们注意到中间人脸图片是左右两张人脸图片特征的混合。回到deepfake或zao这样的变脸应用,他们的原理就是先将计算原来视频中人脸变化所对应的不同向量,然后计算用户的人脸向量,然后将用户人脸向量”滑向“视频中人脸当前表情对应向量从而实现用户人脸展现出视频中人脸的同样表情,当前它们的实现比我们这里介绍的要复杂得多。

接下来我们看如何让一个没有带墨镜的人脸“自然而然”的带上墨镜,首先要做的是计算所有戴墨镜的人脸图片对应的向量,将这些向量加总求平均值得向量A,这样我们就能用向量A表达出”带墨镜“这一特征,然后计算所有没有带墨镜的人脸图片所对应的向量,将这些向量加总求平均值得向量B,将该向量减去”戴墨镜“所对应的特征向量,这样就得出滑动方向,然后将不带墨镜的人脸图片对应向量慢慢根据给定方向滑动,这样我们就可以天衣无缝的给原来没带墨镜的人脸带上墨镜,实现如下:

def  get_vector_from_label(label, batch_size):
    data_flow_label = imageLoader.build(att, batch_size, label = label) #读取所有图片

    origin = np.zeros(shape = vae.z_dim, dtype = 'float32') #原点向量
    current_sum_pos = np.zeros(shape = vae.z_dim, dtype = 'float32') #将所有含有给定特征的图片向量加总
    current_n_pos = 0 #统计当前具备给定特征的图片数量
    current_mean_pos = np.zeros(shape = vae.z_dim, dtype = 'float32') #求加给定特征向量加总后的平均值向量

    current_sum_neg = np.zeros(shape = vae.z_dim, dtype = 'float32') #不具备给定特征的向量加总
    current_n_neg = 0 #不具备给定特征的向量个数
    current_mean_neg =  np.zeros(shape = vae.z_dim, dtype = 'float32') #不具备给定特征的向量加总后求平均所得向量

    current_vector = np.zeros(shape = vae.z_dim, dtype = 'float32')
    current_dist = 0 #给定图片特征向量与给定特征向量之间的距离
    print('label: ' + label) #label表示给定特征
    print('image : POS move : NEG move : distance : delta distance')
    while current_n_pos < 10000:
        batch = next(data_flow_label)
        im = batch[0]
        attribute = batch[1]

        z = vae.encoder.predict(np.array(im)) #计算图片对应的特征向量

        z_pos = z[attribute == 1] #抽取出具有给定特征图片所对应的向量
        z_neg = z[attribute == -1] #抽取出不具备给定特征的图片所对应的向量

        if len(z_pos) > 0:
            current_sum_pos = current_sum_pos + np.sum(z_pos, axis = 0) #将所有含有给定特征的向量加总
            current_n_pos += len(z_pos)
            new_mean_pos = current_sum_pos / current_n_pos #计算平均向量
            #随着图片加载越多,具有给定特征的图片也就越多,于是得到的平均向量在位置上就会发生改变
            movement_pos = np.linalg.norm(new_mean_pos - current_mean_pos) #计算特征向量加总求平均后所得向量的变化量
        if len(z_neg) > 0:
            current_sum_neg = current_sum_neg + np.sum(z_neg, axis = 0) #将不具备给定特征的人脸向量加总
            current_n_neg += len(z_neg)
            new_mean_neg = current_sum_neg / current_n_neg #计算平均向量
            #随着读入图片的增多,不具备给定特征的图片也会增多,于是平均向量也会发生改变
            movement_neg = np.linalg.norm(new_mean_neg - current_mean_neg) #计算平均向量的改变量

        current_vector = new_mean_pos - new_mean_neg #获得连接给定特征的平均向量和不具备给定特征的平均向量之间的连接向量
        new_dist = np.linalg.norm(current_vector) #获得两个向量之间的距离
        dist_change = new_dist - current_dist

        print(str(current_n_pos) + '      :  ' + str(np.round(movement_pos, 3))
              + '      :  ' + str(np.round(movement_neg, 3))
              + '      :  ' + str(np.round(new_dist, 3))
              + '      :  ' + str(np.round(dist_change, 3))
              )
        current_mean_pos = np.copy(new_mean_pos)
        current_mean_neg = np.copy(new_mean_neg)
        current_dist = np.copy(new_dist)

        if np.sum([movement_pos, movement_neg]) < 0.08: #当向量该变量足够小时我们结束对平均向量的计算
            current_vector = current_vector / current_dist
            print("Found the " + label + ' vector')
            break
    return  current_vector

def  add_vector_to_images(feature_vec):  #将给定图片的向量滑向给定特征平均向量,于是让图片具备给定特征
    n_to_show = 5
    factors = [-4, -3, -2, -1, 0, 1, 2, 3, 4]

    example_batch = next(data_flow_generic)
    example_images = example_batch[0]
    example_labels = example_batch[1]

    z_points = vae.encoder.predict(example_images) #获得图片对应特征向量
    fig = plt.figure(figsize = (18, 10))
    counter = 1

    for i in range(n_to_show):
        img = example_images[i].squeeze()
        sub = fig.add_subplot(n_to_show, len(factors) + 1, counter)
        sub.axis('off')
        sub.imshow(img) #显示没有变化前图片

        counter += 1

        for factor in factors:  #让图片对应向量滑向给定特征平均向量
            changed_z_point = z_points[i] + feature_vec * factor
            changed_image = vae.decoder.predict(np.array([changed_z_point]))[0] #根据滑动后向量创建人脸图片

            img = changed_image.squeeze()
            sub = fig.add_subplot(n_to_show, len(factors) + 1, counter)
            sub.axis('off')
            sub.imshow(img)
            counter += 1

    plt.show()

接着我们随机采用若干张没有戴墨镜的人脸图片,然后先调用上面代码计算戴墨镜这一特征对应的向量,然后将人类图片对应向量沿着戴墨镜所对应特征向量的方向滑动,这样就能使得不带墨镜的人脸变成戴墨镜,代码如下:

BATCH_SIZE = 500
eyeglasses_vec = get_vector_from_label("Eyeglasses", BATCH_SIZE) #先获得"带墨镜"对应的特征向量
add_vector_to_images(eyeglasses_vec) #让没有带墨镜的人脸图片添加上带墨镜效果

上面代码运行后结果如下:

从中我们看到,最左边对应没有戴墨镜的人脸图片,最右边的人脸则是戴墨镜的效果,到这里我们就介绍完使用VAE网络实现人脸生成的技术方法

本文分享自微信公众号 - Coding迪斯尼(gh_c9f933e7765d),作者:陈屹

原文出处及转载信息见文内详细说明,如有侵权,请联系 yunjia_community@tencent.com 删除。

原始发表时间:2020-01-15

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • 使用变分编解码器实现自动图像生成

    深度学习不仅仅在擅长于从现有数据中发现规律,而且它能主动运用规律创造出现实世界没有的实例来。例如给网络输入大量的人脸图片,让它识别人脸特征,然后我们可以指导网络...

    望月从良
  • 自动编解码器的训练与数据识别效果解析

    上一节我们构建了自动编解码器网络。我们将图片输入到编码器后,它将数据”压缩“成只包含2个分量的一维向量,该向量输入解码器后,又会被还原成与输入图片非常相似的图片...

    望月从良
  • 用深度学习实现自然语言处理:word embedding,单词向量化

    前几年,腾讯新闻曾发出一片具有爆炸性的文章。并不是文章的内容有什么新奇之处,而是文章的作者与众不同,写文章的不是人,而是网络机器人,或者说是人工智能,是算法通过...

    望月从良
  • 电网中移动目标对虚假数据注入攻击的防御分析

    原文题目:Analysis of Moving Target Defense Against False Data Injection Attacks on P...

    非过度曝光
  • IBM WebSphere ESB入门指南

    本博客介绍一款ESB产品,IBM WebSphere ESB。ESB(Enterprise Service Bus)也即企业服务总线。ESB有很多产品,IBM的...

    用户1208223
  • MyBatis报错 Parameter 'arg0' not found

    在早期,参数没做注解时默认是按顺序获取,以0、1等为索引,所以Mapper是这样写的:

    IT晴天
  • 线性代数学习笔记(几何版)

    线性代数学习请移步https://www.bilibili.com/video/av6731067

    attack
  • centos7上配置python3环境和

        centos7 默认的python版本是2.7,目前主流的python版本都是3.6或者3.7。centos的yum包管理器是基于python2编写的,...

    py3study
  • 干货 | 22个免费的数据可视化和分析工具推荐

    当你分析和可视化数据前,常需要“清理”工作。比如一些输入性列表“New York City” ,同时其他人会说”New York, NY” 。因此你需要标准化这...

    华章科技
  • 互联网流媒体视频无插件直播平台EasyNVR关于视频集成自我展示,web端嵌入视频广场的流程

    随着互联网基础设施建设的发展,4G/5G/NB-IoT各种网络技术的大规模商用,视频随时随地可看、可控的诉求越来越多,互联网思维、架构和技术引入进传统监控行业里...

    EasyNVR

扫码关注云+社区

领取腾讯云代金券