目录

  • 1. 前言
  • 2. 2种搭建思路
  • 3. 写在前面的坑
  • (1)版本一定要严格遵守要求;
  • (2)下载faceswap的github源码
  • (3)Anacoda也不是万能的
  • (4)tensorflow 与cuda、cudnn、python、keras的版本对应
  • 4. 利用Anacoda搭建虚拟环境
  • 4.1下载并安装Anacoda
  • 4.2 首先安装cuda和cudnn
  • 4.3 安装其他主要依赖库
  • 5. 数据预处理
  • 5.1 3种数据
  • 5.2 数据预处理
  • 6. 脸部提取
  • 7. 训练模型
  • 8. 脸部替换
  • 9. 结果处理


1. 前言

最近比较火的AI换脸,其实都源于2018年github上deepfakes 的faceswap,今天就抽空下载faceswap的代码,在Ubuntu 18.04上搭建一下工程环境。

2. 2种搭建思路

有2种搭建思路,一是直接在本机上安装各种依赖库,另一种是利用Anacoda建立虚拟环境。
由于faceswap的代码较早,依赖库的版本普遍版本号很低,尤其是tensorflow和cuda等环境,在本地用pip等根本无法安装指定版本的,而经过本人2天的测试发现,只要faceswap要求的依赖项版本号稍微差一点,就可能出现各种问题。所以最终,我还是选择了在Ubuntu18.04上利用Anacoda搭建虚拟环境,在虚拟环境种安装。
这样的优点有三: 一是随时可以推翻重来; 二是怎么折腾都不影响本机环境; 三是可以利用conda install 安装一些主要依赖库,可以自动安装其他琐碎的依赖项。

3. 写在前面的坑

(1)版本一定要严格遵守要求;

GPU版faceswap需要的主要依赖库和版本号为:
pathlib1.0.1
scandir
1.6
h5py2.7.1
Keras
2.1.2
opencv-python3.3.0.10
tensorflow-gpu
1.4.0
scikit-image
dlib
face_recognition
tqdm

(2)下载faceswap的github源码

注意一定是官方的源码:
https://github.com/deepfakes/faceswap/tree/20753a64b76a156aea17724348269d60dd525f87

github上有一个国人自己搞的 faceswap-master,一开始我下了这个,结果太多的坑,实在走不下去了,浪费了大半天的时间。

(3)Anacoda也不是万能的

因为一些需要的依赖库版本实在太老了,用 conda install 根本找不到,需要在conda虚拟环境下再用pip安装依赖项。

(4)tensorflow 与cuda、cudnn、python、keras的版本对应

这是一个大坑,一定要实现查对好,否则在调用keras函数时出错、或者无法使用GPU进行计算。具体的对应关系参照下面两张图:

face_recognition 如何配置GPU推理 faceswap gpu_ide

face_recognition 如何配置GPU推理 faceswap gpu_python_02


根据前述内容,faceswap 需要 TensorFlow-GPU=1.4.0,对应keras=2.1.2、Pythnotallow=3.5、cuda=8.0、cudnn=6.0.

4. 利用Anacoda搭建虚拟环境

参考博文如下:

https://www.jianshu.com/p/7265011ba3f2

4.1下载并安装Anacoda

(1)可以去清华源下载:
https://mirrors.tuna.tsinghua.edu.cn/help/anaconda/

(2)使用以下命令安装:

bash ~/Downloads/Anaconda3-*-Linux-x86_64.sh 
# *号对应下载的版本号

(3)环境变量配置
在Anaconda安装过程中会让你选择是否修改环境变量,主要要输入“y”,否则需要手动进行修改,运行以下命令:

export PATH="/home/anaconda3/bin:$PATH"
source ~/.bashrc
# 环境变量有问题时,输入 conda命令会提示你如何手动修改

(4)创建并激活虚拟环境,输入以下命令:

conda create -n faceswap python=3.5
conda activate faceswap
# 激活 faceswap 虚拟环境后,你的命令行前面会多出虚拟环境的名称 (faceswap)

face_recognition 如何配置GPU推理 faceswap gpu_虚拟环境_03

(5)万一环境搞砸了,直接删除重来,输入以下命令删除faceswap虚拟环境:

conda remove -n faceswap --all
# 如果想卸载Anaconda,直接 sudo rm -rf */anaconda3

4.2 首先安装cuda和cudnn

(1) 首先根据上一步激活 faceswap 的虚拟环境;
(2)安装cuda8.0,输入以下命令:

conda install cudatoolkit=8.0 -c https://mirrors.tuna.tsinghua.edu.cn/anaconda/pkgs/free/linux-64/
# 这里也可以在anaconda安装完成后替换一下源,具体操作见上述参考博文

(3)安装cudnn6.0,输入以下命令:

conda install cudnn=6.0 -c https://mirrors.tuna.tsinghua.edu.cn/anaconda/pkgs/free/linux-64/

(4)注意,装完cuda和cudnn后,在虚拟环境中用常规的方法查到的是本机的版本,比如:

cat /usr/local/cuda/version.txt
cat /usr/local/cuda/include/cudnn.h | grep CUDNN_MAJOR -A 2

只要安装过程中不报错就行。

4.3 安装其他主要依赖库

tensorflow、keras、opencv等直接用conda install 会找不到对应的版本,所以这里直接在虚拟环境下使用pip安装,输入以下命令:

pip install pathlib==1.0.1 scandir==1.6 h5py==2.7.1 Keras==2.1.2 opencv-python==3.3.0.10 tensorflow-gpu==1.4.0 scikit-image dlib face_recognition tqdm
# 如果失败了一般是网不好,可以加个源 -i https://pypi.tuna.tsinghua.edu.cn/simple/
# 或者可以增加等待时间 --timeout=1000

5. 数据预处理

5.1 3种数据

假设我们要用B替换A,那么我们需要3种数据:
(1)B的训练数据:最好只有B一个人的、各个角度和衣服的图片或视频;
(2)A的训练数据:最好只有A一个人的、各个角度和衣服的图片或视频;
(3)想要替换的、原始的、有B的图片或视频。
注意faceswap原版是不支持视频的,我自己写了2个程序分别将视频转成帧图片和将帧图片合并成视频。

5.2 数据预处理

(1)在faceswap 源码的工程根目录下新建文件夹data,并根据使用次数建立相应的额test文件夹,代码如下:

cd data
mkdir test*
d test*
mkdir srcvideo srcpic srcface targetvideo targetpic targetface inputvideo inputpic outpic
# * 代表了次数,比如今天使用是test1

(2)拷贝数据
拷贝A的训练视频到 ‘/home/wz/Documents/faceswap-master/data/test*/srcvideo/’ 路径;
拷贝B的训练数据到 ‘/home/wz/Documents/faceswap-master/data/test*/targetvideo/’ 路径;
拷贝需要被替换的、含有A的视频到 ‘/home/wz/Documents/faceswap-master/data/test*/inputvideo/’ 路径。

(3)修改video2pic.py中的’videopath’ 和 ‘picpath’,分别修改运行3次,以便将B的训练视频、A的训练视频和需要被替换的视频转成图片。代码如下:

# video2pic.py
import cv2
import os
def save_img():
    video_path = '/home/wz/Documents/faceswap/data/test1/inputvideo/'
    pic_path = '/home/wz/Documents/faceswap/data/test1/inputpic/'
    videos = os.listdir(video_path)
    count = 0
    for video_name in videos:
        file_name = video_name.split('.')[0]
        #folder_name = video_path + file_name
        #os.makedirs(folder_name,exist_ok=True)
        vc = cv2.VideoCapture(video_path+video_name) #读入视频文件
        c=0
        rval=vc.isOpened()

        while rval:   #循环读取视频帧
            c = c + 1
            rval, frame = vc.read()
            #pic_path = folder_name+'/'
            if rval:
            # 通过修改 c%* == 0,来选择跳几帧图像
                if c%1==0:
                    #height, width = frame.shape[:2] 
                    #frame = cv2.resize(frame,(512,int(height*(512/width))))
                    print(pic_path + file_name+ '_'+str(c) + '.jpg')
                    count = count + 1
                    length=len(str(count))
                    if length ==1:
                        COUNT = '0000'+str(count)
                    elif length == 2:
                        COUNT = '000'+str(count)
                    elif length == 3:
                        COUNT = '00'+str(count)
                    elif length == 4:
                        COUNT = '0'+str(count)
                    else:
                        COUNT = str(count)
                    
                    cv2.imwrite(pic_path + COUNT + '.jpg', frame) #存储为图像,保存名为 文件夹名_数字(第几个文件).jpg
                else:
                    continue
            else:
                break
        vc.release()
        print('save_success')
        #print(folder_name)

save_img()

这里还需要将图片中非人脸的照片手动删除。

这个程序,我在别人的代码上增加了5位数编码命名的过程,以便在后续视频合成的时候有顺序可寻。原始代码出自:(找不到了)

6. 脸部提取

还需要将转成图片的训练数据将脸部裁剪出来进行模型训练,这一步需要在conda虚拟环境中进行,代码如下:

python3 faceswap.py extract -i /home/Documents/faceswap/data/test*/srcpic/ -o /home/Documents/faceswap/data/test*/srcface/
python3 faceswap.py extract -i /home/Documents/faceswap/data/test*/targetpic/ -o /home/Documents/faceswap/data/test*/targetface/
# 换成自己的路径

结果如下:

face_recognition 如何配置GPU推理 faceswap gpu_python_04


这里需要将非人脸或者非主角人脸的照片删除。

7. 训练模型

在虚拟环境中输入以下命令:

python3 faceswap.py train -A /home/Documents/faceswap/data/test*/srcface/ -B /home/Documents/faceswap/data/test*/targetface/
# 换成自己的额数据路径
# 切记 A是被替换的人脸数据,B是用来替换的人脸数据

如果这一步出错或者不能使用GPU训练,检查上述tensorflow、cuda、cudnn、keras和python的版本对应关系。

模型训练自动保存模型文件或者手动按回车键停止保存,停止后再次训练会自动加载之前的模型,所以不同的人脸替换,各自模型要及时转移保存。

