root commited on
Commit
aab5d4e
·
1 Parent(s): 3233c6e

fixed roop

Browse files
Files changed (2) hide show
  1. roop-unleashed/roop/core.py +324 -127
  2. roop/core.py +127 -324
roop-unleashed/roop/core.py CHANGED
@@ -2,26 +2,29 @@
2
 
3
  import os
4
  import sys
5
- # single thread doubles cuda performance - needs to be set before torch import
6
- if any(arg.startswith('--execution-provider') for arg in sys.argv):
7
- os.environ['OMP_NUM_THREADS'] = '1'
8
- # reduce tensorflow log level
9
- os.environ['TF_CPP_MIN_LOG_LEVEL'] = '2'
10
  import warnings
11
  from typing import List
12
  import platform
13
  import signal
14
- import shutil
15
- import argparse
16
  import torch
17
  import onnxruntime
18
- import tensorflow
19
-
20
  import roop.globals
21
  import roop.metadata
22
- from roop.predicter import predict_image, predict_video
23
- from roop.processors.frame.core import get_frame_processors_modules
24
- from roop.utilities import has_image_extension, is_image, is_video, detect_fps, create_video, extract_frames, get_temp_frame_paths, restore_audio, create_temp, move_temp, clean_temp, normalize_output_path
 
 
 
 
 
 
 
 
25
 
26
  if 'ROCMExecutionProvider' in roop.globals.execution_providers:
27
  del torch
@@ -30,40 +33,21 @@ warnings.filterwarnings('ignore', category=FutureWarning, module='insightface')
30
  warnings.filterwarnings('ignore', category=UserWarning, module='torchvision')
31
 
32
 
33
- def parse_args() -> None:
34
- signal.signal(signal.SIGINT, lambda signal_number, frame: destroy())
35
- program = argparse.ArgumentParser(formatter_class=lambda prog: argparse.HelpFormatter(prog, max_help_position=100))
36
- program.add_argument('-s', '--source', help='select an source image', dest='source_path')
37
- program.add_argument('-t', '--target', help='select an target image or video', dest='target_path')
38
- program.add_argument('-o', '--output', help='select output file or directory', dest='output_path')
39
- program.add_argument('--frame-processor', help='frame processors (choices: face_swapper, face_enhancer, ...)', dest='frame_processor', default=['face_swapper'], nargs='+')
40
- program.add_argument('--keep-fps', help='keep original fps', dest='keep_fps', action='store_true', default=False)
41
- program.add_argument('--keep-audio', help='keep original audio', dest='keep_audio', action='store_true', default=True)
42
- program.add_argument('--keep-frames', help='keep temporary frames', dest='keep_frames', action='store_true', default=False)
43
- program.add_argument('--many-faces', help='process every face', dest='many_faces', action='store_true', default=False)
44
- program.add_argument('--video-encoder', help='adjust output video encoder', dest='video_encoder', default='libx264', choices=['libx264', 'libx265', 'libvpx-vp9'])
45
- program.add_argument('--video-quality', help='adjust output video quality', dest='video_quality', type=int, default=18, choices=range(52), metavar='[0-51]')
46
- program.add_argument('--max-memory', help='maximum amount of RAM in GB', dest='max_memory', type=int, default=suggest_max_memory())
47
- program.add_argument('--execution-provider', help='available execution provider (choices: cpu, ...)', dest='execution_provider', default=['cpu'], choices=suggest_execution_providers(), nargs='+')
48
- program.add_argument('--execution-threads', help='number of execution threads', dest='execution_threads', type=int, default=suggest_execution_threads())
49
- program.add_argument('-v', '--version', action='version', version=f'{roop.metadata.name} {roop.metadata.version}')
50
-
51
- args = program.parse_args()
52
-
53
- roop.globals.source_path = args.source_path
54
- roop.globals.target_path = args.target_path
55
- roop.globals.output_path = normalize_output_path(roop.globals.source_path, roop.globals.target_path, args.output_path)
56
- roop.globals.frame_processors = args.frame_processor
57
- roop.globals.headless = args.source_path or args.target_path or args.output_path
58
- roop.globals.keep_fps = args.keep_fps
59
- roop.globals.keep_audio = args.keep_audio
60
- roop.globals.keep_frames = args.keep_frames
61
- roop.globals.many_faces = args.many_faces
62
- roop.globals.video_encoder = args.video_encoder
63
- roop.globals.video_quality = args.video_quality
64
- roop.globals.max_memory = args.max_memory
65
- roop.globals.execution_providers = decode_execution_providers(args.execution_provider)
66
- roop.globals.execution_threads = args.execution_threads
67
 
68
 
69
  def encode_execution_providers(execution_providers: List[str]) -> List[str]:
@@ -77,8 +61,8 @@ def decode_execution_providers(execution_providers: List[str]) -> List[str]:
77
 
78
  def suggest_max_memory() -> int:
79
  if platform.system().lower() == 'darwin':
80
- return 10
81
- return 14
82
 
83
 
84
  def suggest_execution_providers() -> List[str]:
@@ -94,12 +78,6 @@ def suggest_execution_threads() -> int:
94
 
95
 
96
  def limit_resources() -> None:
97
- # prevent tensorflow memory leak
98
- gpus = tensorflow.config.experimental.list_physical_devices('GPU')
99
- for gpu in gpus:
100
- tensorflow.config.experimental.set_virtual_device_configuration(gpu, [
101
- tensorflow.config.experimental.VirtualDeviceConfiguration(memory_limit=1024)
102
- ])
103
  # limit memory usage
104
  if roop.globals.max_memory:
105
  memory = roop.globals.max_memory * 1024 ** 3
@@ -107,7 +85,7 @@ def limit_resources() -> None:
107
  memory = roop.globals.max_memory * 1024 ** 6
108
  if platform.system().lower() == 'windows':
109
  import ctypes
110
- kernel32 = ctypes.windll.kernel32
111
  kernel32.SetProcessWorkingSetSize(-1, ctypes.c_size_t(memory), ctypes.c_size_t(memory))
112
  else:
113
  import resource
@@ -115,95 +93,314 @@ def limit_resources() -> None:
115
 
116
 
117
  def release_resources() -> None:
118
- if 'CUDAExecutionProvider' in roop.globals.execution_providers:
119
- torch.cuda.empty_cache()
 
 
 
 
 
 
120
 
121
 
122
  def pre_check() -> bool:
123
  if sys.version_info < (3, 9):
124
  update_status('Python version is not supported - please upgrade to 3.9 or higher.')
125
  return False
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
126
  if not shutil.which('ffmpeg'):
127
  update_status('ffmpeg is not installed.')
128
- return False
129
  return True
130
 
131
 
