victorisgeek commited on
Commit
b43090c
1 Parent(s): 6173074

Upload 3 files

Browse files
Files changed (3) hide show
  1. dofaker/__init__.py +7 -0
  2. dofaker/face_core.py +231 -0
  3. dofaker/pose_core.py +143 -0
dofaker/__init__.py ADDED
@@ -0,0 +1,7 @@
 
 
 
 
 
 
 
 
1
+ from .face_det import FaceAnalysis
2
+ from .face_swap import InSwapper
3
+ from .face_enhance import GFPGAN
4
+ from .super_resolution import BSRGAN
5
+ from .pose import PoseEstimator
6
+ from .face_core import FaceSwapper
7
+ from .pose_core import PoseSwapper
dofaker/face_core.py ADDED
@@ -0,0 +1,231 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+
3
+ import numpy as np
4
+ import cv2
5
+ from moviepy.editor import VideoFileClip
6
+
7
+ from .face_det import FaceAnalysis
8
+ from .super_resolution import BSRGAN
9
+ from dofaker.face_swap import get_swapper_model
10
+ from dofaker.face_enhance import GFPGAN
11
+
12
+
13
+ class FaceSwapper:
14
+
15
+ def __init__(self,
16
+ face_det_model='buffalo_l',
17
+ face_swap_model='inswapper',
18
+ image_sr_model='bsrgan',
19
+ face_enhance_model='gfpgan',
20
+ face_det_model_dir='weights/models',
21
+ face_swap_model_dir='weights/models',
22
+ image_sr_model_dir='weights/models',
23
+ face_enhance_model_dir='weights/models',
24
+ face_sim_thre=0.5,
25
+ log_iters=10,
26
+ use_enhancer=True,
27
+ use_sr=True,
28
+ scale=1):
29
+ self.face_sim_thre = face_sim_thre
30
+ self.log_iters = log_iters
31
+
32
+ self.det_model = FaceAnalysis(name=face_det_model,
33
+ root=face_det_model_dir)
34
+ self.det_model.prepare(ctx_id=1, det_size=(640, 640))
35
+
36
+ self.swapper_model = get_swapper_model(name=face_swap_model,
37
+ root=face_swap_model_dir)
38
+ if use_enhancer:
39
+ self.face_enhance = GFPGAN(name=face_enhance_model,
40
+ root=face_enhance_model_dir)
41
+ else:
42
+ self.face_enhance = None
43
+
44
+ if use_sr:
45
+ self.sr = BSRGAN(name=image_sr_model,
46
+ root=image_sr_model_dir,
47
+ scale=scale)
48
+ self.scale = scale
49
+ else:
50
+ self.sr = None
51
+ self.scale = scale
52
+
53
+ def run(self,
54
+ input_path: str,
55
+ dst_face_paths,
56
+ src_face_paths,
57
+ output_dir='output'):
58
+ if isinstance(dst_face_paths, str):
59
+ dst_face_paths = [dst_face_paths]
60
+ if isinstance(src_face_paths, str):
61
+ src_face_paths = [src_face_paths]
62
+ if input_path.lower().endswith(('jpg', 'jpeg', 'webp', 'png', 'bmp')):
63
+ return self.swap_image(input_path, dst_face_paths, src_face_paths,
64
+ output_dir)
65
+ else:
66
+ return self.swap_video(input_path, dst_face_paths, src_face_paths,
67
+ output_dir)
68
+
69
+ def swap_video(self,
70
+ input_video_path,
71
+ dst_face_paths,
72
+ src_face_paths,
73
+ output_dir='output'):
74
+ assert os.path.exists(
75
+ input_video_path), 'The input video path {} not exist.'
76
+ os.makedirs(output_dir, exist_ok=True)
77
+ src_faces = self.get_faces(src_face_paths)
78
+ if dst_face_paths is not None:
79
+ dst_faces = self.get_faces(dst_face_paths)
80
+ dst_face_embeddings = self.get_faces_embeddings(dst_faces)
81
+ assert len(dst_faces) == len(
82
+ src_faces
83
+ ), 'The detected faces in source images not equal target image faces.'
84
+
85
+ video = cv2.VideoCapture(input_video_path)
86
+ fps = video.get(cv2.CAP_PROP_FPS)
87
+ total_frames = int(video.get(cv2.CAP_PROP_FRAME_COUNT))
88
+ width = int(video.get(cv2.CAP_PROP_FRAME_WIDTH))
89
+ height = int(video.get(cv2.CAP_PROP_FRAME_HEIGHT))
90
+ frame_size = (width, height)
91
+ print('video fps: {}, total_frames: {}, width: {}, height: {}'.format(
92
+ fps, total_frames, width, height))
93
+
94
+ video_name = os.path.basename(input_video_path).split('.')[0]
95
+ four_cc = cv2.VideoWriter_fourcc('m', 'p', '4', 'v')
96
+ temp_video_path = os.path.join(output_dir,
97
+ 'temp_{}.mp4'.format(video_name))
98
+ save_video_path = os.path.join(output_dir, '{}.mp4'.format(video_name))
99
+ output_video = cv2.VideoWriter(
100
+ temp_video_path, four_cc, fps,
101
+ (int(frame_size[0] * self.scale), int(frame_size[1] * self.scale)))
102
+
103
+ i = 0
104
+ while video.isOpened():
105
+ ret, frame = video.read()
106
+ if ret:
107
+ if dst_face_paths is not None:
108
+ swapped_image = self.swap_faces(frame,
109
+ dst_face_embeddings,
110
+ src_faces=src_faces)
111
+ else:
112
+ swapped_image = self.swap_all_faces(frame,
113
+ src_faces=src_faces)
114
+ i += 1
115
+ if i % self.log_iters == 0:
116
+ print('processing {}/{}'.format(i, total_frames))
117
+ output_video.write(swapped_image)
118
+ else:
119
+ break
120
+
121
+ video.release()
122
+ output_video.release()
123
+ self.add_audio_to_video(input_video_path, temp_video_path,
124
+ save_video_path)
125
+ os.remove(temp_video_path)
126
+ return save_video_path
127
+
128
+ def swap_image(self,
129
+ image_path,
130
+ dst_face_paths,
131
+ src_face_paths,
132
+ output_dir='output'):
133
+ os.makedirs(output_dir, exist_ok=True)
134
+ src_faces = self.get_faces(src_face_paths)
135
+ if dst_face_paths is not None:
136
+ dst_faces = self.get_faces(dst_face_paths)
137
+ dst_face_embeddings = self.get_faces_embeddings(dst_faces)
138
+ assert len(dst_faces) == len(
139
+ src_faces
140
+ ), 'The detected faces in source images not equal target image faces.'
141
+
142
+ image = cv2.imread(image_path)
143
+ if dst_face_paths is not None:
144
+ swapped_image = self.swap_faces(image,
145
+ dst_face_embeddings,
146
+ src_faces=src_faces)
147
+ else:
148
+ swapped_image = self.swap_all_faces(image, src_faces=src_faces)
149
+ base_name = os.path.basename(image_path)
150
+ save_path = os.path.join(output_dir, base_name)
151
+ cv2.imwrite(save_path, swapped_image)
152
+ return save_path
153
+
154
+ def add_audio_to_video(self, src_video_path, target_video_path,
155
+ save_video_path):
156
+ audio = VideoFileClip(src_video_path).audio
157
+ target_video = VideoFileClip(target_video_path)
158
+ target_video = target_video.set_audio(audio)
159
+ target_video.write_videofile(save_video_path)
160
+ return target_video_path
161
+
162
+ def get_faces(self, image_paths):
163
+ if isinstance(image_paths, str):
164
+ image_paths = [image_paths]
165
+ faces = []
166
+ for image_path in image_paths:
167
+ image = cv2.imread(image_path)
168
+ assert image is not None, "the source image is None, please check your image {} format.".format(
169
+ image_path)
170
+ img_faces = self.det_model.get(image, max_num=1)
171
+ assert len(
172
+ img_faces
173
+ ) == 1, 'The detected face in image {} must be 1, but got {}, please ensure your image including one face.'.format(
174
+ image_path, len(img_faces))
175
+ faces += img_faces
176
+ return faces
177
+
178
+ def swap_faces(self, image, dst_face_embeddings: np.ndarray,
179
+ src_faces: list) -> np.ndarray:
180
+ res = image.copy()
181
+ image_faces = self.det_model.get(image)
182
+ if len(image_faces) == 0:
183
+ return res
184
+ image_face_embeddings = self.get_faces_embeddings(image_faces)
185
+ sim = np.dot(dst_face_embeddings, image_face_embeddings.T)
186
+
187
+ for i in range(dst_face_embeddings.shape[0]):
188
+ index = np.where(sim[i] > self.face_sim_thre)[0].tolist()
189
+ for idx in index:
190
+ res = self.swapper_model.get(res,
191
+ image_faces[idx],
192
+ src_faces[i],
193
+ paste_back=True)
194
+ if self.face_enhance is not None:
195
+ res = self.face_enhance.get(res,
196
+ image_faces[idx],
197
+ paste_back=True)
198
+
199
+ if self.sr is not None:
200
+ res = self.sr.get(res, image_format='bgr')
201
+ return res
202
+
203
+ def swap_all_faces(self, image, src_faces: list) -> np.ndarray:
204
+ assert len(
205
+ src_faces
206
+ ) == 1, 'If replace all faces in source, the number of src face should be 1, but got {}.'.format(
207
+ len(src_faces))
208
+ res = image.copy()
209
+ image_faces = self.det_model.get(image)
210
+ if len(image_faces) == 0:
211
+ return res
212
+ for image_face in image_faces:
213
+ res = self.swapper_model.get(res,
214
+ image_face,
215
+ src_faces[0],
216
+ paste_back=True)
217
+ if self.face_enhance is not None:
218
+ res = self.face_enhance.get(res, image_face, paste_back=True)
219
+ if self.sr is not None:
220
+ res = self.sr.get(res, image_format='bgr')
221
+ return res
222
+
223
+ def get_faces_embeddings(self, faces):
224
+ feats = []
225
+ for face in faces:
226
+ feats.append(face.normed_embedding)
227
+ if len(feats) == 1:
228
+ feats = np.array(feats, dtype=np.float32).reshape(1, -1)
229
+ else:
230
+ feats = np.array(feats, dtype=np.float32)
231
+ return feats
dofaker/pose_core.py ADDED
@@ -0,0 +1,143 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ import cv2
3
+
4
+ import numpy as np
5
+ from moviepy.editor import VideoFileClip
6
+
7
+ from .pose import PoseEstimator, PoseTransfer
8
+ from .face_enhance import GFPGAN
9
+ from .super_resolution import BSRGAN
10
+ from .face_det import FaceAnalysis
11
+
12
+
13
+ class PoseSwapper:
14
+
15
+ def __init__(self,
16
+ pose_estimator_name='openpose_body',
17
+ pose_estimator_model_dir='weights/models',
18
+ pose_transfer_name='pose_transfer',
19
+ pose_transfer_model_dir='weights/models',
20
+ image_sr_model='bsrgan',
21
+ image_sr_model_dir='weights/models',
22
+ face_enhance_name='gfpgan',
23
+ face_enhance_model_dir='weights/models/',
24
+ face_det_model='buffalo_l',
25
+ face_det_model_dir='weights/models',
26
+ log_iters=10,
27
+ use_enhancer=True,
28
+ use_sr=True,
29
+ scale=1):
30
+ pose_estimator = PoseEstimator(name=pose_estimator_name,
31
+ root=pose_estimator_model_dir)
32
+ self.pose_transfer = PoseTransfer(name=pose_transfer_name,
33
+ root=pose_transfer_model_dir,
34
+ pose_estimator=pose_estimator)
35
+
36
+ if use_enhancer:
37
+ self.det_model = FaceAnalysis(name=face_det_model,
38
+ root=face_det_model_dir)
39
+ self.det_model.prepare(ctx_id=1, det_size=(640, 640))
40
+ self.face_enhance = GFPGAN(name=face_enhance_name,
41
+ root=face_enhance_model_dir)
42
+ self.use_enhancer = use_enhancer
43
+
44
+ if use_sr:
45
+ self.sr_model = BSRGAN(name=image_sr_model,
46
+ root=image_sr_model_dir,
47
+ scale=scale)
48
+ self.scale = scale
49
+ else:
50
+ self.scale = 1
51
+ self.use_sr = use_sr
52
+ self.log_iters = log_iters
53
+
54
+ def run(self, input_path, target_path, output_dir='output'):
55
+ assert os.path.exists(
56
+ input_path), "The input path {} not exists.".format(input_path)
57
+ assert os.path.exists(
58
+ target_path), "The target path {} not exists.".format(target_path)
59
+ os.makedirs(output_dir, exist_ok=True)
60
+ assert input_path.lower().endswith(
61
+ ('jpg', 'jpeg', 'webp', 'png', 'bmp')
62
+ ), "pose swapper input must be image endswith ('jpg', 'jpeg', 'webp', 'png', 'bmp'), but got {}.".format(
63
+ input_path)
64
+ if target_path.lower().endswith(('jpg', 'jpeg', 'webp', 'png', 'bmp')):
65
+ return self.transfer_image(input_path, target_path, output_dir)
66
+ else:
67
+ return self.transfer_video(input_path, target_path, output_dir)
68
+
69
+ def transfer_image(self, input_path, target_path, output_dir):
70
+ source = cv2.imread(input_path)
71
+ target = cv2.imread(target_path)
72
+ transferred_image = self.transfer_pose(source,
73
+ target,
74
+ image_format='bgr')
75
+ base_name = os.path.basename(input_path)
76
+ output_path = os.path.join(output_dir, base_name)
77
+ cv2.imwrite(output_path, transferred_image)
78
+ return output_path
79
+
80
+ def transfer_video(self, input_path, target_path, output_dir):
81
+ source = cv2.imread(input_path)
82
+ video = cv2.VideoCapture(target_path)
83
+ fps = video.get(cv2.CAP_PROP_FPS)
84
+ total_frames = int(video.get(cv2.CAP_PROP_FRAME_COUNT))
85
+ height, width, _ = source.shape
86
+ frame_size = (width, height)
87
+ print('video fps: {}, total_frames: {}, width: {}, height: {}'.format(
88
+ fps, total_frames, width, height))
89
+
90
+ video_name = os.path.basename(input_path).split('.')[0]
91
+ four_cc = cv2.VideoWriter_fourcc('m', 'p', '4', 'v')
92
+ temp_video_path = os.path.join(output_dir,
93
+ 'temp_{}.mp4'.format(video_name))
94
+ save_video_path = os.path.join(output_dir, '{}.mp4'.format(video_name))
95
+ output_video = cv2.VideoWriter(
96
+ temp_video_path, four_cc, fps,
97
+ (int(frame_size[0] * self.scale), int(frame_size[1] * self.scale)))
98
+ i = 0
99
+ while video.isOpened():
100
+ ret, frame = video.read()
101
+ if ret:
102
+ transferred_image = self.transfer_pose(source,
103
+ frame,
104
+ image_format='bgr')
105
+ i += 1
106
+ if i % self.log_iters == 0:
107
+ print('processing {}/{}'.format(i, total_frames))
108
+ output_video.write(transferred_image)
109
+ else:
110
+ break
111
+
112
+ video.release()
113
+ output_video.release()
114
+ print(temp_video_path)
115
+ self.add_audio_to_video(target_path, temp_video_path, save_video_path)
116
+ os.remove(temp_video_path)
117
+ return save_video_path
118
+
119
+ def transfer_pose(self, source, target, image_format='bgr'):
120
+ transferred_image = self.pose_transfer.get(source,
121
+ target,
122
+ image_format=image_format)
123
+ if self.use_enhancer:
124
+ faces = self.det_model.get(transferred_image, max_num=1)
125
+ for face in faces:
126
+ transferred_image = self.face_enhance.get(
127
+ transferred_image,
128
+ face,
129
+ paste_back=True,
130
+ image_format=image_format)
131
+
132
+ if self.use_sr:
133
+ transferred_image = self.sr_model.get(transferred_image,
134
+ image_format=image_format)
135
+ return transferred_image
136
+
137
+ def add_audio_to_video(self, src_video_path, target_video_path,
138
+ save_video_path):
139
+ audio = VideoFileClip(src_video_path).audio
140
+ target_video = VideoFileClip(target_video_path)
141
+ target_video = target_video.set_audio(audio)
142
+ target_video.write_videofile(save_video_path)
143
+ return target_video_path