face_recognition 如何配置GPU推理 faceswap gpu_ide_05


face_recognition 如何配置GPU推理 faceswap gpu_ide_06

8. 脸部替换

在虚拟环境中输入以下命令:

python3 faceswap.py convert -i /home/Documents/faceswap/data/test1/inputpic/ -o /home/Documents/faceswap/data/test1/outpic/ -m /home/Documents/faceswap/models/
# 换成自己的路径

此时的结果为图片。
转换时可以添加很多指定参数,推荐开启“-S --seamless -D --cnn”,使得替换结果看起来很自然。
可指定的参数源码如下:

def add_optional_arguments(self, parser):
        parser.add_argument('-m', '--model-dir',
                            action=FullPaths,
                            dest="model_dir",
                            default="models",
                            help="Model directory. A directory containing the trained model \
                    you wish to process. Defaults to 'models'")

        parser.add_argument('-t', '--trainer',
                            type=str,
                            choices=("Original", "LowMem", "GAN"), # case sensitive because this is used to load a plug-in.
                            default="Original",
                            help="Select the trainer that was used to create the model.")
                            
        parser.add_argument('-s', '--swap-model',
                            action="store_true",
                            dest="swap_model",
                            default=False,
                            help="Swap the model. Instead of A -> B, swap B -> A.")

        parser.add_argument('-c', '--converter',
                            type=str,
                            choices=("Masked", "Adjust", "GAN"), # case sensitive because this is used to load a plugin.
                            default="Masked",
                            help="Converter to use.")

        parser.add_argument('-D', '--detector',
                            type=str,
                            choices=("hog", "cnn"), # case sensitive because this is used to load a plugin.
                            default="hog",
                            help="Detector to use. 'cnn' detects much more angles but will be much more resource intensive and may fail on large files.")

        parser.add_argument('-fr', '--frame-ranges',
                            nargs="+",
                            type=str,
                            help="frame ranges to apply transfer to e.g. For frames 10 to 50 and 90 to 100 use --frame-ranges 10-50 90-100. \
                            Files must have the frame-number as the last number in the name!"
                            )

        parser.add_argument('-d', '--discard-frames',
                            action="store_true",
                            dest="discard_frames",
                            default=False,
                            help="When used with --frame-ranges discards frames that are not processed instead of writing them out unchanged."
                            )

        parser.add_argument('-f', '--filter',
                            type=str,
                            dest="filter",
                            default="filter.jpg",
                            help="Reference image for the person you want to process. Should be a front portrait"
                            )

        parser.add_argument('-b', '--blur-size',
                            type=int,
                            default=2,
                            help="Blur size. (Masked converter only)")


        parser.add_argument('-S', '--seamless',
                            action="store_true",
                            dest="seamless_clone",
                            default=False,
                            help="Seamless mode. (Masked converter only)")

        parser.add_argument('-M', '--mask-type',
                            type=str.lower, #lowercase this, because its just a string later on.
                            dest="mask_type",
                            choices=["rect", "facehull", "facehullandrect"],
                            default="facehullandrect",
                            help="Mask to use to replace faces. (Masked converter only)")

        parser.add_argument('-e', '--erosion-kernel-size',
                            dest="erosion_kernel_size",
                            type=int,
                            default=None,
                            help="Erosion kernel size. (Masked converter only)")

        parser.add_argument('-sm', '--smooth-mask',
                            action="store_true",
                            dest="smooth_mask",
                            default=True,
                            help="Smooth mask (Adjust converter only)")

        parser.add_argument('-aca', '--avg-color-adjust',
                            action="store_true",
                            dest="avg_color_adjust",
                            default=True,
                            help="Average color adjust. (Adjust converter only)")
        return parser

9. 结果处理

需要将上述步骤的结果图片合成为视频,代码如下:

# pic2video.py
import os
import cv2
def makeVideo(path, size):
    filelist = os.listdir(path)
    filelist2 = [os.path.join(path, i) for i in filelist]
    print(filelist2)
    fps = 30  # 我设定位视频每秒1帧,可以自行修改
    # size = (1920, 1080)  # 需要转为视频的图片的尺寸,这里必须和图片尺寸一致
    video = cv2.VideoWriter(path + "result.avi", cv2.VideoWriter_fourcc('M', 'J', 'P', 'G'), fps,
                            size) 

    filelist2.sort()
    for item in filelist2:
        if item.endswith('.jpg'):
            print(item)
            img = cv2.imread(item)
            video.write(img)

    video.release()
    cv2.destroyAllWindows()
    print('视频合成生成完成啦')

if __name__ == '__main__':
    # 换成你自己的路径
    path = r'/home/wz/Documents/faceswap/data/test1/inputpic/' 
    # 需要转为视频的图片的尺寸,必须所有图片大小一样,不然无法合并成功
    # 手动指定尺寸
    size = (720, 1280)
    makeVideo(path, size)

这个程序,我在别人的代码上增加了对遍历文件夹后的名称目录排序的语句,保证合成的视频帧顺序不会乱。