132
- def update_status(message: str, scope: str = 'ROOP.CORE') -> None:
133
- print(f'[{scope}] {message}')
134
-
135
-
136
- def start() -> None:
137
- for frame_processor in get_frame_processors_modules(roop.globals.frame_processors):
138
- if not frame_processor.pre_start():
139
- return
140
- # process image to image
141
- if has_image_extension(roop.globals.target_path):
142
- if predict_image(roop.globals.target_path):
143
- destroy()
144
- shutil.copy2(roop.globals.target_path, roop.globals.output_path)
145
- for frame_processor in get_frame_processors_modules(roop.globals.frame_processors):
146
- update_status('Progressing...', frame_processor.NAME)
147
- frame_processor.process_image(roop.globals.source_path, roop.globals.output_path, roop.globals.output_path)
148
- frame_processor.post_process()
149
- release_resources()
150
- if is_image(roop.globals.target_path):
151
- update_status('Processing to image succeed!')
152
- else:
153
- update_status('Processing to image failed!')
154
- return
155
- # process image to videos
156
- if predict_video(roop.globals.target_path):
157
- destroy()
158
- update_status('Creating temp resources...')
159
- create_temp(roop.globals.target_path)
160
- update_status('Extracting frames...')
161
- extract_frames(roop.globals.target_path)
162
- temp_frame_paths = get_temp_frame_paths(roop.globals.target_path)
163
- for frame_processor in get_frame_processors_modules(roop.globals.frame_processors):
164
- update_status('Progressing...', frame_processor.NAME)
165
- frame_processor.process_video(roop.globals.source_path, temp_frame_paths)
166
- frame_processor.post_process()
167
- release_resources()
168
- # handles fps
169
- if roop.globals.keep_fps:
170
- update_status('Detecting fps...')
171
- fps = detect_fps(roop.globals.target_path)
172
- update_status(f'Creating video with {fps} fps...')
173
- create_video(roop.globals.target_path, fps)
174
- else:
175
- update_status('Creating video with 30.0 fps...')
176
- create_video(roop.globals.target_path)
177
- # handle audio
178
- if roop.globals.keep_audio:
179
- if roop.globals.keep_fps:
180
- update_status('Restoring audio...')
181
- else:
182
- update_status('Restoring audio might cause issues as fps are not kept...')
183
- restore_audio(roop.globals.target_path, roop.globals.output_path)
184
- else:
185
- move_temp(roop.globals.target_path, roop.globals.output_path)
186
- # clean and validate
187
- clean_temp(roop.globals.target_path)
188
- if is_video(roop.globals.target_path):
189
- update_status('Processing to video succeed!')
190
- else:
191
- update_status('Processing to video failed!')
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
192
 
193
 
194
  def destroy() -> None:
195
  if roop.globals.target_path:
196
- clean_temp(roop.globals.target_path)
197
- quit()
 
198
 
199
 
200
  def run() -> None:
201
- parse_args()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
202
  if not pre_check():
203
  return
204
- for frame_processor in get_frame_processors_modules(roop.globals.frame_processors):
205
- if not frame_processor.pre_check():
206
- return
207
- limit_resources()
208
- if roop.globals.headless:
209
- start()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2
 
3
  import os
4
  import sys
5
+ import shutil
6
+ import argparse
 
 
 
7
  import warnings
8
  from typing import List
9
  import platform
10
  import signal
 
 
11
  import torch
12
  import onnxruntime
13
+ import pathlib
14
+ from time import time
15
  import roop.globals
16
  import roop.metadata
17
+ import roop.utilities as util
18
+ import roop.util_ffmpeg as ffmpeg
19
+ from settings import Settings
20
+ from roop.face_util import extract_face_images
21
+ from roop.ProcessEntry import ProcessEntry
22
+ from roop.ProcessMgr import ProcessMgr
23
+ from roop.ProcessOptions import ProcessOptions
24
+ from roop.capturer import get_video_frame_total
25
+ from roop.FaceSet import FaceSet
26
+
27
+ process_mgr = None
28
 
29
  if 'ROCMExecutionProvider' in roop.globals.execution_providers:
30
  del torch
 
33
  warnings.filterwarnings('ignore', category=UserWarning, module='torchvision')
34
 
35
 
36
+ def parse_args():
37
+ parser = argparse.ArgumentParser(description="Run Roop from the command line")
38
+ parser.add_argument('--source_path', type=str, required=True, help="Path to the source file")
39
+ parser.add_argument('--target_path', type=str, required=True, help="Path to the target file")
40
+ parser.add_argument('--output_path', type=str, required=True, help="Path to save the output file")
41
+ parser.add_argument('--execution_provider', type=str, default='CPUExecutionProvider', help="Execution provider for ONNX runtime")
42
+ parser.add_argument('--max_memory', type=int, default=None, help="Max memory to use (in GB)")
43
+ parser.add_argument('--distance_threshold', type=float, default=0.6, help="Distance threshold for face matching")
44
+ parser.add_argument('--blend_ratio', type=float, default=0.5, help="Blend ratio for face swapping")
45
+ parser.add_argument('--face_swap_mode', type=str, default='replace', help="Face swap mode")
46
+ parser.add_argument('--output_image_format', type=str, default='png', help="Output image format")
47
+ parser.add_argument('--output_video_format', type=str, default='mp4', help="Output video format")
48
+ parser.add_argument('--execution_threads', type=int, default=8, help="Number of threads to use for execution")
49
+ parser.add_argument('--skip_audio', action='store_true', help="Skip audio when processing video")
50
+ return parser.parse_args()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
51
 
52
 
53
  def encode_execution_providers(execution_providers: List[str]) -> List[str]:
 
61
 
62
  def suggest_max_memory() -> int:
63
  if platform.system().lower() == 'darwin':
64
+ return 4
65
+ return 16
66
 
67
 
68
  def suggest_execution_providers() -> List[str]:
 
78
 
79
 
80
  def limit_resources() -> None:
 
 
 
 
 
 
81
  # limit memory usage
82
  if roop.globals.max_memory:
83
  memory = roop.globals.max_memory * 1024 ** 3
 
85
  memory = roop.globals.max_memory * 1024 ** 6
86
  if platform.system().lower() == 'windows':
87
  import ctypes
88
+ kernel32 = ctypes.windll.kernel32 # type: ignore[attr-defined]
89
  kernel32.SetProcessWorkingSetSize(-1, ctypes.c_size_t(memory), ctypes.c_size_t(memory))
90
  else:
91
  import resource
 
93
 
94
 
95
  def release_resources() -> None:
96
+ import gc
97
+ global process_mgr
98
+
99
+ if process_mgr is not None:
100
+ process_mgr.release_resources()
101
+ process_mgr = None
102
+
103
+ gc.collect()
104
 
105
 
106
  def pre_check() -> bool:
107
  if sys.version_info < (3, 9):
108
  update_status('Python version is not supported - please upgrade to 3.9 or higher.')
109
  return False
110
+
111
+ download_directory_path = util.resolve_relative_path('../models')
112
+ util.conditional_download(download_directory_path, ['https://huggingface.co/countfloyd/deepfake/resolve/main/inswapper_128.onnx'])
113
+ util.conditional_download(download_directory_path, ['https://huggingface.co/countfloyd/deepfake/resolve/main/GFPGANv1.4.onnx'])
114
+ util.conditional_download(download_directory_path, ['https://github.com/csxmli2016/DMDNet/releases/download/v1/DMDNet.pth'])
115
+ util.conditional_download(download_directory_path, ['https://huggingface.co/countfloyd/deepfake/resolve/main/GPEN-BFR-512.onnx'])
116
+ util.conditional_download(download_directory_path, ['https://huggingface.co/countfloyd/deepfake/resolve/main/restoreformer_plus_plus.onnx'])
117
+ util.conditional_download(download_directory_path, ['https://huggingface.co/countfloyd/deepfake/resolve/main/xseg.onnx'])
118
+ download_directory_path = util.resolve_relative_path('../models/CLIP')
119
+ util.conditional_download(download_directory_path, ['https://huggingface.co/countfloyd/deepfake/resolve/main/rd64-uni-refined.pth'])
120
+ download_directory_path = util.resolve_relative_path('../models/CodeFormer')
121
+ util.conditional_download(download_directory_path, ['https://huggingface.co/countfloyd/deepfake/resolve/main/CodeFormerv0.1.onnx'])
122
+ download_directory_path = util.resolve_relative_path('../models/Frame')
123
+ util.conditional_download(download_directory_path, ['https://huggingface.co/countfloyd/deepfake/resolve/main/deoldify_artistic.onnx'])
124
+ util.conditional_download(download_directory_path, ['https://huggingface.co/countfloyd/deepfake/resolve/main/deoldify_stable.onnx'])
125
+ util.conditional_download(download_directory_path, ['https://huggingface.co/countfloyd/deepfake/resolve/main/isnet-general-use.onnx'])
126
+ util.conditional_download(download_directory_path, ['https://huggingface.co/countfloyd/deepfake/resolve/main/real_esrgan_x4.onnx'])
127
+ util.conditional_download(download_directory_path, ['https://huggingface.co/countfloyd/deepfake/resolve/main/real_esrgan_x2.onnx'])
128
+ util.conditional_download(download_directory_path, ['https://huggingface.co/countfloyd/deepfake/resolve/main/lsdir_x4.onnx'])
129
+
130
  if not shutil.which('ffmpeg'):
131
  update_status('ffmpeg is not installed.')
 
132
  return True
133
 
134
 
135
+ def update_status(message: str) -> None:
136
+ print(message)
137
+
138
+
139
+ def get_processing_plugins(masking_engine):
140
+ processors = {"faceswap": {}}
141
+ if masking_engine is not None:
142
+ processors.update({masking_engine: {}})
143
+
144
+ if roop.globals.selected_enhancer == 'GFPGAN':
145
+ processors.update({"gfpgan": {}})
146
+ elif roop.globals.selected_enhancer == 'Codeformer':
147
+ processors.update({"codeformer": {}})
148
+ elif roop.globals.selected_enhancer == 'DMDNet':
149
+ processors.update({"dmdnet": {}})
150
+ elif roop.globals.selected_enhancer == 'GPEN':
151
+ processors.update({"gpen": {}})
152
+ elif roop.globals.selected_enhancer == 'Restoreformer++':
153
+ processors.update({"restoreformer++": {}})
154
+ return processors
155
+
156
+
157
+ def live_swap(frame, options):
158
+ global process_mgr
159
+
160
+ if frame is None:
161
+ return frame
162
+
163
+ if process_mgr is None:
164
+ process_mgr = ProcessMgr(None)
165
+
166
+ process_mgr.initialize(roop.globals.INPUT_FACESETS, roop.globals.TARGET_FACES, options)
167
+ newframe = process_mgr.process_frame(frame)
168
+ if newframe is None:
169
+ return frame
170
+ return newframe
171
+
172
+
173
+ def batch_process_regular(files: List[ProcessEntry], masking_engine: str, new_clip_text: str, use_new_method, imagemask, num_swap_steps, progress, selected_index=0) -> None:
174
+ global process_mgr
175
+
176
+ release_resources()
177
+ limit_resources()
178
+ if process_mgr is None:
179
+ process_mgr = ProcessMgr(progress)
180
+ mask = imagemask["layers"][0] if imagemask is not None else None
181
+ if len(roop.globals.INPUT_FACESETS) <= selected_index:
182
+ selected_index = 0
183
+ options = ProcessOptions(get_processing_plugins(masking_engine), roop.globals.distance_threshold, roop.globals.blend_ratio, roop.globals.face_swap_mode, selected_index, new_clip_text, mask, num_swap_steps, False)
184
+ process_mgr.initialize(roop.globals.INPUT_FACESETS, roop.globals.TARGET_FACES, options)
185
+ batch_process(files, use_new_method)
186
+ return
187
+
188
+
189
+ def batch_process_with_options(files: List[ProcessEntry], options, progress):
190
+ global process_mgr
191
+
192
+ release_resources()
193
+ limit_resources()
194
+ if process_mgr is None:
195
+ process_mgr = ProcessMgr(progress)
196
+ process_mgr.initialize(roop.globals.INPUT_FACESETS, roop.globals.TARGET_FACES, options)
197
+ roop.globals.keep_frames = False
198
+ roop.globals.wait_after_extraction = False
199
+ roop.globals.skip_audio = False
200
+ batch_process(files, True)
201
+
202
+
203
+ def batch_process(files: List[ProcessEntry], use_new_method) -> None:
204
+ global process_mgr
205
+
206
+ roop.globals.processing = True
207
+
208
+ max_threads = suggest_execution_threads()
209
+ if max_threads == 1:
210
+ roop.globals.execution_threads = 1
211
+
212
+ imagefiles: List[ProcessEntry] = []
213
+ videofiles: List[ProcessEntry] = []
214
+
215
+ update_status('Sorting videos/images')
216
+
217
+ for index, f in enumerate(files):
218
+ fullname = f.filename
219
+ if util.has_image_extension(fullname):
220
+ destination = util.get_destfilename_from_path(fullname, roop.globals.output_path, f'.{roop.globals.CFG.output_image_format}')
221
+ destination = util.replace_template(destination, index=index)
222
+ pathlib.Path(os.path.dirname(destination)).mkdir(parents=True, exist_ok=True)
223
+ f.finalname = destination
224
+ imagefiles.append(f)
225
+
226
+ elif util.is_video(fullname) or util.has_extension(fullname, ['gif']):
227
+ destination = util.get_destfilename_from_path(fullname, roop.globals.output_path, f'__temp.{roop.globals.CFG.output_video_format}')
228
+ f.finalname = destination
229
+ videofiles.append(f)
230
+
231
+ if len(imagefiles) > 0:
232
+ update_status('Processing image(s)')
233
+ origimages = []
234
+ fakeimages = []
235
+ for f in imagefiles:
236
+ origimages.append(f.filename)
237
+ fakeimages.append(f.finalname)
238
+
239
+ process_mgr.run_batch(origimages, fakeimages, roop.globals.execution_threads)
240
+ origimages.clear()
241
+ fakeimages.clear()
242
+
243
+ if len(videofiles) > 0:
244
+ for index, v in enumerate(videofiles):
245
+ if not roop.globals.processing:
246
+ end_processing('Processing stopped!')
247
+ return
248
+ fps = v.fps if v.fps > 0 else util.detect_fps(v.filename)
249
+ if v.endframe == 0:
250
+ v.endframe = get_video_frame_total(v.filename)
251
+
252
+ update_status(f'Creating {os.path.basename(v.finalname)} with {fps} FPS...')
253
+ start_processing = time()
254
+ if roop.globals.keep_frames or not use_new_method:
255
+ util.create_temp(v.filename)
256
+ update_status('Extracting frames...')
257
+ ffmpeg.extract_frames(v.filename, v.startframe, v.endframe, fps)
258
+ if not roop.globals.processing:
259
+ end_processing('Processing stopped!')
260
+ return
261
+
262
+ temp_frame_paths = util.get_temp_frame_paths(v.filename)
263
+ process_mgr.run_batch(temp_frame_paths, temp_frame_paths, roop.globals.execution_threads)
264
+ if not roop.globals.processing:
265
+ end_processing('Processing stopped!')
266
+ return
267
+ if roop.globals.wait_after_extraction:
268
+ extract_path = os.path.dirname(temp_frame_paths[0])
269
+ util.open_folder(extract_path)
270
+ input("Press any key to continue...")
271
+ print("Resorting frames to create video")
272
+ util.sort_rename_frames(extract_path)
273
+
274
+ ffmpeg.create_video(v.filename, v.finalname, fps)
275
+ if not roop.globals.keep_frames:
276
+ util.delete_temp_frames(temp_frame_paths[0])
277
+ else:
278
+ if util.has_extension(v.filename, ['gif']):
279
+ skip_audio = True
280
+ else:
281
+ skip_audio = roop.globals.skip_audio
282
+ process_mgr.run_batch_inmem(v.filename, v.finalname, v.startframe, v.endframe, fps, roop.globals.execution_threads, skip_audio)
283
+
284
+ if not roop.globals.processing:
285
+ end_processing('Processing stopped!')
286
+ return
287
+
288
+ video_file_name = v.finalname
289
+ if os.path.isfile(video_file_name):
290
+ destination = ''
291
+ if util.has_extension(v.filename, ['gif']):
292
+ gifname = util.get_destfilename_from_path(v.filename, roop.globals.output_path, '.gif')
293
+ destination = util.replace_template(gifname, index=index)
294
+ pathlib.Path(os.path.dirname(destination)).mkdir(parents=True, exist_ok=True)
295
+
296
+ update_status('Creating final GIF')
297
+ ffmpeg.create_gif_from_video(video_file_name, destination)
298
+ if os.path.isfile(destination):
299
+ os.remove(video_file_name)
300
+ else:
301
+ skip_audio = roop.globals.skip_audio
302
+ destination = util.replace_template(video_file_name, index=index)
303
+ pathlib.Path(os.path.dirname(destination)).mkdir(parents=True, exist_ok=True)
304
+
305
+ if not skip_audio:
306
+ ffmpeg.restore_audio(video_file_name, v.filename, v.startframe, v.endframe, destination)
307
+ if os.path.isfile(destination):
308
+ os.remove(video_file_name)
309
+ else:
310
+ shutil.move(video_file_name, destination)
311
+ update_status(f'\nProcessing {os.path.basename(destination)} took {time() - start_processing} secs')
312
+
313
+ else:
314
+ update_status(f'Failed processing {os.path.basename(v.finalname)}!')
315
+ end_processing('Finished')
316
+
317
+
318
+ def end_processing(msg: str):
319
+ update_status(msg)
320
+ roop.globals.target_folder_path = None
321
+ release_resources()
322
 
