Merge pull request #877 from qitianai/add-lang

Add multi language UI
pull/879/head
Kenneth Estanislao 2025-01-07 17:57:10 +08:00 committed by GitHub
commit ee19c5158a
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
7 changed files with 269 additions and 128 deletions

158
README.md
View File

@ -13,24 +13,52 @@
<img src="media/avgpcperformancedemo.gif" alt="Performance Demo GIF">
</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.
## Disclaimer
###### 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.
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.
###### 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.
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.
## Quick Start - Pre-built
<div align="center">
<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" />
</a>
<a href="https://krshh.gumroad.com/l/Deep-Live-Cam-Mac">
<img src="https://github.com/user-attachments/assets/d5d913b5-a7de-4609-96b9-979a5749a703" width="285" height="77" />
</a>
</div>
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.
## Features - Everything is real-time
### Mouth Mask
## Quick Start (Windows / Nvidia)
**Retain your original mouth using Mouth Mask**
[![Download](media/download.png)](https://hacksider.gumroad.com/l/vccdmm)
![resizable-gif](media/ludwig.gif)
[Download latest pre-built version with CUDA support](https://hacksider.gumroad.com/l/vccdmm) - No Manual Installation/Downloading required and Early features testing.
### Face Mapping
**Use different faces on multiple subjects**
![face\_mapping\_source](media/streamers.gif)
### Your Movie, Your Face
**Watch movies with any face in real-time**
![movie](media/movie.gif)
## Benchmarks
**Nearly 0% detection!**
![bench](media/deepwarebench.gif)
## 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>
@ -39,24 +67,24 @@ 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)
- [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
```
**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.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.
@ -76,18 +104,20 @@ brew install python-tk@3.10
**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)
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)
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
```
@ -95,11 +125,14 @@ python run.py --execution-provider cuda
**CoreML Execution Provider (Apple Silicon)**
1. Install dependencies:
```bash
pip uninstall onnxruntime onnxruntime-silicon
pip install onnxruntime-silicon==1.13.1
```
2. Usage:
```bash
python run.py --execution-provider coreml
```
@ -107,11 +140,14 @@ python run.py --execution-provider coreml
**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
```
@ -119,11 +155,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
```
@ -131,62 +170,37 @@ 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.
## Features - Everything is realtime
### Mouth Mask
**Retain your original mouth using Mouth Mask**
![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)
- 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.
## Command Line Arguments (Unmaintained)
@ -216,9 +230,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
@ -231,24 +245,28 @@ Looking for a CLI mode? Using the -s/--source argument will make the run program
- [*"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
- [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
- 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">

46
locales/zh.json 100644
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 目标映射器已打开。"
}

View File

@ -44,6 +44,7 @@ def parse_args() -> None:
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-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-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())
@ -78,6 +79,7 @@ def parse_args() -> None:
modules.globals.max_memory = args.max_memory
modules.globals.execution_providers = decode_execution_providers(args.execution_provider)
modules.globals.execution_threads = args.execution_threads
modules.globals.lang = args.lang
#for ENHANCER tumbler:
if 'face_enhancer' in args.frame_processor:
@ -253,5 +255,5 @@ def run() -> None:
if modules.globals.headless:
start()
else:
window = ui.init(start, destroy)
window = ui.init(start, destroy, modules.globals.lang)
window.mainloop()

26
modules/gettext.py 100644
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

@ -1,3 +1,3 @@
name = 'Deep-Live-Cam'
version = '1.7.5'
version = '1.8'
edition = 'GitHub Edition'

View File

@ -26,6 +26,7 @@ from modules.utilities import (
has_image_extension,
)
from modules.video_capture import VideoCapturer
from modules.gettext import LanguageManager
import platform
if platform.system() == "Windows":
@ -63,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
@ -77,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)
@ -154,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)
@ -165,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(),
)
@ -174,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: (
@ -187,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: (
@ -200,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: (
@ -213,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: (
@ -226,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: (
@ -239,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: (
@ -256,12 +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_mapper_window() if not map_faces.get() else None
),
)
map_faces_switch.place(relx=0.1, rely=0.75)
@ -269,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: (
@ -282,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()),
@ -292,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(
@ -302,22 +307,22 @@ 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()
@ -341,7 +346,7 @@ def create_root(start: Callable[[], None], destroy: Callable[[], None]) -> ctk.C
live_button = ctk.CTkButton(
root,
text="Live",
text=_("Live"),
cursor="hand2",
command=lambda: webcam_preview(
root,
@ -371,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():
@ -401,12 +415,12 @@ def analyze_target(start: Callable[[], None], root: ctk.CTk):
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()
@ -430,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,
@ -464,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],
)
@ -495,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,
}
@ -526,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)
@ -542,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:
@ -570,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],
)
@ -613,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],
)
@ -637,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",
@ -645,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",
@ -704,7 +718,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:
@ -744,7 +758,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
@ -760,6 +774,13 @@ def update_preview(frame_number: int = 0) -> None:
def webcam_preview(root: ctk.CTk, camera_index: int):
global POPUP_LIVE
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:
update_status("Please select a source image first")
@ -772,6 +793,7 @@ def webcam_preview(root: ctk.CTk, camera_index: int):
)
def get_available_cameras():
"""Returns a list of available camera names and indices."""
if platform.system() == "Windows":
@ -819,7 +841,7 @@ def get_available_cameras():
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)
@ -935,20 +957,20 @@ def create_webcam_preview(camera_index: int):
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():
POPUP_LIVE.destroy()
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!")
@ -957,16 +979,43 @@ def create_source_target_popup_for_webcam(
refresh_data(map)
update_pop_live_status("Please provide mapping!")
def on_clear_click():
clear_source_target_images(map)
refresh_data(map)
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.place(relx=0.2, rely=0.92, relwidth=0.2, relheight=0.05)
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())
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.6, rely=0.92, relwidth=0.2, relheight=0.05)
close_button.place(relx=0.7, rely=0.92, relwidth=0.2, relheight=0.05)
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):
@ -988,7 +1037,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,
@ -1005,7 +1054,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,
@ -1050,12 +1099,12 @@ def refresh_data(map: list):
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],
)
@ -1075,7 +1124,7 @@ def update_webcam_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,
}
@ -1102,12 +1151,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],
)
@ -1127,7 +1176,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,
}
@ -1150,4 +1199,4 @@ def update_webcam_target(
target_label_dict_live[button_num] = target_image
else:
update_pop_live_status("Face could not be detected in last upload!")
return map
return map

View File

@ -1,7 +1,7 @@
--extra-index-url https://download.pytorch.org/whl/cu118
numpy>=1.23.5,<2
opencv-python==4.8.1.78
opencv-python==4.10.0.84
cv2_enumerate_cameras==1.1.15
onnx==1.16.0
insightface==0.7.3