82 Commits

Author SHA1 Message Date
Kenneth Estanislao
1d41a20abf Update requirements.txt 2025-04-08 23:36:10 +08:00
Kenneth Estanislao
df940ccc3d Update .gitignore 2025-04-08 23:32:41 +08:00
Kenneth Estanislao
834f39ec0c Update requirements.txt 2025-03-28 17:02:31 +08:00
Kenneth Estanislao
56cddde87c Update core.py
Allows CUDA 12 to be used on this version
2025-03-27 02:56:08 +08:00
Kenneth Estanislao
0dbed2883a Update core.py
refactoring the code to make it easier to understand and is more optimized
2025-03-27 02:00:43 +08:00
Kenneth Estanislao
66248a37b4 Merge pull request #990 from wpoPR/pr/improve-macos-installation-instructions
improve macOS Apple Silicon installation instructions
2025-03-24 18:26:28 +08:00
KRSHH
aa9b7ed3b6 Add Tips and Tricks to README 2025-03-22 19:59:40 +05:30
Wesley Oliveira
51a4246050 adding uninstalling conflict python versions
follow sourcery-ai and add a note about uninstalling conflicting Python versions if users encounter issues.
2025-03-21 12:37:21 -03:00
Wesley Oliveira
3f1c072fac improve macOS Apple Silicon installation instructions
Followed the `README` but ran into some errors running it locally. Made a few tweaks and got it working on my M3 PRO. Found this PR (Failing to run on Apple Silicon Mac M3) and thought improving the instructions might help others. Hope this helps!

great tool guys, thx a lot
2025-03-20 16:47:01 -03:00
KRSHH
f91f9203e7 Remove Mac Edition Temporarily 2025-03-19 03:00:32 +05:30
Kenneth Estanislao
80477676b4 Merge pull request #980 from aaddyy227/main
Fix face swapping crash due to None face embeddings
2025-03-16 00:03:39 +08:00
Adrian Zimbran
c728994e6b fixed import and log message 2025-03-10 23:41:28 +02:00
Adrian Zimbran
65da3be2a4 Fix face swapping crash due to None face embeddings
- Add explicit checks for face detection results (source and target faces).
- Handle cases when face embeddings are not available, preventing AttributeError.
- Provide meaningful log messages for easier debugging in future scenarios.
2025-03-10 23:31:56 +02:00
Kenneth Estanislao
390b88216b Update README.md 2025-02-14 17:33:33 +08:00
Kenneth Estanislao
dabaa64695 Merge pull request #932 from harmeetsingh-work/patch-1
Update requirements.txt
2025-02-12 15:21:27 +08:00
Harmeet Singh
1fad1cd43a Update requirements.txt
Made changes for apple silicon. 

Or getting
ERROR: Could not find a version that satisfies the requirement torch==2.5.1+cu118 (from versions: 1.11.0, 1.12.0, 1.12.1, 1.13.0, 1.13.1, 2.0.0, 2.0.1, 2.1.0, 2.1.1, 2.1.2, 2.2.0, 2.2.1, 2.2.2, 2.3.0, 2.3.1, 2.4.0, 2.4.1, 2.5.0, 2.5.1, 2.6.0)
ERROR: No matching distribution found for torch==2.5.1+cu118
2025-02-11 18:44:23 +05:30
Kenneth Estanislao
2f67e2f159 Update requirements.txt 2025-02-09 14:17:49 +08:00
Kenneth Estanislao
a3af249ea6 Update requirements.txt 2025-02-07 19:31:02 +08:00
Kenneth Estanislao
5bc3ada632 Update requirements.txt 2025-02-06 15:37:55 +08:00
KRSHH
650e89eb21 Reduced File Size 2025-02-06 10:40:32 +05:30
Kenneth Estanislao
4d2aea37b7 Update requirements.txt 2025-02-06 00:43:20 +08:00
Kenneth Estanislao
28c4b34db1 Merge pull request #911 from nimishgautam/main
Fix cv2 size errors on first run in ui.py
2025-02-05 12:51:39 +08:00
Kenneth Estanislao
49e8f78513 Merge pull request #913 from soulee-dev/main
fix: typo souce_target_map → source_target_map
2025-02-05 12:18:48 +08:00
Kenneth Estanislao
d753f5d4b0 Merge pull request #917 from carpusherw/patch-1
Fix requirements.txt
2025-02-05 12:17:42 +08:00
KRSHH
4fb69476d8 Change img dimensions 2025-02-05 12:16:08 +08:00
carpusherw
f3adfd194d Fix requirements.txt 2025-02-05 12:16:08 +08:00
Kenneth Estanislao
e5f04cf917 Revert "Update requirements.txt"
This reverts commit d45dedc9a6.
2025-02-05 12:08:19 +08:00
Kenneth Estanislao
67394a3157 Revert "Update requirements.txt"
This reverts commit f82cebf86e.
2025-02-05 12:08:10 +08:00
carpusherw
186d155e1b Fix requirements.txt 2025-02-05 09:17:11 +08:00
KRSHH
87081e78d0 Fixed typo 2025-02-04 21:20:54 +05:30
KRSHH
f79373d4db Updated Features Section 2025-02-04 21:08:36 +05:30
Soul Lee
513e413956 fix: typo souce_target_map → source_target_map 2025-02-03 20:33:44 +09:00
Kenneth Estanislao
f82cebf86e Update requirements.txt 2025-02-03 18:03:27 +08:00
Kenneth Estanislao
d45dedc9a6 Update requirements.txt 2025-02-03 16:38:18 +08:00
Kenneth Estanislao
2d489b57ec Update README.md 2025-02-03 13:13:56 +08:00
Nimish Gåtam
ccc04983cf Update ui.py
removed unnecessary code as per AI code review (which is a thing now because of course it is)
2025-02-01 12:38:37 +01:00
Nimish Gåtam
2506c5a261 Update ui.py
Some checks for first run when models are missing, so it doesn't error out with inv_scale_x > 0 in cv2
2025-02-01 11:52:49 +01:00
Kenneth Estanislao
e862ff1456 Update requirements.txt
updated from CUDA 11.8 to CUDA 12.1
2025-02-01 12:21:55 +08:00
Kenneth Estanislao
db594c0e7c Update README.md 2025-01-29 14:02:07 +08:00
Kenneth Estanislao
6a5b75ec45 Update README.md 2025-01-29 14:00:41 +08:00
Kenneth Estanislao
79e1ce5093 Update requirements.txt
update pillow

In _imagingcms.c in Pillow before 10.3.0, a buffer overflow exists because strcpy is used instead of strncpy.
2025-01-28 14:22:05 +08:00
Kenneth Estanislao
fda4878bfd Update README.md 2025-01-20 04:38:49 +08:00
Kenneth Estanislao
5ff922e2a4 Update README.md 2025-01-18 22:50:07 +08:00
Kenneth Estanislao
9ed5a72289 Update README.md 2025-01-18 22:33:30 +08:00
KRSHH
0c8e2d5794 Changes to TLDR 2025-01-18 19:59:02 +05:30
KRSHH
a0aafbc97c Disclaimer TLDR 2025-01-18 19:57:46 +05:30
KRSHH
f95b07423b Moved Disclaimer to top 2025-01-18 19:53:08 +05:30
KRSHH
3947053c89 Change img dimensions 2025-01-15 22:48:21 +05:30
KRSHH
0e6a6f84f5 Updated Features Section 2025-01-15 22:45:23 +05:30
KRSHH
bb331a6db0 Add files via upload 2025-01-15 22:24:47 +05:30
KRSHH
ec48b0048f Added Contacts 2025-01-15 01:07:16 +05:30
KRSHH
acc4812551 Added Live Show Use Case 2025-01-15 00:33:02 +05:30
KRSHH
87ee05d7b3 Uploaded Live Show GIF 2025-01-15 00:29:22 +05:30
Kenneth Estanislao
ce03dbf200 Update README.md 2025-01-14 03:32:43 +08:00
KRSHH
704aeb73b1 Added Command to install FFMPEG directly 2025-01-14 00:30:07 +05:30
KRSHH
f5c8290e1c Update model URL 2025-01-14 00:26:03 +05:30
KRSHH
f164d9234b Shifted Disclaimer to Bottom
Its pretty much standard in any repo
2025-01-12 16:35:11 +05:30
KRSHH
74009c1d5d Shift TL;DR under Packages 2025-01-11 21:03:26 +05:30
Kenneth Estanislao
e6a1c8dd95 Update README.md 2025-01-07 19:03:21 +08:00
Kenneth Estanislao
0e3f2c8dc0 Update README.md 2025-01-07 19:02:46 +08:00
Kenneth Estanislao
464dc2a0aa Update README.md 2025-01-07 18:56:54 +08:00
Kenneth Estanislao
a05754fb28 Update README.md 2025-01-07 18:55:21 +08:00
Kenneth Estanislao
9727f34923 Update README.md 2025-01-07 18:52:24 +08:00
Kenneth Estanislao
a86544a4b4 Update README.md 2025-01-07 18:48:03 +08:00
Kenneth Estanislao
979da7aa1d Update README.md 2025-01-07 18:33:22 +08:00
Kenneth Estanislao
4a37bb2a97 Update README.md 2025-01-07 18:32:52 +08:00
Kenneth Estanislao
21d3c8766a Merge pull request #879 from hacksider/premain
Premain
2025-01-07 18:12:47 +08:00
Kenneth Estanislao
ee19c5158a Merge pull request #877 from qitianai/add-lang
Add multi language UI
2025-01-07 17:57:10 +08:00
qitianai
693c9bb268 Merge pull request #1 from hacksider/main
merge from source main branch
2025-01-07 15:01:00 +08:00
qitian
5132f86cdc add mutil language 2025-01-07 14:04:18 +08:00
Kenneth Estanislao
cab2efa200 Update README.md
added qitianai on the credits
2025-01-07 13:48:42 +08:00
qitian
6e29e4061b merge from the source and little change 2025-01-07 13:46:17 +08:00
KRSHH
2a7ae010a8 Raised img Res 2025-01-06 23:53:18 +05:30
KRSHH
a834811974 Add URL to buttons
Forgot to add before (regarded)
2025-01-06 23:23:19 +05:30
KRSHH
d2aaf46e69 Change buttons 2025-01-06 23:13:57 +05:30
Makaru
d07d4a6a26 Update ui.py
I pushed it to premain
2025-01-07 01:15:05 +08:00
KRSHH
09f0343639 Shifted features section under Quick start 2025-01-06 18:16:44 +05:30
KRSHH
75913c513e Decreased Disclaimer's Font Size 2025-01-06 18:02:51 +05:30
KRSHH
7f38539508 Fix Grammar in README 2025-01-06 17:51:00 +05:30
Kenneth Estanislao
b38831dfdf Revert "Merge pull request #868 from kier007/main"
This reverts commit c03f697729, reversing
changes made to d8a5cdbc19.
2025-01-06 14:14:21 +08:00
Kenneth Estanislao
b518f4337d Revert "Merge pull request #869 from kier007/patch-1"
This reverts commit b38ef62447, reversing
changes made to c03f697729.
2025-01-06 14:14:04 +08:00
KRSHH
aed933c1db Update branches
Update Branches
2024-12-29 21:44:57 +05:30
12 changed files with 1488 additions and 487 deletions

1
.gitignore vendored
View File

@@ -25,3 +25,4 @@ models/DMDNet.pth
faceswap/
.vscode/
switch_states.json
venv.rar

283
README.md
View File

