From 30159974a4e99586e0d018993dc050eb5c0a0e76 Mon Sep 17 00:00:00 2001 From: henryruhs Date: Fri, 2 Jun 2023 10:29:00 +0200 Subject: [PATCH 1/8] Introduce Python alike package namespace, turn run.py into an entrypoint --- {core => roop}/__init__.py | 0 {core => roop}/analyser.py | 4 +- roop/core.py | 286 +++++++++++++++++++++++++++++++++++++ {core => roop}/globals.py | 0 {core => roop}/swapper.py | 4 +- {core => roop}/utils.py | 0 run.py | 284 +----------------------------------- 7 files changed, 292 insertions(+), 286 deletions(-) rename {core => roop}/__init__.py (100%) rename {core => roop}/analyser.py (90%) create mode 100755 roop/core.py rename {core => roop}/globals.py (100%) rename {core => roop}/swapper.py (96%) rename {core => roop}/utils.py (100%) diff --git a/core/__init__.py b/roop/__init__.py similarity index 100% rename from core/__init__.py rename to roop/__init__.py diff --git a/core/analyser.py b/roop/analyser.py similarity index 90% rename from core/analyser.py rename to roop/analyser.py index 85a0b7c..804f7a8 100644 --- a/core/analyser.py +++ b/roop/analyser.py @@ -1,5 +1,5 @@ import insightface -import core.globals +import roop.globals FACE_ANALYSER = None @@ -7,7 +7,7 @@ FACE_ANALYSER = None def get_face_analyser(): global FACE_ANALYSER if FACE_ANALYSER is None: - FACE_ANALYSER = insightface.app.FaceAnalysis(name='buffalo_l', providers=core.globals.providers) + 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 new file mode 100755 index 0000000..693c695 --- /dev/null +++ b/roop/core.py @@ -0,0 +1,286 @@ +#!/usr/bin/env python3 + +import platform +import signal +import sys +import shutil +import glob +import argparse +import multiprocessing as mp +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 psutil +import cv2 +import threading +from PIL import Image, ImageTk + +import roop.globals +from roop.swapper import process_video, process_img +from roop.utils import is_img, detect_fps, set_fps, create_video, add_audio, extract_frames, rreplace +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='use gpu', dest='gpu', action='store_true', default=False) +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) + +for name, value in vars(parser.parse_args()).items(): + args[name] = value + +sep = "/" +if os.name == "nt": + sep = "\\" + + +def limit_resources(): + if args['max_memory']: + memory = args['max_memory'] * 1024 * 1024 * 1024 + if str(platform.system()).lower() == 'windows': + import ctypes + kernel32 = ctypes.windll.kernel32 + kernel32.SetProcessWorkingSetSize(-1, ctypes.c_size_t(memory), ctypes.c_size_t(memory)) + else: + import resource + resource.setrlimit(resource.RLIMIT_DATA, (memory, memory)) + + +def pre_check(): + if sys.version_info < (3, 9): + quit('Python version is not supported - please upgrade to 3.9 or higher') + if not shutil.which('ffmpeg'): + quit('ffmpeg is not installed!') + 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: + 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") + else: + roop.globals.providers = ['CPUExecutionProvider'] + if '--all-faces' in sys.argv or '-a' in sys.argv: + roop.globals.all_faces = True + + +def start_processing(): + if args['gpu']: + process_video(args['source_img'], args["frame_paths"]) + return + frame_paths = args["frame_paths"] + n = len(frame_paths)//(args['cores_count']) + 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): + 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 preview_video(video_path): + cap = cv2.VideoCapture(video_path) + if not cap.isOpened(): + print("Error opening video file") + return + ret, frame = cap.read() + if ret: + frame = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB) + 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() + + 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")]) + + +def status(string): + if 'cli_mode' in args: + print("Status: " + string) + else: + status_label["text"] = "Status: " + string + window.update() + + +def start(): + 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']): + 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 + 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: + 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.7: + quit() + 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) + if any(probability > 0.7 for probability in probabilities): + 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 + 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: + this_path = output_dir + "/" + video_name + ".mp4" + 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) + args['frame_paths'] = tuple(sorted( + glob.glob(output_dir + "/*.png"), + key=lambda x: int(x.split(sep)[-1].replace(".png", "")) + )) + status("swapping in progress...") + start_processing() + 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" + print("\n\nVideo saved as:", save_path, "\n\n") + status("swap successful!") + + +def run(): + global status_label, window + + pre_check() + limit_resources() + + if args['source_img']: + 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) + + # 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(), start()]) + start_button.place(x=240,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() diff --git a/core/globals.py b/roop/globals.py similarity index 100% rename from core/globals.py rename to roop/globals.py diff --git a/core/swapper.py b/roop/swapper.py similarity index 96% rename from core/swapper.py rename to roop/swapper.py index fdfd646..27d2d37 100644 --- a/core/swapper.py +++ b/roop/swapper.py @@ -2,8 +2,8 @@ import os from tqdm import tqdm import cv2 import insightface -import core.globals -from core.analyser import get_face_single, get_face_many +import roop.globals +from roop.analyser import get_face_single, get_face_many FACE_SWAPPER = None diff --git a/core/utils.py b/roop/utils.py similarity index 100% rename from core/utils.py rename to roop/utils.py diff --git a/run.py b/run.py index 6acf785..3eaca30 100755 --- a/run.py +++ b/run.py @@ -1,285 +1,5 @@ #!/usr/bin/env python3 -import platform -import signal -import sys -import shutil -import glob -import argparse -import multiprocessing as mp -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 psutil -import cv2 -import threading -from PIL import Image, ImageTk -import core.globals -from core.swapper import process_video, process_img -from core.utils import is_img, detect_fps, set_fps, create_video, add_audio, extract_frames, rreplace -from core.analyser import get_face_single +from roop import core -if 'ROCMExecutionProvider' in core.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='use gpu', dest='gpu', action='store_true', default=False) -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) - -for name, value in vars(parser.parse_args()).items(): - args[name] = value - -sep = "/" -if os.name == "nt": - sep = "\\" - - -def limit_resources(): - if args['max_memory']: - memory = args['max_memory'] * 1024 * 1024 * 1024 - if str(platform.system()).lower() == 'windows': - import ctypes - kernel32 = ctypes.windll.kernel32 - kernel32.SetProcessWorkingSetSize(-1, ctypes.c_size_t(memory), ctypes.c_size_t(memory)) - else: - import resource - resource.setrlimit(resource.RLIMIT_DATA, (memory, memory)) - - -def pre_check(): - if sys.version_info < (3, 9): - quit('Python version is not supported - please upgrade to 3.9 or higher') - if not shutil.which('ffmpeg'): - quit('ffmpeg is not installed!') - 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: - NVIDIA_PROVIDERS = ['CUDAExecutionProvider', 'TensorrtExecutionProvider'] - if len(list(set(core.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") - else: - core.globals.providers = ['CPUExecutionProvider'] - if '--all-faces' in sys.argv or '-a' in sys.argv: - core.globals.all_faces = True - - -def start_processing(): - if args['gpu']: - process_video(args['source_img'], args["frame_paths"]) - return - frame_paths = args["frame_paths"] - n = len(frame_paths)//(args['cores_count']) - 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): - 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 preview_video(video_path): - cap = cv2.VideoCapture(video_path) - if not cap.isOpened(): - print("Error opening video file") - return - ret, frame = cap.read() - if ret: - frame = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB) - 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() - - 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(): - core.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")]) - - -def status(string): - if 'cli_mode' in args: - print("Status: " + string) - else: - status_label["text"] = "Status: " + string - window.update() - - -def start(): - 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']): - 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 - 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: - 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.7: - quit() - 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) - if any(probability > 0.7 for probability in probabilities): - 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 - 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: - this_path = output_dir + "/" + video_name + ".mp4" - 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) - args['frame_paths'] = tuple(sorted( - glob.glob(output_dir + "/*.png"), - key=lambda x: int(x.split(sep)[-1].replace(".png", "")) - )) - status("swapping in progress...") - start_processing() - 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" - print("\n\nVideo saved as:", save_path, "\n\n") - status("swap successful!") - - -if __name__ == "__main__": - global status_label, window - - pre_check() - limit_resources() - - if args['source_img']: - 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) - - # 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(), start()]) - start_button.place(x=240,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() +core.run() \ No newline at end of file From 1b628b7b70ad5a2bc2c1e54244a75a03b673d185 Mon Sep 17 00:00:00 2001 From: henryruhs Date: Fri, 2 Jun 2023 15:27:57 +0200 Subject: [PATCH 2/8] Merge against next --- roop/core.py | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/roop/core.py b/roop/core.py index 693c695..1e5cc69 100755 --- a/roop/core.py +++ b/roop/core.py @@ -46,6 +46,9 @@ 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: + roop.globals.all_faces = True + sep = "/" if os.name == "nt": sep = "\\" @@ -194,17 +197,19 @@ def start(): 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.7: + if predict_image(target_path) > 0.85: quit() 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) - if any(probability > 0.7 for probability in probabilities): + if any(probability > 0.85 for probability in probabilities): 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 output_dir.startswith("/"): + output_dir = "." + output_dir Path(output_dir).mkdir(exist_ok=True) status("detecting video's FPS...") fps, exact_fps = detect_fps(target_path) From 8b50aaf8386a8fba31e67e816bccd4d4760460a2 Mon Sep 17 00:00:00 2001 From: henryruhs Date: Fri, 2 Jun 2023 16:21:13 +0200 Subject: [PATCH 3/8] Remove the out_dir hack, as this breaks temp directory on Linux --- roop/core.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/roop/core.py b/roop/core.py index 1e5cc69..487cce1 100755 --- a/roop/core.py +++ b/roop/core.py @@ -208,8 +208,6 @@ def start(): 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 output_dir.startswith("/"): - output_dir = "." + output_dir Path(output_dir).mkdir(exist_ok=True) status("detecting video's FPS...") fps, exact_fps = detect_fps(target_path) From dd5a321c507466e8836f513c7effc14c7c32691a Mon Sep 17 00:00:00 2001 From: henryruhs Date: Fri, 2 Jun 2023 16:38:14 +0200 Subject: [PATCH 4/8] Fix frames vs. cpu cores issue --- roop/core.py | 25 ++++++++++++++----------- 1 file changed, 14 insertions(+), 11 deletions(-) diff --git a/roop/core.py b/roop/core.py index 487cce1..3d89761 100755 --- a/roop/core.py +++ b/roop/core.py @@ -96,19 +96,22 @@ def pre_check(): def start_processing(): - if args['gpu']: + frame_paths = args["frame_paths"] + n = len(frame_paths) // (args['cores_count']) + # single thread + if args['gpu'] or n < 2: process_video(args['source_img'], args["frame_paths"]) return - frame_paths = args["frame_paths"] - n = len(frame_paths)//(args['cores_count']) - 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() + # multi thread of frames to cpu cores ratio is 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): From b37b9983260243208ac94a8b9eec97617674e800 Mon Sep 17 00:00:00 2001 From: henryruhs Date: Fri, 2 Jun 2023 16:40:08 +0200 Subject: [PATCH 5/8] Fix frames vs. cpu cores issue --- roop/core.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/roop/core.py b/roop/core.py index 3d89761..7acf33b 100755 --- a/roop/core.py +++ b/roop/core.py @@ -102,7 +102,7 @@ def start_processing(): if args['gpu'] or n < 2: process_video(args['source_img'], args["frame_paths"]) return - # multi thread of frames to cpu cores ratio is 2 + # multi thread if video frames to cpu cores ratio is 2 if n > 2: processes = [] for i in range(0, len(frame_paths), n): From 82cafcdc8b038f80a8ae9cee8e0dc7ac0155d2ec Mon Sep 17 00:00:00 2001 From: henryruhs Date: Fri, 2 Jun 2023 19:26:08 +0200 Subject: [PATCH 6/8] Fix broken globals --- roop/swapper.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/roop/swapper.py b/roop/swapper.py index 27d2d37..bfc4d63 100644 --- a/roop/swapper.py +++ b/roop/swapper.py @@ -12,7 +12,7 @@ def get_face_swapper(): global FACE_SWAPPER 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=core.globals.providers) + FACE_SWAPPER = insightface.model_zoo.get_model(model_path, providers=roop.globals.providers) return FACE_SWAPPER @@ -49,7 +49,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, core.globals.all_faces) + result = process_faces(source_face, frame, progress, roop.globals.all_faces) cv2.imwrite(frame_path, result) except Exception: progress.set_postfix(status='E', refresh=True) From 030d6a698bd0d7bf2dfe5588a887a833d90223b0 Mon Sep 17 00:00:00 2001 From: henryruhs Date: Fri, 2 Jun 2023 19:43:30 +0200 Subject: [PATCH 7/8] Less verbose ffmpeg outputs --- README.md | 1 + roop/core.py | 2 +- roop/utils.py | 8 ++++---- 3 files changed, 6 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 20eb6d1..af29800 100644 --- a/README.md +++ b/README.md @@ -47,6 +47,7 @@ options: 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 ``` Looking for a CLI mode? Using the -f/--face argument will make the program in cli mode. diff --git a/roop/core.py b/roop/core.py index 7acf33b..8405d08 100755 --- a/roop/core.py +++ b/roop/core.py @@ -102,7 +102,7 @@ def start_processing(): if args['gpu'] or n < 2: process_video(args['source_img'], args["frame_paths"]) return - # multi thread if video frames to cpu cores ratio is 2 + # 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): diff --git a/roop/utils.py b/roop/utils.py index 50aca35..bc3d25a 100644 --- a/roop/utils.py +++ b/roop/utils.py @@ -31,24 +31,24 @@ def detect_fps(input_path): def set_fps(input_path, output_path, fps): input_path, output_path = path(input_path), path(output_path) - os.system(f'ffmpeg -i "{input_path}" -filter:v fps=fps={fps} "{output_path}"') + os.system(f'ffmpeg -i "{input_path}" -filter:v fps=fps={fps} "{output_path}" -loglevel error') def create_video(video_name, fps, output_dir): 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"') + 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" -loglevel error') def extract_frames(input_path, output_dir): input_path, output_dir = path(input_path), path(output_dir) - os.system(f'ffmpeg -i "{input_path}" "{output_dir}{sep}%04d.png"') + os.system(f'ffmpeg -i "{input_path}" "{output_dir}{sep}%04d.png" -loglevel error') 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'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}"') + 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}" -loglevel error') if not os.path.isfile(save_to): shutil.move(output_dir + "/output.mp4", save_to) if not keep_frames: From 2935185b8151ba15d0e3fcf5d3ddf4c649d24c29 Mon Sep 17 00:00:00 2001 From: henryruhs Date: Fri, 2 Jun 2023 19:58:47 +0200 Subject: [PATCH 8/8] Fix multiple ui opening --- run.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/run.py b/run.py index 3eaca30..b52e5cc 100755 --- a/run.py +++ b/run.py @@ -2,4 +2,5 @@ from roop import core -core.run() \ No newline at end of file +if __name__ == '__main__': + core.run()