Adding Drag and Drop feature to Face Mapper and Source and Target

pull/588/head
CH. Krish 2024-09-15 23:00:28 +05:30 committed by GitHub
parent 621c3f035e
commit 523d80550d
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
1 changed files with 448 additions and 147 deletions

View File

@ -4,13 +4,26 @@ import customtkinter as ctk
from typing import Callable, Tuple from typing import Callable, Tuple
import cv2 import cv2
from PIL import Image, ImageOps from PIL import Image, ImageOps
import tkinterdnd2 as tkdnd
import modules.globals import modules.globals
import modules.metadata import modules.metadata
from modules.face_analyser import get_one_face, get_unique_faces_from_target_image, get_unique_faces_from_target_video, add_blank_map, has_valid_map, simplify_maps from modules.face_analyser import (
get_one_face,
get_unique_faces_from_target_image,
get_unique_faces_from_target_video,
add_blank_map,
has_valid_map,
simplify_maps,
)
from modules.capturer import get_video_frame, get_video_frame_total from modules.capturer import get_video_frame, get_video_frame_total
from modules.processors.frame.core import get_frame_processors_modules from modules.processors.frame.core import get_frame_processors_modules
from modules.utilities import is_image, is_video, resolve_relative_path, has_image_extension from modules.utilities import (
is_image,
is_video,
resolve_relative_path,
has_image_extension,
)
ROOT = None ROOT = None
POPUP = None POPUP = None
@ -26,12 +39,12 @@ PREVIEW_DEFAULT_HEIGHT = 540
POPUP_WIDTH = 750 POPUP_WIDTH = 750
POPUP_HEIGHT = 810 POPUP_HEIGHT = 810
POPUP_SCROLL_WIDTH = 740, POPUP_SCROLL_WIDTH = 740
POPUP_SCROLL_HEIGHT = 700 POPUP_SCROLL_HEIGHT = 700
POPUP_LIVE_WIDTH = 900 POPUP_LIVE_WIDTH = 900
POPUP_LIVE_HEIGHT = 820 POPUP_LIVE_HEIGHT = 820
POPUP_LIVE_SCROLL_WIDTH = 890, POPUP_LIVE_SCROLL_WIDTH = 890
POPUP_LIVE_SCROLL_HEIGHT = 700 POPUP_LIVE_SCROLL_HEIGHT = 700
MAPPER_PREVIEW_MAX_HEIGHT = 100 MAPPER_PREVIEW_MAX_HEIGHT = 100
@ -58,7 +71,58 @@ target_label_dict_live = {}
img_ft, vid_ft = modules.globals.file_types img_ft, vid_ft = modules.globals.file_types
def init(start: Callable[[], None], destroy: Callable[[], None]) -> ctk.CTk: class DragDropButton(ctk.CTkButton):
def __init__(self, master, **kwargs):
super().__init__(master, **kwargs)
self.drop_target_register(tkdnd.DND_FILES)
self.dnd_bind("<<Drop>>", self.drop)
def drop(self, event):
file_path = event.data
if file_path.startswith("{"):
file_path = file_path[1:-1]
self.handle_drop(file_path)
def handle_drop(self, file_path):
pass
class SourceButton(DragDropButton):
def handle_drop(self, file_path):
if is_image(file_path):
modules.globals.source_path = file_path
global RECENT_DIRECTORY_SOURCE
RECENT_DIRECTORY_SOURCE = os.path.dirname(modules.globals.source_path)
image = render_image_preview(modules.globals.source_path, (200, 200))
source_label.configure(image=image)
class SourceMapperButton(DragDropButton):
def __init__(self, master, map, button_num, **kwargs):
super().__init__(master, **kwargs)
self.map = map
self.button_num = button_num
def handle_drop(self, file_path):
if is_image(file_path):
update_popup_source(self.master, self.map, self.button_num, file_path)
class TargetButton(DragDropButton):
def handle_drop(self, file_path):
global RECENT_DIRECTORY_TARGET
if is_image(file_path) or is_video(file_path):
modules.globals.target_path = file_path
RECENT_DIRECTORY_TARGET = os.path.dirname(modules.globals.target_path)
if is_image(file_path):
image = render_image_preview(modules.globals.target_path, (200, 200))
target_label.configure(image=image)
elif is_video(file_path):
video_frame = render_video_preview(file_path, (200, 200))
target_label.configure(image=video_frame)
def init(start: Callable[[], None], destroy: Callable[[], None]) -> tkdnd.TkinterDnD.Tk:
global ROOT, PREVIEW global ROOT, PREVIEW
ROOT = create_root(start, destroy) ROOT = create_root(start, destroy)
@ -67,18 +131,22 @@ def init(start: Callable[[], None], destroy: Callable[[], None]) -> ctk.CTk:
return ROOT return ROOT
def create_root(start: Callable[[], None], destroy: Callable[[], None]) -> ctk.CTk: def create_root(
start: Callable[[], None], destroy: Callable[[], None]
) -> tkdnd.TkinterDnD.Tk:
global source_label, target_label, status_label global source_label, target_label, status_label
ctk.deactivate_automatic_dpi_awareness() ctk.deactivate_automatic_dpi_awareness()
ctk.set_appearance_mode('system') ctk.set_appearance_mode("dark") # Re-add this line
ctk.set_default_color_theme(resolve_relative_path('ui.json')) ctk.set_default_color_theme(resolve_relative_path("ui.json"))
root = ctk.CTk() root = tkdnd.TkinterDnD.Tk()
root.minsize(ROOT_WIDTH, ROOT_HEIGHT) root.minsize(ROOT_WIDTH, ROOT_HEIGHT)
root.title(f'{modules.metadata.name} {modules.metadata.version} {modules.metadata.edition}') root.title(
root.configure() f"{modules.metadata.name} {modules.metadata.version} {modules.metadata.edition}"
root.protocol('WM_DELETE_WINDOW', lambda: destroy()) )
root.configure(bg="gray12")
root.protocol("WM_DELETE_WINDOW", lambda: destroy())
source_label = ctk.CTkLabel(root, text=None) source_label = ctk.CTkLabel(root, text=None)
source_label.place(relx=0.1, rely=0.1, relwidth=0.3, relheight=0.25) source_label.place(relx=0.1, rely=0.1, relwidth=0.3, relheight=0.25)
@ -86,71 +154,137 @@ def create_root(start: Callable[[], None], destroy: Callable[[], None]) -> ctk.C
target_label = ctk.CTkLabel(root, text=None) target_label = ctk.CTkLabel(root, text=None)
target_label.place(relx=0.6, rely=0.1, relwidth=0.3, relheight=0.25) target_label.place(relx=0.6, rely=0.1, relwidth=0.3, relheight=0.25)
select_face_button = ctk.CTkButton(root, text='Select a face', cursor='hand2', command=lambda: select_source_path()) select_face_button = SourceButton(
root, text="Select a face", cursor="hand2", command=lambda: select_source_path()
)
select_face_button.place(relx=0.1, rely=0.4, relwidth=0.3, relheight=0.1) select_face_button.place(relx=0.1, rely=0.4, relwidth=0.3, relheight=0.1)
swap_faces_button = ctk.CTkButton(root, text='', cursor='hand2', command=lambda: swap_faces_paths()) swap_faces_button = ctk.CTkButton(
root, text="", cursor="hand2", command=lambda: swap_faces_paths()
)
swap_faces_button.place(relx=0.45, rely=0.4, relwidth=0.1, relheight=0.1) swap_faces_button.place(relx=0.45, rely=0.4, relwidth=0.1, relheight=0.1)
select_target_button = ctk.CTkButton(root, text='Select a target', cursor='hand2', command=lambda: select_target_path()) select_target_button = TargetButton(
root,
text="Select a target",
cursor="hand2",
command=lambda: select_target_path(),
)
select_target_button.place(relx=0.6, rely=0.4, relwidth=0.3, relheight=0.1) select_target_button.place(relx=0.6, rely=0.4, relwidth=0.3, relheight=0.1)
keep_fps_value = ctk.BooleanVar(value=modules.globals.keep_fps) keep_fps_value = ctk.BooleanVar(value=modules.globals.keep_fps)
keep_fps_checkbox = ctk.CTkSwitch(root, text='Keep fps', variable=keep_fps_value, cursor='hand2', command=lambda: setattr(modules.globals, 'keep_fps', not modules.globals.keep_fps)) keep_fps_checkbox = ctk.CTkSwitch(
root,
text="Keep fps",
variable=keep_fps_value,
cursor="hand2",
command=lambda: setattr(
modules.globals, "keep_fps", not modules.globals.keep_fps
),
)
keep_fps_checkbox.place(relx=0.1, rely=0.6) keep_fps_checkbox.place(relx=0.1, rely=0.6)
keep_frames_value = ctk.BooleanVar(value=modules.globals.keep_frames) keep_frames_value = ctk.BooleanVar(value=modules.globals.keep_frames)
keep_frames_switch = ctk.CTkSwitch(root, text='Keep frames', variable=keep_frames_value, cursor='hand2', command=lambda: setattr(modules.globals, 'keep_frames', keep_frames_value.get())) keep_frames_switch = ctk.CTkSwitch(
root,
text="Keep frames",
variable=keep_frames_value,
cursor="hand2",
command=lambda: setattr(
modules.globals, "keep_frames", keep_frames_value.get()
),
)
keep_frames_switch.place(relx=0.1, rely=0.65) keep_frames_switch.place(relx=0.1, rely=0.65)
# for FRAME PROCESSOR ENHANCER tumbler: enhancer_value = ctk.BooleanVar(value=modules.globals.fp_ui["face_enhancer"])
enhancer_value = ctk.BooleanVar(value=modules.globals.fp_ui['face_enhancer']) enhancer_switch = ctk.CTkSwitch(
enhancer_switch = ctk.CTkSwitch(root, text='Face Enhancer', variable=enhancer_value, cursor='hand2', command=lambda: update_tumbler('face_enhancer',enhancer_value.get())) root,
text="Face Enhancer",
variable=enhancer_value,
cursor="hand2",
command=lambda: update_tumbler("face_enhancer", enhancer_value.get()),
)
enhancer_switch.place(relx=0.1, rely=0.7) enhancer_switch.place(relx=0.1, rely=0.7)
keep_audio_value = ctk.BooleanVar(value=modules.globals.keep_audio) keep_audio_value = ctk.BooleanVar(value=modules.globals.keep_audio)
keep_audio_switch = ctk.CTkSwitch(root, text='Keep audio', variable=keep_audio_value, cursor='hand2', command=lambda: setattr(modules.globals, 'keep_audio', keep_audio_value.get())) keep_audio_switch = ctk.CTkSwitch(
root,
text="Keep audio",
variable=keep_audio_value,
cursor="hand2",
command=lambda: setattr(modules.globals, "keep_audio", keep_audio_value.get()),
)
keep_audio_switch.place(relx=0.6, rely=0.6) keep_audio_switch.place(relx=0.6, rely=0.6)
many_faces_value = ctk.BooleanVar(value=modules.globals.many_faces) many_faces_value = ctk.BooleanVar(value=modules.globals.many_faces)
many_faces_switch = ctk.CTkSwitch(root, text='Many faces', variable=many_faces_value, cursor='hand2', command=lambda: setattr(modules.globals, 'many_faces', many_faces_value.get())) many_faces_switch = ctk.CTkSwitch(
root,
text="Many faces",
variable=many_faces_value,
cursor="hand2",
command=lambda: setattr(modules.globals, "many_faces", many_faces_value.get()),
)
many_faces_switch.place(relx=0.6, rely=0.65) many_faces_switch.place(relx=0.6, rely=0.65)
# Add color correction toggle button
color_correction_value = ctk.BooleanVar(value=modules.globals.color_correction) color_correction_value = ctk.BooleanVar(value=modules.globals.color_correction)
color_correction_switch = ctk.CTkSwitch(root, text='Fix Blueish Cam\n(force cv2 to use RGB instead of BGR)', variable=color_correction_value, cursor='hand2', command=lambda: setattr(modules.globals, 'color_correction', color_correction_value.get())) color_correction_switch = ctk.CTkSwitch(
root,
text="Fix Blueish Cam\n(force cv2 to use RGB instead of BGR)",
variable=color_correction_value,
cursor="hand2",
command=lambda: setattr(
modules.globals, "color_correction", color_correction_value.get()
),
)
color_correction_switch.place(relx=0.6, rely=0.70) color_correction_switch.place(relx=0.6, rely=0.70)
# nsfw_value = ctk.BooleanVar(value=modules.globals.nsfw_filter)
# nsfw_switch = ctk.CTkSwitch(root, text='NSFW filter', variable=nsfw_value, cursor='hand2', command=lambda: setattr(modules.globals, 'nsfw_filter', nsfw_value.get()))
# nsfw_switch.place(relx=0.6, rely=0.7)
map_faces = ctk.BooleanVar(value=modules.globals.map_faces) map_faces = ctk.BooleanVar(value=modules.globals.map_faces)
map_faces_switch = ctk.CTkSwitch(root, text='Map faces', variable=map_faces, cursor='hand2', command=lambda: setattr(modules.globals, 'map_faces', map_faces.get())) map_faces_switch = ctk.CTkSwitch(
root,
text="Map faces",
variable=map_faces,
cursor="hand2",
command=lambda: setattr(modules.globals, "map_faces", map_faces.get()),
)
map_faces_switch.place(relx=0.1, rely=0.75) map_faces_switch.place(relx=0.1, rely=0.75)
start_button = ctk.CTkButton(root, text='Start', cursor='hand2', command=lambda: analyze_target(start, root)) start_button = ctk.CTkButton(
root, text="Start", cursor="hand2", command=lambda: analyze_target(start, root)
)
start_button.place(relx=0.15, rely=0.80, relwidth=0.2, relheight=0.05) start_button.place(relx=0.15, rely=0.80, relwidth=0.2, relheight=0.05)
stop_button = ctk.CTkButton(root, text='Destroy', cursor='hand2', command=lambda: destroy()) stop_button = ctk.CTkButton(
root, text="Destroy", cursor="hand2", command=lambda: destroy()
)
stop_button.place(relx=0.4, rely=0.80, relwidth=0.2, relheight=0.05) stop_button.place(relx=0.4, rely=0.80, relwidth=0.2, relheight=0.05)
preview_button = ctk.CTkButton(root, text='Preview', cursor='hand2', command=lambda: toggle_preview()) preview_button = ctk.CTkButton(
root, text="Preview", cursor="hand2", command=lambda: toggle_preview()
)
preview_button.place(relx=0.65, rely=0.80, relwidth=0.2, relheight=0.05) preview_button.place(relx=0.65, rely=0.80, relwidth=0.2, relheight=0.05)
live_button = ctk.CTkButton(root, text='Live', cursor='hand2', command=lambda: webcam_preview(root)) live_button = ctk.CTkButton(
root, text="Live", cursor="hand2", command=lambda: webcam_preview(root)
)
live_button.place(relx=0.40, rely=0.86, relwidth=0.2, relheight=0.05) live_button.place(relx=0.40, rely=0.86, relwidth=0.2, relheight=0.05)
status_label = ctk.CTkLabel(root, text=None, justify='center') status_label = ctk.CTkLabel(root, text=None, justify="center")
status_label.place(relx=0.1, rely=0.9, relwidth=0.8) status_label.place(relx=0.1, rely=0.9, relwidth=0.8)
donate_label = ctk.CTkLabel(root, text='Deep Live Cam', justify='center', cursor='hand2') donate_label = ctk.CTkLabel(
root, text="Deep Live Cam", justify="center", cursor="hand2"
)
donate_label.place(relx=0.1, rely=0.95, relwidth=0.8) donate_label.place(relx=0.1, rely=0.95, relwidth=0.8)
donate_label.configure(text_color=ctk.ThemeManager.theme.get('URL').get('text_color')) donate_label.configure(
donate_label.bind('<Button>', lambda event: webbrowser.open('https://paypal.me/hacksider')) text_color=ctk.ThemeManager.theme.get("URL").get("text_color")
)
donate_label.bind(
"<Button>", lambda event: webbrowser.open("https://paypal.me/hacksider")
)
return root return root
def analyze_target(start: Callable[[], None], root: ctk.CTk): def analyze_target(start: Callable[[], None], root: ctk.CTk):
if POPUP != None and POPUP.winfo_exists(): if POPUP != None and POPUP.winfo_exists():
update_status("Please complete pop-up or close it.") update_status("Please complete pop-up or close it.")
@ -160,10 +294,10 @@ def analyze_target(start: Callable[[], None], root: ctk.CTk):
modules.globals.souce_target_map = [] modules.globals.souce_target_map = []
if is_image(modules.globals.target_path): if is_image(modules.globals.target_path):
update_status('Getting unique faces') update_status("Getting unique faces")
get_unique_faces_from_target_image() get_unique_faces_from_target_image()
elif is_video(modules.globals.target_path): elif is_video(modules.globals.target_path):
update_status('Getting unique faces') update_status("Getting unique faces")
get_unique_faces_from_target_video() get_unique_faces_from_target_video()
if len(modules.globals.souce_target_map) > 0: if len(modules.globals.souce_target_map) > 0:
@ -173,7 +307,10 @@ def analyze_target(start: Callable[[], None], root: ctk.CTk):
else: else:
select_output_path(start) select_output_path(start)
def create_source_target_popup(start: Callable[[], None], root: ctk.CTk, map: list) -> None:
def create_source_target_popup(
start: Callable[[], None], root: ctk.CTk, map: list
) -> None:
global POPUP, popup_status_label global POPUP, popup_status_label
POPUP = ctk.CTkToplevel(root) POPUP = ctk.CTkToplevel(root)
@ -188,40 +325,74 @@ def create_source_target_popup(start: Callable[[], None], root: ctk.CTk, map: li
else: else:
update_pop_status("Atleast 1 source with target is required!") update_pop_status("Atleast 1 source with target is required!")
scrollable_frame = ctk.CTkScrollableFrame(POPUP, width=POPUP_SCROLL_WIDTH, height=POPUP_SCROLL_HEIGHT) scrollable_frame = ctk.CTkScrollableFrame(
scrollable_frame.grid(row=0, column=0, padx=0, pady=0, sticky='nsew') POPUP, width=POPUP_SCROLL_WIDTH, height=POPUP_SCROLL_HEIGHT
)
scrollable_frame.grid(row=0, column=0, padx=0, pady=0, sticky="nsew")
def on_button_click(map, button_num): def on_button_click(map, button_num):
map = update_popup_source(scrollable_frame, map, button_num) update_popup_source(scrollable_frame, map, button_num)
for item in map: for item in map:
id = item['id'] id = item["id"]
button = ctk.CTkButton(scrollable_frame, text="Select source image", command=lambda id=id: on_button_click(map, id), width=DEFAULT_BUTTON_WIDTH, height=DEFAULT_BUTTON_HEIGHT) button = SourceMapperButton(
scrollable_frame,
map,
id,
text="Select source image",
command=lambda id=id: on_button_click(map, id),
width=DEFAULT_BUTTON_WIDTH,
height=DEFAULT_BUTTON_HEIGHT,
)
button.grid(row=id, column=0, padx=50, pady=10) button.grid(row=id, column=0, padx=50, pady=10)
x_label = ctk.CTkLabel(scrollable_frame, text=f"X", width=MAPPER_PREVIEW_MAX_WIDTH, height=MAPPER_PREVIEW_MAX_HEIGHT) x_label = ctk.CTkLabel(
scrollable_frame,
text=f"X",
width=MAPPER_PREVIEW_MAX_WIDTH,
height=MAPPER_PREVIEW_MAX_HEIGHT,
)
x_label.grid(row=id, column=2, padx=10, pady=10) x_label.grid(row=id, column=2, padx=10, pady=10)
image = Image.fromarray(cv2.cvtColor(item['target']['cv2'], cv2.COLOR_BGR2RGB)) image = Image.fromarray(cv2.cvtColor(item["target"]["cv2"], cv2.COLOR_BGR2RGB))
image = image.resize((MAPPER_PREVIEW_MAX_WIDTH, MAPPER_PREVIEW_MAX_HEIGHT), Image.LANCZOS) image = image.resize(
(MAPPER_PREVIEW_MAX_WIDTH, MAPPER_PREVIEW_MAX_HEIGHT), Image.LANCZOS
)
tk_image = ctk.CTkImage(image, size=image.size) tk_image = ctk.CTkImage(image, size=image.size)
target_image = ctk.CTkLabel(scrollable_frame, text=f"T-{id}", width=MAPPER_PREVIEW_MAX_WIDTH, height=MAPPER_PREVIEW_MAX_HEIGHT) target_image = ctk.CTkLabel(
scrollable_frame,
text=f"T-{id}",
width=MAPPER_PREVIEW_MAX_WIDTH,
height=MAPPER_PREVIEW_MAX_HEIGHT,
)
target_image.grid(row=id, column=3, padx=10, pady=10) target_image.grid(row=id, column=3, padx=10, pady=10)
target_image.configure(image=tk_image) target_image.configure(image=tk_image)
popup_status_label = ctk.CTkLabel(POPUP, text=None, justify='center') popup_status_label = ctk.CTkLabel(POPUP, text=None, justify="center")
popup_status_label.grid(row=1, column=0, pady=15) popup_status_label.grid(row=1, column=0, pady=15)
close_button = ctk.CTkButton(POPUP, text="Submit", command=lambda: on_submit_click(start)) close_button = ctk.CTkButton(
POPUP, text="Submit", command=lambda: on_submit_click(start)
)
close_button.grid(row=2, column=0, pady=10) close_button.grid(row=2, column=0, pady=10)
def update_popup_source(scrollable_frame: ctk.CTkScrollableFrame, map: list, button_num: int) -> list: def update_popup_source(
global source_label_dict scrollable_frame: ctk.CTkScrollableFrame,
map: list,
button_num: int,
source_path: str = None,
) -> list:
global source_label_dict, RECENT_DIRECTORY_SOURCE
source_path = ctk.filedialog.askopenfilename(title='select an source image', initialdir=RECENT_DIRECTORY_SOURCE, filetypes=[img_ft]) if source_path is None:
source_path = ctk.filedialog.askopenfilename(
title="select an source image",
initialdir=RECENT_DIRECTORY_SOURCE,
filetypes=[img_ft],
)
if "source" in map[button_num]: if "source" in map[button_num]:
map[button_num].pop("source") map[button_num].pop("source")
@ -231,22 +402,32 @@ def update_popup_source(scrollable_frame: ctk.CTkScrollableFrame, map: list, but
if source_path == "": if source_path == "":
return map return map
else: else:
RECENT_DIRECTORY_SOURCE = os.path.dirname(source_path)
cv2_img = cv2.imread(source_path) cv2_img = cv2.imread(source_path)
face = get_one_face(cv2_img) face = get_one_face(cv2_img)
if face: if face:
x_min, y_min, x_max, y_max = face['bbox'] x_min, y_min, x_max, y_max = face["bbox"]
map[button_num]['source'] = { map[button_num]["source"] = {
'cv2' : cv2_img[int(y_min):int(y_max), int(x_min):int(x_max)], "cv2": cv2_img[int(y_min) : int(y_max), int(x_min) : int(x_max)],
'face' : face "face": face,
} }
image = Image.fromarray(cv2.cvtColor(map[button_num]['source']['cv2'], cv2.COLOR_BGR2RGB)) image = Image.fromarray(
image = image.resize((MAPPER_PREVIEW_MAX_WIDTH, MAPPER_PREVIEW_MAX_HEIGHT), Image.LANCZOS) cv2.cvtColor(map[button_num]["source"]["cv2"], cv2.COLOR_BGR2RGB)
)
image = image.resize(
(MAPPER_PREVIEW_MAX_WIDTH, MAPPER_PREVIEW_MAX_HEIGHT), Image.LANCZOS
)
tk_image = ctk.CTkImage(image, size=image.size) tk_image = ctk.CTkImage(image, size=image.size)
source_image = ctk.CTkLabel(scrollable_frame, text=f"S-{button_num}", width=MAPPER_PREVIEW_MAX_WIDTH, height=MAPPER_PREVIEW_MAX_HEIGHT) source_image = ctk.CTkLabel(
scrollable_frame,
text=f"S-{button_num}",
width=MAPPER_PREVIEW_MAX_WIDTH,
height=MAPPER_PREVIEW_MAX_HEIGHT,
)
source_image.grid(row=button_num, column=1, padx=10, pady=10) source_image.grid(row=button_num, column=1, padx=10, pady=10)
source_image.configure(image=tk_image) source_image.configure(image=tk_image)
source_label_dict[button_num] = source_image source_label_dict[button_num] = source_image
@ -260,15 +441,17 @@ def create_preview(parent: ctk.CTkToplevel) -> ctk.CTkToplevel:
preview = ctk.CTkToplevel(parent) preview = ctk.CTkToplevel(parent)
preview.withdraw() preview.withdraw()
preview.title('Preview') preview.title("Preview")
preview.configure() preview.configure()
preview.protocol('WM_DELETE_WINDOW', lambda: toggle_preview()) preview.protocol("WM_DELETE_WINDOW", lambda: toggle_preview())
preview.resizable(width=True, height=True) preview.resizable(width=True, height=True)
preview_label = ctk.CTkLabel(preview, text=None) preview_label = ctk.CTkLabel(preview, text=None)
preview_label.pack(fill='both', expand=True) preview_label.pack(fill="both", expand=True)
preview_slider = ctk.CTkSlider(preview, from_=0, to=0, command=lambda frame_value: update_preview(frame_value)) preview_slider = ctk.CTkSlider(
preview, from_=0, to=0, command=lambda frame_value: update_preview(frame_value)
)
return preview return preview
@ -277,12 +460,15 @@ def update_status(text: str) -> None:
status_label.configure(text=text) status_label.configure(text=text)
ROOT.update() ROOT.update()
def update_pop_status(text: str) -> None: def update_pop_status(text: str) -> None:
popup_status_label.configure(text=text) popup_status_label.configure(text=text)
def update_pop_live_status(text: str) -> None: def update_pop_live_status(text: str) -> None:
popup_status_label_live.configure(text=text) popup_status_label_live.configure(text=text)
def update_tumbler(var: str, value: bool) -> None: def update_tumbler(var: str, value: bool) -> None:
modules.globals.fp_ui[var] = value modules.globals.fp_ui[var] = value
@ -291,7 +477,11 @@ def select_source_path() -> None:
global RECENT_DIRECTORY_SOURCE, img_ft, vid_ft global RECENT_DIRECTORY_SOURCE, img_ft, vid_ft
PREVIEW.withdraw() PREVIEW.withdraw()
source_path = ctk.filedialog.askopenfilename(title='select an source image', initialdir=RECENT_DIRECTORY_SOURCE, filetypes=[img_ft]) source_path = ctk.filedialog.askopenfilename(
title="select an source image",
initialdir=RECENT_DIRECTORY_SOURCE,
filetypes=[img_ft],
)
if is_image(source_path): if is_image(source_path):
modules.globals.source_path = source_path modules.globals.source_path = source_path
RECENT_DIRECTORY_SOURCE = os.path.dirname(modules.globals.source_path) RECENT_DIRECTORY_SOURCE = os.path.dirname(modules.globals.source_path)
@ -330,7 +520,11 @@ def select_target_path() -> None:
global RECENT_DIRECTORY_TARGET, img_ft, vid_ft global RECENT_DIRECTORY_TARGET, img_ft, vid_ft
PREVIEW.withdraw() PREVIEW.withdraw()
target_path = ctk.filedialog.askopenfilename(title='select an target image or video', initialdir=RECENT_DIRECTORY_TARGET, filetypes=[img_ft, vid_ft]) target_path = ctk.filedialog.askopenfilename(
title="select an target image or video",
initialdir=RECENT_DIRECTORY_TARGET,
filetypes=[img_ft, vid_ft],
)
if is_image(target_path): if is_image(target_path):
modules.globals.target_path = target_path modules.globals.target_path = target_path
RECENT_DIRECTORY_TARGET = os.path.dirname(modules.globals.target_path) RECENT_DIRECTORY_TARGET = os.path.dirname(modules.globals.target_path)
@ -350,9 +544,21 @@ def select_output_path(start: Callable[[], None]) -> None:
global RECENT_DIRECTORY_OUTPUT, img_ft, vid_ft global RECENT_DIRECTORY_OUTPUT, img_ft, vid_ft
if is_image(modules.globals.target_path): if is_image(modules.globals.target_path):
output_path = ctk.filedialog.asksaveasfilename(title='save image output file', filetypes=[img_ft], defaultextension='.png', initialfile='output.png', initialdir=RECENT_DIRECTORY_OUTPUT) output_path = ctk.filedialog.asksaveasfilename(
title="save image output file",
filetypes=[img_ft],
defaultextension=".png",
initialfile="output.png",
initialdir=RECENT_DIRECTORY_OUTPUT,
)
elif is_video(modules.globals.target_path): elif is_video(modules.globals.target_path):
output_path = ctk.filedialog.asksaveasfilename(title='save video output file', filetypes=[vid_ft], defaultextension='.mp4', initialfile='output.mp4', initialdir=RECENT_DIRECTORY_OUTPUT) output_path = ctk.filedialog.asksaveasfilename(
title="save video output file",
filetypes=[vid_ft],
defaultextension=".mp4",
initialfile="output.mp4",
initialdir=RECENT_DIRECTORY_OUTPUT,
)
else: else:
output_path = None output_path = None
if output_path: if output_path:
@ -362,20 +568,25 @@ def select_output_path(start: Callable[[], None]) -> None:
def check_and_ignore_nsfw(target, destroy: Callable = None) -> bool: def check_and_ignore_nsfw(target, destroy: Callable = None) -> bool:
''' Check if the target is NSFW. """Check if the target is NSFW.
TODO: Consider to make blur the target. TODO: Consider to make blur the target.
''' """
from numpy import ndarray from numpy import ndarray
from modules.predicter import predict_image, predict_video, predict_frame from modules.predicter import predict_image, predict_video, predict_frame
if type(target) is str: # image/video file path if type(target) is str: # image/video file path
check_nsfw = predict_image if has_image_extension(target) else predict_video check_nsfw = predict_image if has_image_extension(target) else predict_video
elif type(target) is ndarray: # frame object elif type(target) is ndarray: # frame object
check_nsfw = predict_frame check_nsfw = predict_frame
if check_nsfw and check_nsfw(target): if check_nsfw and check_nsfw(target):
if destroy: destroy(to_quit=False) # Do not need to destroy the window frame if the target is NSFW if destroy:
update_status('Processing ignored!') destroy(
to_quit=False
) # Do not need to destroy the window frame if the target is NSFW
update_status("Processing ignored!")
return True return True
else: return False else:
return False
def fit_image_to_size(image, width: int, height: int): def fit_image_to_size(image, width: int, height: int):
@ -400,7 +611,9 @@ def render_image_preview(image_path: str, size: Tuple[int, int]) -> ctk.CTkImage
return ctk.CTkImage(image, size=image.size) return ctk.CTkImage(image, size=image.size)
def render_video_preview(video_path: str, size: Tuple[int, int], frame_number: int = 0) -> ctk.CTkImage: def render_video_preview(
video_path: str, size: Tuple[int, int], frame_number: int = 0
) -> ctk.CTkImage:
capture = cv2.VideoCapture(video_path) capture = cv2.VideoCapture(video_path)
if frame_number: if frame_number:
capture.set(cv2.CAP_PROP_POS_FRAMES, frame_number) capture.set(cv2.CAP_PROP_POS_FRAMES, frame_number)
@ -415,7 +628,7 @@ def render_video_preview(video_path: str, size: Tuple[int, int], frame_number: i
def toggle_preview() -> None: def toggle_preview() -> None:
if PREVIEW.state() == 'normal': if PREVIEW.state() == "normal":
PREVIEW.withdraw() PREVIEW.withdraw()
elif modules.globals.source_path and modules.globals.target_path: elif modules.globals.source_path and modules.globals.target_path:
init_preview() init_preview()
@ -428,28 +641,32 @@ def init_preview() -> None:
if is_video(modules.globals.target_path): if is_video(modules.globals.target_path):
video_frame_total = get_video_frame_total(modules.globals.target_path) video_frame_total = get_video_frame_total(modules.globals.target_path)
preview_slider.configure(to=video_frame_total) preview_slider.configure(to=video_frame_total)
preview_slider.pack(fill='x') preview_slider.pack(fill="x")
preview_slider.set(0) preview_slider.set(0)
def update_preview(frame_number: int = 0) -> None: def update_preview(frame_number: int = 0) -> None:
if modules.globals.source_path and modules.globals.target_path: if modules.globals.source_path and modules.globals.target_path:
update_status('Processing...') update_status("Processing...")
temp_frame = get_video_frame(modules.globals.target_path, frame_number) temp_frame = get_video_frame(modules.globals.target_path, frame_number)
if modules.globals.nsfw_filter and check_and_ignore_nsfw(temp_frame): if modules.globals.nsfw_filter and check_and_ignore_nsfw(temp_frame):
return return
for frame_processor in get_frame_processors_modules(modules.globals.frame_processors): for frame_processor in get_frame_processors_modules(
modules.globals.frame_processors
):
temp_frame = frame_processor.process_frame( temp_frame = frame_processor.process_frame(
get_one_face(cv2.imread(modules.globals.source_path)), get_one_face(cv2.imread(modules.globals.source_path)), temp_frame
temp_frame
) )
image = Image.fromarray(cv2.cvtColor(temp_frame, cv2.COLOR_BGR2RGB)) image = Image.fromarray(cv2.cvtColor(temp_frame, cv2.COLOR_BGR2RGB))
image = ImageOps.contain(image, (PREVIEW_MAX_WIDTH, PREVIEW_MAX_HEIGHT), Image.LANCZOS) image = ImageOps.contain(
image, (PREVIEW_MAX_WIDTH, PREVIEW_MAX_HEIGHT), Image.LANCZOS
)
image = ctk.CTkImage(image, size=image.size) image = ctk.CTkImage(image, size=image.size)
preview_label.configure(image=image) preview_label.configure(image=image)
update_status('Processing succeed!') update_status("Processing succeed!")
PREVIEW.deiconify() PREVIEW.deiconify()
def webcam_preview(root: ctk.CTk): def webcam_preview(root: ctk.CTk):
if not modules.globals.map_faces: if not modules.globals.map_faces:
if modules.globals.source_path is None: if modules.globals.source_path is None:
@ -460,15 +677,24 @@ def webcam_preview(root: ctk.CTk):
modules.globals.souce_target_map = [] modules.globals.souce_target_map = []
create_source_target_popup_for_webcam(root, modules.globals.souce_target_map) create_source_target_popup_for_webcam(root, modules.globals.souce_target_map)
def create_webcam_preview(): def create_webcam_preview():
global preview_label, PREVIEW global preview_label, PREVIEW
camera = cv2.VideoCapture(0) # Use index for the webcam (adjust the index accordingly if necessary) camera = cv2.VideoCapture(
camera.set(cv2.CAP_PROP_FRAME_WIDTH, PREVIEW_DEFAULT_WIDTH) # Set the width of the resolution 0
camera.set(cv2.CAP_PROP_FRAME_HEIGHT, PREVIEW_DEFAULT_HEIGHT) # Set the height of the resolution ) # Use index for the webcam (adjust the index accordingly if necessary)
camera.set(
cv2.CAP_PROP_FRAME_WIDTH, PREVIEW_DEFAULT_WIDTH
) # Set the width of the resolution
camera.set(
cv2.CAP_PROP_FRAME_HEIGHT, PREVIEW_DEFAULT_HEIGHT
) # Set the height of the resolution
camera.set(cv2.CAP_PROP_FPS, 60) # Set the frame rate of the webcam camera.set(cv2.CAP_PROP_FPS, 60) # Set the frame rate of the webcam
preview_label.configure(width=PREVIEW_DEFAULT_WIDTH, height=PREVIEW_DEFAULT_HEIGHT) # Reset the preview image before startup preview_label.configure(
width=PREVIEW_DEFAULT_WIDTH, height=PREVIEW_DEFAULT_HEIGHT
) # Reset the preview image before startup
PREVIEW.deiconify() # Open preview window PREVIEW.deiconify() # Open preview window
@ -487,7 +713,9 @@ def create_webcam_preview():
temp_frame = cv2.flip(temp_frame, 1) # horizontal flipping temp_frame = cv2.flip(temp_frame, 1) # horizontal flipping
if modules.globals.live_resizable: if modules.globals.live_resizable:
temp_frame = fit_image_to_size(temp_frame, PREVIEW.winfo_width(), PREVIEW.winfo_height()) temp_frame = fit_image_to_size(
temp_frame, PREVIEW.winfo_width(), PREVIEW.winfo_height()
)
if not modules.globals.map_faces: if not modules.globals.map_faces:
# Select and save face image only once # Select and save face image only once
@ -502,14 +730,18 @@ def create_webcam_preview():
for frame_processor in frame_processors: for frame_processor in frame_processors:
temp_frame = frame_processor.process_frame_v2(temp_frame) temp_frame = frame_processor.process_frame_v2(temp_frame)
image = cv2.cvtColor(temp_frame, cv2.COLOR_BGR2RGB) # Convert the image to RGB format to display it with Tkinter image = cv2.cvtColor(
temp_frame, cv2.COLOR_BGR2RGB
) # Convert the image to RGB format to display it with Tkinter
image = Image.fromarray(image) image = Image.fromarray(image)
image = ImageOps.contain(image, (temp_frame.shape[1], temp_frame.shape[0]), Image.LANCZOS) image = ImageOps.contain(
image, (temp_frame.shape[1], temp_frame.shape[0]), Image.LANCZOS
)
image = ctk.CTkImage(image, size=image.size) image = ctk.CTkImage(image, size=image.size)
preview_label.configure(image=image) preview_label.configure(image=image)
ROOT.update() ROOT.update()
if PREVIEW.state() == 'withdrawn': if PREVIEW.state() == "withdrawn":
break break
camera.release() camera.release()
@ -537,21 +769,25 @@ def create_source_target_popup_for_webcam(root: ctk.CTk, map: list) -> None:
refresh_data(map) refresh_data(map)
update_pop_live_status("Please provide mapping!") update_pop_live_status("Please provide mapping!")
popup_status_label_live = ctk.CTkLabel(POPUP_LIVE, text=None, justify='center') popup_status_label_live = ctk.CTkLabel(POPUP_LIVE, text=None, justify="center")
popup_status_label_live.grid(row=1, column=0, pady=15) popup_status_label_live.grid(row=1, column=0, pady=15)
add_button = ctk.CTkButton(POPUP_LIVE, text="Add", command=lambda: on_add_click()) add_button = ctk.CTkButton(POPUP_LIVE, text="Add", command=lambda: on_add_click())
add_button.place(relx=0.2, rely=0.92, relwidth=0.2, relheight=0.05) add_button.place(relx=0.2, rely=0.92, relwidth=0.2, relheight=0.05)
close_button = ctk.CTkButton(POPUP_LIVE, text="Submit", command=lambda: on_submit_click()) close_button = ctk.CTkButton(
POPUP_LIVE, text="Submit", command=lambda: on_submit_click()
)
close_button.place(relx=0.6, rely=0.92, relwidth=0.2, relheight=0.05) close_button.place(relx=0.6, rely=0.92, relwidth=0.2, relheight=0.05)
def refresh_data(map: list): def refresh_data(map: list):
global POPUP_LIVE global POPUP_LIVE
scrollable_frame = ctk.CTkScrollableFrame(POPUP_LIVE, width=POPUP_LIVE_SCROLL_WIDTH, height=POPUP_LIVE_SCROLL_HEIGHT) scrollable_frame = ctk.CTkScrollableFrame(
scrollable_frame.grid(row=0, column=0, padx=0, pady=0, sticky='nsew') POPUP_LIVE, width=POPUP_LIVE_SCROLL_WIDTH, height=POPUP_LIVE_SCROLL_HEIGHT
)
scrollable_frame.grid(row=0, column=0, padx=0, pady=0, sticky="nsew")
def on_sbutton_click(map, button_num): def on_sbutton_click(map, button_num):
map = update_webcam_source(scrollable_frame, map, button_num) map = update_webcam_source(scrollable_frame, map, button_num)
@ -560,40 +796,81 @@ def refresh_data(map: list):
map = update_webcam_target(scrollable_frame, map, button_num) map = update_webcam_target(scrollable_frame, map, button_num)
for item in map: for item in map:
id = item['id'] id = item["id"]
button = ctk.CTkButton(scrollable_frame, text="Select source image", command=lambda id=id: on_sbutton_click(map, id), width=DEFAULT_BUTTON_WIDTH, height=DEFAULT_BUTTON_HEIGHT) button = ctk.CTkButton(
scrollable_frame,
text="Select source image",
command=lambda id=id: on_sbutton_click(map, id),
width=DEFAULT_BUTTON_WIDTH,
height=DEFAULT_BUTTON_HEIGHT,
)
button.grid(row=id, column=0, padx=30, pady=10) button.grid(row=id, column=0, padx=30, pady=10)
x_label = ctk.CTkLabel(scrollable_frame, text=f"X", width=MAPPER_PREVIEW_MAX_WIDTH, height=MAPPER_PREVIEW_MAX_HEIGHT) x_label = ctk.CTkLabel(
scrollable_frame,
text=f"X",
width=MAPPER_PREVIEW_MAX_WIDTH,
height=MAPPER_PREVIEW_MAX_HEIGHT,
)
x_label.grid(row=id, column=2, padx=10, pady=10) x_label.grid(row=id, column=2, padx=10, pady=10)
button = ctk.CTkButton(scrollable_frame, text="Select target image", command=lambda id=id: on_tbutton_click(map, id), width=DEFAULT_BUTTON_WIDTH, height=DEFAULT_BUTTON_HEIGHT) button = ctk.CTkButton(
scrollable_frame,
text="Select target image",
command=lambda id=id: on_tbutton_click(map, id),
width=DEFAULT_BUTTON_WIDTH,
height=DEFAULT_BUTTON_HEIGHT,
)
button.grid(row=id, column=3, padx=20, pady=10) button.grid(row=id, column=3, padx=20, pady=10)
if "source" in item: if "source" in item:
image = Image.fromarray(cv2.cvtColor(item['source']['cv2'], cv2.COLOR_BGR2RGB)) image = Image.fromarray(
image = image.resize((MAPPER_PREVIEW_MAX_WIDTH, MAPPER_PREVIEW_MAX_HEIGHT), Image.LANCZOS) cv2.cvtColor(item["source"]["cv2"], cv2.COLOR_BGR2RGB)
)
image = image.resize(
(MAPPER_PREVIEW_MAX_WIDTH, MAPPER_PREVIEW_MAX_HEIGHT), Image.LANCZOS
)
tk_image = ctk.CTkImage(image, size=image.size) tk_image = ctk.CTkImage(image, size=image.size)
source_image = ctk.CTkLabel(scrollable_frame, text=f"S-{id}", width=MAPPER_PREVIEW_MAX_WIDTH, height=MAPPER_PREVIEW_MAX_HEIGHT) source_image = ctk.CTkLabel(
scrollable_frame,
text=f"S-{id}",
width=MAPPER_PREVIEW_MAX_WIDTH,
height=MAPPER_PREVIEW_MAX_HEIGHT,
)
source_image.grid(row=id, column=1, padx=10, pady=10) source_image.grid(row=id, column=1, padx=10, pady=10)
source_image.configure(image=tk_image) source_image.configure(image=tk_image)
if "target" in item: if "target" in item:
image = Image.fromarray(cv2.cvtColor(item['target']['cv2'], cv2.COLOR_BGR2RGB)) image = Image.fromarray(
image = image.resize((MAPPER_PREVIEW_MAX_WIDTH, MAPPER_PREVIEW_MAX_HEIGHT), Image.LANCZOS) cv2.cvtColor(item["target"]["cv2"], cv2.COLOR_BGR2RGB)
)
image = image.resize(
(MAPPER_PREVIEW_MAX_WIDTH, MAPPER_PREVIEW_MAX_HEIGHT), Image.LANCZOS
)
tk_image = ctk.CTkImage(image, size=image.size) tk_image = ctk.CTkImage(image, size=image.size)
target_image = ctk.CTkLabel(scrollable_frame, text=f"T-{id}", width=MAPPER_PREVIEW_MAX_WIDTH, height=MAPPER_PREVIEW_MAX_HEIGHT) target_image = ctk.CTkLabel(
scrollable_frame,
text=f"T-{id}",
width=MAPPER_PREVIEW_MAX_WIDTH,
height=MAPPER_PREVIEW_MAX_HEIGHT,
)
target_image.grid(row=id, column=4, padx=20, pady=10) target_image.grid(row=id, column=4, padx=20, pady=10)
target_image.configure(image=tk_image) target_image.configure(image=tk_image)
def update_webcam_source(scrollable_frame: ctk.CTkScrollableFrame, map: list, button_num: int) -> list: def update_webcam_source(
scrollable_frame: ctk.CTkScrollableFrame, map: list, button_num: int
) -> list:
global source_label_dict_live global source_label_dict_live
source_path = ctk.filedialog.askopenfilename(title='select an source image', initialdir=RECENT_DIRECTORY_SOURCE, filetypes=[img_ft]) source_path = ctk.filedialog.askopenfilename(
title="select an source image",
initialdir=RECENT_DIRECTORY_SOURCE,
filetypes=[img_ft],
)
if "source" in map[button_num]: if "source" in map[button_num]:
map[button_num].pop("source") map[button_num].pop("source")
@ -607,18 +884,27 @@ def update_webcam_source(scrollable_frame: ctk.CTkScrollableFrame, map: list, bu
face = get_one_face(cv2_img) face = get_one_face(cv2_img)
if face: if face:
x_min, y_min, x_max, y_max = face['bbox'] x_min, y_min, x_max, y_max = face["bbox"]
map[button_num]['source'] = { map[button_num]["source"] = {
'cv2' : cv2_img[int(y_min):int(y_max), int(x_min):int(x_max)], "cv2": cv2_img[int(y_min) : int(y_max), int(x_min) : int(x_max)],
'face' : face "face": face,
} }
image = Image.fromarray(cv2.cvtColor(map[button_num]['source']['cv2'], cv2.COLOR_BGR2RGB)) image = Image.fromarray(
image = image.resize((MAPPER_PREVIEW_MAX_WIDTH, MAPPER_PREVIEW_MAX_HEIGHT), Image.LANCZOS) cv2.cvtColor(map[button_num]["source"]["cv2"], cv2.COLOR_BGR2RGB)
)
image = image.resize(
(MAPPER_PREVIEW_MAX_WIDTH, MAPPER_PREVIEW_MAX_HEIGHT), Image.LANCZOS
)
tk_image = ctk.CTkImage(image, size=image.size) tk_image = ctk.CTkImage(image, size=image.size)
source_image = ctk.CTkLabel(scrollable_frame, text=f"S-{button_num}", width=MAPPER_PREVIEW_MAX_WIDTH, height=MAPPER_PREVIEW_MAX_HEIGHT) source_image = ctk.CTkLabel(
scrollable_frame,
text=f"S-{button_num}",
width=MAPPER_PREVIEW_MAX_WIDTH,
height=MAPPER_PREVIEW_MAX_HEIGHT,
)
source_image.grid(row=button_num, column=1, padx=10, pady=10) source_image.grid(row=button_num, column=1, padx=10, pady=10)
source_image.configure(image=tk_image) source_image.configure(image=tk_image)
source_label_dict_live[button_num] = source_image source_label_dict_live[button_num] = source_image
@ -626,10 +912,17 @@ def update_webcam_source(scrollable_frame: ctk.CTkScrollableFrame, map: list, bu
update_pop_live_status("Face could not be detected in last upload!") update_pop_live_status("Face could not be detected in last upload!")
return map return map
def update_webcam_target(scrollable_frame: ctk.CTkScrollableFrame, map: list, button_num: int) -> list:
def update_webcam_target(
scrollable_frame: ctk.CTkScrollableFrame, map: list, button_num: int
) -> list:
global target_label_dict_live global target_label_dict_live
target_path = ctk.filedialog.askopenfilename(title='select an target image', initialdir=RECENT_DIRECTORY_SOURCE, filetypes=[img_ft]) target_path = ctk.filedialog.askopenfilename(
title="select an target image",
initialdir=RECENT_DIRECTORY_SOURCE,
filetypes=[img_ft],
)
if "target" in map[button_num]: if "target" in map[button_num]:
map[button_num].pop("target") map[button_num].pop("target")
@ -643,22 +936,30 @@ def update_webcam_target(scrollable_frame: ctk.CTkScrollableFrame, map: list, bu
face = get_one_face(cv2_img) face = get_one_face(cv2_img)
if face: if face:
x_min, y_min, x_max, y_max = face['bbox'] x_min, y_min, x_max, y_max = face["bbox"]
map[button_num]['target'] = { map[button_num]["target"] = {
'cv2' : cv2_img[int(y_min):int(y_max), int(x_min):int(x_max)], "cv2": cv2_img[int(y_min) : int(y_max), int(x_min) : int(x_max)],
'face' : face "face": face,
} }
image = Image.fromarray(cv2.cvtColor(map[button_num]['target']['cv2'], cv2.COLOR_BGR2RGB)) image = Image.fromarray(
image = image.resize((MAPPER_PREVIEW_MAX_WIDTH, MAPPER_PREVIEW_MAX_HEIGHT), Image.LANCZOS) cv2.cvtColor(map[button_num]["target"]["cv2"], cv2.COLOR_BGR2RGB)
)
image = image.resize(
(MAPPER_PREVIEW_MAX_WIDTH, MAPPER_PREVIEW_MAX_HEIGHT), Image.LANCZOS
)
tk_image = ctk.CTkImage(image, size=image.size) tk_image = ctk.CTkImage(image, size=image.size)
target_image = ctk.CTkLabel(scrollable_frame, text=f"T-{button_num}", width=MAPPER_PREVIEW_MAX_WIDTH, height=MAPPER_PREVIEW_MAX_HEIGHT) target_image = ctk.CTkLabel(
scrollable_frame,
text=f"T-{button_num}",
width=MAPPER_PREVIEW_MAX_WIDTH,
height=MAPPER_PREVIEW_MAX_HEIGHT,
)
target_image.grid(row=button_num, column=4, padx=20, pady=10) target_image.grid(row=button_num, column=4, padx=20, pady=10)
target_image.configure(image=tk_image) target_image.configure(image=tk_image)
target_label_dict_live[button_num] = target_image target_label_dict_live[button_num] = target_image
else: else:
update_pop_live_status("Face could not be detected in last upload!") update_pop_live_status("Face could not be detected in last upload!")
return map return map