65.9K
CodeProject 正在变化。 阅读更多。
Home

汇总:使用深度伪造更换面部

starIconstarIconstarIconstarIconstarIcon

5.00/5 (1投票)

2021年4月6日

CPOL

6分钟阅读

viewsIcon

8953

本文中,我们将完成我们深度伪造 DIY 解决方案的整个流程。

深度伪造——利用深度学习将一个人的脸替换到视频中的另一个人身上——是当今人工智能最有趣令人担忧的应用方式之一。

虽然深度伪造可以用于合法目的,但它们也可以被用于虚假信息传播。通过能够轻易地将某人的脸部替换到任何视频中,我们还能真正相信我们眼睛看到的吗?一个看似真实的政治家或演员做出或说出令人震惊的事情的视频,可能根本就不是真的。

在这个系列文章中,我们将展示深度伪造是如何工作的,以及如何从头开始实现它们。然后,我们将介绍 DeepFaceLab,这是一个常用于创建逼真深度伪造的、由 Tensorflow 驱动的一体化工具。

上一篇文章 中,我们讨论了 Google AI Platform 以及如何借助 Docker 容器在那里训练模型。在本文中,我将启动一个新的 Notebook(我在 Kaggle 中完成了这个操作,但如果你的电脑支持 GPU,你也可以在本地进行),并使用之前训练得到的模型,将它们用于替换 src 帧中的人脸,最后将它们合并,重构原始视频,但这次我们将得到最终的深度伪造视频。

如果你在 Kaggle Notebook 中训练了你的模型,并且计划继续使用该平台,请将该 Notebook 的输出作为我们将要创建的新 Notebook 的输入。如果你在 AI Platform 中训练了模型,并且计划在 Kaggle 上工作,请像导入数据集一样导入它们。如果你计划在本地工作,那么将模型移动到你的 Notebook 目录。请记住,你的机器需要有 GPU,否则在尝试加载模型时会崩溃。

在我的例子中,我使用了上一个 Notebook 的输出作为我将在 Kaggle 中启动的 Notebook 的输入,以完成该项目。你需要获取的一个重要文件是 shape_predictor_68_face_landmarks.dat 文件。在继续之前,请立即下载它。

将 src 人脸转换为 dst 人脸

在接下来的几行中,我们将从 src 视频中提取帧,从帧中提取人脸,使用这些脸部来获取转换后的 dst 脸部,然后将它们插入到 src 帧中。然后,我们只需将它们串联起来,即可获得深度伪造视频。

让我们开始导入所需的库

import numpy as np
import pandas as pd
import keras
import tensorflow
from tensorflow.keras.models import load_model
import gc
import matplotlib.pyplot as plt
import cv2
import os
import dlib
from IPython.display import clear_output

现在我们需要创建一些目录来保存此 Notebook 中产生的图像

!cd /kaggle/working/
!mkdir frames
!mkdir results
!mkdir transformed
!mkdir final
!ls /kaggle/working/
__notebook__.ipynb  final  frames  results  transformed

现在,是时候提取 src 帧并按照它们的顺序存储了。为此,我们将它们保存到一个特定目录中,文件名按升序编号。

input_path = '/kaggle/input/presidentsdataset/presidents/trump1.mp4'
output_path = '/kaggle/working/frames/'
 
def extract_frames(input_path,output_path):
    videocapture = cv2.VideoCapture(input_path)
    success,image = videocapture.read()
    count = 0
    while success:
        cv2.imwrite(output_path+"%d.jpg" % count, image)     
        success,image = videocapture.read()
        count += 1
    print('Frames extraction has ended')
    return count
 
frames = extract_frames(input_path,output_path)
Frames extraction has ended

让我们来看看一帧的样子

%matplotlib inline
plt.figure()
image = cv2.imread('/kaggle/working/frames/120.jpg')
image = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)
plt.imshow(image)
plt.show()

现在是时候从 src 帧中提取人脸,并以与其相应帧相同的名称保存它们了。我们将像以前一样使用 MTCNN 库来提取人脸。

!pip install mtcnn
from mtcnn import MTCNN
 
