import os import cv2 import tkinter as tk from tkinter import filedialog, font import numpy as np from PIL import Image, ImageTk import json import time import copy import bisect import torch import torchvision import customtkinter as ctk torchvision.disable_beta_transforms_warning() import mimetypes import webbrowser from random import random import rope.GUIElements as GE import rope.Styles as style from skimage import transform as trans from torchvision.transforms import v2 import inspect #print(inspect.currentframe().f_back.f_code.co_name, 'resize_image') class GUI(tk.Tk): def __init__(self, models): super().__init__() self.models = models self.title('Rope-Pearl-00') self.target_media = [] self.target_video_file = [] self.action_q = [] self.video_image = [] self.video_loaded = False self.image_loaded = False self.image_file_name = [] self.stop_marker = [] self.stop_image = [] self.stop_marker_icon = [] self.window_last_change = [] self.blank = tk.PhotoImage() self.output_folder = [] self.output_videos_text = [] self.target_media_buttons = [] self.input_videos_button = [] self.input_videos_text = [] self.target_media_canvas = [] self.source_faces_buttons = [] self.input_videos_button = [] self.input_faces_text = [] self.shift_i_len = 0 self.source_faces_canvas = [] self.video = [] self.video_slider = [] self.found_faces_canvas = [] self.merged_embedding_name = [] self.merged_embeddings_text = [] self.me_name = [] self.merged_faces_canvas = [] self.parameters = {} self.control = {} self.widget = {} self.static_widget = {} self.layer = {} self.temp_emb = [] self.arcface_dst = np.array([[38.2946, 51.6963], [73.5318, 51.5014], [56.0252, 71.7366], [41.5493, 92.3655], [70.7299, 92.2041]], dtype=np.float32) self.json_dict = { "source videos": None, "source faces": None, "saved videos": None, 'dock_win_geom': [1400, 800, self.winfo_screenwidth()/2-400, self.winfo_screenheight()/2-510], } self.marker = { 'frame': '0', 'parameters': '', 'icon_ref': '', } self.markers = [] self.target_face = { "TKButton": [], "ButtonState": "off", "Image": [], "Embedding": [], "SourceFaceAssignments": [], "EmbeddingNumber": 0, #used for adding additional found faces 'AssignedEmbedding': [], #the currently assigned source embedding, including averaged ones } self.target_faces = [] self.source_face = { "TKButton": [], "ButtonState": "off", "Image": [], "Embedding": [] } self.source_faces = [] #region [#111111b4] script_dir = os.path.dirname(__file__) icon_path = os.path.join(script_dir, 'media', 'rope.ico') #endregion #region [#131710b4] # Keyboard Shortcuts self.widget = {} def load_shortcuts_from_json(): try: with open("shortcuts.json", "r") as json_file: return json.load(json_file) except FileNotFoundError: return { "Timeline Beginning": "z", "Nudge Left 30 Frames": "a", "Nudge Right 30 Frames": "d", "Record": "r", "Play": "space", "Save Image": "ctrl+s", "Add Marker": "f", "Delete Marker": "alt+f", "Previous Marker": "q", "Next Marker": "w", "Toggle Restorer": "1", "Toggle Orientation": "2", "Toggle Strength": "3", "Toggle Differencing": "4", "Toggle Occluder": "5", "Toggle Face Parser": "6", "Toggle Text-Based Masking": "7", "Toggle Color Adjustments": "8", "Toggle Face Adjustments": "9", "Clear VRAM": "F1", "Swap Faces": "s", "Nudge Left 1 Frame": "c", "Nudge Right 1 Frame": "v", "Show Mask": "x", } shortcuts = load_shortcuts_from_json() # Update text variables with loaded shortcuts text_vars = {} for shortcut_name, default_value in shortcuts.items(): text_vars[shortcut_name] = tk.StringVar(value=default_value) # Update self.key_actions with loaded shortcuts self.key_actions = { shortcuts["Timeline Beginning"]: lambda: self.preview_control('q'), shortcuts["Nudge Left 30 Frames"]: lambda: self.preview_control('a'), shortcuts["Record"]: lambda: self.toggle_rec_video(), shortcuts["Play"]: lambda: self.toggle_play_video(), shortcuts["Nudge Right 30 Frames"]: lambda: self.preview_control('d'), shortcuts["Save Image"]: lambda: self.save_image(), shortcuts["Add Marker"]: lambda: self.update_marker('add'), shortcuts["Delete Marker"]: lambda: self.update_marker('delete'), shortcuts["Previous Marker"]: lambda: self.update_marker('prev'), shortcuts["Next Marker"]: lambda: self.update_marker('next'), shortcuts["Toggle Restorer"]: lambda: self.toggle_and_update('Restorer', 'Restorer'), shortcuts["Toggle Orientation"]: lambda: self.toggle_and_update('Orient', 'Orientation'), shortcuts["Toggle Strength"]: lambda: self.toggle_and_update('Strength', 'Strength'), shortcuts["Toggle Differencing"]: lambda: self.toggle_and_update('Diff', 'Differencing'), shortcuts["Toggle Occluder"]: lambda: self.toggle_and_update('Occluder', 'Occluder'), shortcuts["Toggle Face Parser"]: lambda: self.toggle_and_update('FaceParser', 'Face Parser'), shortcuts["Toggle Text-Based Masking"]: lambda: self.toggle_and_update('CLIP', 'Text-Based Masking'), shortcuts["Toggle Color Adjustments"]: lambda: self.toggle_and_update('Color', 'Color Adjustments'), shortcuts["Toggle Face Adjustments"]: lambda: self.toggle_and_update('FaceAdj', 'Input Face Adjustments'), shortcuts["Clear VRAM"]: lambda: self.clear_mem(), shortcuts["Swap Faces"]: lambda: self.toggle_swapper(), shortcuts["Nudge Left 1 Frame"]: lambda:self.back_one_frame(), shortcuts["Nudge Right 1 Frame"]: lambda: self.forward_one_frame(), shortcuts["Show Mask"]: lambda: self.toggle_maskview(), } self.bind('', self.handle_key_press) self.bind("", lambda event: self.focus_set()) def handle_key_press(self, event): if isinstance(self.focus_get(), tk.Entry): return f_keys = ['F1', 'F2', 'F3', 'F4', 'F5', 'F6', 'F7', 'F8', 'F9', 'F10', 'F11', 'F12'] modifiers = [mod for mod, mask in [('shift', 0x0001), ('ctrl', 0x0004), ('alt', 0x20000)] if event.state & mask] key_combination = event.keysym if event.keysym in f_keys else '+'.join(filter(None, ['+'.join(modifiers), event.keysym.lower()])) action = self.key_actions.get(key_combination) action and action() def toggle_and_update(self, switch_name, parameter_name): self.widget[f"{switch_name}Switch"].set(not self.widget[f"{switch_name}Switch"].get()) #endregion # self.bind("", lambda event: self.focus_set()) ##### def create_gui(self): #region [#111111b4] # v_f_frame == self.layer['InputVideoFrame'] self.configure(bg=style.bg) ctk.set_appearance_mode("dark") global tmp tmp = ctk.CTkFrame(self, border_width=0, fg_color=style.main, bg_color=style.bg) tmp.grid(row=1, column=0, sticky='NEWS', padx=0, pady=0) tmp.grid_forget() #endregion #region [#111111b4] def vidupdate(): self.resize_image() #Hide/Unhide Inputs Panel def input_panel_checkbox(): current_state = self.checkbox.get() if current_state: self.layer['InputVideoFrame'].grid(row=0, column=0, sticky='NEWS', padx=0, pady=(0,0)) ks_frame.grid_forget() else: self.layer['InputVideoFrame'].grid_forget() v_f_frame.grid_forget() self.after(10, vidupdate) #Hide/Unhide Faces Panel def collapse_faces_panel(): current_state = self.collapse_bottom.get() if current_state: ff_frame.grid(row=5, column=0, sticky='NEWS', padx=0, pady=(1,0)) mf_frame.grid(row=6, column=0, sticky='NEWS', padx=0, pady=1) self.after(10, vidupdate) else: ff_frame.grid_forget() mf_frame.grid_forget() self.after(10, vidupdate) #Hide/Unhide Parameters Panel def collapse_params_panel(): current_state = self.collapse_params.get() if current_state: self.layer['parameter_frame'].grid(row=0, column=2, sticky='NEWS', pady=0, padx=0) self.after(10, vidupdate) else: self.layer['parameter_frame'].grid_forget() self.after(10, vidupdate) #Keyboard Shortcuts def keyboard_shortcuts(): current_state = self.collapse_keyboardshortcuts.get() if current_state: ks_frame.grid(row=0, column=0, sticky='NEWS', padx=0, pady=(0,0)) self.after(10, vidupdate) self.layer['InputVideoFrame'].grid_forget() self.after(10, vidupdate) self.checkbox.deselect() else: ks_frame.grid_forget() v_f_frame.grid_forget() self.after(10, vidupdate) self.layer['InputVideoFrame'].grid(row=0, column=0, sticky='NEWS', padx=0, pady=(0,0)) self.after(10, vidupdate) self.checkbox.select() #endregion # 1 x 3 Top level grid self.grid_columnconfigure(0, weight=1) self.grid_rowconfigure(0, weight=0) self.grid_rowconfigure(1, weight=1) self.grid_rowconfigure(2, weight=0) self.configure(style.frame_style_bg) # Top Frame top_frame = tk.Frame(self, style.canvas_frame_label_1) top_frame.grid(row=0, column=0, sticky='NEWS', padx=1, pady=1) top_frame.grid_columnconfigure(0, weight=1) top_frame.grid_columnconfigure(1, weight=0) # Middle Frame middle_frame = tk.Frame( self, style.frame_style_bg) middle_frame.grid(row=1, column=0, sticky='NEWS', padx=0, pady=0) middle_frame.grid_rowconfigure(0, weight=1) # Videos and Faces middle_frame.grid_columnconfigure(0, weight=0) # Preview middle_frame.grid_columnconfigure(1, weight=1) # Parameters middle_frame.grid_columnconfigure(2, weight=0) # Scrollbar middle_frame.grid_columnconfigure(3, weight=0) #region [#131710b4] global v_f_frame v_f_frame = ctk.CTkFrame(middle_frame, height = 42, border_width=0, fg_color=style.main) v_f_frame.grid(row=0, column=0, sticky='NEWS', padx=0, pady=(0,0)) y=0 x=10 global ks_frame ks_frame = ctk.CTkFrame(middle_frame, height = 42, width=250, border_width=0, fg_color=style.main, background_corner_colors=(style.main,style.main,style.main,style.main)) ks_frame.grid(row=0, column=0, sticky='NEWS', padx=0, pady=(0,0)) ks_frame.grid_forget() def load_shortcuts_from_json(): try: with open("shortcuts.json", "r") as json_file: return json.load(json_file) except FileNotFoundError: return { "Timeline Beginning": "z", "Nudge Left 30 Frames": "a", "Nudge Right 30 Frames": "d", "Record": "r", "Play": "space", "Save Image": "ctrl+s", "Add Marker": "f", "Delete Marker": "alt+f", "Previous Marker": "q", "Next Marker": "w", "Toggle Restorer": "1", "Toggle Orientation": "2", "Toggle Strength": "3", "Toggle Differencing": "4", "Toggle Occluder": "5", "Toggle Face Parser": "6", "Toggle Text-Based Masking": "7", "Toggle Color Adjustments": "8", "Toggle Face Adjustments": "9", "Clear VRAM": "F1", "Swap Faces": "s", "Nudge Left 1 Frame": "c", "Nudge Right 1 Frame": "v", "Show Mask": "x", } shortcuts = load_shortcuts_from_json() def save_shortcuts_to_json(shortcuts): with open("shortcuts.json", "w") as json_file: json.dump(shortcuts, json_file) def update_key_actions(): self.key_actions = { shortcuts["Timeline Beginning"]: lambda: self.preview_control('q'), shortcuts["Nudge Left 30 Frames"]: lambda: self.preview_control('a'), shortcuts["Record"]: lambda: self.toggle_rec_video(), shortcuts["Play"]: lambda: self.toggle_play_video(), shortcuts["Nudge Right 30 Frames"]: lambda: self.preview_control('d'), shortcuts["Save Image"]: lambda: self.save_image(), shortcuts["Add Marker"]: lambda: self.update_marker('add'), shortcuts["Delete Marker"]: lambda: self.update_marker('delete'), shortcuts["Previous Marker"]: lambda: self.update_marker('prev'), shortcuts["Next Marker"]: lambda: self.update_marker('next'), shortcuts["Toggle Restorer"]: lambda: self.toggle_and_update('Restorer', 'Restorer'), shortcuts["Toggle Orientation"]: lambda: self.toggle_and_update('Orient', 'Orientation'), shortcuts["Toggle Strength"]: lambda: self.toggle_and_update('Strength', 'Strength'), shortcuts["Toggle Differencing"]: lambda: self.toggle_and_update('Diff', 'Differencing'), shortcuts["Toggle Occluder"]: lambda: self.toggle_and_update('Occluder', 'Occluder'), shortcuts["Toggle Face Parser"]: lambda: self.toggle_and_update('FaceParser', 'Face Parser'), shortcuts["Toggle Text-Based Masking"]: lambda: self.toggle_and_update('CLIP', 'Text-Based Masking'), shortcuts["Toggle Color Adjustments"]: lambda: self.toggle_and_update('Color', 'Color Adjustments'), shortcuts["Toggle Face Adjustments"]: lambda: self.toggle_and_update('FaceAdj', 'Input Face Adjustments'), shortcuts["Clear VRAM"]: lambda: self.clear_mem(), shortcuts["Swap Faces"]: lambda: self.toggle_swapper(), shortcuts["Nudge Left 1 Frame"]: lambda:self.back_one_frame(), shortcuts["Nudge Right 1 Frame"]: lambda: self.forward_one_frame(), shortcuts["Show Mask"]: lambda: self.toggle_maskview(), } # Load shortcuts from JSON shortcuts = load_shortcuts_from_json() # Update text variables with loaded shortcuts text_vars = {} for shortcut_name, default_value in shortcuts.items(): text_vars[shortcut_name] = tk.StringVar(value=default_value) # Create save_shortcuts function with parameters def save_shortcuts(): # Update the text variables with the current values from the entry widgets for shortcut_name, text_var in text_vars.items(): shortcuts[shortcut_name] = text_var.get() # Save the current shortcuts to JSON save_shortcuts_to_json(shortcuts) update_key_actions() # Create save button with lambda function save_ks_button = ctk.CTkButton(ks_frame, text="Save Shortcuts", command=save_shortcuts, width=150, height=15, corner_radius=3, fg_color=style.main2, hover_color=style.main3) save_ks_button.place(x=40, y=20) # Create labels and entry widgets for each shortcut y = 60 x = 10 for shortcut_name, default_value in shortcuts.items(): ctk.CTkLabel(ks_frame, text=shortcut_name).place(x=x, y=y) ctk.CTkEntry(ks_frame, textvariable=text_vars[shortcut_name], width=50, height=15, border_width=0).place(x=180, y=y) y += 20 #endregion # Bottom Frame bottom_frame = tk.Frame( self, style.canvas_frame_label_1) bottom_frame.grid(row=2, column=0, sticky='NEWS', padx=1, pady=1) bottom_frame.grid_columnconfigure(0, minsize=100) bottom_frame.grid_columnconfigure(1, weight=1) bottom_frame.grid_columnconfigure(2, minsize=100) ####### Top Frame # Left # Label self.layer['topleft'] = tk.Frame(top_frame, style.canvas_frame_label_1, height = 42) self.layer['topleft'].grid(row=0, column=0, sticky='NEWS', pady=0) # Buttons self.widget['StartButton'] = GE.Button(self.layer['topleft'], 'StartRope', 1, self.load_all, None, 'control', 10, 9, width=200) self.widget['OutputFolderButton'] = GE.Button(self.layer['topleft'], 'OutputFolder', 1, self.select_save_video_path, None, 'control', x=240, y=1, width=190) self.output_videos_text = GE.Text(self.layer['topleft'], '', 1, 240, 20, 190, 20) # Right self.layer['topright'] = tk.Frame(top_frame, style.canvas_frame_label_1, height=42, width=413) self.layer['topright'].grid(row=0, column=1, sticky='NEWS', pady=0) self.control['ClearVramButton'] = GE.Button(self.layer['topright'], 'ClearVramButton', 1, self.clear_mem, None, 'control', x=5, y=9, width=85, height=20) self.static_widget['vram_indicator'] = GE.VRAM_Indicator(self.layer['topright'], 1, 300, 20, 100, 11) #region [#111111b4] ##Button - Hide/Unhide Faces Panel self.checkbox = ctk.CTkCheckBox(self.layer['topleft'], text="Input Panel", text_color='#B0B0B0', command=input_panel_checkbox, onvalue=True, offvalue=False, checkbox_width=18, checkbox_height=18, border_width=0, hover_color='#303030', fg_color=style.main) self.checkbox.place(x=500, y=10) self.checkbox.select() #Button - Hide/Unhide Inputs Panel self.collapse_bottom = ctk.CTkCheckBox(self.layer['topleft'], text="Faces Panel",text_color='#B0B0B0', command=collapse_faces_panel, onvalue=True, offvalue=False, checkbox_width=18,checkbox_height=18,border_width=0,hover_color='#303030',fg_color=style.main) self.collapse_bottom.place(x=600, y=10) self.collapse_bottom.select() #Button - Hide/Unhide Params Panel self.collapse_params = ctk.CTkCheckBox(self.layer['topleft'], text="Parameters Panel",text_color='#B0B0B0', command=collapse_params_panel, onvalue=True, offvalue=False, checkbox_width=18,checkbox_height=18,border_width=0,hover_color='#303030',fg_color=style.main) self.collapse_params.place(x=705, y=10) self.collapse_params.select() self.collapse_keyboardshortcuts = ctk.CTkCheckBox(self.layer['topleft'], text="Keyboard Shortcuts",text_color='#B0B0B0', command=keyboard_shortcuts, onvalue=True, offvalue=False, checkbox_width=18,checkbox_height=18,border_width=0,hover_color='#303030',fg_color=style.main) self.collapse_keyboardshortcuts.place(x=840, y=10) #endregion ####### Middle Frame ### Videos and Faces self.layer['InputVideoFrame'] = tk.Frame(middle_frame, style.canvas_frame_label_3) self.layer['InputVideoFrame'].grid(row=0, column=0, sticky='NEWS', padx=1, pady=0) # Buttons self.layer['InputVideoFrame'].grid_rowconfigure(0, weight=0) # Input Media Canvas self.layer['InputVideoFrame'].grid_rowconfigure(1, weight=1) # Input Videos self.layer['InputVideoFrame'].grid_columnconfigure(0, weight=0) # Scrollbar self.layer['InputVideoFrame'].grid_columnconfigure(1, weight=0) # Input Faces Canvas self.layer['InputVideoFrame'].grid_columnconfigure(0, weight=0) # Scrollbar self.layer['InputVideoFrame'].grid_columnconfigure(1, weight=0) # Input Videos # Button Frame frame = tk.Frame(self.layer['InputVideoFrame'], style.canvas_frame_label_2, height = 42) frame.grid(row=0, column=0, columnspan = 2, sticky='NEWS', padx=0, pady=0) # Buttons self.widget['VideoFolderButton'] = GE.Button(frame, 'LoadTVideos', 2, self.select_video_path, None, 'control', 10, 1, width=195) self.input_videos_text = GE.Text(frame, '', 2, 10, 20, 190, 20) # Input Videos Canvas self.target_media_canvas = tk.Canvas(self.layer['InputVideoFrame'], style.canvas_frame_label_3, height=100, width=195) self.target_media_canvas.grid(row=1, column=0, sticky='NEWS', padx=10, pady=10) self.target_media_canvas.bind("", self.target_videos_mouse_wheel) self.target_media_canvas.create_text(8, 20, anchor='w', fill='grey25', font=("Arial italic", 20), text=" Input Videos") # Scroll Canvas scroll_canvas = tk.Canvas(self.layer['InputVideoFrame'], style.canvas_frame_label_3, bd=0, ) scroll_canvas.grid(row=1, column=1, sticky='NEWS', padx=0, pady=0) scroll_canvas.grid_rowconfigure(0, weight=1) scroll_canvas.grid_columnconfigure(0, weight=1) self.static_widget['input_videos_scrollbar'] = GE.Scrollbar_y(scroll_canvas, self.target_media_canvas) # Input Faces # Button Frame frame = tk.Frame(self.layer['InputVideoFrame'], style.canvas_frame_label_2, height = 42) frame.grid(row=0, column=2, columnspan = 2, sticky='NEWS', padx=0, pady=0) # Buttons self.widget['FacesFolderButton'] = GE.Button(frame, 'LoadSFaces', 2, self.select_faces_path, None, 'control', 10, 1, width=195) self.input_faces_text = GE.Text(frame, '', 2, 10, 20, 190, 20) # Scroll Canvas self.source_faces_canvas = tk.Canvas(self.layer['InputVideoFrame'], style.canvas_frame_label_3, height = 100, width=195) self.source_faces_canvas.grid(row=1, column=2, sticky='NEWS', padx=10, pady=10) self.source_faces_canvas.bind("", self.source_faces_mouse_wheel) self.source_faces_canvas.create_text(8, 20, anchor='w', fill='grey25', font=("Arial italic", 20), text=" Input Faces") scroll_canvas = tk.Canvas(self.layer['InputVideoFrame'], style.canvas_frame_label_3, bd=0, ) scroll_canvas.grid(row=1, column=3, sticky='NEWS', padx=0, pady=0) scroll_canvas.grid_rowconfigure(0, weight=1) scroll_canvas.grid_columnconfigure(0, weight=1) self.static_widget['input_faces_scrollbar'] = GE.Scrollbar_y(scroll_canvas, self.source_faces_canvas) # GE.Separator_y(scroll_canvas, 14, 0) GE.Separator_y(self.layer['InputVideoFrame'], 229, 0) GE.Separator_x(self.layer['InputVideoFrame'], 0, 41) ### Preview self.layer['preview_column'] = tk.Frame(middle_frame, style.canvas_bg) self.layer['preview_column'].grid(row=0, column=1, sticky='NEWS', pady=0) self.layer['preview_column'].grid_columnconfigure(0, weight=1) # Preview Data self.layer['preview_column'].grid_rowconfigure(0, weight=0) # Preview Window self.layer['preview_column'].grid_rowconfigure(1, weight=1) # Timeline self.layer['preview_column'].grid_rowconfigure(2, weight=0) # MArkers self.layer['preview_column'].grid_rowconfigure(3, weight=0) # Controls self.layer['preview_column'].grid_rowconfigure(4, weight=0) # Found Faces self.layer['preview_column'].grid_rowconfigure(5, weight=0) # Merged Faces self.layer['preview_column'].grid_rowconfigure(6, weight=0) # Preview Data preview_data = tk.Frame(self.layer['preview_column'], style.canvas_frame_label_2, height = 24) preview_data.grid(row=0, column=0, sticky='NEWS', pady=0) preview_data.grid_columnconfigure(0, weight=1) preview_data.grid_columnconfigure(1, weight=1) preview_data.grid_columnconfigure(2, weight=1) # preview_data.grid_columnconfigure(3, weight=1) preview_data.grid_rowconfigure(0, weight=0) frame = tk.Frame(preview_data, style.canvas_frame_label_2, height = 24, width=100) frame.grid(row=0, column=0) self.widget['AudioButton'] = GE.Button(frame, 'Audio', 2, self.toggle_audio, None, 'control', x=0, y=0, width=100) frame = tk.Frame(preview_data, style.canvas_frame_label_2, height = 24, width=100) frame.grid(row=0, column=1) self.widget['MaskViewButton'] = GE.Button(frame, 'MaskView', 2, self.toggle_maskview, None, 'control', x=0, y=0, width=100) frame = tk.Frame(preview_data, style.canvas_frame_label_2, height = 24, width=200) frame.grid(row=0, column=2) self.widget['PreviewModeTextSel'] = GE.TextSelection(frame, 'PreviewModeTextSel', '', 2, self.set_view, True, 'control', width=200, height=20, x=0, y=0, text_percent=1) # Preview Window self.video = tk.Label(self.layer['preview_column'], bg='black') self.video.grid(row=1, column=0, sticky='NEWS', padx=0, pady=0) self.video.bind("", self.iterate_through_merged_embeddings) self.video.bind("", lambda event: self.toggle_play_video()) # Videos # Timeline # Slider self.layer['slider_frame'] = tk.Frame(self.layer['preview_column'], style.canvas_frame_label_2, height=20) self.layer['slider_frame'].grid(row=2, column=0, sticky='NEWS', pady=0) self.video_slider = GE.Timeline(self.layer['slider_frame'], self.widget, self.temp_toggle_swapper, self.add_action) # Markers self.layer['markers_canvas'] = tk.Canvas(self.layer['preview_column'], style.canvas_frame_label_2, height = 20) self.layer['markers_canvas'].grid(row=3, column=0, sticky='NEWS') self.layer['markers_canvas'].bind('', lambda e:self.update_marker(e.width)) # self.create_ui_button('ToggleStop', marker_frame, 140, 2, width=36, height=36) # Controls self.layer['preview_frame'] = tk.Frame(self.layer['preview_column'], style.canvas_bg, height = 40) self.layer['preview_frame'].grid(row=4, column=0, sticky='NEWS') self.layer['preview_frame'].grid_columnconfigure(0, weight=0) self.layer['preview_frame'].grid_columnconfigure(1, weight=1) self.layer['preview_frame'].grid_columnconfigure(2, weight=0) self.layer['preview_frame'].grid_rowconfigure(0, weight=0) self.layer['preview_frame'].grid_rowconfigure(1, weight=0) # Left Side self.layer['play_controls_left'] = tk.Frame(self.layer['preview_frame'], style.canvas_frame_label_2, height=30, width=100 ) self.layer['play_controls_left'].grid(row=0, column=0, sticky='NEWS', pady=0) self.widget['SaveImageButton'] = GE.Button(self.layer['play_controls_left'], 'SaveImageButton', 2, self.save_image, None, 'control', x=10, y=5, width=100) # Center cente_frame = tk.Frame(self.layer['preview_frame'], style.canvas_frame_label_2, height=30, ) cente_frame.grid(row=0, column=1, sticky='NEWS', pady=0) cente_frame.grid_columnconfigure(0, weight=0) cente_frame.grid_rowconfigure(0, weight=0) play_control_frame = tk.Frame(cente_frame, style.canvas_frame_label_2, height=30, width=270 ) play_control_frame.place(anchor="c", relx=.5, rely=.5) column = 0 col_delta = 50 self.widget['TLBegButton'] = GE.Button(play_control_frame, 'TLBeginning', 2, self.preview_control, 'q', 'control', x=column , y=2, width=20) column += col_delta self.widget['TLLeftButton'] = GE.Button(play_control_frame, 'TLLeft', 2, self.preview_control, 'a', 'control', x=column , y=2, width=20) column += col_delta self.widget['TLRecButton'] = GE.Button(play_control_frame, 'Record', 2, self.toggle_rec_video, None, 'control', x=column , y=2, width=20) column += col_delta self.widget['TLPlayButton'] = GE.Button(play_control_frame, 'Play', 2, self.toggle_play_video, None, 'control', x=column , y=2, width=20) column += col_delta self.widget['TLRightButton'] = GE.Button(play_control_frame, 'TLRight', 2, self.preview_control, 'd', 'control', x=column , y=2, width=20) # Right Side right_playframe = tk.Frame(self.layer['preview_frame'], style.canvas_frame_label_2, height=30, width=120) right_playframe.grid(row=0, column=2, sticky='NEWS', pady=0) self.widget['AddMarkerButton'] = GE.Button(right_playframe, 'AddMarkerButton', 2, self.update_marker, 'add', 'control', x=0, y=5, width=20) self.widget['DelMarkerButton'] = GE.Button(right_playframe, 'DelMarkerButton', 2, self.update_marker, 'delete', 'control', x=25, y=5, width=20) self.widget['PrevMarkerButton'] = GE.Button(right_playframe, 'PrevMarkerButton', 2, self.update_marker, 'prev', 'control', x=50, y=5, width=20) self.widget['NextMarkerButton'] = GE.Button(right_playframe, 'NextMarkerButton', 2, self.update_marker, 'next', 'control', x=75, y=5, width=20) # self.widget['StopMarkerButton'] = GE.Button(right_playframe, 'StopMarkerButton', 2, self.update_marker, 'stop', 'control', x=100, y=5, width=20) self.widget['SaveMarkerButton'] = GE.Button(right_playframe, 'SaveMarkerButton', 2, self.save_markers_json, None, 'control', x=95, y=5, width=20) # Images self.layer['image_controls'] = tk.Frame(self.layer['preview_column'], style.canvas_frame_label_2, height=80) self.layer['image_controls'].grid(row=2, column=0, rowspan=2, sticky='NEWS', pady=0) self.widget['SaveImageButton'] = GE.Button(self.layer['image_controls'], 'SaveImageButton', 2, self.save_image, None, 'control', x=10, y=5, width=100) self.widget['AutoSwapButton'] = GE.Button(self.layer['image_controls'], 'AutoSwapButton', 2, self.toggle_auto_swap, None, 'control', x=150, y=5, width=100) self.layer['image_controls'].grid_forget() # FaceLab self.layer['FaceLab_controls'] = tk.Frame(self.layer['preview_column'], style.canvas_frame_label_2, height=80) self.layer['FaceLab_controls'].grid(row=2, column=0, rowspan=2, sticky='NEWS', pady=0) self.layer['FaceLab_controls'].grid_forget() # Found Faces ff_frame = tk.Frame(self.layer['preview_column'], style.canvas_frame_label_1) ff_frame.grid(row=5, column=0, sticky='NEWS', pady=1) ff_frame.grid_columnconfigure(0, weight=0) ff_frame.grid_columnconfigure(1, weight=1) ff_frame.grid_rowconfigure(0, weight=0) # Buttons button_frame = tk.Frame(ff_frame, style.canvas_frame_label_2, height = 100, width = 112) button_frame.grid( row = 0, column = 0, ) self.widget['FindFacesButton'] = GE.Button(button_frame, 'FindFaces', 2, self.find_faces, None, 'control', x=0, y=0, width=112, height=33) self.widget['ClearFacesButton'] = GE.Button(button_frame, 'ClearFaces', 2, self.clear_faces, None, 'control', x=0, y=33, width=112, height=33) self.widget['SwapFacesButton'] = GE.Button(button_frame, 'SwapFaces', 2, self.toggle_swapper, None, 'control', x=0, y=66, width=112, height=33) # Scroll Canvas self.found_faces_canvas = tk.Canvas(ff_frame, style.canvas_frame_label_3, height = 100 ) self.found_faces_canvas.grid( row = 0, column = 1, sticky='NEWS') self.found_faces_canvas.bind("", self.target_faces_mouse_wheel) self.found_faces_canvas.create_text(8, 45, anchor='w', fill='grey25', font=("Arial italic", 20), text=" Found Faces") self.static_widget['20'] = GE.Separator_y(ff_frame, 111, 0) # Merged Faces mf_frame = tk.Frame(self.layer['preview_column'], style.canvas_frame_label_1) mf_frame.grid(row=6, column=0, sticky='NEWS', pady=0) mf_frame.grid_columnconfigure(0, minsize=10) mf_frame.grid_columnconfigure(1, weight=1) mf_frame.grid_rowconfigure(0, weight=0) # Buttons button_frame = tk.Frame(mf_frame, style.canvas_frame_label_2, height = 100, width = 112) button_frame.grid( row = 0, column = 0, ) self.widget['DelEmbedButton'] = GE.Button(button_frame, 'DelEmbed', 2, self.delete_merged_embedding, None, 'control', x=0, y=0, width=112, height=33) # Merged Embeddings Text self.merged_embedding_name = tk.StringVar() self.merged_embeddings_text = tk.Entry(button_frame, style.entry_2, textvariable=self.merged_embedding_name) self.merged_embeddings_text.place(x=8, y=37, width = 96, height=20) self.merged_embeddings_text.bind("", lambda event: self.save_selected_source_faces(self.merged_embedding_name)) self.me_name = self.nametowidget(self.merged_embeddings_text) # Scroll Canvas self.merged_faces_canvas = tk.Canvas(mf_frame, style.canvas_frame_label_3, height = 100) self.merged_faces_canvas.grid( row = 0, column = 1, sticky='NEWS') self.merged_faces_canvas.grid_rowconfigure(0, weight=1) self.merged_faces_canvas.bind("", lambda event: self.merged_faces_canvas.xview_scroll(-int(event.delta/120.0), "units")) self.merged_faces_canvas.create_text(8, 45, anchor='w', fill='grey25', font=("Arial italic", 20), text=" Merged Faces") self.static_widget['21'] = GE.Separator_y(mf_frame, 111, 0) ### Parameters width=398 self.layer['parameter_frame'] = tk.Frame(middle_frame, style.canvas_frame_label_3, bd=0, width=width) self.layer['parameter_frame'].grid(row=0, column=2, sticky='NEWS', pady=0, padx=1) self.layer['parameter_frame'].grid_rowconfigure(0, weight=0) self.layer['parameter_frame'].grid_rowconfigure(1, weight=1) self.layer['parameter_frame'].grid_rowconfigure(2, weight=0) self.layer['parameter_frame'].grid_columnconfigure(0, weight=0) self.layer['parameter_frame'].grid_columnconfigure(1, weight=0) parameters_control_frame = tk.Frame(self.layer['parameter_frame'], style.canvas_frame_label_2, bd=0, width=width, height = 42) parameters_control_frame.grid(row=0, column=0, columnspan=2, sticky='NEWS', pady=0, padx=0) parameters_control_frame.grid_columnconfigure(0, weight=1) parameters_control_frame.grid_columnconfigure(1, weight=1) parameters_control_frame.grid_columnconfigure(2, weight=1) parameters_control_frame.grid_rowconfigure(0, weight=0) frame = tk.Frame(parameters_control_frame, style.canvas_frame_label_2, height = 42, width=100) frame.grid(row=0, column=0) self.widget['SaveParamsButton'] = GE.Button(frame, 'SaveParamsButton', 2, self.parameter_io, 'save', 'control', x=0 , y=8, width=100) frame = tk.Frame(parameters_control_frame, style.canvas_frame_label_2, height = 42, width=100) frame.grid(row=0, column=1) self.widget['LoadParamsButton'] = GE.Button(frame, 'LoadParamsButton', 2, self.parameter_io, 'load', 'control', x=0 , y=8, width=100) frame = tk.Frame(parameters_control_frame, style.canvas_frame_label_2, height = 42, width=100) frame.grid(row=0, column=2) self.widget['DefaultParamsButton'] = GE.Button(frame, 'DefaultParamsButton', 2, self.parameter_io, 'default', 'control', x=0 , y=8, width=100) self.layer['parameters_canvas'] = tk.Canvas(self.layer['parameter_frame'], style.canvas_frame_label_3, bd=0, width=width) self.layer['parameters_canvas'].grid(row=1, column=0, sticky='NEWS', pady=0, padx=0) self.layer['parameters_frame'] = tk.Frame(self.layer['parameters_canvas'], style.canvas_frame_label_3, bd=0, width=width, height=1150) self.layer['parameters_frame'].grid(row=0, column=0, sticky='NEWS', pady=0, padx=0) self.layer['parameters_canvas'].create_window(0, 0, window = self.layer['parameters_frame'], anchor='nw') self.layer['parameter_scroll_canvas'] = tk.Canvas(self.layer['parameter_frame'], style.canvas_frame_label_3, bd=0, ) self.layer['parameter_scroll_canvas'].grid(row=1, column=1, sticky='NEWS', pady=0) self.layer['parameter_scroll_canvas'].grid_rowconfigure(0, weight=1) self.layer['parameter_scroll_canvas'].grid_columnconfigure(0, weight=1) self.static_widget['parameters_scrollbar'] = GE.Scrollbar_y(self.layer['parameter_scroll_canvas'], self.layer['parameters_canvas']) self.static_widget['30'] = GE.Separator_x(parameters_control_frame, 0, 41) ### Layout ### top_border_delta = 25 bottom_border_delta = 5 switch_delta = 25 row_delta = 20 row = 1 column = 160 # Restore self.widget['RestorerSwitch'] = GE.Switch2(self.layer['parameters_frame'], 'RestorerSwitch', 'Restorer', 3, self.update_data, 'parameter', 398, 20, 1, row) row += switch_delta self.widget['RestorerTypeTextSel'] = GE.TextSelection(self.layer['parameters_frame'], 'RestorerTypeTextSel', 'Restorer Type', 3, self.update_data, 'parameter', 'parameter', 398, 20, 1, row, 0.72) row += row_delta self.widget['RestorerDetTypeTextSel'] = GE.TextSelection(self.layer['parameters_frame'], 'RestorerDetTypeTextSel', 'Detection Alignment', 3, self.update_data, 'parameter', 'parameter', 398, 20, 1, row, 0.72) row += row_delta self.widget['RestorerSlider'] = GE.Slider2(self.layer['parameters_frame'], 'RestorerSlider', 'Blend', 3, self.update_data, 'parameter', 398, 20, 1, row, 0.72) row += top_border_delta self.static_widget['9'] = GE.Separator_x(self.layer['parameters_frame'], 0, row) row += bottom_border_delta # Threshhold self.widget['ThresholdSlider'] = GE.Slider2(self.layer['parameters_frame'], 'ThresholdSlider', 'Similarity Threshhold', 3, self.update_data, 'parameter', 398, 20, 1, row, 0.62) row += top_border_delta self.static_widget['3'] = GE.Separator_x(self.layer['parameters_frame'], 0, row) row += bottom_border_delta # Orientation self.widget['OrientSwitch'] = GE.Switch2(self.layer['parameters_frame'], 'OrientSwitch', 'Orientation', 3, self.update_data, 'parameter', 398, 20, 1, row) row += switch_delta self.widget['OrientSlider'] = GE.Slider2(self.layer['parameters_frame'], 'OrientSlider', 'Angle', 3, self.update_data, 'parameter', 398, 20, 1, row, 0.62) row += top_border_delta self.static_widget['2'] = GE.Separator_x(self.layer['parameters_frame'], 0, row) row += bottom_border_delta # Strength self.widget['StrengthSwitch'] = GE.Switch2(self.layer['parameters_frame'], 'StrengthSwitch', 'Strength', 3, self.update_data, 'parameter', 398, 20, 1, row) row += switch_delta self.widget['StrengthSlider'] = GE.Slider2(self.layer['parameters_frame'], 'StrengthSlider', 'Amount', 3, self.update_data, 'parameter', 398, 20, 1, row, 0.62) row += top_border_delta self.static_widget['5'] = GE.Separator_x(self.layer['parameters_frame'], 0, row) row += bottom_border_delta # Border self.widget['BorderTopSlider'] = GE.Slider2(self.layer['parameters_frame'], 'BorderTopSlider', 'Top Border Distance', 3, self.update_data, 'parameter', 398, 20, 1, row, 0.62) row += row_delta self.widget['BorderSidesSlider'] = GE.Slider2(self.layer['parameters_frame'], 'BorderSidesSlider', 'Sides Border Distance', 3, self.update_data, 'parameter', 398, 20, 1, row, 0.62) row += row_delta self.widget['BorderBottomSlider'] = GE.Slider2(self.layer['parameters_frame'], 'BorderBottomSlider', 'Bottom Border Distance', 3, self.update_data, 'parameter', 398, 20, 1, row, 0.62) row += row_delta self.widget['BorderBlurSlider'] = GE.Slider2(self.layer['parameters_frame'], 'BorderBlurSlider', 'Border Blend', 3, self.update_data, 'parameter', 398, 20, 1, row, 0.62) row += top_border_delta self.static_widget['7'] = GE.Separator_x(self.layer['parameters_frame'], 0, row) row += bottom_border_delta # Diff self.widget['DiffSwitch'] = GE.Switch2(self.layer['parameters_frame'], 'DiffSwitch', 'Differencing', 3, self.update_data, 'parameter', 398, 20, 1, row) row += switch_delta self.widget['DiffSlider'] = GE.Slider2(self.layer['parameters_frame'], 'DiffSlider', 'Amount', 3, self.update_data, 'parameter', 398, 20, 1, row, 0.62) row += top_border_delta self.static_widget['8'] = GE.Separator_x(self.layer['parameters_frame'], 0, row) row += bottom_border_delta # Occluder self.widget['OccluderSwitch'] = GE.Switch2(self.layer['parameters_frame'], 'OccluderSwitch', 'Occluder', 3, self.update_data, 'parameter', 398, 20, 1, row) row += switch_delta self.widget['OccluderSlider'] = GE.Slider2(self.layer['parameters_frame'], 'OccluderSlider', 'Size', 3, self.update_data, 'parameter', 398, 20, 1, row, 0.62) row += top_border_delta self.static_widget['10'] = GE.Separator_x(self.layer['parameters_frame'], 0, row) row += bottom_border_delta # FaceParser - Face self.widget['FaceParserSwitch'] = GE.Switch2(self.layer['parameters_frame'], 'FaceParserSwitch', 'Face Parser', 3, self.update_data, 'parameter', 398, 20, 1, row) row += switch_delta self.widget['FaceParserSlider'] = GE.Slider2(self.layer['parameters_frame'], 'FaceParserSlider', 'Background', 3, self.update_data, 'parameter', 398, 20, 1, row, 0.62) row += row_delta self.widget['MouthParserSlider'] = GE.Slider2(self.layer['parameters_frame'], 'MouthParserSlider', 'Mouth', 3, self.update_data, 'parameter', 398, 20, 1, row, 0.62) row += top_border_delta self.static_widget['12'] = GE.Separator_x(self.layer['parameters_frame'], 0, row) row += bottom_border_delta # CLIP self.widget['CLIPSwitch'] = GE.Switch2(self.layer['parameters_frame'], 'CLIPSwitch', 'Text-Based Masking', 3, self.update_data, 'parameter', 398, 20, 1, row) row += switch_delta self.widget['CLIPTextEntry'] = GE.Text_Entry(self.layer['parameters_frame'], 'CLIPTextEntry', 'Text-Based Masking', 3, self.update_data, 'parameter', 398, 20, 1, row, 0.62) row += row_delta self.widget['CLIPSlider'] = GE.Slider2(self.layer['parameters_frame'], 'CLIPSlider', 'Amount', 3, self.update_data, 'parameter', 398, 20, 1, row, 0.62) row += top_border_delta self.static_widget['12'] = GE.Separator_x(self.layer['parameters_frame'], 0, row) row += bottom_border_delta # Blur self.widget['BlendSlider'] = GE.Slider2(self.layer['parameters_frame'], 'BlendSlider', 'Overall Mask Blend', 3, self.update_data, 'parameter', 398, 20, 1, row, 0.62) row += top_border_delta self.static_widget['13'] = GE.Separator_x(self.layer['parameters_frame'], 0, row) row += bottom_border_delta # Color Adjustments self.widget['ColorSwitch'] = GE.Switch2(self.layer['parameters_frame'], 'ColorSwitch', 'Color Adjustments', 3, self.update_data, 'parameter', 398, 20, 1, row) row += switch_delta self.widget['ColorRedSlider'] = GE.Slider2(self.layer['parameters_frame'], 'ColorRedSlider', 'Red', 3, self.update_data, 'parameter', 398, 20, 1, row, 0.62) row += row_delta self.widget['ColorGreenSlider'] = GE.Slider2(self.layer['parameters_frame'], 'ColorGreenSlider', 'Green', 3, self.update_data, 'parameter', 398, 20, 1, row, 0.62) row += row_delta self.widget['ColorBlueSlider'] = GE.Slider2(self.layer['parameters_frame'], 'ColorBlueSlider', 'Blue', 3, self.update_data, 'parameter', 398, 20, 1, row, 0.62) row += row_delta self.widget['ColorGammaSlider'] = GE.Slider2(self.layer['parameters_frame'], 'ColorGammaSlider', 'Gamma', 3, self.update_data, 'parameter', 398, 20, 1, row, 0.62) row += top_border_delta self.static_widget['6'] = GE.Separator_x(self.layer['parameters_frame'], 0, row) row += bottom_border_delta # KPS Adjustment and scaling self.widget['FaceAdjSwitch'] = GE.Switch2(self.layer['parameters_frame'], 'FaceAdjSwitch', 'Input Face Adjustments', 3, self.update_data, 'parameter', 398, 20, 1, row) row += switch_delta self.widget['KPSXSlider'] = GE.Slider2(self.layer['parameters_frame'], 'KPSXSlider', 'KPS - X', 3, self.update_data, 'parameter', 398, 20, 1, row, 0.62) row += row_delta self.widget['KPSYSlider'] = GE.Slider2(self.layer['parameters_frame'], 'KPSYSlider', 'KPS - Y', 3, self.update_data, 'parameter', 398, 20, 1, row, 0.62) row += row_delta self.widget['KPSScaleSlider'] = GE.Slider2(self.layer['parameters_frame'], 'KPSScaleSlider', 'KPS - Scale', 3, self.update_data, 'parameter', 398, 20, 1, row, 0.62) row += row_delta self.widget['FaceScaleSlider'] = GE.Slider2(self.layer['parameters_frame'], 'FaceScaleSlider', 'Face Scale', 3, self.update_data, 'parameter', 398, 20, 1, row, 0.62) row += top_border_delta self.static_widget['4'] = GE.Separator_x(self.layer['parameters_frame'], 0, row) row += bottom_border_delta # Cats and Dogs self.widget['ThreadsSlider'] = GE.Slider2(self.layer['parameters_frame'], 'ThreadsSlider', 'Threads', 3, self.update_data, 'parameter', 398, 20, 1, row, 0.62) row += row_delta self.widget['DetectTypeTextSel'] = GE.TextSelection(self.layer['parameters_frame'], 'DetectTypeTextSel', 'Detection Type', 3, self.update_data, 'parameter', 'parameter', 398, 20, 1, row, 0.62) row += row_delta self.widget['DetectScoreSlider'] = GE.Slider2(self.layer['parameters_frame'], 'DetectScoreSlider', 'Detect Score', 3, self.update_data, 'parameter', 398, 20, 1, row, 0.62) # Landmarks Detection row += top_border_delta self.static_widget['4'] = GE.Separator_x(self.layer['parameters_frame'], 0, row) row += bottom_border_delta self.widget['LandmarksDetectionAdjSwitch'] = GE.Switch2(self.layer['parameters_frame'], 'LandmarksDetectionAdjSwitch', 'Landmarks Detection Adjustments', 3, self.update_data, 'parameter', 398, 20, 1, row) row += switch_delta self.widget['LandmarksAlignModeFromPointsSwitch'] = GE.Switch2(self.layer['parameters_frame'], 'LandmarksAlignModeFromPointsSwitch', 'From Points', 3, self.update_data, 'parameter', 398, 20, 1, row, 30, 40) row += switch_delta self.widget['LandmarksDetectTypeTextSel'] = GE.TextSelection(self.layer['parameters_frame'], 'LandmarksDetectTypeTextSel', 'Landmarks Detection Type', 3, self.update_data, 'parameter', 'parameter', 398, 20, 1, row, 0.62) row += row_delta self.widget['LandmarksDetectScoreSlider'] = GE.Slider2(self.layer['parameters_frame'], 'LandmarksDetectScoreSlider', 'Landmarks Detect Score', 3, self.update_data, 'parameter', 398, 20, 1, row, 0.62) row += row_delta self.widget['ShowLandmarksSwitch'] = GE.Switch2(self.layer['parameters_frame'], 'ShowLandmarksSwitch', 'Show Landmarks', 3, self.update_data, 'parameter', 398, 20, 1, row) row += top_border_delta self.static_widget['4'] = GE.Separator_x(self.layer['parameters_frame'], 0, row) row += bottom_border_delta # self.widget['RecordTypeTextSel'] = GE.TextSelection(self.layer['parameters_frame'], 'RecordTypeTextSel', 'Record Type', 3, self.update_data, 'parameter', 'parameter', 398, 20, 1, row, 0.62) row += row_delta self.widget['VideoQualSlider'] = GE.Slider2(self.layer['parameters_frame'], 'VideoQualSlider', 'FFMPEG Quality', 3, self.update_data, 'parameter', 398, 20, 1, row, 0.62) row += row_delta self.widget['MergeTextSel'] = GE.TextSelection(self.layer['parameters_frame'], 'MergeTextSel', 'Merge Math', 3, self.select_input_faces, 'merge', '', 398, 20, 1, row, 0.62) row += row_delta self.widget['SwapperTypeTextSel'] = GE.TextSelection(self.layer['parameters_frame'], 'SwapperTypeTextSel', 'Swapper Resolution', 3, self.update_data, 'parameter', 'parameter', 398, 20, 1, row, 0.62) ### Other self.layer['tooltip_frame'] = tk.Frame(self.layer['parameter_frame'], style.canvas_frame_label_3, height=80) self.layer['tooltip_frame'].grid(row=2, column=0, columnspan=2, sticky='NEWS', padx=0, pady=0) self.layer['tooltip_label'] = tk.Label(self.layer['tooltip_frame'], style.info_label, wraplength=width-10, image=self.blank, compound='left', height=80, width=width-10) self.layer['tooltip_label'].place(x=5, y=5) self.static_widget['13'] = GE.Separator_x(self.layer['tooltip_frame'], 0, 0) ######### FaceLab self.layer['facelab_canvas'] = tk.Canvas(self.layer['parameter_frame'], style.canvas_frame_label_3, bd=0, width=width) self.layer['facelab_canvas'].grid(row=1, column=0, sticky='NEWS', pady=0, padx=0) # self.layer['facelab_frame'] = tk.Frame(self.layer['facelab_canvas'], style.canvas_frame_label_3, bd=0, width=width, height=11000) self.layer['facelab_frame'].grid(row=0, column=0, sticky='NEWS', pady=0, padx=0) # self.layer['facelab_canvas'].create_window(0, 0, window=self.layer['facelab_frame'], anchor='nw') # self.layer['facelab_scroll_canvas'] = tk.Canvas(self.layer['parameter_frame'], style.canvas_frame_label_3, bd=0, ) self.layer['facelab_scroll_canvas'].grid(row=1, column=1, sticky='NEWS', pady=0) self.layer['facelab_scroll_canvas'].grid_rowconfigure(0, weight=1) self.layer['facelab_scroll_canvas'].grid_columnconfigure(0, weight=1) self.static_widget['facelab_scrollbar'] =GE.Scrollbar_y(self.layer['facelab_scroll_canvas'] , self.layer['facelab_canvas']) # row_delta = 20 # row = 1 # for i in range(512): # temp_str = 'emb_vec_'+str(i) # self.widget[temp_str] = GE.Slider3(self.layer['facelab_frame'], 'emb_vec_'+str(i), str(i), 3, self.adjust_embedding, i, 398, 20, 1, row, 0.62) # row += row_delta ### Layout ### top_border_delta = 25 bottom_border_delta = 5 switch_delta = 25 row_delta = 20 row = 1 column = 160 ######### Options self.status_left_label = tk.Label(bottom_frame, style.donate_1, cursor="hand2", text=" Questions/Help/Discussions (Discord)") self.status_left_label.grid( row = 0, column = 0, sticky='NEWS') self.status_left_label.bind("", lambda e: self.callback("https://discord.gg/EcdVAFJzqp")) self.status_label = tk.Label(bottom_frame, style.donate_1, text="Rope Github") self.status_label.grid( row = 0, column = 1, sticky='NEWS') self.status_label.bind("", lambda e: self.callback("https://github.com/Hillobar/Rope")) self.donate_label = tk.Label(bottom_frame, style.donate_1, text="Enjoy Rope? Please Support! (Paypal) ", anchor='e') self.donate_label.grid( row = 0, column = 2, sticky='NEWS') self.donate_label.bind("", lambda e: self.callback("https://www.paypal.com/donate/?hosted_button_id=Y5SB9LSXFGRF2")) # Update the parameters or controls dicts and get a new frame def update_data(self, mode, name, use_markers=False): # print(inspect.currentframe().f_back.f_code.co_name,) if mode=='parameter': self.parameters[name] = self.widget[name].get() self.add_action('parameters', self.parameters) elif mode=='control': self.control[name] = self.widget[name].get() self.add_action('control', self.control) if use_markers: self.add_action('get_requested_video_frame', self.video_slider.get()) else: self.add_action('get_requested_video_frame_without_markers', self.video_slider.get()) def callback(self, url): webbrowser.open_new_tab(url) def target_faces_mouse_wheel(self, event): self.found_faces_canvas.xview_scroll(1*int(event.delta/120.0), "units") def source_faces_mouse_wheel(self, event): self.source_faces_canvas.yview_scroll(-int(event.delta/120.0), "units") # Center of visible canvas as a percentage of the entire canvas center = (self.source_faces_canvas.yview()[1]-self.source_faces_canvas.yview()[0])/2 center = center+self.source_faces_canvas.yview()[0] self.static_widget['input_faces_scrollbar'].set(center) def target_videos_mouse_wheel(self, event): self.target_media_canvas.yview_scroll(-int(event.delta/120.0), "units") # Center of visible canvas as a percentage of the entire canvas center = (self.target_media_canvas.yview()[1]-self.target_media_canvas.yview()[0])/2 center = center+self.target_media_canvas.yview()[0] self.static_widget['input_videos_scrollbar'].set(center) def parameters_mouse_wheel(self, event): self.canvas.yview_scroll(1*int(event.delta/120.0), "units") # focus_get() # def preview_control(self, event): # # print(event.char, event.keysym, event.keycode) # # print(type(event)) # if isinstance(event, str): # event = event # else: # event = event.char # if self.focus_get() != self.widget['CLIPTextEntry'] and self.focus_get() != self.merged_embeddings_text: # #asd # if self.video_loaded: # frame = self.video_slider.get() # video_length = self.video_slider.get_length() # if event == ' ': # self.toggle_play_video() # elif event == 'w': # frame += 1 # if frame > video_length: # frame = video_length # self.video_slider.set(frame) # self.add_action("get_requested_video_frame", frame) # elif event == 's': # frame -= 1 # if frame < 0: # frame = 0 # self.video_slider.set(frame) # self.add_action("get_requested_video_frame", frame) # elif event == 'd': # frame += 30 # if frame > video_length: # frame = video_length # self.video_slider.set(frame) # self.add_action("get_requested_video_frame", frame) # elif event == 'a': # frame -= 30 # if frame < 0: # frame = 0 # self.video_slider.set(frame) # self.add_action("get_requested_video_frame", frame) # elif event == 'q': # frame = 0 # self.video_slider.set(frame) # self.add_action("get_requested_video_frame", frame) def forward_one_frame(self): frame = self.video_slider.get() video_length = self.video_slider.get_length() frame += 1 if frame > video_length: frame = video_length self.video_slider.set(frame) self.add_action("get_requested_video_frame", frame) def back_one_frame(self): frame = self.video_slider.get() frame -= 1 if frame < 0: frame = 0 self.video_slider.set(frame) self.add_action("get_requested_video_frame", frame) def preview_control(self, event): # print(event.char, event.keysym, event.keycode) # print(type(event)) if isinstance(event, str): event = event else: event = event.char # if self.focus_get() != self.CLIP_name and self.focus_get() != self.me_name and self.parameters['ImgVidMode'] == 0: if self.widget['PreviewModeTextSel'].get()=='Video' and self.video_loaded: frame = self.video_slider.get() video_length = self.video_slider.get_length() if event == ' ': self.toggle_play_video() elif event == 'w': frame += 1 if frame > video_length: frame = video_length self.video_slider.set(frame) self.add_action("get_requested_video_frame", frame) # self.parameter_update_from_marker(frame) elif event == 's': frame -= 1 if frame < 0: frame = 0 self.video_slider.set(frame) self.add_action("get_requested_video_frame", frame) # self.parameter_update_from_marker(frame) elif event == 'd': frame += 30 if frame > video_length: frame = video_length self.video_slider.set(frame) self.add_action("get_requested_video_frame", frame) # self.parameter_update_from_marker(frame) elif event == 'a': frame -= 30 if frame < 0: frame = 0 self.video_slider.set(frame) self.add_action("get_requested_video_frame", frame) # self.parameter_update_from_marker(frame) elif event == 'q': frame = 0 self.video_slider.set(frame) self.add_action("get_requested_video_frame", frame) # self.parameter_update_from_marker(frame) # refactor - make sure files are closed def initialize_gui( self ): json_object = {} # check if data.json exists, if not then create it, else load it try: data_json_file = open("data.json", "r") except: with open("data.json", "w") as outfile: json.dump(self.json_dict, outfile) else: json_object = json.load(data_json_file) data_json_file.close() # Window position and size try: self.json_dict['dock_win_geom'] = json_object['dock_win_geom'] except: self.json_dict['dock_win_geom'] = self.json_dict['dock_win_geom'] # Initialize the window sizes and positions self.geometry('%dx%d+%d+%d' % (self.json_dict['dock_win_geom'][0], self.json_dict['dock_win_geom'][1] , self.json_dict['dock_win_geom'][2], self.json_dict['dock_win_geom'][3])) self.window_last_change = self.winfo_geometry() # self.bind('', lambda event: self.preview_control(event)) # self.bind('', lambda event: self.preview_control(event)) self.resizable(width=True, height=True) # Build UI, update ui with default data self.create_gui() self.video_image = cv2.cvtColor(cv2.imread('./rope/media/splash.png'), cv2.COLOR_BGR2RGB) self.resize_image() # Create parameters and controls and and selctively fill with UI data for key, value in self.widget.items(): self.widget[key].add_info_frame(self.layer['tooltip_label']) if self.widget[key].get_data_type()=='parameter': self.parameters[key] = self.widget[key].get() elif self.widget[key].get_data_type()=='control': self.control[key] = self.widget[key].get() try: self.json_dict["source videos"] = json_object["source videos"] except KeyError: self.widget['VideoFolderButton'].error_button() else: if self.json_dict["source videos"] == None: self.widget['VideoFolderButton'].error_button() else: path = self.create_path_string(self.json_dict["source videos"], 28) self.input_videos_text.configure(text=path) try: self.json_dict["source faces"] = json_object["source faces"] except KeyError: self.widget['FacesFolderButton'].error_button() else: if self.json_dict["source faces"] == None: self.widget['FacesFolderButton'].error_button() else: path = self.create_path_string(self.json_dict["source faces"], 28) self.input_faces_text.configure(text=path) try: self.json_dict["saved videos"] = json_object["saved videos"] except KeyError: self.widget['OutputFolderButton'].error_button() else: if self.json_dict["saved videos"] == None: self.widget['OutputFolderButton'].error_button() else: path = self.create_path_string(self.json_dict["saved videos"], 28) self.output_videos_text.configure(text=path) self.add_action("saved_video_path", self.json_dict["saved videos"]) # Check for a user parameters file and load if present try: parameters_json_file = open("saved_parameters.json", "r") except: pass else: temp = json.load(parameters_json_file) parameters_json_file.close() for key, value in self.parameters.items(): try: self.parameters[key] = temp[key] except KeyError: pass # Update the UI for key, value in self.parameters.items(): self.widget[key].set(value, request_frame=False) self.add_action('parameters', self.parameters) self.add_action('control', self.control) self.widget['StartButton'].error_button() self.set_view(False, '') def create_path_string(self, path, text_len): if len(path)>text_len: last_folder = os.path.basename(os.path.normpath(path)) last_folder_len = len(last_folder) if last_folder_len>text_len: path = path[:3]+'...'+path[-last_folder_len+6:] else: path = path[:text_len-last_folder_len]+'.../'+path[-last_folder_len:] return path def load_all(self): if not self.json_dict["source videos"] or not self.json_dict["source faces"]: print("Please set faces and videos folders first!") return self.populate_target_videos() self.load_input_faces() self.widget['StartButton'].enable_button() def select_video_path(self): temp = self.json_dict["source videos"] self.json_dict["source videos"] = filedialog.askdirectory(title="Select Target Videos Folder", initialdir=temp) path = self.create_path_string(self.json_dict["source videos"], 28) self.input_videos_text.configure(text=path) with open("data.json", "w") as outfile: json.dump(self.json_dict, outfile) outfile.close() self.widget['VideoFolderButton'].set(False, request_frame=False) self.populate_target_videos() def select_save_video_path(self): temp = self.json_dict["saved videos"] self.json_dict["saved videos"] = filedialog.askdirectory(title="Select Save Video Folder", initialdir=temp) path = self.create_path_string(self.json_dict["saved videos"], 28) self.output_videos_text.configure(text=path) with open("data.json", "w") as outfile: json.dump(self.json_dict, outfile) outfile.close() self.widget['OutputFolderButton'].set(False, request_frame=False) self.add_action("saved_video_path",self.json_dict["saved videos"]) def select_faces_path(self): temp = self.json_dict["source faces"] self.json_dict["source faces"] = filedialog.askdirectory(title="Select Source Faces Folder", initialdir=temp) path = self.create_path_string(self.json_dict["source faces"], 28) self.input_faces_text.configure(text=path) with open("data.json", "w") as outfile: json.dump(self.json_dict, outfile) outfile.close() self.widget['FacesFolderButton'].set(False, request_frame=False) self.load_input_faces() def load_input_faces(self): self.source_faces = [] self.merged_faces_canvas.delete("all") self.source_faces_canvas.delete("all") # First load merged embeddings try: temp0 = [] with open("merged_embeddings.txt", "r") as embedfile: temp = embedfile.read().splitlines() for i in range(0, len(temp), 513): to = [temp[i][6:], np.array(temp[i+1:i+513], dtype='float32')] temp0.append(to) for j in range(len(temp0)): new_source_face = self.source_face.copy() self.source_faces.append(new_source_face) self.source_faces[j]["ButtonState"] = False self.source_faces[j]["Embedding"] = temp0[j][1] self.source_faces[j]["TKButton"] = tk.Button(self.merged_faces_canvas, style.media_button_off_3, image=self.blank, text=temp0[j][0], height=14, width=84, compound='left') self.source_faces[j]["TKButton"].bind("", lambda event, arg=j: self.select_input_faces(event, arg)) self.source_faces[j]["TKButton"].bind("", lambda event: self.merged_faces_canvas.xview_scroll(-int(event.delta/120.0), "units")) self.merged_faces_canvas.create_window((j//4)*92,8+(22*(j%4)), window = self.source_faces[j]["TKButton"],anchor='nw') self.merged_faces_canvas.configure(scrollregion = self.merged_faces_canvas.bbox("all")) self.merged_faces_canvas.xview_moveto(0) except: pass self.shift_i_len = len(self.source_faces) # Next Load images directory = self.json_dict["source faces"] filenames = [os.path.join(dirpath,f) for (dirpath, dirnames, filenames) in os.walk(directory) for f in filenames] # torch.cuda.memory._record_memory_history(True, trace_alloc_max_entries=100000, trace_alloc_record_context=True) i=0 for file in filenames: # Does not include full path # Find all faces and ad to faces[] # Guess File type based on extension try: file_type = mimetypes.guess_type(file)[0][:5] except: print('Unrecognized file type:', file) else: # Its an image if file_type == 'image': img = cv2.imread(file) if img is not None: # convert to RGB format img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB) img = torch.from_numpy(img.astype('uint8')).to('cuda') pad_scale = 0.2 padded_width = int(img.size()[1]*(1.+pad_scale)) padded_height = int(img.size()[0]*(1.+pad_scale)) padding = torch.zeros((padded_height, padded_width, 3), dtype=torch.uint8, device='cuda:0') width_start = int(img.size()[1]*pad_scale/2) width_end = width_start+int(img.size()[1]) height_start = int(img.size()[0]*pad_scale/2) height_end = height_start+int(img.size()[0]) padding[height_start:height_end, width_start:width_end, :] = img img = padding img = img.permute(2,0,1) try: bboxes, kpss = self.models.run_detect(img, detect_mode=self.parameters["DetectTypeTextSel"], max_num=1, score=0.5, use_landmark_detection=self.parameters['LandmarksDetectionAdjSwitch'], landmark_detect_mode=self.parameters["LandmarksDetectTypeTextSel"], landmark_score=0.5, from_points=self.parameters["LandmarksAlignModeFromPointsSwitch"]) # Just one face here kpss = kpss[0] except IndexError: print('Image cropped too close:', file) else: face_emb, cropped_image = self.models.run_recognize(img, kpss) # PIL follows RGB color convention and cropped image come in RGB format crop = torchvision.transforms.functional.to_pil_image(cropped_image) crop = crop.resize((85, 85)) new_source_face = self.source_face.copy() self.source_faces.append(new_source_face) self.source_faces[-1]["Image"] = ImageTk.PhotoImage(image=crop) self.source_faces[-1]["Embedding"] = face_emb self.source_faces[-1]["TKButton"] = tk.Button(self.source_faces_canvas, style.media_button_off_3, image=self.source_faces[-1]["Image"], height=90, width=90) self.source_faces[-1]["ButtonState"] = False self.source_faces[-1]["file"] = file self.source_faces[-1]["TKButton"].bind("", lambda event, arg=len(self.source_faces)-1: self.select_input_faces(event, arg)) self.source_faces[-1]["TKButton"].bind("", self.source_faces_mouse_wheel) self.source_faces_canvas.create_window((i % 2) * 100, (i // 2) * 100, window=self.source_faces[-1]["TKButton"], anchor='nw') self.static_widget['input_faces_scrollbar'].resize_scrollbar(None) i = i + 1 else: print('Bad file', file) torch.cuda.empty_cache() def find_faces(self): try: img = torch.from_numpy(self.video_image).to('cuda') img = img.permute(2,0,1) bboxes, kpss = self.models.run_detect(img, detect_mode=self.parameters["DetectTypeTextSel"], max_num=50, score=self.parameters["DetectScoreSlider"]/100.0, use_landmark_detection=self.parameters['LandmarksDetectionAdjSwitch'], landmark_detect_mode=self.parameters["LandmarksDetectTypeTextSel"], landmark_score=self.parameters["LandmarksDetectScoreSlider"]/100.0, from_points=self.parameters["LandmarksAlignModeFromPointsSwitch"]) ret = [] for face_kps in kpss: # if kpss is not None: # face_kps = kpss[i] face_emb, cropped_img = self.models.run_recognize(img, face_kps) ret.append([face_kps, face_emb, cropped_img]) except Exception: print(" No media selected") else: # Find all faces and add to target_faces[] if ret: # Apply threshold tolerence threshhold = self.parameters["ThresholdSlider"] # if self.parameters["ThresholdState"]: # threshhold = 0.0 # Loop thgouh all faces in video frame for face in ret: found = False # Check if this face has already been found for emb in self.target_faces: if self.findCosineDistance(emb['Embedding'], face[1]) >= threshhold: found = True break # If we dont find any existing simularities, it means that this is a new face and should be added to our found faces if not found: crop = torchvision.transforms.functional.to_pil_image(face[2]) crop = crop.resize((82, 82)) new_target_face = self.target_face.copy() self.target_faces.append(new_target_face) last_index = len(self.target_faces)-1 self.target_faces[last_index]["TKButton"] = tk.Button(self.found_faces_canvas, style.media_button_off_3, height = 86, width = 86) self.target_faces[last_index]["TKButton"].bind("", self.target_faces_mouse_wheel) self.target_faces[last_index]["ButtonState"] = False self.target_faces[last_index]["Image"] = ImageTk.PhotoImage(image=crop) self.target_faces[last_index]["Embedding"] = face[1] self.target_faces[last_index]["EmbeddingNumber"] = 1 # Add image to button self.target_faces[-1]["TKButton"].config( pady = 10, image = self.target_faces[last_index]["Image"], command=lambda k=last_index: self.toggle_found_faces_buttons_state(k)) # Add button to canvas self.found_faces_canvas.create_window((last_index)*92, 8, window=self.target_faces[last_index]["TKButton"], anchor='nw') self.found_faces_canvas.configure(scrollregion = self.found_faces_canvas.bbox("all")) def clear_faces(self): self.target_faces = [] self.found_faces_canvas.delete("all") # toggle the target faces button and make assignments def toggle_found_faces_buttons_state(self, button): # Turn all Target faces off for i in range(len(self.target_faces)): self.target_faces[i]["ButtonState"] = False self.target_faces[i]["TKButton"].config(style.media_button_off_3) # Set only the selected target face to on self.target_faces[button]["ButtonState"] = True self.target_faces[button]["TKButton"].config(style.media_button_on_3) # set all source face buttons to off for i in range(len(self.source_faces)): self.source_faces[i]["ButtonState"] = False self.source_faces[i]["TKButton"].config(style.media_button_off_3) # turn back on the ones that are assigned to the curent target face for i in range(len(self.target_faces[button]["SourceFaceAssignments"])): self.source_faces[self.target_faces[button]["SourceFaceAssignments"][i]]["ButtonState"] = True self.source_faces[self.target_faces[button]["SourceFaceAssignments"][i]]["TKButton"].config(style.media_button_on_3) def select_input_faces(self, event, button): try: if event.state & 0x4 != 0: modifier = 'ctrl' elif event.state & 0x1 != 0: modifier = 'shift' else: modifier = 'none' except: modifier = event # If autoswap isnt on # Clear all the highlights. Clear all states, excpet if a modifier is being used # Start by turning off all the highlights on the input faces buttons if modifier != 'auto': for face in self.source_faces: face["TKButton"].config(style.media_button_off_3) # and also clear the states if not selecting multiples if modifier == 'none': face["ButtonState"] = False # Toggle the state of the selected Input Face if modifier != 'merge': self.source_faces[button]["ButtonState"] = not self.source_faces[button]["ButtonState"] # if shift find any other input faces and activate the state of all faces in between if modifier == 'shift': for i in range(button-1, self.shift_i_len-1, -1): if self.source_faces[i]["ButtonState"]: for j in range(i, button, 1): self.source_faces[j]["ButtonState"] = True break for i in range(button+1, len(self.source_faces), 1): if self.source_faces[i]["ButtonState"]: for j in range(button, i, 1): self.source_faces[j]["ButtonState"] = True break # Highlight all of input faces buttons that have a true state for face in self.source_faces: if face["ButtonState"]: face["TKButton"].config(style.media_button_on_3) if self.widget['PreviewModeTextSel'].get() == 'FaceLab': self.add_action("load_target_image", face["file"]) self.image_loaded = True # Assign all active input faces to the active target face for tface in self.target_faces: if tface["ButtonState"]: # Clear all of the assignments tface["SourceFaceAssignments"] = [] # Iterate through all Input faces temp_holder = [] for j in range(len(self.source_faces)): # If the source face is active if self.source_faces[j]["ButtonState"]: tface["SourceFaceAssignments"].append(j) temp_holder.append(self.source_faces[j]['Embedding']) # do averaging if temp_holder: if self.widget['MergeTextSel'].get() == 'Median': tface['AssignedEmbedding'] = np.median(temp_holder, 0) elif self.widget['MergeTextSel'].get() == 'Mean': tface['AssignedEmbedding'] = np.mean(temp_holder, 0) self.temp_emb = tface['AssignedEmbedding'] # for k in range(512): # self.widget['emb_vec_' + str(k)].set(tface['AssignedEmbedding'][k], False) break self.add_action("target_faces", self.target_faces) self.add_action('get_requested_video_frame', self.video_slider.get()) # latent = torch.from_numpy(self.models.calc_swapper_latent(self.source_faces[button]['Embedding'])).float().to('cuda') # face['ptrdata'] = self.models.run_swap_stg1(latent) def populate_target_videos(self): # Recursively read all media files from directory directory = self.json_dict["source videos"] filenames = [os.path.join(dirpath,f) for (dirpath, dirnames, filenames) in os.walk(directory) for f in filenames] videos = [] images = [] self.target_media = [] self.target_media_buttons = [] self.target_media_canvas.delete("all") for file in filenames: # Does not include full path # Guess File type based on extension try: file_type = mimetypes.guess_type(file)[0][:5] except: print('Unrecognized file type:', file) else: # Its an image if file_type == 'image': try: image = cv2.imread(file) image = cv2.cvtColor(image, cv2.COLOR_BGR2RGB) except: print('Trouble reading file:', file) else: ratio = float(image.shape[0]) / image.shape[1] new_height = 50 new_width = int(new_height / ratio) image = cv2.resize(image, (new_width, new_height)) image[:new_height, :new_width, :] = image images.append([image, file]) # Its a video elif file_type == 'video': try: video = cv2.VideoCapture(file) except: print('Trouble reading file:', file) else: if video.isOpened(): # Grab a frame from the middle for a thumbnail video.set(cv2.CAP_PROP_POS_FRAMES, int(video.get(cv2.CAP_PROP_FRAME_COUNT)/2)) success, video_frame = video.read() if success: video_frame = cv2.cvtColor(video_frame, cv2.COLOR_BGR2RGB) ratio = float(video_frame.shape[0]) / video_frame.shape[1] new_height = 50 new_width = int(new_height / ratio) video_frame = cv2.resize(video_frame, (new_width, new_height)) video_frame[:new_height, :new_width, :] = video_frame videos.append([video_frame, file]) video.release() else: print('Trouble reading file:', file) else: print('Trouble opening file:', file) delx, dely = 100, 79 if self.widget['PreviewModeTextSel'].get()== 'Image':#images for i in range(len(images)): self.target_media_buttons.append(tk.Button(self.target_media_canvas, style.media_button_off_3, height = 86, width = 86)) rgb_video = Image.fromarray(images[i][0]) self.target_media.append(ImageTk.PhotoImage(image=rgb_video)) self.target_media_buttons[i].config( image = self.target_media[i], command=lambda i=i: self.load_target(i, images[i][1], self.widget['PreviewModeTextSel'].get())) self.target_media_buttons[i].bind("", self.target_videos_mouse_wheel) self.target_media_canvas.create_window((i%2)*delx, (i//2)*dely, window = self.target_media_buttons[i], anchor='nw') self.target_media_canvas.configure(scrollregion = self.target_media_canvas.bbox("all")) elif self.widget['PreviewModeTextSel'].get()=='Video':#videos for i in range(len(videos)): self.target_media_buttons.append(tk.Button(self.target_media_canvas, style.media_button_off_3, height = 65, width = 90)) self.target_media.append(ImageTk.PhotoImage(image=Image.fromarray(videos[i][0]))) filename = os.path.basename(videos[i][1]) if len(filename)>14: filename = filename[:11]+'...' self.target_media_buttons[i].bind("", self.target_videos_mouse_wheel) self.target_media_buttons[i].config(image = self.target_media[i], text=filename, compound='top', anchor='n',command=lambda i=i: self.load_target(i, videos[i][1], self.widget['PreviewModeTextSel'].get())) self.target_media_canvas.create_window((i%2)*delx, (i//2)*dely, window = self.target_media_buttons[i], anchor='nw') self.static_widget['input_videos_scrollbar'].resize_scrollbar(None) def auto_swap(self): # Reselect Target Image # try: self.find_faces() self.target_faces[0]["ButtonState"] = True self.target_faces[0]["TKButton"].config(style.media_button_on_3) # Reselect Source images self.select_input_faces('auto', '') self.toggle_swapper(True) # except: # pass def toggle_auto_swap(self): self.widget['AutoSwapButton'].toggle_button() def load_target(self, button, media_file, media_type): # Make sure the video stops playing self.toggle_play_video('stop') self.image_loaded = False self.video_loaded = False self.clear_faces() if media_type == 'Video': self.video_slider.set(0) self.add_action("load_target_video", media_file) self.media_file_name = os.path.splitext(os.path.basename(media_file)) self.video_loaded = True elif media_type == 'Image': self.add_action("load_target_image", media_file) self.media_file_name = os.path.splitext(os.path.basename(media_file)) self.image_loaded = True # # find faces if self.widget['AutoSwapButton'].get(): self.add_action('function', "gui.auto_swap()") for i in range(len(self.target_media_buttons)): self.target_media_buttons[i].config(style.media_button_off_3) self.target_media_buttons[button].config(style.media_button_on_3) # delete all markers self.layer['markers_canvas'].delete('all') self.markers = [] self.stop_marker = [] #region [#111111b4] self.load_markers_json() self.add_action("update_markers_canvas", self.markers) #endregion self.add_action("markers", self.markers) # @profile def set_image(self, image, requested): self.video_image = image[0] frame = image[1] if not requested: self.video_slider.set(frame) self.parameter_update_from_marker(frame) self.resize_image() # @profile def resize_image(self): image = self.video_image if len(image) != 0: x1 = float(self.video.winfo_width()) y1 = float(self.video.winfo_height()) x2 = float(image.shape[1]) y2 = float(image.shape[0]) m1 = x1/y1 m2 = x2/y2 if m2>m1: x2 = x1 y2 = x1/m2 image = cv2.resize(image, (int(x2), int(y2))) padding = int((y1-y2)/2.0) image = cv2.copyMakeBorder( image, padding, padding, 0, 0, cv2.BORDER_CONSTANT) else: y2=y1 x2=y2*m2 image = cv2.resize(image, (int(x2), int(y2))) padding=int((x1-x2)/2.0) image = cv2.copyMakeBorder( image, 0, 0, padding, padding, cv2.BORDER_CONSTANT) image = Image.fromarray(image) image = ImageTk.PhotoImage(image) self.video.image = image self.video.configure(image=self.video.image) def check_for_video_resize(self): # Read the geometry from the last time json was updated. json only updates once the window ahs stopped changing win_geom = '%dx%d+%d+%d' % (self.json_dict['dock_win_geom'][0], self.json_dict['dock_win_geom'][1] , self.json_dict['dock_win_geom'][2], self.json_dict['dock_win_geom'][3]) # # window has started changing if self.winfo_geometry() != win_geom: # Resize image in video window self.resize_image() for k, v in self.widget.items(): v.hide() for k, v in self.static_widget.items(): v.hide() # Check if window has stopped changing if self.winfo_geometry() != self.window_last_change: self.window_last_change = self.winfo_geometry() # The window has stopped changing else: for k, v in self.widget.items(): v.unhide() for k, v in self.static_widget.items(): v.unhide() # Update json str1 = self.winfo_geometry().split('x') str2 = str1[1].split('+') win_geom = [str1[0], str2[0], str2[1], str2[2]] win_geom = [int(strings) for strings in win_geom] self.json_dict['dock_win_geom'] = win_geom with open("data.json", "w") as outfile: json.dump(self.json_dict, outfile) def get_action(self): action = self.action_q[0] self.action_q.pop(0) return action def get_action_length(self): return len(self.action_q) def set_video_slider_length(self, video_length): self.video_slider.set_length(video_length) def findCosineDistance(self, vector1, vector2): vector1 = vector1.ravel() vector2 = vector2.ravel() cos_dist = 1 - np.dot(vector1, vector2)/(np.linalg.norm(vector1)*np.linalg.norm(vector2)) # 2..0 return 100-cos_dist*50 ''' vector1 = vector1.ravel() vector2 = vector2.ravel() return 1 - np.dot(vector1, vector2)/(np.linalg.norm(vector1)*np.linalg.norm(vector2)) ''' def toggle_play_video(self, set_value='toggle'): if self.video_loaded: # Update button if set_value == 'toggle': self.widget['TLPlayButton'].toggle_button() if set_value == 'stop': self.widget['TLPlayButton'].disable_button() if set_value == 'play': self.widget['TLPlayButton'].enable_button() # If play if self.widget['TLPlayButton'].get(): if not self.video_loaded: print("Please select video first!") return else: # and record if self.widget['TLRecButton'].get(): if not self.json_dict["saved videos"]: print("Set saved video folder first!") self.add_action("play_video", "stop_from_gui") else: self.add_action("play_video", "record") # only play else: self.add_action("play_video", "play") else: self.add_action("play_video", "stop_from_gui") def set_player_buttons_to_inactive(self): self.widget['TLRecButton'].disable_button() self.widget['TLPlayButton'].disable_button() def toggle_swapper(self, toggle_value=-1): # print(inspect.currentframe().f_back.f_code.co_name, 'toggle_swapper: '+'toggle_value='+str(toggle_value)) if toggle_value == -1: self.widget['SwapFacesButton'].toggle_button() else: if toggle_value: self.widget['SwapFacesButton'].enable_button() else: self.widget['SwapFacesButton'].disable_button() if self.widget['PreviewModeTextSel'].get()=='Video' or self.widget['PreviewModeTextSel'].get()=='Theater': self.update_data('control', 'SwapFacesButton', use_markers=True) elif self.widget['PreviewModeTextSel'].get()=='Image': self.update_data('control', 'SwapFacesButton', use_markers=False) elif self.widget['PreviewModeTextSel'].get() == 'FaceLab': self.update_data('control', 'SwapFacesButton', use_markers=False) def temp_toggle_swapper(self, state): if state=='off': self.widget['SwapFacesButton'].temp_disable_button() elif state=='on': self.widget['SwapFacesButton'].temp_enable_button() self.update_data('control', 'SwapFacesButton', use_markers=True) def toggle_rec_video(self): # Play button must be off to enable record button #region [#111111b4] self.save_markers_json() #endregion if not self.widget['TLPlayButton'].get(): self.widget['TLRecButton'].toggle_button() if self.widget['TLRecButton'].get(): self.widget['TLRecButton'].enable_button() else: self.widget['TLRecButton'].disable_button() # this makes no sense def add_action(self, action, parameter=None): # # print(inspect.currentframe().f_back.f_code.co_name, '->add_action: '+action) if action != 'get_requested_video_frame' and action != 'get_requested_video_frame_without_markers': self.action_q.append([action, parameter]) # Only do requests when the video is not playing - (moving the timeline or changing parameters) elif self.video_loaded and not self.widget['TLPlayButton'].get(): self.action_q.append([action, parameter]) elif self.image_loaded: self.action_q.append([action, parameter]) def update_vram_indicator(self): try: used, total = self.models.get_gpu_memory() except: pass else: self.static_widget['vram_indicator'].set(used, total) # refactor and thread i/o def save_selected_source_faces(self, text): # get name from text field text = text.get() # get embeddings from all highlightebuttons # iterate through the buttons temp_holder = [] for button in self.source_faces: if button["ButtonState"]: temp_holder.append(button['Embedding']) if temp_holder: if self.widget['MergeTextSel'].get()=='Median': ave_embedding = np.median(temp_holder,0) elif self.widget['MergeTextSel'].get()=='Mean': ave_embedding = np.mean(temp_holder,0) for tface in self.target_faces: if tface["ButtonState"]: ave_embedding = tface['AssignedEmbedding'] if text != "": with open("merged_embeddings.txt", "a") as embedfile: identifier = "Name: "+text embedfile.write("%s\n" % identifier) for number in ave_embedding: embedfile.write("%s\n" % number) else: print('No embedding name specified') else: print('No Source Images selected') self.focus() self.load_input_faces() # refactor and thread i/o def delete_merged_embedding(self): #add multi select # get selected button sel = [] for j in range(len(self.source_faces)): if self.source_faces[j]["ButtonState"]: sel = j break # check if it is a merged embedding # if so, read txt embedding into list temp0 = [] if os.path.exists("merged_embeddings.txt"): with open("merged_embeddings.txt", "r") as embedfile: temp = embedfile.read().splitlines() for i in range(0, len(temp), 513): to = [temp[i], np.array(temp[i+1:i+513], dtype='float32')] temp0.append(to) if j < len(temp0): temp0.pop(j) with open("merged_embeddings.txt", "w") as embedfile: for line in temp0: embedfile.write("%s\n" % line[0]) for i in range(512): embedfile.write("%s\n" % line[1][i]) self.load_input_faces() def iterate_through_merged_embeddings(self, event): if event.delta>0: for i in range(len(self.source_faces)): if self.source_faces[i]["ButtonState"] and i0: self.select_input_faces('none', i-1) break def set_view(self, load_target_videos,b): # self.clear_faces() # self.video_loaded = False # self.image_loaded = False if load_target_videos and self.widget['PreviewModeTextSel'].get() != 'Theater': self.populate_target_videos() self.layer['slider_frame'].grid_forget() self.layer['preview_frame'].grid_forget() self.layer['markers_canvas'].grid_forget() self.layer['image_controls'].grid_forget() self.layer['FaceLab_controls'].grid_forget() self.layer['InputVideoFrame'].grid_forget() self.layer['parameter_frame'].grid_forget() self.layer['parameters_canvas'].grid_forget() self.layer['parameter_scroll_canvas'].grid_forget() self.layer['facelab_canvas'].grid_forget() self.layer['facelab_scroll_canvas'].grid_forget() if self.widget['PreviewModeTextSel'].get()=='Video': self.image_loaded = False self.layer['slider_frame'].grid(row=2, column=0, sticky='NEWS', pady=0) self.layer['preview_frame'].grid(row=4, column=0, sticky='NEWS') self.layer['markers_canvas'].grid(row=3, column=0, sticky='NEWS') self.layer['parameter_frame'].grid(row=0, column=2, sticky='NEWS', pady=0, padx=1) self.layer['parameters_canvas'].grid(row=1, column=0, sticky='NEWS', pady=0, padx=0) self.layer['parameter_scroll_canvas'].grid(row=1, column=1, sticky='NEWS', pady=0) self.layer['InputVideoFrame'].grid(row=0, column=0, sticky='NEWS', padx=1, pady=0) elif self.widget['PreviewModeTextSel'].get()=='Image': self.video_loaded = False self.layer['image_controls'].grid(row=2, column=0, rowspan=2, sticky='NEWS', pady=0) self.layer['parameters_canvas'].grid(row=1, column=0, sticky='NEWS', pady=0, padx=0) self.layer['parameter_scroll_canvas'].grid(row=1, column=1, sticky='NEWS', pady=0) self.layer['InputVideoFrame'].grid(row=0, column=0, sticky='NEWS', padx=1, pady=0) self.layer['parameter_frame'].grid(row=0, column=2, sticky='NEWS', pady=0, padx=1) elif self.widget['PreviewModeTextSel'].get() == 'FaceLab': self.video_loaded = False self.layer['FaceLab_controls'].grid(row=2, column=0, rowspan=2, sticky='NEWS', pady=0) self.layer['facelab_canvas'].grid(row=1, column=0, sticky='NEWS', pady=0, padx=0) self.layer['facelab_scroll_canvas'].grid(row=1, column=1, sticky='NEWS', pady=0) self.layer['InputVideoFrame'].grid(row=0, column=0, sticky='NEWS', padx=1, pady=0) self.layer['parameter_frame'].grid(row=0, column=2, sticky='NEWS', pady=0, padx=1) # # find the input image with the lowest value # for face in self.source_faces: # if face["ButtonState"]: # self.image_loaded = True # self.add_action("load_target_image", face["file"]) # break elif self.widget['PreviewModeTextSel'].get() == 'Theater': self.image_loaded = False self.layer['slider_frame'].grid(row=2, column=0, sticky='NEWS', pady=0) self.layer['preview_frame'].grid(row=4, column=0, sticky='NEWS') self.layer['markers_canvas'].grid(row=3, column=0, sticky='NEWS') def update_marker(self, action): if action=='add': # Delete existing marker at current frame and replace with new data for i in range(len(self.markers)): if self.markers[i]['frame'] == self.video_slider.get(): self.layer['markers_canvas'].delete(self.markers[i]['icon_ref']) self.markers.pop(i) break width = self.layer['markers_canvas'].winfo_width()-20-40-20 position = 20+int(width*self.video_slider.get()/self.video_slider.get_length()) temp_param = copy.deepcopy(self.parameters) temp = { 'frame': self.video_slider.get(), 'parameters': temp_param, 'icon_ref': self.layer['markers_canvas'].create_line(position,0, position, 15, fill='light goldenrod'), } self.markers.append(temp) def sort(e): return e['frame'] self.markers.sort(key=sort) self.add_action("markers", self.markers) # elif action=='stop': # if self.stop_marker == self.video_slider.get(): # self.stop_marker = [] # self.add_action('set_stop', -1) # self.video_slider_canvas.delete(self.stop_image) # else: # self.video_slider_canvas.delete(self.stop_image) # self.stop_marker = self.video_slider.self.timeline_position # self.add_action('set_stop', self.stop_marker) # # width = self.video_slider_canvas.winfo_width() - 30 # position = 15 + int(width * self.video_slider.self.timeline_position / self.video_slider.configure('to')[4]) # self.stop_image = self.video_slider_canvas.create_image(position, 30, image=self.stop_marker_icon) elif action=='delete': for i in range(len(self.markers)): if self.markers[i]['frame'] == self.video_slider.get(): self.layer['markers_canvas'].delete(self.markers[i]['icon_ref']) self.markers.pop(i) break elif action=='prev': temp=[] for i in range(len(self.markers)): temp.append(self.markers[i]['frame']) idx = bisect.bisect_left(temp, self.video_slider.get()) if idx > 0: self.video_slider.set(self.markers[idx-1]['frame']) self.add_action('get_requested_video_frame', self.markers[idx-1]['frame']) self.parameter_update_from_marker(self.markers[idx-1]['frame']) elif action=='next': temp=[] for i in range(len(self.markers)): temp.append(self.markers[i]['frame']) idx = bisect.bisect(temp, self.video_slider.get()) if idx < len(self.markers): self.video_slider.set(self.markers[idx]['frame']) self.add_action('get_requested_video_frame', self.markers[idx]['frame']) self.parameter_update_from_marker(self.markers[idx]['frame']) # resize canvas else : self.layer['markers_canvas'].delete('all') width = self.layer['markers_canvas'].winfo_width()-20-40-20 for marker in self.markers: position = 20+int(width*marker['frame']/self.video_slider.get_length()) marker['icon_ref'] = self.layer['markers_canvas'].create_line(position,0, position, 15, fill='light goldenrod') #region [#111111b4] def save_markers_json(self): if len(self.markers) == 0 or len(self.media_file_name) == 0: return json_file_path = os.path.join(self.json_dict["source videos"], self.media_file_name[0] + "_markers.json") # Save the markers to the JSON file with open(json_file_path, 'w') as json_file: json.dump(self.markers, json_file) print('Markers saved') def load_markers_json(self): if len(self.media_file_name) == 0: return json_file_path = os.path.join(self.json_dict["source videos"], self.media_file_name[0] + "_markers.json") if os.path.exists(json_file_path): # Load the markers from the JSON file with open(json_file_path, 'r') as json_file: self.markers = json.load(json_file) self.add_action("update_markers_canvas", self.markers) def update_markers_canvas(self): self.layer['markers_canvas'].delete('all') width = self.layer['markers_canvas'].winfo_width()-20-40-20 for marker in self.markers: position = 20+int(width*marker['frame']/self.video_slider.get_length()) marker['icon_ref'] = self.layer['markers_canvas'].create_line(position,0, position, 15, fill='light goldenrod') #endregion def toggle_stop(self): if self.stop_marker == self.video_slider.self.timeline_position: self.stop_marker = [] self.add_action('set_stop', -1) self.video_slider_canvas.delete(self.stop_image) else: self.video_slider_canvas.delete(self.stop_image) self.stop_marker = self.video_slider.self.timeline_position self.add_action('set_stop', self.stop_marker) width = self.video_slider_canvas.winfo_width()-30 position = 15+int(width*self.video_slider.self.timeline_position/self.video_slider.configure('to')[4]) self.stop_image = self.video_slider_canvas.create_image(position, 30, image=self.stop_marker_icon) def save_image(self): filename = self.media_file_name[0]+"_"+str(time.time())[:10] filename = os.path.join(self.json_dict["saved videos"], filename) cv2.imwrite(filename+'.png', cv2.cvtColor(self.video_image, cv2.COLOR_BGR2RGB)) print('Image saved as:', filename+'.png') def clear_mem(self): self.widget['RestorerSwitch'].set(False) self.widget['OccluderSwitch'].set(False) self.widget['FaceParserSwitch'].set(False) self.widget['CLIPSwitch'].set(False) self.widget['SwapFacesButton'].set(False) self.models.delete_models() torch.cuda.empty_cache() # Refactor this, doesn't seem very efficient def parameter_update_from_marker(self, frame): # sync marker data temp=[] # create a separate list with the list of frame numbers with markers for i in range(len(self.markers)): temp.append(self.markers[i]['frame']) # find the marker frame to the left of the current frame idx = bisect.bisect(temp, frame) # update UI with current marker state data if idx>0: # update paramter dict with marker entry self.parameters = copy.deepcopy(self.markers[idx-1]['parameters']) # Update ui for key, value in self.parameters.items(): self.widget[key].set(self.parameters[key], request_frame=False) # self.CLIP_text.delete(0, tk.END) # self.CLIP_text.insert(0, self.parameters['CLIPText']) def toggle_audio(self): self.widget['AudioButton'].toggle_button() self.control['AudioButton'] = self.widget['AudioButton'].get() self.add_action('control', self.control) def toggle_maskview(self): self.widget['MaskViewButton'].toggle_button() self.control['MaskViewButton'] = self.widget['MaskViewButton'].get() self.add_action('control', self.control) self.add_action('get_requested_video_frame', self.video_slider.get()) def parameter_io(self, task): if task=='save': with open("saved_parameters.json", "w") as save_file: json.dump(self.parameters, save_file) elif task=='load': try: load_file = open("saved_parameters.json", "r") except FileNotFoundError: print('No save file created yet!') else: # Load the file and save it to parameters self.parameters = json.load(load_file) load_file.close() # Update the UI for key, value in self.parameters.items(): self.widget[key].set(value, request_frame=False) self.add_action('parameters', self.parameters) self.add_action('control', self.control) self.add_action('get_requested_video_frame', self.video_slider.get()) elif task=='default': # Update the UI for key, value in self.parameters.items(): self.widget[key].load_default() self.add_action('parameters', self.parameters) self.add_action('control', self.control) self.add_action('get_requested_video_frame', self.video_slider.get()) def findCosineDistance2(self, vector1, vector2): cos_dist = 1.0 - np.dot(vector1, vector2)/(np.linalg.norm(vector1)*np.linalg.norm(vector2)) # 2..0 print(np.dot(vector1, vector2)) return cos_dist