diff --git a/.github/workflows/build_dot.yaml b/.github/workflows/build_dot.yaml
index 1f2a1dd..390c229 100644
--- a/.github/workflows/build_dot.yaml
+++ b/.github/workflows/build_dot.yaml
@@ -14,7 +14,7 @@ on:
jobs:
build-and-test:
if: github.event.pull_request.draft == false
- runs-on: ubuntu-18.04
+ runs-on: ubuntu-latest
steps:
- name: Code Checkout
uses: actions/checkout@v3
diff --git a/.github/workflows/code_check.yaml b/.github/workflows/code_check.yaml
index 189af44..1e5ca86 100644
--- a/.github/workflows/code_check.yaml
+++ b/.github/workflows/code_check.yaml
@@ -10,7 +10,7 @@ on:
jobs:
code-check:
if: github.event.pull_request.draft == false
- runs-on: ubuntu-18.04
+ runs-on: ubuntu-latest
steps:
- name: Code Checkout
uses: actions/checkout@v2
diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml
index df72649..ff5bdb1 100644
--- a/.pre-commit-config.yaml
+++ b/.pre-commit-config.yaml
@@ -27,7 +27,7 @@ repos:
args: [--max-line-length=150, --extend-ignore=E203]
- repo: https://github.com/PyCQA/isort
- rev: 5.10.1
+ rev: 5.12.0
hooks:
- id: isort
args: ["--profile", "black"]
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 77b0141..f53517a 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -17,26 +17,27 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
* Update readme by @giorgiop in https://github.com/sensity-ai/dot/pull/6
* Add more press on README.md by @giorgiop in https://github.com/sensity-ai/dot/pull/7
* [ImgBot] Optimize images by @imgbot in https://github.com/sensity-ai/dot/pull/8
-* Update README to Download Models from Github Release Binaries by @AjinkyaIndulkar in https://github.com/sensity-ai/dot/pull/19
-* Update README + Add Github Templates by @AjinkyaIndulkar in https://github.com/sensity-ai/dot/pull/16
-* Verify camera ID when running dot in camera mode by @AjinkyaIndulkar in https://github.com/sensity-ai/dot/pull/18
-* Add Feature to Use Config Files by @AjinkyaIndulkar in https://github.com/sensity-ai/dot/pull/17
+* Update README to Download Models from Github Release Binaries by @ajndkr in https://github.com/sensity-ai/dot/pull/19
+* Update README + Add Github Templates by @ajndkr in https://github.com/sensity-ai/dot/pull/16
+* Verify camera ID when running dot in camera mode by @ajndkr in https://github.com/sensity-ai/dot/pull/18
+* Add Feature to Use Config Files by @ajndkr in https://github.com/sensity-ai/dot/pull/17
* ⬆️ Bump numpy from 1.21.1 to 1.22.0 by @dependabot in https://github.com/sensity-ai/dot/pull/25
* Update python version to 3.8 by @vassilispapadop in https://github.com/sensity-ai/dot/pull/28
* Requirements changes now trigger CI by @giorgiop in https://github.com/sensity-ai/dot/pull/27
-* Fix python3.8 pip cache location in CI by @AjinkyaIndulkar in https://github.com/sensity-ai/dot/pull/29
-* Fix `--save_folder` CLI Option by @vassilispapadop and @ghassen1302 in https://github.com/sensity-ai/dot/pull/26
-* Add contributors list by @AjinkyaIndulkar in https://github.com/sensity-ai/dot/pull/31
-* Add Google Colab demo notebook by @AjinkyaIndulkar in https://github.com/sensity-ai/dot/pull/33
-* Speed up SimSwap's `reverse2original` by @AjinkyaIndulkar and @ghassen1302 in https://github.com/sensity-ai/dot/pull/20
-* Add `bumpversion` for semantic versioning by @AjinkyaIndulkar in https://github.com/sensity-ai/dot/pull/34
+* Fix python3.8 pip cache location in CI by @ajndkr in https://github.com/sensity-ai/dot/pull/29
+* Fix `--save_folder` CLI Option by @vassilispapadop and @Ghassen-Chaabouni in https://github.com/sensity-ai/dot/pull/26
+* Add contributors list by @ajndkr in https://github.com/sensity-ai/dot/pull/31
+* Add Google Colab demo notebook by @ajndkr in https://github.com/sensity-ai/dot/pull/33
+* Speed up SimSwap's `reverse2original` by @ajndkr and @Ghassen-Chaabouni in https://github.com/sensity-ai/dot/pull/20
+* Add `bumpversion` for semantic versioning by @ajndkr in https://github.com/sensity-ai/dot/pull/34
* Update README with speed metrics by @giorgiop in https://github.com/sensity-ai/dot/pull/37
+* Add a Graphical interface for dot by @Ghassen-Chaabouni in https://github.com/sensity-ai/dot/pull/85
## New Contributors
* @giorgiop made their first contribution in https://github.com/sensity-ai/dot/pull/6
* @ghassen1302 made their first contribution in https://github.com/sensity-ai/dot/pull/6
* @imgbot made their first contribution in https://github.com/sensity-ai/dot/pull/8
-* @AjinkyaIndulkar made their first contribution in https://github.com/sensity-ai/dot/pull/19
+* @ajndkr made their first contribution in https://github.com/sensity-ai/dot/pull/19
* @dependabot made their first contribution in https://github.com/sensity-ai/dot/pull/25
* @vassilispapadop made their first contribution in https://github.com/sensity-ai/dot/pull/28
diff --git a/README.md b/README.md
index e955a7b..e341031 100644
--- a/README.md
+++ b/README.md
@@ -41,9 +41,44 @@ Supported methods:
- lower quality face swap (via OpenCV)
- [FOMM](https://github.com/AliaksandrSiarohin/first-order-model), First Order Motion Model for image animation
-## Installation
+## Running dot
-### Install Pre-requisites
+### Graphical interface
+
+#### GUI Installation
+
+Download and run the dot executable for your OS:
+
+- Windows:
+ - ToDo
+- Ubuntu:
+ - ToDo
+- Mac:
+ - ToDo
+
+#### GUI Usage
+
+Usage example:
+
+1. Specify the source image in the field `source`.
+2. Specify the camera id number in the field `target`. In most cases, `0` is the correct camera id.
+3. Specify the config file in the field `config_file`. Select a default configuration from the dropdown list or use a custom file.
+4. (Optional) Check the field `use_gpu` to use the GPU.
+5. Click on the `RUN` button to start the deepfake.
+
+For more information about each field, click on the menu `Help/Usage`.
+
+Watch the following demo video for better understanding of the interface
+
+
+
+
+
+### Command Line
+
+#### CLI Installation
+
+##### Install Pre-requisites
- Linux
@@ -61,11 +96,11 @@ Supported methods:
no pre-requisites to be installed, skip this step
-### Create Conda Environment
+##### Create Conda Environment
> The instructions assumes that you have Miniconda installed on your machine. If you don't, you can refer to this [link](https://docs.conda.io/projects/conda/en/latest/user-guide/install/index.html) for installation instructions.
-#### With GPU Support
+###### With GPU Support
```bash
conda env create -f envs/environment-gpu.yaml
@@ -79,20 +114,20 @@ Install the `torch` and `torchvision` dependencies based on the CUDA version ins
To check that `torch` and `torchvision` are installed correctly, run the following command: `python -c "import torch; print(torch.cuda.is_available())"`. If the output is `True`, the dependencies are installed with CUDA support.
-#### With CPU Support (slow, not recommended)
+###### With CPU Support (slow, not recommended)
```bash
conda env create -f envs/environment-cpu.yaml
conda activate dot
```
-### Install dot
+##### Install dot
```bash
pip install -e .
```
-### Download Models
+##### Download Models
- Download GitHub Release binaries from [here](https://github.com/sensity-ai/dot/releases/tag/1.0.0) or use the following `wget` commands:
@@ -115,9 +150,7 @@ pip install -e .
rm -rf *.z*
```
-## Usage
-
-### Running dot
+#### CLI Usage
Run `dot --help` to get a full list of available options.
@@ -148,7 +181,7 @@ Run `dot --help` to get a full list of available options.
**Note**: To enable face superresolution, use the flag `--gpen_type gpen_256` or `--gpen_type gpen_512`. To use *dot* on CPU (not recommended), do not pass the `--use_gpu` flag.
-### Controlling dot
+#### Controlling dot with CLI
> **Disclaimer**: We use the `SimSwap` technique for the following demonstration
diff --git a/assets/gui_dot_demo.gif b/assets/gui_dot_demo.gif
new file mode 100644
index 0000000..02ebd0a
Binary files /dev/null and b/assets/gui_dot_demo.gif differ
diff --git a/requirements-dev.txt b/requirements-dev.txt
index e2d801a..c5e2f1b 100644
--- a/requirements-dev.txt
+++ b/requirements-dev.txt
@@ -1,6 +1,6 @@
#
-# This file is autogenerated by pip-compile with python 3.8
-# To update, run:
+# This file is autogenerated by pip-compile with Python 3.8
+# by the following command:
#
# pip-compile --extra=dev --output-file=requirements-dev.txt --strip-extras setup.cfg
#
@@ -8,6 +8,8 @@ absl-py==1.1.0
# via mediapipe
asttokens==2.0.5
# via stack-data
+atomicwrites==1.4.1
+ # via pytest
attrs==21.4.0
# via
# mediapipe
@@ -30,10 +32,20 @@ click==8.0.2
# via
# black
# dot (setup.cfg)
+colorama==0.4.6
+ # via
+ # click
+ # ipython
+ # pytest
+ # tqdm
coverage==6.4.2
# via pytest-cov
+customtkinter==5.2.0
+ # via dot (setup.cfg)
cycler==0.11.0
# via matplotlib
+darkdetect==0.8.0
+ # via customtkinter
decorator==5.1.1
# via
# ipdb
@@ -68,7 +80,7 @@ ipython==8.10.0
# via
# dot (setup.cfg)
# ipdb
-isort==5.9.3
+isort==5.12.0
# via dot (setup.cfg)
jedi==0.18.1
# via ipython
@@ -130,8 +142,6 @@ parso==0.8.3
# via jedi
pathspec==0.9.0
# via black
-pexpect==4.8.0
- # via ipython
pickleshare==0.7.5
# via ipython
pillow==9.3.0
@@ -156,8 +166,6 @@ protobuf==3.20.2
# dot (setup.cfg)
# mediapipe
# onnxruntime
-ptyprocess==0.7.0
- # via pexpect
pure-eval==0.2.2
# via stack-data
py==1.11.0
diff --git a/requirements.txt b/requirements.txt
index 33bc179..fa9f811 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -1,6 +1,6 @@
#
-# This file is autogenerated by pip-compile with python 3.8
-# To update, run:
+# This file is autogenerated by pip-compile with Python 3.8
+# by the following command:
#
# pip-compile setup.cfg
#
@@ -14,10 +14,21 @@ chardet==4.0.0
# via requests
click==8.0.2
# via dot (setup.cfg)
+colorama==0.4.6
+ # via
+ # click
+ # pytest
+ # tqdm
+customtkinter==5.2.0
+ # via dot (setup.cfg)
cycler==0.11.0
# via matplotlib
+darkdetect==0.8.0
+ # via customtkinter
dlib==19.19.0
# via dot (setup.cfg)
+exceptiongroup==1.1.2
+ # via pytest
face-alignment==1.3.3
# via dot (setup.cfg)
flatbuffers==2.0
@@ -28,6 +39,8 @@ idna==2.10
# via requests
imageio==2.19.3
# via scikit-image
+iniconfig==2.0.0
+ # via pytest
kiwisolver==1.4.3
# via matplotlib
kornia==0.6.5
@@ -72,6 +85,7 @@ packaging==21.3
# via
# kornia
# matplotlib
+ # pytest
# scikit-image
pillow==9.3.0
# via
@@ -80,6 +94,8 @@ pillow==9.3.0
# matplotlib
# scikit-image
# torchvision
+pluggy==1.2.0
+ # via pytest
protobuf==3.20.2
# via
# dot (setup.cfg)
@@ -89,6 +105,8 @@ pyparsing==3.0.9
# via
# matplotlib
# packaging
+pytest==7.4.0
+ # via dot (setup.cfg)
python-dateutil==2.8.2
# via matplotlib
pywavelets==1.3.0
@@ -112,6 +130,8 @@ six==1.16.0
# python-dateutil
tifffile==2022.5.4
# via scikit-image
+tomli==2.0.1
+ # via pytest
torch==1.9.0
# via
# dot (setup.cfg)
diff --git a/setup.cfg b/setup.cfg
index 28a3b77..e5286c0 100644
--- a/setup.cfg
+++ b/setup.cfg
@@ -45,6 +45,8 @@ install_requires =
scipy
torch
torchvision
+ customtkinter
+ pytest
[options.extras_require]
dev =
@@ -53,7 +55,7 @@ dev =
flake8
ipdb
ipython
- isort
+ isort==5.12.0
pre-commit
pytest
pytest-cov
@@ -65,3 +67,4 @@ where = src
[options.entry_points]
console_scripts =
dot = dot.__main__:main
+ dot-ui = dot.ui.ui:main
diff --git a/src/dot/faceswap_cv2/swap.py b/src/dot/faceswap_cv2/swap.py
index dd15749..342c1dd 100644
--- a/src/dot/faceswap_cv2/swap.py
+++ b/src/dot/faceswap_cv2/swap.py
@@ -17,7 +17,9 @@
)
# define globals
-CACHED_PREDICTOR_PATH = "./assets/models/shape_predictor_68_face_landmarks.dat"
+CACHED_PREDICTOR_PATH = (
+ "./saved_models/faceswap_cv/shape_predictor_68_face_landmarks.dat"
+)
class Swap:
diff --git a/src/dot/ui/ui.py b/src/dot/ui/ui.py
new file mode 100644
index 0000000..31537e7
--- /dev/null
+++ b/src/dot/ui/ui.py
@@ -0,0 +1,744 @@
+#!/usr/bin/env python3
+"""
+Copyright (c) 2022, Sensity B.V. All rights reserved.
+licensed under the BSD 3-Clause "New" or "Revised" License.
+"""
+
+import os
+import tkinter
+
+import click
+import customtkinter
+import yaml
+
+from dot.__main__ import run
+
+customtkinter.set_appearance_mode("Dark")
+customtkinter.set_default_color_theme("blue")
+
+
+class ToplevelUsageWindow(customtkinter.CTkToplevel):
+ """
+ The class of the usage window
+ """
+
+ def __init__(self, *args, **kwargs):
+ super().__init__(*args, **kwargs)
+
+ self.title("Usage")
+ self.geometry(f"{700}x{550}")
+ self.resizable(False, False)
+ self.attributes("-topmost", True)
+
+ self.textbox = customtkinter.CTkTextbox(
+ master=self, width=700, height=550, corner_radius=0
+ )
+ self.textbox.grid(row=0, column=0, sticky="nsew")
+ self.textbox.insert(
+ "0.0",
+ """
+ swap_type (str): The type of swap to run.\n
+ source (str): The source image or video.\n
+ target (Union[int, str]): The target image, video or camera id.\n
+ model_path (str, optional): The path to the model's weights. Defaults to None.\n
+ parsing_model_path (str, optional): The path to the parsing model. Defaults to None.\n
+ arcface_model_path (str, optional): The path to the arcface model. Defaults to None.\n
+ checkpoints_dir (str, optional): The path to the checkpoints directory. Defaults to None.\n
+ gpen_type (str, optional): The type of gpen model to use. Defaults to None.\n
+ gpen_path (str, optional): The path to the gpen models. Defaults to "./saved_models/gpen".\n
+ crop_size (int, optional): The size to crop the images to. Defaults to 224.\n
+ save_folder (str, optional): The path to the save folder. Defaults to None.\n
+ show_fps (bool, optional): Pass flag to show fps value. Defaults to False.\n
+ use_gpu (bool, optional): Pass flag to use GPU else use CPU. Defaults to False.\n
+ use_video (bool, optional): Pass flag to use video-swap pipeline. Defaults to False.\n
+ use_image (bool, optional): Pass flag to use image-swap pipeline. Defaults to False.\n
+ limit (int, optional): The number of frames to process. Defaults to None.
+ """,
+ )
+ self.textbox.configure(state=tkinter.DISABLED)
+
+
+class ToplevelAboutWindow(customtkinter.CTkToplevel):
+ """
+ The class of the about window
+ """
+
+ def __init__(self, *args, **kwargs):
+ super().__init__(*args, **kwargs)
+
+ self.title("About DOT")
+ self.geometry(f"{700}x{300}")
+ self.resizable(False, False)
+ self.attributes("-topmost", True)
+
+ self.textbox = customtkinter.CTkTextbox(
+ master=self, width=700, height=300, corner_radius=0
+ )
+ self.textbox.grid(row=0, column=0, sticky="nsew")
+
+ self.textbox.insert(
+ "0.0",
+ """
+ dot (aka Deepfake Offensive Toolkit) makes real-time, controllable deepfakes ready for virtual cameras injection. \n
+ dot is created for performing penetration testing against e.g. identity verification and video conferencing systems, \n
+ for the use by security analysts, Red Team members, and biometrics researchers. \n
+ dot is developed for research and demonstration purposes. \n
+ As an end user, you have the responsibility to obey all applicable laws when using this program. \n
+ Authors and contributing developers assume no liability and are not responsible for any misuse \n
+ or damage caused by the use of this program.
+ """,
+ )
+ self.textbox.configure(state=tkinter.DISABLED)
+
+
+class App(customtkinter.CTk):
+ """
+ The main class of the ui interface
+ """
+
+ def __init__(self):
+ super().__init__()
+
+ # configure window
+ self.title("Deepfake Offensive Toolkit")
+ self.geometry(f"{835}x{635}")
+ self.resizable(False, False)
+
+ self.grid_columnconfigure((0, 1), weight=1)
+ self.grid_rowconfigure((0, 1, 2, 3), weight=1)
+
+ # create menubar
+ menubar = tkinter.Menu(self)
+
+ filemenu = tkinter.Menu(menubar, tearoff=0)
+ filemenu.add_command(label="Exit", command=self.quit)
+ menubar.add_cascade(label="File", menu=filemenu)
+
+ helpmenu = tkinter.Menu(menubar, tearoff=0)
+ helpmenu.add_command(label="Usage", command=self.usage_window)
+ helpmenu.add_separator()
+ helpmenu.add_command(label="About DOT", command=self.about_window)
+ menubar.add_cascade(label="Help", menu=helpmenu)
+
+ self.config(menu=menubar)
+
+ self.toplevel_usage_window = None
+ self.toplevel_about_window = None
+
+ # create entry text for source, target and config
+ self.source_target_config_frame = customtkinter.CTkFrame(self)
+ self.source_target_config_frame.grid(
+ row=0, column=0, padx=(20, 20), pady=(20, 0), sticky="nsew"
+ )
+
+ self.source_label = customtkinter.CTkLabel(
+ master=self.source_target_config_frame, text="source"
+ )
+
+ self.source = customtkinter.CTkEntry(
+ master=self.source_target_config_frame,
+ placeholder_text="source",
+ width=85,
+ )
+ self.source_button = customtkinter.CTkButton(
+ master=self.source_target_config_frame,
+ fg_color="gray",
+ text_color="white",
+ text="Open",
+ command=lambda: self.UploadAction(self.source),
+ width=10,
+ )
+
+ self.target = customtkinter.CTkEntry(
+ master=self.source_target_config_frame, placeholder_text="target", width=85
+ )
+ self.target_label = customtkinter.CTkLabel(
+ master=self.source_target_config_frame, text="target"
+ )
+
+ self.config_file_var = customtkinter.StringVar(
+ value="Select"
+ ) # set initial value
+
+ self.config_file_combobox = customtkinter.CTkOptionMenu(
+ master=self.source_target_config_frame,
+ values=["fomm", "faceswap_cv2", "simswap", "simswaphq"],
+ command=self.optionmenu_callback,
+ variable=self.config_file_var,
+ width=85,
+ button_color="#3C3C3C",
+ fg_color="#343638",
+ dynamic_resizing=False,
+ )
+
+ self.config_file = customtkinter.CTkEntry(
+ master=self.source_target_config_frame, placeholder_text="config", width=85
+ )
+ self.config_file_label = customtkinter.CTkLabel(
+ master=self.source_target_config_frame, text="config_file"
+ )
+ self.config_file_button = customtkinter.CTkButton(
+ master=self.source_target_config_frame,
+ fg_color="gray",
+ text_color="white",
+ text="Open",
+ command=lambda: self.upload_action_config_file(
+ self.config_file_combobox, self.config_file_var
+ ),
+ width=10,
+ )
+
+ self.source_label.grid(row=1, column=0, pady=(50, 10), padx=30, sticky="w")
+ self.source.grid(row=1, column=0, pady=(50, 10), padx=(80, 20), sticky="w")
+ self.source_button.grid(
+ row=1,
+ column=0,
+ pady=(50, 10),
+ padx=(175, 20),
+ sticky="w",
+ )
+
+ self.target.grid(row=2, column=0, pady=10, padx=(80, 20), sticky="w")
+ self.target_label.grid(row=2, column=0, pady=10, padx=(35, 20), sticky="w")
+
+ self.config_file_combobox.grid(
+ row=3, column=0, pady=10, padx=(80, 20), sticky="w"
+ )
+ self.config_file_label.grid(row=3, column=0, pady=10, padx=10, sticky="w")
+
+ self.config_file_button.grid(
+ row=3,
+ column=0,
+ pady=10,
+ padx=(175, 20),
+ sticky="w",
+ )
+
+ # create entry text for dot options
+ self.option_entry_frame = customtkinter.CTkFrame(self)
+ self.option_entry_frame.grid(
+ row=1, column=0, columnspan=4, padx=(20, 20), pady=(20, 0), sticky="nsew"
+ )
+
+ self.advanced_options = customtkinter.CTkLabel(
+ master=self.option_entry_frame, text="Advanced"
+ )
+
+ self.model_path_label = customtkinter.CTkLabel(
+ master=self.option_entry_frame, text="model_path"
+ )
+ self.model_path = customtkinter.CTkEntry(
+ master=self.option_entry_frame, placeholder_text="model_path", width=85
+ )
+
+ self.parsing_model_path_label = customtkinter.CTkLabel(
+ master=self.option_entry_frame, text="parsing_model"
+ )
+ self.parsing_model_path = customtkinter.CTkEntry(
+ master=self.option_entry_frame,
+ placeholder_text="parsing_model_path",
+ width=85,
+ )
+
+ self.arcface_model_path_label = customtkinter.CTkLabel(
+ master=self.option_entry_frame, text="arcface_model"
+ )
+ self.arcface_model_path = customtkinter.CTkEntry(
+ master=self.option_entry_frame,
+ placeholder_text="arcface_model_path",
+ width=85,
+ )
+
+ self.checkpoints_dir_label = customtkinter.CTkLabel(
+ master=self.option_entry_frame, text="checkpoints_dir"
+ )
+ self.checkpoints_dir = customtkinter.CTkEntry(
+ master=self.option_entry_frame, placeholder_text="checkpoints_dir", width=85
+ )
+
+ self.gpen_path_label = customtkinter.CTkLabel(
+ master=self.option_entry_frame, text="gpen_path"
+ )
+ self.gpen_path = customtkinter.CTkEntry(
+ master=self.option_entry_frame, placeholder_text="gpen_path", width=85
+ )
+
+ self.save_folder_label = customtkinter.CTkLabel(
+ master=self.option_entry_frame, text="save_folder"
+ )
+ self.save_folder = customtkinter.CTkEntry(
+ master=self.option_entry_frame, placeholder_text="save_folder", width=85
+ )
+
+ self.crop_size_label = customtkinter.CTkLabel(
+ master=self.option_entry_frame, text="crop_size"
+ )
+ self.crop_size = customtkinter.CTkEntry(
+ master=self.option_entry_frame, placeholder_text="crop_size"
+ )
+
+ self.limit_label = customtkinter.CTkLabel(
+ master=self.option_entry_frame, text="limit"
+ )
+ self.limit = customtkinter.CTkEntry(
+ master=self.option_entry_frame, placeholder_text="limit"
+ )
+
+ self.model_path_button = customtkinter.CTkButton(
+ master=self.option_entry_frame,
+ fg_color="gray",
+ text_color="white",
+ text="Open",
+ command=lambda: self.UploadAction(self.model_path),
+ width=10,
+ )
+ self.parsing_model_path_button = customtkinter.CTkButton(
+ master=self.option_entry_frame,
+ fg_color="gray",
+ text_color="white",
+ text="Open",
+ command=lambda: self.UploadAction(self.parsing_model_path),
+ width=10,
+ )
+ self.arcface_model_path_button = customtkinter.CTkButton(
+ master=self.option_entry_frame,
+ fg_color="gray",
+ text_color="white",
+ text="Open",
+ command=lambda: self.UploadAction(self.arcface_model_path),
+ width=10,
+ )
+ self.checkpoints_dir_button = customtkinter.CTkButton(
+ master=self.option_entry_frame,
+ fg_color="gray",
+ text_color="white",
+ text="Open",
+ command=lambda: self.UploadAction(self.checkpoints_dir),
+ width=10,
+ )
+ self.gpen_path_button = customtkinter.CTkButton(
+ master=self.option_entry_frame,
+ fg_color="gray",
+ text_color="white",
+ text="Open",
+ command=lambda: self.UploadAction(self.gpen_path),
+ width=10,
+ )
+ self.save_folder_button = customtkinter.CTkButton(
+ master=self.option_entry_frame,
+ fg_color="gray",
+ text_color="white",
+ text="Open",
+ command=lambda: self.UploadAction(self.save_folder),
+ width=10,
+ )
+
+ self.advanced_options.grid(row=0, column=0, pady=10, padx=(20, 20), sticky="w")
+
+ self.model_path_label.grid(row=1, column=2, pady=10, padx=(40, 20), sticky="w")
+ self.model_path.grid(row=1, column=2, pady=10, padx=(115, 20), sticky="w")
+ self.model_path_button.grid(
+ row=1,
+ column=2,
+ pady=10,
+ padx=(210, 20),
+ sticky="w",
+ )
+
+ self.parsing_model_path_label.grid(
+ row=2, column=2, pady=10, padx=(23, 20), sticky="w"
+ )
+ self.parsing_model_path.grid(
+ row=2, column=2, pady=10, padx=(115, 20), sticky="w"
+ )
+ self.parsing_model_path_button.grid(
+ row=2,
+ column=2,
+ pady=10,
+ padx=(210, 20),
+ sticky="w",
+ )
+
+ self.arcface_model_path_label.grid(
+ row=3, column=2, pady=10, padx=(21, 20), sticky="w"
+ )
+ self.arcface_model_path.grid(
+ row=3, column=2, pady=10, padx=(115, 20), sticky="w"
+ )
+ self.arcface_model_path_button.grid(
+ row=3,
+ column=2,
+ pady=10,
+ padx=(210, 20),
+ sticky="w",
+ )
+
+ self.checkpoints_dir_label.grid(
+ row=4, column=2, pady=10, padx=(16, 20), sticky="w"
+ )
+ self.checkpoints_dir.grid(row=4, column=2, pady=10, padx=(115, 20), sticky="w")
+ self.checkpoints_dir_button.grid(
+ row=4,
+ column=2,
+ pady=10,
+ padx=(210, 20),
+ sticky="w",
+ )
+
+ self.gpen_path_label.grid(row=1, column=3, pady=10, padx=(48, 20), sticky="w")
+ self.gpen_path.grid(row=1, column=3, pady=10, padx=(115, 20), sticky="w")
+ self.gpen_path_button.grid(
+ row=1,
+ column=3,
+ pady=10,
+ padx=(210, 20),
+ sticky="w",
+ )
+
+ self.save_folder_label.grid(row=2, column=3, pady=10, padx=(40, 20), sticky="w")
+ self.save_folder.grid(row=2, column=3, pady=10, padx=(115, 20), sticky="w")
+ self.save_folder_button.grid(
+ row=2,
+ column=3,
+ pady=10,
+ padx=(210, 20),
+ sticky="w",
+ )
+
+ self.crop_size_label.grid(row=3, column=3, pady=10, padx=(50, 20), sticky="w")
+ self.crop_size.grid(row=3, column=3, pady=10, padx=(115, 20), sticky="w")
+
+ self.limit_label.grid(row=4, column=3, pady=10, padx=(80, 20), sticky="w")
+ self.limit.grid(row=4, column=3, pady=10, padx=(115, 20), sticky="w")
+
+ # create radiobutton frame for swap_type
+ self.swap_type_frame = customtkinter.CTkFrame(self)
+ self.swap_type_frame.grid(
+ row=0, column=1, padx=(20, 20), pady=(20, 0), sticky="nsew"
+ )
+ self.swap_type_radio_var = tkinter.StringVar(value=None)
+ self.swap_type_label_radio_group = customtkinter.CTkLabel(
+ master=self.swap_type_frame, text="swap_type"
+ )
+ self.swap_type_label_radio_group.grid(
+ row=0, column=2, columnspan=1, padx=10, pady=10, sticky=""
+ )
+ self.fomm_radio_button = customtkinter.CTkRadioButton(
+ master=self.swap_type_frame,
+ variable=self.swap_type_radio_var,
+ value="fomm",
+ text="fomm",
+ )
+
+ self.fomm_radio_button.grid(row=1, column=2, pady=10, padx=20, sticky="w")
+ self.faceswap_cv2_radio_button = customtkinter.CTkRadioButton(
+ master=self.swap_type_frame,
+ variable=self.swap_type_radio_var,
+ value="faceswap_cv2",
+ text="faceswap_cv2",
+ )
+ self.faceswap_cv2_radio_button.grid(
+ row=2, column=2, pady=10, padx=20, sticky="w"
+ )
+ self.simswap_radio_button = customtkinter.CTkRadioButton(
+ master=self.swap_type_frame,
+ variable=self.swap_type_radio_var,
+ value="simswap",
+ text="simswap",
+ )
+ self.simswap_radio_button.grid(row=3, column=2, pady=10, padx=20, sticky="w")
+
+ # create radiobutton frame for gpen_type
+ self.gpen_type_frame = customtkinter.CTkFrame(self)
+ self.gpen_type_frame.grid(
+ row=0, column=2, padx=(20, 20), pady=(20, 0), sticky="nsew"
+ )
+ self.gpen_type_radio_var = tkinter.StringVar(value="")
+ self.gpen_type_label_radio_group = customtkinter.CTkLabel(
+ master=self.gpen_type_frame, text="gpen_type"
+ )
+ self.gpen_type_label_radio_group.grid(
+ row=0, column=2, columnspan=1, padx=10, pady=10, sticky=""
+ )
+ self.gpen_type_radio_button_1 = customtkinter.CTkRadioButton(
+ master=self.gpen_type_frame,
+ variable=self.gpen_type_radio_var,
+ value="gpen_256",
+ text="gpen_256",
+ )
+
+ self.gpen_type_radio_button_1.grid(
+ row=1, column=2, pady=10, padx=20, sticky="w"
+ )
+ self.gpen_type_radio_button_2 = customtkinter.CTkRadioButton(
+ master=self.gpen_type_frame,
+ variable=self.gpen_type_radio_var,
+ value="gpen_512",
+ text="gpen_512",
+ )
+ self.gpen_type_radio_button_2.grid(
+ row=2, column=2, pady=10, padx=20, sticky="w"
+ )
+
+ # create checkbox and switch frame
+ self.checkbox_slider_frame = customtkinter.CTkFrame(self)
+ self.checkbox_slider_frame.grid(
+ row=0, column=3, padx=(20, 20), pady=(20, 0), sticky="nsew"
+ )
+
+ self.show_fps_checkbox_var = tkinter.IntVar()
+ self.show_fps_checkbox = customtkinter.CTkCheckBox(
+ master=self.checkbox_slider_frame,
+ text="show_fps",
+ variable=self.show_fps_checkbox_var,
+ )
+
+ self.use_gpu_checkbox_var = tkinter.IntVar()
+ self.use_gpu_checkbox = customtkinter.CTkCheckBox(
+ master=self.checkbox_slider_frame,
+ text="use_gpu",
+ variable=self.use_gpu_checkbox_var,
+ )
+
+ self.use_video_checkbox_var = tkinter.IntVar()
+ self.use_video_checkbox = customtkinter.CTkCheckBox(
+ master=self.checkbox_slider_frame,
+ text="use_video",
+ variable=self.use_video_checkbox_var,
+ )
+
+ self.use_image_checkbox_var = tkinter.IntVar()
+ self.use_image_checkbox = customtkinter.CTkCheckBox(
+ master=self.checkbox_slider_frame,
+ text="use_image",
+ variable=self.use_image_checkbox_var,
+ )
+
+ self.head_pose_checkbox_var = tkinter.IntVar()
+ self.head_pose_checkbox = customtkinter.CTkCheckBox(
+ master=self.checkbox_slider_frame,
+ text="head_pose",
+ variable=self.head_pose_checkbox_var,
+ )
+
+ self.show_fps_checkbox.grid(row=1, column=3, pady=(20, 0), padx=20, sticky="w")
+ self.use_gpu_checkbox.grid(row=2, column=3, pady=(20, 0), padx=20, sticky="w")
+ self.use_video_checkbox.grid(row=3, column=3, pady=(20, 0), padx=20, sticky="w")
+ self.use_image_checkbox.grid(row=4, column=3, pady=(20, 0), padx=20, sticky="w")
+ self.head_pose_checkbox.grid(row=5, column=3, pady=(20, 0), padx=20, sticky="w")
+
+ # create run button
+ self.run_button = customtkinter.CTkButton(
+ master=self,
+ fg_color="white",
+ border_width=2,
+ text_color="black",
+ text="RUN",
+ command=self.start_button_event,
+ )
+ self.run_button.grid(
+ row=2, column=1, columnspan=2, padx=(0, 100), pady=(20, 20), sticky="nsew"
+ )
+
+ def usage_window(self):
+ """
+ Open the usage window
+ """
+
+ if (
+ self.toplevel_usage_window is None
+ or not self.toplevel_usage_window.winfo_exists()
+ ):
+ self.toplevel_usage_window = ToplevelUsageWindow(
+ self
+ ) # create window if its None or destroyed
+ self.toplevel_usage_window.focus()
+
+ def about_window(self):
+ """
+ Open the about window
+ """
+
+ if (
+ self.toplevel_about_window is None
+ or not self.toplevel_about_window.winfo_exists()
+ ):
+ self.toplevel_about_window = ToplevelAboutWindow(
+ self
+ ) # create window if its None or destroyed
+ self.toplevel_about_window.focus()
+
+ def UploadAction(self, entry_element: customtkinter.CTkOptionMenu):
+ """
+ Action for the upload buttons to update the value of a CTkEntry
+
+ Args:
+ entry_element (customtkinter.CTkOptionMenu): The CTkEntry element.
+ """
+
+ filename = tkinter.filedialog.askopenfilename()
+ self.modify_entry(entry_element, filename)
+
+ def modify_entry(self, entry_element: customtkinter.CTkEntry, text: str):
+ """
+ Modify the value of the CTkEntry
+
+ Args:
+ entry_element (customtkinter.CTkOptionMenu): The CTkEntry element.
+ text (str): The new text that will be inserted into the CTkEntry
+ """
+
+ entry_element.delete(0, tkinter.END)
+ entry_element.insert(0, text)
+
+ def upload_action_config_file(
+ self,
+ element: customtkinter.CTkOptionMenu,
+ config_file_var: customtkinter.StringVar,
+ ):
+ """
+ Set the configurations for the swap_type using the upload button
+
+ Args:
+ element (customtkinter.CTkOptionMenu): The OptionMenu element.
+ config_file_var (customtkinter.StringVar): OptionMenu variable.
+ """
+
+ entry_list = [
+ "source",
+ "target",
+ "model_path",
+ "parsing_model_path",
+ "arcface_model_path",
+ "checkpoints_dir",
+ "gpen_path",
+ "save_folder",
+ "crop_size",
+ "limit",
+ ]
+ radio_list = ["swap_type"]
+
+ filename = tkinter.filedialog.askopenfilename()
+
+ config = {}
+ if len(filename) > 0:
+ with open(filename) as f:
+ config = yaml.safe_load(f)
+
+ if config["swap_type"] == "simswap":
+ if config.get("swap_type", "0") == "512":
+ config_file_var = "simswaphq"
+ else:
+ config_file_var = "simswap"
+ else:
+ config_file_var = config["swap_type"]
+
+ element.set(config_file_var)
+
+ for key in config.keys():
+ if key in entry_list:
+ self.modify_entry(eval(f"self.{key}"), config[key])
+ elif key in radio_list:
+ self.swap_type_radio_var = tkinter.StringVar(value=config[key])
+ eval(f"self.{config[key]}_radio_button").invoke()
+
+ for entry in entry_list:
+ if entry not in ["source", "target"]:
+ if entry not in config.keys():
+ self.modify_entry(eval(f"self.{entry}"), "")
+
+ def optionmenu_callback(self, choice: str):
+ """
+ Set the configurations for the swap_type using the optionmenu
+
+ Args:
+ choice (str): The type of swap to run.
+ """
+
+ entry_list = [
+ "source",
+ "target",
+ "model_path",
+ "parsing_model_path",
+ "arcface_model_path",
+ "checkpoints_dir",
+ "gpen_path",
+ "save_folder",
+ "crop_size",
+ "limit",
+ ]
+ radio_list = ["swap_type"]
+
+ config_file = f"./configs/{choice}.yaml"
+ if os.path.isfile(config_file):
+ config = {}
+ with open(config_file) as f:
+ config = yaml.safe_load(f)
+
+ for key in config.keys():
+ if key in entry_list:
+ self.modify_entry(eval(f"self.{key}"), config[key])
+ elif key in radio_list:
+ self.swap_type_radio_var = tkinter.StringVar(value=config[key])
+ eval(f"self.{config[key]}_radio_button").invoke()
+
+ for entry in entry_list:
+ if entry not in ["source", "target"]:
+ if entry not in config.keys():
+ self.modify_entry(eval(f"self.{entry}"), "")
+
+ def start_button_event(self):
+ """
+ Start running the deepfake
+ """
+
+ # load config, if provided
+ config = {}
+ if len(self.config_file.get()) > 0:
+ with open(self.config_file.get()) as f:
+ config = yaml.safe_load(f)
+
+ # run dot
+ run(
+ swap_type=config.get("swap_type", self.swap_type_radio_var.get() or None),
+ source=config.get("source", self.source.get() or None),
+ target=config.get("target", self.target.get() or None),
+ model_path=config.get("model_path", self.model_path.get() or None),
+ parsing_model_path=config.get(
+ "parsing_model_path", self.parsing_model_path.get() or None
+ ),
+ arcface_model_path=config.get(
+ "arcface_model_path", self.arcface_model_path.get() or None
+ ),
+ checkpoints_dir=config.get(
+ "checkpoints_dir", self.checkpoints_dir.get() or None
+ ),
+ gpen_type=config.get("gpen_type", self.gpen_type_radio_var.get()),
+ gpen_path=config.get(
+ "gpen_path", self.gpen_path.get() or "./saved_models/gpen"
+ ),
+ crop_size=config.get(
+ "crop_size",
+ (int(self.crop_size.get()) if len(self.crop_size.get()) > 0 else None)
+ or 224,
+ ),
+ head_pose=config.get("head_pose", int(self.head_pose_checkbox.get())),
+ save_folder=config.get("save_folder", self.save_folder.get() or None),
+ show_fps=config.get("show_fps", int(self.show_fps_checkbox.get())),
+ use_gpu=config.get("use_gpu", int(self.use_gpu_checkbox.get())),
+ use_video=config.get("use_video", int(self.use_video_checkbox.get())),
+ use_image=config.get("use_image", int(self.use_image_checkbox.get())),
+ limit=config.get("limit", self.limit.get() or None),
+ )
+
+
+@click.command()
+def main():
+ """Run the dot UI."""
+
+ app = App()
+ app.mainloop()
+
+
+if __name__ == "__main__":
+ main()