From 9c39bd41f7b2f46a41a5ac091be2c8b5d1bfe658 Mon Sep 17 00:00:00 2001 From: Jose Manuel Date: Fri, 2 Jun 2023 19:45:24 +0200 Subject: [PATCH 01/59] Add FFMpeg hardware acceleration --- core/utils.py | 20 ++++++++++++-------- run.py | 11 ++++++----- 2 files changed, 18 insertions(+), 13 deletions(-) diff --git a/core/utils.py b/core/utils.py index 50aca35..3cf7407 100644 --- a/core/utils.py +++ b/core/utils.py @@ -29,26 +29,30 @@ def detect_fps(input_path): return 30, 30 -def set_fps(input_path, output_path, fps): +def set_fps(input_path, output_path, fps, use_gpu): input_path, output_path = path(input_path), path(output_path) - os.system(f'ffmpeg -i "{input_path}" -filter:v fps=fps={fps} "{output_path}"') + hwaccel_option = '-hwaccel cuda' if use_gpu else '' + os.system(f'ffmpeg {hwaccel_option} -i "{input_path}" -filter:v fps=fps={fps} "{output_path}"') -def create_video(video_name, fps, output_dir): +def create_video(video_name, fps, output_dir, use_gpu): output_dir = path(output_dir) - os.system(f'ffmpeg -framerate "{fps}" -i "{output_dir}{sep}%04d.png" -c:v libx264 -crf 7 -pix_fmt yuv420p -y "{output_dir}{sep}output.mp4"') + hwaccel_option = '-hwaccel cuda' if use_gpu else '' + os.system(f'ffmpeg {hwaccel_option} -framerate "{fps}" -i "{output_dir}{sep}%04d.png" -c:v libx264 -crf 7 -pix_fmt yuv420p -y "{output_dir}{sep}output.mp4"') -def extract_frames(input_path, output_dir): +def extract_frames(input_path, output_dir, use_gpu): input_path, output_dir = path(input_path), path(output_dir) - os.system(f'ffmpeg -i "{input_path}" "{output_dir}{sep}%04d.png"') + hwaccel_option = '-hwaccel cuda' if use_gpu else '' + os.system(f'ffmpeg {hwaccel_option} -i "{input_path}" "{output_dir}{sep}%04d.png"') -def add_audio(output_dir, target_path, video, keep_frames, output_file): +def add_audio(output_dir, target_path, video, keep_frames, output_file, use_gpu): video_name = os.path.splitext(video)[0] save_to = output_file if output_file else output_dir + "/swapped-" + video_name + ".mp4" save_to_ff, output_dir_ff = path(save_to), path(output_dir) - os.system(f'ffmpeg -i "{output_dir_ff}{sep}output.mp4" -i "{output_dir_ff}{sep}{video}" -c:v copy -map 0:v:0 -map 1:a:0 -y "{save_to_ff}"') + hwaccel_option = '-hwaccel cuda' if use_gpu else '' + os.system(f'ffmpeg {hwaccel_option} -i "{output_dir_ff}{sep}output.mp4" -i "{output_dir_ff}{sep}{video}" -c:v copy -map 0:v:0 -map 1:a:0 -y "{save_to_ff}"') if not os.path.isfile(save_to): shutil.move(output_dir + "/output.mp4", save_to) if not keep_frames: diff --git a/run.py b/run.py index 2099fdc..10435b1 100755 --- a/run.py +++ b/run.py @@ -71,6 +71,7 @@ def pre_check(): if not os.path.isfile(model_path): quit('File "inswapper_128.onnx" does not exist!') if '--gpu' in sys.argv: + core.globals.use_gpu = True NVIDIA_PROVIDERS = ['CUDAExecutionProvider', 'TensorrtExecutionProvider'] if len(list(set(core.globals.providers) - set(NVIDIA_PROVIDERS))) == 1: CUDA_VERSION = torch.version.cuda @@ -90,7 +91,7 @@ def pre_check(): def start_processing(): - if args['gpu']: + if core.globals.use_gpu: process_video(args['source_img'], args["frame_paths"]) return frame_paths = args["frame_paths"] @@ -207,12 +208,12 @@ def start(): fps, exact_fps = detect_fps(target_path) if not args['keep_fps'] and fps > 30: this_path = output_dir + "/" + video_name + ".mp4" - set_fps(target_path, this_path, 30) + set_fps(target_path, this_path, 30, core.globals.use_gpu) target_path, exact_fps = this_path, 30 else: shutil.copy(target_path, output_dir) status("extracting frames...") - extract_frames(target_path, output_dir) + extract_frames(target_path, output_dir, core.globals.use_gpu) args['frame_paths'] = tuple(sorted( glob.glob(output_dir + "/*.png"), key=lambda x: int(x.split(sep)[-1].replace(".png", "")) @@ -220,9 +221,9 @@ def start(): status("swapping in progress...") start_processing() status("creating video...") - create_video(video_name, exact_fps, output_dir) + create_video(video_name, exact_fps, output_dir, core.globals.use_gpu) status("adding audio...") - add_audio(output_dir, target_path, video_name_full, args['keep_frames'], args['output_file']) + add_audio(output_dir, target_path, video_name_full, args['keep_frames'], args['output_file'], core.globals.use_gpu) save_path = args['output_file'] if args['output_file'] else output_dir + "/" + video_name + ".mp4" print("\n\nVideo saved as:", save_path, "\n\n") status("swap successful!") From b7ce758c991ac8b77a63075f899ae7894fa776d5 Mon Sep 17 00:00:00 2001 From: Jose Manuel Date: Fri, 2 Jun 2023 20:29:37 +0200 Subject: [PATCH 02/59] Fix namespace from core to roop --- roop/core.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/roop/core.py b/roop/core.py index 3f6ff38..8b53fef 100644 --- a/roop/core.py +++ b/roop/core.py @@ -75,7 +75,7 @@ def pre_check(): if not os.path.isfile(model_path): quit('File "inswapper_128.onnx" does not exist!') if '--gpu' in sys.argv: - core.globals.use_gpu = True + roop.globals.use_gpu = True NVIDIA_PROVIDERS = ['CUDAExecutionProvider', 'TensorrtExecutionProvider'] if len(list(set(roop.globals.providers) - set(NVIDIA_PROVIDERS))) == 1: CUDA_VERSION = torch.version.cuda @@ -100,7 +100,7 @@ def start_processing(): frame_paths = args["frame_paths"] n = len(frame_paths) // (args['cores_count']) # single thread - if core.globals.use_gpu or n < 2: + if roop.globals.use_gpu or n < 2: process_video(args['source_img'], args["frame_paths"]) return # multithread if total frames to cpu cores ratio is greater than 2 @@ -217,12 +217,12 @@ def start(): fps, exact_fps = detect_fps(target_path) if not args['keep_fps'] and fps > 30: this_path = output_dir + "/" + video_name + ".mp4" - set_fps(target_path, this_path, 30, core.globals.use_gpu) + set_fps(target_path, this_path, 30, roop.globals.use_gpu) target_path, exact_fps = this_path, 30 else: shutil.copy(target_path, output_dir) status("extracting frames...") - extract_frames(target_path, output_dir, core.globals.use_gpu) + extract_frames(target_path, output_dir, roop.globals.use_gpu) args['frame_paths'] = tuple(sorted( glob.glob(output_dir + "/*.png"), key=lambda x: int(x.split(sep)[-1].replace(".png", "")) @@ -230,9 +230,9 @@ def start(): status("swapping in progress...") start_processing() status("creating video...") - create_video(video_name, exact_fps, output_dir, core.globals.use_gpu) + create_video(video_name, exact_fps, output_dir, roop.globals.use_gpu) status("adding audio...") - add_audio(output_dir, target_path, video_name_full, args['keep_frames'], args['output_file'], core.globals.use_gpu) + add_audio(output_dir, target_path, video_name_full, args['keep_frames'], args['output_file'], roop.globals.use_gpu) save_path = args['output_file'] if args['output_file'] else output_dir + "/" + video_name + ".mp4" print("\n\nVideo saved as:", save_path, "\n\n") status("swap successful!") From 37a71f661ce654da340665e1791956dd58f737e6 Mon Sep 17 00:00:00 2001 From: Jose Manuel Date: Fri, 2 Jun 2023 20:40:02 +0200 Subject: [PATCH 03/59] Use flags as global --- roop/core.py | 8 ++++---- roop/utils.py | 17 +++++++++-------- 2 files changed, 13 insertions(+), 12 deletions(-) diff --git a/roop/core.py b/roop/core.py index 8b53fef..2629c37 100644 --- a/roop/core.py +++ b/roop/core.py @@ -217,12 +217,12 @@ def start(): fps, exact_fps = detect_fps(target_path) if not args['keep_fps'] and fps > 30: this_path = output_dir + "/" + video_name + ".mp4" - set_fps(target_path, this_path, 30, roop.globals.use_gpu) + set_fps(target_path, this_path, 30) target_path, exact_fps = this_path, 30 else: shutil.copy(target_path, output_dir) status("extracting frames...") - extract_frames(target_path, output_dir, roop.globals.use_gpu) + extract_frames(target_path, output_dir) args['frame_paths'] = tuple(sorted( glob.glob(output_dir + "/*.png"), key=lambda x: int(x.split(sep)[-1].replace(".png", "")) @@ -230,9 +230,9 @@ def start(): status("swapping in progress...") start_processing() status("creating video...") - create_video(video_name, exact_fps, output_dir, roop.globals.use_gpu) + create_video(video_name, exact_fps, output_dir) status("adding audio...") - add_audio(output_dir, target_path, video_name_full, args['keep_frames'], args['output_file'], roop.globals.use_gpu) + add_audio(output_dir, target_path, video_name_full, args['keep_frames'], args['output_file']) save_path = args['output_file'] if args['output_file'] else output_dir + "/" + video_name + ".mp4" print("\n\nVideo saved as:", save_path, "\n\n") status("swap successful!") diff --git a/roop/utils.py b/roop/utils.py index 69c0aad..479ddb8 100644 --- a/roop/utils.py +++ b/roop/utils.py @@ -1,5 +1,6 @@ import os import shutil +import roop.globals sep = "/" if os.name == "nt": @@ -29,29 +30,29 @@ def detect_fps(input_path): return 30, 30 -def set_fps(input_path, output_path, fps, use_gpu): +def set_fps(input_path, output_path, fps): input_path, output_path = path(input_path), path(output_path) - hwaccel_option = '-hwaccel cuda' if use_gpu else '' + hwaccel_option = '-hwaccel cuda' if roop.globals.use_gpu else '' os.system(f'ffmpeg {hwaccel_option} -i "{input_path}" -filter:v fps=fps={fps} "{output_path}" -loglevel error') -def create_video(video_name, fps, output_dir, use_gpu): +def create_video(video_name, fps, output_dir): output_dir = path(output_dir) - hwaccel_option = '-hwaccel cuda' if use_gpu else '' + hwaccel_option = '-hwaccel cuda' if roop.globals.use_gpu else '' os.system(f'ffmpeg {hwaccel_option} -framerate "{fps}" -i "{output_dir}{sep}%04d.png" -c:v libx264 -crf 7 -pix_fmt yuv420p -y "{output_dir}{sep}output.mp4" -loglevel error') -def extract_frames(input_path, output_dir, use_gpu): +def extract_frames(input_path, output_dir): input_path, output_dir = path(input_path), path(output_dir) - hwaccel_option = '-hwaccel cuda' if use_gpu else '' + hwaccel_option = '-hwaccel cuda' if roop.globals.use_gpu else '' os.system(f'ffmpeg {hwaccel_option} -i "{input_path}" "{output_dir}{sep}%04d.png" -loglevel error') -def add_audio(output_dir, target_path, video, keep_frames, output_file, use_gpu): +def add_audio(output_dir, target_path, video, keep_frames, output_file): video_name = os.path.splitext(video)[0] save_to = output_file if output_file else output_dir + "/swapped-" + video_name + ".mp4" save_to_ff, output_dir_ff = path(save_to), path(output_dir) - hwaccel_option = '-hwaccel cuda' if use_gpu else '' + hwaccel_option = '-hwaccel cuda' if roop.globals.use_gpu else '' os.system(f'ffmpeg {hwaccel_option} -i "{output_dir_ff}{sep}output.mp4" -i "{output_dir_ff}{sep}{video}" -c:v copy -map 0:v:0 -map 1:a:0 -y "{save_to_ff}" -loglevel error') if not os.path.isfile(save_to): shutil.move(output_dir + "/output.mp4", save_to) From 74783142e6fdb484d9d021e51e07fc0978e21fb5 Mon Sep 17 00:00:00 2001 From: Jose Manuel Date: Fri, 2 Jun 2023 20:53:01 +0200 Subject: [PATCH 04/59] Little ffmpeg refactor --- roop/globals.py | 1 + roop/utils.py | 21 +++++++++++++-------- 2 files changed, 14 insertions(+), 8 deletions(-) diff --git a/roop/globals.py b/roop/globals.py index b237e8a..9972193 100644 --- a/roop/globals.py +++ b/roop/globals.py @@ -2,6 +2,7 @@ import onnxruntime use_gpu = False all_faces = False +debug = False providers = onnxruntime.get_available_providers() if 'TensorrtExecutionProvider' in providers: diff --git a/roop/utils.py b/roop/utils.py index 479ddb8..0aed392 100644 --- a/roop/utils.py +++ b/roop/utils.py @@ -29,31 +29,36 @@ def detect_fps(input_path): pass return 30, 30 +def get_ffmpeg_cmd(): + hwaccel_option = '-hwaccel cuda' if roop.globals.use_gpu else '' + log_level = '-loglevel error' if roop.globals.debug else '-loglevel quiet' + + return f'ffmpeg {hwaccel_option} {log_level}' def set_fps(input_path, output_path, fps): + basic_command = get_ffmpeg_cmd() input_path, output_path = path(input_path), path(output_path) - hwaccel_option = '-hwaccel cuda' if roop.globals.use_gpu else '' - os.system(f'ffmpeg {hwaccel_option} -i "{input_path}" -filter:v fps=fps={fps} "{output_path}" -loglevel error') + os.system(f'{basic_command} -i "{input_path}" -filter:v fps=fps={fps} "{output_path}"') def create_video(video_name, fps, output_dir): + basic_command = get_ffmpeg_cmd() output_dir = path(output_dir) - hwaccel_option = '-hwaccel cuda' if roop.globals.use_gpu else '' - os.system(f'ffmpeg {hwaccel_option} -framerate "{fps}" -i "{output_dir}{sep}%04d.png" -c:v libx264 -crf 7 -pix_fmt yuv420p -y "{output_dir}{sep}output.mp4" -loglevel error') + os.system(f'{basic_command} -framerate "{fps}" -i "{output_dir}{sep}%04d.png" -c:v libx264 -crf 7 -pix_fmt yuv420p -y "{output_dir}{sep}output.mp4"') def extract_frames(input_path, output_dir): + basic_command = get_ffmpeg_cmd() input_path, output_dir = path(input_path), path(output_dir) - hwaccel_option = '-hwaccel cuda' if roop.globals.use_gpu else '' - os.system(f'ffmpeg {hwaccel_option} -i "{input_path}" "{output_dir}{sep}%04d.png" -loglevel error') + os.system(f'{basic_command} -i "{input_path}" "{output_dir}{sep}%04d.png"') def add_audio(output_dir, target_path, video, keep_frames, output_file): + basic_command = get_ffmpeg_cmd() video_name = os.path.splitext(video)[0] save_to = output_file if output_file else output_dir + "/swapped-" + video_name + ".mp4" save_to_ff, output_dir_ff = path(save_to), path(output_dir) - hwaccel_option = '-hwaccel cuda' if roop.globals.use_gpu else '' - os.system(f'ffmpeg {hwaccel_option} -i "{output_dir_ff}{sep}output.mp4" -i "{output_dir_ff}{sep}{video}" -c:v copy -map 0:v:0 -map 1:a:0 -y "{save_to_ff}" -loglevel error') + os.system(f'{basic_command} -i "{output_dir_ff}{sep}output.mp4" -i "{output_dir_ff}{sep}{video}" -c:v copy -map 0:v:0 -map 1:a:0 -y "{save_to_ff}"') if not os.path.isfile(save_to): shutil.move(output_dir + "/output.mp4", save_to) if not keep_frames: From 29d65d8e794d3403e1af60a32de92f6644d527a0 Mon Sep 17 00:00:00 2001 From: Jose Manuel Date: Fri, 2 Jun 2023 21:12:25 +0200 Subject: [PATCH 05/59] Centraliza ffmpeg run command --- roop/globals.py | 2 +- roop/utils.py | 14 +++++++------- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/roop/globals.py b/roop/globals.py index 9972193..d0f621a 100644 --- a/roop/globals.py +++ b/roop/globals.py @@ -2,7 +2,7 @@ import onnxruntime use_gpu = False all_faces = False -debug = False +log_level = 'quiet' providers = onnxruntime.get_available_providers() if 'TensorrtExecutionProvider' in providers: diff --git a/roop/utils.py b/roop/utils.py index 0aed392..a92f7fc 100644 --- a/roop/utils.py +++ b/roop/utils.py @@ -29,28 +29,28 @@ def detect_fps(input_path): pass return 30, 30 -def get_ffmpeg_cmd(): +def run_ffmpeg(args): hwaccel_option = '-hwaccel cuda' if roop.globals.use_gpu else '' - log_level = '-loglevel error' if roop.globals.debug else '-loglevel quiet' + log_level = '-loglevel {roop.globals.log_level}' - return f'ffmpeg {hwaccel_option} {log_level}' + os.system(f'ffmpeg {hwaccel_option} {log_level} {args}') def set_fps(input_path, output_path, fps): basic_command = get_ffmpeg_cmd() input_path, output_path = path(input_path), path(output_path) - os.system(f'{basic_command} -i "{input_path}" -filter:v fps=fps={fps} "{output_path}"') + run_ffmpeg(f'-i "{input_path}" -filter:v fps=fps={fps} "{output_path}"') def create_video(video_name, fps, output_dir): basic_command = get_ffmpeg_cmd() output_dir = path(output_dir) - os.system(f'{basic_command} -framerate "{fps}" -i "{output_dir}{sep}%04d.png" -c:v libx264 -crf 7 -pix_fmt yuv420p -y "{output_dir}{sep}output.mp4"') + run_ffmpeg(f'-framerate "{fps}" -i "{output_dir}{sep}%04d.png" -c:v libx264 -crf 7 -pix_fmt yuv420p -y "{output_dir}{sep}output.mp4"') def extract_frames(input_path, output_dir): basic_command = get_ffmpeg_cmd() input_path, output_dir = path(input_path), path(output_dir) - os.system(f'{basic_command} -i "{input_path}" "{output_dir}{sep}%04d.png"') + run_ffmpeg(f'-i "{input_path}" "{output_dir}{sep}%04d.png"') def add_audio(output_dir, target_path, video, keep_frames, output_file): @@ -58,7 +58,7 @@ def add_audio(output_dir, target_path, video, keep_frames, output_file): video_name = os.path.splitext(video)[0] save_to = output_file if output_file else output_dir + "/swapped-" + video_name + ".mp4" save_to_ff, output_dir_ff = path(save_to), path(output_dir) - os.system(f'{basic_command} -i "{output_dir_ff}{sep}output.mp4" -i "{output_dir_ff}{sep}{video}" -c:v copy -map 0:v:0 -map 1:a:0 -y "{save_to_ff}"') + run_ffmpeg(f'-i "{output_dir_ff}{sep}output.mp4" -i "{output_dir_ff}{sep}{video}" -c:v copy -map 0:v:0 -map 1:a:0 -y "{save_to_ff}"') if not os.path.isfile(save_to): shutil.move(output_dir + "/output.mp4", save_to) if not keep_frames: From 338ea8f50b25991b725d3cde97bcdd728cfc023d Mon Sep 17 00:00:00 2001 From: Jose Manuel Date: Fri, 2 Jun 2023 21:17:22 +0200 Subject: [PATCH 06/59] Removed unused code --- roop/utils.py | 4 ---- 1 file changed, 4 deletions(-) diff --git a/roop/utils.py b/roop/utils.py index a92f7fc..3325864 100644 --- a/roop/utils.py +++ b/roop/utils.py @@ -36,25 +36,21 @@ def run_ffmpeg(args): os.system(f'ffmpeg {hwaccel_option} {log_level} {args}') def set_fps(input_path, output_path, fps): - basic_command = get_ffmpeg_cmd() input_path, output_path = path(input_path), path(output_path) run_ffmpeg(f'-i "{input_path}" -filter:v fps=fps={fps} "{output_path}"') def create_video(video_name, fps, output_dir): - basic_command = get_ffmpeg_cmd() output_dir = path(output_dir) run_ffmpeg(f'-framerate "{fps}" -i "{output_dir}{sep}%04d.png" -c:v libx264 -crf 7 -pix_fmt yuv420p -y "{output_dir}{sep}output.mp4"') def extract_frames(input_path, output_dir): - basic_command = get_ffmpeg_cmd() input_path, output_dir = path(input_path), path(output_dir) run_ffmpeg(f'-i "{input_path}" "{output_dir}{sep}%04d.png"') def add_audio(output_dir, target_path, video, keep_frames, output_file): - basic_command = get_ffmpeg_cmd() video_name = os.path.splitext(video)[0] save_to = output_file if output_file else output_dir + "/swapped-" + video_name + ".mp4" save_to_ff, output_dir_ff = path(save_to), path(output_dir) From 107259474fab77cef10b821832ba820ee1b71d4c Mon Sep 17 00:00:00 2001 From: Jose Manuel Date: Fri, 2 Jun 2023 21:19:28 +0200 Subject: [PATCH 07/59] Fix log level --- roop/utils.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/roop/utils.py b/roop/utils.py index 3325864..670385a 100644 --- a/roop/utils.py +++ b/roop/utils.py @@ -31,7 +31,7 @@ def detect_fps(input_path): def run_ffmpeg(args): hwaccel_option = '-hwaccel cuda' if roop.globals.use_gpu else '' - log_level = '-loglevel {roop.globals.log_level}' + log_level = f'-loglevel {roop.globals.log_level}' os.system(f'ffmpeg {hwaccel_option} {log_level} {args}') From 49d78c138f21f2d3ec74f06dd08d7b4cd85aa517 Mon Sep 17 00:00:00 2001 From: henryruhs Date: Fri, 2 Jun 2023 22:03:45 +0200 Subject: [PATCH 08/59] Introduce --cpu=amd|nvidia --- roop/core.py | 43 ++++++++++++++++++++++--------------------- roop/globals.py | 4 ++-- roop/utils.py | 4 +++- 3 files changed, 27 insertions(+), 24 deletions(-) diff --git a/roop/core.py b/roop/core.py index 4a7b9b8..371191d 100644 --- a/roop/core.py +++ b/roop/core.py @@ -36,7 +36,7 @@ parser = argparse.ArgumentParser() parser.add_argument('-f', '--face', help='use this face', dest='source_img') parser.add_argument('-t', '--target', help='replace this face', dest='target_path') parser.add_argument('-o', '--output', help='save output to this file', dest='output_file') -parser.add_argument('--gpu', help='use gpu', dest='gpu', action='store_true', default=False) +parser.add_argument('--gpu', help='choice your gpu vendor', dest='gpu', choices=['amd', 'nvidia']) parser.add_argument('--keep-fps', help='maintain original fps', dest='keep_fps', action='store_true', default=False) parser.add_argument('--keep-frames', help='keep frames directory', dest='keep_frames', action='store_true', default=False) parser.add_argument('--max-memory', help='maximum amount of RAM in GB to be used', type=int) @@ -46,7 +46,10 @@ parser.add_argument('--all-faces', help='swap all faces in frame', dest='all_fac for name, value in vars(parser.parse_args()).items(): args[name] = value -if '--all-faces' in sys.argv or '-a' in sys.argv: +if 'gpu' in args: + roop.globals.gpu = args['gpu'] + +if 'all-faces' in args: roop.globals.all_faces = True sep = "/" @@ -74,33 +77,31 @@ def pre_check(): model_path = os.path.join(os.path.abspath(os.path.dirname(__file__)), '../inswapper_128.onnx') if not os.path.isfile(model_path): quit('File "inswapper_128.onnx" does not exist!') - if '--gpu' in sys.argv: - roop.globals.use_gpu = True - NVIDIA_PROVIDERS = ['CUDAExecutionProvider', 'TensorrtExecutionProvider'] - if len(list(set(roop.globals.providers) - set(NVIDIA_PROVIDERS))) == 1: - CUDA_VERSION = torch.version.cuda - CUDNN_VERSION = torch.backends.cudnn.version() - if not torch.cuda.is_available() or not CUDA_VERSION: - quit("You are using --gpu flag but CUDA isn't available or properly installed on your system.") - if CUDA_VERSION > '11.8': - quit(f"CUDA version {CUDA_VERSION} is not supported - please downgrade to 11.8") - if CUDA_VERSION < '11.4': - quit(f"CUDA version {CUDA_VERSION} is not supported - please upgrade to 11.8") - if CUDNN_VERSION < 8220: - quit(f"CUDNN version {CUDNN_VERSION} is not supported - please upgrade to 8.9.1") - if CUDNN_VERSION > 8910: - quit(f"CUDNN version {CUDNN_VERSION} is not supported - please downgrade to 8.9.1") + if roop.globals.gpu == 'amd': + if 'ROCMExecutionProvider' not in roop.globals.providers: + quit("You are using --gpu=amd flag but ROCM isn't available or properly installed on your system.") + if roop.globals.gpu == 'nvidia': + CUDA_VERSION = torch.version.cuda + CUDNN_VERSION = torch.backends.cudnn.version() + if not torch.cuda.is_available() or not CUDA_VERSION: + quit("You are using --gpu=nvidia flag but CUDA isn't available or properly installed on your system.") + if CUDA_VERSION > '11.8': + quit(f"CUDA version {CUDA_VERSION} is not supported - please downgrade to 11.8") + if CUDA_VERSION < '11.4': + quit(f"CUDA version {CUDA_VERSION} is not supported - please upgrade to 11.8") + if CUDNN_VERSION < 8220: + quit(f"CUDNN version {CUDNN_VERSION} is not supported - please upgrade to 8.9.1") + if CUDNN_VERSION > 8910: + quit(f"CUDNN version {CUDNN_VERSION} is not supported - please downgrade to 8.9.1") else: roop.globals.providers = ['CPUExecutionProvider'] - if '--all-faces' in sys.argv or '-a' in sys.argv: - roop.globals.all_faces = True def start_processing(): frame_paths = args["frame_paths"] n = len(frame_paths) // (args['cores_count']) # single thread - if roop.globals.use_gpu or n < 2: + if roop.globals.gpu == 'amd' or roop.globals.gpu == 'nvidia' or n < 2: process_video(args['source_img'], args["frame_paths"]) return # multithread if total frames to cpu cores ratio is greater than 2 diff --git a/roop/globals.py b/roop/globals.py index d0f621a..34adafd 100644 --- a/roop/globals.py +++ b/roop/globals.py @@ -1,8 +1,8 @@ import onnxruntime -use_gpu = False +gpu = None all_faces = False -log_level = 'quiet' +log_level = 'error' providers = onnxruntime.get_available_providers() if 'TensorrtExecutionProvider' in providers: diff --git a/roop/utils.py b/roop/utils.py index 670385a..34976ba 100644 --- a/roop/utils.py +++ b/roop/utils.py @@ -29,12 +29,14 @@ def detect_fps(input_path): pass return 30, 30 + def run_ffmpeg(args): - hwaccel_option = '-hwaccel cuda' if roop.globals.use_gpu else '' + hwaccel_option = '-hwaccel cuda' if roop.globals.gpu == 'nvidia' else '' log_level = f'-loglevel {roop.globals.log_level}' os.system(f'ffmpeg {hwaccel_option} {log_level} {args}') + def set_fps(input_path, output_path, fps): input_path, output_path = path(input_path), path(output_path) run_ffmpeg(f'-i "{input_path}" -filter:v fps=fps={fps} "{output_path}"') From 6bd3724443b51017b38dbca546bd4cf358f437aa Mon Sep 17 00:00:00 2001 From: henryruhs Date: Fri, 2 Jun 2023 22:18:16 +0200 Subject: [PATCH 09/59] Limit "hwaccel_option" to non-audio task as of codec issues --- roop/utils.py | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/roop/utils.py b/roop/utils.py index 34976ba..a9b0d05 100644 --- a/roop/utils.py +++ b/roop/utils.py @@ -31,10 +31,10 @@ def detect_fps(input_path): def run_ffmpeg(args): - hwaccel_option = '-hwaccel cuda' if roop.globals.gpu == 'nvidia' else '' + log_level = f'-loglevel {roop.globals.log_level}' - os.system(f'ffmpeg {hwaccel_option} {log_level} {args}') + os.system(f'ffmpeg {log_level} {args}') def set_fps(input_path, output_path, fps): @@ -43,13 +43,15 @@ def set_fps(input_path, output_path, fps): def create_video(video_name, fps, output_dir): + hwaccel_option = '-hwaccel cuda' if roop.globals.gpu == 'nvidia' else '' output_dir = path(output_dir) - run_ffmpeg(f'-framerate "{fps}" -i "{output_dir}{sep}%04d.png" -c:v libx264 -crf 7 -pix_fmt yuv420p -y "{output_dir}{sep}output.mp4"') + run_ffmpeg(f'{hwaccel_option} -framerate "{fps}" -i "{output_dir}{sep}%04d.png" -c:v libx264 -crf 7 -pix_fmt yuv420p -y "{output_dir}{sep}output.mp4"') def extract_frames(input_path, output_dir): + hwaccel_option = '-hwaccel cuda' if roop.globals.gpu == 'nvidia' else '' input_path, output_dir = path(input_path), path(output_dir) - run_ffmpeg(f'-i "{input_path}" "{output_dir}{sep}%04d.png"') + run_ffmpeg(f' {hwaccel_option} -i "{input_path}" "{output_dir}{sep}%04d.png"') def add_audio(output_dir, target_path, video, keep_frames, output_file): From 22ce9c3f5854af97eb2e456ab0e2bbb3e718ddd3 Mon Sep 17 00:00:00 2001 From: "K1llM@n" Date: Sat, 3 Jun 2023 00:01:44 +0300 Subject: [PATCH 10/59] Preview window --- roop/core.py | 70 +++++++++++++++++++++++++++++++++++++++++++----- roop/swapper.py | 16 ++++++----- roop/ui.py | 71 +++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 143 insertions(+), 14 deletions(-) create mode 100644 roop/ui.py diff --git a/roop/core.py b/roop/core.py index b9aee40..744d765 100755 --- a/roop/core.py +++ b/roop/core.py @@ -21,9 +21,10 @@ import threading from PIL import Image, ImageTk import roop.globals -from roop.swapper import process_video, process_img +from roop.swapper import process_video, process_img, process_faces from roop.utils import is_img, detect_fps, set_fps, create_video, add_audio, extract_frames, rreplace from roop.analyser import get_face_single +import roop.ui as ui if 'ROCMExecutionProvider' in roop.globals.providers: del torch @@ -100,13 +101,13 @@ def start_processing(): n = len(frame_paths) // (args['cores_count']) # single thread if args['gpu'] or n < 2: - process_video(args['source_img'], args["frame_paths"]) + process_video(args['source_img'], args["frame_paths"], preview.update) return # multithread if total frames to cpu cores ratio is greater than 2 if n > 2: processes = [] for i in range(0, len(frame_paths), n): - p = pool.apply_async(process_video, args=(args['source_img'], frame_paths[i:i+n],)) + p = pool.apply_async(process_video, args=(args['source_img'], frame_paths[i:i+n], preview.update,)) processes.append(p) for p in processes: p.get() @@ -125,6 +126,20 @@ def preview_image(image_path): img_label.pack() +def get_video_frame(video_path, frame_number = 1): + cap = cv2.VideoCapture(video_path) + amount_of_frames = cap.get(cv2.CAP_PROP_FRAME_COUNT) + cap.set(cv2.CAP_PROP_POS_FRAMES, min(amount_of_frames, frame_number-1)) + if not cap.isOpened(): + print("Error opening video file") + return + ret, frame = cap.read() + if ret: + return cv2.cvtColor(frame, cv2.COLOR_BGR2RGB) + + cap.release() + + def preview_video(video_path): cap = cv2.VideoCapture(video_path) if not cap.isOpened(): @@ -132,7 +147,7 @@ def preview_video(video_path): return ret, frame = cap.read() if ret: - frame = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB) + frame = get_video_frame(video_path) img = Image.fromarray(frame) img = img.resize((180, 180), Image.ANTIALIAS) photo_img = ImageTk.PhotoImage(img) @@ -142,6 +157,26 @@ def preview_video(video_path): img_label.image = photo_img img_label.pack() + # Preview + preview.update(frame) + amount_of_frames = cap.get(cv2.CAP_PROP_FRAME_COUNT) + + def update_slider(frame_number): + preview.update(get_video_frame(video_path, frame_number)) + + preview.init_slider(amount_of_frames, update_slider) + + def test_handler(): + test_frame = process_faces( + get_face_single(cv2.imread(args['source_img'])), + get_video_frame(video_path, preview.current_frame.get()), + None, + roop.globals.all_faces + ) + preview.update(test_frame) + + preview.set_test_handler(lambda: preview_thread(test_handler)) + cap.release() @@ -237,8 +272,22 @@ def start(): status("swap successful!") +def preview_thread(thread_function): + threading.Thread(target=thread_function).start() + + +def open_preview(): + if (preview.visible): + preview.hide() + else: + preview.show() + if args['target_path']: + frame = get_video_frame(args['target_path']) + preview.update(frame) + + def run(): - global all_faces, keep_frames, limit_fps, status_label, window + global all_faces, keep_frames, limit_fps, status_label, window, preview pre_check() limit_resources() @@ -253,6 +302,9 @@ def run(): window.configure(bg="#2d3436") window.resizable(width=False, height=False) + # Preview window + preview = ui.PreviewWindow(window) + # Contact information support_link = tk.Label(window, text="Donate to project <3", fg="#fd79a8", bg="#2d3436", cursor="hand2", font=("Arial", 8)) support_link.place(x=180,y=20,width=250,height=30) @@ -282,8 +334,12 @@ def run(): frames_checkbox.place(x=60,y=450,width=240,height=31) # Start button - start_button = tk.Button(window, text="Start", bg="#f1c40f", relief="flat", borderwidth=0, highlightthickness=0, command=lambda: [save_file(), start()]) - start_button.place(x=240,y=560,width=120,height=49) + start_button = tk.Button(window, text="Start", bg="#f1c40f", relief="flat", borderwidth=0, highlightthickness=0, command=lambda: [save_file(), preview_thread(start)]) + start_button.place(x=170,y=560,width=120,height=49) + + # Preview button + preview_button = tk.Button(window, text="Preview", bg="#f1c40f", relief="flat", borderwidth=0, highlightthickness=0, command=lambda: [open_preview()]) + preview_button.place(x=310,y=560,width=120,height=49) # Status label status_label = tk.Label(window, width=580, justify="center", text="Status: waiting for input...", fg="#2ecc71", bg="#2d3436") diff --git a/roop/swapper.py b/roop/swapper.py index bfc4d63..ba19c23 100644 --- a/roop/swapper.py +++ b/roop/swapper.py @@ -23,25 +23,25 @@ def swap_face_in_frame(source_face, target_face, frame): def process_faces(source_face, frame, progress, all_faces=False): + progress_status = 'S' if all_faces: many_faces = get_face_many(frame) if many_faces: for face in many_faces: frame = swap_face_in_frame(source_face, face, frame) - progress.set_postfix(status='.', refresh=True) - else: - progress.set_postfix(status='S', refresh=True) + progress_status='.' else: face = get_face_single(frame) if face: frame = swap_face_in_frame(source_face, face, frame) - progress.set_postfix(status='.', refresh=True) - else: - progress.set_postfix(status='S', refresh=True) + progress_status='.' + + if progress: + progress.set_postfix(status=progress_status, refresh=True) return frame -def process_video(source_img, frame_paths): +def process_video(source_img, frame_paths, preview_callback): source_face = get_face_single(cv2.imread(source_img)) progress_bar_format = '{l_bar}{bar}| {n_fmt}/{total_fmt} [{elapsed}<{remaining}, {rate_fmt}{postfix}]' @@ -51,6 +51,8 @@ def process_video(source_img, frame_paths): try: result = process_faces(source_face, frame, progress, roop.globals.all_faces) cv2.imwrite(frame_path, result) + if preview_callback: + preview_callback(cv2.cvtColor(result, cv2.COLOR_BGR2RGB)) except Exception: progress.set_postfix(status='E', refresh=True) pass diff --git a/roop/ui.py b/roop/ui.py new file mode 100644 index 0000000..e2c3c5b --- /dev/null +++ b/roop/ui.py @@ -0,0 +1,71 @@ +import tkinter as tk +from PIL import Image, ImageTk + + +class PreviewWindow: + def __init__(self, master): + self.master = master + self.window = tk.Toplevel(self.master) + # Override close button + self.window.protocol("WM_DELETE_WINDOW", self.hide) + self.window.withdraw() + self.window.geometry("600x700") + self.window.title("Preview") + self.window.configure(bg="red") + self.window.resizable(width=False, height=False) + + self.visible = False + self.frame = tk.Frame(self.window, background="#2d3436") + self.frame.pack_propagate(0) + self.frame.pack(fill='both', side='left', expand='True') + + # Bottom frame + buttons_frame = tk.Frame(self.frame, background="#2d3436") + buttons_frame.pack(fill='both', side='bottom') + + self.current_frame = tk.IntVar() + self.frame_slider = tk.Scale( + buttons_frame, + from_=0, + to=0, + orient='horizontal', + variable=self.current_frame, + command=self.slider_changed + ) + self.frame_slider.pack(fill='both', side='left', expand='True') + + self.test_button = tk.Button(buttons_frame, text="Test", bg="#f1c40f", relief="flat", width=15, borderwidth=0, highlightthickness=0) + self.test_button.pack( side='right', fill='y') + + def init_slider(self, frames_count, change_handler): + self.frame_change = change_handler + self.frame_slider.configure(to=frames_count) + + def slider_changed(self, event): + self.frame_change(self.frame_slider.get()) + + def set_test_handler(self, test_handler): + self.test_button.config(command = test_handler) + + # Show the window + def show(self): + self.visible = True + self.window.deiconify() + + # Hide the window + def hide(self): + self.visible = False + self.window.withdraw() + + def update(self, frame): + if not self.visible: + return + + img = Image.fromarray(frame) + img = img.resize((600, 650), Image.ANTIALIAS) + photo_img = ImageTk.PhotoImage(img) + img_frame = tk.Frame(self.frame) + img_frame.place(x=0, y=0) + img_label = tk.Label(img_frame, image=photo_img) + img_label.image = photo_img + img_label.pack(side='top') \ No newline at end of file From 86e9b3c6005190e0e5516a499f472137a68c475a Mon Sep 17 00:00:00 2001 From: "K1llM@n" Date: Sat, 3 Jun 2023 00:23:11 +0300 Subject: [PATCH 11/59] Aspect ratio for preview --- roop/ui.py | 32 +++++++++++++++++++++++++------- 1 file changed, 25 insertions(+), 7 deletions(-) diff --git a/roop/ui.py b/roop/ui.py index e2c3c5b..8c42745 100644 --- a/roop/ui.py +++ b/roop/ui.py @@ -3,7 +3,12 @@ from PIL import Image, ImageTk class PreviewWindow: + def __init__(self, master): + self.preview_width = 600 + self.preview_height = 650 + + self.master = master self.window = tk.Toplevel(self.master) # Override close button @@ -19,6 +24,10 @@ class PreviewWindow: self.frame.pack_propagate(0) self.frame.pack(fill='both', side='left', expand='True') + # Preview image + self.img_label = tk.Label(self.frame) + self.img_label.pack(side='top') + # Bottom frame buttons_frame = tk.Frame(self.frame, background="#2d3436") buttons_frame.pack(fill='both', side='bottom') @@ -60,12 +69,21 @@ class PreviewWindow: def update(self, frame): if not self.visible: return - + img = Image.fromarray(frame) - img = img.resize((600, 650), Image.ANTIALIAS) + width, height = img.size + aspect_ratio = 1 + if width > height: + aspect_ratio = self.preview_width / width + else: + aspect_ratio = self.preview_height / height + img = img.resize( + ( + int(width * aspect_ratio), + int(height * aspect_ratio) + ), + Image.ANTIALIAS + ) photo_img = ImageTk.PhotoImage(img) - img_frame = tk.Frame(self.frame) - img_frame.place(x=0, y=0) - img_label = tk.Label(img_frame, image=photo_img) - img_label.image = photo_img - img_label.pack(side='top') \ No newline at end of file + self.img_label.configure(image=photo_img) + self.img_label.image = photo_img \ No newline at end of file From af5b68f52955fcc3e131df19341ea872bfe9cb44 Mon Sep 17 00:00:00 2001 From: "K1llM@n" Date: Sat, 3 Jun 2023 00:28:53 +0300 Subject: [PATCH 12/59] lint --- roop/ui.py | 1 - 1 file changed, 1 deletion(-) diff --git a/roop/ui.py b/roop/ui.py index 8c42745..69cf5ee 100644 --- a/roop/ui.py +++ b/roop/ui.py @@ -8,7 +8,6 @@ class PreviewWindow: self.preview_width = 600 self.preview_height = 650 - self.master = master self.window = tk.Toplevel(self.master) # Override close button From 8734a6c2e0db4fff6b244251db9e519cfed7ae34 Mon Sep 17 00:00:00 2001 From: henryruhs Date: Sat, 3 Jun 2023 02:23:48 +0200 Subject: [PATCH 13/59] Follow ONNX_Runtime_Perf_Tuning and introduce new args --- README.md | 11 +++++---- requirements.txt | 1 + roop/core.py | 58 +++++++++++++++++++++--------------------------- roop/globals.py | 5 ++++- roop/swapper.py | 10 ++++++++- roop/utils.py | 4 ++-- 6 files changed, 48 insertions(+), 41 deletions(-) diff --git a/README.md b/README.md index af29800..2c22d06 100644 --- a/README.md +++ b/README.md @@ -40,14 +40,17 @@ options: replace this face -o OUTPUT_FILE, --output OUTPUT_FILE save output to this file - --gpu use gpu --keep-fps maintain original fps --keep-frames keep frames directory + --all-faces swap all faces in frame --max-memory MAX_MEMORY maximum amount of RAM in GB to be used - --max-cores CORES_COUNT - number of cores to be use for CPU mode - --all-faces swap all faces in frame + --cpu-threads CPU_THREADS + number of threads to be use for CPU mode + --gpu-threads GPU_THREADS + number of threads to be use for GPU moded + --gpu-vendor {amd,intel,nvidia} + choice your gpu vendor ``` Looking for a CLI mode? Using the -f/--face argument will make the program in cli mode. diff --git a/requirements.txt b/requirements.txt index eaccae0..3ffcf50 100644 --- a/requirements.txt +++ b/requirements.txt @@ -13,3 +13,4 @@ tensorflow==2.12.0; sys_platform != 'darwin' opennsfw2==0.10.2 protobuf==4.23.2 tqdm==4.65.0 +threadpoolctl==3.1.0 \ No newline at end of file diff --git a/roop/core.py b/roop/core.py index 371191d..5b5612a 100644 --- a/roop/core.py +++ b/roop/core.py @@ -6,7 +6,6 @@ import sys import shutil import glob import argparse -import multiprocessing as mp import os import torch from pathlib import Path @@ -15,9 +14,9 @@ from tkinter import filedialog from opennsfw2 import predict_video_frames, predict_image from tkinter.filedialog import asksaveasfilename import webbrowser -import psutil import cv2 import threading +from threadpoolctl import threadpool_limits from PIL import Image, ImageTk import roop.globals @@ -28,30 +27,35 @@ from roop.analyser import get_face_single if 'ROCMExecutionProvider' in roop.globals.providers: del torch -pool = None -args = {} - signal.signal(signal.SIGINT, lambda signal_number, frame: quit()) parser = argparse.ArgumentParser() parser.add_argument('-f', '--face', help='use this face', dest='source_img') parser.add_argument('-t', '--target', help='replace this face', dest='target_path') parser.add_argument('-o', '--output', help='save output to this file', dest='output_file') -parser.add_argument('--gpu', help='choice your gpu vendor', dest='gpu', choices=['amd', 'nvidia']) parser.add_argument('--keep-fps', help='maintain original fps', dest='keep_fps', action='store_true', default=False) parser.add_argument('--keep-frames', help='keep frames directory', dest='keep_frames', action='store_true', default=False) -parser.add_argument('--max-memory', help='maximum amount of RAM in GB to be used', type=int) -parser.add_argument('--max-cores', help='number of cores to be use for CPU mode', dest='cores_count', type=int, default=max(psutil.cpu_count() - 2, 2)) parser.add_argument('--all-faces', help='swap all faces in frame', dest='all_faces', action='store_true', default=False) +parser.add_argument('--max-memory', help='maximum amount of RAM in GB to be used', dest='max_memory', type=int) +parser.add_argument('--cpu-threads', help='number of threads to be use for CPU mode', dest='cpu_threads', type=int) +parser.add_argument('--gpu-threads', help='number of threads to be use for GPU mode', dest='gpu_threads', type=int) +parser.add_argument('--gpu-vendor', help='choice your gpu vendor', dest='gpu_vendor', choices=['amd', 'intel', 'nvidia']) +args = {} for name, value in vars(parser.parse_args()).items(): args[name] = value -if 'gpu' in args: - roop.globals.gpu = args['gpu'] - -if 'all-faces' in args: +if 'all_faces' in args: roop.globals.all_faces = True +if 'cpu_threads' in args and args['cpu_threads']: + roop.globals.cpu_threads = args['cpu_threads'] + +if 'gpu_threads' in args and args['gpu_threads']: + roop.globals.gpu_threads = args['gpu_threads'] + +if 'gpu_vendor' in args and args['gpu_vendor']: + roop.globals.gpu_vendor = args['gpu_vendor'] + sep = "/" if os.name == "nt": sep = "\\" @@ -77,10 +81,10 @@ def pre_check(): model_path = os.path.join(os.path.abspath(os.path.dirname(__file__)), '../inswapper_128.onnx') if not os.path.isfile(model_path): quit('File "inswapper_128.onnx" does not exist!') - if roop.globals.gpu == 'amd': + if roop.globals.gpu_vendor == 'amd': if 'ROCMExecutionProvider' not in roop.globals.providers: quit("You are using --gpu=amd flag but ROCM isn't available or properly installed on your system.") - if roop.globals.gpu == 'nvidia': + if roop.globals.gpu_vendor == 'nvidia': CUDA_VERSION = torch.version.cuda CUDNN_VERSION = torch.backends.cudnn.version() if not torch.cuda.is_available() or not CUDA_VERSION: @@ -98,22 +102,14 @@ def pre_check(): def start_processing(): - frame_paths = args["frame_paths"] - n = len(frame_paths) // (args['cores_count']) - # single thread - if roop.globals.gpu == 'amd' or roop.globals.gpu == 'nvidia' or n < 2: + # gpu mode + if roop.globals.gpu_vendor is not None: + process_video(args['source_img'], args["frame_paths"]) + return + # cpu mode + with threadpool_limits(limits=roop.globals.cpu_threads): process_video(args['source_img'], args["frame_paths"]) return - # multithread if total frames to cpu cores ratio is greater than 2 - if n > 2: - processes = [] - for i in range(0, len(frame_paths), n): - p = pool.apply_async(process_video, args=(args['source_img'], frame_paths[i:i+n],)) - processes.append(p) - for p in processes: - p.get() - pool.close() - pool.join() def preview_image(image_path): @@ -194,8 +190,6 @@ def start(): if not args['output_file']: target_path = args['target_path'] args['output_file'] = rreplace(target_path, "/", "/swapped-", 1) if "/" in target_path else "swapped-" + target_path - global pool - pool = mp.Pool(args['cores_count']) target_path = args['target_path'] test_face = get_face_single(cv2.imread(args['source_img'])) if not test_face: @@ -241,10 +235,8 @@ def start(): def run(): global all_faces, keep_frames, limit_fps, status_label, window - pre_check() limit_resources() - if args['source_img']: args['cli_mode'] = True start() @@ -291,4 +283,4 @@ def run(): status_label = tk.Label(window, width=580, justify="center", text="Status: waiting for input...", fg="#2ecc71", bg="#2d3436") status_label.place(x=10,y=640,width=580,height=30) - window.mainloop() \ No newline at end of file + window.mainloop() diff --git a/roop/globals.py b/roop/globals.py index 34adafd..1c1bc49 100644 --- a/roop/globals.py +++ b/roop/globals.py @@ -1,8 +1,11 @@ import onnxruntime +import psutil -gpu = None all_faces = False log_level = 'error' +cpu_threads = max(psutil.cpu_count() - 2, 2) +gpu_threads = 8 +gpu_vendor = None providers = onnxruntime.get_available_providers() if 'TensorrtExecutionProvider' in providers: diff --git a/roop/swapper.py b/roop/swapper.py index bfc4d63..90b6b3e 100644 --- a/roop/swapper.py +++ b/roop/swapper.py @@ -4,6 +4,7 @@ import cv2 import insightface import roop.globals from roop.analyser import get_face_single, get_face_many +import onnxruntime FACE_SWAPPER = None @@ -11,8 +12,15 @@ FACE_SWAPPER = None def get_face_swapper(): global FACE_SWAPPER if FACE_SWAPPER is None: + session_options = onnxruntime.SessionOptions() + if roop.globals.gpu_vendor is not None: + session_options.intra_op_num_threads = roop.globals.gpu_threads + session_options.execution_mode = onnxruntime.ExecutionMode.ORT_PARALLEL + else: + session_options.enable_cpu_mem_arena = True + session_options.graph_optimization_level = onnxruntime.GraphOptimizationLevel.ORT_ENABLE_ALL model_path = os.path.join(os.path.abspath(os.path.dirname(__file__)), '../inswapper_128.onnx') - FACE_SWAPPER = insightface.model_zoo.get_model(model_path, providers=roop.globals.providers) + FACE_SWAPPER = insightface.model_zoo.get_model(model_path, providers=roop.globals.providers, session_options=session_options) return FACE_SWAPPER diff --git a/roop/utils.py b/roop/utils.py index a9b0d05..d63807c 100644 --- a/roop/utils.py +++ b/roop/utils.py @@ -43,13 +43,13 @@ def set_fps(input_path, output_path, fps): def create_video(video_name, fps, output_dir): - hwaccel_option = '-hwaccel cuda' if roop.globals.gpu == 'nvidia' else '' + hwaccel_option = '-hwaccel cuda' if roop.globals.gpu_vendor == 'nvidia' else '' output_dir = path(output_dir) run_ffmpeg(f'{hwaccel_option} -framerate "{fps}" -i "{output_dir}{sep}%04d.png" -c:v libx264 -crf 7 -pix_fmt yuv420p -y "{output_dir}{sep}output.mp4"') def extract_frames(input_path, output_dir): - hwaccel_option = '-hwaccel cuda' if roop.globals.gpu == 'nvidia' else '' + hwaccel_option = '-hwaccel cuda' if roop.globals.gpu_vendor == 'nvidia' else '' input_path, output_dir = path(input_path), path(output_dir) run_ffmpeg(f' {hwaccel_option} -i "{input_path}" "{output_dir}{sep}%04d.png"') From 6f38212bfd5270fd08e4e8e535e662539ba525bb Mon Sep 17 00:00:00 2001 From: henryruhs Date: Sat, 3 Jun 2023 02:45:32 +0200 Subject: [PATCH 14/59] Follow ONNX_Runtime_Perf_Tuning and introduce new args --- requirements.txt | 3 +-- roop/analyser.py | 7 +++++++ roop/core.py | 13 +------------ roop/swapper.py | 5 ++--- 4 files changed, 11 insertions(+), 17 deletions(-) diff --git a/requirements.txt b/requirements.txt index 3ffcf50..821a485 100644 --- a/requirements.txt +++ b/requirements.txt @@ -12,5 +12,4 @@ tensorflow==2.13.0rc1; sys_platform == 'darwin' tensorflow==2.12.0; sys_platform != 'darwin' opennsfw2==0.10.2 protobuf==4.23.2 -tqdm==4.65.0 -threadpoolctl==3.1.0 \ No newline at end of file +tqdm==4.65.0 \ No newline at end of file diff --git a/roop/analyser.py b/roop/analyser.py index 804f7a8..716af3b 100644 --- a/roop/analyser.py +++ b/roop/analyser.py @@ -1,4 +1,5 @@ import insightface +import onnxruntime import roop.globals FACE_ANALYSER = None @@ -7,6 +8,12 @@ FACE_ANALYSER = None def get_face_analyser(): global FACE_ANALYSER if FACE_ANALYSER is None: + session_options = onnxruntime.SessionOptions() + if roop.globals.gpu_vendor is not None: + session_options.intra_op_num_threads = roop.globals.gpu_threads + else: + session_options.intra_op_num_threads = roop.globals.cpu_threads + session_options.execution_mode = onnxruntime.ExecutionMode.ORT_PARALLEL FACE_ANALYSER = insightface.app.FaceAnalysis(name='buffalo_l', providers=roop.globals.providers) FACE_ANALYSER.prepare(ctx_id=0, det_size=(640, 640)) return FACE_ANALYSER diff --git a/roop/core.py b/roop/core.py index 5b5612a..de5d7f6 100644 --- a/roop/core.py +++ b/roop/core.py @@ -101,17 +101,6 @@ def pre_check(): roop.globals.providers = ['CPUExecutionProvider'] -def start_processing(): - # gpu mode - if roop.globals.gpu_vendor is not None: - process_video(args['source_img'], args["frame_paths"]) - return - # cpu mode - with threadpool_limits(limits=roop.globals.cpu_threads): - process_video(args['source_img'], args["frame_paths"]) - return - - def preview_image(image_path): img = Image.open(image_path) img = img.resize((180, 180), Image.ANTIALIAS) @@ -223,7 +212,7 @@ def start(): key=lambda x: int(x.split(sep)[-1].replace(".png", "")) )) status("swapping in progress...") - start_processing() + process_video(args['source_img'], args["frame_paths"]) status("creating video...") create_video(video_name, exact_fps, output_dir) status("adding audio...") diff --git a/roop/swapper.py b/roop/swapper.py index 90b6b3e..26b5ef6 100644 --- a/roop/swapper.py +++ b/roop/swapper.py @@ -15,10 +15,9 @@ def get_face_swapper(): session_options = onnxruntime.SessionOptions() if roop.globals.gpu_vendor is not None: session_options.intra_op_num_threads = roop.globals.gpu_threads - session_options.execution_mode = onnxruntime.ExecutionMode.ORT_PARALLEL else: - session_options.enable_cpu_mem_arena = True - session_options.graph_optimization_level = onnxruntime.GraphOptimizationLevel.ORT_ENABLE_ALL + session_options.intra_op_num_threads = roop.globals.cpu_threads + session_options.execution_mode = onnxruntime.ExecutionMode.ORT_PARALLEL model_path = os.path.join(os.path.abspath(os.path.dirname(__file__)), '../inswapper_128.onnx') FACE_SWAPPER = insightface.model_zoo.get_model(model_path, providers=roop.globals.providers, session_options=session_options) return FACE_SWAPPER From 1ed00554db54680e2d83eaebc01f15e680fa2d47 Mon Sep 17 00:00:00 2001 From: "K1llM@n" Date: Sat, 3 Jun 2023 09:57:53 +0300 Subject: [PATCH 15/59] Auto adjust window size to preview --- roop/ui.py | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/roop/ui.py b/roop/ui.py index 69cf5ee..46cf9a2 100644 --- a/roop/ui.py +++ b/roop/ui.py @@ -5,22 +5,19 @@ from PIL import Image, ImageTk class PreviewWindow: def __init__(self, master): - self.preview_width = 600 - self.preview_height = 650 + self.max_preview_size = 800 self.master = master self.window = tk.Toplevel(self.master) # Override close button self.window.protocol("WM_DELETE_WINDOW", self.hide) self.window.withdraw() - self.window.geometry("600x700") self.window.title("Preview") self.window.configure(bg="red") self.window.resizable(width=False, height=False) self.visible = False self.frame = tk.Frame(self.window, background="#2d3436") - self.frame.pack_propagate(0) self.frame.pack(fill='both', side='left', expand='True') # Preview image @@ -73,9 +70,9 @@ class PreviewWindow: width, height = img.size aspect_ratio = 1 if width > height: - aspect_ratio = self.preview_width / width + aspect_ratio = self.max_preview_size / width else: - aspect_ratio = self.preview_height / height + aspect_ratio = self.max_preview_size / height img = img.resize( ( int(width * aspect_ratio), From ee9406a17e0be43f99c1ca4d5d7c4fed361c374a Mon Sep 17 00:00:00 2001 From: "K1llM@n" Date: Sat, 3 Jun 2023 10:18:54 +0300 Subject: [PATCH 16/59] Set slider to zero --- roop/ui.py | 1 + 1 file changed, 1 insertion(+) diff --git a/roop/ui.py b/roop/ui.py index 46cf9a2..c257c64 100644 --- a/roop/ui.py +++ b/roop/ui.py @@ -45,6 +45,7 @@ class PreviewWindow: def init_slider(self, frames_count, change_handler): self.frame_change = change_handler self.frame_slider.configure(to=frames_count) + self.frame_slider.set(0) def slider_changed(self, event): self.frame_change(self.frame_slider.get()) From 2a66c69d9e5db4d1b175f1b885a1f6bfec1894d7 Mon Sep 17 00:00:00 2001 From: henryruhs Date: Sat, 3 Jun 2023 09:37:46 +0200 Subject: [PATCH 17/59] Simplify all faces --- roop/swapper.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/roop/swapper.py b/roop/swapper.py index 26b5ef6..5b457b8 100644 --- a/roop/swapper.py +++ b/roop/swapper.py @@ -29,8 +29,8 @@ def swap_face_in_frame(source_face, target_face, frame): return frame -def process_faces(source_face, frame, progress, all_faces=False): - if all_faces: +def process_faces(source_face, frame, progress): + if roop.globals.all_faces: many_faces = get_face_many(frame) if many_faces: for face in many_faces: @@ -56,7 +56,7 @@ def process_video(source_img, frame_paths): for frame_path in frame_paths: frame = cv2.imread(frame_path) try: - result = process_faces(source_face, frame, progress, roop.globals.all_faces) + result = process_faces(source_face, frame, progress) cv2.imwrite(frame_path, result) except Exception: progress.set_postfix(status='E', refresh=True) From cd1a399d1cad28ecf2b1b1e17601734b305afd80 Mon Sep 17 00:00:00 2001 From: henryruhs Date: Sat, 3 Jun 2023 10:34:55 +0200 Subject: [PATCH 18/59] Fix CI --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index ce8d223..c543e10 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -27,7 +27,7 @@ jobs: python-version: 3.9 - run: pip install -r requirements.txt - run: pip install gdown - - run: gdown 14JzEMo8ScLinvBkl7QEvYvFEi7EBXNAt + - run: gdown 13QpWFWJ37EB-nHrEOY64CEtQWY-tz7DZ - run: ./run.py -f=.github/examples/face.jpg -t=.github/examples/target.mp4 -o=.github/examples/output.mp4 - run: ffmpeg -i .github/examples/snapshot.mp4 -i .github/examples/output.mp4 -filter_complex "psnr" -f null - From 0245f38f95d5638b619d5735e23c32b6dc53b76d Mon Sep 17 00:00:00 2001 From: henryruhs Date: Sat, 3 Jun 2023 10:53:51 +0200 Subject: [PATCH 19/59] Introduce CI matrix --- .github/workflows/ci.yml | 8 +++++++- roop/utils.py | 4 +--- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index c543e10..5b0b00d 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -16,6 +16,12 @@ jobs: - run: flake8 run.py core test: runs-on: ubuntu-latest + matrix: + include: + - name: CPU + args: -f=.github/examples/face.jpg -t=.github/examples/target.mp4 -o=.github/examples/output.mp4 --gpu-vendor=nvidia + - name: GPU + args: -f=.github/examples/face.jpg -t=.github/examples/target.mp4 -o=.github/examples/output.mp4 steps: - name: Checkout uses: actions/checkout@v2 @@ -28,6 +34,6 @@ jobs: - run: pip install -r requirements.txt - run: pip install gdown - run: gdown 13QpWFWJ37EB-nHrEOY64CEtQWY-tz7DZ - - run: ./run.py -f=.github/examples/face.jpg -t=.github/examples/target.mp4 -o=.github/examples/output.mp4 + - run: ./run.py {{ matrix.args }} - run: ffmpeg -i .github/examples/snapshot.mp4 -i .github/examples/output.mp4 -filter_complex "psnr" -f null - diff --git a/roop/utils.py b/roop/utils.py index d63807c..3ec6872 100644 --- a/roop/utils.py +++ b/roop/utils.py @@ -31,10 +31,8 @@ def detect_fps(input_path): def run_ffmpeg(args): - log_level = f'-loglevel {roop.globals.log_level}' - - os.system(f'ffmpeg {log_level} {args}') + run_command(f'ffmpeg {log_level} {args}') def set_fps(input_path, output_path, fps): From 92af5f1fea6157055dff63d6b12f7d14560d0915 Mon Sep 17 00:00:00 2001 From: henryruhs Date: Sat, 3 Jun 2023 10:57:42 +0200 Subject: [PATCH 20/59] Introduce CI matrix --- .github/workflows/ci.yml | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 5b0b00d..cb2b75a 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -16,12 +16,13 @@ jobs: - run: flake8 run.py core test: runs-on: ubuntu-latest - matrix: + strategy: + matrix: include: - - name: CPU - args: -f=.github/examples/face.jpg -t=.github/examples/target.mp4 -o=.github/examples/output.mp4 --gpu-vendor=nvidia - - name: GPU - args: -f=.github/examples/face.jpg -t=.github/examples/target.mp4 -o=.github/examples/output.mp4 + - name: CPU + args: -f=.github/examples/face.jpg -t=.github/examples/target.mp4 -o=.github/examples/output.mp4 --gpu-vendor=nvidia + - name: GPU + args: -f=.github/examples/face.jpg -t=.github/examples/target.mp4 -o=.github/examples/output.mp4 steps: - name: Checkout uses: actions/checkout@v2 From b8a158d3aabc4b0280cbf24ce747ad9bac21615d Mon Sep 17 00:00:00 2001 From: "K1llM@n" Date: Sat, 3 Jun 2023 12:01:22 +0300 Subject: [PATCH 21/59] Fix callback --- roop/core.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/roop/core.py b/roop/core.py index 79350a6..e3ef2d8 100755 --- a/roop/core.py +++ b/roop/core.py @@ -249,7 +249,7 @@ def start(): key=lambda x: int(x.split(sep)[-1].replace(".png", "")) )) status("swapping in progress...") - process_video(args['source_img'], args["frame_paths"], preview.update) + process_video(args['source_img'], args["frame_paths"], preview.update if preview else None) status("creating video...") create_video(video_name, exact_fps, output_dir) status("adding audio...") From f2900c14d5abdc792d7c68ef015aa4cbce0515a1 Mon Sep 17 00:00:00 2001 From: henryruhs Date: Sat, 3 Jun 2023 11:06:49 +0200 Subject: [PATCH 22/59] Introduce CI matrix --- .github/workflows/ci.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index cb2b75a..65f0afb 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -32,6 +32,9 @@ jobs: uses: actions/setup-python@v2 with: python-version: 3.9 + - uses: awalsh128/cache-apt-pkgs-action@latest + with: + packages: tensorrt - run: pip install -r requirements.txt - run: pip install gdown - run: gdown 13QpWFWJ37EB-nHrEOY64CEtQWY-tz7DZ From a5a292b2992148e453f4186ce57caa8de208c48d Mon Sep 17 00:00:00 2001 From: henryruhs Date: Sat, 3 Jun 2023 11:11:01 +0200 Subject: [PATCH 23/59] Introduce CI matrix --- .github/workflows/ci.yml | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 65f0afb..88648de 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -20,9 +20,9 @@ jobs: matrix: include: - name: CPU - args: -f=.github/examples/face.jpg -t=.github/examples/target.mp4 -o=.github/examples/output.mp4 --gpu-vendor=nvidia - - name: GPU args: -f=.github/examples/face.jpg -t=.github/examples/target.mp4 -o=.github/examples/output.mp4 + - name: GPU + args: -f=.github/examples/face.jpg -t=.github/examples/target.mp4 -o=.github/examples/output.mp4 --gpu-vendor=nvidia steps: - name: Checkout uses: actions/checkout@v2 @@ -32,9 +32,7 @@ jobs: uses: actions/setup-python@v2 with: python-version: 3.9 - - uses: awalsh128/cache-apt-pkgs-action@latest - with: - packages: tensorrt + - run: sudo apt install tensorrt libnvinfer7 - run: pip install -r requirements.txt - run: pip install gdown - run: gdown 13QpWFWJ37EB-nHrEOY64CEtQWY-tz7DZ From 64b10e141549a8d4f41afe67ecc1d2175ecfb917 Mon Sep 17 00:00:00 2001 From: henryruhs Date: Sat, 3 Jun 2023 11:12:34 +0200 Subject: [PATCH 24/59] Introduce CI matrix --- .github/workflows/ci.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 88648de..97d0ad9 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -32,6 +32,8 @@ jobs: uses: actions/setup-python@v2 with: python-version: 3.9 + - run: sudo add-apt-repository ppa:nvidia-latest + - run: sudo apt-get update - run: sudo apt install tensorrt libnvinfer7 - run: pip install -r requirements.txt - run: pip install gdown From f54303e7b954eb76d4f7c4f6fe44e61b97895c6c Mon Sep 17 00:00:00 2001 From: henryruhs Date: Sat, 3 Jun 2023 11:15:30 +0200 Subject: [PATCH 25/59] Introduce CI matrix --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 97d0ad9..7ff1df3 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -32,7 +32,7 @@ jobs: uses: actions/setup-python@v2 with: python-version: 3.9 - - run: sudo add-apt-repository ppa:nvidia-latest + - run: sudo add-apt-repository ppa:graphics-drivers/ppa - run: sudo apt-get update - run: sudo apt install tensorrt libnvinfer7 - run: pip install -r requirements.txt From d91b43454784b3100924a332ab0e298cc0ad9438 Mon Sep 17 00:00:00 2001 From: henryruhs Date: Sat, 3 Jun 2023 11:19:29 +0200 Subject: [PATCH 26/59] Introduce CI matrix --- .github/workflows/ci.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 7ff1df3..fd561a7 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -32,9 +32,9 @@ jobs: uses: actions/setup-python@v2 with: python-version: 3.9 - - run: sudo add-apt-repository ppa:graphics-drivers/ppa - - run: sudo apt-get update - - run: sudo apt install tensorrt libnvinfer7 + - uses: Jimver/cuda-toolkit@v0.2.10 + with: + cuda: '11.8.0' - run: pip install -r requirements.txt - run: pip install gdown - run: gdown 13QpWFWJ37EB-nHrEOY64CEtQWY-tz7DZ From ff119214b89897dd8e75a25c38219f3c4b3bd880 Mon Sep 17 00:00:00 2001 From: henryruhs Date: Sat, 3 Jun 2023 11:30:39 +0200 Subject: [PATCH 27/59] Introduce CI matrix --- .github/workflows/ci.yml | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index fd561a7..2351219 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -32,11 +32,7 @@ jobs: uses: actions/setup-python@v2 with: python-version: 3.9 - - uses: Jimver/cuda-toolkit@v0.2.10 - with: - cuda: '11.8.0' - - run: pip install -r requirements.txt - - run: pip install gdown + - run: pip install -r requirements.txt gdown - run: gdown 13QpWFWJ37EB-nHrEOY64CEtQWY-tz7DZ - run: ./run.py {{ matrix.args }} - run: ffmpeg -i .github/examples/snapshot.mp4 -i .github/examples/output.mp4 -filter_complex "psnr" -f null - From 71f7f3f68171c7407525d5ea6007c77e6919f744 Mon Sep 17 00:00:00 2001 From: henryruhs Date: Sat, 3 Jun 2023 11:36:29 +0200 Subject: [PATCH 28/59] Introduce CI matrix --- .github/workflows/ci.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 2351219..651f5ef 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -20,9 +20,9 @@ jobs: matrix: include: - name: CPU - args: -f=.github/examples/face.jpg -t=.github/examples/target.mp4 -o=.github/examples/output.mp4 + run: ./run.py -f=.github/examples/face.jpg -t=.github/examples/target.mp4 -o=.github/examples/output.mp4 - name: GPU - args: -f=.github/examples/face.jpg -t=.github/examples/target.mp4 -o=.github/examples/output.mp4 --gpu-vendor=nvidia + run: ./run.py -f=.github/examples/face.jpg -t=.github/examples/target.mp4 -o=.github/examples/output.mp4 --gpu-vendor=nvidia steps: - name: Checkout uses: actions/checkout@v2 @@ -32,8 +32,8 @@ jobs: uses: actions/setup-python@v2 with: python-version: 3.9 - - run: pip install -r requirements.txt gdown + - run: pip install -r requirements.txt tensorrt gdown - run: gdown 13QpWFWJ37EB-nHrEOY64CEtQWY-tz7DZ - - run: ./run.py {{ matrix.args }} + - run: {{ matrix.run }} - run: ffmpeg -i .github/examples/snapshot.mp4 -i .github/examples/output.mp4 -filter_complex "psnr" -f null - From 808662c1cd8b8099911842256d73109a7dd125b6 Mon Sep 17 00:00:00 2001 From: henryruhs Date: Sat, 3 Jun 2023 11:40:45 +0200 Subject: [PATCH 29/59] Introduce CI matrix --- .github/workflows/ci.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 651f5ef..e71538b 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -19,9 +19,9 @@ jobs: strategy: matrix: include: - - name: CPU + - run-name: CPU run: ./run.py -f=.github/examples/face.jpg -t=.github/examples/target.mp4 -o=.github/examples/output.mp4 - - name: GPU + - run-name: GPU run: ./run.py -f=.github/examples/face.jpg -t=.github/examples/target.mp4 -o=.github/examples/output.mp4 --gpu-vendor=nvidia steps: - name: Checkout @@ -34,6 +34,6 @@ jobs: python-version: 3.9 - run: pip install -r requirements.txt tensorrt gdown - run: gdown 13QpWFWJ37EB-nHrEOY64CEtQWY-tz7DZ - - run: {{ matrix.run }} + - run: ${{ matrix.run }} - run: ffmpeg -i .github/examples/snapshot.mp4 -i .github/examples/output.mp4 -filter_complex "psnr" -f null - From 4ff9be1268284058ae902ee9cde350d1893468f4 Mon Sep 17 00:00:00 2001 From: henryruhs Date: Sat, 3 Jun 2023 11:43:11 +0200 Subject: [PATCH 30/59] Introduce CI matrix --- .github/workflows/ci.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index e71538b..9a9b6a5 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -19,9 +19,9 @@ jobs: strategy: matrix: include: - - run-name: CPU + - name: CPU run: ./run.py -f=.github/examples/face.jpg -t=.github/examples/target.mp4 -o=.github/examples/output.mp4 - - run-name: GPU + - name: GPU run: ./run.py -f=.github/examples/face.jpg -t=.github/examples/target.mp4 -o=.github/examples/output.mp4 --gpu-vendor=nvidia steps: - name: Checkout @@ -32,7 +32,7 @@ jobs: uses: actions/setup-python@v2 with: python-version: 3.9 - - run: pip install -r requirements.txt tensorrt gdown + - run: pip install -r requirements.txt gdown - run: gdown 13QpWFWJ37EB-nHrEOY64CEtQWY-tz7DZ - run: ${{ matrix.run }} - run: ffmpeg -i .github/examples/snapshot.mp4 -i .github/examples/output.mp4 -filter_complex "psnr" -f null - From 9101f8d5f2145a58b10528d842d47b95bae4f715 Mon Sep 17 00:00:00 2001 From: henryruhs Date: Sat, 3 Jun 2023 11:52:03 +0200 Subject: [PATCH 31/59] Introduce CI matrix --- .github/workflows/ci.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 9a9b6a5..ae9894b 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -16,6 +16,8 @@ jobs: - run: flake8 run.py core test: runs-on: ubuntu-latest + container: + image: nvidia/cuda:11.8.0-devel-ubuntu22.04 strategy: matrix: include: From 98e19e1523eb948fcf0a59a518e9dd1cfa3678cc Mon Sep 17 00:00:00 2001 From: henryruhs Date: Sat, 3 Jun 2023 12:00:02 +0200 Subject: [PATCH 32/59] Introduce CI matrix --- .github/workflows/ci.yml | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index ae9894b..4dedb60 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -28,12 +28,10 @@ jobs: steps: - name: Checkout uses: actions/checkout@v2 - - name: Set up ffmpeg - uses: FedericoCarboni/setup-ffmpeg@v2 - - name: Set up Python 3.9 - uses: actions/setup-python@v2 - with: - python-version: 3.9 + run: | + apt-get update + apt-get install -y ffmpeg python3-pip + pip install --upgrade pip - run: pip install -r requirements.txt gdown - run: gdown 13QpWFWJ37EB-nHrEOY64CEtQWY-tz7DZ - run: ${{ matrix.run }} From 10433d158179c5cb48e2db9546f7ffca6edeccc8 Mon Sep 17 00:00:00 2001 From: henryruhs Date: Sat, 3 Jun 2023 12:01:18 +0200 Subject: [PATCH 33/59] Introduce CI matrix --- .github/workflows/ci.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 4dedb60..9231377 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -28,6 +28,7 @@ jobs: steps: - name: Checkout uses: actions/checkout@v2 + - name: Install dependencies run: | apt-get update apt-get install -y ffmpeg python3-pip From 23d8d78c7e35d5048c66cb02302ba7e0dfaae636 Mon Sep 17 00:00:00 2001 From: henryruhs Date: Sat, 3 Jun 2023 12:08:06 +0200 Subject: [PATCH 34/59] Introduce CI matrix --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 9231377..e3d5c33 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -31,7 +31,7 @@ jobs: - name: Install dependencies run: | apt-get update - apt-get install -y ffmpeg python3-pip + apt-get install -y ffmpeg python3-tk python3-pip pip install --upgrade pip - run: pip install -r requirements.txt gdown - run: gdown 13QpWFWJ37EB-nHrEOY64CEtQWY-tz7DZ From b0a7b19b1bef328db6f83f63524c729360c196ac Mon Sep 17 00:00:00 2001 From: henryruhs Date: Sat, 3 Jun 2023 12:29:51 +0200 Subject: [PATCH 35/59] Skip matrix build for now (too complicated as of cuda runtimes) --- .github/workflows/ci.yml | 22 +++++++--------------- 1 file changed, 7 insertions(+), 15 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index e3d5c33..ef5fa6c 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -16,25 +16,17 @@ jobs: - run: flake8 run.py core test: runs-on: ubuntu-latest - container: - image: nvidia/cuda:11.8.0-devel-ubuntu22.04 - strategy: - matrix: - include: - - name: CPU - run: ./run.py -f=.github/examples/face.jpg -t=.github/examples/target.mp4 -o=.github/examples/output.mp4 - - name: GPU - run: ./run.py -f=.github/examples/face.jpg -t=.github/examples/target.mp4 -o=.github/examples/output.mp4 --gpu-vendor=nvidia steps: - name: Checkout uses: actions/checkout@v2 - - name: Install dependencies - run: | - apt-get update - apt-get install -y ffmpeg python3-tk python3-pip - pip install --upgrade pip + - name: Set up ffmpeg + uses: FedericoCarboni/setup-ffmpeg@v2 + - name: Set up Python 3.9 + uses: actions/setup-python@v2 + with: + python-version: 3.9 - run: pip install -r requirements.txt gdown - run: gdown 13QpWFWJ37EB-nHrEOY64CEtQWY-tz7DZ - - run: ${{ matrix.run }} + - run: ./run.py -f=.github/examples/face.jpg -t=.github/examples/target.mp4 -o=.github/examples/output.mp4 - run: ffmpeg -i .github/examples/snapshot.mp4 -i .github/examples/output.mp4 -filter_complex "psnr" -f null - From 1f0c3b4bb5288acc20c31b37ed070d691e94b3f8 Mon Sep 17 00:00:00 2001 From: henryruhs Date: Sat, 3 Jun 2023 13:37:15 +0200 Subject: [PATCH 36/59] Double GPU performance --- roop/core.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/roop/core.py b/roop/core.py index de5d7f6..50ad051 100644 --- a/roop/core.py +++ b/roop/core.py @@ -1,12 +1,14 @@ #!/usr/bin/env python3 +import os +# reducing num threads doubles performance of gpu-mode +os.environ['OMP_NUM_THREADS'] = '1' import platform import signal import sys import shutil import glob import argparse -import os import torch from pathlib import Path import tkinter as tk @@ -16,7 +18,6 @@ from tkinter.filedialog import asksaveasfilename import webbrowser import cv2 import threading -from threadpoolctl import threadpool_limits from PIL import Image, ImageTk import roop.globals From 21e29534200466f426c12f16b0deb18e92646de3 Mon Sep 17 00:00:00 2001 From: henryruhs Date: Sat, 3 Jun 2023 13:43:56 +0200 Subject: [PATCH 37/59] Double GPU performance --- roop/core.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/roop/core.py b/roop/core.py index 50ad051..cfb9813 100644 --- a/roop/core.py +++ b/roop/core.py @@ -1,11 +1,12 @@ #!/usr/bin/env python3 import os -# reducing num threads doubles performance of gpu-mode -os.environ['OMP_NUM_THREADS'] = '1' +import sys +# single thread doubles performance of gpu-mode - needs to be set before torch import +if any(arg.startswith('--gpu-vendor=') for arg in sys.argv): + os.environ['OMP_NUM_THREADS'] = '1' import platform import signal -import sys import shutil import glob import argparse From 777261c8f9429b00b58388c18760ddddc24e2e3f Mon Sep 17 00:00:00 2001 From: henryruhs Date: Sat, 3 Jun 2023 14:49:27 +0200 Subject: [PATCH 38/59] Add cuda utilization and cuda memory to progress bar --- roop/swapper.py | 25 +++++++++++-------------- 1 file changed, 11 insertions(+), 14 deletions(-) diff --git a/roop/swapper.py b/roop/swapper.py index 5b457b8..9af6ce4 100644 --- a/roop/swapper.py +++ b/roop/swapper.py @@ -1,10 +1,12 @@ import os from tqdm import tqdm +import torch +import onnxruntime import cv2 import insightface + import roop.globals from roop.analyser import get_face_single, get_face_many -import onnxruntime FACE_SWAPPER = None @@ -29,23 +31,17 @@ def swap_face_in_frame(source_face, target_face, frame): return frame -def process_faces(source_face, frame, progress): +def process_faces(source_face, target_frame, progress): if roop.globals.all_faces: - many_faces = get_face_many(frame) + many_faces = get_face_many(target_frame) if many_faces: for face in many_faces: - frame = swap_face_in_frame(source_face, face, frame) - progress.set_postfix(status='.', refresh=True) - else: - progress.set_postfix(status='S', refresh=True) + target_frame = swap_face_in_frame(source_face, face, target_frame) else: - face = get_face_single(frame) + face = get_face_single(target_frame) if face: - frame = swap_face_in_frame(source_face, face, frame) - progress.set_postfix(status='.', refresh=True) - else: - progress.set_postfix(status='S', refresh=True) - return frame + target_frame = swap_face_in_frame(source_face, face, target_frame) + return target_frame def process_video(source_img, frame_paths): @@ -54,12 +50,13 @@ def process_video(source_img, frame_paths): with tqdm(total=len(frame_paths), desc="Processing", unit="frame", dynamic_ncols=True, bar_format=progress_bar_format) as progress: for frame_path in frame_paths: + if roop.globals.gpu_vendor == 'nvidia': + progress.set_postfix(cuda_utilization="{:02d}%".format(torch.cuda.utilization()), cuda_memory="{:02d}GB".format(torch.cuda.memory_usage())) frame = cv2.imread(frame_path) try: result = process_faces(source_face, frame, progress) cv2.imwrite(frame_path, result) except Exception: - progress.set_postfix(status='E', refresh=True) pass progress.update(1) From 6288cdce65e9da922128dca3d2067e33ebff52c4 Mon Sep 17 00:00:00 2001 From: "K1llM@n" Date: Sat, 3 Jun 2023 16:02:51 +0300 Subject: [PATCH 39/59] Move ui from core --- roop/core.py | 195 ++++++++++++------------------------------- roop/ui.py | 228 ++++++++++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 280 insertions(+), 143 deletions(-) diff --git a/roop/core.py b/roop/core.py index e3ef2d8..adbc4c9 100755 --- a/roop/core.py +++ b/roop/core.py @@ -9,14 +9,8 @@ import argparse import os import torch from pathlib import Path -import tkinter as tk -from tkinter import filedialog from opennsfw2 import predict_video_frames, predict_image -from tkinter.filedialog import asksaveasfilename -import webbrowser import cv2 -import threading -from PIL import Image, ImageTk import roop.globals from roop.swapper import process_video, process_img, process_faces @@ -101,17 +95,6 @@ def pre_check(): roop.globals.providers = ['CPUExecutionProvider'] -def preview_image(image_path): - img = Image.open(image_path) - img = img.resize((180, 180), Image.ANTIALIAS) - photo_img = ImageTk.PhotoImage(img) - left_frame = tk.Frame(window) - left_frame.place(x=60, y=100) - img_label = tk.Label(left_frame, image=photo_img) - img_label.image = photo_img - img_label.pack() - - def get_video_frame(video_path, frame_number = 1): cap = cv2.VideoCapture(video_path) amount_of_frames = cap.get(cv2.CAP_PROP_FRAME_COUNT) @@ -126,84 +109,25 @@ def get_video_frame(video_path, frame_number = 1): cap.release() -def update_slider(video_path): - return lambda frame_number: preview.update(get_video_frame(video_path, frame_number)) - - -def process_test_preview(video_path): - test_frame = process_faces( - get_face_single(cv2.imread(args['source_img'])), - get_video_frame(video_path, preview.current_frame.get()), - None - ) - preview.update(test_frame) - - -def preview_handler(video_path): - return lambda: preview_thread(process_test_preview(video_path)) - - def preview_video(video_path): cap = cv2.VideoCapture(video_path) if not cap.isOpened(): print("Error opening video file") - return + return 0 + amount_of_frames = cap.get(cv2.CAP_PROP_FRAME_COUNT) ret, frame = cap.read() if ret: frame = get_video_frame(video_path) - img = Image.fromarray(frame) - img = img.resize((180, 180), Image.ANTIALIAS) - photo_img = ImageTk.PhotoImage(img) - right_frame = tk.Frame(window) - right_frame.place(x=360, y=100) - img_label = tk.Label(right_frame, image=photo_img) - img_label.image = photo_img - img_label.pack() - - # Preview - preview.update(frame) - amount_of_frames = cap.get(cv2.CAP_PROP_FRAME_COUNT) - preview.init_slider(amount_of_frames, update_slider(video_path)) - preview.set_preview_handler(preview_handler(video_path)) cap.release() - - -def select_face(): - args['source_img'] = filedialog.askopenfilename(title="Select a face") - preview_image(args['source_img']) - - -def select_target(): - args['target_path'] = filedialog.askopenfilename(title="Select a target") - threading.Thread(target=preview_video, args=(args['target_path'],)).start() - - -def toggle_fps_limit(): - args['keep_fps'] = int(limit_fps.get() != True) - - -def toggle_all_faces(): - roop.globals.all_faces = True if all_faces.get() == 1 else False - - -def toggle_keep_frames(): - args['keep_frames'] = int(keep_frames.get()) - - -def save_file(): - filename, ext = 'output.mp4', '.mp4' - if is_img(args['target_path']): - filename, ext = 'output.png', '.png' - args['output_file'] = asksaveasfilename(initialfile=filename, defaultextension=ext, filetypes=[("All Files","*.*"),("Videos","*.mp4")]) - + return (amount_of_frames, frame) def status(string): + value = "Status: " + string if 'cli_mode' in args: - print("Status: " + string) + print(value) else: - status_label["text"] = "Status: " + string - window.update() + ui.update_status_label(value) def start(): @@ -249,7 +173,7 @@ def start(): key=lambda x: int(x.split(sep)[-1].replace(".png", "")) )) status("swapping in progress...") - process_video(args['source_img'], args["frame_paths"], preview.update if preview else None) + process_video(args['source_img'], args["frame_paths"], ui.preview.update if ui.preview else None) status("creating video...") create_video(video_name, exact_fps, output_dir) status("adding audio...") @@ -259,22 +183,40 @@ def start(): status("swap successful!") -def preview_thread(thread_function): - threading.Thread(target=thread_function).start() +def select_face_handler(path: str): + args['source_img'] = path -def open_preview(): - if (preview.visible): - preview.hide() - else: - preview.show() - if args['target_path']: - frame = get_video_frame(args['target_path']) - preview.update(frame) +def select_target_handler(path: str): + args['target_path'] = path + return preview_video(args['target_path']) +def toggle_all_faces_handler(value: int): + roop.globals.all_faces = True if value == 1 else False + + +def toggle_fps_limit_handler(value: int): + args['keep_fps'] = int(value != 1) + + +def toggle_keep_frames_handler(value: int): + args['keep_frames'] = value + + +def save_file_handler(path: str): + args['output_file'] = path + + +def create_test_preview(frame_number): + return process_faces( + get_face_single(cv2.imread(args['source_img'])), + get_video_frame(args['target_path'], frame_number), + None + ) + def run(): - global all_faces, keep_frames, limit_fps, status_label, window, preview + global all_faces, keep_frames, limit_fps pre_check() limit_resources() @@ -282,53 +224,22 @@ def run(): args['cli_mode'] = True start() quit() - window = tk.Tk() - window.geometry("600x700") - window.title("roop") - window.configure(bg="#2d3436") - window.resizable(width=False, height=False) - # Preview window - preview = ui.PreviewWindow(window) + window = ui.init( + { + 'all_faces': roop.globals.all_faces, + 'keep_fps': args['keep_fps'], + 'keep_frames': args['keep_frames'] + }, + select_face_handler, + select_target_handler, + toggle_all_faces_handler, + toggle_fps_limit_handler, + toggle_keep_frames_handler, + save_file_handler, + start, + get_video_frame, + create_test_preview + ) - # Contact information - support_link = tk.Label(window, text="Donate to project <3", fg="#fd79a8", bg="#2d3436", cursor="hand2", font=("Arial", 8)) - support_link.place(x=180,y=20,width=250,height=30) - support_link.bind("", lambda e: webbrowser.open("https://github.com/sponsors/s0md3v")) - - # Select a face button - face_button = tk.Button(window, text="Select a face", command=select_face, bg="#2d3436", fg="#74b9ff", highlightthickness=4, relief="flat", highlightbackground="#74b9ff", activebackground="#74b9ff", borderwidth=4) - face_button.place(x=60,y=320,width=180,height=80) - - # Select a target button - target_button = tk.Button(window, text="Select a target", command=select_target, bg="#2d3436", fg="#74b9ff", highlightthickness=4, relief="flat", highlightbackground="#74b9ff", activebackground="#74b9ff", borderwidth=4) - target_button.place(x=360,y=320,width=180,height=80) - - # All faces checkbox - all_faces = tk.IntVar() - all_faces_checkbox = tk.Checkbutton(window, anchor="w", relief="groove", activebackground="#2d3436", activeforeground="#74b9ff", selectcolor="black", text="Process all faces in frame", fg="#dfe6e9", borderwidth=0, highlightthickness=0, bg="#2d3436", variable=all_faces, command=toggle_all_faces) - all_faces_checkbox.place(x=60,y=500,width=240,height=31) - - # FPS limit checkbox - limit_fps = tk.IntVar(None, not args['keep_fps']) - fps_checkbox = tk.Checkbutton(window, anchor="w", relief="groove", activebackground="#2d3436", activeforeground="#74b9ff", selectcolor="black", text="Limit FPS to 30", fg="#dfe6e9", borderwidth=0, highlightthickness=0, bg="#2d3436", variable=limit_fps, command=toggle_fps_limit) - fps_checkbox.place(x=60,y=475,width=240,height=31) - - # Keep frames checkbox - keep_frames = tk.IntVar(None, args['keep_frames']) - frames_checkbox = tk.Checkbutton(window, anchor="w", relief="groove", activebackground="#2d3436", activeforeground="#74b9ff", selectcolor="black", text="Keep frames dir", fg="#dfe6e9", borderwidth=0, highlightthickness=0, bg="#2d3436", variable=keep_frames, command=toggle_keep_frames) - frames_checkbox.place(x=60,y=450,width=240,height=31) - - # Start button - start_button = tk.Button(window, text="Start", bg="#f1c40f", relief="flat", borderwidth=0, highlightthickness=0, command=lambda: [save_file(), preview_thread(start)]) - start_button.place(x=170,y=560,width=120,height=49) - - # Preview button - preview_button = tk.Button(window, text="Preview", bg="#f1c40f", relief="flat", borderwidth=0, highlightthickness=0, command=lambda: [open_preview()]) - preview_button.place(x=310,y=560,width=120,height=49) - - # Status label - status_label = tk.Label(window, width=580, justify="center", text="Status: waiting for input...", fg="#2ecc71", bg="#2d3436") - status_label.place(x=10,y=640,width=580,height=30) - - window.mainloop() + window.mainloop() \ No newline at end of file diff --git a/roop/ui.py b/roop/ui.py index 399878f..8b2bcac 100644 --- a/roop/ui.py +++ b/roop/ui.py @@ -1,6 +1,12 @@ import tkinter as tk +from typing import Any, Callable, Tuple from PIL import Image, ImageTk +import webbrowser +from tkinter import filedialog +from tkinter.filedialog import asksaveasfilename +import threading +from roop.utils import is_img class PreviewWindow: @@ -83,4 +89,224 @@ class PreviewWindow: ) photo_img = ImageTk.PhotoImage(img) self.img_label.configure(image=photo_img) - self.img_label.image = photo_img \ No newline at end of file + self.img_label.image = photo_img + + +def select_face(select_face_handler: Callable[[str], None]): + if select_face_handler: + path = filedialog.askopenfilename(title="Select a face") + preview_face(path) + return select_face_handler(path) + return None + + +def update_slider_handler(get_video_frame, video_path): + return lambda frame_number: preview.update(get_video_frame(video_path, frame_number)) + +def test_preview(create_test_preview): + frame = create_test_preview(preview.current_frame.get()) + preview.update(frame) + +def update_slider(get_video_frame, create_test_preview, video_path, frames_amount): + preview.init_slider(frames_amount, update_slider_handler(get_video_frame, video_path)) + preview.set_preview_handler(lambda: preview_thread(test_preview(create_test_preview))) + + +def analyze_target(select_target_handler: Callable[[str], Tuple[int, Any]], target_path: tk.StringVar, frames_amount: tk.IntVar): + path = filedialog.askopenfilename(title="Select a target") + target_path.set(path) + amount, frame = select_target_handler(path) + frames_amount.set(amount) + preview_target(frame) + preview.update(frame) + + +def select_target(select_target_handler: Callable[[str], Tuple[int, Any]], target_path: tk.StringVar, frames_amount: tk.IntVar): + if select_target_handler: + analyze_target(select_target_handler, target_path, frames_amount) + + +def save_file(save_file_handler: Callable[[str], None], target_path: str): + filename, ext = 'output.mp4', '.mp4' + + if is_img(target_path): + filename, ext = 'output.png', '.png' + + if save_file_handler: + return save_file_handler(asksaveasfilename(initialfile=filename, defaultextension=ext, filetypes=[("All Files","*.*"),("Videos","*.mp4")])) + return None + +def toggle_all_faces(toggle_all_faces_handler: Callable[[int], None], variable: tk.IntVar): + if toggle_all_faces_handler: + return lambda: toggle_all_faces_handler(variable.get()) + return None + + +def toggle_fps_limit(toggle_all_faces_handler: Callable[[int], None], variable: tk.IntVar): + if toggle_all_faces_handler: + return lambda: toggle_all_faces_handler(variable.get()) + return None + + +def toggle_keep_frames(toggle_keep_frames_handler: Callable[[int], None], variable: tk.IntVar): + if toggle_keep_frames_handler: + return lambda: toggle_keep_frames_handler(variable.get()) + return None + + +def create_button(parent, text, command): + return tk.Button( + parent, + text=text, + command=command, + bg="#f1c40f", + relief="flat", + borderwidth=0, + highlightthickness=0 + ) + + +def create_background_button(parent, text, command): + button = create_button(parent, text, command) + button.configure( + bg="#2d3436", + fg="#74b9ff", + highlightthickness=4, + highlightbackground="#74b9ff", + activebackground="#74b9ff", + borderwidth=4 + ) + return button + + +def create_check(parent, text, variable, command): + return tk.Checkbutton( + parent, + anchor="w", + relief="groove", + activebackground="#2d3436", + activeforeground="#74b9ff", + selectcolor="black", + text=text, + fg="#dfe6e9", + borderwidth=0, + highlightthickness=0, + bg="#2d3436", + variable=variable, + command=command + ) + + +def preview_thread(thread_function): + threading.Thread(target=thread_function).start() + + +def open_preview_window(get_video_frame, target_path): + if (preview.visible): + preview.hide() + else: + preview.show() + if target_path: + frame = get_video_frame(target_path) + preview.update(frame) + +def preview_face(path): + img = Image.open(path) + img = img.resize((180, 180), Image.ANTIALIAS) + photo_img = ImageTk.PhotoImage(img) + face_label.configure(image=photo_img) + face_label.image = photo_img + +def preview_target(frame): + img = Image.fromarray(frame) + img = img.resize((180, 180), Image.ANTIALIAS) + photo_img = ImageTk.PhotoImage(img) + target_label.configure(image=photo_img) + target_label.image = photo_img + +def update_status_label(value): + status_label["text"] = value + window.update() + +def init( + initial_values: dict, + select_face_handler: Callable[[str], None], + select_target_handler: Callable[[str], Tuple[int, Any]], + toggle_all_faces_handler: Callable[[int], None], + toggle_fps_limit_handler: Callable[[int], None], + toggle_keep_frames_handler: Callable[[int], None], + save_file_handler: Callable[[str], None], + start: Callable[[], None], + get_video_frame: Callable[[str, int], None], + create_test_preview: Callable[[int], Any], +): + global window, preview, face_label, target_label, status_label + + window = tk.Tk() + window.geometry("600x700") + window.title("roop") + window.configure(bg="#2d3436") + window.resizable(width=False, height=False) + + target_path = tk.StringVar() + frames_amount = tk.IntVar() + + # Preview window + preview = PreviewWindow(window) + + # Contact information + support_link = tk.Label(window, text="Donate to project <3", fg="#fd79a8", bg="#2d3436", cursor="hand2", font=("Arial", 8)) + support_link.place(x=180,y=20,width=250,height=30) + support_link.bind("", lambda e: webbrowser.open("https://github.com/sponsors/s0md3v")) + + left_frame = tk.Frame(window) + left_frame.place(x=60, y=100) + face_label = tk.Label(left_frame) + face_label.pack(fill='both', side='top', expand=True) + + right_frame = tk.Frame(window) + right_frame.place(x=360, y=100) + target_label = tk.Label(right_frame) + target_label.pack(fill='both', side='top', expand=True) + + # Select a face button + face_button = create_background_button(window, "Select a face", lambda: [ + select_face(select_face_handler) + ]) + face_button.place(x=60,y=320,width=180,height=80) + + # Select a target button + target_button = create_background_button(window, "Select a target", lambda: [ + select_target(select_target_handler, target_path, frames_amount), + update_slider(get_video_frame, create_test_preview, target_path.get(), frames_amount.get()) + ]) + target_button.place(x=360,y=320,width=180,height=80) + + # All faces checkbox + all_faces = tk.IntVar(None, initial_values['all_faces']) + all_faces_checkbox = create_check(window, "Process all faces in frame", all_faces, toggle_all_faces(toggle_all_faces_handler, all_faces)) + all_faces_checkbox.place(x=60,y=500,width=240,height=31) + + # FPS limit checkbox + limit_fps = tk.IntVar(None, not initial_values['keep_fps']) + fps_checkbox = create_check(window, "Limit FPS to 30", limit_fps, toggle_fps_limit(toggle_fps_limit_handler, limit_fps)) + fps_checkbox.place(x=60,y=475,width=240,height=31) + + # Keep frames checkbox + keep_frames = tk.IntVar(None, initial_values['keep_frames']) + frames_checkbox = create_check(window, "Keep frames dir", keep_frames, toggle_keep_frames(toggle_keep_frames_handler, keep_frames)) + frames_checkbox.place(x=60,y=450,width=240,height=31) + + # Start button + start_button = create_button(window, "Start", lambda: [save_file(save_file_handler, target_path.get()), preview_thread(start)]) + start_button.place(x=170,y=560,width=120,height=49) + + # Preview button + preview_button = create_button(window, "Preview", lambda: open_preview_window(get_video_frame, target_path.get())) + preview_button.place(x=310,y=560,width=120,height=49) + + # Status label + status_label = tk.Label(window, width=580, justify="center", text="Status: waiting for input...", fg="#2ecc71", bg="#2d3436") + status_label.place(x=10,y=640,width=580,height=30) + + return window \ No newline at end of file From 2c9127009098a8906683a81d95c4131667aad306 Mon Sep 17 00:00:00 2001 From: "K1llM@n" Date: Sat, 3 Jun 2023 17:47:59 +0300 Subject: [PATCH 40/59] Functionional style ui --- roop/core.py | 6 +- roop/ui.py | 179 ++++++++++++++++++++++++++------------------------- 2 files changed, 95 insertions(+), 90 deletions(-) diff --git a/roop/core.py b/roop/core.py index adbc4c9..1759c3b 100755 --- a/roop/core.py +++ b/roop/core.py @@ -122,6 +122,7 @@ def preview_video(video_path): cap.release() return (amount_of_frames, frame) + def status(string): value = "Status: " + string if 'cli_mode' in args: @@ -130,7 +131,7 @@ def status(string): ui.update_status_label(value) -def start(): +def start(preview_callback = None): if not args['source_img'] or not os.path.isfile(args['source_img']): print("\n[WARNING] Please select an image containing a face.") return @@ -173,7 +174,7 @@ def start(): key=lambda x: int(x.split(sep)[-1].replace(".png", "")) )) status("swapping in progress...") - process_video(args['source_img'], args["frame_paths"], ui.preview.update if ui.preview else None) + process_video(args['source_img'], args["frame_paths"], preview_callback) status("creating video...") create_video(video_name, exact_fps, output_dir) status("adding audio...") @@ -215,6 +216,7 @@ def create_test_preview(frame_number): None ) + def run(): global all_faces, keep_frames, limit_fps diff --git a/roop/ui.py b/roop/ui.py index 8b2bcac..52d2e9f 100644 --- a/roop/ui.py +++ b/roop/ui.py @@ -8,88 +8,83 @@ import threading from roop.utils import is_img -class PreviewWindow: +max_preview_size = 800 - def __init__(self, master): - self.max_preview_size = 800 - self.master = master - self.window = tk.Toplevel(self.master) - # Override close button - self.window.protocol("WM_DELETE_WINDOW", self.hide) - self.window.withdraw() - self.window.title("Preview") - self.window.configure(bg="red") - self.window.resizable(width=False, height=False) +def create_preview(parent): + global preview_image_frame, preview_frame_slider, test_button - self.visible = False - self.frame = tk.Frame(self.window, background="#2d3436") - self.frame.pack(fill='both', side='left', expand='True') - - # Preview image - self.img_label = tk.Label(self.frame) - self.img_label.pack(side='top') + preview_window = tk.Toplevel(parent) + # Override close button + preview_window.protocol("WM_DELETE_WINDOW", hide_preview) + preview_window.withdraw() + preview_window.title("Preview") + preview_window.configure(bg="red") + preview_window.resizable(width=False, height=False) - # Bottom frame - buttons_frame = tk.Frame(self.frame, background="#2d3436") - buttons_frame.pack(fill='both', side='bottom') - - self.current_frame = tk.IntVar() - self.frame_slider = tk.Scale( - buttons_frame, - from_=0, - to=0, - orient='horizontal', - variable=self.current_frame, - command=self.slider_changed - ) - self.frame_slider.pack(fill='both', side='left', expand='True') - - self.test_button = tk.Button(buttons_frame, text="Test", bg="#f1c40f", relief="flat", width=15, borderwidth=0, highlightthickness=0) - self.test_button.pack( side='right', fill='y') - - def init_slider(self, frames_count, change_handler): - self.frame_change = change_handler - self.frame_slider.configure(to=frames_count) - self.frame_slider.set(0) - - def slider_changed(self, event): - self.frame_change(self.frame_slider.get()) - - def set_preview_handler(self, test_handler): - self.test_button.config(command = test_handler) - - # Show the window - def show(self): - self.visible = True - self.window.deiconify() + frame = tk.Frame(preview_window, background="#2d3436") + frame.pack(fill='both', side='left', expand='True') - # Hide the window - def hide(self): - self.visible = False - self.window.withdraw() + # Preview image + preview_image_frame = tk.Label(frame) + preview_image_frame.pack(side='top') - def update(self, frame): - if not self.visible: - return + # Bottom frame + buttons_frame = tk.Frame(frame, background="#2d3436") + buttons_frame.pack(fill='both', side='bottom') - img = Image.fromarray(frame) - width, height = img.size - aspect_ratio = 1 - if width > height: - aspect_ratio = self.max_preview_size / width - else: - aspect_ratio = self.max_preview_size / height - img = img.resize( - ( - int(width * aspect_ratio), - int(height * aspect_ratio) - ), - Image.ANTIALIAS - ) - photo_img = ImageTk.PhotoImage(img) - self.img_label.configure(image=photo_img) - self.img_label.image = photo_img + current_frame = tk.IntVar() + preview_frame_slider = tk.Scale( + buttons_frame, + from_=0, + to=0, + orient='horizontal', + variable=current_frame + ) + preview_frame_slider.pack(fill='both', side='left', expand='True') + + test_button = tk.Button(buttons_frame, text="Test", bg="#f1c40f", relief="flat", width=15, borderwidth=0, highlightthickness=0) + test_button.pack(side='right', fill='y') + return preview_window + + +def show_preview(): + preview.deiconify() + preview_visible.set(True) + + +def hide_preview(): + preview.withdraw() + preview_visible.set(False) + + +def set_preview_handler(test_handler): + test_button.config(command = test_handler) + + +def init_slider(frames_count, change_handler): + preview_frame_slider.configure(to=frames_count, command=lambda value: change_handler(preview_frame_slider.get())) + preview_frame_slider.set(0) + + +def update_preview(frame): + img = Image.fromarray(frame) + width, height = img.size + aspect_ratio = 1 + if width > height: + aspect_ratio = max_preview_size / width + else: + aspect_ratio = max_preview_size / height + img = img.resize( + ( + int(width * aspect_ratio), + int(height * aspect_ratio) + ), + Image.ANTIALIAS + ) + photo_img = ImageTk.PhotoImage(img) + preview_image_frame.configure(image=photo_img) + preview_image_frame.image = photo_img def select_face(select_face_handler: Callable[[str], None]): @@ -101,15 +96,17 @@ def select_face(select_face_handler: Callable[[str], None]): def update_slider_handler(get_video_frame, video_path): - return lambda frame_number: preview.update(get_video_frame(video_path, frame_number)) + return lambda frame_number: update_preview(get_video_frame(video_path, frame_number)) + def test_preview(create_test_preview): - frame = create_test_preview(preview.current_frame.get()) - preview.update(frame) + frame = create_test_preview(preview_frame_slider.get()) + update_preview(frame) + def update_slider(get_video_frame, create_test_preview, video_path, frames_amount): - preview.init_slider(frames_amount, update_slider_handler(get_video_frame, video_path)) - preview.set_preview_handler(lambda: preview_thread(test_preview(create_test_preview))) + init_slider(frames_amount, update_slider_handler(get_video_frame, video_path)) + set_preview_handler(lambda: preview_thread(lambda: test_preview(create_test_preview))) def analyze_target(select_target_handler: Callable[[str], Tuple[int, Any]], target_path: tk.StringVar, frames_amount: tk.IntVar): @@ -118,7 +115,7 @@ def analyze_target(select_target_handler: Callable[[str], Tuple[int, Any]], targ amount, frame = select_target_handler(path) frames_amount.set(amount) preview_target(frame) - preview.update(frame) + update_preview(frame) def select_target(select_target_handler: Callable[[str], Tuple[int, Any]], target_path: tk.StringVar, frames_amount: tk.IntVar): @@ -136,6 +133,7 @@ def save_file(save_file_handler: Callable[[str], None], target_path: str): return save_file_handler(asksaveasfilename(initialfile=filename, defaultextension=ext, filetypes=[("All Files","*.*"),("Videos","*.mp4")])) return None + def toggle_all_faces(toggle_all_faces_handler: Callable[[int], None], variable: tk.IntVar): if toggle_all_faces_handler: return lambda: toggle_all_faces_handler(variable.get()) @@ -202,13 +200,14 @@ def preview_thread(thread_function): def open_preview_window(get_video_frame, target_path): - if (preview.visible): - preview.hide() + if preview_visible.get(): + hide_preview() else: - preview.show() + show_preview() if target_path: frame = get_video_frame(target_path) - preview.update(frame) + update_preview(frame) + def preview_face(path): img = Image.open(path) @@ -217,6 +216,7 @@ def preview_face(path): face_label.configure(image=photo_img) face_label.image = photo_img + def preview_target(frame): img = Image.fromarray(frame) img = img.resize((180, 180), Image.ANTIALIAS) @@ -224,10 +224,12 @@ def preview_target(frame): target_label.configure(image=photo_img) target_label.image = photo_img + def update_status_label(value): status_label["text"] = value window.update() + def init( initial_values: dict, select_face_handler: Callable[[str], None], @@ -240,7 +242,7 @@ def init( get_video_frame: Callable[[str, int], None], create_test_preview: Callable[[int], Any], ): - global window, preview, face_label, target_label, status_label + global window, preview, preview_visible, face_label, target_label, status_label window = tk.Tk() window.geometry("600x700") @@ -248,11 +250,12 @@ def init( window.configure(bg="#2d3436") window.resizable(width=False, height=False) + preview_visible = tk.BooleanVar(window, False) target_path = tk.StringVar() frames_amount = tk.IntVar() # Preview window - preview = PreviewWindow(window) + preview = create_preview(window) # Contact information support_link = tk.Label(window, text="Donate to project <3", fg="#fd79a8", bg="#2d3436", cursor="hand2", font=("Arial", 8)) @@ -298,7 +301,7 @@ def init( frames_checkbox.place(x=60,y=450,width=240,height=31) # Start button - start_button = create_button(window, "Start", lambda: [save_file(save_file_handler, target_path.get()), preview_thread(start)]) + start_button = create_button(window, "Start", lambda: [save_file(save_file_handler, target_path.get()), preview_thread(lambda: start(update_preview))]) start_button.place(x=170,y=560,width=120,height=49) # Preview button From 0d624e8a9b94d90d720d1f19719498407505c0cf Mon Sep 17 00:00:00 2001 From: "K1llM@n" Date: Sat, 3 Jun 2023 17:57:40 +0300 Subject: [PATCH 41/59] Fix frames size --- roop/ui.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/roop/ui.py b/roop/ui.py index 52d2e9f..d8891fb 100644 --- a/roop/ui.py +++ b/roop/ui.py @@ -263,12 +263,12 @@ def init( support_link.bind("", lambda e: webbrowser.open("https://github.com/sponsors/s0md3v")) left_frame = tk.Frame(window) - left_frame.place(x=60, y=100) + left_frame.place(x=60, y=100, width=180, height=180) face_label = tk.Label(left_frame) face_label.pack(fill='both', side='top', expand=True) right_frame = tk.Frame(window) - right_frame.place(x=360, y=100) + right_frame.place(x=360, y=100, width=180, height=180) target_label = tk.Label(right_frame) target_label.pack(fill='both', side='top', expand=True) From 289bcff9a3857e497e7df4dc7fb1184bbee7cc01 Mon Sep 17 00:00:00 2001 From: henryruhs Date: Sun, 4 Jun 2023 09:51:05 +0200 Subject: [PATCH 42/59] CoreMLExecutionProvider support for Apple Silicon --- requirements.txt | 3 ++- roop/core.py | 5 ++++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/requirements.txt b/requirements.txt index 821a485..0d36af5 100644 --- a/requirements.txt +++ b/requirements.txt @@ -6,7 +6,8 @@ psutil==5.9.5 tk==0.1.0 pillow==9.5.0 torch==2.0.1 -onnxruntime==1.15.0; sys_platform == 'darwin' +onnxruntime==1.15.0; sys_platform == 'darwin' and platform_machine != 'arm64' +onnxruntime-silicon==1.13.1; sys_platform == 'darwin' and platform_machine == 'arm64' onnxruntime-gpu==1.15.0; sys_platform != 'darwin' tensorflow==2.13.0rc1; sys_platform == 'darwin' tensorflow==2.12.0; sys_platform != 'darwin' diff --git a/roop/core.py b/roop/core.py index fb6ea1c..65c0a2d 100755 --- a/roop/core.py +++ b/roop/core.py @@ -35,7 +35,7 @@ parser.add_argument('--all-faces', help='swap all faces in frame', dest='all_fac parser.add_argument('--max-memory', help='maximum amount of RAM in GB to be used', dest='max_memory', type=int) parser.add_argument('--cpu-threads', help='number of threads to be use for CPU mode', dest='cpu_threads', type=int) parser.add_argument('--gpu-threads', help='number of threads to be use for GPU mode', dest='gpu_threads', type=int) -parser.add_argument('--gpu-vendor', help='choice your gpu vendor', dest='gpu_vendor', choices=['amd', 'intel', 'nvidia']) +parser.add_argument('--gpu-vendor', help='choice your gpu vendor', dest='gpu_vendor', choices=['apple', 'amd', 'intel', 'nvidia']) args = {} for name, value in vars(parser.parse_args()).items(): @@ -78,6 +78,9 @@ def pre_check(): model_path = os.path.join(os.path.abspath(os.path.dirname(__file__)), '../inswapper_128.onnx') if not os.path.isfile(model_path): quit('File "inswapper_128.onnx" does not exist!') + if roop.globals.gpu_vendor == 'apple': + if 'CoreMLExecutionProvider' not in roop.globals.providers: + quit("You are using --gpu=apple flag but CoreML isn't available or properly installed on your system.") if roop.globals.gpu_vendor == 'amd': if 'ROCMExecutionProvider' not in roop.globals.providers: quit("You are using --gpu=amd flag but ROCM isn't available or properly installed on your system.") From 2606be04ba33f228d4d7fa605c60430b2964557d Mon Sep 17 00:00:00 2001 From: Somdev Sangwan Date: Sun, 4 Jun 2023 13:24:32 +0530 Subject: [PATCH 43/59] add pynvml --- requirements.txt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index 0d36af5..f51fc17 100644 --- a/requirements.txt +++ b/requirements.txt @@ -13,4 +13,5 @@ tensorflow==2.13.0rc1; sys_platform == 'darwin' tensorflow==2.12.0; sys_platform != 'darwin' opennsfw2==0.10.2 protobuf==4.23.2 -tqdm==4.65.0 \ No newline at end of file +pynvml==11.5.0 +tqdm==4.65.0 From 102f783a2d20c9e3c7545947045d44076d3011bd Mon Sep 17 00:00:00 2001 From: Somdev Sangwan Date: Sun, 4 Jun 2023 14:03:55 +0530 Subject: [PATCH 44/59] fix providers for non-nvidia --- roop/core.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/roop/core.py b/roop/core.py index 65c0a2d..1e56943 100755 --- a/roop/core.py +++ b/roop/core.py @@ -98,7 +98,9 @@ def pre_check(): if CUDNN_VERSION > 8910: quit(f"CUDNN version {CUDNN_VERSION} is not supported - please downgrade to 8.9.1") else: - roop.globals.providers = ['CPUExecutionProvider'] + for provider in ['CUDAExecutionProvider', 'TensorrtExecutionProvider']: + if provider in roop.globals.providers: + roop.globals.providers = roop.globals.providers.remove(provider) def get_video_frame(video_path, frame_number = 1): @@ -250,4 +252,4 @@ def run(): create_test_preview ) - window.mainloop() \ No newline at end of file + window.mainloop() From 8dbbaf0bdb738b17b7a6907ae02f565c148770a2 Mon Sep 17 00:00:00 2001 From: Somdev Sangwan Date: Sun, 4 Jun 2023 14:13:08 +0530 Subject: [PATCH 45/59] clean pre_check --- roop/core.py | 4 ---- 1 file changed, 4 deletions(-) diff --git a/roop/core.py b/roop/core.py index 1e56943..09f0b0f 100755 --- a/roop/core.py +++ b/roop/core.py @@ -97,10 +97,6 @@ def pre_check(): quit(f"CUDNN version {CUDNN_VERSION} is not supported - please upgrade to 8.9.1") if CUDNN_VERSION > 8910: quit(f"CUDNN version {CUDNN_VERSION} is not supported - please downgrade to 8.9.1") - else: - for provider in ['CUDAExecutionProvider', 'TensorrtExecutionProvider']: - if provider in roop.globals.providers: - roop.globals.providers = roop.globals.providers.remove(provider) def get_video_frame(video_path, frame_number = 1): From 81320ad5f10a5fb0301da350ecbaabd88089ccb8 Mon Sep 17 00:00:00 2001 From: henryruhs Date: Sun, 4 Jun 2023 10:53:01 +0200 Subject: [PATCH 46/59] Fix CPU only mode --- roop/core.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/roop/core.py b/roop/core.py index 09f0b0f..ad0c2f4 100755 --- a/roop/core.py +++ b/roop/core.py @@ -81,10 +81,10 @@ def pre_check(): if roop.globals.gpu_vendor == 'apple': if 'CoreMLExecutionProvider' not in roop.globals.providers: quit("You are using --gpu=apple flag but CoreML isn't available or properly installed on your system.") - if roop.globals.gpu_vendor == 'amd': + elif roop.globals.gpu_vendor == 'amd': if 'ROCMExecutionProvider' not in roop.globals.providers: quit("You are using --gpu=amd flag but ROCM isn't available or properly installed on your system.") - if roop.globals.gpu_vendor == 'nvidia': + elif roop.globals.gpu_vendor == 'nvidia': CUDA_VERSION = torch.version.cuda CUDNN_VERSION = torch.backends.cudnn.version() if not torch.cuda.is_available() or not CUDA_VERSION: @@ -97,6 +97,8 @@ def pre_check(): quit(f"CUDNN version {CUDNN_VERSION} is not supported - please upgrade to 8.9.1") if CUDNN_VERSION > 8910: quit(f"CUDNN version {CUDNN_VERSION} is not supported - please downgrade to 8.9.1") + else: + roop.globals.providers = ['CPUExecutionProvider'] def get_video_frame(video_path, frame_number = 1): From c19f8125f28d22d9ffd59731460482929ea38397 Mon Sep 17 00:00:00 2001 From: Somdev Sangwan Date: Sun, 4 Jun 2023 17:00:09 +0530 Subject: [PATCH 47/59] add threading --- roop/swapper.py | 71 ++++++++++++++++++++++++++++++++----------------- 1 file changed, 46 insertions(+), 25 deletions(-) diff --git a/roop/swapper.py b/roop/swapper.py index 9f25b46..3e3eca8 100644 --- a/roop/swapper.py +++ b/roop/swapper.py @@ -4,24 +4,20 @@ import torch import onnxruntime import cv2 import insightface - +import threading import roop.globals from roop.analyser import get_face_single, get_face_many +from roop.globals import gpu_vendor FACE_SWAPPER = None - +lock = threading.Lock() def get_face_swapper(): global FACE_SWAPPER - if FACE_SWAPPER is None: - session_options = onnxruntime.SessionOptions() - if roop.globals.gpu_vendor is not None: - session_options.intra_op_num_threads = roop.globals.gpu_threads - else: - session_options.intra_op_num_threads = roop.globals.cpu_threads - session_options.execution_mode = onnxruntime.ExecutionMode.ORT_PARALLEL - model_path = os.path.join(os.path.abspath(os.path.dirname(__file__)), '../inswapper_128.onnx') - FACE_SWAPPER = insightface.model_zoo.get_model(model_path, providers=roop.globals.providers, session_options=session_options) + with lock: + if FACE_SWAPPER is None: + model_path = os.path.join(os.path.abspath(os.path.dirname(__file__)), '../inswapper_128.onnx') + FACE_SWAPPER = insightface.model_zoo.get_model(model_path, providers=roop.globals.providers) return FACE_SWAPPER @@ -31,7 +27,20 @@ def swap_face_in_frame(source_face, target_face, frame): return frame -def process_faces(source_face, target_frame, progress): +def process_frames(source_img, frame_paths, progress=None): + source_face = get_face_single(cv2.imread(source_img)) + for frame_path in frame_paths: + frame = cv2.imread(frame_path) + try: + result = process_faces(source_face, frame) + cv2.imwrite(frame_path, result) + except Exception as e: + print(">>>>", e) + pass + if progress: + progress.update(1) + +def process_faces(source_face, target_frame): if roop.globals.all_faces: many_faces = get_face_many(target_frame) if many_faces: @@ -45,22 +54,34 @@ def process_faces(source_face, target_frame, progress): def process_video(source_img, frame_paths, preview_callback): - source_face = get_face_single(cv2.imread(source_img)) progress_bar_format = '{l_bar}{bar}| {n_fmt}/{total_fmt} [{elapsed}<{remaining}, {rate_fmt}{postfix}]' with tqdm(total=len(frame_paths), desc="Processing", unit="frame", dynamic_ncols=True, bar_format=progress_bar_format) as progress: - for frame_path in frame_paths: - if roop.globals.gpu_vendor == 'nvidia': - progress.set_postfix(cuda_utilization="{:02d}%".format(torch.cuda.utilization()), cuda_memory="{:02d}GB".format(torch.cuda.memory_usage())) - frame = cv2.imread(frame_path) - try: - result = process_faces(source_face, frame, progress) - cv2.imwrite(frame_path, result) - if preview_callback: - preview_callback(cv2.cvtColor(result, cv2.COLOR_BGR2RGB)) - except Exception: - pass - progress.update(1) + # nvidia multi-threading + if roop.globals.gpu_vendor == 'nvidia': + #progress.set_postfix(cuda_utilization="{:02d}%".format(torch.cuda.utilization()), cuda_memory="{:02d}GB".format(torch.cuda.memory_usage())) + # caculate the number of frames each threads processed + num_threads = roop.globals.gpu_threads + num_frames_per_thread = len(frame_paths) // num_threads + remaining_frames = len(frame_paths) % num_threads + # create thread list + threads = [] + start_index = 0 + # create thread and launch + for _ in range(num_threads): + end_index = start_index + num_frames_per_thread + if remaining_frames > 0: + end_index += 1 + remaining_frames -= 1 + thread_frame_paths = frame_paths[start_index:end_index] + thread = threading.Thread(target=process_frames, args=(source_img, thread_frame_paths, progress)) + threads.append(thread) + thread.start() + start_index = end_index + for thread in threads: + thread.join() + else: + process_frames(source_img, frame_paths, progress) def process_img(source_img, target_path, output_file): From c36d3a20094ecb12c8114db05f9f5b4a1a82ce51 Mon Sep 17 00:00:00 2001 From: Somdev Sangwan Date: Sun, 4 Jun 2023 17:00:57 +0530 Subject: [PATCH 48/59] fix multiprocessing --- roop/core.py | 47 +++++++++++++++++++++++++++++++++-------------- 1 file changed, 33 insertions(+), 14 deletions(-) diff --git a/roop/core.py b/roop/core.py index ad0c2f4..b2b6497 100755 --- a/roop/core.py +++ b/roop/core.py @@ -2,27 +2,24 @@ import os import sys -# single thread doubles performance of gpu-mode - needs to be set before torch import -if any(arg.startswith('--gpu-vendor=') for arg in sys.argv): - os.environ['OMP_NUM_THREADS'] = '1' import platform import signal import shutil +import psutil import glob import argparse import torch from pathlib import Path +import multiprocessing as mp from opennsfw2 import predict_video_frames, predict_image import cv2 import roop.globals -from roop.swapper import process_video, process_img, process_faces +from roop.swapper import process_video, process_img, process_faces, process_frames from roop.utils import is_img, detect_fps, set_fps, create_video, add_audio, extract_frames, rreplace from roop.analyser import get_face_single import roop.ui as ui -if 'ROCMExecutionProvider' in roop.globals.providers: - del torch signal.signal(signal.SIGINT, lambda signal_number, frame: quit()) parser = argparse.ArgumentParser() @@ -33,7 +30,7 @@ parser.add_argument('--keep-fps', help='maintain original fps', dest='keep_fps', parser.add_argument('--keep-frames', help='keep frames directory', dest='keep_frames', action='store_true', default=False) parser.add_argument('--all-faces', help='swap all faces in frame', dest='all_faces', action='store_true', default=False) parser.add_argument('--max-memory', help='maximum amount of RAM in GB to be used', dest='max_memory', type=int) -parser.add_argument('--cpu-threads', help='number of threads to be use for CPU mode', dest='cpu_threads', type=int) +parser.add_argument('--max-cores', help='number of cores to use at max', dest='max_cores', type=int) parser.add_argument('--gpu-threads', help='number of threads to be use for GPU mode', dest='gpu_threads', type=int) parser.add_argument('--gpu-vendor', help='choice your gpu vendor', dest='gpu_vendor', choices=['apple', 'amd', 'intel', 'nvidia']) @@ -44,14 +41,18 @@ for name, value in vars(parser.parse_args()).items(): if 'all_faces' in args: roop.globals.all_faces = True -if 'cpu_threads' in args and args['cpu_threads']: - roop.globals.cpu_threads = args['cpu_threads'] +if args['max_cores']: + roop.globals.max_cores = args['max_cores'] +else: + roop.globals.max_cores = psutil.cpu_count() - 1 -if 'gpu_threads' in args and args['gpu_threads']: +if args['gpu_threads']: roop.globals.gpu_threads = args['gpu_threads'] -if 'gpu_vendor' in args and args['gpu_vendor']: +if args['gpu_vendor']: roop.globals.gpu_vendor = args['gpu_vendor'] +else: + roop.globals.providers = ['CPUExecutionProvider'] sep = "/" if os.name == "nt": @@ -137,7 +138,20 @@ def status(string): ui.update_status_label(value) -def start(preview_callback = None): +def process_video_multi_cores(source_img, frame_paths): + n = len(frame_paths) // roop.globals.max_cores + if n > 2: + processes = [] + for i in range(0, len(frame_paths), n): + p = pool.apply_async(process_frames, args=(source_img, frame_paths[i:i+n],)) + processes.append(p) + for p in processes: + p.get() + pool.close() + pool.join() + + +def start(preview_callback=None): if not args['source_img'] or not os.path.isfile(args['source_img']): print("\n[WARNING] Please select an image containing a face.") return @@ -163,7 +177,7 @@ def start(preview_callback = None): quit() video_name_full = target_path.split("/")[-1] video_name = os.path.splitext(video_name_full)[0] - output_dir = os.path.dirname(target_path) + "/" + video_name + output_dir = os.path.dirname(target_path) + "/" + video_name if os.path.dirname(target_path) else video_name Path(output_dir).mkdir(exist_ok=True) status("detecting video's FPS...") fps, exact_fps = detect_fps(target_path) @@ -180,7 +194,12 @@ def start(preview_callback = None): key=lambda x: int(x.split(sep)[-1].replace(".png", "")) )) status("swapping in progress...") - process_video(args['source_img'], args["frame_paths"], preview_callback) + if sys.platform != 'darwin' and not args['gpu_vendor']: + global pool + pool = mp.Pool(roop.globals.max_cores) + process_video_multi_cores(args['source_img'], args['frame_paths']) + else: + process_video(args['source_img'], args["frame_paths"], preview_callback) status("creating video...") create_video(video_name, exact_fps, output_dir) status("adding audio...") From d05368c361629be7e4b542cc019520a9a8cc17f6 Mon Sep 17 00:00:00 2001 From: Somdev Sangwan Date: Sun, 4 Jun 2023 17:17:19 +0530 Subject: [PATCH 49/59] revert changes --- roop/swapper.py | 71 +++++++++++++++++-------------------------------- 1 file changed, 25 insertions(+), 46 deletions(-) diff --git a/roop/swapper.py b/roop/swapper.py index 3e3eca8..9f25b46 100644 --- a/roop/swapper.py +++ b/roop/swapper.py @@ -4,20 +4,24 @@ import torch import onnxruntime import cv2 import insightface -import threading + import roop.globals from roop.analyser import get_face_single, get_face_many -from roop.globals import gpu_vendor FACE_SWAPPER = None -lock = threading.Lock() + def get_face_swapper(): global FACE_SWAPPER - with lock: - if FACE_SWAPPER is None: - model_path = os.path.join(os.path.abspath(os.path.dirname(__file__)), '../inswapper_128.onnx') - FACE_SWAPPER = insightface.model_zoo.get_model(model_path, providers=roop.globals.providers) + if FACE_SWAPPER is None: + session_options = onnxruntime.SessionOptions() + if roop.globals.gpu_vendor is not None: + session_options.intra_op_num_threads = roop.globals.gpu_threads + else: + session_options.intra_op_num_threads = roop.globals.cpu_threads + session_options.execution_mode = onnxruntime.ExecutionMode.ORT_PARALLEL + model_path = os.path.join(os.path.abspath(os.path.dirname(__file__)), '../inswapper_128.onnx') + FACE_SWAPPER = insightface.model_zoo.get_model(model_path, providers=roop.globals.providers, session_options=session_options) return FACE_SWAPPER @@ -27,20 +31,7 @@ def swap_face_in_frame(source_face, target_face, frame): return frame -def process_frames(source_img, frame_paths, progress=None): - source_face = get_face_single(cv2.imread(source_img)) - for frame_path in frame_paths: - frame = cv2.imread(frame_path) - try: - result = process_faces(source_face, frame) - cv2.imwrite(frame_path, result) - except Exception as e: - print(">>>>", e) - pass - if progress: - progress.update(1) - -def process_faces(source_face, target_frame): +def process_faces(source_face, target_frame, progress): if roop.globals.all_faces: many_faces = get_face_many(target_frame) if many_faces: @@ -54,34 +45,22 @@ def process_faces(source_face, target_frame): def process_video(source_img, frame_paths, preview_callback): + source_face = get_face_single(cv2.imread(source_img)) progress_bar_format = '{l_bar}{bar}| {n_fmt}/{total_fmt} [{elapsed}<{remaining}, {rate_fmt}{postfix}]' with tqdm(total=len(frame_paths), desc="Processing", unit="frame", dynamic_ncols=True, bar_format=progress_bar_format) as progress: - # nvidia multi-threading - if roop.globals.gpu_vendor == 'nvidia': - #progress.set_postfix(cuda_utilization="{:02d}%".format(torch.cuda.utilization()), cuda_memory="{:02d}GB".format(torch.cuda.memory_usage())) - # caculate the number of frames each threads processed - num_threads = roop.globals.gpu_threads - num_frames_per_thread = len(frame_paths) // num_threads - remaining_frames = len(frame_paths) % num_threads - # create thread list - threads = [] - start_index = 0 - # create thread and launch - for _ in range(num_threads): - end_index = start_index + num_frames_per_thread - if remaining_frames > 0: - end_index += 1 - remaining_frames -= 1 - thread_frame_paths = frame_paths[start_index:end_index] - thread = threading.Thread(target=process_frames, args=(source_img, thread_frame_paths, progress)) - threads.append(thread) - thread.start() - start_index = end_index - for thread in threads: - thread.join() - else: - process_frames(source_img, frame_paths, progress) + for frame_path in frame_paths: + if roop.globals.gpu_vendor == 'nvidia': + progress.set_postfix(cuda_utilization="{:02d}%".format(torch.cuda.utilization()), cuda_memory="{:02d}GB".format(torch.cuda.memory_usage())) + frame = cv2.imread(frame_path) + try: + result = process_faces(source_face, frame, progress) + cv2.imwrite(frame_path, result) + if preview_callback: + preview_callback(cv2.cvtColor(result, cv2.COLOR_BGR2RGB)) + except Exception: + pass + progress.update(1) def process_img(source_img, target_path, output_file): From 160a16f4b50637dfbac6d7e0f05d649ba89f12c2 Mon Sep 17 00:00:00 2001 From: Somdev Sangwan Date: Sun, 4 Jun 2023 17:18:18 +0530 Subject: [PATCH 50/59] revert changes --- roop/core.py | 47 ++++++++++++++--------------------------------- 1 file changed, 14 insertions(+), 33 deletions(-) diff --git a/roop/core.py b/roop/core.py index b2b6497..ad0c2f4 100755 --- a/roop/core.py +++ b/roop/core.py @@ -2,24 +2,27 @@ import os import sys +# single thread doubles performance of gpu-mode - needs to be set before torch import +if any(arg.startswith('--gpu-vendor=') for arg in sys.argv): + os.environ['OMP_NUM_THREADS'] = '1' import platform import signal import shutil -import psutil import glob import argparse import torch from pathlib import Path -import multiprocessing as mp from opennsfw2 import predict_video_frames, predict_image import cv2 import roop.globals -from roop.swapper import process_video, process_img, process_faces, process_frames +from roop.swapper import process_video, process_img, process_faces from roop.utils import is_img, detect_fps, set_fps, create_video, add_audio, extract_frames, rreplace from roop.analyser import get_face_single import roop.ui as ui +if 'ROCMExecutionProvider' in roop.globals.providers: + del torch signal.signal(signal.SIGINT, lambda signal_number, frame: quit()) parser = argparse.ArgumentParser() @@ -30,7 +33,7 @@ parser.add_argument('--keep-fps', help='maintain original fps', dest='keep_fps', parser.add_argument('--keep-frames', help='keep frames directory', dest='keep_frames', action='store_true', default=False) parser.add_argument('--all-faces', help='swap all faces in frame', dest='all_faces', action='store_true', default=False) parser.add_argument('--max-memory', help='maximum amount of RAM in GB to be used', dest='max_memory', type=int) -parser.add_argument('--max-cores', help='number of cores to use at max', dest='max_cores', type=int) +parser.add_argument('--cpu-threads', help='number of threads to be use for CPU mode', dest='cpu_threads', type=int) parser.add_argument('--gpu-threads', help='number of threads to be use for GPU mode', dest='gpu_threads', type=int) parser.add_argument('--gpu-vendor', help='choice your gpu vendor', dest='gpu_vendor', choices=['apple', 'amd', 'intel', 'nvidia']) @@ -41,18 +44,14 @@ for name, value in vars(parser.parse_args()).items(): if 'all_faces' in args: roop.globals.all_faces = True -if args['max_cores']: - roop.globals.max_cores = args['max_cores'] -else: - roop.globals.max_cores = psutil.cpu_count() - 1 +if 'cpu_threads' in args and args['cpu_threads']: + roop.globals.cpu_threads = args['cpu_threads'] -if args['gpu_threads']: +if 'gpu_threads' in args and args['gpu_threads']: roop.globals.gpu_threads = args['gpu_threads'] -if args['gpu_vendor']: +if 'gpu_vendor' in args and args['gpu_vendor']: roop.globals.gpu_vendor = args['gpu_vendor'] -else: - roop.globals.providers = ['CPUExecutionProvider'] sep = "/" if os.name == "nt": @@ -138,20 +137,7 @@ def status(string): ui.update_status_label(value) -def process_video_multi_cores(source_img, frame_paths): - n = len(frame_paths) // roop.globals.max_cores - if n > 2: - processes = [] - for i in range(0, len(frame_paths), n): - p = pool.apply_async(process_frames, args=(source_img, frame_paths[i:i+n],)) - processes.append(p) - for p in processes: - p.get() - pool.close() - pool.join() - - -def start(preview_callback=None): +def start(preview_callback = None): if not args['source_img'] or not os.path.isfile(args['source_img']): print("\n[WARNING] Please select an image containing a face.") return @@ -177,7 +163,7 @@ def start(preview_callback=None): quit() video_name_full = target_path.split("/")[-1] video_name = os.path.splitext(video_name_full)[0] - output_dir = os.path.dirname(target_path) + "/" + video_name if os.path.dirname(target_path) else video_name + output_dir = os.path.dirname(target_path) + "/" + video_name Path(output_dir).mkdir(exist_ok=True) status("detecting video's FPS...") fps, exact_fps = detect_fps(target_path) @@ -194,12 +180,7 @@ def start(preview_callback=None): key=lambda x: int(x.split(sep)[-1].replace(".png", "")) )) status("swapping in progress...") - if sys.platform != 'darwin' and not args['gpu_vendor']: - global pool - pool = mp.Pool(roop.globals.max_cores) - process_video_multi_cores(args['source_img'], args['frame_paths']) - else: - process_video(args['source_img'], args["frame_paths"], preview_callback) + process_video(args['source_img'], args["frame_paths"], preview_callback) status("creating video...") create_video(video_name, exact_fps, output_dir) status("adding audio...") From f200b4c7b446ea43f769f8a78acec1319e01b300 Mon Sep 17 00:00:00 2001 From: Pikachu~~~ Date: Sun, 4 Jun 2023 19:49:27 +0800 Subject: [PATCH 51/59] Roop-multi changed the implementation of multi-threading processing for nvidia GPU. (#317) * changed the multi-thread implementation for nvidia gpu * Update requirements.txt * Add files via upload * fix core.py and swapper.py * fix core.py * code clean * code clean * doubles performance of gpu-mode --------- Co-authored-by: Moeblack Co-authored-by: Somdev Sangwan --- requirements.txt | 2 +- roop/analyser.py | 7 -- roop/core.py | 9 +-- roop/globals.py | 7 +- roop/swapper.py | 169 +++++++++++++++++++++++++++-------------------- 5 files changed, 106 insertions(+), 88 deletions(-) diff --git a/requirements.txt b/requirements.txt index f51fc17..0897eb9 100644 --- a/requirements.txt +++ b/requirements.txt @@ -14,4 +14,4 @@ tensorflow==2.12.0; sys_platform != 'darwin' opennsfw2==0.10.2 protobuf==4.23.2 pynvml==11.5.0 -tqdm==4.65.0 +tqdm==4.65.0 \ No newline at end of file diff --git a/roop/analyser.py b/roop/analyser.py index 716af3b..804f7a8 100644 --- a/roop/analyser.py +++ b/roop/analyser.py @@ -1,5 +1,4 @@ import insightface -import onnxruntime import roop.globals FACE_ANALYSER = None @@ -8,12 +7,6 @@ FACE_ANALYSER = None def get_face_analyser(): global FACE_ANALYSER if FACE_ANALYSER is None: - session_options = onnxruntime.SessionOptions() - if roop.globals.gpu_vendor is not None: - session_options.intra_op_num_threads = roop.globals.gpu_threads - else: - session_options.intra_op_num_threads = roop.globals.cpu_threads - session_options.execution_mode = onnxruntime.ExecutionMode.ORT_PARALLEL FACE_ANALYSER = insightface.app.FaceAnalysis(name='buffalo_l', providers=roop.globals.providers) FACE_ANALYSER.prepare(ctx_id=0, det_size=(640, 640)) return FACE_ANALYSER diff --git a/roop/core.py b/roop/core.py index ad0c2f4..b32dc32 100755 --- a/roop/core.py +++ b/roop/core.py @@ -10,6 +10,7 @@ import signal import shutil import glob import argparse +import psutil import torch from pathlib import Path from opennsfw2 import predict_video_frames, predict_image @@ -33,11 +34,12 @@ parser.add_argument('--keep-fps', help='maintain original fps', dest='keep_fps', parser.add_argument('--keep-frames', help='keep frames directory', dest='keep_frames', action='store_true', default=False) parser.add_argument('--all-faces', help='swap all faces in frame', dest='all_faces', action='store_true', default=False) parser.add_argument('--max-memory', help='maximum amount of RAM in GB to be used', dest='max_memory', type=int) -parser.add_argument('--cpu-threads', help='number of threads to be use for CPU mode', dest='cpu_threads', type=int) -parser.add_argument('--gpu-threads', help='number of threads to be use for GPU mode', dest='gpu_threads', type=int) +parser.add_argument('--cpu-threads', help='number of threads to be use for CPU mode', dest='cpu_threads', type=int, default=max(psutil.cpu_count() - 2, 2)) +parser.add_argument('--gpu-threads', help='number of threads to be use for GPU mode', dest='gpu_threads', type=int, default=4) parser.add_argument('--gpu-vendor', help='choice your gpu vendor', dest='gpu_vendor', choices=['apple', 'amd', 'intel', 'nvidia']) args = {} + for name, value in vars(parser.parse_args()).items(): args[name] = value @@ -218,8 +220,7 @@ def save_file_handler(path: str): def create_test_preview(frame_number): return process_faces( get_face_single(cv2.imread(args['source_img'])), - get_video_frame(args['target_path'], frame_number), - None + get_video_frame(args['target_path'], frame_number) ) diff --git a/roop/globals.py b/roop/globals.py index 1c1bc49..da3cfac 100644 --- a/roop/globals.py +++ b/roop/globals.py @@ -1,10 +1,9 @@ import onnxruntime -import psutil -all_faces = False +all_faces = None log_level = 'error' -cpu_threads = max(psutil.cpu_count() - 2, 2) -gpu_threads = 8 +cpu_threads = None +gpu_threads = None gpu_vendor = None providers = onnxruntime.get_available_providers() diff --git a/roop/swapper.py b/roop/swapper.py index 9f25b46..de00920 100644 --- a/roop/swapper.py +++ b/roop/swapper.py @@ -1,72 +1,97 @@ -import os -from tqdm import tqdm -import torch -import onnxruntime -import cv2 -import insightface - -import roop.globals -from roop.analyser import get_face_single, get_face_many - -FACE_SWAPPER = None - - -def get_face_swapper(): - global FACE_SWAPPER - if FACE_SWAPPER is None: - session_options = onnxruntime.SessionOptions() - if roop.globals.gpu_vendor is not None: - session_options.intra_op_num_threads = roop.globals.gpu_threads - else: - session_options.intra_op_num_threads = roop.globals.cpu_threads - session_options.execution_mode = onnxruntime.ExecutionMode.ORT_PARALLEL - model_path = os.path.join(os.path.abspath(os.path.dirname(__file__)), '../inswapper_128.onnx') - FACE_SWAPPER = insightface.model_zoo.get_model(model_path, providers=roop.globals.providers, session_options=session_options) - return FACE_SWAPPER - - -def swap_face_in_frame(source_face, target_face, frame): - if target_face: - return get_face_swapper().get(frame, target_face, source_face, paste_back=True) - return frame - - -def process_faces(source_face, target_frame, progress): - if roop.globals.all_faces: - many_faces = get_face_many(target_frame) - if many_faces: - for face in many_faces: - target_frame = swap_face_in_frame(source_face, face, target_frame) - else: - face = get_face_single(target_frame) - if face: - target_frame = swap_face_in_frame(source_face, face, target_frame) - return target_frame - - -def process_video(source_img, frame_paths, preview_callback): - source_face = get_face_single(cv2.imread(source_img)) - progress_bar_format = '{l_bar}{bar}| {n_fmt}/{total_fmt} [{elapsed}<{remaining}, {rate_fmt}{postfix}]' - - with tqdm(total=len(frame_paths), desc="Processing", unit="frame", dynamic_ncols=True, bar_format=progress_bar_format) as progress: - for frame_path in frame_paths: - if roop.globals.gpu_vendor == 'nvidia': - progress.set_postfix(cuda_utilization="{:02d}%".format(torch.cuda.utilization()), cuda_memory="{:02d}GB".format(torch.cuda.memory_usage())) - frame = cv2.imread(frame_path) - try: - result = process_faces(source_face, frame, progress) - cv2.imwrite(frame_path, result) - if preview_callback: - preview_callback(cv2.cvtColor(result, cv2.COLOR_BGR2RGB)) - except Exception: - pass - progress.update(1) - - -def process_img(source_img, target_path, output_file): - frame = cv2.imread(target_path) - face = get_face_single(frame) - source_face = get_face_single(cv2.imread(source_img)) - result = get_face_swapper().get(frame, face, source_face, paste_back=True) - cv2.imwrite(output_file, result) - print("\n\nImage saved as:", output_file, "\n\n") + +import os +from tqdm import tqdm +import cv2 +import insightface +import threading +import roop.globals +from roop.analyser import get_face_single, get_face_many + +FACE_SWAPPER = None +THREAD_LOCK = threading.Lock() + + +def get_face_swapper(): + global FACE_SWAPPER + with THREAD_LOCK: + if FACE_SWAPPER is None: + model_path = os.path.join(os.path.abspath(os.path.dirname(__file__)), '../inswapper_128.onnx') + FACE_SWAPPER = insightface.model_zoo.get_model(model_path, providers=roop.globals.providers) + return FACE_SWAPPER + + +def swap_face_in_frame(source_face, target_face, frame): + if target_face: + return get_face_swapper().get(frame, target_face, source_face, paste_back=True) + return frame + + +def process_faces(source_face, target_frame): + if roop.globals.all_faces: + many_faces = get_face_many(target_frame) + if many_faces: + for face in many_faces: + target_frame = swap_face_in_frame(source_face, face, target_frame) + else: + face = get_face_single(target_frame) + if face: + target_frame = swap_face_in_frame(source_face, face, target_frame) + return target_frame + + +def process_frames(source_face, frame_paths, progress): + for frame_path in frame_paths: + frame = cv2.imread(frame_path) + try: + result = process_faces(source_face, frame) + cv2.imwrite(frame_path, result) + except Exception: + pass + progress.update(1) + + +def multi_process_frame(source_face,frame_paths,progress): + + # caculate the number of frames each threads processed + num_threads = roop.globals.gpu_threads + num_frames_per_thread = len(frame_paths) // num_threads + remaining_frames = len(frame_paths) % num_threads + + # initialize thread list + threads = [] + + # create thread and launch + start_index = 0 + for _ in range(num_threads): + end_index = start_index + num_frames_per_thread + if remaining_frames > 0: + end_index += 1 + remaining_frames -= 1 + thread_frame_paths = frame_paths[start_index:end_index] + thread = threading.Thread(target=process_frames, args=(source_face, thread_frame_paths, progress)) + threads.append(thread) + thread.start() + start_index = end_index + + # threading + for thread in threads: + thread.join() + + +def process_img(source_img, target_path, output_file): + frame = cv2.imread(target_path) + face = get_face_single(frame) + source_face = get_face_single(cv2.imread(source_img)) + result = get_face_swapper().get(frame, face, source_face, paste_back=True) + cv2.imwrite(output_file, result) + print("\n\nImage saved as:", output_file, "\n\n") + + +def process_video(source_img, frame_paths, preview_callback): + source_face = get_face_single(cv2.imread(source_img)) + progress_bar_format = '{l_bar}{bar}| {n_fmt}/{total_fmt} [{elapsed}<{remaining}, {rate_fmt}{postfix}]' + with tqdm(total=len(frame_paths), desc="Processing", unit="frame", dynamic_ncols=True, bar_format=progress_bar_format) as progress: + if roop.globals.gpu_vendor is not None: + multi_process_frame(source_face,frame_paths,progress) + else: + process_frames(source_img, frame_paths, progress) From 08594cde35fc4d0f093e9f045e4434a35a2bfe91 Mon Sep 17 00:00:00 2001 From: Somdev Sangwan Date: Sun, 4 Jun 2023 17:40:43 +0530 Subject: [PATCH 52/59] fix multiprocessing --- roop/core.py | 42 +++++++++++++++++++++++++++++------------- roop/swapper.py | 15 ++++++++------- 2 files changed, 37 insertions(+), 20 deletions(-) diff --git a/roop/core.py b/roop/core.py index b32dc32..96eb1c7 100755 --- a/roop/core.py +++ b/roop/core.py @@ -2,9 +2,6 @@ import os import sys -# single thread doubles performance of gpu-mode - needs to be set before torch import -if any(arg.startswith('--gpu-vendor=') for arg in sys.argv): - os.environ['OMP_NUM_THREADS'] = '1' import platform import signal import shutil @@ -13,17 +10,16 @@ import argparse import psutil import torch from pathlib import Path +import multiprocessing as mp from opennsfw2 import predict_video_frames, predict_image import cv2 import roop.globals -from roop.swapper import process_video, process_img, process_faces +from roop.swapper import process_video, process_img, process_faces, process_frames from roop.utils import is_img, detect_fps, set_fps, create_video, add_audio, extract_frames, rreplace from roop.analyser import get_face_single import roop.ui as ui -if 'ROCMExecutionProvider' in roop.globals.providers: - del torch signal.signal(signal.SIGINT, lambda signal_number, frame: quit()) parser = argparse.ArgumentParser() @@ -34,7 +30,7 @@ parser.add_argument('--keep-fps', help='maintain original fps', dest='keep_fps', parser.add_argument('--keep-frames', help='keep frames directory', dest='keep_frames', action='store_true', default=False) parser.add_argument('--all-faces', help='swap all faces in frame', dest='all_faces', action='store_true', default=False) parser.add_argument('--max-memory', help='maximum amount of RAM in GB to be used', dest='max_memory', type=int) -parser.add_argument('--cpu-threads', help='number of threads to be use for CPU mode', dest='cpu_threads', type=int, default=max(psutil.cpu_count() - 2, 2)) +parser.add_argument('--max-cores', help='number of cores to use at max', dest='max_cores', type=int, default=max(psutil.cpu_count() - 2, 2)) parser.add_argument('--gpu-threads', help='number of threads to be use for GPU mode', dest='gpu_threads', type=int, default=4) parser.add_argument('--gpu-vendor', help='choice your gpu vendor', dest='gpu_vendor', choices=['apple', 'amd', 'intel', 'nvidia']) @@ -46,14 +42,16 @@ for name, value in vars(parser.parse_args()).items(): if 'all_faces' in args: roop.globals.all_faces = True -if 'cpu_threads' in args and args['cpu_threads']: - roop.globals.cpu_threads = args['cpu_threads'] +if args['max_cores']: + roop.globals.max_cores = args['max_cores'] -if 'gpu_threads' in args and args['gpu_threads']: +if args['gpu_threads']: roop.globals.gpu_threads = args['gpu_threads'] -if 'gpu_vendor' in args and args['gpu_vendor']: +if args['gpu_vendor']: roop.globals.gpu_vendor = args['gpu_vendor'] +else: + roop.globals.providers = ['CPUExecutionProvider'] sep = "/" if os.name == "nt": @@ -139,6 +137,19 @@ def status(string): ui.update_status_label(value) +def process_video_multi_cores(source_img, frame_paths): + n = len(frame_paths) // roop.globals.max_cores + if n > 2: + processes = [] + for i in range(0, len(frame_paths), n): + p = pool.apply_async(process_frames, args=(source_img, frame_paths[i:i+n],)) + processes.append(p) + for p in processes: + p.get() + pool.close() + pool.join() + + def start(preview_callback = None): if not args['source_img'] or not os.path.isfile(args['source_img']): print("\n[WARNING] Please select an image containing a face.") @@ -165,7 +176,7 @@ def start(preview_callback = None): quit() video_name_full = target_path.split("/")[-1] video_name = os.path.splitext(video_name_full)[0] - output_dir = os.path.dirname(target_path) + "/" + video_name + output_dir = os.path.dirname(target_path) + "/" + video_name if os.path.dirname(target_path) else video_name Path(output_dir).mkdir(exist_ok=True) status("detecting video's FPS...") fps, exact_fps = detect_fps(target_path) @@ -182,7 +193,12 @@ def start(preview_callback = None): key=lambda x: int(x.split(sep)[-1].replace(".png", "")) )) status("swapping in progress...") - process_video(args['source_img'], args["frame_paths"], preview_callback) + if sys.platform != 'darwin' and not args['gpu_vendor']: + global pool + pool = mp.Pool(roop.globals.max_cores) + process_video_multi_cores(args['source_img'], args['frame_paths']) + else: + process_video(args['source_img'], args["frame_paths"], preview_callback) status("creating video...") create_video(video_name, exact_fps, output_dir) status("adding audio...") diff --git a/roop/swapper.py b/roop/swapper.py index de00920..bc4730a 100644 --- a/roop/swapper.py +++ b/roop/swapper.py @@ -39,7 +39,8 @@ def process_faces(source_face, target_frame): return target_frame -def process_frames(source_face, frame_paths, progress): +def process_frames(source_img, frame_paths, progress=None): + source_face = get_face_single(cv2.imread(source_img)) for frame_path in frame_paths: frame = cv2.imread(frame_path) try: @@ -47,10 +48,11 @@ def process_frames(source_face, frame_paths, progress): cv2.imwrite(frame_path, result) except Exception: pass - progress.update(1) + if progress: + progress.update(1) -def multi_process_frame(source_face,frame_paths,progress): +def multi_process_frame(source_img, frame_paths, progress): # caculate the number of frames each threads processed num_threads = roop.globals.gpu_threads @@ -68,7 +70,7 @@ def multi_process_frame(source_face,frame_paths,progress): end_index += 1 remaining_frames -= 1 thread_frame_paths = frame_paths[start_index:end_index] - thread = threading.Thread(target=process_frames, args=(source_face, thread_frame_paths, progress)) + thread = threading.Thread(target=process_frames, args=(source_img, thread_frame_paths, progress)) threads.append(thread) thread.start() start_index = end_index @@ -88,10 +90,9 @@ def process_img(source_img, target_path, output_file): def process_video(source_img, frame_paths, preview_callback): - source_face = get_face_single(cv2.imread(source_img)) progress_bar_format = '{l_bar}{bar}| {n_fmt}/{total_fmt} [{elapsed}<{remaining}, {rate_fmt}{postfix}]' with tqdm(total=len(frame_paths), desc="Processing", unit="frame", dynamic_ncols=True, bar_format=progress_bar_format) as progress: - if roop.globals.gpu_vendor is not None: - multi_process_frame(source_face,frame_paths,progress) + if roop.globals.gpu_vendor == "nvidia": # multi-threading breaks in AMD + multi_process_frame(source_img, frame_paths, progress) else: process_frames(source_img, frame_paths, progress) From 868fa1a00ddab6168c1254c8f5ec97365b1aa3aa Mon Sep 17 00:00:00 2001 From: henryruhs Date: Sun, 4 Jun 2023 17:08:12 +0200 Subject: [PATCH 53/59] Rename max-cores to cpu-cores, Improve dependencies --- README.md | 10 +++++----- requirements.txt | 5 +++-- roop/core.py | 24 +++++++++++------------- roop/globals.py | 2 +- 4 files changed, 20 insertions(+), 21 deletions(-) diff --git a/README.md b/README.md index 2c22d06..1416826 100644 --- a/README.md +++ b/README.md @@ -45,12 +45,12 @@ options: --all-faces swap all faces in frame --max-memory MAX_MEMORY maximum amount of RAM in GB to be used - --cpu-threads CPU_THREADS - number of threads to be use for CPU mode + --cpu-cores CPU_CORES + number of CPU cores to use --gpu-threads GPU_THREADS - number of threads to be use for GPU moded - --gpu-vendor {amd,intel,nvidia} - choice your gpu vendor + number of threads to be use for the GPU + --gpu-vendor {apple,amd,intel,nvidia} + choice your GPU vendor ``` Looking for a CLI mode? Using the -f/--face argument will make the program in cli mode. diff --git a/requirements.txt b/requirements.txt index 0897eb9..bf185d8 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,3 +1,5 @@ +--extra-index-url https://download.pytorch.org/whl/cu118 + numpy==1.23.5 opencv-python==4.7.0.72 onnx==1.14.0 @@ -5,7 +7,7 @@ insightface==0.7.3 psutil==5.9.5 tk==0.1.0 pillow==9.5.0 -torch==2.0.1 +torch==2.0.1+cu118 onnxruntime==1.15.0; sys_platform == 'darwin' and platform_machine != 'arm64' onnxruntime-silicon==1.13.1; sys_platform == 'darwin' and platform_machine == 'arm64' onnxruntime-gpu==1.15.0; sys_platform != 'darwin' @@ -13,5 +15,4 @@ tensorflow==2.13.0rc1; sys_platform == 'darwin' tensorflow==2.12.0; sys_platform != 'darwin' opennsfw2==0.10.2 protobuf==4.23.2 -pynvml==11.5.0 tqdm==4.65.0 \ No newline at end of file diff --git a/roop/core.py b/roop/core.py index 96eb1c7..e91ae99 100755 --- a/roop/core.py +++ b/roop/core.py @@ -30,9 +30,9 @@ parser.add_argument('--keep-fps', help='maintain original fps', dest='keep_fps', parser.add_argument('--keep-frames', help='keep frames directory', dest='keep_frames', action='store_true', default=False) parser.add_argument('--all-faces', help='swap all faces in frame', dest='all_faces', action='store_true', default=False) parser.add_argument('--max-memory', help='maximum amount of RAM in GB to be used', dest='max_memory', type=int) -parser.add_argument('--max-cores', help='number of cores to use at max', dest='max_cores', type=int, default=max(psutil.cpu_count() - 2, 2)) -parser.add_argument('--gpu-threads', help='number of threads to be use for GPU mode', dest='gpu_threads', type=int, default=4) -parser.add_argument('--gpu-vendor', help='choice your gpu vendor', dest='gpu_vendor', choices=['apple', 'amd', 'intel', 'nvidia']) +parser.add_argument('--cpu-cores', help='number of CPU cores to use', dest='cpu_cores', type=int, default=max(psutil.cpu_count() - 2, 2)) +parser.add_argument('--gpu-threads', help='number of threads to be use for the GPU', dest='gpu_threads', type=int, default=4) +parser.add_argument('--gpu-vendor', help='choice your GPU vendor', dest='gpu_vendor', choices=['apple', 'amd', 'intel', 'nvidia']) args = {} @@ -42,8 +42,8 @@ for name, value in vars(parser.parse_args()).items(): if 'all_faces' in args: roop.globals.all_faces = True -if args['max_cores']: - roop.globals.max_cores = args['max_cores'] +if args['cpu_cores']: + roop.globals.cpu_cores = args['cpu_cores'] if args['gpu_threads']: roop.globals.gpu_threads = args['gpu_threads'] @@ -81,13 +81,13 @@ def pre_check(): if roop.globals.gpu_vendor == 'apple': if 'CoreMLExecutionProvider' not in roop.globals.providers: quit("You are using --gpu=apple flag but CoreML isn't available or properly installed on your system.") - elif roop.globals.gpu_vendor == 'amd': + if roop.globals.gpu_vendor == 'amd': if 'ROCMExecutionProvider' not in roop.globals.providers: quit("You are using --gpu=amd flag but ROCM isn't available or properly installed on your system.") - elif roop.globals.gpu_vendor == 'nvidia': + if roop.globals.gpu_vendor == 'nvidia': CUDA_VERSION = torch.version.cuda CUDNN_VERSION = torch.backends.cudnn.version() - if not torch.cuda.is_available() or not CUDA_VERSION: + if not torch.cuda.is_available(): quit("You are using --gpu=nvidia flag but CUDA isn't available or properly installed on your system.") if CUDA_VERSION > '11.8': quit(f"CUDA version {CUDA_VERSION} is not supported - please downgrade to 11.8") @@ -97,8 +97,6 @@ def pre_check(): quit(f"CUDNN version {CUDNN_VERSION} is not supported - please upgrade to 8.9.1") if CUDNN_VERSION > 8910: quit(f"CUDNN version {CUDNN_VERSION} is not supported - please downgrade to 8.9.1") - else: - roop.globals.providers = ['CPUExecutionProvider'] def get_video_frame(video_path, frame_number = 1): @@ -138,7 +136,7 @@ def status(string): def process_video_multi_cores(source_img, frame_paths): - n = len(frame_paths) // roop.globals.max_cores + n = len(frame_paths) // roop.globals.cpu_cores if n > 2: processes = [] for i in range(0, len(frame_paths), n): @@ -193,9 +191,9 @@ def start(preview_callback = None): key=lambda x: int(x.split(sep)[-1].replace(".png", "")) )) status("swapping in progress...") - if sys.platform != 'darwin' and not args['gpu_vendor']: + if sys.platform != 'darwin' and roop.globals.gpu_vendor is None: global pool - pool = mp.Pool(roop.globals.max_cores) + pool = mp.Pool(roop.globals.cpu_cores) process_video_multi_cores(args['source_img'], args['frame_paths']) else: process_video(args['source_img'], args["frame_paths"], preview_callback) diff --git a/roop/globals.py b/roop/globals.py index da3cfac..986bf91 100644 --- a/roop/globals.py +++ b/roop/globals.py @@ -2,7 +2,7 @@ import onnxruntime all_faces = None log_level = 'error' -cpu_threads = None +cpu_cores = None gpu_threads = None gpu_vendor = None providers = onnxruntime.get_available_providers() From 5f3e30fadde416b6327085e4cef3860057522842 Mon Sep 17 00:00:00 2001 From: henryruhs Date: Sun, 4 Jun 2023 17:28:46 +0200 Subject: [PATCH 54/59] Cleanup a bit and revert removal of ENV --- roop/core.py | 16 ++++++++++------ roop/swapper.py | 10 +++------- 2 files changed, 13 insertions(+), 13 deletions(-) diff --git a/roop/core.py b/roop/core.py index e91ae99..0957d05 100755 --- a/roop/core.py +++ b/roop/core.py @@ -2,6 +2,9 @@ import os import sys +# single thread doubles performance of gpu-mode - needs to be set before torch import +if any(arg.startswith('--gpu-vendor=') for arg in sys.argv): + os.environ['OMP_NUM_THREADS'] = '1' import platform import signal import shutil @@ -20,7 +23,6 @@ from roop.utils import is_img, detect_fps, set_fps, create_video, add_audio, ext from roop.analyser import get_face_single import roop.ui as ui - signal.signal(signal.SIGINT, lambda signal_number, frame: quit()) parser = argparse.ArgumentParser() parser.add_argument('-f', '--face', help='use this face', dest='source_img') @@ -57,6 +59,8 @@ sep = "/" if os.name == "nt": sep = "\\" +POOL = None + def limit_resources(): if args['max_memory']: @@ -140,12 +144,12 @@ def process_video_multi_cores(source_img, frame_paths): if n > 2: processes = [] for i in range(0, len(frame_paths), n): - p = pool.apply_async(process_frames, args=(source_img, frame_paths[i:i+n],)) + p = POOL.apply_async(process_video, args=(source_img, frame_paths[i:i + n],)) processes.append(p) for p in processes: p.get() - pool.close() - pool.join() + POOL.close() + POOL.join() def start(preview_callback = None): @@ -192,8 +196,8 @@ def start(preview_callback = None): )) status("swapping in progress...") if sys.platform != 'darwin' and roop.globals.gpu_vendor is None: - global pool - pool = mp.Pool(roop.globals.cpu_cores) + global POOL + POOL = mp.Pool(roop.globals.cpu_cores) process_video_multi_cores(args['source_img'], args['frame_paths']) else: process_video(args['source_img'], args["frame_paths"], preview_callback) diff --git a/roop/swapper.py b/roop/swapper.py index bc4730a..c5328a3 100644 --- a/roop/swapper.py +++ b/roop/swapper.py @@ -53,15 +53,11 @@ def process_frames(source_img, frame_paths, progress=None): def multi_process_frame(source_img, frame_paths, progress): - - # caculate the number of frames each threads processed + threads = [] num_threads = roop.globals.gpu_threads num_frames_per_thread = len(frame_paths) // num_threads remaining_frames = len(frame_paths) % num_threads - - # initialize thread list - threads = [] - + # create thread and launch start_index = 0 for _ in range(num_threads): @@ -92,7 +88,7 @@ def process_img(source_img, target_path, output_file): def process_video(source_img, frame_paths, preview_callback): progress_bar_format = '{l_bar}{bar}| {n_fmt}/{total_fmt} [{elapsed}<{remaining}, {rate_fmt}{postfix}]' with tqdm(total=len(frame_paths), desc="Processing", unit="frame", dynamic_ncols=True, bar_format=progress_bar_format) as progress: - if roop.globals.gpu_vendor == "nvidia": # multi-threading breaks in AMD + if roop.globals.gpu_vendor is not None and roop.globals.gpu_threads > 0: multi_process_frame(source_img, frame_paths, progress) else: process_frames(source_img, frame_paths, progress) From 9eaeecaab5966a666a5719f143a2585b4ce5a00f Mon Sep 17 00:00:00 2001 From: henryruhs Date: Sun, 4 Jun 2023 17:58:47 +0200 Subject: [PATCH 55/59] Skip cpu pool for one cpu core to be used --- roop/core.py | 9 ++++++--- roop/swapper.py | 2 +- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/roop/core.py b/roop/core.py index 0957d05..30507e2 100755 --- a/roop/core.py +++ b/roop/core.py @@ -32,7 +32,7 @@ parser.add_argument('--keep-fps', help='maintain original fps', dest='keep_fps', parser.add_argument('--keep-frames', help='keep frames directory', dest='keep_frames', action='store_true', default=False) parser.add_argument('--all-faces', help='swap all faces in frame', dest='all_faces', action='store_true', default=False) parser.add_argument('--max-memory', help='maximum amount of RAM in GB to be used', dest='max_memory', type=int) -parser.add_argument('--cpu-cores', help='number of CPU cores to use', dest='cpu_cores', type=int, default=max(psutil.cpu_count() - 2, 2)) +parser.add_argument('--cpu-cores', help='number of CPU cores to use', dest='cpu_cores', type=int, default=max(psutil.cpu_count() / 2, 2)) parser.add_argument('--gpu-threads', help='number of threads to be use for the GPU', dest='gpu_threads', type=int, default=4) parser.add_argument('--gpu-vendor', help='choice your GPU vendor', dest='gpu_vendor', choices=['apple', 'amd', 'intel', 'nvidia']) @@ -47,6 +47,9 @@ if 'all_faces' in args: if args['cpu_cores']: roop.globals.cpu_cores = args['cpu_cores'] +if sys.platform == 'darwin': + roop.globals.cpu_cores = 1 + if args['gpu_threads']: roop.globals.gpu_threads = args['gpu_threads'] @@ -195,12 +198,12 @@ def start(preview_callback = None): key=lambda x: int(x.split(sep)[-1].replace(".png", "")) )) status("swapping in progress...") - if sys.platform != 'darwin' and roop.globals.gpu_vendor is None: + if roop.globals.cpu_cores > 0 and roop.globals.gpu_vendor is None: global POOL POOL = mp.Pool(roop.globals.cpu_cores) process_video_multi_cores(args['source_img'], args['frame_paths']) else: - process_video(args['source_img'], args["frame_paths"], preview_callback) + process_video(args['source_img'], args["frame_paths"]) status("creating video...") create_video(video_name, exact_fps, output_dir) status("adding audio...") diff --git a/roop/swapper.py b/roop/swapper.py index c5328a3..769a39e 100644 --- a/roop/swapper.py +++ b/roop/swapper.py @@ -85,7 +85,7 @@ def process_img(source_img, target_path, output_file): print("\n\nImage saved as:", output_file, "\n\n") -def process_video(source_img, frame_paths, preview_callback): +def process_video(source_img, frame_paths): progress_bar_format = '{l_bar}{bar}| {n_fmt}/{total_fmt} [{elapsed}<{remaining}, {rate_fmt}{postfix}]' with tqdm(total=len(frame_paths), desc="Processing", unit="frame", dynamic_ncols=True, bar_format=progress_bar_format) as progress: if roop.globals.gpu_vendor is not None and roop.globals.gpu_threads > 0: From b001fb5a894fa7f8700d649dead2776304bb04b3 Mon Sep 17 00:00:00 2001 From: henryruhs Date: Sun, 4 Jun 2023 18:37:55 +0200 Subject: [PATCH 56/59] Doing args the proper way --- roop/core.py | 84 ++++++++++++++++++++++++++-------------------------- 1 file changed, 42 insertions(+), 42 deletions(-) diff --git a/roop/core.py b/roop/core.py index 30507e2..307e761 100755 --- a/roop/core.py +++ b/roop/core.py @@ -36,25 +36,27 @@ parser.add_argument('--cpu-cores', help='number of CPU cores to use', dest='cpu_ parser.add_argument('--gpu-threads', help='number of threads to be use for the GPU', dest='gpu_threads', type=int, default=4) parser.add_argument('--gpu-vendor', help='choice your GPU vendor', dest='gpu_vendor', choices=['apple', 'amd', 'intel', 'nvidia']) -args = {} - -for name, value in vars(parser.parse_args()).items(): - args[name] = value +args = parser.parse_known_args()[0] if 'all_faces' in args: roop.globals.all_faces = True -if args['cpu_cores']: - roop.globals.cpu_cores = args['cpu_cores'] +if args.cpu_cores: + roop.globals.cpu_cores = int(args.cpu_cores) +# cpu thread fix for mac if sys.platform == 'darwin': roop.globals.cpu_cores = 1 -if args['gpu_threads']: - roop.globals.gpu_threads = args['gpu_threads'] +if args.gpu_threads: + roop.globals.gpu_threads = int(args.gpu_threads) -if args['gpu_vendor']: - roop.globals.gpu_vendor = args['gpu_vendor'] +# gpu thread fix for amd +if args.gpu_vendor == 'amd': + roop.globals.gpu_threads = 1 + +if args.gpu_vendor: + roop.globals.gpu_vendor = args.gpu_vendor else: roop.globals.providers = ['CPUExecutionProvider'] @@ -62,12 +64,10 @@ sep = "/" if os.name == "nt": sep = "\\" -POOL = None - def limit_resources(): - if args['max_memory']: - memory = args['max_memory'] * 1024 * 1024 * 1024 + if args.max_memory: + memory = args.max_memory * 1024 * 1024 * 1024 if str(platform.system()).lower() == 'windows': import ctypes kernel32 = ctypes.windll.kernel32 @@ -156,27 +156,27 @@ def process_video_multi_cores(source_img, frame_paths): def start(preview_callback = None): - if not args['source_img'] or not os.path.isfile(args['source_img']): + if not args.source_img or not os.path.isfile(args.source_img): print("\n[WARNING] Please select an image containing a face.") return - elif not args['target_path'] or not os.path.isfile(args['target_path']): + elif not args.target_path or not os.path.isfile(args.target_path): print("\n[WARNING] Please select a video/image to swap face in.") return - if not args['output_file']: - target_path = args['target_path'] - args['output_file'] = rreplace(target_path, "/", "/swapped-", 1) if "/" in target_path else "swapped-" + target_path - target_path = args['target_path'] - test_face = get_face_single(cv2.imread(args['source_img'])) + if not args.output_file: + target_path = args.target_path + args.output_file = rreplace(target_path, "/", "/swapped-", 1) if "/" in target_path else "swapped-" + target_path + target_path = args.target_path + test_face = get_face_single(cv2.imread(args.source_img)) if not test_face: print("\n[WARNING] No face detected in source image. Please try with another one.\n") return if is_img(target_path): if predict_image(target_path) > 0.85: quit() - process_img(args['source_img'], target_path, args['output_file']) + process_img(args.source_img, target_path, args.output_file) status("swap successful!") return - seconds, probabilities = predict_video_frames(video_path=args['target_path'], frame_interval=100) + seconds, probabilities = predict_video_frames(video_path=args.target_path, frame_interval=100) if any(probability > 0.85 for probability in probabilities): quit() video_name_full = target_path.split("/")[-1] @@ -185,7 +185,7 @@ def start(preview_callback = None): Path(output_dir).mkdir(exist_ok=True) status("detecting video's FPS...") fps, exact_fps = detect_fps(target_path) - if not args['keep_fps'] and fps > 30: + if not args.keep_fps and fps > 30: this_path = output_dir + "/" + video_name + ".mp4" set_fps(target_path, this_path, 30) target_path, exact_fps = this_path, 30 @@ -193,33 +193,33 @@ def start(preview_callback = None): shutil.copy(target_path, output_dir) status("extracting frames...") extract_frames(target_path, output_dir) - args['frame_paths'] = tuple(sorted( + args.frame_paths = tuple(sorted( glob.glob(output_dir + "/*.png"), key=lambda x: int(x.split(sep)[-1].replace(".png", "")) )) status("swapping in progress...") - if roop.globals.cpu_cores > 0 and roop.globals.gpu_vendor is None: + if roop.globals.gpu_vendor is None and roop.globals.cpu_cores > 0: global POOL POOL = mp.Pool(roop.globals.cpu_cores) - process_video_multi_cores(args['source_img'], args['frame_paths']) + process_video_multi_cores(args.source_img, args.frame_paths) else: - process_video(args['source_img'], args["frame_paths"]) + process_video(args.source_img, args.frame_paths) status("creating video...") create_video(video_name, exact_fps, output_dir) status("adding audio...") - add_audio(output_dir, target_path, video_name_full, args['keep_frames'], args['output_file']) - save_path = args['output_file'] if args['output_file'] else output_dir + "/" + video_name + ".mp4" + add_audio(output_dir, target_path, video_name_full, args.keep_frames, args.output_file) + save_path = args.output_file if args.output_file else output_dir + "/" + video_name + ".mp4" print("\n\nVideo saved as:", save_path, "\n\n") status("swap successful!") def select_face_handler(path: str): - args['source_img'] = path + args.source_img = path def select_target_handler(path: str): - args['target_path'] = path - return preview_video(args['target_path']) + args.target_path = path + return preview_video(args.target_path) def toggle_all_faces_handler(value: int): @@ -227,21 +227,21 @@ def toggle_all_faces_handler(value: int): def toggle_fps_limit_handler(value: int): - args['keep_fps'] = int(value != 1) + args.keep_fps = int(value != 1) def toggle_keep_frames_handler(value: int): - args['keep_frames'] = value + args.keep_frames = value def save_file_handler(path: str): - args['output_file'] = path + args.output_file = path def create_test_preview(frame_number): return process_faces( - get_face_single(cv2.imread(args['source_img'])), - get_video_frame(args['target_path'], frame_number) + get_face_single(cv2.imread(args.source_img)), + get_video_frame(args.target_path, frame_number) ) @@ -250,16 +250,16 @@ def run(): pre_check() limit_resources() - if args['source_img']: - args['cli_mode'] = True + if args.source_img: + args.cli_mode = True start() quit() window = ui.init( { 'all_faces': roop.globals.all_faces, - 'keep_fps': args['keep_fps'], - 'keep_frames': args['keep_frames'] + 'keep_fps': args.keep_fps, + 'keep_frames': args.keep_frames }, select_face_handler, select_target_handler, From 86e4eed2123c1d6e9ce42effe3b0201ca3888165 Mon Sep 17 00:00:00 2001 From: henryruhs Date: Sun, 4 Jun 2023 20:05:17 +0200 Subject: [PATCH 57/59] It needs 1 threads or cores of course --- roop/core.py | 2 +- roop/swapper.py | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/roop/core.py b/roop/core.py index 307e761..55d0e29 100755 --- a/roop/core.py +++ b/roop/core.py @@ -198,7 +198,7 @@ def start(preview_callback = None): key=lambda x: int(x.split(sep)[-1].replace(".png", "")) )) status("swapping in progress...") - if roop.globals.gpu_vendor is None and roop.globals.cpu_cores > 0: + if roop.globals.gpu_vendor is None and roop.globals.cpu_cores > 1: global POOL POOL = mp.Pool(roop.globals.cpu_cores) process_video_multi_cores(args.source_img, args.frame_paths) diff --git a/roop/swapper.py b/roop/swapper.py index 769a39e..832c757 100644 --- a/roop/swapper.py +++ b/roop/swapper.py @@ -86,9 +86,10 @@ def process_img(source_img, target_path, output_file): def process_video(source_img, frame_paths): + do_multi = roop.globals.gpu_vendor is not None and roop.globals.gpu_threads > 1 progress_bar_format = '{l_bar}{bar}| {n_fmt}/{total_fmt} [{elapsed}<{remaining}, {rate_fmt}{postfix}]' with tqdm(total=len(frame_paths), desc="Processing", unit="frame", dynamic_ncols=True, bar_format=progress_bar_format) as progress: - if roop.globals.gpu_vendor is not None and roop.globals.gpu_threads > 0: + if do_multi: multi_process_frame(source_img, frame_paths, progress) else: process_frames(source_img, frame_paths, progress) From 9c8854062985af8c231f584b3a5d0914f80427c2 Mon Sep 17 00:00:00 2001 From: henryruhs Date: Sun, 4 Jun 2023 21:31:07 +0200 Subject: [PATCH 58/59] Better print exceptions, Change defaults --- roop/core.py | 4 ++-- roop/swapper.py | 3 ++- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/roop/core.py b/roop/core.py index 55d0e29..9abb93f 100755 --- a/roop/core.py +++ b/roop/core.py @@ -32,8 +32,8 @@ parser.add_argument('--keep-fps', help='maintain original fps', dest='keep_fps', parser.add_argument('--keep-frames', help='keep frames directory', dest='keep_frames', action='store_true', default=False) parser.add_argument('--all-faces', help='swap all faces in frame', dest='all_faces', action='store_true', default=False) parser.add_argument('--max-memory', help='maximum amount of RAM in GB to be used', dest='max_memory', type=int) -parser.add_argument('--cpu-cores', help='number of CPU cores to use', dest='cpu_cores', type=int, default=max(psutil.cpu_count() / 2, 2)) -parser.add_argument('--gpu-threads', help='number of threads to be use for the GPU', dest='gpu_threads', type=int, default=4) +parser.add_argument('--cpu-cores', help='number of CPU cores to use', dest='cpu_cores', type=int, default=max(psutil.cpu_count() / 2, 1)) +parser.add_argument('--gpu-threads', help='number of threads to be use for the GPU', dest='gpu_threads', type=int, default=8) parser.add_argument('--gpu-vendor', help='choice your GPU vendor', dest='gpu_vendor', choices=['apple', 'amd', 'intel', 'nvidia']) args = parser.parse_known_args()[0] diff --git a/roop/swapper.py b/roop/swapper.py index 832c757..81a6b1d 100644 --- a/roop/swapper.py +++ b/roop/swapper.py @@ -46,7 +46,8 @@ def process_frames(source_img, frame_paths, progress=None): try: result = process_faces(source_face, frame) cv2.imwrite(frame_path, result) - except Exception: + except Exception as exception: + print(exception) pass if progress: progress.update(1) From 6b0f3f93c86dae61b49cc6b3c74f24239ba73b6a Mon Sep 17 00:00:00 2001 From: henryruhs Date: Sun, 4 Jun 2023 22:22:35 +0200 Subject: [PATCH 59/59] Fix memory leak thanks to tensorflow --- roop/core.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/roop/core.py b/roop/core.py index 9abb93f..c14bc48 100755 --- a/roop/core.py +++ b/roop/core.py @@ -12,6 +12,7 @@ import glob import argparse import psutil import torch +import tensorflow from pathlib import Path import multiprocessing as mp from opennsfw2 import predict_video_frames, predict_image @@ -66,6 +67,10 @@ if os.name == "nt": def limit_resources(): + # prevent tensorflow memory leak + gpus = tensorflow.config.experimental.list_physical_devices('GPU') + for gpu in gpus: + tensorflow.config.experimental.set_memory_growth(gpu, True) if args.max_memory: memory = args.max_memory * 1024 * 1024 * 1024 if str(platform.system()).lower() == 'windows':