UPDATE: Multiple Source Images Support
Feature Request #143 +VersionUP (0.6.0 alpha1)
This commit is contained in:
parent
7706d6aa34
commit
e62ff09e5c
33
README.md
33
README.md
@ -2,7 +2,7 @@
|
||||
|
||||
<img src="https://github.com/Gourieff/Assets/raw/main/sd-webui-reactor/ReActor_logo_red.png?raw=true" alt="logo" width="180px"/>
|
||||
|
||||

|
||||

|
||||
|
||||
<a href="https://boosty.to/artgourieff" target="_blank">
|
||||
<img src="https://lovemet.ru/www/boosty.jpg" width="108" alt="Support Me on Boosty"/>
|
||||
@ -38,11 +38,20 @@
|
||||
|
||||
<a name="latestupdate">
|
||||
|
||||
## What's new in the latest update
|
||||
## What's new in the latest updates
|
||||
|
||||
### 0.6.0 <sub><sup>ALPHA1
|
||||
|
||||
- UI reworked
|
||||
- You can now load several source images (with reference faces) or set the path to the folder containing faces images
|
||||
|
||||
<img src="https://github.com/Gourieff/Assets/blob/main/sd-webui-reactor/multiple_source_images_demo_01.png?raw=true" alt="0.6.0-whatsnew-01" width="100%"/>
|
||||
|
||||
<img src="https://github.com/Gourieff/Assets/blob/main/sd-webui-reactor/multiple_source_images_demo_02.png?raw=true" alt="0.6.0-whatsnew-02" width="100%"/>
|
||||
|
||||
### 0.5.1
|
||||
|
||||
- You can now save face models as "safetensors" files (stored in `<sd-web-ui-folder>\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 `<sd-web-ui-folder>\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;
|
||||
|
||||
<img src="https://github.com/Gourieff/Assets/blob/main/sd-webui-reactor/face_model_demo_01.jpg?raw=true" alt="0.5.0-whatsnew-01" width="100%"/>
|
||||
@ -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 @@
|
||||
|
||||
<a name="colab">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.<br>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
|
||||
|
||||
|
||||
27
README_RU.md
27
README_RU.md
@ -2,7 +2,7 @@
|
||||
|
||||
<img src="https://github.com/Gourieff/Assets/raw/main/sd-webui-reactor/ReActor_logo_red.png?raw=true" alt="logo" width="180px"/>
|
||||
|
||||

|
||||

|
||||
|
||||
<a href="https://boosty.to/artgourieff" target="_blank">
|
||||
<img src="https://lovemet.ru/www/boosty.jpg" width="108" alt="Поддержать проект на Boosty"/>
|
||||
@ -37,7 +37,14 @@
|
||||
|
||||
<a name="latestupdate">
|
||||
|
||||
## Что нового в последнем обновлении
|
||||
## Что нового в последних обновлениях
|
||||
|
||||
### 0.6.0 <sub><sup>ALPHA1
|
||||
|
||||
- UI переработан
|
||||
- Появилась возможность загружать несколько исходных изображений с лицами или задавать путь к папке, содержащей такие изображения
|
||||
|
||||
<img src="https://github.com/Gourieff/Assets/blob/main/sd-webui-reactor/multiple_source_images_demo_01.png?raw=true" alt="0.6.0-whatsnew-01" width="100%"/>
|
||||
|
||||
### 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 @@
|
||||
|
||||
<a name="colab">Если вы используете [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.<br>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 устанавливает эту версию при каждом запуске.
|
||||
|
||||
|
||||
@ -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
|
||||
|
||||
4
reactor_ui/__init__.py
Normal file
4
reactor_ui/__init__.py
Normal file
@ -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
|
||||
182
reactor_ui/reactor_main_ui.py
Normal file
182
reactor_ui/reactor_main_ui.py
Normal file
@ -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("<br>", visible=show_br)
|
||||
with gr.Column(visible=True) as control_col_1:
|
||||
gr.Markdown("<center>🔽🔽🔽 Single Image has priority when both Areas in use 🔽🔽🔽</center>")
|
||||
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("<span style='display:block;text-align:right;padding-right:3px;margin: -15px 0;font-size:1.1em'><sup>Clear Hash if you see the previous face was swapped instead of the new one</sup></span>")
|
||||
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("<br>", 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("<br>", 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("<br>", 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("<br>", 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
|
||||
77
reactor_ui/reactor_settings_ui.py
Normal file
77
reactor_ui/reactor_settings_ui.py
Normal file
@ -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("<br>", 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
|
||||
25
reactor_ui/reactor_tools_ui.py
Normal file
25
reactor_ui/reactor_tools_ui.py
Normal file
@ -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],
|
||||
)
|
||||
39
reactor_ui/reactor_upscale_ui.py
Normal file
39
reactor_ui/reactor_upscale_ui.py
Normal file
@ -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("<br>", 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
|
||||
@ -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()
|
||||
|
||||
@ -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
|
||||
)
|
||||
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)
|
||||
|
||||
# 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("<br>")
|
||||
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("<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("<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("<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("<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,
|
||||
)
|
||||
|
||||
# TAB MAIN
|
||||
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("<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)"
|
||||
)
|
||||
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("<br>")
|
||||
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("<span style='display:block;text-align:right;padding:3px;font-size:0.666em'>by Eugene Gourieff</span>")
|
||||
gr.Markdown("<span style='display:block;text-align:right;padding:3px;font-size:0.666em;margin-bottom:-12px;'>by <a style='font-weight:normal' href='https://github.com/Gourieff' target='_blank'>Eugene Gourieff</a></span>")
|
||||
|
||||
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,7 +303,23 @@ 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 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"
|
||||
@ -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()
|
||||
)
|
||||
enable = gr.Checkbox(False, label="Enable", info=f"The Fast and Simple FaceSwap Extension - {version_flag}")
|
||||
|
||||
# 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("<br>")
|
||||
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"
|
||||
)
|
||||
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",
|
||||
)
|
||||
model, device, console_logging_level, source_hash_check, target_hash_check = ui_settings.show(hash_check_block=False)
|
||||
|
||||
gr.Markdown("<span style='display:block;text-align:right;padding-right:3px;font-size:0.666em;margin: -9px 0'>by Eugene Gourieff</span>")
|
||||
gr.Markdown("<span style='display:block;text-align:right;padding-right:3px;font-size:0.666em;margin: -9px 0'>by <a style='font-weight:normal' href='https://github.com/Gourieff' target='_blank'>Eugene Gourieff</a></span>")
|
||||
|
||||
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":
|
||||
@ -894,9 +526,15 @@ class FaceSwapScriptExtras(scripts_postprocessing.ScriptPostprocessing):
|
||||
|
||||
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):
|
||||
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) 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,7 +551,22 @@ 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,
|
||||
)
|
||||
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
|
||||
|
||||
@ -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):
|
||||
|
||||
@ -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]
|
||||
|
||||
@ -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"]
|
||||
@ -348,6 +366,122 @@ def swap_face(
|
||||
output_info: str = ""
|
||||
swapped = 0
|
||||
|
||||
# *****************
|
||||
# SWAP from FOLDER or MULTIPLE images:
|
||||
|
||||
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)
|
||||
|
||||
if len(source_faces_ff) > 0:
|
||||
|
||||
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)
|
||||
|
||||
for i,source_faces in enumerate(source_faces_ff):
|
||||
|
||||
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)
|
||||
|
||||
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:
|
||||
|
||||
else:
|
||||
|
||||
if select_source == 0 and source_img is not None:
|
||||
|
||||
source_img = cv2.cvtColor(np.array(source_img), cv2.COLOR_RGB2BGR)
|
||||
@ -387,8 +521,10 @@ def swap_face(
|
||||
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:
|
||||
|
||||
@ -427,6 +563,7 @@ def swap_face(
|
||||
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)
|
||||
|
||||
@ -435,8 +572,70 @@ def swap_face(
|
||||
|
||||
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:
|
||||
error_msg = "Please load an Image"
|
||||
logger.error(error_msg)
|
||||
return error_msg
|
||||
if name is None:
|
||||
error_msg = "Please filled out the 'Face Model Name' field"
|
||||
logger.error(error_msg)
|
||||
return error_msg
|
||||
apply_logging_patch(1)
|
||||
image = cv2.cvtColor(np.array(image), cv2.COLOR_RGB2BGR)
|
||||
logger.status("Building Face Model...")
|
||||
face_model = analyze_faces(image)
|
||||
if face_model is not None and len(face_model) > 0:
|
||||
face_model_path = os.path.join(FACE_MODELS_PATH, name + ".safetensors")
|
||||
save_face_model(face_model[0],face_model_path)
|
||||
logger.status("--Done!--")
|
||||
done_msg = f"Face model has been saved to '{face_model_path}'"
|
||||
logger.status(done_msg)
|
||||
return done_msg
|
||||
else:
|
||||
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)
|
||||
|
||||
@ -513,35 +712,4 @@ def swap_face(
|
||||
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
|
||||
|
||||
|
||||
def build_face_model(image: Image.Image, name: str):
|
||||
if image is None:
|
||||
error_msg = "Please load an Image"
|
||||
logger.error(error_msg)
|
||||
return error_msg
|
||||
if name is None:
|
||||
error_msg = "Please filled out the 'Face Model Name' field"
|
||||
logger.error(error_msg)
|
||||
return error_msg
|
||||
apply_logging_patch(1)
|
||||
image = cv2.cvtColor(np.array(image), cv2.COLOR_RGB2BGR)
|
||||
logger.status("Building Face Model...")
|
||||
face_model = analyze_faces(image)
|
||||
if face_model is not None and len(face_model) > 0:
|
||||
face_model_path = os.path.join(FACE_MODELS_PATH, name + ".safetensors")
|
||||
save_face_model(face_model[0],face_model_path)
|
||||
logger.status("--Done!--")
|
||||
done_msg = f"Face model has been saved to '{face_model_path}'"
|
||||
logger.status(done_msg)
|
||||
return done_msg
|
||||
else:
|
||||
no_face_msg = "No face found, please try another image"
|
||||
logger.error(no_face_msg)
|
||||
return no_face_msg
|
||||
|
||||
@ -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
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user