pull/1298/merge
rehanbgmi 2025-06-24 07:47:00 +01:00 committed by GitHub
commit 52ff425ec8
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
18 changed files with 1452 additions and 470 deletions

123
README.md
View File

@ -134,12 +134,57 @@ Place these files in the "**models**" folder.
We highly recommend using a `venv` to avoid issues.
For Windows:
```bash
python -m venv venv
venv\Scripts\activate
pip install -r requirements.txt
```
**For Windows:**
It is highly recommended to use Python 3.10 for Windows for best compatibility with all features and dependencies.
**Automated Setup (Recommended):**
1. **Run the setup script:**
Double-click `setup_windows.bat` or run it from your command prompt:
```batch
setup_windows.bat
```
This script will:
* Check if Python is in your PATH.
* Warn if `ffmpeg` is not found (see "Manual Steps / Notes" below for ffmpeg help).
* Create a virtual environment named `.venv` (consistent with macOS setup).
* Activate the virtual environment for the script's session.
* Upgrade pip.
* Install Python packages from `requirements.txt`.
Wait for the script to complete. It will pause at the end; press any key to close the window if you double-clicked it.
2. **Run the application:**
After setup, use the provided `.bat` scripts to run the application. These scripts automatically activate the correct virtual environment:
* `run_windows.bat`: Runs the application with the CPU execution provider by default. This is a good starting point if you don't have a dedicated GPU or are unsure.
* `run-cuda.bat`: Runs with the CUDA (NVIDIA GPU) execution provider. Requires an NVIDIA GPU and CUDA Toolkit installed (see GPU Acceleration section).
* `run-directml.bat`: Runs with the DirectML (AMD/Intel GPU on Windows) execution provider.
Example: Double-click `run_windows.bat` to launch the UI, or run from a command prompt:
```batch
run_windows.bat --source path\to\your_face.jpg --target path\to\video.mp4
```
**Manual Steps / Notes:**
* **Python:** Ensure Python 3.10 is installed and added to your system's PATH. You can download it from [python.org](https://www.python.org/downloads/).
* **ffmpeg:**
* `ffmpeg` is required for video processing. The `setup_windows.bat` script will warn if it's not found in your PATH.
* An easy way to install `ffmpeg` on Windows is to open PowerShell as Administrator and run:
```powershell
Set-ExecutionPolicy Bypass -Scope Process -Force; [System.Net.ServicePointManager]::SecurityProtocol = [System.Net.ServicePointManager]::SecurityProtocol -bor 3072; iex ((New-Object System.Net.WebClient).DownloadString('https://community.chocolatey.org/install.ps1')); choco install ffmpeg -y
```
Alternatively, download from [ffmpeg.org](https://ffmpeg.org/download.html), extract the files, and add the `bin` folder (containing `ffmpeg.exe`) to your system's PATH environment variable. The original README also linked to a [YouTube guide](https://www.youtube.com/watch?v=OlNWCpFdVMA) or `iex (irm ffmpeg.tc.ht)` via PowerShell.
* **Visual Studio Runtimes:** If you encounter errors during `pip install` for packages that compile C code (e.g., some scientific computing or image processing libraries), you might need the [Visual Studio Build Tools (or Runtimes)](https://visualstudio.microsoft.com/visual-cpp-build-tools/). Ensure "C++ build tools" (or similar workload) are selected during installation.
* **Virtual Environment (Manual Alternative):** If you prefer to set up the virtual environment manually instead of using `setup_windows.bat`:
```batch
python -m venv .venv
.venv\Scripts\activate.bat
python -m pip install --upgrade pip
python -m pip install -r requirements.txt
```
(The new automated scripts use `.venv` as the folder name for consistency with the macOS setup).
For Linux:
```bash
# Ensure you use the installed Python 3.10
@ -150,22 +195,64 @@ pip install -r requirements.txt
**For macOS:**
Apple Silicon (M1/M2/M3) requires specific setup:
For a streamlined setup on macOS, use the provided shell scripts:
```bash
# Install Python 3.10 (specific version is important)
brew install python@3.10
1. **Make scripts executable:**
Open your terminal, navigate to the cloned `Deep-Live-Cam` directory, and run:
```bash
chmod +x setup_mac.sh
chmod +x run_mac*.sh
```
# Install tkinter package (required for the GUI)
brew install python-tk@3.10
2. **Run the setup script:**
This will check for Python 3.9+, ffmpeg, create a virtual environment (`.venv`), and install required Python packages.
```bash
./setup_mac.sh
```
If you encounter issues with specific packages during `pip install` (especially for libraries that compile C code, like some image processing libraries), you might need to install system libraries via Homebrew (e.g., `brew install jpeg libtiff ...`) or ensure Xcode Command Line Tools are installed (`xcode-select --install`).
# Create and activate virtual environment with Python 3.10
python3.10 -m venv venv
source venv/bin/activate
3. **Activate the virtual environment (for manual runs):**
After setup, if you want to run commands manually or use developer tools from your terminal session:
```bash
source .venv/bin/activate
```
(To deactivate, simply type `deactivate` in the terminal.)
# Install dependencies
pip install -r requirements.txt
```
4. **Run the application:**
Use the provided run scripts for convenience. These scripts automatically activate the virtual environment.
* `./run_mac.sh`: Runs the application with the CPU execution provider by default. This is a good starting point.
* `./run_mac_cpu.sh`: Explicitly uses the CPU execution provider.
* `./run_mac_coreml.sh`: Attempts to use the CoreML execution provider for potential hardware acceleration on Apple Silicon and Intel Macs.
* `./run_mac_mps.sh`: Attempts to use the MPS (Metal Performance Shaders) execution provider, primarily for Apple Silicon Macs.
Example of running with specific source/target arguments:
```bash
./run_mac.sh --source path/to/your_face.jpg --target path/to/video.mp4
```
Or, to simply launch the UI:
```bash
./run_mac.sh
```
**Important Notes for macOS GPU Acceleration (CoreML/MPS):**
* The `setup_mac.sh` script installs packages from `requirements.txt`, which typically includes a general CPU-based version of `onnxruntime`.
* For optimal performance on Apple Silicon (M1/M2/M3) or specific GPU acceleration, you might need to install a different `onnxruntime` package *after* running `setup_mac.sh` and while the virtual environment (`.venv`) is active.
* **Example for `onnxruntime-silicon` (often requires Python 3.10 for older versions like 1.13.1):**
The original `README` noted that `onnxruntime-silicon==1.13.1` was specific to Python 3.10. If you intend to use this exact version for CoreML:
```bash
# Ensure you are using Python 3.10 if required by your chosen onnxruntime-silicon version
# After running setup_mac.sh and activating .venv:
# source .venv/bin/activate
pip uninstall onnxruntime onnxruntime-gpu # Uninstall any existing onnxruntime
pip install onnxruntime-silicon==1.13.1 # Or your desired version
# Then use ./run_mac_coreml.sh
```
Check the ONNX Runtime documentation for the latest recommended packages for Apple Silicon.
* **For MPS with ONNX Runtime:** This may require a specific build or version of `onnxruntime`. Consult the ONNX Runtime documentation. For PyTorch-based operations (like the Face Enhancer or Hair Segmenter if they were PyTorch native and not ONNX), PyTorch should automatically try to use MPS on compatible Apple Silicon hardware if available.
* **User Interface (Tkinter):** If you encounter errors related to `_tkinter` not being found when launching the UI, ensure your Python installation supports Tk. For Python installed via Homebrew, this is usually `python-tk` (e.g., `brew install python-tk@3.9` or `brew install python-tk@3.10`, matching your Python version).
** In case something goes wrong and you need to reinstall the virtual environment **

View File

@ -176,9 +176,12 @@ def update_status(message: str, scope: str = 'DLC.CORE') -> None:
ui.update_status(message)
def start() -> None:
for frame_processor in get_frame_processors_modules(modules.globals.frame_processors):
if not frame_processor.pre_start():
return
# Note: pre_start is called in run() before start() now.
# If it were to be called here, it would also need the status_fn_callback.
# For example:
# for frame_processor in get_frame_processors_modules(modules.globals.frame_processors):
# if not frame_processor.pre_start(status_fn_callback=update_status): # If pre_start was here
# return
update_status('Processing...')
# process image to image
if has_image_extension(modules.globals.target_path):
@ -190,7 +193,7 @@ def start() -> None:
print("Error copying file:", str(e))
for frame_processor in get_frame_processors_modules(modules.globals.frame_processors):
update_status('Progressing...', frame_processor.NAME)
frame_processor.process_image(modules.globals.source_path, modules.globals.output_path, modules.globals.output_path)
frame_processor.process_image(modules.globals.source_path, modules.globals.output_path, modules.globals.output_path, status_fn_callback=update_status)
release_resources()
if is_image(modules.globals.target_path):
update_status('Processing to image succeed!')
@ -210,7 +213,7 @@ def start() -> None:
temp_frame_paths = get_temp_frame_paths(modules.globals.target_path)
for frame_processor in get_frame_processors_modules(modules.globals.frame_processors):
update_status('Progressing...', frame_processor.NAME)
frame_processor.process_video(modules.globals.source_path, temp_frame_paths)
frame_processor.process_video(modules.globals.source_path, temp_frame_paths, status_fn_callback=update_status)
release_resources()
# handles fps
if modules.globals.keep_fps:
@ -249,7 +252,9 @@ def run() -> None:
if not pre_check():
return
for frame_processor in get_frame_processors_modules(modules.globals.frame_processors):
if not frame_processor.pre_check():
if not frame_processor.pre_check(): # pre_check in face_swapper does not use update_status
return
if hasattr(frame_processor, 'pre_start') and not frame_processor.pre_start(status_fn_callback=update_status): # Pass callback here
return
limit_resources()
if modules.globals.headless:

View File

@ -20,7 +20,8 @@ def get_face_analyser() -> Any:
if FACE_ANALYSER is None:
FACE_ANALYSER = insightface.app.FaceAnalysis(name='buffalo_l', providers=modules.globals.execution_providers)
FACE_ANALYSER.prepare(ctx_id=0, det_size=(640, 640))
# Lowered detection threshold for potentially better webcam face detection (default is 0.5)
FACE_ANALYSER.prepare(ctx_id=0, det_size=(640, 640), det_thresh=0.4)
return FACE_ANALYSER

View File

@ -41,3 +41,4 @@ show_mouth_mask_box = False
mask_feather_ratio = 8
mask_down_size = 0.50
mask_size = 1
enable_hair_swapping = False # Default state for enabling/disabling hair swapping

View File

@ -0,0 +1,125 @@
import torch
import numpy as np
from PIL import Image
from transformers import SegformerImageProcessor, SegformerForSemanticSegmentation
import cv2 # Imported for BGR to RGB conversion, though PIL can also do it.
# Global variables for caching
HAIR_SEGMENTER_PROCESSOR = None
HAIR_SEGMENTER_MODEL = None
MODEL_NAME = "isjackwild/segformer-b0-finetuned-segments-skin-hair-clothing"
def segment_hair(image_np: np.ndarray) -> np.ndarray:
"""
Segments hair from an image.
Args:
image_np: NumPy array representing the image (BGR format from OpenCV).
Returns:
NumPy array representing the binary hair mask.
"""
global HAIR_SEGMENTER_PROCESSOR, HAIR_SEGMENTER_MODEL
if HAIR_SEGMENTER_PROCESSOR is None or HAIR_SEGMENTER_MODEL is None:
print(f"Loading hair segmentation model and processor ({MODEL_NAME}) for the first time...")
try:
HAIR_SEGMENTER_PROCESSOR = SegformerImageProcessor.from_pretrained(MODEL_NAME)
HAIR_SEGMENTER_MODEL = SegformerForSemanticSegmentation.from_pretrained(MODEL_NAME)
if torch.cuda.is_available():
try:
HAIR_SEGMENTER_MODEL = HAIR_SEGMENTER_MODEL.to('cuda')
print("INFO: Hair segmentation model moved to CUDA (GPU).")
except Exception as e_cuda:
print(f"ERROR: Failed to move hair segmentation model to CUDA: {e_cuda}. Using CPU instead.")
# Fallback to CPU if .to('cuda') fails
HAIR_SEGMENTER_MODEL = HAIR_SEGMENTER_MODEL.to('cpu')
else:
print("INFO: CUDA not available. Hair segmentation model will use CPU.")
print("INFO: Hair segmentation model and processor loaded successfully (device: {}).".format(HAIR_SEGMENTER_MODEL.device))
except Exception as e:
print(f"ERROR: Failed to load hair segmentation model/processor: {e}")
# Return an empty mask compatible with expected output shape (H, W)
return np.zeros((image_np.shape[0], image_np.shape[1]), dtype=np.uint8)
# Convert BGR (OpenCV) to RGB (PIL)
image_rgb = cv2.cvtColor(image_np, cv2.COLOR_BGR2RGB)
image_pil = Image.fromarray(image_rgb)
inputs = HAIR_SEGMENTER_PROCESSOR(images=image_pil, return_tensors="pt")
if HAIR_SEGMENTER_MODEL.device.type == 'cuda':
try:
# SegformerImageProcessor output (BatchEncoding) is a dict-like object.
# We need to move its tensor components, commonly 'pixel_values'.
if 'pixel_values' in inputs:
inputs['pixel_values'] = inputs['pixel_values'].to('cuda')
else: # Fallback if the structure is different than expected
inputs = inputs.to('cuda')
# If inputs has other tensor components that need to be moved, they'd need similar handling.
except Exception as e_inputs_cuda:
print(f"ERROR: Failed to move inputs to CUDA: {e_inputs_cuda}. Attempting inference on CPU.")
# If moving inputs to CUDA fails, we should ensure model is also on CPU for this inference pass
# This is a tricky situation; ideally, this failure shouldn't happen if model moved successfully.
# For simplicity, we'll assume if model is on CUDA, inputs should also be.
# A more robust solution might involve moving model back to CPU if inputs can't be moved.
with torch.no_grad(): # Important for inference
outputs = HAIR_SEGMENTER_MODEL(**inputs)
logits = outputs.logits # Shape: batch_size, num_labels, height, width
# Upsample logits to original image size
upsampled_logits = torch.nn.functional.interpolate(
logits,
size=(image_np.shape[0], image_np.shape[1]), # H, W
mode='bilinear',
align_corners=False
)
segmentation_map = upsampled_logits.argmax(dim=1).squeeze().cpu().numpy().astype(np.uint8)
# Label 2 is for hair in this model
return np.where(segmentation_map == 2, 255, 0).astype(np.uint8)
if __name__ == '__main__':
# This is a conceptual test.
# In a real scenario, you would load an image using OpenCV or Pillow.
# For example:
# sample_image_np = cv2.imread("path/to/your/image.jpg")
# if sample_image_np is not None:
# hair_mask_output = segment_hair(sample_image_np)
# cv2.imwrite("hair_mask_output.png", hair_mask_output)
# print("Hair mask saved to hair_mask_output.png")
# else:
# print("Failed to load sample image.")
print("Conceptual test: Hair segmenter module created.")
# Create a dummy image for a basic test run if no image is available.
dummy_image_np = np.zeros((100, 100, 3), dtype=np.uint8) # 100x100 BGR image
dummy_image_np[:, :, 1] = 255 # Make it green to distinguish from black mask
try:
print("Running segment_hair with a dummy image...")
hair_mask_output = segment_hair(dummy_image_np)
print(f"segment_hair returned a mask of shape: {hair_mask_output.shape}")
# Check if the output is a 2D array (mask) and has the same H, W as input
assert hair_mask_output.shape == (dummy_image_np.shape[0], dummy_image_np.shape[1])
# Check if the mask is binary (0 or 255)
assert np.all(np.isin(hair_mask_output, [0, 255]))
print("Dummy image test successful. Hair mask seems to be generated correctly.")
# Attempt to save the dummy mask (optional, just for visual confirmation if needed)
# cv2.imwrite("dummy_hair_mask_output.png", hair_mask_output)
# print("Dummy hair mask saved to dummy_hair_mask_output.png")
except ImportError as e:
print(f"An ImportError occurred: {e}. This might be due to missing dependencies like transformers, torch, or Pillow.")
print("Please ensure all required packages are installed by updating requirements.txt and installing them.")
except Exception as e:
print(f"An error occurred during the dummy image test: {e}")
print("This could be due to issues with model loading, processing, or other runtime errors.")
print("To perform a full test, replace the dummy image with a real image path.")

File diff suppressed because it is too large Load Diff

View File

@ -19,6 +19,7 @@ from modules.face_analyser import (
)
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.face_swapper import reset_tracker_state # Added import
from modules.utilities import (
is_image,
is_video,
@ -105,6 +106,7 @@ def save_switch_states():
"show_fps": modules.globals.show_fps,
"mouth_mask": modules.globals.mouth_mask,
"show_mouth_mask_box": modules.globals.show_mouth_mask_box,
"enable_hair_swapping": modules.globals.enable_hair_swapping,
}
with open("switch_states.json", "w") as f:
json.dump(switch_states, f)
@ -129,6 +131,9 @@ def load_switch_states():
modules.globals.show_mouth_mask_box = switch_states.get(
"show_mouth_mask_box", False
)
modules.globals.enable_hair_swapping = switch_states.get(
"enable_hair_swapping", True # Default to True if not found
)
except FileNotFoundError:
# If the file doesn't exist, use default values
pass
@ -236,6 +241,7 @@ def create_root(start: Callable[[], None], destroy: Callable[[], None]) -> ctk.C
command=lambda: (
setattr(modules.globals, "many_faces", many_faces_value.get()),
save_switch_states(),
reset_tracker_state() # Added reset call
),
)
many_faces_switch.place(relx=0.6, rely=0.65)
@ -253,10 +259,6 @@ def create_root(start: Callable[[], None], destroy: Callable[[], None]) -> ctk.C
)
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_switch = ctk.CTkSwitch(
root,
@ -266,7 +268,8 @@ def create_root(start: Callable[[], None], destroy: Callable[[], None]) -> ctk.C
command=lambda: (
setattr(modules.globals, "map_faces", map_faces.get()),
save_switch_states(),
close_mapper_window() if not map_faces.get() else None
close_mapper_window() if not map_faces.get() else None,
reset_tracker_state() # Added reset call
),
)
map_faces_switch.place(relx=0.1, rely=0.75)
@ -284,6 +287,19 @@ def create_root(start: Callable[[], None], destroy: Callable[[], None]) -> ctk.C
)
show_fps_switch.place(relx=0.6, rely=0.75)
hair_swapping_value = ctk.BooleanVar(value=modules.globals.enable_hair_swapping)
hair_swapping_switch = ctk.CTkSwitch(
root,
text=_("Swap Hair"),
variable=hair_swapping_value,
cursor="hand2",
command=lambda: (
setattr(modules.globals, "enable_hair_swapping", hair_swapping_value.get()),
save_switch_states(),
)
)
hair_swapping_switch.place(relx=0.6, rely=0.80)
mouth_mask_var = ctk.BooleanVar(value=modules.globals.mouth_mask)
mouth_mask_switch = ctk.CTkSwitch(
root,
@ -309,21 +325,20 @@ def create_root(start: Callable[[], None], destroy: Callable[[], None]) -> ctk.C
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.85, relwidth=0.2, relheight=0.05)
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.85, relwidth=0.2, relheight=0.05)
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.85, relwidth=0.2, relheight=0.05)
# --- Camera Selection ---
camera_label = ctk.CTkLabel(root, text=_("Select Camera:"))
camera_label.place(relx=0.1, rely=0.86, relwidth=0.2, relheight=0.05)
camera_label.place(relx=0.1, rely=0.91, relwidth=0.2, relheight=0.05)
available_cameras = get_available_cameras()
camera_indices, camera_names = available_cameras
@ -342,7 +357,7 @@ def create_root(start: Callable[[], None], destroy: Callable[[], None]) -> ctk.C
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.91, relwidth=0.25, relheight=0.05)
live_button = ctk.CTkButton(
root,
@ -362,16 +377,15 @@ def create_root(start: Callable[[], None], destroy: Callable[[], None]) -> ctk.C
else "disabled"
),
)
live_button.place(relx=0.65, rely=0.86, relwidth=0.2, relheight=0.05)
# --- End Camera Selection ---
live_button.place(relx=0.65, rely=0.91, relwidth=0.2, relheight=0.05)
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.96, relwidth=0.8)
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.99, relwidth=0.8)
donate_label.configure(
text_color=ctk.ThemeManager.theme.get("URL").get("text_color")
)
@ -593,9 +607,11 @@ def select_source_path() -> None:
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)
reset_tracker_state() # Added reset call
else:
modules.globals.source_path = None
source_label.configure(image=None)
reset_tracker_state() # Added reset call even if source is cleared
def swap_faces_paths() -> None:
@ -880,12 +896,96 @@ def create_webcam_preview(camera_index: int):
PREVIEW.deiconify()
frame_processors = get_frame_processors_modules(modules.globals.frame_processors)
source_image = None
# --- Source Image Loading and Validation (Moved before the loop) ---
source_face_obj_for_cam = None
source_frame_full_for_cam = None
source_frame_full_for_cam_map_faces = None
if not modules.globals.map_faces:
if not modules.globals.source_path:
update_status("Error: No source image selected for webcam mode.")
cap.release()
PREVIEW.withdraw()
while PREVIEW.state() != "withdrawn" and ROOT.winfo_exists():
ROOT.update_idletasks()
ROOT.update()
time.sleep(0.05)
return
if not os.path.exists(modules.globals.source_path):
update_status(f"Error: Source image not found at {modules.globals.source_path}")
cap.release()
PREVIEW.withdraw()
while PREVIEW.state() != "withdrawn" and ROOT.winfo_exists():
ROOT.update_idletasks()
ROOT.update()
time.sleep(0.05)
return
source_frame_full_for_cam = cv2.imread(modules.globals.source_path)
if source_frame_full_for_cam is None:
update_status(f"Error: Could not read source image at {modules.globals.source_path}")
cap.release()
PREVIEW.withdraw()
while PREVIEW.state() != "withdrawn" and ROOT.winfo_exists():
ROOT.update_idletasks()
ROOT.update()
time.sleep(0.05)
return
source_face_obj_for_cam = get_one_face(source_frame_full_for_cam)
if source_face_obj_for_cam is None:
update_status(f"Error: No face detected in source image {modules.globals.source_path}")
cap.release()
PREVIEW.withdraw()
while PREVIEW.state() != "withdrawn" and ROOT.winfo_exists():
ROOT.update_idletasks()
ROOT.update()
time.sleep(0.05)
return
else: # modules.globals.map_faces is True
if not modules.globals.source_path:
update_status("Error: No global source image selected (for hair/background in map_faces mode).")
cap.release()
PREVIEW.withdraw()
while PREVIEW.state() != "withdrawn" and ROOT.winfo_exists():
ROOT.update_idletasks()
ROOT.update()
time.sleep(0.05)
return
if not os.path.exists(modules.globals.source_path):
update_status(f"Error: Source image (for hair/background) not found at {modules.globals.source_path}")
cap.release()
PREVIEW.withdraw()
while PREVIEW.state() != "withdrawn" and ROOT.winfo_exists():
ROOT.update_idletasks()
ROOT.update()
time.sleep(0.05)
return
source_frame_full_for_cam_map_faces = cv2.imread(modules.globals.source_path)
if source_frame_full_for_cam_map_faces is None:
update_status(f"Error: Could not read source image (for hair/background) at {modules.globals.source_path}")
cap.release()
PREVIEW.withdraw()
while PREVIEW.state() != "withdrawn" and ROOT.winfo_exists():
ROOT.update_idletasks()
ROOT.update()
time.sleep(0.05)
return
if not modules.globals.source_target_map and not modules.globals.simple_map:
update_status("Warning: No face map defined for map_faces mode. Swapper may not work as expected.")
# --- End Source Image Loading ---
prev_time = time.time()
fps_update_interval = 0.5
frame_count = 0
fps = 0
reset_tracker_state() # Ensure tracker is reset before starting webcam loop
while True:
ret, frame = cap.read()
if not ret:
@ -900,32 +1000,32 @@ def create_webcam_preview(camera_index: int):
temp_frame = fit_image_to_size(
temp_frame, PREVIEW.winfo_width(), PREVIEW.winfo_height()
)
else:
temp_frame = fit_image_to_size(
temp_frame, PREVIEW.winfo_width(), PREVIEW.winfo_height()
)
# REMOVED: detection_frame_counter += 1
# REMOVED: if detection_frame_counter % DETECTION_INTERVAL == 0:
# The following block is now unindented to run every frame
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))
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(None, temp_frame)
else:
temp_frame = frame_processor.process_frame(source_image, temp_frame)
if source_face_obj_for_cam is not None and source_frame_full_for_cam is not 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(None, temp_frame)
else:
temp_frame = frame_processor.process_frame(source_face_obj_for_cam, source_frame_full_for_cam, 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)
else:
temp_frame = frame_processor.process_frame_v2(temp_frame)
if source_frame_full_for_cam_map_faces is not None:
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)
else:
temp_frame = frame_processor.process_frame_v2(source_frame_full_for_cam_map_faces, temp_frame)
# Calculate and display FPS
current_time = time.time()
frame_count += 1
if current_time - prev_time >= fps_update_interval:

View File

@ -50,7 +50,16 @@ class VideoCapturer:
continue
else:
# Unix-like systems (Linux/Mac) capture method
self.cap = cv2.VideoCapture(self.device_index)
if platform.system() == "Darwin": # macOS
print(f"INFO: macOS detected. Attempting to use cv2.CAP_AVFOUNDATION exclusively for camera index {self.device_index}.")
self.cap = cv2.VideoCapture(self.device_index, cv2.CAP_AVFOUNDATION)
# The check 'if not self.cap or not self.cap.isOpened():' later in the function
# will now directly reflect the success or failure of AVFoundation.
if not self.cap or not self.cap.isOpened():
print(f"ERROR: cv2.CAP_AVFOUNDATION failed to open camera index {self.device_index}. Capture will likely fail.")
# No fallback to default cv2.VideoCapture(self.device_index) here for macOS.
else: # Other Unix-like systems (e.g., Linux)
self.cap = cv2.VideoCapture(self.device_index)
if not self.cap or not self.cap.isOpened():
raise RuntimeError("Failed to open camera")

View File

@ -2,7 +2,7 @@
numpy>=1.23.5,<2
typing-extensions>=4.8.0
opencv-python==4.10.0.84
opencv-contrib-python==4.10.0.84
cv2_enumerate_cameras==1.1.15
onnx==1.16.0
insightface==0.7.3
@ -19,3 +19,4 @@ onnxruntime-gpu==1.17; sys_platform != 'darwin'
tensorflow; sys_platform != 'darwin'
opennsfw2==0.10.2
protobuf==4.23.2
transformers>=4.0.0

View File

@ -1 +1,16 @@
python run.py --execution-provider cuda
@echo off
set VENV_DIR=.venv
:: Check if virtual environment exists
if not exist "%VENV_DIR%\Scripts\activate.bat" (
echo Virtual environment '%VENV_DIR%' not found.
echo Please run setup_windows.bat first.
pause
exit /b 1
)
echo Activating virtual environment...
call "%VENV_DIR%\Scripts\activate.bat"
echo Starting the application with CUDA execution provider...
python run.py --execution-provider cuda %*

View File

@ -1 +1,16 @@
python run.py --execution-provider dml
@echo off
set VENV_DIR=.venv
:: Check if virtual environment exists
if not exist "%VENV_DIR%\Scripts\activate.bat" (
echo Virtual environment '%VENV_DIR%' not found.
echo Please run setup_windows.bat first.
pause
exit /b 1
)
echo Activating virtual environment...
call "%VENV_DIR%\Scripts\activate.bat"
echo Starting the application with DirectML execution provider...
python run.py --execution-provider dml %*

20
run_mac.sh 100644
View File

@ -0,0 +1,20 @@
#!/usr/bin/env bash
VENV_DIR=".venv"
# Check if virtual environment exists
if [ ! -d "$VENV_DIR" ]; then
echo "Virtual environment '$VENV_DIR' not found."
echo "Please run ./setup_mac.sh first to create the environment and install dependencies."
exit 1
fi
echo "Activating virtual environment..."
source "$VENV_DIR/bin/activate"
echo "Starting the application with CPU execution provider..."
# Passes all arguments passed to this script (e.g., --source, --target) to run.py
python3 run.py --execution-provider cpu "$@"
# Deactivate after script finishes (optional, as shell context closes)
# deactivate

13
run_mac_coreml.sh 100644
View File

@ -0,0 +1,13 @@
#!/usr/bin/env bash
VENV_DIR=".venv"
if [ ! -d "$VENV_DIR" ]; then
echo "Virtual environment '$VENV_DIR' not found."
echo "Please run ./setup_mac.sh first."
exit 1
fi
source "$VENV_DIR/bin/activate"
echo "Starting the application with CoreML execution provider..."
python3 run.py --execution-provider coreml "$@"

13
run_mac_cpu.sh 100644
View File

@ -0,0 +1,13 @@
#!/usr/bin/env bash
VENV_DIR=".venv"
if [ ! -d "$VENV_DIR" ]; then
echo "Virtual environment '$VENV_DIR' not found."
echo "Please run ./setup_mac.sh first."
exit 1
fi
source "$VENV_DIR/bin/activate"
echo "Starting the application with CPU execution provider..."
python3 run.py --execution-provider cpu "$@"

13
run_mac_mps.sh 100644
View File

@ -0,0 +1,13 @@
#!/usr/bin/env bash
VENV_DIR=".venv"
if [ ! -d "$VENV_DIR" ]; then
echo "Virtual environment '$VENV_DIR' not found."
echo "Please run ./setup_mac.sh first."
exit 1
fi
source "$VENV_DIR/bin/activate"
echo "Starting the application with MPS execution provider (for Apple Silicon)..."
python3 run.py --execution-provider mps "$@"

20
run_windows.bat 100644
View File

@ -0,0 +1,20 @@
@echo off
set VENV_DIR=.venv
:: Check if virtual environment exists
if not exist "%VENV_DIR%\Scripts\activate.bat" (
echo Virtual environment '%VENV_DIR%' not found.
echo Please run setup_windows.bat first to create the environment and install dependencies.
pause
exit /b 1
)
echo Activating virtual environment...
call "%VENV_DIR%\Scripts\activate.bat"
echo Starting the application with CPU execution provider...
:: Passes all arguments passed to this script to run.py
python run.py --execution-provider cpu %*
:: Optional: Deactivate after script finishes
:: call deactivate

81
setup_mac.sh 100644
View File

@ -0,0 +1,81 @@
#!/usr/bin/env bash
# Exit immediately if a command exits with a non-zero status.
set -e
echo "Starting macOS setup..."
# 1. Check for Python 3
echo "Checking for Python 3..."
if ! command -v python3 &> /dev/null
then
echo "Python 3 could not be found. Please install Python 3."
echo "You can often install it using Homebrew: brew install python"
exit 1
fi
# 2. Check Python version (>= 3.9)
echo "Checking Python 3 version..."
python3 -c 'import sys; exit(0) if sys.version_info >= (3,9) else exit(1)'
if [ $? -ne 0 ]; then
echo "Python 3.9 or higher is required."
echo "Your version is: $(python3 --version)"
echo "Please upgrade your Python version. Consider using pyenv or Homebrew to manage Python versions."
exit 1
fi
echo "Python 3.9+ found: $(python3 --version)"
# 3. Check for ffmpeg
echo "Checking for ffmpeg..."
if ! command -v ffmpeg &> /dev/null
then
echo "WARNING: ffmpeg could not be found. This program requires ffmpeg for video processing."
echo "You can install it using Homebrew: brew install ffmpeg"
echo "Continuing with setup, but video processing might fail later."
else
echo "ffmpeg found: $(ffmpeg -version | head -n 1)"
fi
# 4. Define virtual environment directory
VENV_DIR=".venv"
# 5. Create virtual environment
if [ -d "$VENV_DIR" ]; then
echo "Virtual environment '$VENV_DIR' already exists. Skipping creation."
else
echo "Creating virtual environment in '$VENV_DIR'..."
python3 -m venv "$VENV_DIR"
fi
# 6. Activate virtual environment (for this script's session)
echo "Activating virtual environment..."
source "$VENV_DIR/bin/activate"
# 7. Upgrade pip
echo "Upgrading pip..."
pip install --upgrade pip
# 8. Install requirements
echo "Installing requirements from requirements.txt..."
if [ -f "requirements.txt" ]; then
pip install -r requirements.txt
else
echo "ERROR: requirements.txt not found. Cannot install dependencies."
# Deactivate on error if desired, or leave active for user to debug
# deactivate
exit 1
fi
echo ""
echo "Setup complete!"
echo ""
echo "To activate the virtual environment in your terminal, run:"
echo " source $VENV_DIR/bin/activate"
echo ""
echo "After activating, you can run the application using:"
echo " python3 run.py [arguments]"
echo "Or use one of the run_mac_*.sh scripts (e.g., ./run_mac_cpu.sh)."
echo ""
# Deactivate at the end of the script's execution (optional, as script session ends)
# deactivate

80
setup_windows.bat 100644
View File

@ -0,0 +1,80 @@
@echo off
echo Starting Windows setup...
:: 1. Check for Python
echo Checking for Python...
python --version >nul 2>&1
if errorlevel 1 (
echo Python could not be found in your PATH.
echo Please install Python 3 (3.10 or higher recommended) and ensure it's added to your PATH.
echo You can download Python from https://www.python.org/downloads/
pause
exit /b 1
)
:: Optional: Check Python version (e.g., >= 3.9 or >=3.10).
:: This is a bit more complex in pure batch. For now, rely on user having a modern Python 3.
:: The README will recommend 3.10.
:: If we reach here, Python is found.
echo Python was found. Attempting to display version:
for /f "delims=" %%i in ('python --version 2^>^&1') do echo %%i
:: 2. Check for ffmpeg (informational)
echo Checking for ffmpeg...
ffmpeg -version >nul 2>&1
if errorlevel 1 (
echo WARNING: ffmpeg could not be found in your PATH. This program requires ffmpeg for video processing.
echo Please download ffmpeg from https://ffmpeg.org/download.html and add it to your system's PATH.
echo (The README.md contains a link for a potentially easier ffmpeg install method using a PowerShell command)
echo Continuing with setup, but video processing might fail later.
pause
) else (
echo ffmpeg found.
)
:: 3. Define virtual environment directory
set VENV_DIR=.venv
:: 4. Create virtual environment
if exist "%VENV_DIR%\Scripts\activate.bat" (
echo Virtual environment '%VENV_DIR%' already exists. Skipping creation.
) else (
echo Creating virtual environment in '%VENV_DIR%'...
python -m venv "%VENV_DIR%"
if errorlevel 1 (
echo Failed to create virtual environment. Please check your Python installation.
pause
exit /b 1
)
)
:: 5. Activate virtual environment (for this script's session)
echo Activating virtual environment...
call "%VENV_DIR%\Scripts\activate.bat"
:: 6. Upgrade pip
echo Upgrading pip...
python -m pip install --upgrade pip
:: 7. Install requirements
echo Installing requirements from requirements.txt...
if exist "requirements.txt" (
python -m pip install -r requirements.txt
) else (
echo ERROR: requirements.txt not found. Cannot install dependencies.
pause
exit /b 1
)
echo.
echo Setup complete!
echo.
echo To activate the virtual environment in your command prompt, run:
echo %VENV_DIR%\Scripts\activate.bat
echo.
echo After activating, you can run the application using:
echo python run.py [arguments]
echo Or use one of the run-*.bat scripts (e.g., run-cuda.bat, run_windows.bat).
echo.
pause
exit /b 0