323
 
324
  def destroy() -> None:
325
  if roop.globals.target_path:
326
+ util.clean_temp(roop.globals.target_path)
327
+ release_resources()
328
+ sys.exit()
329
 
330
 
331
  def run() -> None:
332
+ args = parse_args()
333
+
334
+ roop.globals.source_path = args.source_path
335
+ roop.globals.target_path = args.target_path
336
+ roop.globals.output_path = args.output_path
337
+ roop.globals.execution_providers = decode_execution_providers([args.execution_provider])
338
+ roop.globals.max_memory = args.max_memory
339
+ roop.globals.distance_threshold = args.distance_threshold
340
+ roop.globals.blend_ratio = args.blend_ratio
341
+ roop.globals.face_swap_mode = args.face_swap_mode
342
+ roop.globals.CFG = Settings('config.yaml')
343
+ roop.globals.execution_threads = args.execution_threads
344
+ roop.globals.output_image_format = args.output_image_format
345
+ roop.globals.output_video_format = args.output_video_format
346
+ roop.globals.skip_audio = args.skip_audio
347
+ roop.globals.face_swap_mode == 'selected'
348
+ # Ensure these values are set
349
+ if not roop.globals.video_encoder:
350
+ roop.globals.video_encoder = 'libx264' # or another suitable default value
351
+ if not roop.globals.video_quality:
352
+ roop.globals.video_quality = 23 # or another suitable default value
353
+
354
+ signal.signal(signal.SIGINT, lambda signal_number, frame: destroy())
355
+
356
  if not pre_check():
357
  return
358
+
359
+ # Extract faces from the source and target files and create FaceSet objects
360
+ source_faces = extract_face_images(args.source_path, (False, 0))
361
+ target_faces = extract_face_images(args.target_path, (False, util.has_image_extension(args.target_path)))
362
+ print("Number of targets faces is ", target_faces.count)
363
+
364
+ if source_faces:
365
+ source_face_set = FaceSet()
366
+ for face_data in source_faces:
367
+ face = face_data[0]
368
+ face.mask_offsets = (0, 0, 0, 0, 1, 20)
369
+ source_face_set.faces.append(face)
370
+ if len(source_face_set.faces) > 1:
371
+ source_face_set.AverageEmbeddings()
372
+ roop.globals.INPUT_FACESETS.append(source_face_set)
373
+
374
+ if target_faces:
375
+ target_face_set = FaceSet()
376
+ for face_data in target_faces:
377
+ face = face_data[0]
378
+ face.mask_offsets = (0, 0, 0, 0, 1, 20)
379
+ target_face_set.faces.append(face)
380
+ if len(target_face_set.faces) > 1:
381
+ target_face_set.AverageEmbeddings()
382
+ roop.globals.TARGET_FACES.append(target_face_set.faces[0]) # Assuming using the first face for target
383
+
384
+ # Detect fps and endframe values for the source and target videos
385
+ source_fps = util.detect_fps(args.source_path)
386
+ source_endframe = get_video_frame_total(args.source_path)
387
+ target_fps = util.detect_fps(args.target_path)
388
+ target_endframe = get_video_frame_total(args.target_path)
389
+
390
+ # Initialize ProcessEntry objects using detected values
391
+ source_entry = ProcessEntry(
392
+ filename=args.source_path,
393
+ start=0,
394
+ end=source_endframe,
395
+ fps=source_fps
396
+ )
397
+
398
+ target_entry = ProcessEntry(
399
+ filename=args.target_path,
400
+ start=0,
401
+ end=target_endframe,
402
+ fps=target_fps
403
+ )
404
+
405
+ files = [source_entry, target_entry]
406
+ batch_process_regular(files, None, None, False, None, 1, None)
roop/core.py CHANGED
@@ -2,29 +2,26 @@
2
 