def extract_faces(source,destination,detector):
    counter = 0
    for dirname, _, filenames in os.walk(source):
        for filename in filenames:
            try:
                image = cv2.imread(os.path.join(dirname, filename))
                image = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)
                detections = detector.detect_faces(image)
                x, y, width, height = detections[0]['box']
                x1,y1,x2,y2 = x-10,y+10,x-10 +width + 20,y+10+height
                face = image[y1:y2, x1:x2]
                face = cv2.resize(face, (120, 120), interpolation=cv2.INTER_LINEAR)
                plt.imsave(os.path.join(destination,filename),face)
                clear_output(wait=True)
                print("Extraction progress: "+str(counter)+"/"+str(len(filenames)-1))
            except:
                pass
            counter += 1
Extraction progress: 1701/1701

让我们来看看其中一张提取的人脸

%matplotlib inline
plt.figure()
image = cv2.imread('/kaggle/working/results/120.jpg')
image = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)
plt.imshow(image)
plt.show()

图像

现在我们已经提取了所有的人脸并保存在各自的目录中,让我们转换所有的人脸来获取它们的 dst 版本。请记住,这里的目标是获取具有 src 人脸的面部表情的 dst 脸部。要加载自编码器模型,请发出这些命令。

autoencoder_a = load_model("/kaggle/input/deepfakes-model-training/autoencoder_a.hdf5")
autoencoder_b = load_model("/kaggle/input/deepfakes-model-training/autoencoder_b.hdf5")

使用 autoencoder_a 的编码器和 autoencoder_b 的解码器来转换人脸。

# LOADING THE ENCODER A
encoder_a = keras.Model(autoencoder_a.layers[1].input, autoencoder_a.layers[1].output)
# LOADING THE DECODER B
decoder_b = keras.Model(autoencoder_b.layers[2].input, autoencoder_b.layers[2].output)

现在让我们创建将实际人脸转换的函数。

def face_transform(source,destination,encoder,decoder):
    counter = 0
    for dirname, _, filenames in os.walk(source):
        for filename in filenames:
            # load the image
            try:
                image = cv2.imread(os.path.join(source, filename))
                image = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)
                image = image.astype('float32')
                image /= 255.0
                image = encoder.predict(np.array([image]))
                image = decoder.predict(image)
                image = cv2.normalize(image, None, alpha = 0, beta = 255, norm_type = cv2.NORM_MINMAX, dtype = cv2.CV_32F)
                image = image.astype(np.uint8)
                plt.imsave(os.path.join(destination,filename),image[0])
                counter += 1
                clear_output(wait=True)
                print("Transformation progress: "+str(counter)+"/"+str(len(filenames)))
            except:
                print('exception')
                Pass

通过发出以下命令来运行它。

face_transform('/kaggle/working/results/','/kaggle/working/transformed',encoder_a,decoder_b)
Transformation progress: 1701/1701

让我们绘制与之前显示的人脸相对应的转换后的图像。

%matplotlib inline
plt.figure()
image = cv2.imread('/kaggle/working/transformed/120.jpg')
image = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)
plt.imshow(image)
plt.show()

一旦你将转换后的 dst 脸部保存为与其对应的 src 脸部相同的名称,就可以开始换脸了。

用 dst 脸部替换 src 帧中的人脸

执行换脸操作有几种方法。你可以使用自己的脚本,或者使用现有的脚本为你节省数小时的工作。最终,你很可能会得到与你的 DIY 解决方案相同的结果,因为大多数这些算法都基于相同的原理(Delaunay 三角剖分应用于换脸)。我研究了三个流行的脚本:WuhuikaiFaceSwapSatya MallickFaceSwap,以及 Matthew Earlfaceswap

我们将使用最后一个,因为它实现了局部换脸,这在测试期间对我们来说效果更好。根据你在人脸转换期间获得的结果,你可能会发现其他方法更适合你,所以不妨都试试!现在,让我们开始实际的代码。

将建议的仓库克隆到 Notebook 的 /working 目录。

!git clone https://github.com/matthewearl/faceswap.git

