From c08bec22e3037c0df2fd6710d6b293d2e4b9f8d0 Mon Sep 17 00:00:00 2001 From: Kenneth Estanislao Date: Tue, 1 Oct 2024 14:26:03 +0800 Subject: [PATCH 1/8] Update issue templates --- .github/ISSUE_TEMPLATE/bug_report.md | 38 ++++++++++++++++++++++++++++ 1 file changed, 38 insertions(+) create mode 100644 .github/ISSUE_TEMPLATE/bug_report.md diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md new file mode 100644 index 0000000..dd84ea7 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -0,0 +1,38 @@ +--- +name: Bug report +about: Create a report to help us improve +title: '' +labels: '' +assignees: '' + +--- + +**Describe the bug** +A clear and concise description of what the bug is. + +**To Reproduce** +Steps to reproduce the behavior: +1. Go to '...' +2. Click on '....' +3. Scroll down to '....' +4. See error + +**Expected behavior** +A clear and concise description of what you expected to happen. + +**Screenshots** +If applicable, add screenshots to help explain your problem. + +**Desktop (please complete the following information):** + - OS: [e.g. iOS] + - Browser [e.g. chrome, safari] + - Version [e.g. 22] + +**Smartphone (please complete the following information):** + - Device: [e.g. iPhone6] + - OS: [e.g. iOS8.1] + - Browser [e.g. stock browser, safari] + - Version [e.g. 22] + +**Additional context** +Add any other context about the problem here. From 12c0a7ac868683bb80004835bb8f5616ff6cd0fb Mon Sep 17 00:00:00 2001 From: KRSHH <136873090+KRSHH@users.noreply.github.com> Date: Wed, 2 Oct 2024 13:23:39 +0530 Subject: [PATCH 2/8] Faceswap live opacity slider --- modules/ui.py | 58 +++++++++++++++++++++++++++++++-------------------- 1 file changed, 35 insertions(+), 23 deletions(-) diff --git a/modules/ui.py b/modules/ui.py index ec2210a..6ce27ae 100644 --- a/modules/ui.py +++ b/modules/ui.py @@ -971,38 +971,45 @@ def webcam_preview(root: ctk.CTk): def create_webcam_preview(): - global preview_label, PREVIEW + global preview_label, PREVIEW, opacity_slider - camera = cv2.VideoCapture( - 0 - ) # 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 = cv2.VideoCapture(0) + 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_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) - PREVIEW.deiconify() # Open preview window + PREVIEW.deiconify() frame_processors = get_frame_processors_modules(modules.globals.frame_processors) - source_image = None # Initialize variable for the selected face image + source_image = None + + # Create opacity slider + opacity_slider = ctk.CTkSlider( + PREVIEW, + from_=0, + to=100, + number_of_steps=10, # Increment by 10 + command=lambda value: setattr(modules.globals, "opacity", int(value)), + fg_color=("gray75", "gray25"), + progress_color=("DodgerBlue", "DodgerBlue"), + button_color=("DodgerBlue", "DodgerBlue"), + button_hover_color=("RoyalBlue", "RoyalBlue"), + ) + opacity_slider.pack(fill="x", padx=20, pady=(0, 10)) + opacity_slider.set(100) # Default opacity is 100 (fully opaque) while camera: ret, frame = camera.read() if not ret: break - temp_frame = frame.copy() # Create a copy of the frame + temp_frame = frame.copy() if modules.globals.live_mirror: - temp_frame = cv2.flip(temp_frame, 1) # horizontal flipping + temp_frame = cv2.flip(temp_frame, 1) if modules.globals.live_resizable: temp_frame = fit_image_to_size( @@ -1010,7 +1017,6 @@ def create_webcam_preview(): ) if not modules.globals.map_faces: - # Select and save face image only once if source_image is None and modules.globals.source_path: source_image = get_one_face(cv2.imread(modules.globals.source_path)) @@ -1022,9 +1028,12 @@ def create_webcam_preview(): for frame_processor in frame_processors: 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 + # Apply opacity blending (if needed) + if modules.globals.opacity < 100: + alpha = modules.globals.opacity / 100.0 + temp_frame = cv2.addWeighted(frame, 1 - alpha, temp_frame, alpha, 0) + + 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 @@ -1037,7 +1046,10 @@ def create_webcam_preview(): break camera.release() - PREVIEW.withdraw() # Close preview window when loop is finished + PREVIEW.withdraw() + + # Remove opacity slider when webcam preview is closed + opacity_slider.pack_forget() def create_source_target_popup_for_webcam(root: ctk.CTk, map: list) -> None: From 7d6bdad086a7062fb43e39f2450919e1ec2f4596 Mon Sep 17 00:00:00 2001 From: KRSHH <136873090+KRSHH@users.noreply.github.com> Date: Wed, 2 Oct 2024 13:24:23 +0530 Subject: [PATCH 3/8] Default opacity global --- modules/globals.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/modules/globals.py b/modules/globals.py index 16ed2b5..f1bed22 100644 --- a/modules/globals.py +++ b/modules/globals.py @@ -34,4 +34,5 @@ headless = None log_level = 'error' fp_ui: Dict[str, bool] = {} camera_input_combobox = None -webcam_preview_running = False \ No newline at end of file +webcam_preview_running = False +opacity = 100 From f19e4251439cedc96264ca6e8c5518a111ed4501 Mon Sep 17 00:00:00 2001 From: KRSHH <136873090+KRSHH@users.noreply.github.com> Date: Wed, 2 Oct 2024 14:20:56 +0530 Subject: [PATCH 4/8] Update ui.py --- modules/ui.py | 79 +++++++++++++++++++++++++++++++++++---------------- 1 file changed, 54 insertions(+), 25 deletions(-) diff --git a/modules/ui.py b/modules/ui.py index 6ce27ae..8647017 100644 --- a/modules/ui.py +++ b/modules/ui.py @@ -25,6 +25,7 @@ from modules.utilities import ( has_image_extension, ) +modules.globals.face_opacity = 100 os.environ["QT_AUTO_SCREEN_SCALE_FACTOR"] = "1" os.environ["QT_SCREEN_SCALE_FACTORS"] = "1" os.environ["QT_SCALE_FACTOR"] = "1" @@ -970,36 +971,68 @@ def webcam_preview(root: ctk.CTk): create_source_target_popup_for_webcam(root, modules.globals.souce_target_map) +# 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(): - global preview_label, PREVIEW, opacity_slider + global preview_label, PREVIEW camera = cv2.VideoCapture(0) 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_label.configure(width=PREVIEW_DEFAULT_WIDTH, height=PREVIEW_DEFAULT_HEIGHT) - PREVIEW.deiconify() - frame_processors = get_frame_processors_modules(modules.globals.frame_processors) + # Remove any existing widgets in PREVIEW window + for widget in PREVIEW.winfo_children(): + widget.destroy() - source_image = None + # 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) + + # Create a frame for the slider + slider_frame = ctk.CTkFrame(main_frame) + slider_frame.pack(fill="x", padx=10, pady=(0, 10)) + + opacity_label = ctk.CTkLabel(slider_frame, text="Face Opacity:") + opacity_label.pack(side="left", padx=(0, 10)) - # Create opacity slider opacity_slider = ctk.CTkSlider( - PREVIEW, + slider_frame, from_=0, to=100, - number_of_steps=10, # Increment by 10 - command=lambda value: setattr(modules.globals, "opacity", int(value)), + number_of_steps=10, + command=update_opacity, fg_color=("gray75", "gray25"), progress_color=("DodgerBlue", "DodgerBlue"), button_color=("DodgerBlue", "DodgerBlue"), button_hover_color=("RoyalBlue", "RoyalBlue"), ) - opacity_slider.pack(fill="x", padx=20, pady=(0, 10)) - opacity_slider.set(100) # Default opacity is 100 (fully opaque) + opacity_slider.pack(side="left", fill="x", expand=True) + opacity_slider.set(modules.globals.face_opacity) + + frame_processors = get_frame_processors_modules(modules.globals.frame_processors) + + 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("", update_frame_size) while camera: ret, frame = camera.read() @@ -1011,32 +1044,31 @@ def create_webcam_preview(): if modules.globals.live_mirror: temp_frame = cv2.flip(temp_frame, 1) - 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 source_image is None and 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: 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: modules.globals.target_path = None for frame_processor in frame_processors: temp_frame = frame_processor.process_frame_v2(temp_frame) - # Apply opacity blending (if needed) - if modules.globals.opacity < 100: - alpha = modules.globals.opacity / 100.0 - temp_frame = cv2.addWeighted(frame, 1 - alpha, temp_frame, alpha, 0) - 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, + (preview_frame.winfo_width(), preview_frame.winfo_height()), + Image.LANCZOS, ) image = ctk.CTkImage(image, size=image.size) preview_label.configure(image=image) @@ -1048,9 +1080,6 @@ def create_webcam_preview(): camera.release() PREVIEW.withdraw() - # Remove opacity slider when webcam preview is closed - opacity_slider.pack_forget() - def create_source_target_popup_for_webcam(root: ctk.CTk, map: list) -> None: global POPUP_LIVE, popup_status_label_live From 61b51fc5d4dbf28119174099c0c93178fc0d5922 Mon Sep 17 00:00:00 2001 From: KRSHH <136873090+KRSHH@users.noreply.github.com> Date: Wed, 2 Oct 2024 14:37:19 +0530 Subject: [PATCH 5/8] Move the slider from live to root --- modules/ui.py | 44 +++++++++++++++++++++++--------------------- 1 file changed, 23 insertions(+), 21 deletions(-) diff --git a/modules/ui.py b/modules/ui.py index 8647017..0145410 100644 --- a/modules/ui.py +++ b/modules/ui.py @@ -455,6 +455,29 @@ def create_root( ) 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_rowconfigure((0, 1, 2), weight=1) @@ -1002,27 +1025,6 @@ def create_webcam_preview(): preview_label = ctk.CTkLabel(preview_frame, text="") preview_label.pack(fill="both", expand=True) - # Create a frame for the slider - slider_frame = ctk.CTkFrame(main_frame) - slider_frame.pack(fill="x", padx=10, pady=(0, 10)) - - opacity_label = ctk.CTkLabel(slider_frame, text="Face Opacity:") - opacity_label.pack(side="left", padx=(0, 10)) - - opacity_slider = ctk.CTkSlider( - slider_frame, - from_=0, - to=100, - number_of_steps=10, - command=update_opacity, - fg_color=("gray75", "gray25"), - progress_color=("DodgerBlue", "DodgerBlue"), - button_color=("DodgerBlue", "DodgerBlue"), - button_hover_color=("RoyalBlue", "RoyalBlue"), - ) - opacity_slider.pack(side="left", fill="x", expand=True) - opacity_slider.set(modules.globals.face_opacity) - frame_processors = get_frame_processors_modules(modules.globals.frame_processors) source_image = None From 3d741bd269004ab82c329d7a4ede538b7bfffa68 Mon Sep 17 00:00:00 2001 From: KRSHH <136873090+KRSHH@users.noreply.github.com> Date: Wed, 2 Oct 2024 18:38:37 +0530 Subject: [PATCH 6/8] Update README.md --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index af73c7c..973d845 100644 --- a/README.md +++ b/README.md @@ -371,11 +371,11 @@ For the latest experimental builds and features, see the [experimental branch](h **TODO:** -- [x] Support multiple faces - [ ] Develop a version for web app/service -- [ ] UI/UX enhancements for desktop app - [ ] Speed up model loading - [ ] 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. From fcc2d0582f466d3b68787c19b8c83505446919fc Mon Sep 17 00:00:00 2001 From: GhoulBoii Date: Wed, 2 Oct 2024 20:32:48 +0530 Subject: [PATCH 7/8] feat(ui): add camera switcher --- modules/ui.py | 42 ++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 40 insertions(+), 2 deletions(-) diff --git a/modules/ui.py b/modules/ui.py index 0145410..f6c3924 100644 --- a/modules/ui.py +++ b/modules/ui.py @@ -411,6 +411,30 @@ def create_root( ) live_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_strings = [str(cam) for cam in 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) + live_button = ctk.CTkButton( + root, + text="Live", + cursor="hand2", + command=lambda: webcam_preview(int(camera_variable.get())), + ) + live_button.place(relx=0.15, rely=0.86, relwidth=0.2, relheight=0.05) + # --- End Camera Selection --- + stop_button = ModernButton( button_frame, text="Destroy", @@ -983,7 +1007,7 @@ def update_preview(frame_number: int = 0) -> None: PREVIEW.deiconify() -def webcam_preview(root: ctk.CTk): +def webcam_preview(root: ctk.CTk, camera_index: int): if not modules.globals.map_faces: if modules.globals.source_path is None: # No image selected @@ -994,6 +1018,17 @@ def webcam_preview(root: ctk.CTk): create_source_target_popup_for_webcam(root, modules.globals.souce_target_map) +def get_available_cameras(): + """Returns a list of available camera indices.""" + available_cameras = [] + for index in range(10): # Check for cameras with index 0 to 9 + camera = cv2.VideoCapture(index) + if camera.isOpened(): + available_cameras.append(index) + camera.release() + return available_cameras + + # Add this function to update the opacity value def update_opacity(value): modules.globals.face_opacity = int(value) @@ -1003,7 +1038,10 @@ def update_opacity(value): def create_webcam_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_HEIGHT, PREVIEW_DEFAULT_HEIGHT) camera.set(cv2.CAP_PROP_FPS, 60) From 874ccbbbe025d06bc65b9ea97e044d45d547e5c0 Mon Sep 17 00:00:00 2001 From: Kshamendra Date: Wed, 2 Oct 2024 20:52:25 +0530 Subject: [PATCH 8/8] fix(ui): making get_available_cameras() dynamic --- modules/ui.py | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/modules/ui.py b/modules/ui.py index f6c3924..7a6cbe4 100644 --- a/modules/ui.py +++ b/modules/ui.py @@ -414,7 +414,7 @@ def create_root( # --- 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() + available_cameras = get_available_cameras(10) # Convert camera indices to strings for CTkOptionMenu available_camera_strings = [str(cam) for cam in available_cameras] camera_variable = ctk.StringVar( @@ -1018,14 +1018,14 @@ def webcam_preview(root: ctk.CTk, camera_index: int): create_source_target_popup_for_webcam(root, modules.globals.souce_target_map) -def get_available_cameras(): +def get_available_cameras(max_cameras=10): """Returns a list of available camera indices.""" available_cameras = [] - for index in range(10): # Check for cameras with index 0 to 9 - camera = cv2.VideoCapture(index) - if camera.isOpened(): - available_cameras.append(index) - camera.release() + for i in range(max_cameras): + cap = cv2.VideoCapture(i) + if cap.isOpened(): + available_cameras.append(i) + cap.release() return available_cameras