import os import webbrowser import customtkinter as ctk from typing import Callable, Tuple import cv2 from PIL import Image, ImageOps import tkinterdnd2 as tkdnd import time import modules.globals import modules.metadata from modules.face_analyser import ( get_one_face, get_unique_faces_from_target_image, get_unique_faces_from_target_video, add_blank_map, has_valid_map, simplify_maps, ) from modules.capturer import get_video_frame, get_video_frame_total from modules.processors.frame.core import get_frame_processors_modules from modules.utilities import ( is_image, is_video, resolve_relative_path, has_image_extension, ) os.environ["QT_AUTO_SCREEN_SCALE_FACTOR"] = "1" os.environ["QT_SCREEN_SCALE_FACTORS"] = "1" os.environ["QT_SCALE_FACTOR"] = "1" ROOT = None POPUP = None POPUP_LIVE = None ROOT_HEIGHT = 800 ROOT_WIDTH = 1000 PREVIEW = None PREVIEW_MAX_HEIGHT = 800 PREVIEW_MAX_WIDTH = 1400 PREVIEW_DEFAULT_WIDTH = 1280 PREVIEW_DEFAULT_HEIGHT = 720 POPUP_WIDTH = 700 POPUP_HEIGHT = 800 POPUP_SCROLL_WIDTH = 680 POPUP_SCROLL_HEIGHT = 600 POPUP_LIVE_WIDTH = 850 POPUP_LIVE_HEIGHT = 700 POPUP_LIVE_SCROLL_WIDTH = 830 POPUP_LIVE_SCROLL_HEIGHT = 600 MAPPER_PREVIEW_MAX_HEIGHT = 120 MAPPER_PREVIEW_MAX_WIDTH = 120 DEFAULT_BUTTON_WIDTH = 200 DEFAULT_BUTTON_HEIGHT = 40 RECENT_DIRECTORY_SOURCE = None RECENT_DIRECTORY_TARGET = None RECENT_DIRECTORY_OUTPUT = None preview_label = None preview_slider = None source_label = None target_label = None status_label = None popup_status_label = None popup_status_label_live = None source_label_dict = {} source_label_dict_live = {} target_label_dict_live = {} img_ft, vid_ft = modules.globals.file_types class ModernButton(ctk.CTkButton): def __init__(self, master, **kwargs): super().__init__(master, **kwargs) self.configure( font=("Roboto", 16, "bold"), corner_radius=15, border_width=2, border_color="#3a7ebf", hover_color="#2b5d8b", fg_color="#3a7ebf", text_color="white", ) class DragDropButton(ModernButton): def __init__(self, master, **kwargs): super().__init__(master, **kwargs) self.drop_target_register(tkdnd.DND_FILES) self.dnd_bind("<>", self.drop) def drop(self, event): file_path = event.data if file_path.startswith("{"): file_path = file_path[1:-1] self.handle_drop(file_path) def handle_drop(self, file_path): pass class SourceButton(DragDropButton): def handle_drop(self, file_path): if is_image(file_path): modules.globals.source_path = file_path global RECENT_DIRECTORY_SOURCE RECENT_DIRECTORY_SOURCE = os.path.dirname(modules.globals.source_path) image = render_image_preview(modules.globals.source_path, (250, 250)) source_label.configure(image=image) source_label.configure(text="") class SourceMapperButton(DragDropButton): def __init__(self, master, map, button_num, **kwargs): super().__init__(master, **kwargs) self.map = map self.button_num = button_num def handle_drop(self, file_path): if is_image(file_path): update_popup_source( self.master.master, self.map, self.button_num, file_path ) class TargetButton(DragDropButton): def handle_drop(self, file_path): global RECENT_DIRECTORY_TARGET if is_image(file_path) or is_video(file_path): modules.globals.target_path = file_path RECENT_DIRECTORY_TARGET = os.path.dirname(modules.globals.target_path) if is_image(file_path): image = render_image_preview(modules.globals.target_path, (250, 250)) target_label.configure(image=image) target_label.configure(text="") elif is_video(file_path): video_frame = render_video_preview(file_path, (250, 250)) target_label.configure(image=video_frame) target_label.configure(text="") class ModernLabel(ctk.CTkLabel): def __init__(self, master, **kwargs): super().__init__(master, **kwargs) self.configure( font=("Roboto", 16), corner_radius=10, fg_color="#2a2d2e", text_color="white", ) class DragDropLabel(ModernLabel): def __init__(self, master, **kwargs): super().__init__(master, **kwargs) self.drop_target_register(tkdnd.DND_FILES) self.dnd_bind("<>", self.drop) def drop(self, event): file_path = event.data if file_path.startswith("{"): file_path = file_path[1:-1] self.handle_drop(file_path) def handle_drop(self, file_path): pass class SourceLabel(DragDropLabel): def handle_drop(self, file_path): if is_image(file_path): modules.globals.source_path = file_path global RECENT_DIRECTORY_SOURCE RECENT_DIRECTORY_SOURCE = os.path.dirname(modules.globals.source_path) image = render_image_preview(modules.globals.source_path, (250, 250)) source_label.configure(image=image) source_label.configure(text="") class TargetLabel(DragDropLabel): def handle_drop(self, file_path): global RECENT_DIRECTORY_TARGET if is_image(file_path) or is_video(file_path): modules.globals.target_path = file_path RECENT_DIRECTORY_TARGET = os.path.dirname(modules.globals.target_path) if is_image(file_path): image = render_image_preview(modules.globals.target_path, (250, 250)) target_label.configure(image=image) target_label.configure(text="") elif is_video(file_path): video_frame = render_video_preview(file_path, (250, 250)) target_label.configure(image=video_frame) target_label.configure(text="") def init(start: Callable[[], None], destroy: Callable[[], None]) -> tkdnd.TkinterDnD.Tk: global ROOT, PREVIEW ROOT = create_root(start, destroy) PREVIEW = create_preview(ROOT) return ROOT def create_root( start: Callable[[], None], destroy: Callable[[], None] ) -> tkdnd.TkinterDnD.Tk: global source_label, target_label, status_label ctk.set_appearance_mode("dark") ctk.set_default_color_theme("blue") root = tkdnd.TkinterDnD.Tk() root.title( f"{modules.metadata.name} {modules.metadata.version} {modules.metadata.edition}" ) root.configure(bg="#1a1a1a") root.protocol("WM_DELETE_WINDOW", lambda: destroy()) root.resizable(True, True) root.attributes("-alpha", 1.0) # Set window opacity to fully opaque main_frame = ctk.CTkFrame(root, fg_color="#1a1a1a") main_frame.pack(fill="both", expand=True, padx=20, pady=20) # Create two vertical frames for source and target source_frame = ctk.CTkFrame(main_frame, fg_color="#2a2d2e", corner_radius=15) source_frame.grid(row=0, column=0, padx=10, pady=10, sticky="nsew") target_frame = ctk.CTkFrame(main_frame, fg_color="#2a2d2e", corner_radius=15) target_frame.grid(row=0, column=2, padx=10, pady=10, sticky="nsew") # Create a middle frame for swap button middle_frame = ctk.CTkFrame(main_frame, fg_color="#1a1a1a") middle_frame.grid(row=0, column=1, padx=5, pady=10, sticky="ns") source_label = SourceLabel( source_frame, text="Drag & Drop\nSource Image Here", justify="center", width=250, height=250, ) source_label.pack(pady=(20, 10)) target_label = TargetLabel( target_frame, text="Drag & Drop\nTarget Image/Video Here", justify="center", width=250, height=250, ) target_label.pack(pady=(20, 10)) select_face_button = SourceButton( source_frame, text="Select a face", cursor="hand2", command=lambda: select_source_path(), ) select_face_button.pack(pady=10) select_target_button = TargetButton( target_frame, text="Select a target", cursor="hand2", command=lambda: select_target_path(), ) select_target_button.pack(pady=10) swap_faces_button = ModernButton( middle_frame, text="↔", cursor="hand2", command=lambda: swap_faces_paths(), width=50, height=50, ) swap_faces_button.pack(expand=True) options_frame = ctk.CTkFrame(main_frame, fg_color="#2a2d2e", corner_radius=15) options_frame.grid(row=1, column=0, columnspan=3, padx=10, pady=10, sticky="nsew") # Create a single column for options, centered options_column = ctk.CTkFrame(options_frame, fg_color="#2a2d2e") options_column.pack(expand=True) # Switches keep_fps_value = ctk.BooleanVar(value=modules.globals.keep_fps) keep_fps_checkbox = ctk.CTkSwitch( options_column, text="Keep fps", variable=keep_fps_value, cursor="hand2", command=lambda: setattr( modules.globals, "keep_fps", not modules.globals.keep_fps ), progress_color="#3a7ebf", font=("Roboto", 14, "bold"), ) keep_fps_checkbox.pack(pady=5, anchor="w") keep_frames_value = ctk.BooleanVar(value=modules.globals.keep_frames) keep_frames_switch = ctk.CTkSwitch( options_column, text="Keep frames", variable=keep_frames_value, cursor="hand2", command=lambda: setattr( modules.globals, "keep_frames", keep_frames_value.get() ), progress_color="#3a7ebf", font=("Roboto", 14, "bold"), ) keep_frames_switch.pack(pady=5, anchor="w") enhancer_value = ctk.BooleanVar(value=modules.globals.fp_ui["face_enhancer"]) enhancer_switch = ctk.CTkSwitch( options_column, text="Face Enhancer", variable=enhancer_value, cursor="hand2", command=lambda: update_tumbler("face_enhancer", enhancer_value.get()), progress_color="#3a7ebf", font=("Roboto", 14, "bold"), ) enhancer_switch.pack(pady=5, anchor="w") keep_audio_value = ctk.BooleanVar(value=modules.globals.keep_audio) keep_audio_switch = ctk.CTkSwitch( options_column, text="Keep audio", variable=keep_audio_value, cursor="hand2", command=lambda: setattr(modules.globals, "keep_audio", keep_audio_value.get()), progress_color="#3a7ebf", font=("Roboto", 14, "bold"), ) keep_audio_switch.pack(pady=5, anchor="w") many_faces_value = ctk.BooleanVar(value=modules.globals.many_faces) many_faces_switch = ctk.CTkSwitch( options_column, text="Many faces", variable=many_faces_value, cursor="hand2", command=lambda: setattr(modules.globals, "many_faces", many_faces_value.get()), progress_color="#3a7ebf", font=("Roboto", 14, "bold"), ) many_faces_switch.pack(pady=5, anchor="w") color_correction_value = ctk.BooleanVar(value=modules.globals.color_correction) color_correction_switch = ctk.CTkSwitch( options_column, text="Fix Blueish Cam", variable=color_correction_value, cursor="hand2", command=lambda: setattr( modules.globals, "color_correction", color_correction_value.get() ), progress_color="#3a7ebf", font=("Roboto", 14, "bold"), ) color_correction_switch.pack(pady=5, anchor="w") map_faces = ctk.BooleanVar(value=modules.globals.map_faces) map_faces_switch = ctk.CTkSwitch( options_column, text="Map faces", variable=map_faces, cursor="hand2", command=lambda: setattr(modules.globals, "map_faces", map_faces.get()), progress_color="#3a7ebf", font=("Roboto", 14, "bold"), ) map_faces_switch.pack(pady=5, anchor="w") button_frame = ctk.CTkFrame(main_frame, fg_color="#1a1a1a") button_frame.grid(row=2, column=0, columnspan=3, padx=10, pady=10, sticky="nsew") start_button = ModernButton( button_frame, text="Start", cursor="hand2", command=lambda: analyze_target(start, root), fg_color="#4CAF50", hover_color="#45a049", ) start_button.pack(side="left", padx=10, expand=True) preview_button = ModernButton( button_frame, text="Preview", cursor="hand2", command=lambda: toggle_preview(), ) preview_button.pack(side="left", padx=10, expand=True) live_button = ModernButton( button_frame, text="Live", cursor="hand2", command=lambda: webcam_preview(root), ) live_button.pack(side="left", padx=10, expand=True) stop_button = ModernButton( button_frame, text="Destroy", cursor="hand2", command=lambda: destroy(), fg_color="#f44336", hover_color="#d32f2f", ) stop_button.pack(side="left", padx=10, expand=True) status_label = ModernLabel( main_frame, text=None, justify="center", fg_color="#1a1a1a" ) status_label.grid(row=3, column=0, columnspan=3, pady=10, sticky="ew") donate_frame = ctk.CTkFrame(main_frame, fg_color="#1a1a1a") donate_frame.grid(row=4, column=0, columnspan=3, pady=5, sticky="ew") donate_label = ModernLabel( donate_frame, text="Donate", justify="center", cursor="hand2", fg_color="#1870c4", text_color="#1870c4", ) donate_label.pack(side="left", expand=True) donate_label.bind( "