3
  import os
4
  import sys
5
- import shutil
6
- import argparse
 
 
 
7
  import warnings
8
  from typing import List
9
  import platform
10
  import signal
 
 
11
  import torch
12
  import onnxruntime
13
- import pathlib
14
- from time import time
15
  import roop.globals
16
  import roop.metadata
17
- import roop.utilities as util
18
- import roop.util_ffmpeg as ffmpeg
19
- from settings import Settings
20
- from roop.face_util import extract_face_images
21
- from roop.ProcessEntry import ProcessEntry
22
- from roop.ProcessMgr import ProcessMgr
23
- from roop.ProcessOptions import ProcessOptions
24
- from roop.capturer import get_video_frame_total
25
- from roop.FaceSet import FaceSet
26
-
27
- process_mgr = None
28
 
29
  if 'ROCMExecutionProvider' in roop.globals.execution_providers:
30
  del torch
@@ -33,21 +30,40 @@ warnings.filterwarnings('ignore', category=FutureWarning, module='insightface')
33
  warnings.filterwarnings('ignore', category=UserWarning, module='torchvision')
34
 
35
 
36
- def parse_args():
37
- parser = argparse.ArgumentParser(description="Run Roop from the command line")
38
- parser.add_argument('--source_path', type=str, required=True, help="Path to the source file")
39
- parser.add_argument('--target_path', type=str, required=True, help="Path to the target file")
40
- parser.add_argument('--output_path', type=str, required=True, help="Path to save the output file")
41
- parser.add_argument('--execution_provider', type=str, default='CPUExecutionProvider', help="Execution provider for ONNX runtime")
42
- parser.add_argument('--max_memory', type=int, default=None, help="Max memory to use (in GB)")
43
- parser.add_argument('--distance_threshold', type=float, default=0.6, help="Distance threshold for face matching")
44
- parser.add_argument('--blend_ratio', type=float, default=0.5, help="Blend ratio for face swapping")
45
- parser.add_argument('--face_swap_mode', type=str, default='replace', help="Face swap mode")
46
- parser.add_argument('--output_image_format', type=str, default='png', help="Output image format")
47
- parser.add_argument('--output_video_format', type=str, default='mp4', help="Output video format")
48
- parser.add_argument('--execution_threads', type=int, default=8, help="Number of threads to use for execution")
49
- parser.add_argument('--skip_audio', action='store_true', help="Skip audio when processing video")
50
- return parser.parse_args()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
51
 
52
 
53
  def encode_execution_providers(execution_providers: List[str]) -> List[str]:
@@ -61,8 +77,8 @@ def decode_execution_providers(execution_providers: List[str]) -> List[str]:
61
 
62
  def suggest_max_memory() -> int:
63
  if platform.system().lower() == 'darwin':
64
- return 4
65
- return 16
66
 
67
 
68
  def suggest_execution_providers() -> List[str]:
@@ -78,6 +94,12 @@ def suggest_execution_threads() -> int:
78
 
79
 
80
  def limit_resources() -> None:
 
 
 
 
 
 
81
  # limit memory usage
82
  if roop.globals.max_memory:
83
  memory = roop.globals.max_memory * 1024 ** 3
@@ -85,7 +107,7 @@ def limit_resources() -> None:
85
  memory = roop.globals.max_memory * 1024 ** 6
86
  if platform.system().lower() == 'windows':
87
  import ctypes
88
- kernel32 = ctypes.windll.kernel32 # type: ignore[attr-defined]
89
  kernel32.SetProcessWorkingSetSize(-1, ctypes.c_size_t(memory), ctypes.c_size_t(memory))
90
  else:
91
  import resource
@@ -93,314 +115,95 @@ def limit_resources() -> None:
93
 
94
 
95
  def release_resources() -> None:
96
- import gc
97
- global process_mgr
98
-
99
- if process_mgr is not None:
100
- process_mgr.release_resources()
101
- process_mgr = None
102
-
103
- gc.collect()
104
 
105
 
106
  def pre_check() -> bool:
107
  if sys.version_info < (3, 9):
108
  update_status('Python version is not supported - please upgrade to 3.9 or higher.')
109
  return False
110
-
111
- download_directory_path = util.resolve_relative_path('../models')
112
- util.conditional_download(download_directory_path, ['https://huggingface.co/countfloyd/deepfake/resolve/main/inswapper_128.onnx'])
113
- util.conditional_download(download_directory_path, ['https://huggingface.co/countfloyd/deepfake/resolve/main/GFPGANv1.4.onnx'])
114
- util.conditional_download(download_directory_path, ['https://github.com/csxmli2016/DMDNet/releases/download/v1/DMDNet.pth'])
115
- util.conditional_download(download_directory_path, ['https://huggingface.co/countfloyd/deepfake/resolve/main/GPEN-BFR-512.onnx'])
116
- util.conditional_download(download_directory_path, ['https://huggingface.co/countfloyd/deepfake/resolve/main/restoreformer_plus_plus.onnx'])
117
- util.conditional_download(download_directory_path, ['https://huggingface.co/countfloyd/deepfake/resolve/main/xseg.onnx'])
118
- download_directory_path = util.resolve_relative_path('../models/CLIP')
119
- util.conditional_download(download_directory_path, ['https://huggingface.co/countfloyd/deepfake/resolve/main/rd64-uni-refined.pth'])
120
- download_directory_path = util.resolve_relative_path('../models/CodeFormer')
121
- util.conditional_download(download_directory_path, ['https://huggingface.co/countfloyd/deepfake/resolve/main/CodeFormerv0.1.onnx'])
122
- download_directory_path = util.resolve_relative_path('../models/Frame')
123
- util.conditional_download(download_directory_path, ['https://huggingface.co/countfloyd/deepfake/resolve/main/deoldify_artistic.onnx'])
124
- util.conditional_download(download_directory_path, ['https://huggingface.co/countfloyd/deepfake/resolve/main/deoldify_stable.onnx'])
125
- util.conditional_download(download_directory_path, ['https://huggingface.co/countfloyd/deepfake/resolve/main/isnet-general-use.onnx'])
126
- util.conditional_download(download_directory_path, ['https://huggingface.co/countfloyd/deepfake/resolve/main/real_esrgan_x4.onnx'])
127
- util.conditional_download(download_directory_path, ['https://huggingface.co/countfloyd/deepfake/resolve/main/real_esrgan_x2.onnx'])
128
- util.conditional_download(download_directory_path, ['https://huggingface.co/countfloyd/deepfake/resolve/main/lsdir_x4.onnx'])
129
-
130
  if not shutil.which('ffmpeg'):
131
  update_status('ffmpeg is not installed.')
 