@@ -9,37 +9,97 @@
</p>
<p align="center">
<img src="media/demo.gif" alt="Demo GIF">
<img src="media/avgpcperformancedemo.gif" alt="Performance Demo GIF">
<img src="media/demo.gif" alt="Demo GIF" width="800">
</p>
## Disclaimer
## Disclaimer
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.
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.
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.
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.
- 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.
## Quick Start - Download Prebuilt
<div style="margin: 28px 0;">
<div style="margin-bottom: 20px;">
<a href="https://hacksider.gumroad.com/l/vccdmm" target="_blank">
<img src="https://github.com/user-attachments/assets/c702bb7d-d9c0-466a-9ad2-02849294e540" alt="Download Button 1" style="width: 280px; display: block;">
</a>
</div>
<div>
<a href="https://krshh.gumroad.com/l/Deep-Live-Cam-Mac" target="_blank">
<img src="https://github.com/user-attachments/assets/9a302750-2d54-457d-bdc8-6ed7c6af0e1a" alt="Download Button 2" style="width: 280px; display: block;">
</a>
</div>
</div>
## Quick Start - Pre-built (Windows / Nvidia)
<a href="https://hacksider.gumroad.com/l/vccdmm"> <img src="https://github.com/user-attachments/assets/7d993b32-e3e8-4cd3-bbfb-a549152ebdd5" width="285" height="77" />
##### This is the fastest build you can get if you have a discrete NVIDIA GPU.
###### These Pre-builts are perfect for non-technical users or those who don't have time to, or can't manually install all the requirements. Just a heads-up: this is an open-source project, so you can also install it manually.
## TLDR; Live Deepfake in just 3 Clicks
![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
### Mouth Mask
**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)
**Please be aware that the installation needs technical skills and is not for beginners, consider downloading the prebuilt.**
**Please be aware that the installation requires technical skills and is not for beginners. Consider downloading the prebuilt version.**
<details>
<summary>Click to see the process</summary>
@@ -48,24 +108,25 @@ Users are expected to use this software responsibly and legally. If using a real
This is more likely to work on your computer but will be slower as it utilizes the CPU.
**1. Setup Your Platform**
**1. Set up Your Platform**
- Python (3.10 recommended)
- pip
- git
- [ffmpeg](https://www.youtube.com/watch?v=OlNWCpFdVMA)
- [Visual Studio 2022 Runtimes (Windows)](https://visualstudio.microsoft.com/visual-cpp-build-tools/)
- Python (3.10 recommended)
- pip
- git
- [ffmpeg](https://www.youtube.com/watch?v=OlNWCpFdVMA) - ```iex (irm ffmpeg.tc.ht)```
- [Visual Studio 2022 Runtimes (Windows)](https://visualstudio.microsoft.com/visual-cpp-build-tools/)
**2. Clone Repository**
**2. Clone the Repository**
```bash
https://github.com/hacksider/Deep-Live-Cam.git
git clone https://github.com/hacksider/Deep-Live-Cam.git
cd Deep-Live-Cam
```
**3. Download Models**
**3. Download the Models**
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.onnx) (Note: Use this [replacement version](https://github.com/facefusion/facefusion-assets/releases/download/models/inswapper_128.onnx) if you encounter issues)
2. [inswapper\_128\_fp16.onnx](https://huggingface.co/hacksider/deep-live-cam/resolve/main/inswapper_128_fp16.onnx)
Place these files in the "**models**" folder.
@@ -73,54 +134,112 @@ 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 macOS:** Install or upgrade the `python-tk` package:
**For macOS:**
Apple Silicon (M1/M2/M3) requires specific setup:
```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
# 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).
### GPU Acceleration
### GPU Acceleration
**CUDA Execution Provider (Nvidia)**
1. Install [CUDA Toolkit 11.8](https://developer.nvidia.com/cuda-11-8-0-download-archive) or [CUDA Toolkit 12.1.1](https://developer.nvidia.com/cuda-12-1-1-download-archive)
1. Install [CUDA Toolkit 11.8.0](https://developer.nvidia.com/cuda-11-8-0-download-archive)
2. Install dependencies:
```bash
pip uninstall onnxruntime onnxruntime-gpu
pip install onnxruntime-gpu==1.16.3
```
3. Usage:
```bash
python run.py --execution-provider cuda
```
**CoreML Execution Provider (Apple Silicon)**
1. Install dependencies:
Apple Silicon (M1/M2/M3) specific installation:
1. Make sure you've completed the macOS setup above using Python 3.10.
2. Install dependencies:
```bash
pip uninstall onnxruntime onnxruntime-silicon
pip install onnxruntime-silicon==1.13.1
```
2. Usage:
3. Usage (important: specify Python 3.10):
```bash
python run.py --execution-provider coreml
python3.10 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)**
1. Install dependencies:
```bash
pip uninstall onnxruntime onnxruntime-coreml
pip install onnxruntime-coreml==1.13.1
```
2. Usage:
```bash
python run.py --execution-provider coreml
```
@@ -128,11 +247,14 @@ python run.py --execution-provider coreml
**DirectML Execution Provider (Windows)**
1. Install dependencies:
```bash
pip uninstall onnxruntime onnxruntime-directml
pip install onnxruntime-directml==1.15.1
```
2. Usage:
```bash
python run.py --execution-provider directml
```
@@ -140,62 +262,49 @@ python run.py --execution-provider directml
**OpenVINO™ Execution Provider (Intel)**
1. Install dependencies:
```bash
pip uninstall onnxruntime onnxruntime-openvino
pip install onnxruntime-openvino==1.15.0
```
2. Usage:
```bash
python run.py --execution-provider openvino
```
</details>
## Usage
**1. Image/Video Mode**
- Execute `python run.py`.
- Choose a source face image and a target image/video.
- Click "Start".
- The output will be saved in a directory named after the target video.
- Execute `python run.py`.
- Choose a source face image and a target image/video.
- Click "Start".
- The output will be saved in a directory named after the target video.
**2. Webcam Mode**
- Execute `python run.py`.
- Select a source face image.
- Click "Live".
- Wait for the preview to appear (10-30 seconds).
- Use a screen capture tool like OBS to stream.
- To change the face, select a new source image.
- Execute `python run.py`.
- Select a source face image.
- Click "Live".
- Wait for the preview to appear (10-30 seconds).
- Use a screen capture tool like OBS to stream.
- To change the face, select a new source image.
## Features - Everything is realtime
## Tips and Tricks
### Mouth Mask
Check out these helpful guides to get the most out of Deep-Live-Cam:
**Retain your original mouth using Mouth Mask**
- [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
- [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
![resizable-gif](media/ludwig.gif)
### Face Mapping
**Use different faces on multiple subjects**
![face_mapping_source](media/streamers.gif)
### Your Movie, Your Face
**Watch movies with any face in realtime**
![movie](media/movie.gif)
## Benchmarks
**Nearly 0% detection!**
![bench](media/deepwarebench.gif)
Visit our [official blog](https://deeplivecam.net/index.php/blog/tips-and-tricks) for more tips and tutorials.
## Command Line Arguments (Unmaintained)
@@ -212,7 +321,6 @@ options:
--many-faces process every face
--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-quality [0-51] adjust output video quality
--live-mirror the live camera display as you see it in the front-facing camera frame
@@ -225,9 +333,9 @@ options:
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 ready to improve, that's why we didn't cherrypick anything.**
**We are always open to criticism and are ready to improve, that's why we didn't cherry-pick anything.**
- [*"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
@@ -241,26 +349,27 @@ Looking for a CLI mode? Using the -s/--source argument will make the run program
- [*"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
- [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).
- [havok2-htwo](https://github.com/havok2-htwo) : for sharing the code for webcam
- [GosuDRM](https://github.com/GosuDRM) : for open version of roop
- [pereiraroland26](https://github.com/pereiraroland26) : Multiple faces support
- [vic4key](https://github.com/vic4key) : For supporting/contributing on this project
- [KRSHH](https://github.com/KRSHH) : For his contributions
- [kier007](https://github.com/kier007) : for improving the user experience
- and [all developers](https://github.com/hacksider/Deep-Live-Cam/graphs/contributors) behind libraries used in this project.
- Foot Note: Please be informed that the base author of the code is [s0md3v](https://github.com/s0md3v/roop)
- All the wonderful users who helped making this project go viral by starring the repo ❤️
- [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).
- [havok2-htwo](https://github.com/havok2-htwo): for sharing the code for webcam
- [GosuDRM](https://github.com/GosuDRM): for the open version of roop
- [pereiraroland26](https://github.com/pereiraroland26): Multiple faces support
- [vic4key](https://github.com/vic4key): For supporting/contributing to this project
- [kier007](https://github.com/kier007): for improving the user experience
- [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.
- 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
![Alt](https://repobeats.axiom.co/api/embed/fec8e29c45dfdb9c5916f3a7830e1249308d20e1.svg "Repobeats analytics image")
## Stars to the Moon 🚀
<a href="https://star-history.com/#hacksider/deep-live-cam&Date">
@@ -269,4 +378,4 @@ Looking for a CLI mode? Using the -s/--source argument will make the run program
<source media="(prefers-color-scheme: light)" srcset="https://api.star-history.com/svg?repos=hacksider/deep-live-cam&type=Date" />
<img alt="Star History Chart" src="https://api.star-history.com/svg?repos=hacksider/deep-live-cam&type=Date" />
</picture>
</a>
</a>

46
locales/zh.json Normal file
View File

@@ -0,0 +1,46 @@
{
"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 目标映射器已打开。"
}

BIN
media/live_show.gif Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.2 MiB

BIN
media/meme.gif Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.0 MiB

File diff suppressed because it is too large Load Diff

View File

@@ -39,13 +39,13 @@ def get_many_faces(frame: Frame) -> Any:
return None
def has_valid_map() -> bool:
for map in modules.globals.souce_target_map:
for map in modules.globals.source_target_map:
if "source" in map and "target" in map:
return True
return False
def default_source_face() -> Any:
for map in modules.globals.souce_target_map:
for map in modules.globals.source_target_map:
if "source" in map:
return map['source']['face']
return None
@@ -53,7 +53,7 @@ def default_source_face() -> Any:
def simplify_maps() -> Any:
centroids = []
faces = []
for map in modules.globals.souce_target_map:
for map in modules.globals.source_target_map:
if "source" in map and "target" in map:
centroids.append(map['target']['face'].normed_embedding)
faces.append(map['source']['face'])
@@ -64,10 +64,10 @@ def simplify_maps() -> Any:
def add_blank_map() -> Any:
try:
max_id = -1
if len(modules.globals.souce_target_map) > 0:
max_id = max(modules.globals.souce_target_map, key=lambda x: x['id'])['id']
if len(modules.globals.source_target_map) > 0:
max_id = max(modules.globals.source_target_map, key=lambda x: x['id'])['id']
modules.globals.souce_target_map.append({
modules.globals.source_target_map.append({
'id' : max_id + 1
})
except ValueError:
@@ -75,14 +75,14 @@ def add_blank_map() -> Any:
def get_unique_faces_from_target_image() -> Any:
try:
modules.globals.souce_target_map = []
modules.globals.source_target_map = []
target_frame = cv2.imread(modules.globals.target_path)
many_faces = get_many_faces(target_frame)
i = 0
for face in many_faces:
x_min, y_min, x_max, y_max = face['bbox']
modules.globals.souce_target_map.append({
modules.globals.source_target_map.append({
'id' : i,
'target' : {
'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:
try:
modules.globals.souce_target_map = []
modules.globals.source_target_map = []
frame_face_embeddings = []
face_embeddings = []
@@ -127,7 +127,7 @@ def get_unique_faces_from_target_video() -> Any:
face['target_centroid'] = closest_centroid_index
for i in range(len(centroids)):
modules.globals.souce_target_map.append({
modules.globals.source_target_map.append({
'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}"):
temp.append({'frame': frame['frame'], 'faces': [face for face in frame['faces'] if face['target_centroid'] == i], 'location': frame['location']})
modules.globals.souce_target_map[i]['target_faces_in_frame'] = temp
modules.globals.source_target_map[i]['target_faces_in_frame'] = temp
# dump_faces(centroids, frame_face_embeddings)
default_target_face()
@@ -144,7 +144,7 @@ def get_unique_faces_from_target_video() -> Any:
def default_target_face():
for map in modules.globals.souce_target_map:
for map in modules.globals.source_target_map:
best_face = None
best_frame = None
for frame in map['target_faces_in_frame']:

26
modules/gettext.py Normal file
View File

@@ -0,0 +1,26 @@
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)

View File

@@ -9,7 +9,7 @@ file_types = [
("Video", ("*.mp4", "*.mkv")),
]
souce_target_map = []
source_target_map = []
simple_map = {}
source_path = None

View File

@@ -4,6 +4,7 @@ import insightface
import threading
import numpy as np
import modules.globals
import logging
import modules.processors.frame.core
from modules.core import update_status
from modules.face_analyser import get_one_face, get_many_faces, default_source_face
@@ -105,24 +106,30 @@ def process_frame(source_face: Face, temp_frame: Frame) -> Frame:
many_faces = get_many_faces(temp_frame)
if many_faces:
for target_face in many_faces:
temp_frame = swap_face(source_face, target_face, temp_frame)
if source_face and target_face:
temp_frame = swap_face(source_face, target_face, temp_frame)
else:
print("Face detection failed for target/source.")
else:
target_face = get_one_face(temp_frame)
if target_face:
if target_face and source_face:
temp_frame = swap_face(source_face, target_face, temp_frame)
else:
logging.error("Face detection failed for target or source.")
return temp_frame
def process_frame_v2(temp_frame: Frame, temp_frame_path: str = "") -> Frame:
if is_image(modules.globals.target_path):
if modules.globals.many_faces:
source_face = default_source_face()
for map in modules.globals.souce_target_map:
for map in modules.globals.source_target_map:
target_face = map["target"]["face"]
temp_frame = swap_face(source_face, target_face, temp_frame)
elif not modules.globals.many_faces:
for map in modules.globals.souce_target_map:
for map in modules.globals.source_target_map:
if "source" in map:
source_face = map["source"]["face"]
target_face = map["target"]["face"]
@@ -131,7 +138,7 @@ def process_frame_v2(temp_frame: Frame, temp_frame_path: str = "") -> Frame:
elif is_video(modules.globals.target_path):
if modules.globals.many_faces:
source_face = default_source_face()
for map in modules.globals.souce_target_map:
for map in modules.globals.source_target_map:
target_frame = [
f
for f in map["target_faces_in_frame"]
@@ -143,7 +150,7 @@ def process_frame_v2(temp_frame: Frame, temp_frame_path: str = "") -> Frame:
temp_frame = swap_face(source_face, target_face, temp_frame)
elif not modules.globals.many_faces:
for map in modules.globals.souce_target_map:
for map in modules.globals.source_target_map:
if "source" in map:
target_frame = [
f

View File

@@ -7,7 +7,6 @@ from cv2_enumerate_cameras import enumerate_cameras # Add this import
from PIL import Image, ImageOps
import time
import json
import modules.globals
import modules.metadata
from modules.face_analyser import (
@@ -26,6 +25,12 @@ from modules.utilities import (
resolve_relative_path,
has_image_extension,
)
from modules.video_capture import VideoCapturer
from modules.gettext import LanguageManager
import platform
if platform.system() == "Windows":
from pygrabber.dshow_graph import FilterGraph
ROOT = None
POPUP = None
@@ -59,6 +64,7 @@ RECENT_DIRECTORY_SOURCE = None
RECENT_DIRECTORY_TARGET = None
RECENT_DIRECTORY_OUTPUT = None
_ = None
preview_label = None
preview_slider = None
source_label = None
@@ -73,9 +79,11 @@ target_label_dict_live = {}
img_ft, vid_ft = modules.globals.file_types
def init(start: Callable[[], None], destroy: Callable[[], None]) -> ctk.CTk:
global ROOT, PREVIEW
def init(start: Callable[[], None], destroy: Callable[[], None], lang: str) -> ctk.CTk:
global ROOT, PREVIEW, _
lang_manager = LanguageManager(lang)
_ = lang_manager._
ROOT = create_root(start, destroy)
PREVIEW = create_preview(ROOT)
@@ -96,7 +104,7 @@ def save_switch_states():
"fp_ui": modules.globals.fp_ui,
"show_fps": modules.globals.show_fps,
"mouth_mask": modules.globals.mouth_mask,
"show_mouth_mask_box": modules.globals.show_mouth_mask_box
"show_mouth_mask_box": modules.globals.show_mouth_mask_box,
}
with open("switch_states.json", "w") as f:
json.dump(switch_states, f)
@@ -118,7 +126,9 @@ def load_switch_states():
modules.globals.fp_ui = switch_states.get("fp_ui", {"face_enhancer": False})
modules.globals.show_fps = switch_states.get("show_fps", False)
modules.globals.mouth_mask = switch_states.get("mouth_mask", False)
modules.globals.show_mouth_mask_box = switch_states.get("show_mouth_mask_box", False)
modules.globals.show_mouth_mask_box = switch_states.get(
"show_mouth_mask_box", False
)
except FileNotFoundError:
# If the file doesn't exist, use default values
pass
@@ -148,7 +158,7 @@ def create_root(start: Callable[[], None], destroy: Callable[[], None]) -> ctk.C
target_label.place(relx=0.6, rely=0.1, relwidth=0.3, relheight=0.25)
select_face_button = ctk.CTkButton(
root, text="Select a face", cursor="hand2", command=lambda: select_source_path()
root, text=_("Select a face"), cursor="hand2", command=lambda: select_source_path()
)
select_face_button.place(relx=0.1, rely=0.4, relwidth=0.3, relheight=0.1)
@@ -159,7 +169,7 @@ def create_root(start: Callable[[], None], destroy: Callable[[], None]) -> ctk.C
select_target_button = ctk.CTkButton(
root,
text="Select a target",
text=_("Select a target"),
cursor="hand2",
command=lambda: select_target_path(),
)
@@ -168,7 +178,7 @@ def create_root(start: Callable[[], None], destroy: Callable[[], None]) -> ctk.C
keep_fps_value = ctk.BooleanVar(value=modules.globals.keep_fps)
keep_fps_checkbox = ctk.CTkSwitch(
root,
text="Keep fps",
text=_("Keep fps"),
variable=keep_fps_value,
cursor="hand2",
command=lambda: (
@@ -181,7 +191,7 @@ def create_root(start: Callable[[], None], destroy: Callable[[], None]) -> ctk.C
keep_frames_value = ctk.BooleanVar(value=modules.globals.keep_frames)
keep_frames_switch = ctk.CTkSwitch(
root,
text="Keep frames",
text=_("Keep frames"),
variable=keep_frames_value,
cursor="hand2",
command=lambda: (
@@ -194,7 +204,7 @@ def create_root(start: Callable[[], None], destroy: Callable[[], None]) -> ctk.C
enhancer_value = ctk.BooleanVar(value=modules.globals.fp_ui["face_enhancer"])
enhancer_switch = ctk.CTkSwitch(
root,
text="Face Enhancer",
text=_("Face Enhancer"),
variable=enhancer_value,
cursor="hand2",
command=lambda: (
@@ -207,7 +217,7 @@ def create_root(start: Callable[[], None], destroy: Callable[[], None]) -> ctk.C
keep_audio_value = ctk.BooleanVar(value=modules.globals.keep_audio)
keep_audio_switch = ctk.CTkSwitch(
root,
text="Keep audio",
text=_("Keep audio"),
variable=keep_audio_value,
cursor="hand2",
command=lambda: (
@@ -220,7 +230,7 @@ def create_root(start: Callable[[], None], destroy: Callable[[], None]) -> ctk.C
many_faces_value = ctk.BooleanVar(value=modules.globals.many_faces)
many_faces_switch = ctk.CTkSwitch(
root,
text="Many faces",
text=_("Many faces"),
variable=many_faces_value,
cursor="hand2",
command=lambda: (
@@ -233,7 +243,7 @@ def create_root(start: Callable[[], None], destroy: Callable[[], None]) -> ctk.C
color_correction_value = ctk.BooleanVar(value=modules.globals.color_correction)
color_correction_switch = ctk.CTkSwitch(
root,
text="Fix Blueish Cam",
text=_("Fix Blueish Cam"),
variable=color_correction_value,
cursor="hand2",
command=lambda: (
@@ -250,13 +260,13 @@ def create_root(start: Callable[[], None], destroy: Callable[[], None]) -> ctk.C
map_faces = ctk.BooleanVar(value=modules.globals.map_faces)
map_faces_switch = ctk.CTkSwitch(
root,
text="Map faces",
text=_("Map faces"),
variable=map_faces,
cursor="hand2",
command=lambda: (
setattr(modules.globals, "map_faces", map_faces.get()),
save_switch_states(),
close_popup_if_switch_off()
close_mapper_window() if not map_faces.get() else None
),
)
map_faces_switch.place(relx=0.1, rely=0.75)
@@ -264,7 +274,7 @@ def create_root(start: Callable[[], None], destroy: Callable[[], None]) -> ctk.C
show_fps_value = ctk.BooleanVar(value=modules.globals.show_fps)
show_fps_switch = ctk.CTkSwitch(
root,
text="Show FPS",
text=_("Show FPS"),
variable=show_fps_value,
cursor="hand2",
command=lambda: (
@@ -277,7 +287,7 @@ def create_root(start: Callable[[], None], destroy: Callable[[], None]) -> ctk.C
mouth_mask_var = ctk.BooleanVar(value=modules.globals.mouth_mask)
mouth_mask_switch = ctk.CTkSwitch(
root,
text="Mouth Mask",
text=_("Mouth Mask"),
variable=mouth_mask_var,
cursor="hand2",
command=lambda: setattr(modules.globals, "mouth_mask", mouth_mask_var.get()),
@@ -287,7 +297,7 @@ def create_root(start: Callable[[], None], destroy: Callable[[], None]) -> ctk.C
show_mouth_mask_box_var = ctk.BooleanVar(value=modules.globals.show_mouth_mask_box)
show_mouth_mask_box_switch = ctk.CTkSwitch(
root,
text="Show Mouth Mask Box",
text=_("Show Mouth Mask Box"),
variable=show_mouth_mask_box_var,
cursor="hand2",
command=lambda: setattr(
@@ -297,48 +307,59 @@ def create_root(start: Callable[[], None], destroy: Callable[[], None]) -> ctk.C
show_mouth_mask_box_switch.place(relx=0.6, rely=0.55)
start_button = ctk.CTkButton(
root, text="Start", cursor="hand2", command=lambda: analyze_target(start, root)
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)
stop_button = ctk.CTkButton(
root, text="Destroy", cursor="hand2", command=lambda: destroy()
root, text=_("Destroy"), cursor="hand2", command=lambda: destroy()
)
stop_button.place(relx=0.4, rely=0.80, relwidth=0.2, relheight=0.05)
preview_button = ctk.CTkButton(
root, text="Preview", cursor="hand2", command=lambda: toggle_preview()
root, text=_("Preview"), cursor="hand2", command=lambda: toggle_preview()
)
preview_button.place(relx=0.65, rely=0.80, relwidth=0.2, relheight=0.05)
# --- Camera Selection ---
camera_label = ctk.CTkLabel(root, text="Select Camera:")
camera_label = ctk.CTkLabel(root, text=_("Select Camera:"))
camera_label.place(relx=0.1, rely=0.86, relwidth=0.2, relheight=0.05)
available_cameras = get_available_cameras()
# Convert camera indices to strings for CTkOptionMenu
available_camera_indices, available_camera_strings = available_cameras
camera_variable = ctk.StringVar(
value=(
available_camera_strings[0]
if available_camera_strings
else "No cameras found"
camera_indices, camera_names = available_cameras
if not camera_names or camera_names[0] == "No cameras found":
camera_variable = ctk.StringVar(value="No cameras found")
camera_optionmenu = ctk.CTkOptionMenu(
root,
variable=camera_variable,
values=["No cameras found"],
state="disabled",
)
)
camera_optionmenu = ctk.CTkOptionMenu(
root, variable=camera_variable, values=available_camera_strings
)
else:
camera_variable = ctk.StringVar(value=camera_names[0])
camera_optionmenu = ctk.CTkOptionMenu(
root, variable=camera_variable, values=camera_names
)
camera_optionmenu.place(relx=0.35, rely=0.86, relwidth=0.25, relheight=0.05)
live_button = ctk.CTkButton(
root,
text="Live",
text=_("Live"),
cursor="hand2",
command=lambda: webcam_preview(
root,
available_camera_indices[
available_camera_strings.index(camera_variable.get())
],
(
camera_indices[camera_names.index(camera_variable.get())]
if camera_names and camera_names[0] != "No cameras found"
else None
),
),
state=(
"normal"
if camera_names and camera_names[0] != "No cameras found"
else "disabled"
),
)
live_button.place(relx=0.65, rely=0.86, relwidth=0.2, relheight=0.05)
@@ -355,11 +376,20 @@ def create_root(start: Callable[[], None], destroy: Callable[[], None]) -> ctk.C
text_color=ctk.ThemeManager.theme.get("URL").get("text_color")
)
donate_label.bind(
"<Button>", lambda event: webbrowser.open("https://paypal.me/hacksider")
"<Button>", lambda event: webbrowser.open("https://deeplivecam.net")
)
return root
def close_mapper_window():
global POPUP, POPUP_LIVE
if POPUP and POPUP.winfo_exists():
POPUP.destroy()
POPUP = None
if POPUP_LIVE and POPUP_LIVE.winfo_exists():
POPUP_LIVE.destroy()
POPUP_LIVE = None
def analyze_target(start: Callable[[], None], root: ctk.CTk):
if POPUP != None and POPUP.winfo_exists():
@@ -367,7 +397,7 @@ def analyze_target(start: Callable[[], None], root: ctk.CTk):
return
if modules.globals.map_faces:
modules.globals.souce_target_map = []
modules.globals.source_target_map = []
if is_image(modules.globals.target_path):
update_status("Getting unique faces")
@@ -376,37 +406,28 @@ def analyze_target(start: Callable[[], None], root: ctk.CTk):
update_status("Getting unique faces")
get_unique_faces_from_target_video()
if len(modules.globals.souce_target_map) > 0:
create_source_target_popup(start, root, modules.globals.souce_target_map)
if len(modules.globals.source_target_map) > 0:
create_source_target_popup(start, root, modules.globals.source_target_map)
else:
update_status("No faces found in target")
else:
select_output_path(start)
def close_popup_if_switch_off():
global POPUP, POPUP_LIVE
if not modules.globals.map_faces:
if POPUP and POPUP.winfo_exists():
POPUP.destroy()
POPUP = None
if POPUP_LIVE and POPUP_LIVE.winfo_exists():
POPUP_LIVE.destroy()
POPUP_LIVE = None
def create_source_target_popup(
start: Callable[[], None], root: ctk.CTk, map: list
start: Callable[[], None], root: ctk.CTk, map: list
) -> None:
global POPUP, popup_status_label
POPUP = ctk.CTkToplevel(root)
POPUP.title("Source x Target Mapper")
POPUP.title(_("Source x Target Mapper"))
POPUP.geometry(f"{POPUP_WIDTH}x{POPUP_HEIGHT}")
POPUP.focus()
def on_submit_click(start):
if has_valid_map():
simplify_maps()
update_pop_live_status("Mappings submitted successfully!")
POPUP.destroy()
select_output_path(start)
else:
update_pop_status("Atleast 1 source with target is required!")
@@ -423,7 +444,7 @@ def create_source_target_popup(
button = ctk.CTkButton(
scrollable_frame,
text="Select source image",
text=_("Select source image"),
command=lambda id=id: on_button_click(map, id),
width=DEFAULT_BUTTON_WIDTH,
height=DEFAULT_BUTTON_HEIGHT,
@@ -457,18 +478,18 @@ def create_source_target_popup(
popup_status_label.grid(row=1, column=0, pady=15)
close_button = ctk.CTkButton(
POPUP, text="Submit", command=lambda: on_submit_click(start)
POPUP, text=_("Submit"), command=lambda: on_submit_click(start)
)
close_button.grid(row=2, column=0, pady=10)
def update_popup_source(
scrollable_frame: ctk.CTkScrollableFrame, map: list, button_num: int
scrollable_frame: ctk.CTkScrollableFrame, map: list, button_num: int
) -> list:
global source_label_dict
source_path = ctk.filedialog.askopenfilename(
title="select an source image",
title=_("select an source image"),
initialdir=RECENT_DIRECTORY_SOURCE,
filetypes=[img_ft],
)
@@ -488,7 +509,7 @@ def update_popup_source(
x_min, y_min, x_max, y_max = face["bbox"]
map[button_num]["source"] = {
"cv2": cv2_img[int(y_min) : int(y_max), int(x_min) : int(x_max)],
"cv2": cv2_img[int(y_min): int(y_max), int(x_min): int(x_max)],
"face": face,
}
@@ -519,7 +540,7 @@ def create_preview(parent: ctk.CTkToplevel) -> ctk.CTkToplevel:
preview = ctk.CTkToplevel(parent)
preview.withdraw()
preview.title("Preview")
preview.title(_("Preview"))
preview.configure()
preview.protocol("WM_DELETE_WINDOW", lambda: toggle_preview())
preview.resizable(width=True, height=True)
@@ -535,16 +556,16 @@ def create_preview(parent: ctk.CTkToplevel) -> ctk.CTkToplevel:
def update_status(text: str) -> None:
status_label.configure(text=text)
status_label.configure(text=_(text))
ROOT.update()
def update_pop_status(text: str) -> None:
popup_status_label.configure(text=text)
popup_status_label.configure(text=_(text))
def update_pop_live_status(text: str) -> None:
popup_status_label_live.configure(text=text)
popup_status_label_live.configure(text=_(text))
def update_tumbler(var: str, value: bool) -> None:
@@ -563,7 +584,7 @@ def select_source_path() -> None:
PREVIEW.withdraw()
source_path = ctk.filedialog.askopenfilename(
title="select an source image",
title=_("select an source image"),
initialdir=RECENT_DIRECTORY_SOURCE,
filetypes=[img_ft],
)
@@ -606,7 +627,7 @@ def select_target_path() -> None:
PREVIEW.withdraw()
target_path = ctk.filedialog.askopenfilename(
title="select an target image or video",
title=_("select an target image or video"),
initialdir=RECENT_DIRECTORY_TARGET,
filetypes=[img_ft, vid_ft],
)
@@ -630,7 +651,7 @@ def select_output_path(start: Callable[[], None]) -> None:
if is_image(modules.globals.target_path):
output_path = ctk.filedialog.asksaveasfilename(
title="save image output file",
title=_("save image output file"),
filetypes=[img_ft],
defaultextension=".png",
initialfile="output.png",
@@ -638,7 +659,7 @@ def select_output_path(start: Callable[[], None]) -> None:
)
elif is_video(modules.globals.target_path):
output_path = ctk.filedialog.asksaveasfilename(
title="save video output file",
title=_("save video output file"),
filetypes=[vid_ft],
defaultextension=".mp4",
initialfile="output.mp4",
@@ -675,17 +696,21 @@ def check_and_ignore_nsfw(target, destroy: Callable = None) -> bool:
def fit_image_to_size(image, width: int, height: int):
if width is None and height is None:
if width is None or height is None or width <= 0 or height <= 0:
return image
h, w, _ = image.shape
ratio_h = 0.0
ratio_w = 0.0
if width > height:
ratio_h = height / h
else:
ratio_w = width / w
ratio = max(ratio_w, ratio_h)
new_size = (int(ratio * w), int(ratio * h))
ratio_w = width / w
ratio_h = height / h
# Use the smaller ratio to ensure the image fits within the given dimensions
ratio = min(ratio_w, ratio_h)
# Compute new dimensions, ensuring they're at least 1 pixel
new_width = max(1, int(ratio * w))
new_height = max(1, int(ratio * h))
new_size = (new_width, new_height)
return cv2.resize(image, dsize=new_size)
@@ -697,7 +722,7 @@ def render_image_preview(image_path: str, size: Tuple[int, int]) -> ctk.CTkImage
def render_video_preview(
video_path: str, size: Tuple[int, int], frame_number: int = 0
video_path: str, size: Tuple[int, int], frame_number: int = 0
) -> ctk.CTkImage:
capture = cv2.VideoCapture(video_path)
if frame_number:
@@ -737,7 +762,7 @@ def update_preview(frame_number: int = 0) -> None:
if modules.globals.nsfw_filter and check_and_ignore_nsfw(temp_frame):
return
for frame_processor in get_frame_processors_modules(
modules.globals.frame_processors
modules.globals.frame_processors
):
temp_frame = frame_processor.process_frame(
get_one_face(cv2.imread(modules.globals.source_path)), temp_frame
@@ -755,142 +780,201 @@ def update_preview(frame_number: int = 0) -> None:
def webcam_preview(root: ctk.CTk, camera_index: int):
global POPUP_LIVE
if POPUP_LIVE is not None and POPUP_LIVE.winfo_exists():
if POPUP_LIVE and POPUP_LIVE.winfo_exists():
update_status("Source x Target Mapper is already open.")
POPUP_LIVE.focus()
return
if not modules.globals.map_faces:
if modules.globals.source_path is None:
# No image selected
update_status("Please select a source image first")
return
create_webcam_preview(camera_index)
else:
modules.globals.souce_target_map = []
modules.globals.source_target_map = []
create_source_target_popup_for_webcam(
root, modules.globals.souce_target_map, camera_index
root, modules.globals.source_target_map, camera_index
)
def get_available_cameras():
"""Returns a list of available camera names and indices."""
camera_indices = []
camera_names = []
if platform.system() == "Windows":
try:
graph = FilterGraph()
devices = graph.get_input_devices()
for camera in enumerate_cameras():
cap = cv2.VideoCapture(camera.index)
if cap.isOpened():
camera_indices.append(camera.index)
camera_names.append(camera.name)
cap.release()
return (camera_indices, camera_names)
# Create list of indices and names
camera_indices = list(range(len(devices)))
camera_names = devices
# If no cameras found through DirectShow, try OpenCV fallback
if not camera_names:
# Try to open camera with index -1 and 0
test_indices = [-1, 0]
working_cameras = []
for idx in test_indices:
cap = cv2.VideoCapture(idx)
if cap.isOpened():
working_cameras.append(f"Camera {idx}")
cap.release()
if working_cameras:
return test_indices[: len(working_cameras)], working_cameras
# If still no cameras found, return empty lists
if not camera_names:
return [], ["No cameras found"]
return camera_indices, camera_names
except Exception as e:
print(f"Error detecting cameras: {str(e)}")
return [], ["No cameras found"]
else:
# Unix-like systems (Linux/Mac) camera detection
camera_indices = []
camera_names = []
if platform.system() == "Darwin": # macOS specific handling
# Try to open the default FaceTime camera first
cap = cv2.VideoCapture(0)
if cap.isOpened():
camera_indices.append(0)
camera_names.append("FaceTime Camera")
cap.release()
# On macOS, additional cameras typically use indices 1 and 2
for i in [1, 2]:
cap = cv2.VideoCapture(i)
if cap.isOpened():
camera_indices.append(i)
camera_names.append(f"Camera {i}")
cap.release()
else:
# Linux camera detection - test first 10 indices
for i in range(10):
cap = cv2.VideoCapture(i)
if cap.isOpened():
camera_indices.append(i)
camera_names.append(f"Camera {i}")
cap.release()
if not camera_names:
return [], ["No cameras found"]
return camera_indices, camera_names
def create_webcam_preview(camera_index: int):
global preview_label, PREVIEW
camera = cv2.VideoCapture(camera_index)
camera.set(cv2.CAP_PROP_FRAME_WIDTH, PREVIEW_DEFAULT_WIDTH)
camera.set(cv2.CAP_PROP_FRAME_HEIGHT, PREVIEW_DEFAULT_HEIGHT)
camera.set(cv2.CAP_PROP_FPS, 60)
cap = VideoCapturer(camera_index)
if not cap.start(PREVIEW_DEFAULT_WIDTH, PREVIEW_DEFAULT_HEIGHT, 60):
update_status("Failed to start camera")
return
preview_label.configure(width=PREVIEW_DEFAULT_WIDTH, height=PREVIEW_DEFAULT_HEIGHT)
PREVIEW.deiconify()
frame_processors = get_frame_processors_modules(modules.globals.frame_processors)
source_image = None
prev_time = time.time()
fps_update_interval = 0.5 # Update FPS every 0.5 seconds
fps_update_interval = 0.5
frame_count = 0
fps = 0
try:
while camera:
ret, frame = camera.read()
if not ret:
break
while True:
ret, frame = cap.read()
if not ret:
break
temp_frame = frame.copy()
temp_frame = frame.copy()
if modules.globals.live_mirror:
temp_frame = cv2.flip(temp_frame, 1)
if modules.globals.live_mirror:
temp_frame = cv2.flip(temp_frame, 1)
if modules.globals.live_resizable:
temp_frame = fit_image_to_size(
temp_frame, PREVIEW.winfo_width(), PREVIEW.winfo_height()
)
if not modules.globals.map_faces:
if source_image is None and modules.globals.source_path:
source_image = get_one_face(cv2.imread(modules.globals.source_path))
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)
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)
# Calculate and display FPS
current_time = time.time()
frame_count += 1
if current_time - prev_time >= fps_update_interval:
fps = frame_count / (current_time - prev_time)
frame_count = 0
prev_time = current_time
if modules.globals.show_fps:
cv2.putText(
temp_frame,
f"FPS: {fps:.1f}",
(10, 30),
cv2.FONT_HERSHEY_SIMPLEX,
1,
(0, 255, 0),
2,
)
image = cv2.cvtColor(temp_frame, cv2.COLOR_BGR2RGB)
image = Image.fromarray(image)
image = ImageOps.contain(
image, (temp_frame.shape[1], temp_frame.shape[0]), Image.LANCZOS
if modules.globals.live_resizable:
temp_frame = fit_image_to_size(
temp_frame, PREVIEW.winfo_width(), PREVIEW.winfo_height()
)
image = ctk.CTkImage(image, size=image.size)
preview_label.configure(image=image)
ROOT.update()
if PREVIEW.state() == "withdrawn":
break
finally:
camera.release()
PREVIEW.withdraw()
else:
temp_frame = fit_image_to_size(
temp_frame, PREVIEW.winfo_width(), PREVIEW.winfo_height()
)
if not modules.globals.map_faces:
if source_image is None and modules.globals.source_path:
source_image = get_one_face(cv2.imread(modules.globals.source_path))
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)
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)
# Calculate and display FPS
current_time = time.time()
frame_count += 1
if current_time - prev_time >= fps_update_interval:
fps = frame_count / (current_time - prev_time)
frame_count = 0
prev_time = current_time
if modules.globals.show_fps:
cv2.putText(
temp_frame,
f"FPS: {fps:.1f}",
(10, 30),
cv2.FONT_HERSHEY_SIMPLEX,
1,
(0, 255, 0),
2,
)
image = cv2.cvtColor(temp_frame, cv2.COLOR_BGR2RGB)
image = Image.fromarray(image)
image = ImageOps.contain(
image, (temp_frame.shape[1], temp_frame.shape[0]), Image.LANCZOS
)
image = ctk.CTkImage(image, size=image.size)
preview_label.configure(image=image)
ROOT.update()
if PREVIEW.state() == "withdrawn":
break
cap.release()
PREVIEW.withdraw()
def create_source_target_popup_for_webcam(
root: ctk.CTk, map: list, camera_index: int
root: ctk.CTk, map: list, camera_index: int
) -> None:
global POPUP_LIVE, popup_status_label_live
POPUP_LIVE = ctk.CTkToplevel(root)
POPUP_LIVE.title("Source x Target Mapper")
POPUP_LIVE.title(_("Source x Target Mapper"))
POPUP_LIVE.geometry(f"{POPUP_LIVE_WIDTH}x{POPUP_LIVE_HEIGHT}")
POPUP_LIVE.focus()
def on_submit_click():
if has_valid_map():
simplify_maps()
create_webcam_preview(camera_index)
update_pop_live_status("Mappings successfully submitted!")
create_webcam_preview(camera_index) # Open the preview window
else:
update_pop_live_status("At least 1 source with target is required!")
@@ -900,34 +984,42 @@ def create_source_target_popup_for_webcam(
update_pop_live_status("Please provide mapping!")
def on_clear_click():
for item in map:
if "source" in item:
item.pop("source")
if "target" in item:
item.pop("target")
clear_source_target_images(map)
refresh_data(map)
update_pop_live_status("Source and target images cleared.")
update_pop_live_status("All mappings cleared!")
popup_status_label_live = ctk.CTkLabel(POPUP_LIVE, text=None, justify="center")
popup_status_label_live.grid(row=1, column=0, pady=15)
add_button = ctk.CTkButton(POPUP_LIVE, text="Add", command=lambda: on_add_click())
add_button = ctk.CTkButton(POPUP_LIVE, text=_("Add"), command=lambda: on_add_click())
add_button.place(relx=0.1, rely=0.92, relwidth=0.2, relheight=0.05)
clear_button = ctk.CTkButton(
POPUP_LIVE,
text="Clear",
command=lambda: on_clear_click(),
state="normal",
)
clear_button.place(relx=0.4, rely=0.92, relwidth=0.15, relheight=0.05)
clear_button = ctk.CTkButton(POPUP_LIVE, text=_("Clear"), command=lambda: on_clear_click())
clear_button.place(relx=0.4, rely=0.92, relwidth=0.2, relheight=0.05)
close_button = ctk.CTkButton(
POPUP_LIVE, text="Submit", command=lambda: on_submit_click()
POPUP_LIVE, text=_("Submit"), command=lambda: on_submit_click()
)
close_button.place(relx=0.7, rely=0.92, relwidth=0.2, relheight=0.05)
refresh_data(map)
def clear_source_target_images(map: list):
global source_label_dict_live, target_label_dict_live
for item in map:
if "source" in item:
del item["source"]
if "target" in item:
del item["target"]
for button_num in list(source_label_dict_live.keys()):
source_label_dict_live[button_num].destroy()
del source_label_dict_live[button_num]
for button_num in list(target_label_dict_live.keys()):
target_label_dict_live[button_num].destroy()
del target_label_dict_live[button_num]
def refresh_data(map: list):
@@ -949,7 +1041,7 @@ def refresh_data(map: list):
button = ctk.CTkButton(
scrollable_frame,
text="Select source image",
text=_("Select source image"),
command=lambda id=id: on_sbutton_click(map, id),
width=DEFAULT_BUTTON_WIDTH,
height=DEFAULT_BUTTON_HEIGHT,
@@ -966,7 +1058,7 @@ def refresh_data(map: list):
button = ctk.CTkButton(
scrollable_frame,
text="Select target image",
text=_("Select target image"),
command=lambda id=id: on_tbutton_click(map, id),
width=DEFAULT_BUTTON_WIDTH,
height=DEFAULT_BUTTON_HEIGHT,
@@ -974,45 +1066,49 @@ def refresh_data(map: list):
button.grid(row=id, column=3, padx=20, pady=10)
if "source" in item:
source_label = ctk.CTkLabel(
image = Image.fromarray(
cv2.cvtColor(item["source"]["cv2"], cv2.COLOR_BGR2RGB)
)
image = image.resize(
(MAPPER_PREVIEW_MAX_WIDTH, MAPPER_PREVIEW_MAX_HEIGHT), Image.LANCZOS
)
tk_image = ctk.CTkImage(image, size=image.size)
source_image = ctk.CTkLabel(
scrollable_frame,
text="",
text=f"S-{id}",
width=MAPPER_PREVIEW_MAX_WIDTH,
height=MAPPER_PREVIEW_MAX_HEIGHT,
)
source_label.grid(row=id, column=1, padx=10, pady=10)
else:
ctk.CTkLabel(
scrollable_frame,
text="No Source",
width=MAPPER_PREVIEW_MAX_WIDTH,
height=MAPPER_PREVIEW_MAX_HEIGHT,
).grid(row=id, column=1, padx=10, pady=10)
source_image.grid(row=id, column=1, padx=10, pady=10)
source_image.configure(image=tk_image)
if "target" in item:
target_label = ctk.CTkLabel(
image = Image.fromarray(
cv2.cvtColor(item["target"]["cv2"], cv2.COLOR_BGR2RGB)
)
image = image.resize(
(MAPPER_PREVIEW_MAX_WIDTH, MAPPER_PREVIEW_MAX_HEIGHT), Image.LANCZOS
)
tk_image = ctk.CTkImage(image, size=image.size)
target_image = ctk.CTkLabel(
scrollable_frame,
text="",
text=f"T-{id}",
width=MAPPER_PREVIEW_MAX_WIDTH,
height=MAPPER_PREVIEW_MAX_HEIGHT,
)
target_label.grid(row=id, column=4, padx=20, pady=10)
else:
ctk.CTkLabel(
scrollable_frame,
text="No Target",
width=MAPPER_PREVIEW_MAX_WIDTH,
height=MAPPER_PREVIEW_MAX_HEIGHT,
).grid(row=id, column=4, padx=20, pady=10)
target_image.grid(row=id, column=4, padx=20, pady=10)
target_image.configure(image=tk_image)
def update_webcam_source(
scrollable_frame: ctk.CTkScrollableFrame, map: list, button_num: int
scrollable_frame: ctk.CTkScrollableFrame, map: list, button_num: int
) -> list:
global source_label_dict_live
source_path = ctk.filedialog.askopenfilename(
title="select an source image",
title=_("select an source image"),
initialdir=RECENT_DIRECTORY_SOURCE,
filetypes=[img_ft],
)
@@ -1026,18 +1122,13 @@ def update_webcam_source(
return map
else:
cv2_img = cv2.imread(source_path)
if cv2_img is None:
update_pop_live_status("Failed to load the selected image. Please try again.")
return map
face = get_one_face(cv2_img)
if face:
x_min, y_min, x_max, y_max = face["bbox"]
map[button_num]["source"] = {
"cv2": cv2_img[int(y_min) : int(y_max), int(x_min) : int(x_max)],
"cv2": cv2_img[int(y_min): int(y_max), int(x_min): int(x_max)],
"face": face,
}
@@ -1064,12 +1155,12 @@ def update_webcam_source(
def update_webcam_target(
scrollable_frame: ctk.CTkScrollableFrame, map: list, button_num: int
scrollable_frame: ctk.CTkScrollableFrame, map: list, button_num: int
) -> list:
global target_label_dict_live
target_path = ctk.filedialog.askopenfilename(
title="select an target image",
title=_("select an target image"),
initialdir=RECENT_DIRECTORY_SOURCE,
filetypes=[img_ft],
)
@@ -1089,7 +1180,7 @@ def update_webcam_target(
x_min, y_min, x_max, y_max = face["bbox"]
map[button_num]["target"] = {
"cv2": cv2_img[int(y_min) : int(y_max), int(x_min) : int(x_max)],
"cv2": cv2_img[int(y_min): int(y_max), int(x_min): int(x_max)],
"face": face,
}

View File

@@ -1,21 +1,22 @@
--extra-index-url https://download.pytorch.org/whl/cu118
--extra-index-url https://download.pytorch.org/whl/nightly/cu128
numpy>=1.23.5,<2
opencv-python==4.10.0.84
cv2_enumerate_cameras==1.1.15
onnx==1.16.0
typing-extensions>=4.8.0
opencv-python==4.11.0.86
onnx==1.17.0
cv2_enumerate_cameras==1.1.18.3
insightface==0.7.3
psutil==5.9.8
tk==0.1.0
customtkinter==5.2.2
pillow==9.5.0
torch==2.0.1+cu118; sys_platform != 'darwin'
torch==2.0.1; sys_platform == 'darwin'
torchvision==0.15.2+cu118; sys_platform != 'darwin'
torchvision==0.15.2; sys_platform == 'darwin'
onnxruntime-silicon==1.16.3; sys_platform == 'darwin' and platform_machine == 'arm64'
onnxruntime-gpu==1.16.3; sys_platform != 'darwin'
tensorflow==2.12.1; sys_platform != 'darwin'
pillow==11.1.0
torch; sys_platform != 'darwin'
torch; sys_platform == 'darwin'
torchvision; sys_platform != 'darwin'
torchvision; sys_platform == 'darwin'
onnxruntime-silicon==1.21; sys_platform == 'darwin' and platform_machine == 'arm64'
onnxruntime-gpu==1.21; sys_platform != 'darwin'
tensorflow; sys_platform != 'darwin'
opennsfw2==0.10.2
protobuf==4.23.2
tqdm==4.66.4