fixed_rope / rope /GUI.py
MonsterMMORPG's picture
Upload folder using huggingface_hub (#1)
17eddbd verified
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('<Key>', self.handle_key_press)
self.bind("<Return>", 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("<Return>", 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("<MouseWheel>", 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("<MouseWheel>", 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("<MouseWheel>", self.iterate_through_merged_embeddings)
self.video.bind("<ButtonRelease-1>", 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('<Configure>', 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("<MouseWheel>", 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("<Return>", 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("<MouseWheel>", 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("<Button-1>", 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("<Button-1>", 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("<Button-1>", 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('<Key>', lambda event: self.preview_control(event))
# self.bind('<space>', 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("<ButtonRelease-1>", lambda event, arg=j: self.select_input_faces(event, arg))
self.source_faces[j]["TKButton"].bind("<MouseWheel>", 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("<ButtonRelease-1>", lambda event, arg=len(self.source_faces)-1: self.select_input_faces(event, arg))
self.source_faces[-1]["TKButton"].bind("<MouseWheel>", 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("<MouseWheel>", 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("<MouseWheel>", 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("<MouseWheel>", 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 i<len(self.source_faces)-1:
self.select_input_faces('none', i+1)
break
elif event.delta<0:
for i in range(len(self.source_faces)):
if self.source_faces[i]["ButtonState"]and i>0:
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