From 523d80550d4c4eab49d1992be74bb68971da75bb Mon Sep 17 00:00:00 2001 From: "CH. Krish" <136873090+KRSHH@users.noreply.github.com> Date: Sun, 15 Sep 2024 23:00:28 +0530 Subject: [PATCH 1/6] Adding Drag and Drop feature to Face Mapper and Source and Target --- modules/ui.py | 595 +++++++++++++++++++++++++++++++++++++------------- 1 file changed, 448 insertions(+), 147 deletions(-) diff --git a/modules/ui.py b/modules/ui.py index a8c6522..8b80021 100644 --- a/modules/ui.py +++ b/modules/ui.py @@ -4,13 +4,26 @@ import customtkinter as ctk from typing import Callable, Tuple import cv2 from PIL import Image, ImageOps +import tkinterdnd2 as tkdnd 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.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 +from modules.utilities import ( + is_image, + is_video, + resolve_relative_path, + has_image_extension, +) ROOT = None POPUP = None @@ -20,18 +33,18 @@ ROOT_WIDTH = 600 PREVIEW = None PREVIEW_MAX_HEIGHT = 700 -PREVIEW_MAX_WIDTH = 1200 -PREVIEW_DEFAULT_WIDTH = 960 +PREVIEW_MAX_WIDTH = 1200 +PREVIEW_DEFAULT_WIDTH = 960 PREVIEW_DEFAULT_HEIGHT = 540 POPUP_WIDTH = 750 POPUP_HEIGHT = 810 -POPUP_SCROLL_WIDTH = 740, +POPUP_SCROLL_WIDTH = 740 POPUP_SCROLL_HEIGHT = 700 POPUP_LIVE_WIDTH = 900 POPUP_LIVE_HEIGHT = 820 -POPUP_LIVE_SCROLL_WIDTH = 890, +POPUP_LIVE_SCROLL_WIDTH = 890 POPUP_LIVE_SCROLL_HEIGHT = 700 MAPPER_PREVIEW_MAX_HEIGHT = 100 @@ -58,7 +71,58 @@ target_label_dict_live = {} img_ft, vid_ft = modules.globals.file_types -def init(start: Callable[[], None], destroy: Callable[[], None]) -> ctk.CTk: +class DragDropButton(ctk.CTkButton): + 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, (200, 200)) + source_label.configure(image=image) + + +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, 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, (200, 200)) + target_label.configure(image=image) + elif is_video(file_path): + video_frame = render_video_preview(file_path, (200, 200)) + target_label.configure(image=video_frame) + + +def init(start: Callable[[], None], destroy: Callable[[], None]) -> tkdnd.TkinterDnD.Tk: global ROOT, PREVIEW ROOT = create_root(start, destroy) @@ -67,18 +131,22 @@ def init(start: Callable[[], None], destroy: Callable[[], None]) -> ctk.CTk: return ROOT -def create_root(start: Callable[[], None], destroy: Callable[[], None]) -> ctk.CTk: +def create_root( + start: Callable[[], None], destroy: Callable[[], None] +) -> tkdnd.TkinterDnD.Tk: global source_label, target_label, status_label ctk.deactivate_automatic_dpi_awareness() - ctk.set_appearance_mode('system') - ctk.set_default_color_theme(resolve_relative_path('ui.json')) + ctk.set_appearance_mode("dark") # Re-add this line + ctk.set_default_color_theme(resolve_relative_path("ui.json")) - root = ctk.CTk() + root = tkdnd.TkinterDnD.Tk() root.minsize(ROOT_WIDTH, ROOT_HEIGHT) - root.title(f'{modules.metadata.name} {modules.metadata.version} {modules.metadata.edition}') - root.configure() - root.protocol('WM_DELETE_WINDOW', lambda: destroy()) + root.title( + f"{modules.metadata.name} {modules.metadata.version} {modules.metadata.edition}" + ) + root.configure(bg="gray12") + root.protocol("WM_DELETE_WINDOW", lambda: destroy()) source_label = ctk.CTkLabel(root, text=None) source_label.place(relx=0.1, rely=0.1, relwidth=0.3, relheight=0.25) @@ -86,71 +154,137 @@ def create_root(start: Callable[[], None], destroy: Callable[[], None]) -> ctk.C target_label = ctk.CTkLabel(root, text=None) target_label.place(relx=0.6, rely=0.1, relwidth=0.3, relheight=0.25) - select_face_button = ctk.CTkButton(root, text='Select a face', cursor='hand2', command=lambda: select_source_path()) + select_face_button = SourceButton( + root, text="Select a face", cursor="hand2", command=lambda: select_source_path() + ) select_face_button.place(relx=0.1, rely=0.4, relwidth=0.3, relheight=0.1) - swap_faces_button = ctk.CTkButton(root, text='↔', cursor='hand2', command=lambda: swap_faces_paths()) + swap_faces_button = ctk.CTkButton( + root, text="↔", cursor="hand2", command=lambda: swap_faces_paths() + ) swap_faces_button.place(relx=0.45, rely=0.4, relwidth=0.1, relheight=0.1) - select_target_button = ctk.CTkButton(root, text='Select a target', cursor='hand2', command=lambda: select_target_path()) + select_target_button = TargetButton( + root, + text="Select a target", + cursor="hand2", + command=lambda: select_target_path(), + ) select_target_button.place(relx=0.6, rely=0.4, relwidth=0.3, relheight=0.1) keep_fps_value = ctk.BooleanVar(value=modules.globals.keep_fps) - keep_fps_checkbox = ctk.CTkSwitch(root, text='Keep fps', variable=keep_fps_value, cursor='hand2', command=lambda: setattr(modules.globals, 'keep_fps', not modules.globals.keep_fps)) + keep_fps_checkbox = ctk.CTkSwitch( + root, + text="Keep fps", + variable=keep_fps_value, + cursor="hand2", + command=lambda: setattr( + modules.globals, "keep_fps", not modules.globals.keep_fps + ), + ) keep_fps_checkbox.place(relx=0.1, rely=0.6) keep_frames_value = ctk.BooleanVar(value=modules.globals.keep_frames) - keep_frames_switch = ctk.CTkSwitch(root, text='Keep frames', variable=keep_frames_value, cursor='hand2', command=lambda: setattr(modules.globals, 'keep_frames', keep_frames_value.get())) + keep_frames_switch = ctk.CTkSwitch( + root, + text="Keep frames", + variable=keep_frames_value, + cursor="hand2", + command=lambda: setattr( + modules.globals, "keep_frames", keep_frames_value.get() + ), + ) keep_frames_switch.place(relx=0.1, rely=0.65) - # for FRAME PROCESSOR ENHANCER tumbler: - enhancer_value = ctk.BooleanVar(value=modules.globals.fp_ui['face_enhancer']) - enhancer_switch = ctk.CTkSwitch(root, text='Face Enhancer', variable=enhancer_value, cursor='hand2', command=lambda: update_tumbler('face_enhancer',enhancer_value.get())) + enhancer_value = ctk.BooleanVar(value=modules.globals.fp_ui["face_enhancer"]) + enhancer_switch = ctk.CTkSwitch( + root, + text="Face Enhancer", + variable=enhancer_value, + cursor="hand2", + command=lambda: update_tumbler("face_enhancer", enhancer_value.get()), + ) enhancer_switch.place(relx=0.1, rely=0.7) keep_audio_value = ctk.BooleanVar(value=modules.globals.keep_audio) - keep_audio_switch = ctk.CTkSwitch(root, text='Keep audio', variable=keep_audio_value, cursor='hand2', command=lambda: setattr(modules.globals, 'keep_audio', keep_audio_value.get())) + keep_audio_switch = ctk.CTkSwitch( + root, + text="Keep audio", + variable=keep_audio_value, + cursor="hand2", + command=lambda: setattr(modules.globals, "keep_audio", keep_audio_value.get()), + ) keep_audio_switch.place(relx=0.6, rely=0.6) many_faces_value = ctk.BooleanVar(value=modules.globals.many_faces) - many_faces_switch = ctk.CTkSwitch(root, text='Many faces', variable=many_faces_value, cursor='hand2', command=lambda: setattr(modules.globals, 'many_faces', many_faces_value.get())) + many_faces_switch = ctk.CTkSwitch( + root, + text="Many faces", + variable=many_faces_value, + cursor="hand2", + command=lambda: setattr(modules.globals, "many_faces", many_faces_value.get()), + ) many_faces_switch.place(relx=0.6, rely=0.65) - # Add color correction toggle button color_correction_value = ctk.BooleanVar(value=modules.globals.color_correction) - color_correction_switch = ctk.CTkSwitch(root, text='Fix Blueish Cam\n(force cv2 to use RGB instead of BGR)', variable=color_correction_value, cursor='hand2', command=lambda: setattr(modules.globals, 'color_correction', color_correction_value.get())) + color_correction_switch = ctk.CTkSwitch( + root, + text="Fix Blueish Cam\n(force cv2 to use RGB instead of BGR)", + variable=color_correction_value, + cursor="hand2", + command=lambda: setattr( + modules.globals, "color_correction", color_correction_value.get() + ), + ) color_correction_switch.place(relx=0.6, rely=0.70) -# nsfw_value = ctk.BooleanVar(value=modules.globals.nsfw_filter) -# nsfw_switch = ctk.CTkSwitch(root, text='NSFW filter', variable=nsfw_value, cursor='hand2', command=lambda: setattr(modules.globals, 'nsfw_filter', nsfw_value.get())) -# nsfw_switch.place(relx=0.6, rely=0.7) - map_faces = ctk.BooleanVar(value=modules.globals.map_faces) - map_faces_switch = ctk.CTkSwitch(root, text='Map faces', variable=map_faces, cursor='hand2', command=lambda: setattr(modules.globals, 'map_faces', map_faces.get())) + map_faces_switch = ctk.CTkSwitch( + root, + text="Map faces", + variable=map_faces, + cursor="hand2", + command=lambda: setattr(modules.globals, "map_faces", map_faces.get()), + ) map_faces_switch.place(relx=0.1, rely=0.75) - start_button = ctk.CTkButton(root, text='Start', cursor='hand2', command=lambda: analyze_target(start, root)) + start_button = ctk.CTkButton( + root, text="Start", cursor="hand2", command=lambda: analyze_target(start, root) + ) start_button.place(relx=0.15, rely=0.80, relwidth=0.2, relheight=0.05) - stop_button = ctk.CTkButton(root, text='Destroy', cursor='hand2', command=lambda: destroy()) + stop_button = ctk.CTkButton( + root, text="Destroy", cursor="hand2", command=lambda: destroy() + ) stop_button.place(relx=0.4, rely=0.80, relwidth=0.2, relheight=0.05) - preview_button = ctk.CTkButton(root, text='Preview', cursor='hand2', command=lambda: toggle_preview()) + preview_button = ctk.CTkButton( + root, text="Preview", cursor="hand2", command=lambda: toggle_preview() + ) preview_button.place(relx=0.65, rely=0.80, relwidth=0.2, relheight=0.05) - live_button = ctk.CTkButton(root, text='Live', cursor='hand2', command=lambda: webcam_preview(root)) + live_button = ctk.CTkButton( + root, text="Live", cursor="hand2", command=lambda: webcam_preview(root) + ) live_button.place(relx=0.40, rely=0.86, relwidth=0.2, relheight=0.05) - status_label = ctk.CTkLabel(root, text=None, justify='center') + status_label = ctk.CTkLabel(root, text=None, justify="center") status_label.place(relx=0.1, rely=0.9, relwidth=0.8) - donate_label = ctk.CTkLabel(root, text='Deep Live Cam', justify='center', cursor='hand2') + donate_label = ctk.CTkLabel( + root, text="Deep Live Cam", justify="center", cursor="hand2" + ) donate_label.place(relx=0.1, rely=0.95, relwidth=0.8) - donate_label.configure(text_color=ctk.ThemeManager.theme.get('URL').get('text_color')) - donate_label.bind('