Compare commits
9 Commits
7e75f07879
...
f38ebb485a
Author | SHA1 | Date |
---|---|---|
|
f38ebb485a | |
|
95742c8fd5 | |
|
60e27f4755 | |
|
3d741bd269 | |
|
d4e5b8078d | |
|
61b51fc5d4 | |
|
f19e425143 | |
|
7d6bdad086 | |
|
12c0a7ac86 |
|
@ -371,11 +371,11 @@ For the latest experimental builds and features, see the [experimental branch](h
|
||||||
|
|
||||||
**TODO:**
|
**TODO:**
|
||||||
|
|
||||||
- [x] Support multiple faces
|
|
||||||
- [ ] Develop a version for web app/service
|
- [ ] Develop a version for web app/service
|
||||||
- [ ] UI/UX enhancements for desktop app
|
|
||||||
- [ ] Speed up model loading
|
- [ ] Speed up model loading
|
||||||
- [ ] Speed up real-time face swapping
|
- [ ] Speed up real-time face swapping
|
||||||
|
- [x] Support multiple faces
|
||||||
|
- [x] UI/UX enhancements for desktop app
|
||||||
|
|
||||||
This is an open-source project developed in our free time. Updates may be delayed.
|
This is an open-source project developed in our free time. Updates may be delayed.
|
||||||
|
|
||||||
|
|
|
@ -34,4 +34,5 @@ headless = None
|
||||||
log_level = 'error'
|
log_level = 'error'
|
||||||
fp_ui: Dict[str, bool] = {}
|
fp_ui: Dict[str, bool] = {}
|
||||||
camera_input_combobox = None
|
camera_input_combobox = None
|
||||||
webcam_preview_running = False
|
webcam_preview_running = False
|
||||||
|
opacity = 100
|
||||||
|
|
130
modules/ui.py
130
modules/ui.py
|
@ -3,6 +3,7 @@ import webbrowser
|
||||||
import customtkinter as ctk
|
import customtkinter as ctk
|
||||||
from typing import Callable, Tuple
|
from typing import Callable, Tuple
|
||||||
import cv2
|
import cv2
|
||||||
|
from cv2_enumerate_cameras import enumerate_cameras
|
||||||
from PIL import Image, ImageOps
|
from PIL import Image, ImageOps
|
||||||
import tkinterdnd2 as tkdnd
|
import tkinterdnd2 as tkdnd
|
||||||
import time
|
import time
|
||||||
|
@ -25,6 +26,7 @@ from modules.utilities import (
|
||||||
has_image_extension,
|
has_image_extension,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
modules.globals.face_opacity = 100
|
||||||
os.environ["QT_AUTO_SCREEN_SCALE_FACTOR"] = "1"
|
os.environ["QT_AUTO_SCREEN_SCALE_FACTOR"] = "1"
|
||||||
os.environ["QT_SCREEN_SCALE_FACTORS"] = "1"
|
os.environ["QT_SCREEN_SCALE_FACTORS"] = "1"
|
||||||
os.environ["QT_SCALE_FACTOR"] = "1"
|
os.environ["QT_SCALE_FACTOR"] = "1"
|
||||||
|
@ -402,11 +404,33 @@ def create_root(
|
||||||
)
|
)
|
||||||
preview_button.pack(side="left", padx=10, expand=True)
|
preview_button.pack(side="left", padx=10, expand=True)
|
||||||
|
|
||||||
|
# --- Camera Selection ---
|
||||||
|
camera_label = ctk.CTkLabel(root, text="Select Camera:")
|
||||||
|
camera_label.place(relx=0.4, rely=0.86, relwidth=0.2, relheight=0.05)
|
||||||
|
available_cameras = get_available_cameras()
|
||||||
|
# Convert camera indices to strings for CTkOptionMenu
|
||||||
|
available_camera_indices, available_camera_strings = available_cameras
|
||||||
|
camera_variable = ctk.StringVar(
|
||||||
|
value=available_camera_strings[0]
|
||||||
|
if available_camera_strings
|
||||||
|
else "No cameras found"
|
||||||
|
)
|
||||||
|
camera_optionmenu = ctk.CTkOptionMenu(
|
||||||
|
root, variable=camera_variable, values=available_camera_strings
|
||||||
|
)
|
||||||
|
camera_optionmenu.place(relx=0.65, rely=0.86, relwidth=0.2, relheight=0.05)
|
||||||
|
# --- End Camera Selection ---
|
||||||
|
|
||||||
live_button = ModernButton(
|
live_button = ModernButton(
|
||||||
button_frame,
|
button_frame,
|
||||||
text="Live",
|
text="Live",
|
||||||
cursor="hand2",
|
cursor="hand2",
|
||||||
command=lambda: webcam_preview(root),
|
command=lambda: webcam_preview(
|
||||||
|
root,
|
||||||
|
available_camera_indices[
|
||||||
|
available_camera_strings.index(camera_variable.get())
|
||||||
|
],
|
||||||
|
),
|
||||||
)
|
)
|
||||||
live_button.pack(side="left", padx=10, expand=True)
|
live_button.pack(side="left", padx=10, expand=True)
|
||||||
|
|
||||||
|
@ -682,29 +706,6 @@ def create_preview(parent: ctk.CTkToplevel) -> ctk.CTkToplevel:
|
||||||
)
|
)
|
||||||
preview_slider.pack(fill="x", padx=20, pady=10)
|
preview_slider.pack(fill="x", padx=20, pady=10)
|
||||||
|
|
||||||
last_update_time = 0
|
|
||||||
debounce_delay = 0.1 # Adjust this delay as needed (in seconds)
|
|
||||||
|
|
||||||
def on_key_press(event):
|
|
||||||
nonlocal last_update_time
|
|
||||||
|
|
||||||
current_time = time.time()
|
|
||||||
if current_time - last_update_time > debounce_delay:
|
|
||||||
current_frame = int(preview_slider.get())
|
|
||||||
if event.keysym == "Left":
|
|
||||||
new_frame = max(0, current_frame - 1)
|
|
||||||
elif event.keysym == "Right":
|
|
||||||
new_frame = min(int(preview_slider.cget("to")), current_frame + 1)
|
|
||||||
else:
|
|
||||||
return # Ignore other key presses
|
|
||||||
|
|
||||||
preview_slider.set(new_frame)
|
|
||||||
update_preview(new_frame)
|
|
||||||
last_update_time = current_time
|
|
||||||
|
|
||||||
preview.bind("<Left>", on_key_press)
|
|
||||||
preview.bind("<Right>", on_key_press)
|
|
||||||
|
|
||||||
return preview
|
return preview
|
||||||
|
|
||||||
|
|
||||||
|
@ -902,11 +903,6 @@ def init_preview() -> None:
|
||||||
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)
|
||||||
# Disable slider if it's an image
|
|
||||||
if is_image(modules.globals.target_path):
|
|
||||||
preview_slider.configure(state="disabled")
|
|
||||||
else:
|
|
||||||
preview_slider.configure(state="normal")
|
|
||||||
|
|
||||||
|
|
||||||
def update_preview(frame_number: int = 0) -> None:
|
def update_preview(frame_number: int = 0) -> None:
|
||||||
|
@ -959,42 +955,75 @@ def update_preview(frame_number: int = 0) -> None:
|
||||||
PREVIEW.deiconify()
|
PREVIEW.deiconify()
|
||||||
|
|
||||||
|
|
||||||
def webcam_preview(root: ctk.CTk):
|
def webcam_preview(root: ctk.CTk, camera_index: int):
|
||||||
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:
|
||||||
# No image selected
|
# No image selected
|
||||||
return
|
return
|
||||||
create_webcam_preview()
|
create_webcam_preview(camera_index)
|
||||||
else:
|
else:
|
||||||
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 get_available_cameras():
|
||||||
|
"""Returns a list of available camera names and indices."""
|
||||||
|
camera_indices = []
|
||||||
|
camera_names = []
|
||||||
|
|
||||||
|
for camera in enumerate_cameras():
|
||||||
|
cap = cv2.VideoCapture(camera.index)
|
||||||
|
if cap.isOpened():
|
||||||
|
camera_indices.append(camera.index)
|
||||||
|
camera_names.append(camera.name)
|
||||||
|
cap.release()
|
||||||
|
return (camera_indices, camera_names)
|
||||||
|
|
||||||
|
|
||||||
|
# Add this function to update the opacity value
|
||||||
|
def update_opacity(value):
|
||||||
|
modules.globals.face_opacity = int(value)
|
||||||
|
|
||||||
|
|
||||||
|
# Modify the create_webcam_preview function to include the slider
|
||||||
|
def create_webcam_preview(camera_index):
|
||||||
global preview_label, PREVIEW
|
global preview_label, PREVIEW
|
||||||
stream_url = 'http://192.168.0.103:4747'
|
|
||||||
camera = cv2.VideoCapture(
|
|
||||||
# stream_url
|
|
||||||
#7
|
|
||||||
1, cv2.CAP_DSHOW
|
|
||||||
) # 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
|
|
||||||
|
|
||||||
preview_label.configure(
|
camera = cv2.VideoCapture(camera_index)
|
||||||
width=PREVIEW_DEFAULT_WIDTH, height=PREVIEW_DEFAULT_HEIGHT
|
if not camera.isOpened():
|
||||||
) # Reset the preview image before startup
|
update_status(f"Error: Could not open camera with index {camera_index}")
|
||||||
|
return
|
||||||
|
camera.set(cv2.CAP_PROP_FRAME_WIDTH, PREVIEW_DEFAULT_WIDTH)
|
||||||
|
camera.set(cv2.CAP_PROP_FRAME_HEIGHT, PREVIEW_DEFAULT_HEIGHT)
|
||||||
|
camera.set(cv2.CAP_PROP_FPS, 60)
|
||||||
|
|
||||||
PREVIEW.deiconify() # Open preview window
|
PREVIEW.deiconify()
|
||||||
|
|
||||||
|
# Remove any existing widgets in PREVIEW window
|
||||||
|
for widget in PREVIEW.winfo_children():
|
||||||
|
widget.destroy()
|
||||||
|
|
||||||
|
# Create a main frame to hold all widgets
|
||||||
|
main_frame = ctk.CTkFrame(PREVIEW)
|
||||||
|
main_frame.pack(fill="both", expand=True)
|
||||||
|
|
||||||
|
# Create a frame for the preview label
|
||||||
|
preview_frame = ctk.CTkFrame(main_frame)
|
||||||
|
preview_frame.pack(fill="both", expand=True, padx=10, pady=10)
|
||||||
|
|
||||||
|
preview_label = ctk.CTkLabel(preview_frame, text="")
|
||||||
|
preview_label.pack(fill="both", expand=True)
|
||||||
|
|
||||||
frame_processors = get_frame_processors_modules(modules.globals.frame_processors)
|
frame_processors = get_frame_processors_modules(modules.globals.frame_processors)
|
||||||
|
|
||||||
source_image = None # Initialize variable for the selected face image
|
source_image = None
|
||||||
|
|
||||||
|
def update_frame_size(event):
|
||||||
|
nonlocal temp_frame
|
||||||
|
if modules.globals.live_resizable:
|
||||||
|
temp_frame = fit_image_to_size(temp_frame, event.width, event.height)
|
||||||
|
|
||||||
|
preview_frame.bind("<Configure>", update_frame_size)
|
||||||
|
|
||||||
while camera:
|
while camera:
|
||||||
ret, frame = camera.read()
|
ret, frame = camera.read()
|
||||||
|
@ -1012,7 +1041,6 @@ def create_webcam_preview():
|
||||||
)
|
)
|
||||||
|
|
||||||
if not modules.globals.map_faces:
|
if not modules.globals.map_faces:
|
||||||
# Select and save face image only once
|
|
||||||
if source_image is None and modules.globals.source_path:
|
if source_image is None and modules.globals.source_path:
|
||||||
source_image = get_one_face(cv2.imread(modules.globals.source_path))
|
source_image = get_one_face(cv2.imread(modules.globals.source_path))
|
||||||
|
|
||||||
|
|
|
@ -2,6 +2,7 @@
|
||||||
|
|
||||||
numpy>=1.23.5,<2
|
numpy>=1.23.5,<2
|
||||||
opencv-python==4.8.1.78
|
opencv-python==4.8.1.78
|
||||||
|
cv2_enumerate_cameras==1.1.15
|
||||||
onnx==1.16.0
|
onnx==1.16.0
|
||||||
insightface==0.7.3
|
insightface==0.7.3
|
||||||
psutil==5.9.8
|
psutil==5.9.8
|
||||||
|
|
Loading…
Reference in New Issue