目录
- 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
scandir1.6
h5py2.7.1
Keras2.1.2
opencv-python3.3.0.10
tensorflow-gpu1.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进行计算。具体的对应关系参照下面两张图:
根据前述内容,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)
(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/
# 换成自己的路径
结果如下:
这里需要将非人脸或者非主角人脸的照片删除。
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的版本对应关系。
模型训练自动保存模型文件或者手动按回车键停止保存,停止后再次训练会自动加载之前的模型,所以不同的人脸替换,各自模型要及时转移保存。
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)
这个程序,我在别人的代码上增加了对遍历文件夹后的名称目录排序的语句,保证合成的视频帧顺序不会乱。