mirror of
https://github.com/s0md3v/roop.git
synced 2025-12-06 18:08:29 +00:00
Migrate to theme based UI using customtkinter
This commit is contained in:
parent
7ccc36dbf4
commit
315c980be8
@ -6,6 +6,7 @@ onnx==1.14.0
|
||||
insightface==0.7.3
|
||||
psutil==5.9.5
|
||||
tk==0.1.0
|
||||
customtkinter==5.1.3
|
||||
pillow==9.5.0
|
||||
torch==2.0.1+cu118; sys_platform != 'darwin'
|
||||
torch==2.0.1; sys_platform == 'darwin'
|
||||
|
||||
@ -7,6 +7,7 @@ FACE_ANALYSER = None
|
||||
|
||||
def get_face_analyser() -> Any:
|
||||
global FACE_ANALYSER
|
||||
|
||||
if FACE_ANALYSER is None:
|
||||
FACE_ANALYSER = insightface.app.FaceAnalysis(name='buffalo_l', providers=roop.globals.providers)
|
||||
FACE_ANALYSER.prepare(ctx_id=0, det_size=(640, 640))
|
||||
|
||||
@ -48,6 +48,7 @@ else:
|
||||
|
||||
def get_code_former():
|
||||
global CODE_FORMER
|
||||
|
||||
with THREAD_LOCK:
|
||||
if CODE_FORMER is None:
|
||||
CODE_FORMER = ARCH_REGISTRY.get("CodeFormer")(
|
||||
|
||||
@ -7,19 +7,20 @@ import insightface
|
||||
import threading
|
||||
import roop.globals
|
||||
from roop.analyser import get_one_face, get_many_faces
|
||||
from roop.utilities import conditional_download
|
||||
from roop.utilities import conditional_download, resolve_relative_path
|
||||
|
||||
FACE_SWAPPER = None
|
||||
THREAD_LOCK = threading.Lock()
|
||||
|
||||
|
||||
def pre_check() -> None:
|
||||
download_directory_path = os.path.join(os.path.abspath(os.path.dirname(__file__)), '../models')
|
||||
download_directory_path = resolve_relative_path('../models')
|
||||
conditional_download(download_directory_path, ['https://huggingface.co/deepinsight/inswapper/resolve/main/inswapper_128.onnx'])
|
||||
|
||||
|
||||
def get_face_swapper() -> None:
|
||||
global FACE_SWAPPER
|
||||
|
||||
with THREAD_LOCK:
|
||||
if FACE_SWAPPER is None:
|
||||
model_path = os.path.join(os.path.abspath(os.path.dirname(__file__)), '../models/inswapper_128.onnx')
|
||||
|
||||
155
roop/ui.json
Normal file
155
roop/ui.json
Normal file
@ -0,0 +1,155 @@
|
||||
{
|
||||
"CTk": {
|
||||
"fg_color": ["gray95", "gray10"]
|
||||
},
|
||||
"CTkToplevel": {
|
||||
"fg_color": ["gray95", "gray10"]
|
||||
},
|
||||
"CTkFrame": {
|
||||
"corner_radius": 6,
|
||||
"border_width": 0,
|
||||
"fg_color": ["gray90", "gray13"],
|
||||
"top_fg_color": ["gray85", "gray16"],
|
||||
"border_color": ["gray65", "gray28"]
|
||||
},
|
||||
"CTkButton": {
|
||||
"corner_radius": 6,
|
||||
"border_width": 0,
|
||||
"fg_color": ["#3a7ebf", "#1f538d"],
|
||||
"hover_color": ["#325882", "#14375e"],
|
||||
"border_color": ["#3E454A", "#949A9F"],
|
||||
"text_color": ["#DCE4EE", "#DCE4EE"],
|
||||
"text_color_disabled": ["gray74", "gray60"]
|
||||
},
|
||||
"CTkLabel": {
|
||||
"corner_radius": 0,
|
||||
"fg_color": "transparent",
|
||||
"text_color": ["gray14", "gray84"]
|
||||
},
|
||||
"CTkEntry": {
|
||||
"corner_radius": 6,
|
||||
"border_width": 2,
|
||||
"fg_color": ["#F9F9FA", "#343638"],
|
||||
"border_color": ["#979DA2", "#565B5E"],
|
||||
"text_color": ["gray14", "gray84"],
|
||||
"placeholder_text_color": ["gray52", "gray62"]
|
||||
},
|
||||
"CTkCheckbox": {
|
||||
"corner_radius": 6,
|
||||
"border_width": 3,
|
||||
"fg_color": ["#3a7ebf", "#1f538d"],
|
||||
"border_color": ["#3E454A", "#949A9F"],
|
||||
"hover_color": ["#325882", "#14375e"],
|
||||
"checkmark_color": ["#DCE4EE", "gray90"],
|
||||
"text_color": ["gray14", "gray84"],
|
||||
"text_color_disabled": ["gray60", "gray45"]
|
||||
},
|
||||
"CTkSwitch": {
|
||||
"corner_radius": 1000,
|
||||
"border_width": 3,
|
||||
"button_length": 0,
|
||||
"fg_color": ["#939BA2", "#4A4D50"],
|
||||
"progress_color": ["#3a7ebf", "#1f538d"],
|
||||
"button_color": ["gray36", "#D5D9DE"],
|
||||
"button_hover_color": ["gray20", "gray100"],
|
||||
"text_color": ["gray14", "gray84"],
|
||||
"text_color_disabled": ["gray60", "gray45"]
|
||||
},
|
||||
"CTkRadiobutton": {
|
||||
"corner_radius": 1000,
|
||||
"border_width_checked": 6,
|
||||
"border_width_unchecked": 3,
|
||||
"fg_color": ["#3a7ebf", "#1f538d"],
|
||||
"border_color": ["#3E454A", "#949A9F"],
|
||||
"hover_color": ["#325882", "#14375e"],
|
||||
"text_color": ["gray14", "gray84"],
|
||||
"text_color_disabled": ["gray60", "gray45"]
|
||||
},
|
||||
"CTkProgressBar": {
|
||||
"corner_radius": 1000,
|
||||
"border_width": 0,
|
||||
"fg_color": ["#939BA2", "#4A4D50"],
|
||||
"progress_color": ["#3a7ebf", "#1f538d"],
|
||||
"border_color": ["gray", "gray"]
|
||||
},
|
||||
"CTkSlider": {
|
||||
"corner_radius": 1000,
|
||||
"button_corner_radius": 1000,
|
||||
"border_width": 6,
|
||||
"button_length": 0,
|
||||
"fg_color": ["#939BA2", "#4A4D50"],
|
||||
"progress_color": ["gray40", "#AAB0B5"],
|
||||
"button_color": ["#3a7ebf", "#1f538d"],
|
||||
"button_hover_color": ["#325882", "#14375e"]
|
||||
},
|
||||
"CTkOptionMenu": {
|
||||
"corner_radius": 6,
|
||||
"fg_color": ["#3a7ebf", "#1f538d"],
|
||||
"button_color": ["#325882", "#14375e"],
|
||||
"button_hover_color": ["#234567", "#1e2c40"],
|
||||
"text_color": ["#DCE4EE", "#DCE4EE"],
|
||||
"text_color_disabled": ["gray74", "gray60"]
|
||||
},
|
||||
"CTkComboBox": {
|
||||
"corner_radius": 6,
|
||||
"border_width": 2,
|
||||
"fg_color": ["#F9F9FA", "#343638"],
|
||||
"border_color": ["#979DA2", "#565B5E"],
|
||||
"button_color": ["#979DA2", "#565B5E"],
|
||||
"button_hover_color": ["#6E7174", "#7A848D"],
|
||||
"text_color": ["gray14", "gray84"],
|
||||
"text_color_disabled": ["gray50", "gray45"]
|
||||
},
|
||||
"CTkScrollbar": {
|
||||
"corner_radius": 1000,
|
||||
"border_spacing": 4,
|
||||
"fg_color": "transparent",
|
||||
"button_color": ["gray55", "gray41"],
|
||||
"button_hover_color": ["gray40", "gray53"]
|
||||
},
|
||||
"CTkSegmentedButton": {
|
||||
"corner_radius": 6,
|
||||
"border_width": 2,
|
||||
"fg_color": ["#979DA2", "gray29"],
|
||||
"selected_color": ["#3a7ebf", "#1f538d"],
|
||||
"selected_hover_color": ["#325882", "#14375e"],
|
||||
"unselected_color": ["#979DA2", "gray29"],
|
||||
"unselected_hover_color": ["gray70", "gray41"],
|
||||
"text_color": ["#DCE4EE", "#DCE4EE"],
|
||||
"text_color_disabled": ["gray74", "gray60"]
|
||||
},
|
||||
"CTkTextbox": {
|
||||
"corner_radius": 6,
|
||||
"border_width": 0,
|
||||
"fg_color": ["gray100", "gray20"],
|
||||
"border_color": ["#979DA2", "#565B5E"],
|
||||
"text_color": ["gray14", "gray84"],
|
||||
"scrollbar_button_color": ["gray55", "gray41"],
|
||||
"scrollbar_button_hover_color": ["gray40", "gray53"]
|
||||
},
|
||||
"CTkScrollableFrame": {
|
||||
"label_fg_color": ["gray80", "gray21"]
|
||||
},
|
||||
"DropdownMenu": {
|
||||
"fg_color": ["gray90", "gray20"],
|
||||
"hover_color": ["gray75", "gray28"],
|
||||
"text_color": ["gray14", "gray84"]
|
||||
},
|
||||
"CTkFont": {
|
||||
"macOS": {
|
||||
"family": "Avenir",
|
||||
"size": 12,
|
||||
"weight": "normal"
|
||||
},
|
||||
"Windows": {
|
||||
"family": "Corbel",
|
||||
"size": 12,
|
||||
"weight": "normal"
|
||||
},
|
||||
"Linux": {
|
||||
"family": "Montserrat",
|
||||
"size": 12,
|
||||
"weight": "normal"
|
||||
}
|
||||
}
|
||||
}
|
||||
132
roop/ui.py
132
roop/ui.py
@ -1,7 +1,6 @@
|
||||
import os
|
||||
import tkinter as tk
|
||||
from tkinter import filedialog
|
||||
from typing import Callable, Any, Tuple
|
||||
import customtkinter as ctk
|
||||
from typing import Callable, Tuple
|
||||
|
||||
import cv2
|
||||
from PIL import Image, ImageTk, ImageOps
|
||||
@ -10,12 +9,8 @@ import roop.globals
|
||||
from roop.analyser import get_one_face
|
||||
from roop.capturer import get_video_frame
|
||||
from roop.swapper import process_faces
|
||||
from roop.utilities import is_image, is_video
|
||||
from roop.utilities import is_image, is_video, resolve_relative_path
|
||||
|
||||
PRIMARY_COLOR = '#2d3436'
|
||||
SECONDARY_COLOR = '#74b9ff'
|
||||
TERTIARY_COLOR = '#f1c40f'
|
||||
ACCENT_COLOR = '#2ecc71'
|
||||
WINDOW_HEIGHT = 700
|
||||
WINDOW_WIDTH = 600
|
||||
PREVIEW_MAX_HEIGHT = 700
|
||||
@ -25,7 +20,7 @@ RECENT_DIRECTORY_TARGET = None
|
||||
RECENT_DIRECTORY_OUTPUT = None
|
||||
|
||||
|
||||
def init(start: Callable, destroy: Callable) -> tk.Tk:
|
||||
def init(start: Callable, destroy: Callable) -> ctk.CTk:
|
||||
global ROOT, PREVIEW
|
||||
|
||||
ROOT = create_root(start, destroy)
|
||||
@ -34,131 +29,88 @@ def init(start: Callable, destroy: Callable) -> tk.Tk:
|
||||
return ROOT
|
||||
|
||||
|
||||
def create_root(start: Callable, destroy: Callable) -> tk.Tk:
|
||||
def create_root(start: Callable, destroy: Callable) -> ctk.CTk:
|
||||
global source_label, target_label, status_label
|
||||
|
||||
root = tk.Tk()
|
||||
ctk.set_appearance_mode('system')
|
||||
ctk.set_default_color_theme(resolve_relative_path('ui.json'))
|
||||
root = ctk.CTk()
|
||||
root.minsize(WINDOW_WIDTH, WINDOW_HEIGHT)
|
||||
root.title('roop')
|
||||
root.configure(bg=PRIMARY_COLOR)
|
||||
root.option_add('*Font', ('Arial', 11))
|
||||
root.configure()
|
||||
root.protocol('WM_DELETE_WINDOW', lambda: destroy())
|
||||
|
||||
source_label = tk.Label(root, bg=PRIMARY_COLOR)
|
||||
source_label = ctk.CTkLabel(root, text=None)
|
||||
source_label.place(relx=0.1, rely=0.1, relwidth=0.3, relheight=0.25)
|
||||
|
||||
target_label = tk.Label(root, bg=PRIMARY_COLOR)
|
||||
target_label = ctk.CTkLabel(root, text=None)
|
||||
target_label.place(relx=0.6, rely=0.1, relwidth=0.3, relheight=0.25)
|
||||
|
||||
source_button = create_primary_button(root, 'Select a face', lambda: select_source_path())
|
||||
source_button = ctk.CTkButton(root, text='Select a face', command=lambda: select_source_path())
|
||||
source_button.place(relx=0.1, rely=0.4, relwidth=0.3, relheight=0.1)
|
||||
|
||||
target_button = create_primary_button(root, 'Select a target', lambda: select_target_path())
|
||||
target_button = ctk.CTkButton(root, text='Select a target', command=lambda: select_target_path())
|
||||
target_button.place(relx=0.6, rely=0.4, relwidth=0.3, relheight=0.1)
|
||||
|
||||
keep_fps_value = tk.BooleanVar(value=roop.globals.keep_fps)
|
||||
keep_fps_checkbox = create_checkbox(root, 'Limit to 30 fps', keep_fps_value, lambda: setattr(roop.globals, 'keep_fps', not roop.globals.keep_fps))
|
||||
keep_fps_value = ctk.BooleanVar(value=roop.globals.keep_fps)
|
||||
keep_fps_checkbox = ctk.CTkSwitch(root, text='Keep fps', variable=keep_fps_value, command=lambda: setattr(roop.globals, 'keep_fps', not roop.globals.keep_fps))
|
||||
keep_fps_checkbox.place(relx=0.1, rely=0.6)
|
||||
|
||||
keep_frames_value = tk.BooleanVar(value=roop.globals.keep_frames)
|
||||
keep_frames_checkbox = create_checkbox(root, 'Keep frames dir', keep_frames_value, lambda: setattr(roop.globals, 'keep_frames', keep_frames_value.get()))
|
||||
keep_frames_checkbox.place(relx=0.1, rely=0.65)
|
||||
keep_frames_value = ctk.BooleanVar(value=roop.globals.keep_frames)
|
||||
keep_frames_switch = ctk.CTkSwitch(root, text='Keep frames', variable=keep_frames_value, command=lambda: setattr(roop.globals, 'keep_frames', keep_frames_value.get()))
|
||||
keep_frames_switch.place(relx=0.1, rely=0.65)
|
||||
|
||||
keep_audio_value = tk.BooleanVar(value=roop.globals.keep_audio)
|
||||
keep_audio_checkbox = create_checkbox(root, 'Keep original audio', keep_audio_value, lambda: setattr(roop.globals, 'keep_audio', keep_audio_value.get()))
|
||||
keep_audio_checkbox.place(relx=0.6, rely=0.6)
|
||||
keep_audio_value = ctk.BooleanVar(value=roop.globals.keep_audio)
|
||||
keep_audio_switch = ctk.CTkSwitch(root, text='Keep audio', variable=keep_audio_value, command=lambda: setattr(roop.globals, 'keep_audio', keep_audio_value.get()))
|
||||
keep_audio_switch.place(relx=0.6, rely=0.6)
|
||||
|
||||
many_faces_value = tk.BooleanVar(value=roop.globals.many_faces)
|
||||
many_faces_checkbox = create_checkbox(root, 'Replace all faces', many_faces_value, lambda: setattr(roop.globals, 'many_faces', many_faces_value.get()))
|
||||
many_faces_checkbox.place(relx=0.6, rely=0.65)
|
||||
many_faces_value = ctk.BooleanVar(value=roop.globals.many_faces)
|
||||
many_faces_switch = ctk.CTkSwitch(root, text='Many faces', variable=many_faces_value, command=lambda: setattr(roop.globals, 'many_faces', many_faces_value.get()))
|
||||
many_faces_switch.place(relx=0.6, rely=0.65)
|
||||
|
||||
start_button = create_secondary_button(root, 'Start', lambda: select_output_path(start))
|
||||
start_button = ctk.CTkButton(root, text='Start', command=lambda: select_output_path(start))
|
||||
start_button.place(relx=0.15, rely=0.75, relwidth=0.2, relheight=0.05)
|
||||
|
||||
stop_button = create_secondary_button(root, 'Destroy', lambda: destroy())
|
||||
stop_button = ctk.CTkButton(root, text='Destroy', command=lambda: destroy())
|
||||
stop_button.place(relx=0.4, rely=0.75, relwidth=0.2, relheight=0.05)
|
||||
|
||||
preview_button = create_secondary_button(root, 'Preview', lambda: toggle_preview())
|
||||
preview_button = ctk.CTkButton(root, text='Preview', command=lambda: toggle_preview())
|
||||
preview_button.place(relx=0.65, rely=0.75, relwidth=0.2, relheight=0.05)
|
||||
|
||||
status_label = tk.Label(root, justify='center', text='Status: None', fg=ACCENT_COLOR, bg=PRIMARY_COLOR)
|
||||
status_label = ctk.CTkLabel(root, text='Status: None', justify='center')
|
||||
status_label.place(relx=0.1, rely=0.9)
|
||||
|
||||
return root
|
||||
|
||||
|
||||
def create_preview(parent) -> tk.Toplevel:
|
||||
global preview_label, preview_scale
|
||||
def create_preview(parent) -> ctk.CTkToplevel:
|
||||
global preview_label, preview_slider
|
||||
|
||||
preview = tk.Toplevel(parent)
|
||||
preview = ctk.CTkToplevel(parent)
|
||||
preview.withdraw()
|
||||
preview.title('Preview')
|
||||
preview.configure(bg=PRIMARY_COLOR)
|
||||
preview.option_add('*Font', ('Arial', 11))
|
||||
preview.configure()
|
||||
preview.protocol('WM_DELETE_WINDOW', lambda: toggle_preview())
|
||||
preview.resizable(width=False, height=False)
|
||||
|
||||
preview_label = tk.Label(preview, bg=PRIMARY_COLOR)
|
||||
preview_label = ctk.CTkLabel(preview, text=None)
|
||||
preview_label.pack(fill='both', expand=True)
|
||||
|
||||
preview_scale = tk.Scale(preview, orient='horizontal', command=lambda frame_value: update_preview(int(frame_value)))
|
||||
preview_scale.pack(fill='x')
|
||||
preview_slider = ctk.CTkSlider(preview, from_=0, to=100, border_width=10, command=lambda frame_value: update_preview(frame_value))
|
||||
preview_slider.pack(fill='x')
|
||||
|
||||
return preview
|
||||
|
||||
|
||||
def create_primary_button(parent: Any, text: str, command: Callable) -> tk.Button:
|
||||
return tk.Button(
|
||||
parent,
|
||||
text=text,
|
||||
command=command,
|
||||
bg=PRIMARY_COLOR,
|
||||
fg=SECONDARY_COLOR,
|
||||
relief='flat',
|
||||
highlightthickness=4,
|
||||
highlightbackground=SECONDARY_COLOR,
|
||||
activebackground=SECONDARY_COLOR,
|
||||
borderwidth=4
|
||||
)
|
||||
|
||||
|
||||
def create_secondary_button(parent: Any, text: str, command: Callable) -> tk.Button:
|
||||
return tk.Button(
|
||||
parent,
|
||||
text=text,
|
||||
command=command,
|
||||
bg=TERTIARY_COLOR,
|
||||
relief='flat',
|
||||
borderwidth=0,
|
||||
highlightthickness=0
|
||||
)
|
||||
|
||||
|
||||
def create_checkbox(parent: Any, text: str, variable: tk.BooleanVar, command: Callable) -> tk.Checkbutton:
|
||||
return tk.Checkbutton(
|
||||
parent,
|
||||
text=text,
|
||||
variable=variable,
|
||||
command=command,
|
||||
relief='flat',
|
||||
bg=PRIMARY_COLOR,
|
||||
activebackground=PRIMARY_COLOR,
|
||||
activeforeground=SECONDARY_COLOR,
|
||||
selectcolor=PRIMARY_COLOR,
|
||||
fg=SECONDARY_COLOR,
|
||||
borderwidth=0,
|
||||
highlightthickness=0
|
||||
)
|
||||
|
||||
|
||||
def update_status(text: str) -> None:
|
||||
status_label['text'] = text
|
||||
status_label.configure(text=text)
|
||||
ROOT.update()
|
||||
|
||||
|
||||
def select_source_path() -> None:
|
||||
global RECENT_DIRECTORY_SOURCE
|
||||
source_path = filedialog.askopenfilename(title='Select an face image', initialdir=RECENT_DIRECTORY_SOURCE)
|
||||
|
||||
source_path = ctk.filedialog.askopenfilename(title='Select an face image', initialdir=RECENT_DIRECTORY_SOURCE)
|
||||
if is_image(source_path):
|
||||
roop.globals.source_path = source_path
|
||||
RECENT_DIRECTORY_SOURCE = os.path.dirname(roop.globals.source_path)
|
||||
@ -173,7 +125,8 @@ def select_source_path() -> None:
|
||||
|
||||
def select_target_path() -> None:
|
||||
global RECENT_DIRECTORY_TARGET
|
||||
target_path = filedialog.askopenfilename(title='Select an image or video target', initialdir=RECENT_DIRECTORY_TARGET)
|
||||
|
||||
target_path = ctk.filedialog.askopenfilename(title='Select an image or video target', initialdir=RECENT_DIRECTORY_TARGET)
|
||||
if is_image(target_path):
|
||||
roop.globals.target_path = target_path
|
||||
RECENT_DIRECTORY_TARGET = os.path.dirname(roop.globals.target_path)
|
||||
@ -194,10 +147,11 @@ def select_target_path() -> None:
|
||||
|
||||
def select_output_path(start):
|
||||
global RECENT_DIRECTORY_OUTPUT
|
||||
|
||||
if is_image(roop.globals.target_path):
|
||||
output_path = filedialog.asksaveasfilename(title='Save image output', initialfile='output.png', initialdir=RECENT_DIRECTORY_OUTPUT)
|
||||
output_path = ctk.filedialog.asksaveasfilename(title='Save image output', initialfile='output.png', initialdir=RECENT_DIRECTORY_OUTPUT)
|
||||
elif is_video(roop.globals.target_path):
|
||||
output_path = filedialog.asksaveasfilename(title='Save video output', initialfile='output.mp4', initialdir=RECENT_DIRECTORY_OUTPUT)
|
||||
output_path = ctk.filedialog.asksaveasfilename(title='Save video output', initialfile='output.mp4', initialdir=RECENT_DIRECTORY_OUTPUT)
|
||||
if output_path:
|
||||
roop.globals.output_path = output_path
|
||||
RECENT_DIRECTORY_OUTPUT = os.path.dirname(roop.globals.output_path)
|
||||
|
||||
@ -126,3 +126,7 @@ def conditional_download(download_directory_path: str, urls: List[str]):
|
||||
total = int(request.headers.get('Content-Length', 0))
|
||||
with tqdm(total=total, desc='Downloading', unit='B', unit_scale=True, unit_divisor=1024) as progress:
|
||||
urllib.request.urlretrieve(url, download_file_path, reporthook=lambda count, block_size, total_size: progress.update(block_size))
|
||||
|
||||
|
||||
def resolve_relative_path(path: str) -> str:
|
||||
return os.path.abspath(os.path.join(os.path.dirname(__file__), path))
|
||||
|
||||
Loading…
Reference in New Issue
Block a user