132
  return True
133
 
134
 
135
- def update_status(message: str) -> None:
136
- print(message)
137
-
138
-
139
- def get_processing_plugins(masking_engine):
140
- processors = {"faceswap": {}}
141
- if masking_engine is not None:
142
- processors.update({masking_engine: {}})
143
-
144
- if roop.globals.selected_enhancer == 'GFPGAN':
145
- processors.update({"gfpgan": {}})
146
- elif roop.globals.selected_enhancer == 'Codeformer':
147
- processors.update({"codeformer": {}})
148
- elif roop.globals.selected_enhancer == 'DMDNet':
149
- processors.update({"dmdnet": {}})
150
- elif roop.globals.selected_enhancer == 'GPEN':
151
- processors.update({"gpen": {}})
152
- elif roop.globals.selected_enhancer == 'Restoreformer++':
153
- processors.update({"restoreformer++": {}})
154
- return processors
155
-
156
-
157
- def live_swap(frame, options):
158
- global process_mgr
159
-
160
- if frame is None:
161
- return frame
162
-
163
- if process_mgr is None:
164
- process_mgr = ProcessMgr(None)
165
-
166
- process_mgr.initialize(roop.globals.INPUT_FACESETS, roop.globals.TARGET_FACES, options)
167
- newframe = process_mgr.process_frame(frame)
168
- if newframe is None:
169
- return frame
170
- return newframe
171
-
172
-
173
- def batch_process_regular(files: List[ProcessEntry], masking_engine: str, new_clip_text: str, use_new_method, imagemask, num_swap_steps, progress, selected_index=0) -> None:
174
- global process_mgr
175
-
176
- release_resources()
177
- limit_resources()
178
- if process_mgr is None:
179
- process_mgr = ProcessMgr(progress)
180
- mask = imagemask["layers"][0] if imagemask is not None else None
181
- if len(roop.globals.INPUT_FACESETS) <= selected_index:
182
- selected_index = 0
183
- options = ProcessOptions(get_processing_plugins(masking_engine), roop.globals.distance_threshold, roop.globals.blend_ratio, roop.globals.face_swap_mode, selected_index, new_clip_text, mask, num_swap_steps, False)
184
- process_mgr.initialize(roop.globals.INPUT_FACESETS, roop.globals.TARGET_FACES, options)
185
- batch_process(files, use_new_method)
186
- return
187
-
188
-
189
- def batch_process_with_options(files: List[ProcessEntry], options, progress):
190
- global process_mgr
191
-
192
- release_resources()
193
- limit_resources()
194
- if process_mgr is None:
195
- process_mgr = ProcessMgr(progress)
196
- process_mgr.initialize(roop.globals.INPUT_FACESETS, roop.globals.TARGET_FACES, options)
197
- roop.globals.keep_frames = False
198
- roop.globals.wait_after_extraction = False
199
- roop.globals.skip_audio = False
200
- batch_process(files, True)
201
-
202
-
203
- def batch_process(files: List[ProcessEntry], use_new_method) -> None:
204
- global process_mgr
205
-
206
- roop.globals.processing = True
207
-
208
- max_threads = suggest_execution_threads()
209
- if max_threads == 1:
210
- roop.globals.execution_threads = 1
211
-
212
- imagefiles: List[ProcessEntry] = []
213
- videofiles: List[ProcessEntry] = []
214
-
215
- update_status('Sorting videos/images')
216
-
217
- for index, f in enumerate(files):
218
- fullname = f.filename
219
- if util.has_image_extension(fullname):
220
- destination = util.get_destfilename_from_path(fullname, roop.globals.output_path, f'.{roop.globals.CFG.output_image_format}')
221
- destination = util.replace_template(destination, index=index)
222
- pathlib.Path(os.path.dirname(destination)).mkdir(parents=True, exist_ok=True)
223
- f.finalname = destination
224
- imagefiles.append(f)
225
-
226
- elif util.is_video(fullname) or util.has_extension(fullname, ['gif']):
227
- destination = util.get_destfilename_from_path(fullname, roop.globals.output_path, f'__temp.{roop.globals.CFG.output_video_format}')
228
- f.finalname = destination
229
- videofiles.append(f)
230
-
231
- if len(imagefiles) > 0:
232
- update_status('Processing image(s)')
233
- origimages = []
234
- fakeimages = []
235
- for f in imagefiles:
236
- origimages.append(f.filename)
237
- fakeimages.append(f.finalname)
238
-
239
- process_mgr.run_batch(origimages, fakeimages, roop.globals.execution_threads)
240
- origimages.clear()
241
- fakeimages.clear()
242
-
243
- if len(videofiles) > 0:
244
- for index, v in enumerate(videofiles):
245
- if not roop.globals.processing:
246
- end_processing('Processing stopped!')
247
- return
248
- fps = v.fps if v.fps > 0 else util.detect_fps(v.filename)
249
- if v.endframe == 0:
250
- v.endframe = get_video_frame_total(v.filename)
251
-
252
- update_status(f'Creating {os.path.basename(v.finalname)} with {fps} FPS...')
253
- start_processing = time()
254
- if roop.globals.keep_frames or not use_new_method:
255
- util.create_temp(v.filename)
256
- update_status('Extracting frames...')
257
- ffmpeg.extract_frames(v.filename, v.startframe, v.endframe, fps)
258
- if not roop.globals.processing:
259
- end_processing('Processing stopped!')
260
- return
261
-
262
- temp_frame_paths = util.get_temp_frame_paths(v.filename)
263
- process_mgr.run_batch(temp_frame_paths, temp_frame_paths, roop.globals.execution_threads)
264
- if not roop.globals.processing:
265
- end_processing('Processing stopped!')
266
- return
267
- if roop.globals.wait_after_extraction:
268
- extract_path = os.path.dirname(temp_frame_paths[0])
269
- util.open_folder(extract_path)
270
- input("Press any key to continue...")
271
- print("Resorting frames to create video")
272
- util.sort_rename_frames(extract_path)
273
-
274
- ffmpeg.create_video(v.filename, v.finalname, fps)
275
- if not roop.globals.keep_frames:
276
- util.delete_temp_frames(temp_frame_paths[0])
277
- else:
278
- if util.has_extension(v.filename, ['gif']):
279
- skip_audio = True
280
- else:
281
- skip_audio = roop.globals.skip_audio
282
- process_mgr.run_batch_inmem(v.filename, v.finalname, v.startframe, v.endframe, fps, roop.globals.execution_threads, skip_audio)
283
-
284
- if not roop.globals.processing:
285
- end_processing('Processing stopped!')
286
- return
287
-
288
- video_file_name = v.finalname
289
- if os.path.isfile(video_file_name):
290
- destination = ''
291
- if util.has_extension(v.filename, ['gif']):
292
- gifname = util.get_destfilename_from_path(v.filename, roop.globals.output_path, '.gif')
293
- destination = util.replace_template(gifname, index=index)
294
- pathlib.Path(os.path.dirname(destination)).mkdir(parents=True, exist_ok=True)
295
-
296
- update_status('Creating final GIF')
297
- ffmpeg.create_gif_from_video(video_file_name, destination)
298
- if os.path.isfile(destination):
299
- os.remove(video_file_name)
300
- else:
301
- skip_audio = roop.globals.skip_audio
302
- destination = util.replace_template(video_file_name, index=index)
303
- pathlib.Path(os.path.dirname(destination)).mkdir(parents=True, exist_ok=True)
304
-
305
- if not skip_audio:
306
- ffmpeg.restore_audio(video_file_name, v.filename, v.startframe, v.endframe, destination)
307
- if os.path.isfile(destination):
308
- os.remove(video_file_name)
309
- else:
310
- shutil.move(video_file_name, destination)
311
- update_status(f'\nProcessing {os.path.basename(destination)} took {time() - start_processing} secs')
312
-
313
- else:
314
- update_status(f'Failed processing {os.path.basename(v.finalname)}!')
315
- end_processing('Finished')
316
-
317
-
318
- def end_processing(msg: str):
319
- update_status(msg)
320
- roop.globals.target_folder_path = None
321
- release_resources()
322
 
323
 
324
  def destroy() -> None:
325
  if roop.globals.target_path:
326
- util.clean_temp(roop.globals.target_path)
327
- release_resources()
328
- sys.exit()
329
 
330
 
331
  def run() -> None:
332
- args = parse_args()
333
-
334
- roop.globals.source_path = args.source_path
335
- roop.globals.target_path = args.target_path
336
- roop.globals.output_path = args.output_path
337
- roop.globals.execution_providers = decode_execution_providers([args.execution_provider])
338
- roop.globals.max_memory = args.max_memory
339
- roop.globals.distance_threshold = args.distance_threshold
340
- roop.globals.blend_ratio = args.blend_ratio
341
- roop.globals.face_swap_mode = args.face_swap_mode
342
- roop.globals.CFG = Settings('config.yaml')
343
- roop.globals.execution_threads = args.execution_threads
344
- roop.globals.output_image_format = args.output_image_format
345
- roop.globals.output_video_format = args.output_video_format
346
- roop.globals.skip_audio = args.skip_audio
347
- roop.globals.face_swap_mode == 'selected'
348
- # Ensure these values are set
349
- if not roop.globals.video_encoder:
350
- roop.globals.video_encoder = 'libx264' # or another suitable default value
351
- if not roop.globals.video_quality:
352
- roop.globals.video_quality = 23 # or another suitable default value
353
-
354
- signal.signal(signal.SIGINT, lambda signal_number, frame: destroy())
355
-
356
  if not pre_check():
357
  return
358
-
359
- # Extract faces from the source and target files and create FaceSet objects
360
- source_faces = extract_face_images(args.source_path, (False, 0))
361
- target_faces = extract_face_images(args.target_path, (False, util.has_image_extension(args.target_path)))
362
- print("Number of targets faces is ", target_faces.count)
363
-
364
- if source_faces:
365
- source_face_set = FaceSet()
366
- for face_data in source_faces:
367
- face = face_data[0]
368
- face.mask_offsets = (0, 0, 0, 0, 1, 20)
369
- source_face_set.faces.append(face)
370
- if len(source_face_set.faces) > 1:
371
- source_face_set.AverageEmbeddings()
372
- roop.globals.INPUT_FACESETS.append(source_face_set)
373
-
374
- if target_faces:
375
- target_face_set = FaceSet()
376
- for face_data in target_faces:
377
- face = face_data[0]
378
- face.mask_offsets = (0, 0, 0, 0, 1, 20)
379
- target_face_set.faces.append(face)
380
- if len(target_face_set.faces) > 1:
381
- target_face_set.AverageEmbeddings()
382
- roop.globals.TARGET_FACES.append(target_face_set.faces[0]) # Assuming using the first face for target
383
-
384
- # Detect fps and endframe values for the source and target videos
385
- source_fps = util.detect_fps(args.source_path)
386
- source_endframe = get_video_frame_total(args.source_path)
387
- target_fps = util.detect_fps(args.target_path)
388
- target_endframe = get_video_frame_total(args.target_path)
389
-
390
- # Initialize ProcessEntry objects using detected values
391
- source_entry = ProcessEntry(
392
- filename=args.source_path,
393
- start=0,
394
- end=source_endframe,
395
- fps=source_fps
396
- )
397
-
398
- target_entry = ProcessEntry(
399
- filename=args.target_path,
400
- start=0,
401
- end=target_endframe,
402
- fps=target_fps
403
- )
404
-
405
- files = [source_entry, target_entry]
406
- batch_process_regular(files, None, None, False, None, 1, None)
 
2
 
3
  import os
4
  import sys
5
+ # single thread doubles cuda performance - needs to be set before torch import
6
+ if any(arg.startswith('--execution-provider') for arg in sys.argv):
7
+ os.environ['OMP_NUM_THREADS'] = '1'
8
+ # reduce tensorflow log level
9
+ os.environ['TF_CPP_MIN_LOG_LEVEL'] = '2'
10
  import warnings
11
  from typing import List
12
  import platform
13
  import signal
14
+ import shutil
15
+ import argparse
16
  import torch
17
  import onnxruntime
18
+ import tensorflow
19
+
20
  import roop.globals
21
  import roop.metadata
22
+ from roop.predicter import predict_image, predict_video
23
+ from roop.processors.frame.core import get_frame_processors_modules
24
+ from roop.utilities import has_image_extension, is_image, is_video, detect_fps, create_video, extract_frames, get_temp_frame_paths, restore_audio, create_temp, move_temp, clean_temp, normalize_output_path
 
 
 
 
 
 
 
 
25
 
26
  if 'ROCMExecutionProvider' in roop.globals.execution_providers:
27
  del torch
 
30
  warnings.filterwarnings('ignore', category=UserWarning, module='torchvision')
31
 
32
 
