Compare commits

...

5 Commits

Author SHA1 Message Date
Kenneth Estanislao f38ebb485a Update ui.py
removed opacity, will work on it later to optimize
2024-10-04 15:39:08 +08:00
Kenneth Estanislao 95742c8fd5 Merge pull request #686 from GhoulBoii/main
BOUNTY - Webcam Merged (tested)
2024-10-04 14:46:06 +08:00
Kenneth Estanislao 60e27f4755 Revert "Merge pull request #685 from KRSHH/main"
This reverts commit d4e5b8078d, reversing
changes made to c08bec22e3.
2024-10-03 14:51:38 +08:00
KRSHH 3d741bd269
Update README.md 2024-10-02 18:38:37 +05:30
KRSHH d4e5b8078d
Merge pull request #685 from KRSHH/main
Live faceswap opacity slider
2024-10-02 15:24:53 +05:30
3 changed files with 60 additions and 72 deletions

View File

@ -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.

View File

@ -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
@ -403,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)
@ -455,29 +478,6 @@ def create_root(
) )
remove_donate_button.pack(side="right", padx=(10, 0)) remove_donate_button.pack(side="right", padx=(10, 0))
# Add opacity slider
opacity_frame = ctk.CTkFrame(options_column, fg_color="#2a2d2e")
opacity_frame.pack(pady=5, anchor="w", fill="x")
opacity_label = ctk.CTkLabel(
opacity_frame, text="Face Opacity:", font=("Roboto", 14, "bold")
)
opacity_label.pack(side="left", padx=(0, 10))
opacity_slider = ctk.CTkSlider(
opacity_frame,
from_=0,
to=100,
number_of_steps=100,
command=update_opacity,
fg_color=("gray75", "gray25"),
progress_color="#3a7ebf",
button_color="#3a7ebf",
button_hover_color="#2b5d8b",
)
opacity_slider.pack(side="left", fill="x", expand=True)
opacity_slider.set(modules.globals.face_opacity)
main_frame.grid_columnconfigure((0, 2), weight=1) main_frame.grid_columnconfigure((0, 2), weight=1)
main_frame.grid_rowconfigure((0, 1, 2), weight=1) main_frame.grid_rowconfigure((0, 1, 2), weight=1)
@ -706,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
@ -926,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:
@ -983,27 +955,44 @@ 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 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 # Add this function to update the opacity value
def update_opacity(value): def update_opacity(value):
modules.globals.face_opacity = int(value) modules.globals.face_opacity = int(value)
# Modify the create_webcam_preview function to include the slider # Modify the create_webcam_preview function to include the slider
def create_webcam_preview(): def create_webcam_preview(camera_index):
global preview_label, PREVIEW global preview_label, PREVIEW
camera = cv2.VideoCapture(0) camera = cv2.VideoCapture(camera_index)
if not camera.isOpened():
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_WIDTH, PREVIEW_DEFAULT_WIDTH)
camera.set(cv2.CAP_PROP_FRAME_HEIGHT, PREVIEW_DEFAULT_HEIGHT) camera.set(cv2.CAP_PROP_FRAME_HEIGHT, PREVIEW_DEFAULT_HEIGHT)
camera.set(cv2.CAP_PROP_FPS, 60) camera.set(cv2.CAP_PROP_FPS, 60)
@ -1041,36 +1030,34 @@ def create_webcam_preview():
if not ret: if not ret:
break break
temp_frame = frame.copy() temp_frame = frame.copy() # Create a copy of the frame
if modules.globals.live_mirror: if modules.globals.live_mirror:
temp_frame = cv2.flip(temp_frame, 1) temp_frame = cv2.flip(temp_frame, 1) # horizontal flipping
if modules.globals.live_resizable:
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:
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))
original_frame = temp_frame.copy()
for frame_processor in frame_processors: for frame_processor in frame_processors:
temp_frame = frame_processor.process_frame(source_image, temp_frame) temp_frame = frame_processor.process_frame(source_image, temp_frame)
# Apply opacity
opacity = modules.globals.face_opacity / 100
temp_frame = cv2.addWeighted(
temp_frame, opacity, original_frame, 1 - opacity, 0
)
else: else:
modules.globals.target_path = None modules.globals.target_path = None
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) 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 = ImageOps.contain(
image, image, (temp_frame.shape[1], temp_frame.shape[0]), Image.LANCZOS
(preview_frame.winfo_width(), preview_frame.winfo_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)
@ -1080,7 +1067,7 @@ def create_webcam_preview():
break break
camera.release() camera.release()
PREVIEW.withdraw() PREVIEW.withdraw() # Close preview window when loop is finished
def create_source_target_popup_for_webcam(root: ctk.CTk, map: list) -> None: def create_source_target_popup_for_webcam(root: ctk.CTk, map: list) -> None:

View File

@ -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