Merge pull request #868 from kier007/main

Update ui.py
pull/869/head
Kenneth Estanislao 2025-01-05 20:33:49 +08:00 committed by GitHub
commit c03f697729
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
1 changed files with 147 additions and 195 deletions

View File

@ -7,6 +7,7 @@ from cv2_enumerate_cameras import enumerate_cameras # Add this import
from PIL import Image, ImageOps from PIL import Image, ImageOps
import time import time
import json import json
import modules.globals import modules.globals
import modules.metadata import modules.metadata
from modules.face_analyser import ( from modules.face_analyser import (
@ -25,11 +26,6 @@ from modules.utilities import (
resolve_relative_path, resolve_relative_path,
has_image_extension, has_image_extension,
) )
from modules.video_capture import VideoCapturer
import platform
if platform.system() == "Windows":
from pygrabber.dshow_graph import FilterGraph
ROOT = None ROOT = None
POPUP = None POPUP = None
@ -100,7 +96,7 @@ def save_switch_states():
"fp_ui": modules.globals.fp_ui, "fp_ui": modules.globals.fp_ui,
"show_fps": modules.globals.show_fps, "show_fps": modules.globals.show_fps,
"mouth_mask": modules.globals.mouth_mask, "mouth_mask": modules.globals.mouth_mask,
"show_mouth_mask_box": modules.globals.show_mouth_mask_box, "show_mouth_mask_box": modules.globals.show_mouth_mask_box
} }
with open("switch_states.json", "w") as f: with open("switch_states.json", "w") as f:
json.dump(switch_states, f) json.dump(switch_states, f)
@ -122,9 +118,7 @@ def load_switch_states():
modules.globals.fp_ui = switch_states.get("fp_ui", {"face_enhancer": False}) modules.globals.fp_ui = switch_states.get("fp_ui", {"face_enhancer": False})
modules.globals.show_fps = switch_states.get("show_fps", False) modules.globals.show_fps = switch_states.get("show_fps", False)
modules.globals.mouth_mask = switch_states.get("mouth_mask", False) modules.globals.mouth_mask = switch_states.get("mouth_mask", False)
modules.globals.show_mouth_mask_box = switch_states.get( modules.globals.show_mouth_mask_box = switch_states.get("show_mouth_mask_box", False)
"show_mouth_mask_box", False
)
except FileNotFoundError: except FileNotFoundError:
# If the file doesn't exist, use default values # If the file doesn't exist, use default values
pass pass
@ -321,22 +315,18 @@ def create_root(start: Callable[[], None], destroy: Callable[[], None]) -> ctk.C
camera_label.place(relx=0.1, rely=0.86, relwidth=0.2, relheight=0.05) camera_label.place(relx=0.1, rely=0.86, relwidth=0.2, relheight=0.05)
available_cameras = get_available_cameras() available_cameras = get_available_cameras()
camera_indices, camera_names = available_cameras # Convert camera indices to strings for CTkOptionMenu
available_camera_indices, available_camera_strings = available_cameras
if not camera_names or camera_names[0] == "No cameras found": camera_variable = ctk.StringVar(
camera_variable = ctk.StringVar(value="No cameras found") value=(
camera_optionmenu = ctk.CTkOptionMenu( available_camera_strings[0]
root, if available_camera_strings
variable=camera_variable, else "No cameras found"
values=["No cameras found"],
state="disabled",
) )
else: )
camera_variable = ctk.StringVar(value=camera_names[0]) camera_optionmenu = ctk.CTkOptionMenu(
camera_optionmenu = ctk.CTkOptionMenu( root, variable=camera_variable, values=available_camera_strings
root, variable=camera_variable, values=camera_names )
)
camera_optionmenu.place(relx=0.35, rely=0.86, relwidth=0.25, relheight=0.05) camera_optionmenu.place(relx=0.35, rely=0.86, relwidth=0.25, relheight=0.05)
live_button = ctk.CTkButton( live_button = ctk.CTkButton(
@ -345,16 +335,9 @@ def create_root(start: Callable[[], None], destroy: Callable[[], None]) -> ctk.C
cursor="hand2", cursor="hand2",
command=lambda: webcam_preview( command=lambda: webcam_preview(
root, root,
( available_camera_indices[
camera_indices[camera_names.index(camera_variable.get())] available_camera_strings.index(camera_variable.get())
if camera_names and camera_names[0] != "No cameras found" ],
else None
),
),
state=(
"normal"
if camera_names and camera_names[0] != "No cameras found"
else "disabled"
), ),
) )
live_button.place(relx=0.65, rely=0.86, relwidth=0.2, relheight=0.05) live_button.place(relx=0.65, rely=0.86, relwidth=0.2, relheight=0.05)
@ -371,7 +354,7 @@ def create_root(start: Callable[[], None], destroy: Callable[[], None]) -> ctk.C
text_color=ctk.ThemeManager.theme.get("URL").get("text_color") text_color=ctk.ThemeManager.theme.get("URL").get("text_color")
) )
donate_label.bind( donate_label.bind(
"<Button>", lambda event: webbrowser.open("https://deeplivecam.net") "<Button>", lambda event: webbrowser.open("https://paypal.me/hacksider")
) )
return root return root
@ -412,8 +395,8 @@ def create_source_target_popup(
def on_submit_click(start): def on_submit_click(start):
if has_valid_map(): if has_valid_map():
POPUP.destroy() simplify_maps()
select_output_path(start) update_pop_live_status("Mappings submitted successfully!")
else: else:
update_pop_status("Atleast 1 source with target is required!") update_pop_status("Atleast 1 source with target is required!")
@ -760,9 +743,15 @@ def update_preview(frame_number: int = 0) -> None:
def webcam_preview(root: ctk.CTk, camera_index: int): def webcam_preview(root: ctk.CTk, camera_index: int):
global POPUP_LIVE
if POPUP_LIVE is not None and POPUP_LIVE.winfo_exists():
POPUP_LIVE.focus()
return
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:
update_status("Please select a source image first") # No image selected
return return
create_webcam_preview(camera_index) create_webcam_preview(camera_index)
else: else:
@ -774,164 +763,108 @@ def webcam_preview(root: ctk.CTk, camera_index: int):
def get_available_cameras(): def get_available_cameras():
"""Returns a list of available camera names and indices.""" """Returns a list of available camera names and indices."""
if platform.system() == "Windows": camera_indices = []
try: camera_names = []
graph = FilterGraph()
devices = graph.get_input_devices()
# Create list of indices and names for camera in enumerate_cameras():
camera_indices = list(range(len(devices))) cap = cv2.VideoCapture(camera.index)
camera_names = devices if cap.isOpened():
camera_indices.append(camera.index)
# If no cameras found through DirectShow, try OpenCV fallback camera_names.append(camera.name)
if not camera_names: cap.release()
# Try to open camera with index -1 and 0 return (camera_indices, camera_names)
test_indices = [-1, 0]
working_cameras = []
for idx in test_indices:
cap = cv2.VideoCapture(idx)
if cap.isOpened():
working_cameras.append(f"Camera {idx}")
cap.release()
if working_cameras:
return test_indices[: len(working_cameras)], working_cameras
# If still no cameras found, return empty lists
if not camera_names:
return [], ["No cameras found"]
return camera_indices, camera_names
except Exception as e:
print(f"Error detecting cameras: {str(e)}")
return [], ["No cameras found"]
else:
# Unix-like systems (Linux/Mac) camera detection
camera_indices = []
camera_names = []
if platform.system() == "Darwin": # macOS specific handling
# Try to open the default FaceTime camera first
cap = cv2.VideoCapture(0)
if cap.isOpened():
camera_indices.append(0)
camera_names.append("FaceTime Camera")
cap.release()
# On macOS, additional cameras typically use indices 1 and 2
for i in [1, 2]:
cap = cv2.VideoCapture(i)
if cap.isOpened():
camera_indices.append(i)
camera_names.append(f"Camera {i}")
cap.release()
else:
# Linux camera detection - test first 10 indices
for i in range(10):
cap = cv2.VideoCapture(i)
if cap.isOpened():
camera_indices.append(i)
camera_names.append(f"Camera {i}")
cap.release()
if not camera_names:
return [], ["No cameras found"]
return camera_indices, camera_names
def create_webcam_preview(camera_index: int): def create_webcam_preview(camera_index: int):
global preview_label, PREVIEW global preview_label, PREVIEW
cap = VideoCapturer(camera_index) camera = cv2.VideoCapture(camera_index)
if not cap.start(PREVIEW_DEFAULT_WIDTH, PREVIEW_DEFAULT_HEIGHT, 60): camera.set(cv2.CAP_PROP_FRAME_WIDTH, PREVIEW_DEFAULT_WIDTH)
update_status("Failed to start camera") camera.set(cv2.CAP_PROP_FRAME_HEIGHT, PREVIEW_DEFAULT_HEIGHT)
return camera.set(cv2.CAP_PROP_FPS, 60)
preview_label.configure(width=PREVIEW_DEFAULT_WIDTH, height=PREVIEW_DEFAULT_HEIGHT) preview_label.configure(width=PREVIEW_DEFAULT_WIDTH, height=PREVIEW_DEFAULT_HEIGHT)
PREVIEW.deiconify() PREVIEW.deiconify()
frame_processors = get_frame_processors_modules(modules.globals.frame_processors) frame_processors = get_frame_processors_modules(modules.globals.frame_processors)
source_image = None source_image = None
prev_time = time.time() prev_time = time.time()
fps_update_interval = 0.5 fps_update_interval = 0.5 # Update FPS every 0.5 seconds
frame_count = 0 frame_count = 0
fps = 0 fps = 0
while True: try:
ret, frame = cap.read() while camera:
if not ret: ret, frame = camera.read()
break if not ret:
break
temp_frame = frame.copy() temp_frame = frame.copy()
if modules.globals.live_mirror: if modules.globals.live_mirror:
temp_frame = cv2.flip(temp_frame, 1) temp_frame = cv2.flip(temp_frame, 1)
if modules.globals.live_resizable: if modules.globals.live_resizable:
temp_frame = fit_image_to_size( temp_frame = fit_image_to_size(
temp_frame, PREVIEW.winfo_width(), PREVIEW.winfo_height() temp_frame, PREVIEW.winfo_width(), PREVIEW.winfo_height()
) )
else: if not modules.globals.map_faces:
temp_frame = fit_image_to_size( if source_image is None and modules.globals.source_path:
temp_frame, PREVIEW.winfo_width(), PREVIEW.winfo_height() source_image = get_one_face(cv2.imread(modules.globals.source_path))
)
if not modules.globals.map_faces: for frame_processor in frame_processors:
if source_image is None and modules.globals.source_path: if frame_processor.NAME == "DLC.FACE-ENHANCER":
source_image = get_one_face(cv2.imread(modules.globals.source_path)) if modules.globals.fp_ui["face_enhancer"]:
temp_frame = frame_processor.process_frame(None, temp_frame)
else:
temp_frame = frame_processor.process_frame(source_image, temp_frame)
else:
modules.globals.target_path = None
for frame_processor in frame_processors: for frame_processor in frame_processors:
if frame_processor.NAME == "DLC.FACE-ENHANCER": if frame_processor.NAME == "DLC.FACE-ENHANCER":
if modules.globals.fp_ui["face_enhancer"]: if modules.globals.fp_ui["face_enhancer"]:
temp_frame = frame_processor.process_frame(None, temp_frame) temp_frame = frame_processor.process_frame_v2(temp_frame)
else: else:
temp_frame = frame_processor.process_frame(source_image, temp_frame)
else:
modules.globals.target_path = None
for frame_processor in frame_processors:
if frame_processor.NAME == "DLC.FACE-ENHANCER":
if modules.globals.fp_ui["face_enhancer"]:
temp_frame = frame_processor.process_frame_v2(temp_frame) temp_frame = frame_processor.process_frame_v2(temp_frame)
else:
temp_frame = frame_processor.process_frame_v2(temp_frame)
# Calculate and display FPS # Calculate and display FPS
current_time = time.time() current_time = time.time()
frame_count += 1 frame_count += 1
if current_time - prev_time >= fps_update_interval: if current_time - prev_time >= fps_update_interval:
fps = frame_count / (current_time - prev_time) fps = frame_count / (current_time - prev_time)
frame_count = 0 frame_count = 0
prev_time = current_time prev_time = current_time
if modules.globals.show_fps: if modules.globals.show_fps:
cv2.putText( cv2.putText(
temp_frame, temp_frame,
f"FPS: {fps:.1f}", f"FPS: {fps:.1f}",
(10, 30), (10, 30),
cv2.FONT_HERSHEY_SIMPLEX, cv2.FONT_HERSHEY_SIMPLEX,
1, 1,
(0, 255, 0), (0, 255, 0),
2, 2,
)
image = cv2.cvtColor(temp_frame, cv2.COLOR_BGR2RGB)
image = Image.fromarray(image)
image = ImageOps.contain(
image, (temp_frame.shape[1], temp_frame.shape[0]), Image.LANCZOS
) )
image = ctk.CTkImage(image, size=image.size)
preview_label.configure(image=image)
ROOT.update()
image = cv2.cvtColor(temp_frame, cv2.COLOR_BGR2RGB) if PREVIEW.state() == "withdrawn":
image = Image.fromarray(image) break
image = ImageOps.contain( finally:
image, (temp_frame.shape[1], temp_frame.shape[0]), Image.LANCZOS camera.release()
) PREVIEW.withdraw()
image = ctk.CTkImage(image, size=image.size)
preview_label.configure(image=image)
ROOT.update()
if PREVIEW.state() == "withdrawn":
break
cap.release()
PREVIEW.withdraw()
def create_source_target_popup_for_webcam( def create_source_target_popup_for_webcam(
@ -946,7 +879,6 @@ def create_source_target_popup_for_webcam(
def on_submit_click(): def on_submit_click():
if has_valid_map(): if has_valid_map():
POPUP_LIVE.destroy()
simplify_maps() simplify_maps()
create_webcam_preview(camera_index) create_webcam_preview(camera_index)
else: else:
@ -957,16 +889,35 @@ def create_source_target_popup_for_webcam(
refresh_data(map) refresh_data(map)
update_pop_live_status("Please provide mapping!") update_pop_live_status("Please provide mapping!")
def on_clear_click():
for item in map:
if "source" in item:
item.pop("source")
if "target" in item:
item.pop("target")
refresh_data(map)
update_pop_live_status("Source and target images cleared.")
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.1, rely=0.92, relwidth=0.2, relheight=0.05)
clear_button = ctk.CTkButton(
POPUP_LIVE,
text="Clear",
command=lambda: on_clear_click(),
state="normal",
)
clear_button.place(relx=0.4, rely=0.92, relwidth=0.15, relheight=0.05)
close_button = ctk.CTkButton( close_button = ctk.CTkButton(
POPUP_LIVE, text="Submit", command=lambda: on_submit_click() 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.7, rely=0.92, relwidth=0.2, relheight=0.05)
refresh_data(map)
def refresh_data(map: list): def refresh_data(map: list):
@ -1013,40 +964,36 @@ def refresh_data(map: list):
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( source_label = ctk.CTkLabel(
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)
source_image = ctk.CTkLabel(
scrollable_frame, scrollable_frame,
text=f"S-{id}", text="",
width=MAPPER_PREVIEW_MAX_WIDTH, width=MAPPER_PREVIEW_MAX_WIDTH,
height=MAPPER_PREVIEW_MAX_HEIGHT, height=MAPPER_PREVIEW_MAX_HEIGHT,
) )
source_image.grid(row=id, column=1, padx=10, pady=10) source_label.grid(row=id, column=1, padx=10, pady=10)
source_image.configure(image=tk_image) else:
ctk.CTkLabel(
scrollable_frame,
text="No Source",
width=MAPPER_PREVIEW_MAX_WIDTH,
height=MAPPER_PREVIEW_MAX_HEIGHT,
).grid(row=id, column=1, padx=10, pady=10)
if "target" in item: if "target" in item:
image = Image.fromarray( target_label = ctk.CTkLabel(
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)
target_image = ctk.CTkLabel(
scrollable_frame, scrollable_frame,
text=f"T-{id}", text="",
width=MAPPER_PREVIEW_MAX_WIDTH, width=MAPPER_PREVIEW_MAX_WIDTH,
height=MAPPER_PREVIEW_MAX_HEIGHT, height=MAPPER_PREVIEW_MAX_HEIGHT,
) )
target_image.grid(row=id, column=4, padx=20, pady=10) target_label.grid(row=id, column=4, padx=20, pady=10)
target_image.configure(image=tk_image) else:
ctk.CTkLabel(
scrollable_frame,
text="No Target",
width=MAPPER_PREVIEW_MAX_WIDTH,
height=MAPPER_PREVIEW_MAX_HEIGHT,
).grid(row=id, column=4, padx=20, pady=10)
def update_webcam_source( def update_webcam_source(
@ -1069,6 +1016,11 @@ def update_webcam_source(
return map return map
else: else:
cv2_img = cv2.imread(source_path) cv2_img = cv2.imread(source_path)
if cv2_img is None:
update_pop_live_status("Failed to load the selected image. Please try again.")
return map
face = get_one_face(cv2_img) face = get_one_face(cv2_img)
if face: if face: