Compare commits

..

25 Commits

Author SHA1 Message Date
KRSHH 51b38fe253 Revert "Slight Performance Improvement"
This reverts commit 9a472e2435.
2024-11-07 14:58:03 +05:30
KRSHH 97a76c5e5b Revert "Update ui.py"
This reverts commit df72a5431b.
2024-11-07 14:57:47 +05:30
KRSHH df72a5431b Update ui.py 2024-10-29 14:53:19 +05:30
KRSHH 9a472e2435 Slight Performance Improvement
avg 9.5 to avg 10
2024-10-29 14:07:47 +05:30
KRSHH a581b81bd9 Change Switch/Slider positions 2024-10-28 16:53:55 +05:30
KRSHH 69b7970b87 Improved Camera Selection Menu 2024-10-28 13:50:44 +05:30
KRSHH 7fce1fd8b4 Update instructions.txt
Links to Models in Instructions
2024-10-28 13:15:13 +05:30
KRSHH c2918a52df Minimum Windows Size 2024-10-28 13:12:02 +05:30
KRSHH 64c0f085b1 Change Switch positions 2024-10-28 12:20:54 +05:30
KRSHH 3c30ab8a5d Opacity 2024-10-28 11:45:50 +05:30
KRSHH acc8e778b6 Mouth Mask And UI 2024-10-28 10:56:01 +05:30
KRSHH 4b6f60f59b Delete files from main 2024-10-21 19:15:21 +05:30
KRSHH 1178232268 Upload images to folder 2024-10-21 19:12:46 +05:30
KRSHH ad918c5523 Shift Media to a folder 2024-10-21 19:10:21 +05:30
KRSHH 7b8d6171b7 Frame by Frame Navigation 2024-10-19 14:04:05 +05:30
KRSHH 6b86b0a72d Preview Window Slider 2024-10-19 13:58:55 +05:30
KRSHH 936e78f93b Fix Mapper for Live 2024-10-17 13:06:00 +05:30
KRSHH aca0bcb7ce Formatting Fix 2024-10-14 21:48:57 +05:30
KRSHH 966cb5a7df Removed Package Repetition 2024-10-14 21:46:22 +05:30
KRSHH 1ab4bf06b1 Tips for DLC 2024-10-14 21:26:34 +05:30
KRSHH 6649e2a5df remember/save switch states 2024-10-14 20:32:17 +05:30
KRSHH 72d4c9e9c4 Update metadata.py - 1.6 2024-10-14 19:50:09 +05:30
KRSHH 76d65247b7 improved performance enhancement 2024-10-14 19:49:51 +05:30
KRSHH 37f224cb47 FPS Counter 2024-10-14 19:46:48 +05:30
KRSHH b58ffffd37 Fix the button position and bugs 2024-10-14 19:11:44 +05:30
36 changed files with 1913 additions and 1237 deletions
+26 -14
View File
@@ -1,26 +1,38 @@
***[Remove this]The issue would be closed without notice and be considered spam if the template is not followed.*** ---
name: Bug report
about: Create a report to help us improve
title: ''
labels: ''
assignees: ''
---
**Describe the bug** **Describe the bug**
A clear and concise description of what the bug is. 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** **Screenshots**
If applicable, add screenshots to help explain your problem. If applicable, add screenshots to help explain your problem.
**Error Message**
`<The error message in terminal>`
**Desktop (please complete the following information):** **Desktop (please complete the following information):**
- OS: [e.g. Windows] - 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] - Version [e.g. 22]
- GPU
- CPU
**Additional context** **Additional context**
Add any other context about the problem here. Add any other context about the problem here.
**Confirmation (Mandatory)**
- [ ] I have followed the template
- [ ] This is not a query about how to increase performance
- [ ] I have checked the issues page, and this is not a duplicate
-1
View File
@@ -24,4 +24,3 @@ models/GFPGANv1.4.pth
models/DMDNet.pth models/DMDNet.pth
faceswap/ faceswap/
.vscode/ .vscode/
switch_states.json
+1 -38
View File
@@ -1,38 +1 @@
# Collaboration Guidelines and Codebase Quality Standards Please always push on the experimental to ensure we don't mess with the main branch. All the test will be done on the experimental and will be pushed to the main branch after few days of testing.
To ensure smooth collaboration and maintain the high quality of our codebase, please adhere to the following guidelines:
## Branching Strategy
* **`premain`**:
* Always push your changes to the `premain` branch initially.
* This safeguards the `main` branch from unintentional disruptions.
* All tests will be performed on the `premain` branch.
* Changes will only be merged into `main` after several hours or days of rigorous testing.
* **`experimental`**:
* For large or potentially disruptive changes, use the `experimental` branch.
* This allows for thorough discussion and review before considering a merge into `main`.
## Pre-Pull Request Checklist
Before creating a Pull Request (PR), ensure you have completed the following tests:
### Functionality
* **Realtime Faceswap**:
* Test with face enhancer **enabled** and **disabled**.
* **Map Faces**:
* Test with both options (**enabled** and **disabled**).
* **Camera Listing**:
* Verify that all cameras are listed accurately.
### Stability
* **Realtime FPS**:
* Confirm that there is no drop in real-time frames per second (FPS).
* **Boot Time**:
* Changes should not negatively impact the boot time of either the application or the real-time faceswap feature.
* **GPU Overloading**:
* Test for a minimum of 15 minutes to guarantee no GPU overloading, which could lead to crashes.
* **App Performance**:
* The application should remain responsive and not exhibit any lag.
+252 -213
View File
@@ -5,120 +5,49 @@
</p> </p>
<p align="center"> <p align="center">
<a href="https://trendshift.io/repositories/11395" target="_blank"><img src="https://trendshift.io/api/badge/repositories/11395" alt="hacksider%2FDeep-Live-Cam | Trendshift" style="width: 250px; height: 55px;" width="250" height="55"/></a> <img src="media/demo.gif" alt="Demo GIF">
<img src="media/avgpcperformancedemo.gif" alt="Performance Demo GIF">
</p> </p>
<p align="center"> ## Disclaimer
<img src="media/demo.gif" alt="Demo GIF" width="800">
</p>
## Disclaimer This software is intended as a productive contribution to the AI-generated media industry. It aims to assist artists with tasks like animating custom characters or using them as models for clothing, etc.
This deepfake software is designed to be a productive tool for the AI-generated media industry. It can assist artists in animating custom characters, creating engaging content, and even using models for clothing design. We are aware of the potential for unethical applications and are committed to preventative measures. A built-in check prevents the program from processing inappropriate media (nudity, graphic content, sensitive material like war footage, etc.). We will continue to develop this project responsibly, adhering to law and ethics. We may shut down the project or add watermarks if legally required.
We are aware of the potential for unethical applications and are committed to preventative measures. A built-in check prevents the program from processing inappropriate media (nudity, graphic content, sensitive material like war footage, etc.). We will continue to develop this project responsibly, adhering to the law and ethics. We may shut down the project or add watermarks if legally required.
- Ethical Use: Users are expected to use this software responsibly and legally. If using a real person's face, obtain their consent and clearly label any output as a deepfake when sharing online.
- Content Restrictions: The software includes built-in checks to prevent processing inappropriate media, such as nudity, graphic content, or sensitive material.
- Legal Compliance: We adhere to all relevant laws and ethical guidelines. If legally required, we may shut down the project or add watermarks to the output.
- User Responsibility: We are not responsible for end-user actions. Users must ensure their use of the software aligns with ethical standards and legal requirements.
By using this software, you agree to these terms and commit to using it in a manner that respects the rights and dignity of others.
Users are expected to use this software responsibly and legally. If using a real person's face, obtain their consent and clearly label any output as a deepfake when sharing online. We are not responsible for end-user actions. Users are expected to use this software responsibly and legally. If using a real person's face, obtain their consent and clearly label any output as a deepfake when sharing online. We are not responsible for end-user actions.
## TLDR; Live Deepfake in just 3 Clicks ## Quick Start (Windows / Nvidia)
![easysteps](https://github.com/user-attachments/assets/af825228-852c-411b-b787-ffd9aac72fc6)
1. Select a face
2. Select which camera to use
3. Press live!
## Features & Uses - Everything is in real-time [![Download](media/download.png)](https://hacksider.gumroad.com/l/vccdmm)
### Mouth Mask [Download latest pre-built version with CUDA support](https://hacksider.gumroad.com/l/vccdmm) - No Manual Installation/Downloading required.
**Retain your original mouth for accurate movement using Mouth Mask**
<p align="center">
<img src="media/ludwig.gif" alt="resizable-gif">
</p>
### Face Mapping
**Use different faces on multiple subjects simultaneously**
<p align="center">
<img src="media/streamers.gif" alt="face_mapping_source">
</p>
### Your Movie, Your Face
**Watch movies with any face in real-time**
<p align="center">
<img src="media/movie.gif" alt="movie">
</p>
### Live Show
**Run Live shows and performances**
<p align="center">
<img src="media/live_show.gif" alt="show">
</p>
### Memes
**Create Your Most Viral Meme Yet**
<p align="center">
<img src="media/meme.gif" alt="show" width="450">
<br>
<sub>Created using Many Faces feature in Deep-Live-Cam</sub>
</p>
### Omegle
**Surprise people on Omegle**
<p align="center">
<video src="https://github.com/user-attachments/assets/2e9b9b82-fa04-4b70-9f56-b1f68e7672d0" width="450" controls></video>
</p>
## Installation (Manual) ## Installation (Manual)
**Please be aware that the installation needs technical skills and is NOT for beginners, consider downloading the prebuilt. Please do NOT open platform and installation related issues on GitHub before discussing it on the discord server.**
**Please be aware that the installation requires technical skills and is not for beginners. Consider downloading the prebuilt version.** ### Basic Installation (CPU)
<details>
<summary>Click to see the process</summary>
### Installation
This is more likely to work on your computer but will be slower as it utilizes the CPU. This is more likely to work on your computer but will be slower as it utilizes the CPU.
**1. Set up Your Platform** **1. Setup Your Platform**
- Python (3.10 recommended) - Python (3.10 recommended)
- pip - pip
- git - git
- [ffmpeg](https://www.youtube.com/watch?v=OlNWCpFdVMA) - ```iex (irm ffmpeg.tc.ht)``` - [ffmpeg](https://www.youtube.com/watch?v=OlNWCpFdVMA)
- [Visual Studio 2022 Runtimes (Windows)](https://visualstudio.microsoft.com/visual-cpp-build-tools/) - [Visual Studio 2022 Runtimes (Windows)](https://visualstudio.microsoft.com/visual-cpp-build-tools/)
**2. Clone the Repository** **2. Clone Repository**
```bash ```bash
git clone https://github.com/hacksider/Deep-Live-Cam.git https://github.com/hacksider/Deep-Live-Cam.git
cd Deep-Live-Cam
``` ```
**3. Download the Models** **3. Download Models**
1. [GFPGANv1.4](https://huggingface.co/hacksider/deep-live-cam/resolve/main/GFPGANv1.4.pth) 1. [GFPGANv1.4](https://huggingface.co/hacksider/deep-live-cam/resolve/main/GFPGANv1.4.pth)
2. [inswapper\_128\_fp16.onnx](https://huggingface.co/hacksider/deep-live-cam/resolve/main/inswapper_128_fp16.onnx) 2. [inswapper_128_fp16.onnx](https://huggingface.co/hacksider/deep-live-cam/resolve/main/inswapper_128.onnx) (Note: Use this [replacement version](https://github.com/facefusion/facefusion-assets/releases/download/models/inswapper_128.onnx) if you encounter issues)
Place these files in the "**models**" folder. Place these files in the "**models**" folder.
@@ -126,112 +55,57 @@ Place these files in the "**models**" folder.
We highly recommend using a `venv` to avoid issues. We highly recommend using a `venv` to avoid issues.
For Windows:
```bash ```bash
python -m venv venv
venv\Scripts\activate
pip install -r requirements.txt pip install -r requirements.txt
``` ```
**For macOS:** **For macOS:** Install or upgrade the `python-tk` package:
Apple Silicon (M1/M2/M3) requires specific setup:
```bash ```bash
# Install Python 3.10 (specific version is important)
brew install python@3.10
# Install tkinter package (required for the GUI)
brew install python-tk@3.10 brew install python-tk@3.10
# Create and activate virtual environment with Python 3.10
python3.10 -m venv venv
source venv/bin/activate
# Install dependencies
pip install -r requirements.txt
```
** In case something goes wrong and you need to reinstall the virtual environment **
```bash
# Deactivate the virtual environment
rm -rf venv
# Reinstall the virtual environment
python -m venv venv
source venv/bin/activate
# install the dependencies again
pip install -r requirements.txt
``` ```
**Run:** If you don't have a GPU, you can run Deep-Live-Cam using `python run.py`. Note that initial execution will download models (~300MB). **Run:** If you don't have a GPU, you can run Deep-Live-Cam using `python run.py`. Note that initial execution will download models (~300MB).
### GPU Acceleration
### GPU Acceleration (Optional)
<details>
<summary>Click to see the details</summary>
**CUDA Execution Provider (Nvidia)** **CUDA Execution Provider (Nvidia)**
1. Install [CUDA Toolkit 11.8.0](https://developer.nvidia.com/cuda-11-8-0-download-archive) 1. Install [CUDA Toolkit 11.8](https://developer.nvidia.com/cuda-11-8-0-download-archive)
2. Install dependencies: 2. Install dependencies:
```bash ```bash
pip uninstall onnxruntime onnxruntime-gpu pip uninstall onnxruntime onnxruntime-gpu
pip install onnxruntime-gpu==1.16.3 pip install onnxruntime-gpu==1.16.3
``` ```
3. Usage: 3. Usage:
```bash ```bash
python run.py --execution-provider cuda python run.py --execution-provider cuda
``` ```
**CoreML Execution Provider (Apple Silicon)** **CoreML Execution Provider (Apple Silicon)**
Apple Silicon (M1/M2/M3) specific installation: 1. Install dependencies:
1. Make sure you've completed the macOS setup above using Python 3.10.
2. Install dependencies:
```bash ```bash
pip uninstall onnxruntime onnxruntime-silicon pip uninstall onnxruntime onnxruntime-silicon
pip install onnxruntime-silicon==1.13.1 pip install onnxruntime-silicon==1.13.1
``` ```
2. Usage:
3. Usage (important: specify Python 3.10):
```bash ```bash
python3.10 run.py --execution-provider coreml python run.py --execution-provider coreml
``` ```
**Important Notes for macOS:**
- You **must** use Python 3.10, not newer versions like 3.11 or 3.13
- Always run with `python3.10` command not just `python` if you have multiple Python versions installed
- If you get error about `_tkinter` missing, reinstall the tkinter package: `brew reinstall python-tk@3.10`
- If you get model loading errors, check that your models are in the correct folder
- If you encounter conflicts with other Python versions, consider uninstalling them:
```bash
# List all installed Python versions
brew list | grep python
# Uninstall conflicting versions if needed
brew uninstall --ignore-dependencies python@3.11 python@3.13
# Keep only Python 3.10
brew cleanup
```
**CoreML Execution Provider (Apple Legacy)** **CoreML Execution Provider (Apple Legacy)**
1. Install dependencies: 1. Install dependencies:
```bash ```bash
pip uninstall onnxruntime onnxruntime-coreml pip uninstall onnxruntime onnxruntime-coreml
pip install onnxruntime-coreml==1.13.1 pip install onnxruntime-coreml==1.13.1
``` ```
2. Usage: 2. Usage:
```bash ```bash
python run.py --execution-provider coreml python run.py --execution-provider coreml
``` ```
@@ -239,14 +113,11 @@ python run.py --execution-provider coreml
**DirectML Execution Provider (Windows)** **DirectML Execution Provider (Windows)**
1. Install dependencies: 1. Install dependencies:
```bash ```bash
pip uninstall onnxruntime onnxruntime-directml pip uninstall onnxruntime onnxruntime-directml
pip install onnxruntime-directml==1.15.1 pip install onnxruntime-directml==1.15.1
``` ```
2. Usage: 2. Usage:
```bash ```bash
python run.py --execution-provider directml python run.py --execution-provider directml
``` ```
@@ -254,51 +125,75 @@ python run.py --execution-provider directml
**OpenVINO™ Execution Provider (Intel)** **OpenVINO™ Execution Provider (Intel)**
1. Install dependencies: 1. Install dependencies:
```bash ```bash
pip uninstall onnxruntime onnxruntime-openvino pip uninstall onnxruntime onnxruntime-openvino
pip install onnxruntime-openvino==1.15.0 pip install onnxruntime-openvino==1.15.0
``` ```
2. Usage: 2. Usage:
```bash ```bash
python run.py --execution-provider openvino python run.py --execution-provider openvino
``` ```
</details> </details>
## Usage ## Usage
**1. Image/Video Mode** **1. Image/Video Mode**
- Execute `python run.py`. - Execute `python run.py`.
- Choose a source face image and a target image/video. - Choose a source face image and a target image/video.
- Click "Start". - Click "Start".
- The output will be saved in a directory named after the target video. - The output will be saved in a directory named after the target video.
**2. Webcam Mode** **2. Webcam Mode**
- Execute `python run.py`. - Execute `python run.py`.
- Select a source face image. - Select a source face image.
- Click "Live". - Click "Live".
- Wait for the preview to appear (10-30 seconds). - Wait for the preview to appear (10-30 seconds).
- Use a screen capture tool like OBS to stream. - Use a screen capture tool like OBS to stream.
- To change the face, select a new source image. - To change the face, select a new source image.
## Tips and Tricks ![demo-gif](media/demo.gif)
Check out these helpful guides to get the most out of Deep-Live-Cam: ## Features
- [Unlocking the Secrets to the Perfect Deepfake Image](https://deeplivecam.net/index.php/blog/tips-and-tricks/unlocking-the-secrets-to-the-perfect-deepfake-image) - Learn how to create the best deepfake with full head coverage ### Resizable Preview Window
- [Video Call with DeepLiveCam](https://deeplivecam.net/index.php/blog/tips-and-tricks/video-call-with-deeplivecam) - Make your meetings livelier by using DeepLiveCam with OBS and meeting software
- [Have a Special Guest!](https://deeplivecam.net/index.php/blog/tips-and-tricks/have-a-special-guest) - Tutorial on how to use face mapping to add special guests to your stream
- [Watch Deepfake Movies in Realtime](https://deeplivecam.net/index.php/blog/tips-and-tricks/watch-deepfake-movies-in-realtime) - See yourself star in any video without processing the video
- [Better Quality without Sacrificing Speed](https://deeplivecam.net/index.php/blog/tips-and-tricks/better-quality-without-sacrificing-speed) - Tips for achieving better results without impacting performance
- [Instant Vtuber!](https://deeplivecam.net/index.php/blog/tips-and-tricks/instant-vtuber) - Create a new persona/vtuber easily using Metahuman Creator
Visit our [official blog](https://deeplivecam.net/index.php/blog/tips-and-tricks) for more tips and tutorials. Dynamically improve performance using the `--live-resizable` parameter.
## Command Line Arguments (Unmaintained) ![resizable-gif](media/resizable.gif)
### Face Mapping
Track and change faces on the fly.
![face_mapping_source](media/face_mapping_source.gif)
**Source Video:**
![face-mapping](media/face_mapping.png)
**Enable Face Mapping:**
![face-mapping2](media/face_mapping2.png)
**Map the Faces:**
![face_mapping_result](media/face_mapping_result.gif)
**See the Magic!**
![movie](media/movie.gif)
**Watch movies in realtime:**
It's as simple as opening a movie on the screen, and selecting OBS as your camera!
![image](media/movie_img.png)
## Command Line Arguments
``` ```
options: options:
@@ -312,7 +207,7 @@ options:
--keep-frames keep temporary frames --keep-frames keep temporary frames
--many-faces process every face --many-faces process every face
--map-faces map source target faces --map-faces map source target faces
--mouth-mask mask the mouth region --nsfw-filter filter the NSFW image or video
--video-encoder {libx264,libx265,libvpx-vp9} adjust output video encoder --video-encoder {libx264,libx265,libvpx-vp9} adjust output video encoder
--video-quality [0-51] adjust output video quality --video-quality [0-51] adjust output video quality
--live-mirror the live camera display as you see it in the front-facing camera frame --live-mirror the live camera display as you see it in the front-facing camera frame
@@ -325,44 +220,188 @@ options:
Looking for a CLI mode? Using the -s/--source argument will make the run program in cli mode. Looking for a CLI mode? Using the -s/--source argument will make the run program in cli mode.
## Press
**We are always open to criticism and are ready to improve, that's why we didn't cherry-pick anything.** ## Webcam Mode on WSL2 Ubuntu (Optional)
<details>
<summary>Click to see the details</summary>
If you want to use WSL2 on Windows 11 you will notice, that Ubuntu WSL2 doesn't come with USB-Webcam support in the Kernel. You need to do two things: Compile the Kernel with the right modules integrated and forward your USB Webcam from Windows to Ubuntu with the usbipd app. Here are detailed Steps:
This tutorial will guide you through the process of setting up WSL2 Ubuntu with USB webcam support, rebuilding the kernel, and preparing the environment for the Deep-Live-Cam project.
**1. Install WSL2 Ubuntu**
Install WSL2 Ubuntu from the Microsoft Store or using PowerShell:
**2. Enable USB Support in WSL2**
1. Install the USB/IP tool for Windows:
[https://learn.microsoft.com/en-us/windows/wsl/connect-usb](https://learn.microsoft.com/en-us/windows/wsl/connect-usb)
2. In Windows PowerShell (as Administrator), connect your webcam to WSL:
```powershell
usbipd list
usbipd bind --busid x-x # Replace x-x with your webcam's bus ID
usbipd attach --wsl --busid x-x # Replace x-x with your webcam's bus ID
```
You need to redo the above every time you reboot wsl or re-connect your webcam/usb device.
**3. Rebuild WSL2 Ubuntu Kernel with USB and Webcam Modules**
Follow these steps to rebuild the kernel:
1. Start with this guide: [https://github.com/PINTO0309/wsl2_linux_kernel_usbcam_enable_conf](https://github.com/PINTO0309/wsl2_linux_kernel_usbcam_enable_conf)
2. When you reach the `sudo wget [github.com](http://github.com/)...PINTO0309` step, which won't work for newer kernel versions, follow this video instead or alternatively follow the video tutorial from the beginning:
[https://www.youtube.com/watch?v=t_YnACEPmrM](https://www.youtube.com/watch?v=t_YnACEPmrM)
Additional info: [https://askubuntu.com/questions/1413377/camera-not-working-in-cheese-in-wsl2](https://askubuntu.com/questions/1413377/camera-not-working-in-cheese-in-wsl2)
3. After rebuilding, restart WSL with the new kernel.
**4. Set Up Deep-Live-Cam Project**
Within Ubuntu:
1. Clone the repository:
```bash
git clone [https://github.com/hacksider/Deep-Live-Cam](https://github.com/hacksider/Deep-Live-Cam)
```
2. Follow the installation instructions in the repository, including cuda toolkit 11.8, make 100% sure it's not cuda toolkit 12.x.
**5. Verify and Load Kernel Modules**
1. Check if USB and webcam modules are built into the kernel:
```bash
zcat /proc/config.gz | grep -i "CONFIG_USB_VIDEO_CLASS"
```
2. If modules are loadable (m), not built-in (y), check if the file exists:
```bash
ls /lib/modules/$(uname -r)/kernel/drivers/media/usb/uvc/
```
3. Load the module and check for errors (optional if built-in):
```bash
sudo modprobe uvcvideo
dmesg | tail
```
4. Verify video devices:
```bash
sudo ls -al /dev/video*
```
**6. Set Up Permissions**
1. Add user to video group and set permissions:
```bash
sudo usermod -a -G video $USER
sudo chgrp video /dev/video0 /dev/video1
sudo chmod 660 /dev/video0 /dev/video1
```
2. Create a udev rule for permanent permissions:
```bash
sudo nano /etc/udev/rules.d/81-webcam.rules
```
Add this content:
```
KERNEL=="video[0-9]*", GROUP="video", MODE="0660"
```
3. Reload udev rules:
```bash
sudo udevadm control --reload-rules && sudo udevadm trigger
```
4. Log out and log back into your WSL session.
5. Start Deep-Live-Cam with `python run.py --execution-provider cuda --max-memory 8` where 8 can be changed to the number of GB VRAM of your GPU has, minus 1-2GB. If you have a RTX3080 with 10GB I suggest adding 8GB. Leave some left for Windows.
**Final Notes**
- Steps 6 and 7 may be optional if the modules are built into the kernel and permissions are already set correctly.
- Always ensure you're using compatible versions of CUDA, ONNX, and other dependencies.
- If issues persist, consider checking the Deep-Live-Cam project's specific requirements and troubleshooting steps.
By following these steps, you should have a WSL2 Ubuntu environment with USB webcam support ready for the Deep-Live-Cam project. If you encounter any issues, refer back to the specific error messages and troubleshooting steps provided.
**Troubleshooting CUDA Issues**
If you encounter this error:
```
[ONNXRuntimeError] : 1 : FAIL : Failed to load library [libonnxruntime_providers_cuda.so](http://libonnxruntime_providers_cuda.so/) with error: libcufft.so.10: cannot open shared object file: No such file or directory
```
Follow these steps:
1. Install CUDA Toolkit 11.8 (ONNX 1.16.3 requires CUDA 11.x, not 12.x):
[https://developer.nvidia.com/cuda-11-8-0-download-archive](https://developer.nvidia.com/cuda-11-8-0-download-archive)
select: Linux, x86_64, WSL-Ubuntu, 2.0, deb (local)
2. Check CUDA version:
```bash
/usr/local/cuda/bin/nvcc --version
```
3. If the wrong version is installed, remove it completely:
[https://askubuntu.com/questions/530043/removing-nvidia-cuda-toolkit-and-installing-new-one](https://askubuntu.com/questions/530043/removing-nvidia-cuda-toolkit-and-installing-new-one)
4. Install CUDA Toolkit 11.8 again [https://developer.nvidia.com/cuda-11-8-0-download-archive](https://developer.nvidia.com/cuda-11-8-0-download-archive), select: Linux, x86_64, WSL-Ubuntu, 2.0, deb (local)
```bash
sudo apt-get -y install cuda-toolkit-11-8
```
</details>
## Future Updates & Roadmap
For the latest experimental builds and features, see the [experimental branch](https://github.com/hacksider/Deep-Live-Cam/tree/experimental).
**TODO:**
- [ ] Develop a version for web app/service
- [ ] 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.
**Tips and Links:**
- [How to make the most of Deep-Live-Cam](https://hacksider.gumroad.com/p/how-to-make-the-most-on-deep-live-cam)
- Face enhancer is good, but still very slow for any live streaming purpose.
- [*"Deep-Live-Cam goes viral, allowing anyone to become a digital doppelganger"*](https://arstechnica.com/information-technology/2024/08/new-ai-tool-enables-real-time-face-swapping-on-webcams-raising-fraud-concerns/) - Ars Technica
- [*"Thanks Deep Live Cam, shapeshifters are among us now"*](https://dataconomy.com/2024/08/15/what-is-deep-live-cam-github-deepfake/) - Dataconomy
- [*"This free AI tool lets you become anyone during video-calls"*](https://www.newsbytesapp.com/news/science/deep-live-cam-ai-impersonation-tool-goes-viral/story) - NewsBytes
- [*"OK, this viral AI live stream software is truly terrifying"*](https://www.creativebloq.com/ai/ok-this-viral-ai-live-stream-software-is-truly-terrifying) - Creative Bloq
- [*"Deepfake AI Tool Lets You Become Anyone in a Video Call With Single Photo"*](https://petapixel.com/2024/08/14/deep-live-cam-deepfake-ai-tool-lets-you-become-anyone-in-a-video-call-with-single-photo-mark-zuckerberg-jd-vance-elon-musk/) - PetaPixel
- [*"Deep-Live-Cam Uses AI to Transform Your Face in Real-Time, Celebrities Included"*](https://www.techeblog.com/deep-live-cam-ai-transform-face/) - TechEBlog
- [*"An AI tool that "makes you look like anyone" during a video call is going viral online"*](https://telegrafi.com/en/a-tool-that-makes-you-look-like-anyone-during-a-video-call-is-going-viral-on-the-Internet/) - Telegrafi
- [*"This Deepfake Tool Turning Images Into Livestreams is Topping the GitHub Charts"*](https://decrypt.co/244565/this-deepfake-tool-turning-images-into-livestreams-is-topping-the-github-charts) - Emerge
- [*"New Real-Time Face-Swapping AI Allows Anyone to Mimic Famous Faces"*](https://www.digitalmusicnews.com/2024/08/15/face-swapping-ai-real-time-mimic/) - Digital Music News
- [*"This real-time webcam deepfake tool raises alarms about the future of identity theft"*](https://www.diyphotography.net/this-real-time-webcam-deepfake-tool-raises-alarms-about-the-future-of-identity-theft/) - DIYPhotography
- [*"That's Crazy, Oh God. That's Fucking Freaky Dude... That's So Wild Dude"*](https://www.youtube.com/watch?time_continue=1074&v=py4Tc-Y8BcY) - SomeOrdinaryGamers
- [*"Alright look look look, now look chat, we can do any face we want to look like chat"*](https://www.youtube.com/live/mFsCe7AIxq8?feature=shared&t=2686) - IShowSpeed
## Credits ## Credits
- [ffmpeg](https://ffmpeg.org/): for making video-related operations easy - [ffmpeg](https://ffmpeg.org/): for making video related operations easy
- [deepinsight](https://github.com/deepinsight): for their [insightface](https://github.com/deepinsight/insightface) project which provided a well-made library and models. Please be reminded that the [use of the model is for non-commercial research purposes only](https://github.com/deepinsight/insightface?tab=readme-ov-file#license). - [deepinsight](https://github.com/deepinsight): for their [insightface](https://github.com/deepinsight/insightface) project which provided a well-made library and models. Please be reminded that the [use of the model is for non-commercial research purposes only](https://github.com/deepinsight/insightface?tab=readme-ov-file#license).
- [havok2-htwo](https://github.com/havok2-htwo): for sharing the code for webcam - [havok2-htwo](https://github.com/havok2-htwo) : for sharing the code for webcam
- [GosuDRM](https://github.com/GosuDRM): for the open version of roop - [GosuDRM](https://github.com/GosuDRM) : for open version of roop
- [pereiraroland26](https://github.com/pereiraroland26): Multiple faces support - [pereiraroland26](https://github.com/pereiraroland26) : Multiple faces support
- [vic4key](https://github.com/vic4key): For supporting/contributing to this project - [vic4key](https://github.com/vic4key) : For supporting/contributing on this project
- [kier007](https://github.com/kier007): for improving the user experience - [KRSHH](https://github.com/KRSHH) : For updating the UI
- [qitianai](https://github.com/qitianai): for multi-lingual support - and [all developers](https://github.com/hacksider/Deep-Live-Cam/graphs/contributors) behind libraries used in this project.
- and [all developers](https://github.com/hacksider/Deep-Live-Cam/graphs/contributors) behind libraries used in this project. - Foot Note: [This is originally roop-cam, see the full history of the code here.](https://github.com/hacksider/roop-cam) Please be informed that the base author of the code is [s0md3v](https://github.com/s0md3v/roop)
- Footnote: Please be informed that the base author of the code is [s0md3v](https://github.com/s0md3v/roop)
- All the wonderful users who helped make this project go viral by starring the repo ❤️
[![Stargazers](https://reporoster.com/stars/hacksider/Deep-Live-Cam)](https://github.com/hacksider/Deep-Live-Cam/stargazers)
## Contributions ## Contributions
![Alt](https://repobeats.axiom.co/api/embed/fec8e29c45dfdb9c5916f3a7830e1249308d20e1.svg "Repobeats analytics image") ![Alt](https://repobeats.axiom.co/api/embed/fec8e29c45dfdb9c5916f3a7830e1249308d20e1.svg "Repobeats analytics image")
## Star History
## Stars to the Moon 🚀
<a href="https://star-history.com/#hacksider/deep-live-cam&Date"> <a href="https://star-history.com/#hacksider/deep-live-cam&Date">
<picture> <picture>
-46
View File
@@ -1,46 +0,0 @@
{
"Source x Target Mapper": "Source x Target Mapper",
"select an source image": "选择一个源图像",
"Preview": "预览",
"select an target image or video": "选择一个目标图像或视频",
"save image output file": "保存图像输出文件",
"save video output file": "保存视频输出文件",
"select an target image": "选择一个目标图像",
"source": "源",
"Select a target": "选择一个目标",
"Select a face": "选择一张脸",
"Keep audio": "保留音频",
"Face Enhancer": "面纹增强器",
"Many faces": "多脸",
"Show FPS": "显示帧率",
"Keep fps": "保持帧率",
"Keep frames": "保持帧数",
"Fix Blueish Cam": "修复偏蓝的摄像头",
"Mouth Mask": "口罩",
"Show Mouth Mask Box": "显示口罩盒",
"Start": "开始",
"Live": "直播",
"Destroy": "结束",
"Map faces": "识别人脸",
"Processing...": "处理中...",
"Processing succeed!": "处理成功!",
"Processing ignored!": "处理被忽略!",
"Failed to start camera": "启动相机失败",
"Please complete pop-up or close it.": "请先完成弹出窗口或者关闭它",
"Getting unique faces": "获取独特面部",
"Please select a source image first": "请先选择一个源图像",
"No faces found in target": "目标图像中没有人脸",
"Add": "添加",
"Clear": "清除",
"Submit": "确认",
"Select source image": "请选取源图像",
"Select target image": "请选取目标图像",
"Please provide mapping!": "请提供映射",
"Atleast 1 source with target is required!": "至少需要一个来源图像与目标图像相关!",
"At least 1 source with target is required!": "至少需要一个来源图像与目标图像相关!",
"Face could not be detected in last upload!": "最近上传的图像中没有检测到人脸!",
"Select Camera:": "选择摄像头",
"All mappings cleared!": "所有映射均已清除!",
"Mappings successfully submitted!": "成功提交映射!",
"Source x Target Mapper is already open.": "源 x 目标映射器已打开。"
}
Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.8 MiB

BIN
View File
Binary file not shown.
Binary file not shown.

After

Width:  |  Height:  |  Size: 76 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 104 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.0 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.2 MiB

After

Width:  |  Height:  |  Size: 8.6 MiB

BIN
View File
Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.3 MiB

BIN
View File
Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.0 MiB

BIN
View File
Binary file not shown.

Before

Width:  |  Height:  |  Size: 14 MiB

After

Width:  |  Height:  |  Size: 1.6 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 794 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.3 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 13 MiB

+1 -1
View File
@@ -1,4 +1,4 @@
just put the models in this folder - just put the models in this folder -
https://huggingface.co/hacksider/deep-live-cam/resolve/main/inswapper_128_fp16.onnx?download=true https://huggingface.co/hacksider/deep-live-cam/resolve/main/inswapper_128_fp16.onnx?download=true
https://github.com/TencentARC/GFPGAN/releases/download/v1.3.4/GFPGANv1.4.pth https://github.com/TencentARC/GFPGAN/releases/download/v1.3.4/GFPGANv1.4.pth
+1 -5
View File
@@ -41,10 +41,8 @@ def parse_args() -> None:
program.add_argument('--many-faces', help='process every face', dest='many_faces', action='store_true', default=False) program.add_argument('--many-faces', help='process every face', dest='many_faces', action='store_true', default=False)
program.add_argument('--nsfw-filter', help='filter the NSFW image or video', dest='nsfw_filter', action='store_true', default=False) program.add_argument('--nsfw-filter', help='filter the NSFW image or video', dest='nsfw_filter', action='store_true', default=False)
program.add_argument('--map-faces', help='map source target faces', dest='map_faces', action='store_true', default=False) program.add_argument('--map-faces', help='map source target faces', dest='map_faces', action='store_true', default=False)
program.add_argument('--mouth-mask', help='mask the mouth region', dest='mouth_mask', action='store_true', default=False)
program.add_argument('--video-encoder', help='adjust output video encoder', dest='video_encoder', default='libx264', choices=['libx264', 'libx265', 'libvpx-vp9']) program.add_argument('--video-encoder', help='adjust output video encoder', dest='video_encoder', default='libx264', choices=['libx264', 'libx265', 'libvpx-vp9'])
program.add_argument('--video-quality', help='adjust output video quality', dest='video_quality', type=int, default=18, choices=range(52), metavar='[0-51]') program.add_argument('--video-quality', help='adjust output video quality', dest='video_quality', type=int, default=18, choices=range(52), metavar='[0-51]')
program.add_argument('-l', '--lang', help='Ui language', default="en")
program.add_argument('--live-mirror', help='The live camera display as you see it in the front-facing camera frame', dest='live_mirror', action='store_true', default=False) program.add_argument('--live-mirror', help='The live camera display as you see it in the front-facing camera frame', dest='live_mirror', action='store_true', default=False)
program.add_argument('--live-resizable', help='The live camera frame is resizable', dest='live_resizable', action='store_true', default=False) program.add_argument('--live-resizable', help='The live camera frame is resizable', dest='live_resizable', action='store_true', default=False)
program.add_argument('--max-memory', help='maximum amount of RAM in GB', dest='max_memory', type=int, default=suggest_max_memory()) program.add_argument('--max-memory', help='maximum amount of RAM in GB', dest='max_memory', type=int, default=suggest_max_memory())
@@ -69,7 +67,6 @@ def parse_args() -> None:
modules.globals.keep_audio = args.keep_audio modules.globals.keep_audio = args.keep_audio
modules.globals.keep_frames = args.keep_frames modules.globals.keep_frames = args.keep_frames
modules.globals.many_faces = args.many_faces modules.globals.many_faces = args.many_faces
modules.globals.mouth_mask = args.mouth_mask
modules.globals.nsfw_filter = args.nsfw_filter modules.globals.nsfw_filter = args.nsfw_filter
modules.globals.map_faces = args.map_faces modules.globals.map_faces = args.map_faces
modules.globals.video_encoder = args.video_encoder modules.globals.video_encoder = args.video_encoder
@@ -79,7 +76,6 @@ def parse_args() -> None:
modules.globals.max_memory = args.max_memory modules.globals.max_memory = args.max_memory
modules.globals.execution_providers = decode_execution_providers(args.execution_provider) modules.globals.execution_providers = decode_execution_providers(args.execution_provider)
modules.globals.execution_threads = args.execution_threads modules.globals.execution_threads = args.execution_threads
modules.globals.lang = args.lang
#for ENHANCER tumbler: #for ENHANCER tumbler:
if 'face_enhancer' in args.frame_processor: if 'face_enhancer' in args.frame_processor:
@@ -255,5 +251,5 @@ def run() -> None:
if modules.globals.headless: if modules.globals.headless:
start() start()
else: else:
window = ui.init(start, destroy, modules.globals.lang) window = ui.init(start, destroy)
window.mainloop() window.mainloop()
+12 -12
View File
@@ -39,13 +39,13 @@ def get_many_faces(frame: Frame) -> Any:
return None return None
def has_valid_map() -> bool: def has_valid_map() -> bool:
for map in modules.globals.source_target_map: for map in modules.globals.souce_target_map:
if "source" in map and "target" in map: if "source" in map and "target" in map:
return True return True
return False return False
def default_source_face() -> Any: def default_source_face() -> Any:
for map in modules.globals.source_target_map: for map in modules.globals.souce_target_map:
if "source" in map: if "source" in map:
return map['source']['face'] return map['source']['face']
return None return None
@@ -53,7 +53,7 @@ def default_source_face() -> Any:
def simplify_maps() -> Any: def simplify_maps() -> Any:
centroids = [] centroids = []
faces = [] faces = []
for map in modules.globals.source_target_map: for map in modules.globals.souce_target_map:
if "source" in map and "target" in map: if "source" in map and "target" in map:
centroids.append(map['target']['face'].normed_embedding) centroids.append(map['target']['face'].normed_embedding)
faces.append(map['source']['face']) faces.append(map['source']['face'])
@@ -64,10 +64,10 @@ def simplify_maps() -> Any:
def add_blank_map() -> Any: def add_blank_map() -> Any:
try: try:
max_id = -1 max_id = -1
if len(modules.globals.source_target_map) > 0: if len(modules.globals.souce_target_map) > 0:
max_id = max(modules.globals.source_target_map, key=lambda x: x['id'])['id'] max_id = max(modules.globals.souce_target_map, key=lambda x: x['id'])['id']
modules.globals.source_target_map.append({ modules.globals.souce_target_map.append({
'id' : max_id + 1 'id' : max_id + 1
}) })
except ValueError: except ValueError:
@@ -75,14 +75,14 @@ def add_blank_map() -> Any:
def get_unique_faces_from_target_image() -> Any: def get_unique_faces_from_target_image() -> Any:
try: try:
modules.globals.source_target_map = [] modules.globals.souce_target_map = []
target_frame = cv2.imread(modules.globals.target_path) target_frame = cv2.imread(modules.globals.target_path)
many_faces = get_many_faces(target_frame) many_faces = get_many_faces(target_frame)
i = 0 i = 0
for face in many_faces: for face in many_faces:
x_min, y_min, x_max, y_max = face['bbox'] x_min, y_min, x_max, y_max = face['bbox']
modules.globals.source_target_map.append({ modules.globals.souce_target_map.append({
'id' : i, 'id' : i,
'target' : { 'target' : {
'cv2' : target_frame[int(y_min):int(y_max), int(x_min):int(x_max)], 'cv2' : target_frame[int(y_min):int(y_max), int(x_min):int(x_max)],
@@ -96,7 +96,7 @@ def get_unique_faces_from_target_image() -> Any:
def get_unique_faces_from_target_video() -> Any: def get_unique_faces_from_target_video() -> Any:
try: try:
modules.globals.source_target_map = [] modules.globals.souce_target_map = []
frame_face_embeddings = [] frame_face_embeddings = []
face_embeddings = [] face_embeddings = []
@@ -127,7 +127,7 @@ def get_unique_faces_from_target_video() -> Any:
face['target_centroid'] = closest_centroid_index face['target_centroid'] = closest_centroid_index
for i in range(len(centroids)): for i in range(len(centroids)):
modules.globals.source_target_map.append({ modules.globals.souce_target_map.append({
'id' : i 'id' : i
}) })
@@ -135,7 +135,7 @@ def get_unique_faces_from_target_video() -> Any:
for frame in tqdm(frame_face_embeddings, desc=f"Mapping frame embeddings to centroids-{i}"): for frame in tqdm(frame_face_embeddings, desc=f"Mapping frame embeddings to centroids-{i}"):
temp.append({'frame': frame['frame'], 'faces': [face for face in frame['faces'] if face['target_centroid'] == i], 'location': frame['location']}) temp.append({'frame': frame['frame'], 'faces': [face for face in frame['faces'] if face['target_centroid'] == i], 'location': frame['location']})
modules.globals.source_target_map[i]['target_faces_in_frame'] = temp modules.globals.souce_target_map[i]['target_faces_in_frame'] = temp
# dump_faces(centroids, frame_face_embeddings) # dump_faces(centroids, frame_face_embeddings)
default_target_face() default_target_face()
@@ -144,7 +144,7 @@ def get_unique_faces_from_target_video() -> Any:
def default_target_face(): def default_target_face():
for map in modules.globals.source_target_map: for map in modules.globals.souce_target_map:
best_face = None best_face = None
best_frame = None best_frame = None
for frame in map['target_faces_in_frame']: for frame in map['target_faces_in_frame']:
-26
View File
@@ -1,26 +0,0 @@
import json
from pathlib import Path
class LanguageManager:
def __init__(self, default_language="en"):
self.current_language = default_language
self.translations = {}
self.load_language(default_language)
def load_language(self, language_code) -> bool:
"""load language file"""
if language_code == "en":
return True
try:
file_path = Path(__file__).parent.parent / f"locales/{language_code}.json"
with open(file_path, "r", encoding="utf-8") as file:
self.translations = json.load(file)
self.current_language = language_code
return True
except FileNotFoundError:
print(f"Language file not found: {language_code}")
return False
def _(self, key, default=None) -> str:
"""get translate text"""
return self.translations.get(key, default if default else key)
+17 -14
View File
@@ -9,35 +9,38 @@ file_types = [
("Video", ("*.mp4", "*.mkv")), ("Video", ("*.mp4", "*.mkv")),
] ]
source_target_map = [] souce_target_map = []
simple_map = {} simple_map = {}
source_path = None source_path = None
target_path = None target_path = None
output_path = None output_path = None
frame_processors: List[str] = [] frame_processors: List[str] = []
keep_fps = True keep_fps = True # Initialize with default value
keep_audio = True keep_audio = True # Initialize with default value
keep_frames = False keep_frames = False # Initialize with default value
many_faces = False many_faces = False # Initialize with default value
map_faces = False map_faces = False # Initialize with default value
color_correction = False # New global variable for color correction toggle color_correction = False # Initialize with default value
nsfw_filter = False nsfw_filter = False # Initialize with default value
video_encoder = None video_encoder = None
video_quality = None video_quality = None
live_mirror = False live_mirror = False # Initialize with default value
live_resizable = True live_resizable = False # Initialize with default value
max_memory = None max_memory = None
execution_providers: List[str] = [] execution_providers: List[str] = []
execution_threads = None execution_threads = None
headless = None headless = None
log_level = "error" log_level = "error"
fp_ui: Dict[str, bool] = {"face_enhancer": False} fp_ui: Dict[str, bool] = {"face_enhancer": False} # Initialize with default value
camera_input_combobox = None camera_input_combobox = None
webcam_preview_running = False webcam_preview_running = False
show_fps = False show_fps = False # Initialize with default value
mouth_mask = False mouth_mask = False
show_mouth_mask_box = False show_mouth_mask_box = False
mask_down_size = 0.5
mask_size = 1.0
mask_feather_ratio = 8 mask_feather_ratio = 8
mask_down_size = 0.50 opacity_switch = False
mask_size = 1 face_opacity = 100
selected_camera = None
+3 -3
View File
@@ -1,3 +1,3 @@
name = 'Deep-Live-Cam' name = 'Deep Live Cam'
version = '1.9' version = '1.6.0'
edition = 'GitHub Edition' edition = 'Portable'
+17 -47
View File
@@ -9,41 +9,23 @@ import modules.processors.frame.core
from modules.core import update_status from modules.core import update_status
from modules.face_analyser import get_one_face from modules.face_analyser import get_one_face
from modules.typing import Frame, Face from modules.typing import Frame, Face
import platform from modules.utilities import conditional_download, resolve_relative_path, is_image, is_video
import torch
from modules.utilities import (
conditional_download,
is_image,
is_video,
)
FACE_ENHANCER = None FACE_ENHANCER = None
THREAD_SEMAPHORE = threading.Semaphore() THREAD_SEMAPHORE = threading.Semaphore()
THREAD_LOCK = threading.Lock() THREAD_LOCK = threading.Lock()
NAME = "DLC.FACE-ENHANCER" NAME = 'DLC.FACE-ENHANCER'
abs_dir = os.path.dirname(os.path.abspath(__file__))
models_dir = os.path.join(
os.path.dirname(os.path.dirname(os.path.dirname(abs_dir))), "models"
)
def pre_check() -> bool: def pre_check() -> bool:
download_directory_path = models_dir download_directory_path = resolve_relative_path('..\models')
conditional_download( conditional_download(download_directory_path, ['https://github.com/TencentARC/GFPGAN/releases/download/v1.3.4/GFPGANv1.4.pth'])
download_directory_path,
[
"https://github.com/TencentARC/GFPGAN/releases/download/v1.3.4/GFPGANv1.4.pth"
],
)
return True return True
def pre_start() -> bool: def pre_start() -> bool:
if not is_image(modules.globals.target_path) and not is_video( if not is_image(modules.globals.target_path) and not is_video(modules.globals.target_path):
modules.globals.target_path update_status('Select an image or video for target path.', NAME)
):
update_status("Select an image or video for target path.", NAME)
return False return False
return True return True
@@ -53,24 +35,21 @@ def get_face_enhancer() -> Any:
with THREAD_LOCK: with THREAD_LOCK:
if FACE_ENHANCER is None: if FACE_ENHANCER is None:
model_path = os.path.join(models_dir, "GFPGANv1.4.pth") if os.name == 'nt':
model_path = resolve_relative_path('..\models\GFPGANv1.4.pth')
match platform.system(): # todo: set models path https://github.com/TencentARC/GFPGAN/issues/399
case "Darwin": # Mac OS else:
if torch.backends.mps.is_available(): model_path = resolve_relative_path('../models/GFPGANv1.4.pth')
mps_device = torch.device("mps") FACE_ENHANCER = gfpgan.GFPGANer(model_path=model_path, upscale=1) # type: ignore[attr-defined]
FACE_ENHANCER = gfpgan.GFPGANer(model_path=model_path, upscale=1, device=mps_device) # type: ignore[attr-defined]
else:
FACE_ENHANCER = gfpgan.GFPGANer(model_path=model_path, upscale=1) # type: ignore[attr-defined]
case _: # Other OS
FACE_ENHANCER = gfpgan.GFPGANer(model_path=model_path, upscale=1) # type: ignore[attr-defined]
return FACE_ENHANCER return FACE_ENHANCER
def enhance_face(temp_frame: Frame) -> Frame: def enhance_face(temp_frame: Frame) -> Frame:
with THREAD_SEMAPHORE: with THREAD_SEMAPHORE:
_, _, temp_frame = get_face_enhancer().enhance(temp_frame, paste_back=True) _, _, temp_frame = get_face_enhancer().enhance(
temp_frame,
paste_back=True
)
return temp_frame return temp_frame
@@ -81,9 +60,7 @@ def process_frame(source_face: Face, temp_frame: Frame) -> Frame:
return temp_frame return temp_frame
def process_frames( def process_frames(source_path: str, temp_frame_paths: List[str], progress: Any = None) -> None:
source_path: str, temp_frame_paths: List[str], progress: Any = None
) -> None:
for temp_frame_path in temp_frame_paths: for temp_frame_path in temp_frame_paths:
temp_frame = cv2.imread(temp_frame_path) temp_frame = cv2.imread(temp_frame_path)
result = process_frame(None, temp_frame) result = process_frame(None, temp_frame)
@@ -100,10 +77,3 @@ def process_image(source_path: str, target_path: str, output_path: str) -> None:
def process_video(source_path: str, temp_frame_paths: List[str]) -> None: def process_video(source_path: str, temp_frame_paths: List[str]) -> None:
modules.processors.frame.core.process_video(None, temp_frame_paths, process_frames) modules.processors.frame.core.process_video(None, temp_frame_paths, process_frames)
def process_frame_v2(temp_frame: Frame) -> Frame:
target_face = get_one_face(temp_frame)
if target_face:
temp_frame = enhance_face(temp_frame)
return temp_frame
+391 -156
View File
@@ -1,44 +1,50 @@
import os # <-- Added for os.path.exists
from typing import Any, List from typing import Any, List
import cv2 import cv2
import insightface import insightface
import threading import threading
import numpy as np
import modules.globals import modules.globals
import modules.processors.frame.core import modules.processors.frame.core
# Ensure update_status is imported if not already globally accessible
# If it's part of modules.core, it might already be accessible via modules.core.update_status
from modules.core import update_status from modules.core import update_status
from modules.face_analyser import get_one_face, get_many_faces, default_source_face from modules.face_analyser import get_one_face, get_many_faces, default_source_face
from modules.typing import Face, Frame from modules.typing import Face, Frame
from modules.utilities import conditional_download, resolve_relative_path, is_image, is_video from modules.utilities import (
conditional_download,
resolve_relative_path,
is_image,
is_video,
)
from modules.cluster_analysis import find_closest_centroid from modules.cluster_analysis import find_closest_centroid
FACE_SWAPPER = None FACE_SWAPPER = None
THREAD_LOCK = threading.Lock() THREAD_LOCK = threading.Lock()
NAME = 'DLC.FACE-SWAPPER' NAME = "DLC.FACE-SWAPPER"
def pre_check() -> bool: def pre_check() -> bool:
download_directory_path = resolve_relative_path('../models') download_directory_path = resolve_relative_path("../models")
# Ensure both models are mentioned or downloaded if necessary conditional_download(
# Conditional download might need adjustment if you want it to fetch FP32 too download_directory_path,
conditional_download(download_directory_path, ['https://huggingface.co/hacksider/deep-live-cam/blob/main/inswapper_128_fp16.onnx']) [
# Add a check or download for the FP32 model if you have a URL "https://huggingface.co/hacksider/deep-live-cam/blob/main/inswapper_128_fp16.onnx"
# conditional_download(download_directory_path, ['URL_TO_FP32_MODEL_HERE']) ],
)
return True return True
def pre_start() -> bool: def pre_start() -> bool:
# --- No changes needed in pre_start ---
if not modules.globals.map_faces and not is_image(modules.globals.source_path): if not modules.globals.map_faces and not is_image(modules.globals.source_path):
update_status('Select an image for source path.', NAME) update_status("Select an image for source path.", NAME)
return False return False
elif not modules.globals.map_faces and not get_one_face(cv2.imread(modules.globals.source_path)): elif not modules.globals.map_faces and not get_one_face(
update_status('No face in source path detected.', NAME) cv2.imread(modules.globals.source_path)
):
update_status("No face in source path detected.", NAME)
return False return False
if not is_image(modules.globals.target_path) and not is_video(modules.globals.target_path): if not is_image(modules.globals.target_path) and not is_video(
update_status('Select an image or video for target path.', NAME) modules.globals.target_path
):
update_status("Select an image or video for target path.", NAME)
return False return False
return True return True
@@ -48,57 +54,48 @@ def get_face_swapper() -> Any:
with THREAD_LOCK: with THREAD_LOCK:
if FACE_SWAPPER is None: if FACE_SWAPPER is None:
# --- MODIFICATION START --- model_path = resolve_relative_path("../models/inswapper_128_fp16.onnx")
# Define paths for both FP32 and FP16 models FACE_SWAPPER = insightface.model_zoo.get_model(
model_dir = resolve_relative_path('../models') model_path, providers=modules.globals.execution_providers
model_path_fp32 = os.path.join(model_dir, 'inswapper_128.onnx') )
model_path_fp16 = os.path.join(model_dir, 'inswapper_128_fp16.onnx')
chosen_model_path = None
# Prioritize FP32 model
if os.path.exists(model_path_fp32):
chosen_model_path = model_path_fp32
update_status(f"Loading FP32 model: {os.path.basename(chosen_model_path)}", NAME)
# Fallback to FP16 model
elif os.path.exists(model_path_fp16):
chosen_model_path = model_path_fp16
update_status(f"FP32 model not found. Loading FP16 model: {os.path.basename(chosen_model_path)}", NAME)
# Error if neither model is found
else:
error_message = f"Face Swapper model not found. Please ensure 'inswapper_128.onnx' (recommended) or 'inswapper_128_fp16.onnx' exists in the '{model_dir}' directory."
update_status(error_message, NAME)
raise FileNotFoundError(error_message)
# Load the chosen model
try:
FACE_SWAPPER = insightface.model_zoo.get_model(chosen_model_path, providers=modules.globals.execution_providers)
except Exception as e:
update_status(f"Error loading Face Swapper model {os.path.basename(chosen_model_path)}: {e}", NAME)
# Optionally, re-raise the exception or handle it more gracefully
raise e
# --- MODIFICATION END ---
return FACE_SWAPPER return FACE_SWAPPER
def swap_face(source_face: Face, target_face: Face, temp_frame: Frame) -> Frame: def swap_face(source_face: Face, target_face: Face, temp_frame: Frame) -> Frame:
# --- No changes needed in swap_face --- swapped_frame = get_face_swapper().get(
swapper = get_face_swapper() temp_frame, target_face, source_face, paste_back=True
if swapper is None: )
# Handle case where model failed to load
update_status("Face swapper model not loaded, skipping swap.", NAME) # Apply opacity if enabled
return temp_frame if modules.globals.opacity_switch:
return swapper.get(temp_frame, target_face, source_face, paste_back=True) opacity = modules.globals.face_opacity / 100
swapped_frame = cv2.addWeighted(
swapped_frame, opacity, temp_frame, 1 - opacity, 0
)
# Apply mouth mask if enabled
if modules.globals.mouth_mask:
face_mask = create_face_mask(target_face, temp_frame)
mouth_mask_data = create_lower_mouth_mask(target_face, temp_frame)
mouth_mask, mouth_cutout, mouth_box, lower_lip_polygon = mouth_mask_data
if mouth_box is not None:
swapped_frame = apply_mouth_area(
swapped_frame, mouth_cutout, mouth_box, face_mask, lower_lip_polygon
)
if modules.globals.show_mouth_mask_box:
swapped_frame = draw_mouth_mask_visualization(
swapped_frame, target_face, mouth_mask_data
)
return swapped_frame
def process_frame(source_face: Face, temp_frame: Frame) -> Frame: def process_frame(source_face: Face, temp_frame: Frame) -> Frame:
# --- No changes needed in process_frame ---
# Ensure the frame is in RGB format if color correction is enabled # Ensure the frame is in RGB format if color correction is enabled
# Note: InsightFace swapper often expects BGR by default. Double-check if color issues appear. if modules.globals.color_correction:
# If color correction is needed *before* swapping and insightface needs BGR: temp_frame = cv2.cvtColor(temp_frame, cv2.COLOR_BGR2RGB)
# original_was_bgr = True # Assume input is BGR
# if modules.globals.color_correction:
# temp_frame = cv2.cvtColor(temp_frame, cv2.COLOR_BGR2RGB)
# original_was_bgr = False # Now it's RGB
if modules.globals.many_faces: if modules.globals.many_faces:
many_faces = get_many_faces(temp_frame) many_faces = get_many_faces(temp_frame)
@@ -109,51 +106,52 @@ def process_frame(source_face: Face, temp_frame: Frame) -> Frame:
target_face = get_one_face(temp_frame) target_face = get_one_face(temp_frame)
if target_face: if target_face:
temp_frame = swap_face(source_face, target_face, temp_frame) temp_frame = swap_face(source_face, target_face, temp_frame)
# Convert back if necessary (example, might not be needed depending on workflow)
# if modules.globals.color_correction and not original_was_bgr:
# temp_frame = cv2.cvtColor(temp_frame, cv2.COLOR_RGB2BGR)
return temp_frame return temp_frame
def process_frame_v2(temp_frame: Frame, temp_frame_path: str = "") -> Frame: def process_frame_v2(temp_frame: Frame, temp_frame_path: str = "") -> Frame:
# --- No changes needed in process_frame_v2 ---
# (Assuming swap_face handles the potential None return from get_face_swapper)
if is_image(modules.globals.target_path): if is_image(modules.globals.target_path):
if modules.globals.many_faces: if modules.globals.many_faces:
source_face = default_source_face() source_face = default_source_face()
for map_entry in modules.globals.souce_target_map: # Renamed 'map' to 'map_entry' for map in modules.globals.souce_target_map:
target_face = map_entry['target']['face'] target_face = map["target"]["face"]
temp_frame = swap_face(source_face, target_face, temp_frame) temp_frame = swap_face(source_face, target_face, temp_frame)
elif not modules.globals.many_faces: elif not modules.globals.many_faces:
for map_entry in modules.globals.souce_target_map: # Renamed 'map' to 'map_entry' for map in modules.globals.souce_target_map:
if "source" in map_entry: if "source" in map:
source_face = map_entry['source']['face'] source_face = map["source"]["face"]
target_face = map_entry['target']['face'] target_face = map["target"]["face"]
temp_frame = swap_face(source_face, target_face, temp_frame) temp_frame = swap_face(source_face, target_face, temp_frame)
elif is_video(modules.globals.target_path): elif is_video(modules.globals.target_path):
if modules.globals.many_faces: if modules.globals.many_faces:
source_face = default_source_face() source_face = default_source_face()
for map_entry in modules.globals.souce_target_map: # Renamed 'map' to 'map_entry' for map in modules.globals.souce_target_map:
target_frame = [f for f in map_entry['target_faces_in_frame'] if f['location'] == temp_frame_path] target_frame = [
f
for f in map["target_faces_in_frame"]
if f["location"] == temp_frame_path
]
for frame in target_frame: for frame in target_frame:
for target_face in frame['faces']: for target_face in frame["faces"]:
temp_frame = swap_face(source_face, target_face, temp_frame) temp_frame = swap_face(source_face, target_face, temp_frame)
elif not modules.globals.many_faces: elif not modules.globals.many_faces:
for map_entry in modules.globals.souce_target_map: # Renamed 'map' to 'map_entry' for map in modules.globals.souce_target_map:
if "source" in map_entry: if "source" in map:
target_frame = [f for f in map_entry['target_faces_in_frame'] if f['location'] == temp_frame_path] target_frame = [
source_face = map_entry['source']['face'] f
for f in map["target_faces_in_frame"]
if f["location"] == temp_frame_path
]
source_face = map["source"]["face"]
for frame in target_frame: for frame in target_frame:
for target_face in frame['faces']: for target_face in frame["faces"]:
temp_frame = swap_face(source_face, target_face, temp_frame) temp_frame = swap_face(source_face, target_face, temp_frame)
else: # Fallback for neither image nor video (e.g., live feed?) else:
detected_faces = get_many_faces(temp_frame) detected_faces = get_many_faces(temp_frame)
if modules.globals.many_faces: if modules.globals.many_faces:
if detected_faces: if detected_faces:
@@ -162,97 +160,334 @@ def process_frame_v2(temp_frame: Frame, temp_frame_path: str = "") -> Frame:
temp_frame = swap_face(source_face, target_face, temp_frame) temp_frame = swap_face(source_face, target_face, temp_frame)
elif not modules.globals.many_faces: elif not modules.globals.many_faces:
if detected_faces and hasattr(modules.globals, 'simple_map') and modules.globals.simple_map: # Check simple_map exists if detected_faces:
if len(detected_faces) <= len(modules.globals.simple_map['target_embeddings']): if len(detected_faces) <= len(
modules.globals.simple_map["target_embeddings"]
):
for detected_face in detected_faces: for detected_face in detected_faces:
closest_centroid_index, _ = find_closest_centroid(modules.globals.simple_map['target_embeddings'], detected_face.normed_embedding) closest_centroid_index, _ = find_closest_centroid(
temp_frame = swap_face(modules.globals.simple_map['source_faces'][closest_centroid_index], detected_face, temp_frame) modules.globals.simple_map["target_embeddings"],
detected_face.normed_embedding,
)
temp_frame = swap_face(
modules.globals.simple_map["source_faces"][
closest_centroid_index
],
detected_face,
temp_frame,
)
else: else:
detected_faces_centroids = [face.normed_embedding for face in detected_faces] detected_faces_centroids = []
for face in detected_faces:
detected_faces_centroids.append(face.normed_embedding)
i = 0 i = 0
for target_embedding in modules.globals.simple_map['target_embeddings']: for target_embedding in modules.globals.simple_map[
closest_centroid_index, _ = find_closest_centroid(detected_faces_centroids, target_embedding) "target_embeddings"
# Ensure index is valid before accessing detected_faces ]:
if closest_centroid_index < len(detected_faces): closest_centroid_index, _ = find_closest_centroid(
temp_frame = swap_face(modules.globals.simple_map['source_faces'][i], detected_faces[closest_centroid_index], temp_frame) detected_faces_centroids, target_embedding
)
temp_frame = swap_face(
modules.globals.simple_map["source_faces"][i],
detected_faces[closest_centroid_index],
temp_frame,
)
i += 1 i += 1
return temp_frame return temp_frame
def process_frames(source_path: str, temp_frame_paths: List[str], progress: Any = None) -> None: def process_frames(
# --- No changes needed in process_frames --- source_path: str, temp_frame_paths: List[str], progress: Any = None
# Note: Ensure get_one_face is called only once if possible for efficiency if !map_faces ) -> None:
source_face = None
if not modules.globals.map_faces: if not modules.globals.map_faces:
source_img = cv2.imread(source_path) source_face = get_one_face(cv2.imread(source_path))
if source_img is not None: for temp_frame_path in temp_frame_paths:
source_face = get_one_face(source_img) temp_frame = cv2.imread(temp_frame_path)
if source_face is None: try:
update_status(f"Could not find face in source image: {source_path}, skipping swap.", NAME) result = process_frame(source_face, temp_frame)
# If no source face, maybe skip processing? Or handle differently. cv2.imwrite(temp_frame_path, result)
# For now, it will proceed but swap_face might fail later. except Exception as exception:
print(exception)
for temp_frame_path in temp_frame_paths: pass
temp_frame = cv2.imread(temp_frame_path) if progress:
if temp_frame is None: progress.update(1)
update_status(f"Warning: Could not read frame {temp_frame_path}", NAME) else:
if progress: progress.update(1) # Still update progress even if frame fails for temp_frame_path in temp_frame_paths:
continue # Skip to next frame temp_frame = cv2.imread(temp_frame_path)
try:
try: result = process_frame_v2(temp_frame, temp_frame_path)
if not modules.globals.map_faces: cv2.imwrite(temp_frame_path, result)
if source_face: # Only process if source face was found except Exception as exception:
result = process_frame(source_face, temp_frame) print(exception)
else: pass
result = temp_frame # No source face, return original frame
else:
result = process_frame_v2(temp_frame, temp_frame_path)
cv2.imwrite(temp_frame_path, result)
except Exception as exception:
update_status(f"Error processing frame {os.path.basename(temp_frame_path)}: {exception}", NAME)
# Decide whether to 'pass' (continue processing other frames) or raise
pass # Continue processing other frames
finally:
if progress: if progress:
progress.update(1) progress.update(1)
def process_image(source_path: str, target_path: str, output_path: str) -> None: def process_image(source_path: str, target_path: str, output_path: str) -> None:
# --- No changes needed in process_image ---
# Note: Added checks for successful image reads and face detection
target_frame = cv2.imread(target_path) # Read original target for processing
if target_frame is None:
update_status(f"Error: Could not read target image: {target_path}", NAME)
return
if not modules.globals.map_faces: if not modules.globals.map_faces:
source_img = cv2.imread(source_path) source_face = get_one_face(cv2.imread(source_path))
if source_img is None: target_frame = cv2.imread(target_path)
update_status(f"Error: Could not read source image: {source_path}", NAME)
return
source_face = get_one_face(source_img)
if source_face is None:
update_status(f"Error: No face found in source image: {source_path}", NAME)
return
result = process_frame(source_face, target_frame) result = process_frame(source_face, target_frame)
cv2.imwrite(output_path, result)
else: else:
if modules.globals.many_faces: if modules.globals.many_faces:
update_status('Many faces enabled. Using first source image (if applicable in v2). Processing...', NAME) update_status(
# For process_frame_v2 on single image, it reads the 'output_path' which should be a copy "Many faces enabled. Using first source image. Progressing...", NAME
# Let's process the 'target_frame' we read instead. )
result = process_frame_v2(target_frame) # Process the frame directly target_frame = cv2.imread(output_path)
result = process_frame_v2(target_frame)
# Write the final result to the output path cv2.imwrite(output_path, result)
success = cv2.imwrite(output_path, result)
if not success:
update_status(f"Error: Failed to write output image to: {output_path}", NAME)
def process_video(source_path: str, temp_frame_paths: List[str]) -> None: def process_video(source_path: str, temp_frame_paths: List[str]) -> None:
# --- No changes needed in process_video ---
if modules.globals.map_faces and modules.globals.many_faces: if modules.globals.map_faces and modules.globals.many_faces:
update_status('Many faces enabled. Using first source image (if applicable in v2). Processing...', NAME) update_status(
# The core processing logic is delegated, which is good. "Many faces enabled. Using first source image. Progressing...", NAME
modules.processors.frame.core.process_video(source_path, temp_frame_paths, process_frames) )
modules.processors.frame.core.process_video(
source_path, temp_frame_paths, process_frames
)
def create_face_mask(face: Face, frame: Frame) -> np.ndarray:
mask = np.zeros(frame.shape[:2], dtype=np.uint8)
landmarks = face.landmark_2d_106
if landmarks is not None:
landmarks = landmarks.astype(np.int32)
right_side_face = landmarks[0:16]
left_side_face = landmarks[17:32]
right_eye = landmarks[33:42]
right_eye_brow = landmarks[43:51]
left_eye = landmarks[87:96]
left_eye_brow = landmarks[97:105]
right_eyebrow_top = np.min(right_eye_brow[:, 1])
left_eyebrow_top = np.min(left_eye_brow[:, 1])
eyebrow_top = min(right_eyebrow_top, left_eyebrow_top)
face_top = np.min([right_side_face[0, 1], left_side_face[-1, 1]])
forehead_height = face_top - eyebrow_top
extended_forehead_height = int(forehead_height * 5.0)
forehead_left = right_side_face[0].copy()
forehead_right = left_side_face[-1].copy()
forehead_left[1] -= extended_forehead_height
forehead_right[1] -= extended_forehead_height
face_outline = np.vstack(
[
[forehead_left],
right_side_face,
left_side_face[::-1],
[forehead_right],
]
)
padding = int(np.linalg.norm(right_side_face[0] - left_side_face[-1]) * 0.05)
hull = cv2.convexHull(face_outline)
hull_padded = []
for point in hull:
x, y = point[0]
center = np.mean(face_outline, axis=0)
direction = np.array([x, y]) - center
direction = direction / np.linalg.norm(direction)
padded_point = np.array([x, y]) + direction * padding
hull_padded.append(padded_point)
hull_padded = np.array(hull_padded, dtype=np.int32)
cv2.fillConvexPoly(mask, hull_padded, 255)
mask = cv2.GaussianBlur(mask, (5, 5), 3)
return mask
def create_lower_mouth_mask(face: Face, frame: Frame) -> tuple:
mask = np.zeros(frame.shape[:2], dtype=np.uint8)
mouth_cutout = None
landmarks = face.landmark_2d_106
if landmarks is not None:
lower_lip_order = [
65,
66,
62,
70,
69,
18,
19,
20,
21,
22,
23,
24,
0,
8,
7,
6,
5,
4,
3,
2,
65,
]
lower_lip_landmarks = landmarks[lower_lip_order].astype(np.float32)
center = np.mean(lower_lip_landmarks, axis=0)
expansion_factor = 1 + modules.globals.mask_down_size
expanded_landmarks = (lower_lip_landmarks - center) * expansion_factor + center
toplip_indices = [20, 0, 1, 2, 3, 4, 5]
toplip_extension = modules.globals.mask_size * 0.5
for idx in toplip_indices:
direction = expanded_landmarks[idx] - center
direction = direction / np.linalg.norm(direction)
expanded_landmarks[idx] += direction * toplip_extension
chin_indices = [11, 12, 13, 14, 15, 16]
chin_extension = 2 * 0.2
for idx in chin_indices:
expanded_landmarks[idx][1] += (
expanded_landmarks[idx][1] - center[1]
) * chin_extension
expanded_landmarks = expanded_landmarks.astype(np.int32)
min_x, min_y = np.min(expanded_landmarks, axis=0)
max_x, max_y = np.max(expanded_landmarks, axis=0)
padding = int((max_x - min_x) * 0.1)
min_x = max(0, min_x - padding)
min_y = max(0, min_y - padding)
max_x = min(frame.shape[1], max_x + padding)
max_y = min(frame.shape[0], max_y + padding)
if max_x <= min_x or max_y <= min_y:
if (max_x - min_x) <= 1:
max_x = min_x + 1
if (max_y - min_y) <= 1:
max_y = min_y + 1
mask_roi = np.zeros((max_y - min_y, max_x - min_x), dtype=np.uint8)
cv2.fillPoly(mask_roi, [expanded_landmarks - [min_x, min_y]], 255)
mask_roi = cv2.GaussianBlur(mask_roi, (15, 15), 5)
mask[min_y:max_y, min_x:max_x] = mask_roi
mouth_cutout = frame[min_y:max_y, min_x:max_x].copy()
return mask, mouth_cutout, (min_x, min_y, max_x, max_y), expanded_landmarks
return mask, mouth_cutout, None, None
def apply_mouth_area(
frame: Frame,
mouth_cutout: np.ndarray,
mouth_box: tuple,
face_mask: np.ndarray,
mouth_polygon: np.ndarray,
) -> Frame:
min_x, min_y, max_x, max_y = mouth_box
box_width = max_x - min_x
box_height = max_y - min_y
if (
mouth_cutout is None
or box_width is None
or box_height is None
or face_mask is None
or mouth_polygon is None
):
return frame
try:
resized_mouth_cutout = cv2.resize(mouth_cutout, (box_width, box_height))
roi = frame[min_y:max_y, min_x:max_x]
if roi.shape != resized_mouth_cutout.shape:
resized_mouth_cutout = cv2.resize(
resized_mouth_cutout, (roi.shape[1], roi.shape[0])
)
color_corrected_mouth = apply_color_transfer(resized_mouth_cutout, roi)
polygon_mask = np.zeros(roi.shape[:2], dtype=np.uint8)
adjusted_polygon = mouth_polygon - [min_x, min_y]
cv2.fillPoly(polygon_mask, [adjusted_polygon], 255)
feather_amount = min(
30,
box_width // modules.globals.mask_feather_ratio,
box_height // modules.globals.mask_feather_ratio,
)
feathered_mask = cv2.GaussianBlur(
polygon_mask.astype(float), (0, 0), feather_amount
)
feathered_mask = feathered_mask / feathered_mask.max()
face_mask_roi = face_mask[min_y:max_y, min_x:max_x]
combined_mask = feathered_mask * (face_mask_roi / 255.0)
combined_mask = combined_mask[:, :, np.newaxis]
blended = (
color_corrected_mouth * combined_mask + roi * (1 - combined_mask)
).astype(np.uint8)
face_mask_3channel = (
np.repeat(face_mask_roi[:, :, np.newaxis], 3, axis=2) / 255.0
)
final_blend = blended * face_mask_3channel + roi * (1 - face_mask_3channel)
frame[min_y:max_y, min_x:max_x] = final_blend.astype(np.uint8)
except Exception as e:
pass
return frame
def apply_color_transfer(source: np.ndarray, target: np.ndarray) -> np.ndarray:
source = cv2.cvtColor(source, cv2.COLOR_BGR2LAB).astype("float32")
target = cv2.cvtColor(target, cv2.COLOR_BGR2LAB).astype("float32")
source_mean, source_std = cv2.meanStdDev(source)
target_mean, target_std = cv2.meanStdDev(target)
source_mean = source_mean.reshape(1, 1, 3)
source_std = source_std.reshape(1, 1, 3)
target_mean = target_mean.reshape(1, 1, 3)
target_std = target_std.reshape(1, 1, 3)
source = (source - source_mean) * (target_std / source_std) + target_mean
return cv2.cvtColor(np.clip(source, 0, 255).astype("uint8"), cv2.COLOR_LAB2BGR)
def draw_mouth_mask_visualization(
frame: Frame, face: Face, mouth_mask_data: tuple
) -> Frame:
landmarks = face.landmark_2d_106
if landmarks is not None and mouth_mask_data is not None:
mask, mouth_cutout, (min_x, min_y, max_x, max_y), lower_lip_polygon = (
mouth_mask_data
)
vis_frame = frame.copy()
# Draw the lower lip polygon
cv2.polylines(vis_frame, [lower_lip_polygon], True, (0, 255, 0), 2)
# Add labels
cv2.putText(
vis_frame,
"Mouth Mask",
(min_x, min_y - 10),
cv2.FONT_HERSHEY_SIMPLEX,
0.5,
(255, 255, 255),
1,
)
return vis_frame
return frame
+1023 -465
View File
File diff suppressed because it is too large Load Diff
+19 -87
View File
@@ -12,23 +12,16 @@ from tqdm import tqdm
import modules.globals import modules.globals
TEMP_FILE = "temp.mp4" TEMP_FILE = 'temp.mp4'
TEMP_DIRECTORY = "temp" TEMP_DIRECTORY = 'temp'
# monkey patch ssl for mac # monkey patch ssl for mac
if platform.system().lower() == "darwin": if platform.system().lower() == 'darwin':
ssl._create_default_https_context = ssl._create_unverified_context ssl._create_default_https_context = ssl._create_unverified_context
def run_ffmpeg(args: List[str]) -> bool: def run_ffmpeg(args: List[str]) -> bool:
commands = [ commands = ['ffmpeg', '-hide_banner', '-hwaccel', 'auto', '-loglevel', modules.globals.log_level]
"ffmpeg",
"-hide_banner",
"-hwaccel",
"auto",
"-loglevel",
modules.globals.log_level,
]
commands.extend(args) commands.extend(args)
try: try:
subprocess.check_output(commands, stderr=subprocess.STDOUT) subprocess.check_output(commands, stderr=subprocess.STDOUT)
@@ -39,19 +32,8 @@ def run_ffmpeg(args: List[str]) -> bool:
def detect_fps(target_path: str) -> float: def detect_fps(target_path: str) -> float:
command = [ command = ['ffprobe', '-v', 'error', '-select_streams', 'v:0', '-show_entries', 'stream=r_frame_rate', '-of', 'default=noprint_wrappers=1:nokey=1', target_path]
"ffprobe", output = subprocess.check_output(command).decode().strip().split('/')
"-v",
"error",
"-select_streams",
"v:0",
"-show_entries",
"stream=r_frame_rate",
"-of",
"default=noprint_wrappers=1:nokey=1",
target_path,
]
output = subprocess.check_output(command).decode().strip().split("/")
try: try:
numerator, denominator = map(int, output) numerator, denominator = map(int, output)
return numerator / denominator return numerator / denominator
@@ -62,65 +44,25 @@ def detect_fps(target_path: str) -> float:
def extract_frames(target_path: str) -> None: def extract_frames(target_path: str) -> None:
temp_directory_path = get_temp_directory_path(target_path) temp_directory_path = get_temp_directory_path(target_path)
run_ffmpeg( run_ffmpeg(['-i', target_path, '-pix_fmt', 'rgb24', os.path.join(temp_directory_path, '%04d.png')])
[
"-i",
target_path,
"-pix_fmt",
"rgb24",
os.path.join(temp_directory_path, "%04d.png"),
]
)
def create_video(target_path: str, fps: float = 30.0) -> None: def create_video(target_path: str, fps: float = 30.0) -> None:
temp_output_path = get_temp_output_path(target_path) temp_output_path = get_temp_output_path(target_path)
temp_directory_path = get_temp_directory_path(target_path) temp_directory_path = get_temp_directory_path(target_path)
run_ffmpeg( run_ffmpeg(['-r', str(fps), '-i', os.path.join(temp_directory_path, '%04d.png'), '-c:v', modules.globals.video_encoder, '-crf', str(modules.globals.video_quality), '-pix_fmt', 'yuv420p', '-vf', 'colorspace=bt709:iall=bt601-6-625:fast=1', '-y', temp_output_path])
[
"-r",
str(fps),
"-i",
os.path.join(temp_directory_path, "%04d.png"),
"-c:v",
modules.globals.video_encoder,
"-crf",
str(modules.globals.video_quality),
"-pix_fmt",
"yuv420p",
"-vf",
"colorspace=bt709:iall=bt601-6-625:fast=1",
"-y",
temp_output_path,
]
)
def restore_audio(target_path: str, output_path: str) -> None: def restore_audio(target_path: str, output_path: str) -> None:
temp_output_path = get_temp_output_path(target_path) temp_output_path = get_temp_output_path(target_path)
done = run_ffmpeg( done = run_ffmpeg(['-i', temp_output_path, '-i', target_path, '-c:v', 'copy', '-map', '0:v:0', '-map', '1:a:0', '-y', output_path])
[
"-i",
temp_output_path,
"-i",
target_path,
"-c:v",
"copy",
"-map",
"0:v:0",
"-map",
"1:a:0",
"-y",
output_path,
]
)
if not done: if not done:
move_temp(target_path, output_path) move_temp(target_path, output_path)
def get_temp_frame_paths(target_path: str) -> List[str]: def get_temp_frame_paths(target_path: str) -> List[str]:
temp_directory_path = get_temp_directory_path(target_path) temp_directory_path = get_temp_directory_path(target_path)
return glob.glob((os.path.join(glob.escape(temp_directory_path), "*.png"))) return glob.glob((os.path.join(glob.escape(temp_directory_path), '*.png')))
def get_temp_directory_path(target_path: str) -> str: def get_temp_directory_path(target_path: str) -> str:
@@ -139,9 +81,7 @@ def normalize_output_path(source_path: str, target_path: str, output_path: str)
source_name, _ = os.path.splitext(os.path.basename(source_path)) source_name, _ = os.path.splitext(os.path.basename(source_path))
target_name, target_extension = os.path.splitext(os.path.basename(target_path)) target_name, target_extension = os.path.splitext(os.path.basename(target_path))
if os.path.isdir(output_path): if os.path.isdir(output_path):
return os.path.join( return os.path.join(output_path, source_name + '-' + target_name + target_extension)
output_path, source_name + "-" + target_name + target_extension
)
return output_path return output_path
@@ -168,20 +108,20 @@ def clean_temp(target_path: str) -> None:
def has_image_extension(image_path: str) -> bool: def has_image_extension(image_path: str) -> bool:
return image_path.lower().endswith(("png", "jpg", "jpeg")) return image_path.lower().endswith(('png', 'jpg', 'jpeg'))
def is_image(image_path: str) -> bool: def is_image(image_path: str) -> bool:
if image_path and os.path.isfile(image_path): if image_path and os.path.isfile(image_path):
mimetype, _ = mimetypes.guess_type(image_path) mimetype, _ = mimetypes.guess_type(image_path)
return bool(mimetype and mimetype.startswith("image/")) return bool(mimetype and mimetype.startswith('image/'))
return False return False
def is_video(video_path: str) -> bool: def is_video(video_path: str) -> bool:
if video_path and os.path.isfile(video_path): if video_path and os.path.isfile(video_path):
mimetype, _ = mimetypes.guess_type(video_path) mimetype, _ = mimetypes.guess_type(video_path)
return bool(mimetype and mimetype.startswith("video/")) return bool(mimetype and mimetype.startswith('video/'))
return False return False
@@ -189,20 +129,12 @@ def conditional_download(download_directory_path: str, urls: List[str]) -> None:
if not os.path.exists(download_directory_path): if not os.path.exists(download_directory_path):
os.makedirs(download_directory_path) os.makedirs(download_directory_path)
for url in urls: for url in urls:
download_file_path = os.path.join( download_file_path = os.path.join(download_directory_path, os.path.basename(url))
download_directory_path, os.path.basename(url)
)
if not os.path.exists(download_file_path): if not os.path.exists(download_file_path):
request = urllib.request.urlopen(url) # type: ignore[attr-defined] request = urllib.request.urlopen(url) # type: ignore[attr-defined]
total = int(request.headers.get("Content-Length", 0)) total = int(request.headers.get('Content-Length', 0))
with tqdm( with tqdm(total=total, desc='Downloading', unit='B', unit_scale=True, unit_divisor=1024) as progress:
total=total, urllib.request.urlretrieve(url, download_file_path, reporthook=lambda count, block_size, total_size: progress.update(block_size)) # type: ignore[attr-defined]
desc="Downloading",
unit="B",
unit_scale=True,
unit_divisor=1024,
) as progress:
urllib.request.urlretrieve(url, download_file_path, reporthook=lambda count, block_size, total_size: progress.update(block_size)) # type: ignore[attr-defined]
def resolve_relative_path(path: str) -> str: def resolve_relative_path(path: str) -> str:
-94
View File
@@ -1,94 +0,0 @@
import cv2
import numpy as np
from typing import Optional, Tuple, Callable
import platform
import threading
# Only import Windows-specific library if on Windows
if platform.system() == "Windows":
from pygrabber.dshow_graph import FilterGraph
class VideoCapturer:
def __init__(self, device_index: int):
self.device_index = device_index
self.frame_callback = None
self._current_frame = None
self._frame_ready = threading.Event()
self.is_running = False
self.cap = None
# Initialize Windows-specific components if on Windows
if platform.system() == "Windows":
self.graph = FilterGraph()
# Verify device exists
devices = self.graph.get_input_devices()
if self.device_index >= len(devices):
raise ValueError(
f"Invalid device index {device_index}. Available devices: {len(devices)}"
)
def start(self, width: int = 960, height: int = 540, fps: int = 60) -> bool:
"""Initialize and start video capture"""
try:
if platform.system() == "Windows":
# Windows-specific capture methods
capture_methods = [
(self.device_index, cv2.CAP_DSHOW), # Try DirectShow first
(self.device_index, cv2.CAP_ANY), # Then try default backend
(-1, cv2.CAP_ANY), # Try -1 as fallback
(0, cv2.CAP_ANY), # Finally try 0 without specific backend
]
for dev_id, backend in capture_methods:
try:
self.cap = cv2.VideoCapture(dev_id, backend)
if self.cap.isOpened():
break
self.cap.release()
except Exception:
continue
else:
# Unix-like systems (Linux/Mac) capture method
self.cap = cv2.VideoCapture(self.device_index)
if not self.cap or not self.cap.isOpened():
raise RuntimeError("Failed to open camera")
# Configure format
self.cap.set(cv2.CAP_PROP_FRAME_WIDTH, width)
self.cap.set(cv2.CAP_PROP_FRAME_HEIGHT, height)
self.cap.set(cv2.CAP_PROP_FPS, fps)
self.is_running = True
return True
except Exception as e:
print(f"Failed to start capture: {str(e)}")
if self.cap:
self.cap.release()
return False
def read(self) -> Tuple[bool, Optional[np.ndarray]]:
"""Read a frame from the camera"""
if not self.is_running or self.cap is None:
return False, None
ret, frame = self.cap.read()
if ret:
self._current_frame = frame
if self.frame_callback:
self.frame_callback(frame)
return True, frame
return False, None
def release(self) -> None:
"""Stop capture and release resources"""
if self.is_running and self.cap is not None:
self.cap.release()
self.is_running = False
self.cap = None
def set_frame_callback(self, callback: Callable[[np.ndarray], None]) -> None:
"""Set callback for frame processing"""
self.frame_callback = callback
Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 31 KiB

+13 -13
View File
@@ -1,23 +1,23 @@
--extra-index-url https://download.pytorch.org/whl/cu118
numpy>=1.23.5,<2 numpy>=1.23.5,<2
typing-extensions>=4.8.0 opencv-python==4.8.1.78
opencv-python==4.11.0.86 cv2_enumerate_cameras==1.1.15
onnx==1.17.0 onnx==1.16.0
cv2_enumerate_cameras==1.1.18.3
insightface==0.7.3 insightface==0.7.3
psutil==5.9.8 psutil==5.9.8
tk==0.1.0 tk==0.1.0
customtkinter==5.2.2 pillow==9.5.0
pillow==11.1.0 torch==2.0.1+cu118; sys_platform != 'darwin'
torch; sys_platform != 'darwin' --index-url https://download.pytorch.org/whl/cu126 torch==2.0.1; sys_platform == 'darwin'
torch; sys_platform == 'darwin' --index-url https://download.pytorch.org/whl/cu126 torchvision==0.15.2+cu118; sys_platform != 'darwin'
torchvision; sys_platform != 'darwin' --index-url https://download.pytorch.org/whl/cu126 torchvision==0.15.2; sys_platform == 'darwin'
torchvision; sys_platform == 'darwin' --index-url https://download.pytorch.org/whl/cu126
onnxruntime-silicon==1.16.3; sys_platform == 'darwin' and platform_machine == 'arm64' onnxruntime-silicon==1.16.3; sys_platform == 'darwin' and platform_machine == 'arm64'
onnxruntime-gpu==1.21; sys_platform != 'darwin' onnxruntime-gpu==1.16.3; sys_platform != 'darwin'
tensorflow; sys_platform != 'darwin' tensorflow==2.12.1; sys_platform != 'darwin'
opennsfw2==0.10.2 opennsfw2==0.10.2
protobuf==4.23.2 protobuf==4.23.2
tqdm==4.66.4 tqdm==4.66.4
gfpgan==1.3.8 gfpgan==1.3.8
tkinterdnd2==0.4.2 tkinterdnd2==0.4.2
pygrabber==0.2 customtkinter==5.2.2
+1 -1
View File
@@ -1 +1 @@
python run.py --execution-provider cuda python run.py --execution-provider cuda --execution-threads 60 --max-memory 60
-1
View File
@@ -1 +0,0 @@
python run.py --execution-provider dml
+1
View File
@@ -0,0 +1 @@
python run.py --execution-provider dml
+13
View File
@@ -0,0 +1,13 @@
@echo off
:: Installing Microsoft Visual C++ Runtime - all versions 1.0.1 if it's not already installed
choco install vcredist-all
:: Installing CUDA if it's not already installed
choco install cuda
:: Inatalling ffmpeg if it's not already installed
choco install ffmpeg
:: Installing Python if it's not already installed
choco install python -y
:: Assuming successful installation, we ensure pip is upgraded
python -m ensurepip --upgrade
:: Use pip to install the packages listed in 'requirements.txt'
pip install -r requirements.txt
+122
View File
@@ -0,0 +1,122 @@
@echo off
setlocal EnableDelayedExpansion
:: 1. Setup your platform
echo Setting up your platform...
:: Python
where python >nul 2>&1
if %ERRORLEVEL% neq 0 (
echo Python is not installed. Please install Python 3.10 or later.
pause
exit /b
)
:: Pip
where pip >nul 2>&1
if %ERRORLEVEL% neq 0 (
echo Pip is not installed. Please install Pip.
pause
exit /b
)
:: Git
where git >nul 2>&1
if %ERRORLEVEL% neq 0 (
echo Git is not installed. Installing Git...
winget install --id Git.Git -e --source winget
)
:: FFMPEG
where ffmpeg >nul 2>&1
if %ERRORLEVEL% neq 0 (
echo FFMPEG is not installed. Installing FFMPEG...
winget install --id Gyan.FFmpeg -e --source winget
)
:: Visual Studio 2022 Runtimes
echo Installing Visual Studio 2022 Runtimes...
winget install --id Microsoft.VC++2015-2022Redist-x64 -e --source winget
:: 2. Clone Repository
if exist Deep-Live-Cam (
echo Deep-Live-Cam directory already exists.
set /p overwrite="Do you want to overwrite? (Y/N): "
if /i "%overwrite%"=="Y" (
rmdir /s /q Deep-Live-Cam
git clone https://github.com/hacksider/Deep-Live-Cam.git
) else (
echo Skipping clone, using existing directory.
)
) else (
git clone https://github.com/hacksider/Deep-Live-Cam.git
)
cd Deep-Live-Cam
:: 3. Download Models
echo Downloading models...
mkdir models
curl -L -o models/GFPGANv1.4.pth https://path.to.model/GFPGANv1.4.pth
curl -L -o models/inswapper_128_fp16.onnx https://path.to.model/inswapper_128_fp16.onnx
:: 4. Install dependencies
echo Creating a virtual environment...
python -m venv venv
call venv\Scripts\activate
echo Installing required Python packages...
pip install --upgrade pip
pip install -r requirements.txt
echo Setup complete. You can now run the application.
:: GPU Acceleration Options
echo.
echo Choose the GPU Acceleration Option if applicable:
echo 1. CUDA (Nvidia)
echo 2. CoreML (Apple Silicon)
echo 3. CoreML (Apple Legacy)
echo 4. DirectML (Windows)
echo 5. OpenVINO (Intel)
echo 6. None
set /p choice="Enter your choice (1-6): "
if "%choice%"=="1" (
echo Installing CUDA dependencies...
pip uninstall -y onnxruntime onnxruntime-gpu
pip install onnxruntime-gpu==1.16.3
set exec_provider="cuda"
) else if "%choice%"=="2" (
echo Installing CoreML (Apple Silicon) dependencies...
pip uninstall -y onnxruntime onnxruntime-silicon
pip install onnxruntime-silicon==1.13.1
set exec_provider="coreml"
) else if "%choice%"=="3" (
echo Installing CoreML (Apple Legacy) dependencies...
pip uninstall -y onnxruntime onnxruntime-coreml
pip install onnxruntime-coreml==1.13.1
set exec_provider="coreml"
) else if "%choice%"=="4" (
echo Installing DirectML dependencies...
pip uninstall -y onnxruntime onnxruntime-directml
pip install onnxruntime-directml==1.15.1
set exec_provider="directml"
) else if "%choice%"=="5" (
echo Installing OpenVINO dependencies...
pip uninstall -y onnxruntime onnxruntime-openvino
pip install onnxruntime-openvino==1.15.0
set exec_provider="openvino"
) else (
echo Skipping GPU acceleration setup.
)
:: Run the application
if defined exec_provider (
echo Running the application with %exec_provider% execution provider...
python run.py --execution-provider %exec_provider%
) else (
echo Running the application...
python run.py
)
pause