diff --git a/README.md b/README.md
index dc3da48..695634b 100644
--- a/README.md
+++ b/README.md
@@ -2,7 +2,7 @@
- 
+ 
@@ -38,11 +38,20 @@
-## What's new in the latest update
+## What's new in the latest updates
+
+### 0.6.0 ALPHA1
+
+- UI reworked
+- You can now load several source images (with reference faces) or set the path to the folder containing faces images
+
+
+
+
### 0.5.1
-- You can now save face models as "safetensors" files (stored in `\models\reactor\faces`) and load them into ReActor, keeping super lightweight face models of the faces you use;
+- You can save face models as "safetensors" files (stored in `\models\reactor\faces`) and load them into ReActor, keeping super lightweight face models of the faces you use;
- "Face Mask Correction" option - if you encounter some pixelation around face contours, this option will be useful;
@@ -59,8 +68,8 @@
- OR only **VS C++ Build Tools** (if you don't need the whole Visual Studio) and select "Desktop Development with C++" under "Workloads -> Desktop & Mobile":
https://visualstudio.microsoft.com/visual-cpp-build-tools/
- OR if you don't want to install VS or VS C++ BT - follow [this steps (sec. VIII)](#insightfacebuild)
-2. In web-ui, go to the "Extensions" tab and use this URL `https://github.com/Gourieff/sd-webui-reactor` in the "Install from URL" tab and click "Install"
-3. Please, wait for several minutes until the installation process will be finished
+2. In web-ui, go to the "Extensions" tab, load "Available" extensions and type "ReActor" in the search field or use this URL `https://github.com/Gourieff/sd-webui-reactor` in the "Install from URL" tab - and click "Install"
+3. Please, wait for several minutes until the installation process will be finished (be patient, don't interrupt the process)
4. Check the last message in your SD-WebUI Console:
* If you see the message "--- PLEASE, RESTART the Server! ---" - so, do it, stop the Server (CTRL+C or CMD+C) and start it again - or just go to the "Installed" tab, click "Apply and restart UI"
* If you see the message "Done!", just reload the UI
@@ -73,7 +82,7 @@
3. Go to (Windows)`automatic\venv\Scripts` or (MacOS/Linux)`automatic/venv/bin`, run Terminal or Console (cmd) for that folder and type `activate`
4. Run `pip install insightface==0.7.3`
5. Run SD.Next, go to the "Extensions" tab and use this URL `https://github.com/Gourieff/sd-webui-reactor` in the "Install from URL" tab and click "Install"
-6. Please, wait for several minutes until the installation process will be finished
+6. Please, wait for several minutes until the installation process will be finished (be patient, don't interrupt the process)
7. Check the last message in your SD.Next Console:
* If you see the message "--- PLEASE, RESTART the Server! ---" - stop the Server (CTRL+C or CMD+C) or just close your console
8. Go to the `automatic\extensions\sd-webui-reactor` directory - if you see there `models\insightface` folder with the file `inswapper_128.onnx`, just move the file to the `automatic\models\insightface` folder
@@ -81,8 +90,8 @@
If you use [Cagliostro Colab UI](https://github.com/Linaqruf/sd-notebook-collection):
-1. In active WebUI, go to the "Extensions" tab and use this URL `https://github.com/Gourieff/sd-webui-reactor` in the "Install from URL" tab and click "Install"
-2. Please, wait for several minutes until the installation process will be finished
+1. In active WebUI, go to the "Extensions" tab, load "Available" extensions and type "ReActor" in the search field or use this URL `https://github.com/Gourieff/sd-webui-reactor` in the "Install from URL" tab - and click "Install"
+2. Please, wait for several minutes until the installation process will be finished (be patient, don't interrupt the process)
3. When you see the message "--- PLEASE, RESTART the Server! ---" (in your Colab Notebook Start UI section "Start Cagliostro Colab UI") - just go to the "Installed" tab and click "Apply and restart UI"
4. Enjoy!
@@ -99,7 +108,7 @@
- Ability to set the **Postprocessing order**
- **100% compatibility** with different **SD WebUIs**: Automatic1111, SD.Next, Cagliostro Colab UI
- **Fast performance** even with CPU, ReActor for SD WebUI is absolutely not picky about how powerful your GPU is
-- **CUDA** acceleration support from the version 0.5.0
+- **CUDA** acceleration support since version 0.5.0
- **[API](/API.md) support**: both SD WebUI built-in and external (via POST/GET requests)
- **ComfyUI [support](https://github.com/Gourieff/comfyui-reactor-node)**
- **Mac M1/M2 [support](https://github.com/Gourieff/sd-webui-reactor/issues/42)**
@@ -191,7 +200,7 @@ Please, check the path where "inswapper_128.onnx" model is stored. It must be in
7. Then one-by-one:
- `pip install insightface==0.7.3`
- `pip install onnx`
- - `pip install onnxruntime-gpu>=1.16.1`
+ - `pip install "onnxruntime-gpu>=1.16.1"`
- `pip install opencv-python`
- `pip install tqdm`
8. Type `deactivate`, you can close your Terminal or Console and start your SD WebUI, ReActor should start OK - if not, welcome to the Issues section.
@@ -226,7 +235,7 @@ and put it to the `stable-diffusion-webui\models\insightface` replacing existing
4. Then:
- `python -m pip install -U pip`
- `pip uninstall -y onnxruntime onnxruntime-gpu onnxruntime-silicon onnxruntime-extensions`
-- `pip install onnxruntime-gpu>=1.16.1`
+- `pip install "onnxruntime-gpu>=1.16.1"`
If it didn't help - it seems that you have another extension reinstalling `onnxruntime` when SD WebUI checks requirements. Please see your extensions list. Some extensions can causes reinstalling of `onnxruntime-gpu` to `onnxruntime<1.16.1` every time SD WebUI runs.
ORT 1.16.0 has a bug https://github.com/microsoft/onnxruntime/issues/17631 - don't install it!
@@ -239,7 +248,7 @@ If it didn't help - it seems that you have another extension reinstalling `onnxr
5. Then:
- `python -m pip install -U pip`
- `pip uninstall protobuf`
-- `pip install protobuf>=3.20.3`
+- `pip install "protobuf>=3.20.3"`
If this method doesn't help - there is some other extension that has a wrong version of protobuf dependence and SD WebUI installs it on a startup requirements check
diff --git a/README_RU.md b/README_RU.md
index e7a07a1..78bb651 100644
--- a/README_RU.md
+++ b/README_RU.md
@@ -2,7 +2,7 @@
- 
+ 
@@ -37,7 +37,14 @@
-## Что нового в последнем обновлении
+## Что нового в последних обновлениях
+
+### 0.6.0 ALPHA1
+
+- UI переработан
+- Появилась возможность загружать несколько исходных изображений с лицами или задавать путь к папке, содержащей такие изображения
+
+
### 0.5.1
@@ -60,8 +67,8 @@
- ИЛИ только **VS C++ Build Tools** (если вам не нужен весь пакет Visual Studio), выберите "Desktop Development with C++" в разделе "Workloads -> Desktop & Mobile":
https://visualstudio.microsoft.com/visual-cpp-build-tools/
- ИЛИ если же вы не хотите устанавливать что-либо из вышеуказанного - выполните [следующие шаги (пункт VIII)](#insightfacebuild)
-2. Внутри SD Web-UI перейдите во вкладку "Extensions" и вставьте ссылку `https://github.com/Gourieff/sd-webui-reactor` в "Install from URL" и нажмите "Install"
-3. Пожалуйста, подождите несколько минут, пока процесс установки полностью не завершится
+2. Внутри SD Web-UI перейдите во вкладку "Extensions", загрузите список доступных расширений (вкладка "Available") и введите "ReActor" в строке поиска или же вставьте ссылку `https://github.com/Gourieff/sd-webui-reactor` в "Install from URL" - и нажмите "Install"
+3. Пожалуйста, подождите несколько минут, пока процесс установки полностью не завершится (наберитесь терпения, не прерывайте процесс)
4. Проверьте последнее сообщение в консоли SD-WebUI:
* Если вы видите "--- PLEASE, RESTART the Server! ---" - остановите Сервер (CTRL+C или CMD+C) и запустите его заново - ИЛИ же перейдите во вкладку "Installed", нажмите "Apply and restart UI"
* Если вы видите "Done!", просто перезагрузите UI, нажав на "Reload UI"
@@ -74,7 +81,7 @@
3. Перейдите в (Windows)`automatic\venv\Scripts` или (MacOS/Linux)`automatic/venv/bin`, запустите Терминал или Консоль (cmd) для данной папки и выполните `activate`
4. Выполните `pip install insightface==0.7.3`
5. Запустите SD.Next, перейдите во вкладку "Extensions", вставьте эту ссылку `https://github.com/Gourieff/sd-webui-reactor` в "Install from URL" и нажмите "Install"
-6. Пожалуйста, подождите несколько минут, пока процесс установки полностью не завершится
+6. Пожалуйста, подождите несколько минут, пока процесс установки полностью не завершится (наберитесь терпения, не прерывайте процесс)
7. Проверьте последнее сообщение в консоли SD.Next:
* Если вы видите "--- PLEASE, RESTART the Server! ---" - остановите Сервер (CTRL+C или CMD+C) или просто закройте консоль
8. Перейдите в директорию `automatic\extensions\sd-webui-reactor` - если вы видите там папку `models\insightface` с файлом `inswapper_128.onnx` внутри, переместите его в папку `automatic\models\insightface`
@@ -82,8 +89,8 @@
Если вы используете [Cagliostro Colab UI](https://github.com/Linaqruf/sd-notebook-collection):
-1. В активном WebUI, перейдите во вкладку "Extensions", вставьте ссылку `https://github.com/Gourieff/sd-webui-reactor` в "Install from URL" и нажмите "Install"
-2. Пожалуйста, подождите некоторое время, пока процесс установки полностью не завершится
+1. В активном WebUI перейдите во вкладку "Extensions", загрузите список доступных расширений (вкладка "Available") и введите "ReActor" в строке поиска или же вставьте ссылку `https://github.com/Gourieff/sd-webui-reactor` в "Install from URL" - и нажмите "Install"
+2. Пожалуйста, подождите некоторое время, пока процесс установки полностью не завершится (наберитесь терпения, не прерывайте процесс)
3. Когда вы увидите сообщение "--- PLEASE, RESTART the Server! ---" (в секции "Start UI" вашего ноутбука "Start Cagliostro Colab UI") - перейдите во вкладку "Installed" и нажмите "Apply and restart UI"
4. Готово!
@@ -198,7 +205,7 @@ Inpainting также работает, но замена лица происх
7. Далее:
- `pip install insightface==0.7.3`
- `pip install onnx`
- - `pip install onnxruntime-gpu>=1.16.1`
+ - `pip install "onnxruntime-gpu>=1.16.1"`
- `pip install opencv-python`
- `pip install tqdm`
8. Выполните `deactivate`, закройте Терминал или Консоль и запустите SD WebUI, ReActor должен запуститься без к-л проблем - если же нет, добро пожаловать в раздел "Issues".
@@ -233,7 +240,7 @@ Inpainting также работает, но замена лица происх
4. Затем:
- `python -m pip install -U pip`
- `pip uninstall -y onnxruntime onnxruntime-gpu onnxruntime-silicon onnxruntime-extensions`
-- `pip install onnxruntime-gpu>=1.16.1`
+- `pip install "onnxruntime-gpu>=1.16.1"`
Если это не помогло - значит какое-то другое расширение переустанавливает `onnxruntime` всякий раз, когда SD WebUI проверяет требования пакетов. Внимательно посмотрите список активных расширений. Некоторые расширения могут вызывать переустановку `onnxruntime-gpu` на версию `onnxruntime<1.16.1` при каждом запуске SD WebUI.
ORT 1.16.0 выкатили с ошибкой https://github.com/microsoft/onnxruntime/issues/17631 - не устанавливайте её!
@@ -246,7 +253,7 @@ Inpainting также работает, но замена лица происх
5. Затем:
- `python -m pip install -U pip`
- `pip uninstall protobuf`
-- `pip install protobuf>=3.20.3`
+- `pip install "protobuf>=3.20.3"`
Если это не помгло - значит, есть к-л другое расширение, которое использует неподходящую версию пакета protobuf, и SD WebUI устанавливает эту версию при каждом запуске.
diff --git a/example/api_example.py b/example/api_example.py
index f3485c1..bcd2504 100644
--- a/example/api_example.py
+++ b/example/api_example.py
@@ -44,8 +44,9 @@ args=[
False, #19 Target Image Hash Check, False - by default
"CUDA", #20 CPU or CUDA (if you have it), CPU - by default
True, #21 Face Mask Correction
- 1, #22 Select Source, 0 - Image, 1 - Face Model
- "elena.safetensors", #23 Filename of the face model (from "models/reactor/faces"), e.g. elena.safetensors
+ 1, #22 Select Source, 0 - Image, 1 - Face Model, 2 - Source Folder
+ "elena.safetensors", #23 Filename of the face model (from "models/reactor/faces"), e.g. elena.safetensors, don't forger to set #22 to 1
+ "C:\PATH_TO_FACES_IMAGES", #24 The path to the folder containing source faces images, don't forger to set #22 to 2
]
# The args for ReActor can be found by
diff --git a/modules/reactor_mask.py b/reactor_modules/reactor_mask.py
similarity index 100%
rename from modules/reactor_mask.py
rename to reactor_modules/reactor_mask.py
diff --git a/reactor_ui/__init__.py b/reactor_ui/__init__.py
new file mode 100644
index 0000000..00711fd
--- /dev/null
+++ b/reactor_ui/__init__.py
@@ -0,0 +1,4 @@
+import reactor_ui.reactor_upscale_ui as ui_upscale
+import reactor_ui.reactor_tools_ui as ui_tools
+import reactor_ui.reactor_settings_ui as ui_settings
+import reactor_ui.reactor_main_ui as ui_main
diff --git a/reactor_ui/reactor_main_ui.py b/reactor_ui/reactor_main_ui.py
new file mode 100644
index 0000000..5ed4212
--- /dev/null
+++ b/reactor_ui/reactor_main_ui.py
@@ -0,0 +1,182 @@
+import gradio as gr
+from scripts.reactor_helpers import (
+ get_model_names,
+ get_facemodels
+)
+from scripts.reactor_swapper import (
+ clear_faces_list,
+)
+from modules import shared
+
+SAVE_ORIGINAL: bool = False
+
+def update_fm_list(selected: str):
+ return gr.Dropdown.update(
+ value=selected, choices=get_model_names(get_facemodels)
+ )
+
+# TAB MAIN
+def show(is_img2img: bool, show_br: bool = True, **msgs):
+
+ def on_select_source(selected: bool, evt: gr.SelectData):
+ global SAVE_ORIGINAL
+ if evt.index == 2:
+ if SAVE_ORIGINAL != selected:
+ SAVE_ORIGINAL = selected
+ return {
+ control_col_1: gr.Column.update(visible=False),
+ control_col_2: gr.Column.update(visible=False),
+ control_col_3: gr.Column.update(visible=True),
+ save_original: gr.Checkbox.update(value=False,visible=False),
+ imgs_hash_clear: gr.Button.update(visible=True)
+ }
+ if evt.index == 0:
+ return {
+ control_col_1: gr.Column.update(visible=True),
+ control_col_2: gr.Column.update(visible=False),
+ control_col_3: gr.Column.update(visible=False),
+ save_original: gr.Checkbox.update(value=SAVE_ORIGINAL,visible=show_br),
+ imgs_hash_clear: gr.Button.update(visible=False)
+ }
+ if evt.index == 1:
+ return {
+ control_col_1: gr.Column.update(visible=False),
+ control_col_2: gr.Column.update(visible=True),
+ control_col_3: gr.Column.update(visible=False),
+ save_original: gr.Checkbox.update(value=SAVE_ORIGINAL,visible=show_br),
+ imgs_hash_clear: gr.Button.update(visible=False)
+ }
+
+ progressbar_area = gr.Markdown("")
+ with gr.Tab("Main"):
+ with gr.Column():
+ with gr.Row():
+ select_source = gr.Radio(
+ ["Image(s)","Face Model","Folder"],
+ value="Image(s)",
+ label="Select Source",
+ type="index",
+ scale=1,
+ )
+ with gr.Column(visible=False) as control_col_2:
+ with gr.Row():
+ face_models = get_model_names(get_facemodels)
+ face_model = gr.Dropdown(
+ choices=face_models,
+ label="Choose Face Model",
+ value="None",
+ scale=1,
+ )
+ fm_update = gr.Button(
+ value="🔄",
+ variant="tool",
+ )
+ fm_update.click(
+ update_fm_list,
+ inputs=[face_model],
+ outputs=[face_model],
+ )
+ imgs_hash_clear = gr.Button(
+ value="Clear Source Images Hash",
+ scale=1,
+ visible=False,
+ )
+ imgs_hash_clear.click(clear_faces_list,None,[progressbar_area])
+ gr.Markdown("
", visible=show_br)
+ with gr.Column(visible=True) as control_col_1:
+ gr.Markdown("🔽🔽🔽 Single Image has priority when both Areas in use 🔽🔽🔽")
+ with gr.Row():
+ img = gr.Image(
+ type="pil",
+ label="Single Source Image",
+ )
+ imgs = gr.Files(
+ label=f"Multiple Source Images{msgs['extra_multiple_source']}",
+ file_types=["image"],
+ )
+ with gr.Column(visible=False) as control_col_3:
+ gr.Markdown("Clear Hash if you see the previous face was swapped instead of the new one")
+ source_folder = gr.Textbox(
+ value="",
+ placeholder="Paste here the path to the folder containing source faces images",
+ label=f"Source Folder{msgs['extra_multiple_source']}",
+ )
+ setattr(face_model, "do_not_save_to_config", True)
+ if is_img2img:
+ save_original = gr.Checkbox(
+ False,
+ label="Save Original (Swap in generated only)",
+ info="Save the original image(s) made before swapping (it always saves Original when you use Multiple Images or Folder)"
+ )
+ else:
+ save_original = gr.Checkbox(
+ False,
+ label="Save Original",
+ info="Save the original image(s) made before swapping (it always saves Original when you use Multiple Images or Folder)",
+ visible=show_br
+ )
+ # imgs.upload(on_files_upload_uncheck_so,[save_original],[save_original],show_progress=False)
+ # imgs.clear(on_files_clear,None,[save_original],show_progress=False)
+ imgs.clear(clear_faces_list,None,None,show_progress=False)
+ mask_face = gr.Checkbox(
+ False,
+ label="Face Mask Correction",
+ info="Apply this option if you see some pixelation around face contours"
+ )
+ gr.Markdown("
", visible=show_br)
+ gr.Markdown("Source Image (above):")
+ with gr.Row():
+ source_faces_index = gr.Textbox(
+ value="0",
+ placeholder="Which face(s) to use as Source (comma separated)",
+ label="Comma separated face number(s); Example: 0,2,1",
+ )
+ gender_source = gr.Radio(
+ ["No", "Female Only", "Male Only"],
+ value="No",
+ label="Gender Detection (Source)",
+ type="index",
+ )
+ gr.Markdown("
", visible=show_br)
+ gr.Markdown("Target Image (result):")
+ with gr.Row():
+ faces_index = gr.Textbox(
+ value="0",
+ placeholder="Which face(s) to Swap into Target (comma separated)",
+ label="Comma separated face number(s); Example: 1,0,2",
+ )
+ gender_target = gr.Radio(
+ ["No", "Female Only", "Male Only"],
+ value="No",
+ label="Gender Detection (Target)",
+ type="index",
+ )
+ gr.Markdown("
", visible=show_br)
+ with gr.Row():
+ face_restorer_name = gr.Radio(
+ label="Restore Face",
+ choices=["None"] + [x.name() for x in shared.face_restorers],
+ value=shared.face_restorers[0].name(),
+ type="value",
+ )
+ with gr.Column():
+ face_restorer_visibility = gr.Slider(
+ 0, 1, 1, step=0.1, label="Restore Face Visibility"
+ )
+ codeformer_weight = gr.Slider(
+ 0, 1, 0.5, step=0.1, label="CodeFormer Weight", info="0 = maximum effect, 1 = minimum effect"
+ )
+ gr.Markdown("
", visible=show_br)
+ swap_in_source = gr.Checkbox(
+ False,
+ label="Swap in source image",
+ visible=is_img2img,
+ )
+ swap_in_generated = gr.Checkbox(
+ True,
+ label="Swap in generated image",
+ visible=is_img2img,
+ )
+ select_source.select(on_select_source,[save_original],[control_col_1,control_col_2,control_col_3,save_original,imgs_hash_clear],show_progress=False)
+
+ return img, imgs, select_source, face_model, source_folder, save_original, mask_face, source_faces_index, gender_source, faces_index, gender_target, face_restorer_name, face_restorer_visibility, codeformer_weight, swap_in_source, swap_in_generated
diff --git a/reactor_ui/reactor_settings_ui.py b/reactor_ui/reactor_settings_ui.py
new file mode 100644
index 0000000..a5447a5
--- /dev/null
+++ b/reactor_ui/reactor_settings_ui.py
@@ -0,0 +1,77 @@
+import gradio as gr
+from scripts.reactor_logger import logger
+from scripts.reactor_helpers import get_models, set_Device
+from scripts.reactor_globals import DEVICE, DEVICE_LIST
+try:
+ import torch.cuda as cuda
+ EP_is_visible = True if cuda.is_available() else False
+except:
+ EP_is_visible = False
+
+def update_models_list(selected: str):
+ return gr.Dropdown.update(
+ value=selected, choices=get_models()
+ )
+
+def show(hash_check_block: bool = True):
+ # TAB SETTINGS
+ with gr.Tab("Settings"):
+ models = get_models()
+ with gr.Row(visible=EP_is_visible):
+ device = gr.Radio(
+ label="Execution Provider",
+ choices=DEVICE_LIST,
+ value=DEVICE,
+ type="value",
+ info="If you already run 'Generate' - RESTART is required to apply. Click 'Save', (A1111) Extensions Tab -> 'Apply and restart UI' or (SD.Next) close the Server and start it again",
+ scale=2,
+ )
+ save_device_btn = gr.Button("Save", scale=0)
+ save = gr.Markdown("", visible=EP_is_visible)
+ setattr(device, "do_not_save_to_config", True)
+ save_device_btn.click(
+ set_Device,
+ inputs=[device],
+ outputs=[save],
+ )
+ with gr.Row():
+ if len(models) == 0:
+ logger.warning(
+ "You should at least have one model in models directory, please read the doc here: https://github.com/Gourieff/sd-webui-reactor/"
+ )
+ model = gr.Dropdown(
+ choices=models,
+ label="Model not found, please download one and refresh the list"
+ )
+ else:
+ model = gr.Dropdown(
+ choices=models, label="Model", value=models[0]
+ )
+ models_update = gr.Button(
+ value="🔄",
+ variant="tool",
+ )
+ models_update.click(
+ update_models_list,
+ inputs=[model],
+ outputs=[model],
+ )
+ console_logging_level = gr.Radio(
+ ["No log", "Minimum", "Default"],
+ value="Minimum",
+ label="Console Log Level",
+ type="index"
+ )
+ gr.Markdown("
", visible=hash_check_block)
+ with gr.Row(visible=hash_check_block):
+ source_hash_check = gr.Checkbox(
+ True,
+ label="Source Image Hash Check",
+ info="Recommended to keep it ON. Processing is faster when Source Image is the same."
+ )
+ target_hash_check = gr.Checkbox(
+ False,
+ label="Target Image Hash Check",
+ info="Affects if you use Extras tab or img2img with only 'Swap in source image' on."
+ )
+ return model, device, console_logging_level, source_hash_check, target_hash_check
\ No newline at end of file
diff --git a/reactor_ui/reactor_tools_ui.py b/reactor_ui/reactor_tools_ui.py
new file mode 100644
index 0000000..0abcb0e
--- /dev/null
+++ b/reactor_ui/reactor_tools_ui.py
@@ -0,0 +1,25 @@
+import gradio as gr
+from scripts.reactor_swapper import build_face_model
+
+# TAB TOOLS
+def show():
+ with gr.Tab("Tools"):
+ with gr.Tab("Face Models"):
+ gr.Markdown("Load an image containing one person, name it and click 'Build and Save'")
+ img_fm = gr.Image(
+ type="pil",
+ label="Load Image to build Face Model",
+ )
+ with gr.Row(equal_height=True):
+ fm_name = gr.Textbox(
+ value="",
+ placeholder="Please type any name (e.g. Elena)",
+ label="Face Model Name",
+ )
+ save_fm_btn = gr.Button("Build and Save")
+ save_fm = gr.Markdown("You can find saved models in 'models/reactor/faces'")
+ save_fm_btn.click(
+ build_face_model,
+ inputs=[img_fm, fm_name],
+ outputs=[save_fm],
+ )
diff --git a/reactor_ui/reactor_upscale_ui.py b/reactor_ui/reactor_upscale_ui.py
new file mode 100644
index 0000000..71216a4
--- /dev/null
+++ b/reactor_ui/reactor_upscale_ui.py
@@ -0,0 +1,39 @@
+import gradio as gr
+from modules import shared
+
+def update_upscalers_list(selected: str):
+ return gr.Dropdown.update(
+ value=selected, choices=[upscaler.name for upscaler in shared.sd_upscalers]
+ )
+
+# TAB UPSCALE
+def show(show_br: bool = True):
+ with gr.Tab("Upscale"):
+ restore_first = gr.Checkbox(
+ True,
+ label="1. Restore Face -> 2. Upscale (-Uncheck- if you want vice versa)",
+ info="Postprocessing Order"
+ )
+ with gr.Row():
+ upscaler_name = gr.Dropdown(
+ choices=[upscaler.name for upscaler in shared.sd_upscalers],
+ label="Upscaler",
+ value="None",
+ info="Won't scale if you choose -Swap in Source- via img2img, only 1x-postprocessing will affect (texturing, denoising, restyling etc.)"
+ )
+ upscalers_update = gr.Button(
+ value="🔄",
+ variant="tool",
+ )
+ upscalers_update.click(
+ update_upscalers_list,
+ inputs=[upscaler_name],
+ outputs=[upscaler_name],
+ )
+ gr.Markdown("
", visible=show_br)
+ with gr.Row():
+ upscaler_scale = gr.Slider(1, 8, 1, step=0.1, label="Scale by")
+ upscaler_visibility = gr.Slider(
+ 0, 1, 1, step=0.1, label="Upscaler Visibility (if scale = 1)"
+ )
+ return restore_first, upscaler_name, upscaler_scale, upscaler_visibility
\ No newline at end of file
diff --git a/scripts/reactor_api.py b/scripts/reactor_api.py
index b5df7fa..2fbd5b5 100644
--- a/scripts/reactor_api.py
+++ b/scripts/reactor_api.py
@@ -73,8 +73,9 @@ def reactor_api(_: gr.Blocks, app: FastAPI):
result_file_path: str = Body("",title="(if 'save_to_file = 1') Result file path"),
device: str = Body("CPU",title="CPU or CUDA (if you have it)"),
mask_face: int = Body(0,title="Face Mask Correction, 1 - True, 0 - False"),
- select_source: int = Body(0,title="Select Source, 0 - Image, 1 - Face Model"),
- face_model: str = Body("None",title="Filename of the face model (from 'models/reactor/faces'), e.g. elena.safetensors")
+ select_source: int = Body(0,title="Select Source, 0 - Image, 1 - Face Model, 2 - Source Folder"),
+ face_model: str = Body("None",title="Filename of the face model (from 'models/reactor/faces'), e.g. elena.safetensors"),
+ source_folder: str = Body("",title="The path to the folder containing source faces images")
):
s_image = api.decode_base64_to_image(source_image)
t_image = api.decode_base64_to_image(target_image)
@@ -88,7 +89,7 @@ def reactor_api(_: gr.Blocks, app: FastAPI):
use_model = get_full_model(model)
if use_model is None:
Exception("Model not found")
- result = swap_face(s_image, t_image, use_model, sf_index, f_index, up_options, gender_s, gender_t, True, True, device, mask_face, select_source, face_model)
+ result = swap_face(s_image, t_image, use_model, sf_index, f_index, up_options, gender_s, gender_t, True, True, device, mask_face, select_source, face_model, source_folder, None)
if save_to_file == 1:
if result_file_path == "":
result_file_path = default_file_path()
diff --git a/scripts/reactor_faceswap.py b/scripts/reactor_faceswap.py
index 623e275..8812a4f 100644
--- a/scripts/reactor_faceswap.py
+++ b/scripts/reactor_faceswap.py
@@ -1,11 +1,6 @@
import os, glob
import gradio as gr
from PIL import Image
-try:
- import torch.cuda as cuda
- EP_is_visible = True if cuda.is_available() else False
-except:
- EP_is_visible = False
from typing import List
@@ -19,43 +14,22 @@ from modules.processing import (
)
from modules.face_restoration import FaceRestoration
from modules.images import save_image
-try:
- from modules.paths_internal import models_path
-except:
- try:
- from modules.paths import models_path
- except:
- model_path = os.path.abspath("models")
+from reactor_ui import ui_main, ui_upscale, ui_tools, ui_settings
from scripts.reactor_logger import logger
from scripts.reactor_swapper import (
EnhancementOptions,
swap_face,
check_process_halt,
- reset_messaged,
- build_face_model
+ reset_messaged,
)
from scripts.reactor_version import version_flag, app_title
from scripts.console_log_patch import apply_logging_patch
-from scripts.reactor_helpers import make_grid, get_image_path, set_Device, get_model_names, get_facemodels
-from scripts.reactor_globals import DEVICE, DEVICE_LIST
-
-
-MODELS_PATH = None
-
-def get_models():
- global MODELS_PATH
- models_path_init = os.path.join(models_path, "insightface/*")
- models = glob.glob(models_path_init)
- models = [x for x in models if x.endswith(".onnx") or x.endswith(".pth")]
- models_names = []
- for model in models:
- model_path = os.path.split(model)
- if MODELS_PATH is None:
- MODELS_PATH = model_path[0]
- model_name = model_path[1]
- models_names.append(model_name)
- return models_names
+from scripts.reactor_helpers import (
+ make_grid,
+ set_Device,
+)
+from scripts.reactor_globals import SWAPPER_MODELS_PATH #, DEVICE, DEVICE_LIST
class FaceSwapScript(scripts.Script):
@@ -68,237 +42,33 @@ class FaceSwapScript(scripts.Script):
def ui(self, is_img2img):
with gr.Accordion(f"{app_title}", open=False):
- def update_fm_list(selected: str):
- return gr.Dropdown.update(
- value=selected, choices=get_model_names(get_facemodels)
- )
- def update_upscalers_list(selected: str):
- return gr.Dropdown.update(
- value=selected, choices=[upscaler.name for upscaler in shared.sd_upscalers]
- )
- def update_models_list(selected: str):
- return gr.Dropdown.update(
- value=selected, choices=get_models()
- )
+ # def on_files_upload_uncheck_so(selected: bool):
+ # global SAVE_ORIGINAL
+ # SAVE_ORIGINAL = selected
+ # return gr.Checkbox.update(value=False,visible=False)
+ # def on_files_clear():
+ # clear_faces_list()
+ # return gr.Checkbox.update(value=SAVE_ORIGINAL,visible=True)
+ enable = gr.Checkbox(False, label="Enable", info=f"The Fast and Simple FaceSwap Extension - {version_flag}")
+ gr.Markdown("
")
+
# TAB MAIN
- with gr.Tab("Main"):
- with gr.Column():
- img = gr.Image(
- type="pil",
- label="Source Image",
- )
- # face_model = gr.File(
- # file_types=[".safetensors"],
- # label="Face Model",
- # show_label=True,
- # )
- enable = gr.Checkbox(False, label="Enable", info=f"The Fast and Simple FaceSwap Extension - {version_flag}")
- gr.Markdown("
")
- with gr.Row():
- select_source = gr.Radio(
- ["Image","Face Model"],
- value="Image",
- label="Select Source",
- type="index",
- scale=1,
- )
- face_models = get_model_names(get_facemodels)
- face_model = gr.Dropdown(
- choices=face_models,
- label="Choose Face Model",
- value="None",
- scale=2,
- )
- fm_update = gr.Button(
- value="🔄",
- variant="tool",
- )
- fm_update.click(
- update_fm_list,
- inputs=[face_model],
- outputs=[face_model],
- )
- setattr(face_model, "do_not_save_to_config", True)
- save_original = gr.Checkbox(
- False,
- label="Save Original",
- info="Save the original image(s) made before swapping; If you use \"img2img\" - this option will affect with \"Swap in generated\" only"
- )
- mask_face = gr.Checkbox(
- False,
- label="Face Mask Correction",
- info="Apply this option if you see some pixelation around face contours"
- )
- gr.Markdown("
")
- gr.Markdown("Source Image (above):")
- with gr.Row():
- source_faces_index = gr.Textbox(
- value="0",
- placeholder="Which face(s) to use as Source (comma separated)",
- label="Comma separated face number(s); Example: 0,2,1",
- )
- gender_source = gr.Radio(
- ["No", "Female Only", "Male Only"],
- value="No",
- label="Gender Detection (Source)",
- type="index",
- )
- gr.Markdown("
")
- gr.Markdown("Target Image (result):")
- with gr.Row():
- faces_index = gr.Textbox(
- value="0",
- placeholder="Which face(s) to Swap into Target (comma separated)",
- label="Comma separated face number(s); Example: 1,0,2",
- )
- gender_target = gr.Radio(
- ["No", "Female Only", "Male Only"],
- value="No",
- label="Gender Detection (Target)",
- type="index",
- )
- gr.Markdown("
")
- with gr.Row():
- face_restorer_name = gr.Radio(
- label="Restore Face",
- choices=["None"] + [x.name() for x in shared.face_restorers],
- value=shared.face_restorers[0].name(),
- type="value",
- )
- with gr.Column():
- face_restorer_visibility = gr.Slider(
- 0, 1, 1, step=0.1, label="Restore Face Visibility"
- )
- codeformer_weight = gr.Slider(
- 0, 1, 0.5, step=0.1, label="CodeFormer Weight", info="0 = maximum effect, 1 = minimum effect"
- )
- gr.Markdown("
")
- swap_in_source = gr.Checkbox(
- False,
- label="Swap in source image",
- visible=is_img2img,
- )
- swap_in_generated = gr.Checkbox(
- True,
- label="Swap in generated image",
- visible=is_img2img,
- )
+ msgs: dict = {
+ "extra_multiple_source": "",
+ }
+ img, imgs, select_source, face_model, source_folder, save_original, mask_face, source_faces_index, gender_source, faces_index, gender_target, face_restorer_name, face_restorer_visibility, codeformer_weight, swap_in_source, swap_in_generated = ui_main.show(is_img2img=is_img2img, **msgs)
# TAB UPSCALE
- with gr.Tab("Upscale"):
- restore_first = gr.Checkbox(
- True,
- label="1. Restore Face -> 2. Upscale (-Uncheck- if you want vice versa)",
- info="Postprocessing Order"
- )
- with gr.Row():
- upscaler_name = gr.Dropdown(
- choices=[upscaler.name for upscaler in shared.sd_upscalers],
- label="Upscaler",
- value="None",
- info="Won't scale if you choose -Swap in Source- via img2img, only 1x-postprocessing will affect (texturing, denoising, restyling etc.)"
- )
- upscalers_update = gr.Button(
- value="🔄",
- variant="tool",
- )
- upscalers_update.click(
- update_upscalers_list,
- inputs=[upscaler_name],
- outputs=[upscaler_name],
- )
- gr.Markdown("
")
- with gr.Row():
- upscaler_scale = gr.Slider(1, 8, 1, step=0.1, label="Scale by")
- upscaler_visibility = gr.Slider(
- 0, 1, 1, step=0.1, label="Upscaler Visibility (if scale = 1)"
- )
-
+ restore_first, upscaler_name, upscaler_scale, upscaler_visibility = ui_upscale.show()
+
# TAB TOOLS
- with gr.Tab("Tools 🆕"):
- with gr.Tab("Face Models"):
- gr.Markdown("Load an image containing one person, name it and click 'Build and Save'")
- img_fm = gr.Image(
- type="pil",
- label="Load Image to build Face Model",
- )
- with gr.Row(equal_height=True):
- fm_name = gr.Textbox(
- value="",
- placeholder="Please type any name (e.g. Elena)",
- label="Face Model Name",
- )
- save_fm_btn = gr.Button("Build and Save")
- save_fm = gr.Markdown("You can find saved models in 'models/reactor/faces'")
- save_fm_btn.click(
- build_face_model,
- inputs=[img_fm, fm_name],
- outputs=[save_fm],
- )
+ ui_tools.show()
# TAB SETTINGS
- with gr.Tab("Settings"):
- models = get_models()
- with gr.Row(visible=EP_is_visible):
- device = gr.Radio(
- label="Execution Provider",
- choices=DEVICE_LIST,
- value=DEVICE,
- type="value",
- info="If you already run 'Generate' - RESTART is required to apply. Click 'Save', (A1111) Extensions Tab -> 'Apply and restart UI' or (SD.Next) close the Server and start it again",
- scale=2,
- )
- save_device_btn = gr.Button("Save", scale=0)
- save = gr.Markdown("", visible=EP_is_visible)
- setattr(device, "do_not_save_to_config", True)
- save_device_btn.click(
- set_Device,
- inputs=[device],
- outputs=[save],
- )
- with gr.Row():
- if len(models) == 0:
- logger.warning(
- "You should at least have one model in models directory, please read the doc here: https://github.com/Gourieff/sd-webui-reactor/"
- )
- model = gr.Dropdown(
- choices=models,
- label="Model not found, please download one and refresh the list"
- )
- else:
- model = gr.Dropdown(
- choices=models, label="Model", value=models[0]
- )
- models_update = gr.Button(
- value="🔄",
- variant="tool",
- )
- models_update.click(
- update_models_list,
- inputs=[model],
- outputs=[model],
- )
- console_logging_level = gr.Radio(
- ["No log", "Minimum", "Default"],
- value="Minimum",
- label="Console Log Level",
- type="index"
- )
- gr.Markdown("
")
- with gr.Row():
- source_hash_check = gr.Checkbox(
- True,
- label="Source Image Hash Check",
- info="Recommended to keep it ON. Processing is faster when Source Image is the same."
- )
- target_hash_check = gr.Checkbox(
- False,
- label="Target Image Hash Check",
- info="Affects if you use Extras tab or img2img with only 'Swap in source image' on."
- )
+ model, device, console_logging_level, source_hash_check, target_hash_check = ui_settings.show()
- gr.Markdown("by Eugene Gourieff")
+ gr.Markdown("by Eugene Gourieff")
return [
img,
@@ -325,6 +95,8 @@ class FaceSwapScript(scripts.Script):
mask_face,
select_source,
face_model,
+ source_folder,
+ imgs,
]
@@ -381,6 +153,8 @@ class FaceSwapScript(scripts.Script):
mask_face,
select_source,
face_model,
+ source_folder,
+ imgs,
):
self.enable = enable
if self.enable:
@@ -391,7 +165,7 @@ class FaceSwapScript(scripts.Script):
if check_process_halt():
return
- global MODELS_PATH
+ global SWAPPER_MODELS_PATH
self.source = img
self.face_restorer_name = face_restorer_name
self.upscaler_scale = upscaler_scale
@@ -401,7 +175,7 @@ class FaceSwapScript(scripts.Script):
self.upscaler_name = upscaler_name
self.swap_in_source = swap_in_source
self.swap_in_generated = swap_in_generated
- self.model = os.path.join(MODELS_PATH,model)
+ self.model = os.path.join(SWAPPER_MODELS_PATH,model)
self.console_logging_level = console_logging_level
self.gender_source = gender_source
self.gender_target = gender_target
@@ -413,6 +187,8 @@ class FaceSwapScript(scripts.Script):
self.mask_face = mask_face
self.select_source = select_source
self.face_model = face_model
+ self.source_folder = source_folder
+ self.source_imgs = imgs
if self.gender_source is None or self.gender_source == "No":
self.gender_source = 0
if self.gender_target is None or self.gender_target == "No":
@@ -439,7 +215,7 @@ class FaceSwapScript(scripts.Script):
logger.debug("*** Set Device")
set_Device(self.device)
- if (self.source is not None and self.select_source == 0) or ((self.face_model is not None and self.face_model != "None") and self.select_source == 1):
+ if ((self.source is not None or self.source_imgs is not None) and self.select_source == 0) or ((self.face_model is not None and self.face_model != "None") and self.select_source == 1) or ((self.source_folder is not None and self.source_folder != "") and self.select_source == 2):
logger.debug("*** Log patch")
apply_logging_patch(console_logging_level)
if isinstance(p, StableDiffusionProcessingImg2Img) and self.swap_in_source:
@@ -463,6 +239,8 @@ class FaceSwapScript(scripts.Script):
mask_face=self.mask_face,
select_source=self.select_source,
face_model = self.face_model,
+ source_folder = None,
+ source_imgs = None,
)
p.init_images[i] = result
# result_path = get_image_path(p.init_images[i], p.outpath_samples, "", p.all_seeds[i], p.all_prompts[i], "txt", p=p, suffix="-swapped")
@@ -486,7 +264,7 @@ class FaceSwapScript(scripts.Script):
if check_process_halt():
return
- if self.save_original:
+ if self.save_original or ((self.select_source == 2 and self.source_folder is not None and self.source_folder != "") or (self.select_source == 0 and self.source_imgs is not None and self.source is None)):
postprocess_run: bool = True
@@ -497,8 +275,13 @@ class FaceSwapScript(scripts.Script):
# result_info: List = processed.infotexts
if self.swap_in_generated:
+
logger.status("Working: source face index %s, target face index %s", self.source_faces_index, self.faces_index)
- # if self.source is not None:
+
+ if self.source is not None:
+ # self.source_folder = None
+ self.source_imgs = None
+
for i,(img,info) in enumerate(zip(orig_images, orig_infotexts)):
if check_process_halt():
postprocess_run = False
@@ -520,16 +303,32 @@ class FaceSwapScript(scripts.Script):
mask_face=self.mask_face,
select_source=self.select_source,
face_model = self.face_model,
+ source_folder = self.source_folder,
+ source_imgs = self.source_imgs,
)
- if result is not None and swapped > 0:
- result_images.append(result)
- suffix = "-swapped"
- try:
- img_path = save_image(result, p.outpath_samples, "", p.all_seeds[0], p.all_prompts[0], "png",info=info, p=p, suffix=suffix)
- except:
- logger.error("Cannot save a result image - please, check SD WebUI Settings (Saving and Paths)")
- elif result is None:
- logger.error("Cannot create a result image")
+
+ if self.select_source == 2 or (self.select_source == 0 and self.source_imgs is not None and self.source is None):
+ if len(result) > 0 and swapped > 0:
+ result_images.extend(result)
+ suffix = "-swapped"
+ for i,x in enumerate(result):
+ try:
+ img_path = save_image(result[i], p.outpath_samples, "", p.all_seeds[0], p.all_prompts[0], "png",info=info, p=p, suffix=suffix)
+ except:
+ logger.error("Cannot save a result image - please, check SD WebUI Settings (Saving and Paths)")
+ elif len(result) == 0:
+ logger.error("Cannot create a result image")
+
+ else:
+ if result is not None and swapped > 0:
+ result_images.append(result)
+ suffix = "-swapped"
+ try:
+ img_path = save_image(result, p.outpath_samples, "", p.all_seeds[0], p.all_prompts[0], "png",info=info, p=p, suffix=suffix)
+ except:
+ logger.error("Cannot save a result image - please, check SD WebUI Settings (Saving and Paths)")
+ elif result is None:
+ logger.error("Cannot create a result image")
# if len(output) != 0:
# split_fullfn = os.path.splitext(img_path[0])
@@ -554,7 +353,7 @@ class FaceSwapScript(scripts.Script):
images = kwargs["images"]
def postprocess_image(self, p, script_pp: scripts.PostprocessImageArgs, *args):
- if self.enable and self.swap_in_generated and not self.save_original:
+ if self.enable and self.swap_in_generated and not self.save_original and ((self.select_source == 0 and self.source is not None) or self.select_source == 1):
logger.debug("*** Check postprocess_image")
@@ -583,6 +382,8 @@ class FaceSwapScript(scripts.Script):
mask_face=self.mask_face,
select_source=self.select_source,
face_model = self.face_model,
+ source_folder = None,
+ source_imgs = None,
)
try:
pp = scripts_postprocessing.PostprocessedImage(result)
@@ -606,197 +407,24 @@ class FaceSwapScriptExtras(scripts_postprocessing.ScriptPostprocessing):
def ui(self):
with gr.Accordion(f"{app_title}", open=False):
- def update_fm_list(selected: str):
- return gr.Dropdown.update(
- value=selected, choices=get_model_names(get_facemodels)
- )
- def update_upscalers_list(selected: str):
- return gr.Dropdown.update(
- value=selected, choices=[upscaler.name for upscaler in shared.sd_upscalers]
- )
- def update_models_list(selected: str):
- return gr.Dropdown.update(
- value=selected, choices=get_models()
- )
-
- # TAB MAIN
- with gr.Tab("Main"):
- with gr.Column():
- img = gr.Image(type="pil")
- enable = gr.Checkbox(False, label="Enable", info=f"The Fast and Simple FaceSwap Extension - {version_flag}")
- # gr.Markdown("
")
- with gr.Row():
- select_source = gr.Radio(
- ["Image","Face Model"],
- value="Image",
- label="Select Source",
- type="index",
- scale=1,
- )
- face_models = get_model_names(get_facemodels)
- face_model = gr.Dropdown(
- choices=face_models,
- label="Choose Face Model",
- value="None",
- scale=2,
- )
- fm_update = gr.Button(
- value="🔄",
- variant="tool",
- )
- fm_update.click(
- update_fm_list,
- inputs=[face_model],
- outputs=[face_model],
- )
- setattr(face_model, "do_not_save_to_config", True)
- mask_face = gr.Checkbox(
- False,
- label="Face Mask Correction",
- info="Apply this option if you see some pixelation around face contours"
- )
- gr.Markdown("Source Image (above):")
- with gr.Row():
- source_faces_index = gr.Textbox(
- value="0",
- placeholder="Which face(s) to use as Source (comma separated)",
- label="Comma separated face number(s); Example: 0,2,1",
- )
- gender_source = gr.Radio(
- ["No", "Female Only", "Male Only"],
- value="No",
- label="Gender Detection (Source)",
- type="index",
- )
- gr.Markdown("Target Image (result):")
- with gr.Row():
- faces_index = gr.Textbox(
- value="0",
- placeholder="Which face(s) to Swap into Target (comma separated)",
- label="Comma separated face number(s); Example: 1,0,2",
- )
- gender_target = gr.Radio(
- ["No", "Female Only", "Male Only"],
- value="No",
- label="Gender Detection (Target)",
- type="index",
- )
- with gr.Row():
- face_restorer_name = gr.Radio(
- label="Restore Face",
- choices=["None"] + [x.name() for x in shared.face_restorers],
- value=shared.face_restorers[0].name(),
- type="value",
- )
- with gr.Column():
- face_restorer_visibility = gr.Slider(
- 0, 1, 1, step=0.1, label="Restore Face Visibility"
- )
- codeformer_weight = gr.Slider(
- 0, 1, 0.5, step=0.1, label="CodeFormer Weight", info="0 = maximum effect, 1 = minimum effect"
- )
+ enable = gr.Checkbox(False, label="Enable", info=f"The Fast and Simple FaceSwap Extension - {version_flag}")
+ # TAB MAIN
+ msgs: dict = {
+ "extra_multiple_source": " | Сomparison grid as a result",
+ }
+ img, imgs, select_source, face_model, source_folder, save_original, mask_face, source_faces_index, gender_source, faces_index, gender_target, face_restorer_name, face_restorer_visibility, codeformer_weight, swap_in_source, swap_in_generated = ui_main.show(is_img2img=False, show_br=False, **msgs)
+
# TAB UPSCALE
- with gr.Tab("Upscale"):
- restore_first = gr.Checkbox(
- True,
- label="1. Restore Face -> 2. Upscale (-Uncheck- if you want vice versa)",
- info="Postprocessing Order"
- )
- with gr.Row():
- upscaler_name = gr.Dropdown(
- choices=[upscaler.name for upscaler in shared.sd_upscalers],
- label="Upscaler",
- value="None",
- info="Won't scale if you choose -Swap in Source- via img2img, only 1x-postprocessing will affect (texturing, denoising, restyling etc.)"
- )
- upscalers_update = gr.Button(
- value="🔄",
- variant="tool",
- )
- upscalers_update.click(
- update_upscalers_list,
- inputs=[upscaler_name],
- outputs=[upscaler_name],
- )
- with gr.Row():
- upscaler_scale = gr.Slider(1, 8, 1, step=0.1, label="Scale by")
- upscaler_visibility = gr.Slider(
- 0, 1, 1, step=0.1, label="Upscaler Visibility (if scale = 1)"
- )
-
+ restore_first, upscaler_name, upscaler_scale, upscaler_visibility = ui_upscale.show(show_br=False)
+
# TAB TOOLS
- with gr.Tab("Tools 🆕"):
- with gr.Tab("Face Models"):
- gr.Markdown("Load an image containing one person, name it and click 'Build and Save'")
- img_fm = gr.Image(
- type="pil",
- label="Load Image to build Face Model",
- )
- with gr.Row(equal_height=True):
- fm_name = gr.Textbox(
- value="",
- placeholder="Please type any name (e.g. Elena)",
- label="Face Model Name",
- )
- save_fm_btn = gr.Button("Build and Save")
- save_fm = gr.Markdown("You can find saved models in 'models/reactor/faces'")
- save_fm_btn.click(
- build_face_model,
- inputs=[img_fm, fm_name],
- outputs=[save_fm],
- )
-
+ ui_tools.show()
+
# TAB SETTINGS
- with gr.Tab("Settings"):
- models = get_models()
- with gr.Row(visible=EP_is_visible):
- device = gr.Radio(
- label="Execution Provider",
- choices=DEVICE_LIST,
- value=DEVICE,
- type="value",
- info="If you already run 'Generate' - RESTART is required to apply. Click 'Save', (A1111) Extensions Tab -> 'Apply and restart UI' or (SD.Next) close the Server and start it again",
- scale=2,
- )
- save_device_btn = gr.Button("Save", scale=0)
- save = gr.Markdown("", visible=EP_is_visible)
- setattr(device, "do_not_save_to_config", True)
- save_device_btn.click(
- set_Device,
- inputs=[device],
- outputs=[save],
- )
- with gr.Row():
- if len(models) == 0:
- logger.warning(
- "You should at least have one model in models directory, please read the doc here: https://github.com/Gourieff/sd-webui-reactor/"
- )
- model = gr.Dropdown(
- choices=models,
- label="Model not found, please download one and refresh the list",
- )
- else:
- model = gr.Dropdown(
- choices=models, label="Model", value=models[0]
- )
- models_update = gr.Button(
- value="🔄",
- variant="tool",
- )
- models_update.click(
- update_models_list,
- inputs=[model],
- outputs=[model],
- )
- console_logging_level = gr.Radio(
- ["No log", "Minimum", "Default"],
- value="Minimum",
- label="Console Log Level",
- type="index",
- )
-
- gr.Markdown("by Eugene Gourieff")
+ model, device, console_logging_level, source_hash_check, target_hash_check = ui_settings.show(hash_check_block=False)
+
+ gr.Markdown("by Eugene Gourieff")
args = {
'img': img,
@@ -818,6 +446,8 @@ class FaceSwapScriptExtras(scripts_postprocessing.ScriptPostprocessing):
'mask_face': mask_face,
'select_source': select_source,
'face_model': face_model,
+ 'source_folder': source_folder,
+ 'imgs': imgs,
}
return args
@@ -853,7 +483,7 @@ class FaceSwapScriptExtras(scripts_postprocessing.ScriptPostprocessing):
if check_process_halt():
return
- global MODELS_PATH
+ global SWAPPER_MODELS_PATH
self.source = args['img']
self.face_restorer_name = args['face_restorer_name']
self.upscaler_scale = args['upscaler_scale']
@@ -861,7 +491,7 @@ class FaceSwapScriptExtras(scripts_postprocessing.ScriptPostprocessing):
self.face_restorer_visibility = args['face_restorer_visibility']
self.restore_first = args['restore_first']
self.upscaler_name = args['upscaler_name']
- self.model = os.path.join(MODELS_PATH, args['model'])
+ self.model = os.path.join(SWAPPER_MODELS_PATH, args['model'])
self.console_logging_level = args['console_logging_level']
self.gender_source = args['gender_source']
self.gender_target = args['gender_target']
@@ -870,6 +500,8 @@ class FaceSwapScriptExtras(scripts_postprocessing.ScriptPostprocessing):
self.mask_face = args['mask_face']
self.select_source = args['select_source']
self.face_model = args['face_model']
+ self.source_folder = args['source_folder']
+ self.source_imgs = args['imgs']
if self.gender_source is None or self.gender_source == "No":
self.gender_source = 0
if self.gender_target is None or self.gender_target == "No":
@@ -893,10 +525,16 @@ class FaceSwapScriptExtras(scripts_postprocessing.ScriptPostprocessing):
reset_messaged()
set_Device(self.device)
+
+ logger.debug("We're here: process() 1")
- if (self.source is not None and self.select_source == 0) or ((self.face_model is not None and self.face_model != "None") and self.select_source == 1):
+ if (self.source is not None and self.select_source == 0) or ((self.face_model is not None and self.face_model != "None") and self.select_source == 1) or ((self.source_folder is not None and self.source_folder != "") and self.select_source == 2) or ((self.source_imgs is not None and self.source is None) and self.select_source == 0):
+
+ logger.debug("We're here: process() 2")
+
apply_logging_patch(self.console_logging_level)
logger.status("Working: source face index %s, target face index %s", self.source_faces_index, self.faces_index)
+ # if self.select_source != 2:
image: Image.Image = pp.image
result, output, swapped = swap_face(
self.source,
@@ -913,12 +551,27 @@ class FaceSwapScriptExtras(scripts_postprocessing.ScriptPostprocessing):
mask_face=self.mask_face,
select_source=self.select_source,
face_model=self.face_model,
+ source_folder=self.source_folder,
+ source_imgs=self.source_imgs,
)
- try:
- pp.info["ReActor"] = True
- pp.image = result
- logger.status("---Done!---")
- except Exception:
- logger.error("Cannot create a result image")
+ if self.select_source == 2 or (self.select_source == 0 and self.source_imgs is not None and self.source is None):
+ if len(result) > 0 and swapped > 0:
+ image = result[0]
+ if len(result) > 1:
+ grid = make_grid(result)
+ result.insert(0, grid)
+ image = grid
+ pp.info["ReActor"] = True
+ pp.image = image
+ logger.status("---Done!---")
+ else:
+ logger.error("Cannot create a result image")
+ else:
+ try:
+ pp.info["ReActor"] = True
+ pp.image = result
+ logger.status("---Done!---")
+ except Exception:
+ logger.error("Cannot create a result image")
else:
logger.error("Please provide a source face")
diff --git a/scripts/reactor_globals.py b/scripts/reactor_globals.py
index f828254..7e8f4dc 100644
--- a/scripts/reactor_globals.py
+++ b/scripts/reactor_globals.py
@@ -14,7 +14,8 @@ BASE_PATH = os.path.join(Path(__file__).parents[1])
DEVICE_LIST: list = ["CPU", "CUDA"]
MODELS_PATH = models_path
-REACTOR_MODELS_PATH = os.path.join(models_path, "reactor")
+SWAPPER_MODELS_PATH = os.path.join(MODELS_PATH, "insightface")
+REACTOR_MODELS_PATH = os.path.join(MODELS_PATH, "reactor")
FACE_MODELS_PATH = os.path.join(REACTOR_MODELS_PATH, "faces")
if not os.path.exists(REACTOR_MODELS_PATH):
diff --git a/scripts/reactor_helpers.py b/scripts/reactor_helpers.py
index cfce05f..2db883d 100644
--- a/scripts/reactor_helpers.py
+++ b/scripts/reactor_helpers.py
@@ -13,6 +13,16 @@ from modules.images import FilenameGenerator, get_next_sequence_number
from modules import shared, script_callbacks
from scripts.reactor_globals import DEVICE, BASE_PATH, FACE_MODELS_PATH
+try:
+ from modules.paths_internal import models_path
+except:
+ try:
+ from modules.paths import models_path
+ except:
+ model_path = os.path.abspath("models")
+
+MODELS_PATH = None
+
def set_Device(value):
global DEVICE
DEVICE = value
@@ -155,6 +165,20 @@ def save_face_model(face: Face, filename: str) -> None:
except Exception as e:
print(f"Error: {e}")
+def get_models():
+ global MODELS_PATH
+ models_path_init = os.path.join(models_path, "insightface/*")
+ models = glob.glob(models_path_init)
+ models = [x for x in models if x.endswith(".onnx") or x.endswith(".pth")]
+ models_names = []
+ for model in models:
+ model_path = os.path.split(model)
+ if MODELS_PATH is None:
+ MODELS_PATH = model_path[0]
+ model_name = model_path[1]
+ models_names.append(model_name)
+ return models_names
+
def load_face_model(filename: str):
face = {}
model_path = os.path.join(FACE_MODELS_PATH, filename)
@@ -175,3 +199,11 @@ def get_model_names(get_models):
for x in models:
names.append(os.path.basename(x))
return names
+
+def get_images_from_folder(path: str):
+ images_path = os.path.join(path, "*")
+ images = glob.glob(images_path)
+ return [Image.open(x) for x in images if x.endswith(('jpg', 'png', 'jpeg', 'webp', 'bmp'))]
+
+def get_images_from_list(imgs: List):
+ return [Image.open(os.path.abspath(x.name)) for x in imgs]
diff --git a/scripts/reactor_swapper.py b/scripts/reactor_swapper.py
index 69b1e62..75f44af 100644
--- a/scripts/reactor_swapper.py
+++ b/scripts/reactor_swapper.py
@@ -11,7 +11,14 @@ import insightface
from insightface.app.common import Face
from scripts.reactor_globals import FACE_MODELS_PATH
-from scripts.reactor_helpers import get_image_md5hash, get_Device, save_face_model, load_face_model
+from scripts.reactor_helpers import (
+ get_image_md5hash,
+ get_Device,
+ save_face_model,
+ load_face_model,
+ get_images_from_folder,
+ get_images_from_list
+)
from scripts.console_log_patch import apply_logging_patch
from modules.face_restoration import FaceRestoration
@@ -22,7 +29,7 @@ except: # SD.Next
from modules.upscaler import UpscalerData
from modules.shared import state
from scripts.reactor_logger import logger
-from modules.reactor_mask import apply_face_mask
+from reactor_modules.reactor_mask import apply_face_mask
try:
from modules.paths_internal import models_path
@@ -92,6 +99,14 @@ SOURCE_FACES = None
SOURCE_IMAGE_HASH = None
TARGET_FACES = None
TARGET_IMAGE_HASH = None
+SOURCE_FACES_LIST = []
+SOURCE_IMAGE_LIST_HASH = []
+
+def clear_faces_list():
+ global SOURCE_FACES_LIST, SOURCE_IMAGE_LIST_HASH
+ SOURCE_FACES_LIST = []
+ SOURCE_IMAGE_LIST_HASH = []
+ logger.status("Source Images Hash has been reset (for Multiple or Folder Source)")
def getAnalysisModel():
@@ -315,8 +330,11 @@ def swap_face(
mask_face: bool = False,
select_source: int = 0,
face_model: str = "None",
+ source_folder: str = "",
+ source_imgs: Union[List, None] = None,
):
- global SOURCE_FACES, SOURCE_IMAGE_HASH, TARGET_FACES, TARGET_IMAGE_HASH, PROVIDERS
+ global SOURCE_FACES, SOURCE_IMAGE_HASH, TARGET_FACES, TARGET_IMAGE_HASH, PROVIDERS, SOURCE_FACES_LIST, SOURCE_IMAGE_LIST_HASH
+
result_image = target_img
PROVIDERS = ["CUDAExecutionProvider"] if device == "CUDA" else ["CPUExecutionProvider"]
@@ -347,179 +365,229 @@ def swap_face(
output: List = []
output_info: str = ""
swapped = 0
+
+ # *****************
+ # SWAP from FOLDER or MULTIPLE images:
- if select_source == 0 and source_img is not None:
+ if (select_source == 0 and source_imgs is not None) or (select_source == 2 and (source_folder is not None and source_folder != "")):
+
+ result = []
+
+ source_images = get_images_from_folder(source_folder) if select_source == 2 else get_images_from_list(source_imgs)
+
+ if len(source_images) > 0:
+ source_img_ff = []
+ source_faces_ff = []
+ for i, source_image in enumerate(source_images):
+
+ source_image = cv2.cvtColor(np.array(source_image), cv2.COLOR_RGB2BGR)
+ source_img_ff.append(source_image)
+
+ if source_hash_check:
+
+ source_image_md5hash = get_image_md5hash(source_image)
+
+ if len(SOURCE_IMAGE_LIST_HASH) == 0:
+ SOURCE_IMAGE_LIST_HASH = [source_image_md5hash]
+ source_image_same = False
+ elif len(SOURCE_IMAGE_LIST_HASH) == i:
+ SOURCE_IMAGE_LIST_HASH.append(source_image_md5hash)
+ source_image_same = False
+ else:
+ source_image_same = True if SOURCE_IMAGE_LIST_HASH[i] == source_image_md5hash else False
+ if not source_image_same:
+ SOURCE_IMAGE_LIST_HASH[i] = source_image_md5hash
+
+ logger.info("(Image %s) Source Image MD5 Hash = %s", i, SOURCE_IMAGE_LIST_HASH[i])
+ logger.info("(Image %s) Source Image the Same? %s", i, source_image_same)
+
+ if len(SOURCE_FACES_LIST) == 0:
+ logger.status(f"Analyzing Source Image {i}...")
+ source_faces = analyze_faces(source_image)
+ SOURCE_FACES_LIST = [source_faces]
+ elif len(SOURCE_FACES_LIST) == i and not source_image_same:
+ logger.status(f"Analyzing Source Image {i}...")
+ source_faces = analyze_faces(source_image)
+ SOURCE_FACES_LIST.append(source_faces)
+ elif len(SOURCE_FACES_LIST) != i and not source_image_same:
+ logger.status(f"Analyzing Source Image {i}...")
+ source_faces = analyze_faces(source_image)
+ SOURCE_FACES_LIST[i] = source_faces
+ elif source_image_same:
+ logger.status("(Image %s) Using Hashed Source Face(s) Model...", i)
+ source_faces = SOURCE_FACES_LIST[i]
+
+ else:
+ logger.status(f"Analyzing Source Image {i}...")
+ source_faces = analyze_faces(source_image)
+
+ if source_faces is not None:
+ source_faces_ff.append(source_faces)
- source_img = cv2.cvtColor(np.array(source_img), cv2.COLOR_RGB2BGR)
+ if len(source_faces_ff) > 0:
- if source_hash_check:
-
- source_image_md5hash = get_image_md5hash(source_img)
-
- if SOURCE_IMAGE_HASH is None:
- SOURCE_IMAGE_HASH = source_image_md5hash
- source_image_same = False
- else:
- source_image_same = True if SOURCE_IMAGE_HASH == source_image_md5hash else False
- if not source_image_same:
- SOURCE_IMAGE_HASH = source_image_md5hash
-
- logger.info("Source Image MD5 Hash = %s", SOURCE_IMAGE_HASH)
- logger.info("Source Image the Same? %s", source_image_same)
-
- if SOURCE_FACES is None or not source_image_same:
- logger.status("Analyzing Source Image...")
- source_faces = analyze_faces(source_img)
- SOURCE_FACES = source_faces
- elif source_image_same:
- logger.status("Using Hashed Source Face(s) Model...")
- source_faces = SOURCE_FACES
-
- else:
- logger.status("Analyzing Source Image...")
- source_faces = analyze_faces(source_img)
-
- elif select_source == 1 and (face_model is not None and face_model != "None"):
- source_face_model = [load_face_model(face_model)]
- if source_face_model is not None:
- source_faces_index = [0]
- source_faces = source_face_model
- logger.status("Using Loaded Source Face Model...")
- else:
- logger.error(f"Cannot load Face Model File: {face_model}.safetensors")
- else:
- logger.error("Cannot detect any Source")
-
- if source_faces is not None:
-
- if target_hash_check:
-
- target_image_md5hash = get_image_md5hash(target_img)
-
- if TARGET_IMAGE_HASH is None:
- TARGET_IMAGE_HASH = target_image_md5hash
- target_image_same = False
- else:
- target_image_same = True if TARGET_IMAGE_HASH == target_image_md5hash else False
- if not target_image_same:
- TARGET_IMAGE_HASH = target_image_md5hash
-
- logger.info("Target Image MD5 Hash = %s", TARGET_IMAGE_HASH)
- logger.info("Target Image the Same? %s", target_image_same)
+ if target_hash_check:
- if TARGET_FACES is None or not target_image_same:
+ target_image_md5hash = get_image_md5hash(target_img)
+
+ if TARGET_IMAGE_HASH is None:
+ TARGET_IMAGE_HASH = target_image_md5hash
+ target_image_same = False
+ else:
+ target_image_same = True if TARGET_IMAGE_HASH == target_image_md5hash else False
+ if not target_image_same:
+ TARGET_IMAGE_HASH = target_image_md5hash
+
+ logger.info("Target Image MD5 Hash = %s", TARGET_IMAGE_HASH)
+ logger.info("Target Image the Same? %s", target_image_same)
+
+ if TARGET_FACES is None or not target_image_same:
+ logger.status("Analyzing Target Image...")
+ target_faces = analyze_faces(target_img)
+ TARGET_FACES = target_faces
+ elif target_image_same:
+ logger.status("Using Hashed Target Face(s) Model...")
+ target_faces = TARGET_FACES
+
+ else:
logger.status("Analyzing Target Image...")
target_faces = analyze_faces(target_img)
- TARGET_FACES = target_faces
- elif target_image_same:
- logger.status("Using Hashed Target Face(s) Model...")
- target_faces = TARGET_FACES
-
- else:
- logger.status("Analyzing Target Image...")
- target_faces = analyze_faces(target_img)
- logger.status("Detecting Source Face, Index = %s", source_faces_index[0])
- if select_source == 0 and source_img is not None:
- source_face, wrong_gender, source_age, source_gender = get_face_single(source_img, source_faces, face_index=source_faces_index[0], gender_source=gender_source)
- else:
- source_face = sorted(source_faces, key=lambda x: x.bbox[0])[source_faces_index[0]]
- wrong_gender = 0
- source_age = source_face["age"]
- source_gender = "Female" if source_face["gender"] == 0 else "Male"
- if source_age != "None" or source_gender != "None":
- logger.status("Detected: -%s- y.o. %s", source_age, source_gender)
+ for i,source_faces in enumerate(source_faces_ff):
- output_info = f"SourceFaceIndex={source_faces_index[0]};Age={source_age};Gender={source_gender}\n"
- output.append(output_info)
-
- if len(source_faces_index) != 0 and len(source_faces_index) != 1 and len(source_faces_index) != len(faces_index):
- logger.status("Source Faces must have no entries (default=0), one entry, or same number of entries as target faces.")
- elif source_face is not None:
-
- result = target_img
- face_swapper = getFaceSwapModel(model)
-
- source_face_idx = 0
-
- for face_num in faces_index:
- if check_process_halt():
- return result_image, [], 0
- if len(source_faces_index) > 1 and source_face_idx > 0:
- logger.status("Detecting Source Face, Index = %s", source_faces_index[source_face_idx])
- source_face, wrong_gender, source_age, source_gender = get_face_single(source_img, source_faces, face_index=source_faces_index[source_face_idx], gender_source=gender_source)
- if source_age != "None" or source_gender != "None":
- logger.status("Detected: -%s- y.o. %s", source_age, source_gender)
-
- output_info = f"SourceFaceIndex={source_faces_index[source_face_idx]};Age={source_age};Gender={source_gender}\n"
- output.append(output_info)
-
- source_face_idx += 1
-
- if source_face is not None and wrong_gender == 0:
- logger.status("Detecting Target Face, Index = %s", face_num)
- target_face, wrong_gender, target_age, target_gender = get_face_single(target_img, target_faces, face_index=face_num, gender_target=gender_target)
- if target_age != "None" or target_gender != "None":
- logger.status("Detected: -%s- y.o. %s", target_age, target_gender)
-
- output_info = f"TargetFaceIndex={face_num};Age={target_age};Gender={target_gender}\n"
- output.append(output_info)
-
- if target_face is not None and wrong_gender == 0:
- logger.status("Swapping Source into Target")
- swapped_image = face_swapper.get(result, target_face, source_face)
-
- if mask_face:
- result = apply_face_mask(swapped_image=swapped_image,target_image=result,target_face=target_face,entire_mask_image=entire_mask_image)
- else:
- result = swapped_image
- swapped += 1
-
- elif wrong_gender == 1:
- wrong_gender = 0
-
- if source_face_idx == len(source_faces_index):
- result_image = Image.fromarray(cv2.cvtColor(result, cv2.COLOR_BGR2RGB))
-
- if enhancement_options is not None and len(source_faces_index) > 1:
- result_image = enhance_image(result_image, enhancement_options)
-
- return result_image, output, swapped
-
- else:
- logger.status(f"No target face found for {face_num}")
+ logger.status("(Image %s) Detecting Source Face, Index = %s", i, source_faces_index[0])
+ source_face, wrong_gender, source_age, source_gender = get_face_single(source_img_ff[i], source_faces, face_index=source_faces_index[0], gender_source=gender_source)
- elif wrong_gender == 1:
- wrong_gender = 0
-
- if source_face_idx == len(source_faces_index):
- result_image = Image.fromarray(cv2.cvtColor(result, cv2.COLOR_BGR2RGB))
-
- if enhancement_options is not None and len(source_faces_index) > 1:
- result_image = enhance_image(result_image, enhancement_options)
-
- return result_image, output, swapped
-
- else:
- logger.status(f"No source face found for face number {source_face_idx}.")
+ if source_age != "None" or source_gender != "None":
+ logger.status("(Image %s) Detected: -%s- y.o. %s", i, source_age, source_gender)
+
+ if len(source_faces_index) != 0 and len(source_faces_index) != 1 and len(source_faces_index) != len(faces_index):
+ logger.status("Source Faces must have no entries (default=0), one entry, or same number of entries as target faces.")
+
+ elif source_face is not None:
+
+ result_image, output, swapped = operate(source_img_ff[i],target_img,target_img_orig,model,source_faces_index,faces_index,source_faces,target_faces,gender_source,gender_target,source_face,wrong_gender,source_age,source_gender,output,swapped,mask_face,entire_mask_image,enhancement_options)
+
+ result.append(result_image)
+
+ result = [result_image] if len(result) == 0 else result
+
+ return result, output, swapped
+
+ # END
+ # *****************
+
+ # ***********************
+ # SWAP from IMG or MODEL:
- result_image = Image.fromarray(cv2.cvtColor(result, cv2.COLOR_BGR2RGB))
-
- if enhancement_options is not None and swapped > 0:
- if mask_face and entire_mask_image is not None:
- result_image = enhance_image_and_mask(result_image, enhancement_options,Image.fromarray(target_img_orig),Image.fromarray(entire_mask_image).convert("L"))
- else:
- result_image = enhance_image(result_image, enhancement_options)
- elif mask_face and entire_mask_image is not None and swapped > 0:
- result_image = Image.composite(result_image,Image.fromarray(target_img_orig),Image.fromarray(entire_mask_image).convert("L"))
-
- else:
- logger.status("No source face(s) in the provided Index")
else:
- logger.status("No source face(s) found")
-
- return result_image, output, swapped
+
+ if select_source == 0 and source_img is not None:
+
+ source_img = cv2.cvtColor(np.array(source_img), cv2.COLOR_RGB2BGR)
+ if source_hash_check:
+
+ source_image_md5hash = get_image_md5hash(source_img)
+
+ if SOURCE_IMAGE_HASH is None:
+ SOURCE_IMAGE_HASH = source_image_md5hash
+ source_image_same = False
+ else:
+ source_image_same = True if SOURCE_IMAGE_HASH == source_image_md5hash else False
+ if not source_image_same:
+ SOURCE_IMAGE_HASH = source_image_md5hash
+
+ logger.info("Source Image MD5 Hash = %s", SOURCE_IMAGE_HASH)
+ logger.info("Source Image the Same? %s", source_image_same)
+
+ if SOURCE_FACES is None or not source_image_same:
+ logger.status("Analyzing Source Image...")
+ source_faces = analyze_faces(source_img)
+ SOURCE_FACES = source_faces
+ elif source_image_same:
+ logger.status("Using Hashed Source Face(s) Model...")
+ source_faces = SOURCE_FACES
+
+ else:
+ logger.status("Analyzing Source Image...")
+ source_faces = analyze_faces(source_img)
+
+ elif select_source == 1 and (face_model is not None and face_model != "None"):
+ source_face_model = [load_face_model(face_model)]
+ if source_face_model is not None:
+ source_faces_index = [0]
+ source_faces = source_face_model
+ logger.status("Using Loaded Source Face Model...")
+ else:
+ logger.error(f"Cannot load Face Model File: {face_model}.safetensors")
+
+ else:
+ logger.error("Cannot detect any Source")
+ return result_image, [], 0
+
+ if source_faces is not None:
+
+ if target_hash_check:
+
+ target_image_md5hash = get_image_md5hash(target_img)
+
+ if TARGET_IMAGE_HASH is None:
+ TARGET_IMAGE_HASH = target_image_md5hash
+ target_image_same = False
+ else:
+ target_image_same = True if TARGET_IMAGE_HASH == target_image_md5hash else False
+ if not target_image_same:
+ TARGET_IMAGE_HASH = target_image_md5hash
+
+ logger.info("Target Image MD5 Hash = %s", TARGET_IMAGE_HASH)
+ logger.info("Target Image the Same? %s", target_image_same)
+
+ if TARGET_FACES is None or not target_image_same:
+ logger.status("Analyzing Target Image...")
+ target_faces = analyze_faces(target_img)
+ TARGET_FACES = target_faces
+ elif target_image_same:
+ logger.status("Using Hashed Target Face(s) Model...")
+ target_faces = TARGET_FACES
+
+ else:
+ logger.status("Analyzing Target Image...")
+ target_faces = analyze_faces(target_img)
+
+ logger.status("Detecting Source Face, Index = %s", source_faces_index[0])
+ if select_source == 0 and source_img is not None:
+ source_face, wrong_gender, source_age, source_gender = get_face_single(source_img, source_faces, face_index=source_faces_index[0], gender_source=gender_source)
+ else:
+ source_face = sorted(source_faces, key=lambda x: x.bbox[0])[source_faces_index[0]]
+ wrong_gender = 0
+ source_age = source_face["age"]
+ source_gender = "Female" if source_face["gender"] == 0 else "Male"
+
+ if source_age != "None" or source_gender != "None":
+ logger.status("Detected: -%s- y.o. %s", source_age, source_gender)
+
+ output_info = f"SourceFaceIndex={source_faces_index[0]};Age={source_age};Gender={source_gender}\n"
+ output.append(output_info)
+
+ if len(source_faces_index) != 0 and len(source_faces_index) != 1 and len(source_faces_index) != len(faces_index):
+ logger.status("Source Faces must have no entries (default=0), one entry, or same number of entries as target faces.")
+
+ elif source_face is not None:
+
+ result_image, output, swapped = operate(source_img,target_img,target_img_orig,model,source_faces_index,faces_index,source_faces,target_faces,gender_source,gender_target,source_face,wrong_gender,source_age,source_gender,output,swapped,mask_face,entire_mask_image,enhancement_options)
+
+ else:
+ logger.status("No source face(s) in the provided Index")
+ else:
+ logger.status("No source face(s) found")
+
+ return result_image, output, swapped
+
+ # END
+ # **********************
+
+ return result_image, [], 0
def build_face_model(image: Image.Image, name: str):
if image is None:
@@ -545,3 +613,103 @@ def build_face_model(image: Image.Image, name: str):
no_face_msg = "No face found, please try another image"
logger.error(no_face_msg)
return no_face_msg
+
+
+def operate(
+ source_img,
+ target_img,
+ target_img_orig,
+ model,
+ source_faces_index,
+ faces_index,
+ source_faces,
+ target_faces,
+ gender_source,
+ gender_target,
+ source_face,
+ wrong_gender,
+ source_age,
+ source_gender,
+ output,
+ swapped,
+ mask_face,
+ entire_mask_image,
+ enhancement_options,
+ ):
+ result = target_img
+ face_swapper = getFaceSwapModel(model)
+
+ source_face_idx = 0
+
+ for face_num in faces_index:
+ if check_process_halt():
+ return result_image, [], 0
+ if len(source_faces_index) > 1 and source_face_idx > 0:
+ logger.status("Detecting Source Face, Index = %s", source_faces_index[source_face_idx])
+ source_face, wrong_gender, source_age, source_gender = get_face_single(source_img, source_faces, face_index=source_faces_index[source_face_idx], gender_source=gender_source)
+ if source_age != "None" or source_gender != "None":
+ logger.status("Detected: -%s- y.o. %s", source_age, source_gender)
+
+ output_info = f"SourceFaceIndex={source_faces_index[source_face_idx]};Age={source_age};Gender={source_gender}\n"
+ output.append(output_info)
+
+ source_face_idx += 1
+
+ if source_face is not None and wrong_gender == 0:
+ logger.status("Detecting Target Face, Index = %s", face_num)
+ target_face, wrong_gender, target_age, target_gender = get_face_single(target_img, target_faces, face_index=face_num, gender_target=gender_target)
+ if target_age != "None" or target_gender != "None":
+ logger.status("Detected: -%s- y.o. %s", target_age, target_gender)
+
+ output_info = f"TargetFaceIndex={face_num};Age={target_age};Gender={target_gender}\n"
+ output.append(output_info)
+
+ if target_face is not None and wrong_gender == 0:
+ logger.status("Swapping Source into Target")
+ swapped_image = face_swapper.get(result, target_face, source_face)
+
+ if mask_face:
+ result = apply_face_mask(swapped_image=swapped_image,target_image=result,target_face=target_face,entire_mask_image=entire_mask_image)
+ else:
+ result = swapped_image
+ swapped += 1
+
+ elif wrong_gender == 1:
+ wrong_gender = 0
+
+ if source_face_idx == len(source_faces_index):
+ result_image = Image.fromarray(cv2.cvtColor(result, cv2.COLOR_BGR2RGB))
+
+ if enhancement_options is not None and len(source_faces_index) > 1:
+ result_image = enhance_image(result_image, enhancement_options)
+
+ return result_image, output, swapped
+
+ else:
+ logger.status(f"No target face found for {face_num}")
+
+ elif wrong_gender == 1:
+ wrong_gender = 0
+
+ if source_face_idx == len(source_faces_index):
+ result_image = Image.fromarray(cv2.cvtColor(result, cv2.COLOR_BGR2RGB))
+
+ if enhancement_options is not None and len(source_faces_index) > 1:
+ result_image = enhance_image(result_image, enhancement_options)
+
+ return result_image, output, swapped
+
+ else:
+ logger.status(f"No source face found for face number {source_face_idx}.")
+
+ result_image = Image.fromarray(cv2.cvtColor(result, cv2.COLOR_BGR2RGB))
+
+ if enhancement_options is not None and swapped > 0:
+ if mask_face and entire_mask_image is not None:
+ result_image = enhance_image_and_mask(result_image, enhancement_options,Image.fromarray(target_img_orig),Image.fromarray(entire_mask_image).convert("L"))
+ else:
+ result_image = enhance_image(result_image, enhancement_options)
+ elif mask_face and entire_mask_image is not None and swapped > 0:
+ result_image = Image.composite(result_image,Image.fromarray(target_img_orig),Image.fromarray(entire_mask_image).convert("L"))
+
+ return result_image, output, swapped
diff --git a/scripts/reactor_version.py b/scripts/reactor_version.py
index 9e68bc1..996347a 100644
--- a/scripts/reactor_version.py
+++ b/scripts/reactor_version.py
@@ -1,5 +1,5 @@
app_title = "ReActor"
-version_flag = "v0.5.1"
+version_flag = "v0.6.0-a1"
from scripts.reactor_logger import logger, get_Run, set_Run