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"/>
|
<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">
|
<a href="https://boosty.to/artgourieff" target="_blank">
|
||||||
<img src="https://lovemet.ru/www/boosty.jpg" width="108" alt="Support Me on Boosty"/>
|
<img src="https://lovemet.ru/www/boosty.jpg" width="108" alt="Support Me on Boosty"/>
|
||||||
@ -38,11 +38,20 @@
|
|||||||
|
|
||||||
<a name="latestupdate">
|
<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
|
### 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;
|
- "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%"/>
|
<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":
|
- 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/
|
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)
|
- 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"
|
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
|
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:
|
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 "--- 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
|
* 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`
|
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`
|
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"
|
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:
|
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
|
* 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
|
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):
|
<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"
|
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
|
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"
|
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!
|
4. Enjoy!
|
||||||
|
|
||||||
@ -99,7 +108,7 @@
|
|||||||
- Ability to set the **Postprocessing order**
|
- Ability to set the **Postprocessing order**
|
||||||
- **100% compatibility** with different **SD WebUIs**: Automatic1111, SD.Next, Cagliostro Colab UI
|
- **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
|
- **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)
|
- **[API](/API.md) support**: both SD WebUI built-in and external (via POST/GET requests)
|
||||||
- **ComfyUI [support](https://github.com/Gourieff/comfyui-reactor-node)**
|
- **ComfyUI [support](https://github.com/Gourieff/comfyui-reactor-node)**
|
||||||
- **Mac M1/M2 [support](https://github.com/Gourieff/sd-webui-reactor/issues/42)**
|
- **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:
|
7. Then one-by-one:
|
||||||
- `pip install insightface==0.7.3`
|
- `pip install insightface==0.7.3`
|
||||||
- `pip install onnx`
|
- `pip install onnx`
|
||||||
- `pip install onnxruntime-gpu>=1.16.1`
|
- `pip install "onnxruntime-gpu>=1.16.1"`
|
||||||
- `pip install opencv-python`
|
- `pip install opencv-python`
|
||||||
- `pip install tqdm`
|
- `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.
|
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:
|
4. Then:
|
||||||
- `python -m pip install -U pip`
|
- `python -m pip install -U pip`
|
||||||
- `pip uninstall -y onnxruntime onnxruntime-gpu onnxruntime-silicon onnxruntime-extensions`
|
- `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!
|
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:
|
5. Then:
|
||||||
- `python -m pip install -U pip`
|
- `python -m pip install -U pip`
|
||||||
- `pip uninstall protobuf`
|
- `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
|
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"/>
|
<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">
|
<a href="https://boosty.to/artgourieff" target="_blank">
|
||||||
<img src="https://lovemet.ru/www/boosty.jpg" width="108" alt="Поддержать проект на Boosty"/>
|
<img src="https://lovemet.ru/www/boosty.jpg" width="108" alt="Поддержать проект на Boosty"/>
|
||||||
@ -37,7 +37,14 @@
|
|||||||
|
|
||||||
<a name="latestupdate">
|
<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
|
### 0.5.1
|
||||||
|
|
||||||
@ -60,8 +67,8 @@
|
|||||||
- ИЛИ только **VS C++ Build Tools** (если вам не нужен весь пакет Visual Studio), выберите "Desktop Development with C++" в разделе "Workloads -> Desktop & Mobile":
|
- ИЛИ только **VS C++ Build Tools** (если вам не нужен весь пакет Visual Studio), выберите "Desktop Development with C++" в разделе "Workloads -> Desktop & Mobile":
|
||||||
https://visualstudio.microsoft.com/visual-cpp-build-tools/
|
https://visualstudio.microsoft.com/visual-cpp-build-tools/
|
||||||
- ИЛИ если же вы не хотите устанавливать что-либо из вышеуказанного - выполните [следующие шаги (пункт VIII)](#insightfacebuild)
|
- ИЛИ если же вы не хотите устанавливать что-либо из вышеуказанного - выполните [следующие шаги (пункт VIII)](#insightfacebuild)
|
||||||
2. Внутри SD Web-UI перейдите во вкладку "Extensions" и вставьте ссылку `https://github.com/Gourieff/sd-webui-reactor` в "Install from URL" и нажмите "Install"
|
2. Внутри SD Web-UI перейдите во вкладку "Extensions", загрузите список доступных расширений (вкладка "Available") и введите "ReActor" в строке поиска или же вставьте ссылку `https://github.com/Gourieff/sd-webui-reactor` в "Install from URL" - и нажмите "Install"
|
||||||
3. Пожалуйста, подождите несколько минут, пока процесс установки полностью не завершится
|
3. Пожалуйста, подождите несколько минут, пока процесс установки полностью не завершится (наберитесь терпения, не прерывайте процесс)
|
||||||
4. Проверьте последнее сообщение в консоли SD-WebUI:
|
4. Проверьте последнее сообщение в консоли SD-WebUI:
|
||||||
* Если вы видите "--- PLEASE, RESTART the Server! ---" - остановите Сервер (CTRL+C или CMD+C) и запустите его заново - ИЛИ же перейдите во вкладку "Installed", нажмите "Apply and restart UI"
|
* Если вы видите "--- PLEASE, RESTART the Server! ---" - остановите Сервер (CTRL+C или CMD+C) и запустите его заново - ИЛИ же перейдите во вкладку "Installed", нажмите "Apply and restart UI"
|
||||||
* Если вы видите "Done!", просто перезагрузите UI, нажав на "Reload UI"
|
* Если вы видите "Done!", просто перезагрузите UI, нажав на "Reload UI"
|
||||||
@ -74,7 +81,7 @@
|
|||||||
3. Перейдите в (Windows)`automatic\venv\Scripts` или (MacOS/Linux)`automatic/venv/bin`, запустите Терминал или Консоль (cmd) для данной папки и выполните `activate`
|
3. Перейдите в (Windows)`automatic\venv\Scripts` или (MacOS/Linux)`automatic/venv/bin`, запустите Терминал или Консоль (cmd) для данной папки и выполните `activate`
|
||||||
4. Выполните `pip install insightface==0.7.3`
|
4. Выполните `pip install insightface==0.7.3`
|
||||||
5. Запустите SD.Next, перейдите во вкладку "Extensions", вставьте эту ссылку `https://github.com/Gourieff/sd-webui-reactor` в "Install from URL" и нажмите "Install"
|
5. Запустите SD.Next, перейдите во вкладку "Extensions", вставьте эту ссылку `https://github.com/Gourieff/sd-webui-reactor` в "Install from URL" и нажмите "Install"
|
||||||
6. Пожалуйста, подождите несколько минут, пока процесс установки полностью не завершится
|
6. Пожалуйста, подождите несколько минут, пока процесс установки полностью не завершится (наберитесь терпения, не прерывайте процесс)
|
||||||
7. Проверьте последнее сообщение в консоли SD.Next:
|
7. Проверьте последнее сообщение в консоли SD.Next:
|
||||||
* Если вы видите "--- PLEASE, RESTART the Server! ---" - остановите Сервер (CTRL+C или CMD+C) или просто закройте консоль
|
* Если вы видите "--- PLEASE, RESTART the Server! ---" - остановите Сервер (CTRL+C или CMD+C) или просто закройте консоль
|
||||||
8. Перейдите в директорию `automatic\extensions\sd-webui-reactor` - если вы видите там папку `models\insightface` с файлом `inswapper_128.onnx` внутри, переместите его в папку `automatic\models\insightface`
|
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):
|
<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"
|
1. В активном WebUI перейдите во вкладку "Extensions", загрузите список доступных расширений (вкладка "Available") и введите "ReActor" в строке поиска или же вставьте ссылку `https://github.com/Gourieff/sd-webui-reactor` в "Install from URL" - и нажмите "Install"
|
||||||
2. Пожалуйста, подождите некоторое время, пока процесс установки полностью не завершится
|
2. Пожалуйста, подождите некоторое время, пока процесс установки полностью не завершится (наберитесь терпения, не прерывайте процесс)
|
||||||
3. Когда вы увидите сообщение "--- PLEASE, RESTART the Server! ---" (в секции "Start UI" вашего ноутбука "Start Cagliostro Colab UI") - перейдите во вкладку "Installed" и нажмите "Apply and restart UI"
|
3. Когда вы увидите сообщение "--- PLEASE, RESTART the Server! ---" (в секции "Start UI" вашего ноутбука "Start Cagliostro Colab UI") - перейдите во вкладку "Installed" и нажмите "Apply and restart UI"
|
||||||
4. Готово!
|
4. Готово!
|
||||||
|
|
||||||
@ -198,7 +205,7 @@ Inpainting также работает, но замена лица происх
|
|||||||
7. Далее:
|
7. Далее:
|
||||||
- `pip install insightface==0.7.3`
|
- `pip install insightface==0.7.3`
|
||||||
- `pip install onnx`
|
- `pip install onnx`
|
||||||
- `pip install onnxruntime-gpu>=1.16.1`
|
- `pip install "onnxruntime-gpu>=1.16.1"`
|
||||||
- `pip install opencv-python`
|
- `pip install opencv-python`
|
||||||
- `pip install tqdm`
|
- `pip install tqdm`
|
||||||
8. Выполните `deactivate`, закройте Терминал или Консоль и запустите SD WebUI, ReActor должен запуститься без к-л проблем - если же нет, добро пожаловать в раздел "Issues".
|
8. Выполните `deactivate`, закройте Терминал или Консоль и запустите SD WebUI, ReActor должен запуститься без к-л проблем - если же нет, добро пожаловать в раздел "Issues".
|
||||||
@ -233,7 +240,7 @@ Inpainting также работает, но замена лица происх
|
|||||||
4. Затем:
|
4. Затем:
|
||||||
- `python -m pip install -U pip`
|
- `python -m pip install -U pip`
|
||||||
- `pip uninstall -y onnxruntime onnxruntime-gpu onnxruntime-silicon onnxruntime-extensions`
|
- `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 - не устанавливайте её!
|
Если это не помогло - значит какое-то другое расширение переустанавливает `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. Затем:
|
5. Затем:
|
||||||
- `python -m pip install -U pip`
|
- `python -m pip install -U pip`
|
||||||
- `pip uninstall protobuf`
|
- `pip uninstall protobuf`
|
||||||
- `pip install protobuf>=3.20.3`
|
- `pip install "protobuf>=3.20.3"`
|
||||||
|
|
||||||
Если это не помгло - значит, есть к-л другое расширение, которое использует неподходящую версию пакета protobuf, и SD WebUI устанавливает эту версию при каждом запуске.
|
Если это не помгло - значит, есть к-л другое расширение, которое использует неподходящую версию пакета protobuf, и SD WebUI устанавливает эту версию при каждом запуске.
|
||||||
|
|
||||||
|
|||||||
@ -44,8 +44,9 @@ args=[
|
|||||||
False, #19 Target Image Hash Check, False - by default
|
False, #19 Target Image Hash Check, False - by default
|
||||||
"CUDA", #20 CPU or CUDA (if you have it), CPU - by default
|
"CUDA", #20 CPU or CUDA (if you have it), CPU - by default
|
||||||
True, #21 Face Mask Correction
|
True, #21 Face Mask Correction
|
||||||
1, #22 Select Source, 0 - Image, 1 - Face Model
|
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
|
"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
|
# 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"),
|
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)"),
|
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"),
|
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"),
|
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")
|
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)
|
s_image = api.decode_base64_to_image(source_image)
|
||||||
t_image = api.decode_base64_to_image(target_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)
|
use_model = get_full_model(model)
|
||||||
if use_model is None:
|
if use_model is None:
|
||||||
Exception("Model not found")
|
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 save_to_file == 1:
|
||||||
if result_file_path == "":
|
if result_file_path == "":
|
||||||
result_file_path = default_file_path()
|
result_file_path = default_file_path()
|
||||||
|
|||||||
@ -1,11 +1,6 @@
|
|||||||
import os, glob
|
import os, glob
|
||||||
import gradio as gr
|
import gradio as gr
|
||||||
from PIL import Image
|
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
|
from typing import List
|
||||||
|
|
||||||
@ -19,43 +14,22 @@ from modules.processing import (
|
|||||||
)
|
)
|
||||||
from modules.face_restoration import FaceRestoration
|
from modules.face_restoration import FaceRestoration
|
||||||
from modules.images import save_image
|
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_logger import logger
|
||||||
from scripts.reactor_swapper import (
|
from scripts.reactor_swapper import (
|
||||||
EnhancementOptions,
|
EnhancementOptions,
|
||||||
swap_face,
|
swap_face,
|
||||||
check_process_halt,
|
check_process_halt,
|
||||||
reset_messaged,
|
reset_messaged,
|
||||||
build_face_model
|
|
||||||
)
|
)
|
||||||
from scripts.reactor_version import version_flag, app_title
|
from scripts.reactor_version import version_flag, app_title
|
||||||
from scripts.console_log_patch import apply_logging_patch
|
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_helpers import (
|
||||||
from scripts.reactor_globals import DEVICE, DEVICE_LIST
|
make_grid,
|
||||||
|
set_Device,
|
||||||
|
)
|
||||||
MODELS_PATH = None
|
from scripts.reactor_globals import SWAPPER_MODELS_PATH #, DEVICE, DEVICE_LIST
|
||||||
|
|
||||||
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
|
|
||||||
|
|
||||||
|
|
||||||
class FaceSwapScript(scripts.Script):
|
class FaceSwapScript(scripts.Script):
|
||||||
@ -68,237 +42,33 @@ class FaceSwapScript(scripts.Script):
|
|||||||
def ui(self, is_img2img):
|
def ui(self, is_img2img):
|
||||||
with gr.Accordion(f"{app_title}", open=False):
|
with gr.Accordion(f"{app_title}", open=False):
|
||||||
|
|
||||||
def update_fm_list(selected: str):
|
# def on_files_upload_uncheck_so(selected: bool):
|
||||||
return gr.Dropdown.update(
|
# global SAVE_ORIGINAL
|
||||||
value=selected, choices=get_model_names(get_facemodels)
|
# SAVE_ORIGINAL = selected
|
||||||
)
|
# return gr.Checkbox.update(value=False,visible=False)
|
||||||
def update_upscalers_list(selected: str):
|
# def on_files_clear():
|
||||||
return gr.Dropdown.update(
|
# clear_faces_list()
|
||||||
value=selected, choices=[upscaler.name for upscaler in shared.sd_upscalers]
|
# return gr.Checkbox.update(value=SAVE_ORIGINAL,visible=True)
|
||||||
)
|
|
||||||
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}")
|
||||||
|
gr.Markdown("<br>")
|
||||||
|
|
||||||
# TAB MAIN
|
# TAB MAIN
|
||||||
with gr.Tab("Main"):
|
msgs: dict = {
|
||||||
with gr.Column():
|
"extra_multiple_source": "",
|
||||||
img = gr.Image(
|
}
|
||||||
type="pil",
|
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)
|
||||||
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 UPSCALE
|
# TAB UPSCALE
|
||||||
with gr.Tab("Upscale"):
|
restore_first, upscaler_name, upscaler_scale, upscaler_visibility = ui_upscale.show()
|
||||||
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)"
|
|
||||||
)
|
|
||||||
|
|
||||||
# TAB TOOLS
|
# TAB TOOLS
|
||||||
with gr.Tab("Tools 🆕"):
|
ui_tools.show()
|
||||||
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],
|
|
||||||
)
|
|
||||||
|
|
||||||
# TAB SETTINGS
|
# TAB SETTINGS
|
||||||
with gr.Tab("Settings"):
|
model, device, console_logging_level, source_hash_check, target_hash_check = ui_settings.show()
|
||||||
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."
|
|
||||||
)
|
|
||||||
|
|
||||||
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 [
|
return [
|
||||||
img,
|
img,
|
||||||
@ -325,6 +95,8 @@ class FaceSwapScript(scripts.Script):
|
|||||||
mask_face,
|
mask_face,
|
||||||
select_source,
|
select_source,
|
||||||
face_model,
|
face_model,
|
||||||
|
source_folder,
|
||||||
|
imgs,
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
@ -381,6 +153,8 @@ class FaceSwapScript(scripts.Script):
|
|||||||
mask_face,
|
mask_face,
|
||||||
select_source,
|
select_source,
|
||||||
face_model,
|
face_model,
|
||||||
|
source_folder,
|
||||||
|
imgs,
|
||||||
):
|
):
|
||||||
self.enable = enable
|
self.enable = enable
|
||||||
if self.enable:
|
if self.enable:
|
||||||
@ -391,7 +165,7 @@ class FaceSwapScript(scripts.Script):
|
|||||||
if check_process_halt():
|
if check_process_halt():
|
||||||
return
|
return
|
||||||
|
|
||||||
global MODELS_PATH
|
global SWAPPER_MODELS_PATH
|
||||||
self.source = img
|
self.source = img
|
||||||
self.face_restorer_name = face_restorer_name
|
self.face_restorer_name = face_restorer_name
|
||||||
self.upscaler_scale = upscaler_scale
|
self.upscaler_scale = upscaler_scale
|
||||||
@ -401,7 +175,7 @@ class FaceSwapScript(scripts.Script):
|
|||||||
self.upscaler_name = upscaler_name
|
self.upscaler_name = upscaler_name
|
||||||
self.swap_in_source = swap_in_source
|
self.swap_in_source = swap_in_source
|
||||||
self.swap_in_generated = swap_in_generated
|
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.console_logging_level = console_logging_level
|
||||||
self.gender_source = gender_source
|
self.gender_source = gender_source
|
||||||
self.gender_target = gender_target
|
self.gender_target = gender_target
|
||||||
@ -413,6 +187,8 @@ class FaceSwapScript(scripts.Script):
|
|||||||
self.mask_face = mask_face
|
self.mask_face = mask_face
|
||||||
self.select_source = select_source
|
self.select_source = select_source
|
||||||
self.face_model = face_model
|
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":
|
if self.gender_source is None or self.gender_source == "No":
|
||||||
self.gender_source = 0
|
self.gender_source = 0
|
||||||
if self.gender_target is None or self.gender_target == "No":
|
if self.gender_target is None or self.gender_target == "No":
|
||||||
@ -439,7 +215,7 @@ class FaceSwapScript(scripts.Script):
|
|||||||
logger.debug("*** Set Device")
|
logger.debug("*** Set Device")
|
||||||
set_Device(self.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")
|
logger.debug("*** Log patch")
|
||||||
apply_logging_patch(console_logging_level)
|
apply_logging_patch(console_logging_level)
|
||||||
if isinstance(p, StableDiffusionProcessingImg2Img) and self.swap_in_source:
|
if isinstance(p, StableDiffusionProcessingImg2Img) and self.swap_in_source:
|
||||||
@ -463,6 +239,8 @@ class FaceSwapScript(scripts.Script):
|
|||||||
mask_face=self.mask_face,
|
mask_face=self.mask_face,
|
||||||
select_source=self.select_source,
|
select_source=self.select_source,
|
||||||
face_model = self.face_model,
|
face_model = self.face_model,
|
||||||
|
source_folder = None,
|
||||||
|
source_imgs = None,
|
||||||
)
|
)
|
||||||
p.init_images[i] = result
|
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")
|
# 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():
|
if check_process_halt():
|
||||||
return
|
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
|
postprocess_run: bool = True
|
||||||
|
|
||||||
@ -497,8 +275,13 @@ class FaceSwapScript(scripts.Script):
|
|||||||
# result_info: List = processed.infotexts
|
# result_info: List = processed.infotexts
|
||||||
|
|
||||||
if self.swap_in_generated:
|
if self.swap_in_generated:
|
||||||
|
|
||||||
logger.status("Working: source face index %s, target face index %s", self.source_faces_index, self.faces_index)
|
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)):
|
for i,(img,info) in enumerate(zip(orig_images, orig_infotexts)):
|
||||||
if check_process_halt():
|
if check_process_halt():
|
||||||
postprocess_run = False
|
postprocess_run = False
|
||||||
@ -520,16 +303,32 @@ class FaceSwapScript(scripts.Script):
|
|||||||
mask_face=self.mask_face,
|
mask_face=self.mask_face,
|
||||||
select_source=self.select_source,
|
select_source=self.select_source,
|
||||||
face_model = self.face_model,
|
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)
|
if self.select_source == 2 or (self.select_source == 0 and self.source_imgs is not None and self.source is None):
|
||||||
suffix = "-swapped"
|
if len(result) > 0 and swapped > 0:
|
||||||
try:
|
result_images.extend(result)
|
||||||
img_path = save_image(result, p.outpath_samples, "", p.all_seeds[0], p.all_prompts[0], "png",info=info, p=p, suffix=suffix)
|
suffix = "-swapped"
|
||||||
except:
|
for i,x in enumerate(result):
|
||||||
logger.error("Cannot save a result image - please, check SD WebUI Settings (Saving and Paths)")
|
try:
|
||||||
elif result is None:
|
img_path = save_image(result[i], p.outpath_samples, "", p.all_seeds[0], p.all_prompts[0], "png",info=info, p=p, suffix=suffix)
|
||||||
logger.error("Cannot create a result image")
|
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:
|
# if len(output) != 0:
|
||||||
# split_fullfn = os.path.splitext(img_path[0])
|
# split_fullfn = os.path.splitext(img_path[0])
|
||||||
@ -554,7 +353,7 @@ class FaceSwapScript(scripts.Script):
|
|||||||
images = kwargs["images"]
|
images = kwargs["images"]
|
||||||
|
|
||||||
def postprocess_image(self, p, script_pp: scripts.PostprocessImageArgs, *args):
|
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")
|
logger.debug("*** Check postprocess_image")
|
||||||
|
|
||||||
@ -583,6 +382,8 @@ class FaceSwapScript(scripts.Script):
|
|||||||
mask_face=self.mask_face,
|
mask_face=self.mask_face,
|
||||||
select_source=self.select_source,
|
select_source=self.select_source,
|
||||||
face_model = self.face_model,
|
face_model = self.face_model,
|
||||||
|
source_folder = None,
|
||||||
|
source_imgs = None,
|
||||||
)
|
)
|
||||||
try:
|
try:
|
||||||
pp = scripts_postprocessing.PostprocessedImage(result)
|
pp = scripts_postprocessing.PostprocessedImage(result)
|
||||||
@ -606,197 +407,24 @@ class FaceSwapScriptExtras(scripts_postprocessing.ScriptPostprocessing):
|
|||||||
def ui(self):
|
def ui(self):
|
||||||
with gr.Accordion(f"{app_title}", open=False):
|
with gr.Accordion(f"{app_title}", open=False):
|
||||||
|
|
||||||
def update_fm_list(selected: str):
|
enable = gr.Checkbox(False, label="Enable", info=f"The Fast and Simple FaceSwap Extension - {version_flag}")
|
||||||
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("<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"
|
|
||||||
)
|
|
||||||
|
|
||||||
|
# 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
|
# TAB UPSCALE
|
||||||
with gr.Tab("Upscale"):
|
restore_first, upscaler_name, upscaler_scale, upscaler_visibility = ui_upscale.show(show_br=False)
|
||||||
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)"
|
|
||||||
)
|
|
||||||
|
|
||||||
# TAB TOOLS
|
# TAB TOOLS
|
||||||
with gr.Tab("Tools 🆕"):
|
ui_tools.show()
|
||||||
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],
|
|
||||||
)
|
|
||||||
|
|
||||||
# TAB SETTINGS
|
# TAB SETTINGS
|
||||||
with gr.Tab("Settings"):
|
model, device, console_logging_level, source_hash_check, target_hash_check = ui_settings.show(hash_check_block=False)
|
||||||
models = get_models()
|
|
||||||
with gr.Row(visible=EP_is_visible):
|
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>")
|
||||||
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("<span style='display:block;text-align:right;padding-right:3px;font-size:0.666em;margin: -9px 0'>by Eugene Gourieff</span>")
|
|
||||||
|
|
||||||
args = {
|
args = {
|
||||||
'img': img,
|
'img': img,
|
||||||
@ -818,6 +446,8 @@ class FaceSwapScriptExtras(scripts_postprocessing.ScriptPostprocessing):
|
|||||||
'mask_face': mask_face,
|
'mask_face': mask_face,
|
||||||
'select_source': select_source,
|
'select_source': select_source,
|
||||||
'face_model': face_model,
|
'face_model': face_model,
|
||||||
|
'source_folder': source_folder,
|
||||||
|
'imgs': imgs,
|
||||||
}
|
}
|
||||||
return args
|
return args
|
||||||
|
|
||||||
@ -853,7 +483,7 @@ class FaceSwapScriptExtras(scripts_postprocessing.ScriptPostprocessing):
|
|||||||
if check_process_halt():
|
if check_process_halt():
|
||||||
return
|
return
|
||||||
|
|
||||||
global MODELS_PATH
|
global SWAPPER_MODELS_PATH
|
||||||
self.source = args['img']
|
self.source = args['img']
|
||||||
self.face_restorer_name = args['face_restorer_name']
|
self.face_restorer_name = args['face_restorer_name']
|
||||||
self.upscaler_scale = args['upscaler_scale']
|
self.upscaler_scale = args['upscaler_scale']
|
||||||
@ -861,7 +491,7 @@ class FaceSwapScriptExtras(scripts_postprocessing.ScriptPostprocessing):
|
|||||||
self.face_restorer_visibility = args['face_restorer_visibility']
|
self.face_restorer_visibility = args['face_restorer_visibility']
|
||||||
self.restore_first = args['restore_first']
|
self.restore_first = args['restore_first']
|
||||||
self.upscaler_name = args['upscaler_name']
|
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.console_logging_level = args['console_logging_level']
|
||||||
self.gender_source = args['gender_source']
|
self.gender_source = args['gender_source']
|
||||||
self.gender_target = args['gender_target']
|
self.gender_target = args['gender_target']
|
||||||
@ -870,6 +500,8 @@ class FaceSwapScriptExtras(scripts_postprocessing.ScriptPostprocessing):
|
|||||||
self.mask_face = args['mask_face']
|
self.mask_face = args['mask_face']
|
||||||
self.select_source = args['select_source']
|
self.select_source = args['select_source']
|
||||||
self.face_model = args['face_model']
|
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":
|
if self.gender_source is None or self.gender_source == "No":
|
||||||
self.gender_source = 0
|
self.gender_source = 0
|
||||||
if self.gender_target is None or self.gender_target == "No":
|
if self.gender_target is None or self.gender_target == "No":
|
||||||
@ -893,10 +525,16 @@ class FaceSwapScriptExtras(scripts_postprocessing.ScriptPostprocessing):
|
|||||||
reset_messaged()
|
reset_messaged()
|
||||||
|
|
||||||
set_Device(self.device)
|
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)
|
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)
|
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
|
image: Image.Image = pp.image
|
||||||
result, output, swapped = swap_face(
|
result, output, swapped = swap_face(
|
||||||
self.source,
|
self.source,
|
||||||
@ -913,12 +551,27 @@ class FaceSwapScriptExtras(scripts_postprocessing.ScriptPostprocessing):
|
|||||||
mask_face=self.mask_face,
|
mask_face=self.mask_face,
|
||||||
select_source=self.select_source,
|
select_source=self.select_source,
|
||||||
face_model=self.face_model,
|
face_model=self.face_model,
|
||||||
|
source_folder=self.source_folder,
|
||||||
|
source_imgs=self.source_imgs,
|
||||||
)
|
)
|
||||||
try:
|
if self.select_source == 2 or (self.select_source == 0 and self.source_imgs is not None and self.source is None):
|
||||||
pp.info["ReActor"] = True
|
if len(result) > 0 and swapped > 0:
|
||||||
pp.image = result
|
image = result[0]
|
||||||
logger.status("---Done!---")
|
if len(result) > 1:
|
||||||
except Exception:
|
grid = make_grid(result)
|
||||||
logger.error("Cannot create a result image")
|
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:
|
else:
|
||||||
logger.error("Please provide a source face")
|
logger.error("Please provide a source face")
|
||||||
|
|||||||
@ -14,7 +14,8 @@ BASE_PATH = os.path.join(Path(__file__).parents[1])
|
|||||||
DEVICE_LIST: list = ["CPU", "CUDA"]
|
DEVICE_LIST: list = ["CPU", "CUDA"]
|
||||||
|
|
||||||
MODELS_PATH = models_path
|
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")
|
FACE_MODELS_PATH = os.path.join(REACTOR_MODELS_PATH, "faces")
|
||||||
|
|
||||||
if not os.path.exists(REACTOR_MODELS_PATH):
|
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 modules import shared, script_callbacks
|
||||||
from scripts.reactor_globals import DEVICE, BASE_PATH, FACE_MODELS_PATH
|
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):
|
def set_Device(value):
|
||||||
global DEVICE
|
global DEVICE
|
||||||
DEVICE = value
|
DEVICE = value
|
||||||
@ -155,6 +165,20 @@ def save_face_model(face: Face, filename: str) -> None:
|
|||||||
except Exception as e:
|
except Exception as e:
|
||||||
print(f"Error: {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):
|
def load_face_model(filename: str):
|
||||||
face = {}
|
face = {}
|
||||||
model_path = os.path.join(FACE_MODELS_PATH, filename)
|
model_path = os.path.join(FACE_MODELS_PATH, filename)
|
||||||
@ -175,3 +199,11 @@ def get_model_names(get_models):
|
|||||||
for x in models:
|
for x in models:
|
||||||
names.append(os.path.basename(x))
|
names.append(os.path.basename(x))
|
||||||
return names
|
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 insightface.app.common import Face
|
||||||
|
|
||||||
from scripts.reactor_globals import FACE_MODELS_PATH
|
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 scripts.console_log_patch import apply_logging_patch
|
||||||
|
|
||||||
from modules.face_restoration import FaceRestoration
|
from modules.face_restoration import FaceRestoration
|
||||||
@ -22,7 +29,7 @@ except: # SD.Next
|
|||||||
from modules.upscaler import UpscalerData
|
from modules.upscaler import UpscalerData
|
||||||
from modules.shared import state
|
from modules.shared import state
|
||||||
from scripts.reactor_logger import logger
|
from scripts.reactor_logger import logger
|
||||||
from modules.reactor_mask import apply_face_mask
|
from reactor_modules.reactor_mask import apply_face_mask
|
||||||
|
|
||||||
try:
|
try:
|
||||||
from modules.paths_internal import models_path
|
from modules.paths_internal import models_path
|
||||||
@ -92,6 +99,14 @@ SOURCE_FACES = None
|
|||||||
SOURCE_IMAGE_HASH = None
|
SOURCE_IMAGE_HASH = None
|
||||||
TARGET_FACES = None
|
TARGET_FACES = None
|
||||||
TARGET_IMAGE_HASH = 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():
|
def getAnalysisModel():
|
||||||
@ -315,8 +330,11 @@ def swap_face(
|
|||||||
mask_face: bool = False,
|
mask_face: bool = False,
|
||||||
select_source: int = 0,
|
select_source: int = 0,
|
||||||
face_model: str = "None",
|
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
|
result_image = target_img
|
||||||
|
|
||||||
PROVIDERS = ["CUDAExecutionProvider"] if device == "CUDA" else ["CPUExecutionProvider"]
|
PROVIDERS = ["CUDAExecutionProvider"] if device == "CUDA" else ["CPUExecutionProvider"]
|
||||||
@ -347,179 +365,229 @@ def swap_face(
|
|||||||
output: List = []
|
output: List = []
|
||||||
output_info: str = ""
|
output_info: str = ""
|
||||||
swapped = 0
|
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:
|
if target_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_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...")
|
logger.status("Analyzing Target Image...")
|
||||||
target_faces = analyze_faces(target_img)
|
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])
|
for i,source_faces in enumerate(source_faces_ff):
|
||||||
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"
|
logger.status("(Image %s) Detecting Source Face, Index = %s", i, source_faces_index[0])
|
||||||
output.append(output_info)
|
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 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}")
|
|
||||||
|
|
||||||
elif wrong_gender == 1:
|
if source_age != "None" or source_gender != "None":
|
||||||
wrong_gender = 0
|
logger.status("(Image %s) Detected: -%s- y.o. %s", i, source_age, source_gender)
|
||||||
|
|
||||||
if source_face_idx == len(source_faces_index):
|
if len(source_faces_index) != 0 and len(source_faces_index) != 1 and len(source_faces_index) != len(faces_index):
|
||||||
result_image = Image.fromarray(cv2.cvtColor(result, cv2.COLOR_BGR2RGB))
|
logger.status("Source Faces must have no entries (default=0), one entry, or same number of entries as target faces.")
|
||||||
|
|
||||||
if enhancement_options is not None and len(source_faces_index) > 1:
|
elif source_face is not None:
|
||||||
result_image = enhance_image(result_image, enhancement_options)
|
|
||||||
|
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)
|
||||||
return result_image, output, swapped
|
|
||||||
|
result.append(result_image)
|
||||||
else:
|
|
||||||
logger.status(f"No source face found for face number {source_face_idx}.")
|
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:
|
else:
|
||||||
logger.status("No source face(s) found")
|
|
||||||
|
if select_source == 0 and source_img is not None:
|
||||||
return result_image, output, swapped
|
|
||||||
|
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):
|
def build_face_model(image: Image.Image, name: str):
|
||||||
if image is None:
|
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"
|
no_face_msg = "No face found, please try another image"
|
||||||
logger.error(no_face_msg)
|
logger.error(no_face_msg)
|
||||||
return 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
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
app_title = "ReActor"
|
app_title = "ReActor"
|
||||||
version_flag = "v0.5.1"
|
version_flag = "v0.6.0-a1"
|
||||||
|
|
||||||
from scripts.reactor_logger import logger, get_Run, set_Run
|
from scripts.reactor_logger import logger, get_Run, set_Run
|
||||||
|
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user