33
+ def parse_args() -> None:
34
+ signal.signal(signal.SIGINT, lambda signal_number, frame: destroy())
35
+ program = argparse.ArgumentParser(formatter_class=lambda prog: argparse.HelpFormatter(prog, max_help_position=100))
36
+ program.add_argument('-s', '--source', help='select an source image', dest='source_path')
37
+ program.add_argument('-t', '--target', help='select an target image or video', dest='target_path')
38
+ program.add_argument('-o', '--output', help='select output file or directory', dest='output_path')
39
+ program.add_argument('--frame-processor', help='frame processors (choices: face_swapper, face_enhancer, ...)', dest='frame_processor', default=['face_swapper'], nargs='+')
40
+ program.add_argument('--keep-fps', help='keep original fps', dest='keep_fps', action='store_true', default=False)
41
+ program.add_argument('--keep-audio', help='keep original audio', dest='keep_audio', action='store_true', default=True)
42
+ program.add_argument('--keep-frames', help='keep temporary frames', dest='keep_frames', action='store_true', default=False)
43
+ program.add_argument('--many-faces', help='process every face', dest='many_faces', action='store_true', default=False)
44
+ program.add_argument('--video-encoder', help='adjust output video encoder', dest='video_encoder', default='libx264', choices=['libx264', 'libx265', 'libvpx-vp9'])
45
+ program.add_argument('--video-quality', help='adjust output video quality', dest='video_quality', type=int, default=18, choices=range(52), metavar='[0-51]')
46
+ program.add_argument('--max-memory', help='maximum amount of RAM in GB', dest='max_memory', type=int, default=suggest_max_memory())
47
+ program.add_argument('--execution-provider', help='available execution provider (choices: cpu, ...)', dest='execution_provider', default=['cpu'], choices=suggest_execution_providers(), nargs='+')
48
+ program.add_argument('--execution-threads', help='number of execution threads', dest='execution_threads', type=int, default=suggest_execution_threads())
49
+ program.add_argument('-v', '--version', action='version', version=f'{roop.metadata.name} {roop.metadata.version}')
50
+
51
+ args = program.parse_args()
52
+
53
+ roop.globals.source_path = args.source_path
54
+ roop.globals.target_path = args.target_path
55
+ roop.globals.output_path = normalize_output_path(roop.globals.source_path, roop.globals.target_path, args.output_path)
56
+ roop.globals.frame_processors = args.frame_processor
57
+ roop.globals.headless = args.source_path or args.target_path or args.output_path
58
+ roop.globals.keep_fps = args.keep_fps
59
+ roop.globals.keep_audio = args.keep_audio
60
+ roop.globals.keep_frames = args.keep_frames
61
+ roop.globals.many_faces = args.many_faces
62
+ roop.globals.video_encoder = args.video_encoder
63
+ roop.globals.video_quality = args.video_quality
64
+ roop.globals.max_memory = args.max_memory
65
+ roop.globals.execution_providers = decode_execution_providers(args.execution_provider)
66
+ roop.globals.execution_threads = args.execution_threads
67
 
68
 
69
  def encode_execution_providers(execution_providers: List[str]) -> List[str]:
 
77
 
78
  def suggest_max_memory() -> int:
79
  if platform.system().lower() == 'darwin':
80
+ return 10
81
+ return 14
82
 
83
 
84
  def suggest_execution_providers() -> List[str]:
 
94
 
95
 
96
  def limit_resources() -> None:
97
+ # prevent tensorflow memory leak
98
+ gpus = tensorflow.config.experimental.list_physical_devices('GPU')
99
+ for gpu in gpus:
100
+ tensorflow.config.experimental.set_virtual_device_configuration(gpu, [
101
+ tensorflow.config.experimental.VirtualDeviceConfiguration(memory_limit=1024)
102
+ ])
103
  # limit memory usage
104
  if roop.globals.max_memory:
105
  memory = roop.globals.max_memory * 1024 ** 3
 
107
  memory = roop.globals.max_memory * 1024 ** 6
108
  if platform.system().lower() == 'windows':
109
  import ctypes
110
+ kernel32 = ctypes.windll.kernel32
111
  kernel32.SetProcessWorkingSetSize(-1, ctypes.c_size_t(memory), ctypes.c_size_t(memory))
112
  else:
113
  import resource
 
115
 
116
 
117
  def release_resources() -> None:
118
+ if 'CUDAExecutionProvider' in roop.globals.execution_providers:
119
+ torch.cuda.empty_cache()
 
 
 
 
 
 
120
 
121
 
122
  def pre_check() -> bool:
123
  if sys.version_info < (3, 9):
124
  update_status('Python version is not supported - please upgrade to 3.9 or higher.')
125
  return False
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
126
  if not shutil.which('ffmpeg'):
127
  update_status('ffmpeg is not installed.')
128
+ return False
129
  return True
130
 
131
 
132
+ def update_status(message: str, scope: str = 'ROOP.CORE') -> None:
133
+ print(f'[{scope}] {message}')
134
+
135
+
136
+ def start() -> None:
137
+ for frame_processor in get_frame_processors_modules(roop.globals.frame_processors):
138
+ if not frame_processor.pre_start():
139
+ return
140
+ # process image to image
141
+ if has_image_extension(roop.globals.target_path):
142
+ if predict_image(roop.globals.target_path):
143
+ destroy()
144
+ shutil.copy2(roop.globals.target_path, roop.globals.output_path)
145
+ for frame_processor in get_frame_processors_modules(roop.globals.frame_processors):
146
+ update_status('Progressing...', frame_processor.NAME)
147
+ frame_processor.process_image(roop.globals.source_path, roop.globals.output_path, roop.globals.output_path)
148
+ frame_processor.post_process()
149
+ release_resources()
150
+ if is_image(roop.globals.target_path):
151
+ update_status('Processing to image succeed!')
152
+ else:
153
+ update_status('Processing to image failed!')
154
+ return
155
+ # process image to videos
156
+ if predict_video(roop.globals.target_path):
157
+ destroy()
158
+ update_status('Creating temp resources...')
159
+ create_temp(roop.globals.target_path)
160
+ update_status('Extracting frames...')
161
+ extract_frames(roop.globals.target_path)
162
+ temp_frame_paths = get_temp_frame_paths(roop.globals.target_path)
163
+ for frame_processor in get_frame_processors_modules(roop.globals.frame_processors):
164
+ update_status('Progressing...', frame_processor.NAME)
165
+ frame_processor.process_video(roop.globals.source_path, temp_frame_paths)
166
+ frame_processor.post_process()
167
+ release_resources()
168
+ # handles fps
169
+ if roop.globals.keep_fps:
170
+ update_status('Detecting fps...')
171
+ fps = detect_fps(roop.globals.target_path)
172
+ update_status(f'Creating video with {fps} fps...')
173
+ create_video(roop.globals.target_path, fps)
174
+ else:
175
+ update_status('Creating video with 30.0 fps...')
176
+ create_video(roop.globals.target_path)
177
+ # handle audio
178
+ if roop.globals.keep_audio:
179
+ if roop.globals.keep_fps:
180
+ update_status('Restoring audio...')
181
+ else:
182
+ update_status('Restoring audio might cause issues as fps are not kept...')
183
+ restore_audio(roop.globals.target_path, roop.globals.output_path)
184
+ else:
185
+ move_temp(roop.globals.target_path, roop.globals.output_path)
186
+ # clean and validate
187
+ clean_temp(roop.globals.target_path)
188
+ if is_video(roop.globals.target_path):
189
+ update_status('Processing to video succeed!')
190
+ else:
191
+ update_status('Processing to video failed!')
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
192
 
193
 
194
  def destroy() -> None:
195
  if roop.globals.target_path:
196
+ clean_temp(roop.globals.target_path)
197
+ quit()
 
198
 
199
 
200
  def run() -> None:
201
+ parse_args()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
202
  if not pre_check():
203
  return
204
+ for frame_processor in get_frame_processors_modules(roop.globals.frame_processors):
205
+ if not frame_processor.pre_check():
206
+ return
207
+ limit_resources()
208
+ if roop.globals.headless:
209
+ start()