请记住,我们将使用之前提到的 shape_predictor_68_face_landmarks 文件。这是一个由 dlib 库用来检测我们帧中包含人脸的区域的模型。

现在我们需要对脚本进行一些修改,使其能够在我们的 Notebook 中执行。为此,请使用 python 魔术命令 %load ./faceswap/faceswap.py 加载 faceswap.py 文件。

该文件将自动在该 Notebook 单元中打开,允许你进行修改。

文件打开后,找到 PREDICTOR_PATH 变量,并将其修改为指向你的 shape_predictor_68_face_landmarks.dat 文件的那个。在我的例子中,我将其修改为 PREDICTOR_PATH = '../input/shape-predictor-68-face-landmarksdat/shape_predictor_68_face_landmarks.dat'

其次,我们需要修改最后一行,以便将最终的换脸输出图像保存到我们需要的文件夹中,并命名为我们需要的名称。为此,请注释掉最后一行,并在其下方添加以下内容。

cv2.imwrite(sys.argv[3], output_im)

最后,要重写文件,请删除文件顶部注释掉的 python 魔术命令 %load ./faceswap/faceswap.py,并用 %%writefile ./faceswap/faceswap.py 替换它。

运行该单元,如果一切顺利,你应该会看到文件已被覆盖的消息。如果你对如何修改文件以及最终结果应该是什么样子有疑问,请查看 本文的 Notebook

现在是时候使用修改后的脚本进行换脸了。我编写了一个函数来为我们处理繁重的工作。

def massive_face_swap(source,destination,output):
    counter = 0
    for dirname, _, filenames in os.walk(source):
        for filename in filenames:
            current_src = os.path.join(dirname, filename)
            current_dst = os.path.join(destination, filename)
            current_out = os.path.join(output, filename)
            !python /kaggle/working/faceswap/faceswap.py {current_dst} {current_src} {current_out}
            clear_output(wait=True)
            print("Swap progress: "+str(counter)+"/"+str(len(filenames)-1))
            counter += 1

通过传入转换后的人脸集、帧以及你希望保存换脸后帧的相对路径来运行它。

massive_face_swap('./transformed','./frames','./final')
Swap progress: 1700/1700

让我们绘制一个示例。

%matplotlib inline
plt.figure()
image = cv2.imread('/kaggle/working/final/150.jpg')
image = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)
plt.imshow(image)
plt.show()

与原始帧进行比较。

plt.figure()
image = cv2.imread('/kaggle/working/frames/150.jpg')
image = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)
plt.imshow(image)
plt.show()

不算太差,对吧?然而,你可能会注意到人脸转换和替换存在一些不完美之处。在深度伪造创建中,这可能是因为人脸集太小,或者你没有足够多的迭代来训练模型。在我们的例子中,正如我之前提到的,我们训练了 2700 个 epoch,但通常这些模型需要训练超过 15000 次迭代。

这是我们能获得的一些最好的帧。

现在你已经看到了最后几个步骤的复杂性,是时候将所有内容整合起来,生成我们的深度伪造视频了。

视频重建:获取我们的深度伪造视频

要最终获得我们期待已久的深度伪造视频,我们只需要用生成的帧而不是原始帧来重建视频。为此,借助 OpenCV,运行以下几行代码。

frames = []
for i in range(1701):
    image = image = cv2.imread(os.path.join('/kaggle/working/final', str(i)+'.jpg'))
    height, width, layers = image.shape
    frames.append(image)
frames = np.array(frames)
videomaker = cv2.VideoWriter('/kaggle/working/deepfake.avi', cv2.VideoWriter_fourcc(*'DIVX'), 25, (width,height))
 
for frame in frames:
    videomaker.write(frame)
 
videomaker.release()

执行结束后,你可以在 /kaggle/working 目录下看到你的 deepfake.avi 文件(我的 在这里),如果你定义了那个目录的话。就到这里吧!希望这些步骤能帮助你进行这次深度伪造 DIY 创建。在 最后一篇文章 中,我们将探讨 DeepFaceLab 作为我们上一个方法的替代方案。希望在那里见到你!

© . All rights reserved.