diff --git a/.gitignore b/.gitignore index 792ad62..17b7059 100644 --- a/.gitignore +++ b/.gitignore @@ -7,3 +7,4 @@ __pycache__/ example toDo.txt +last_device.txt diff --git a/README.md b/README.md index 1925068..1535b1e 100644 --- a/README.md +++ b/README.md @@ -1,32 +1,9 @@
- logo + logo - ![Version](https://img.shields.io/badge/version-0.5.0_alpha1-lightgreen?style=for-the-badge&labelColor=darkgreen) + ![Version](https://img.shields.io/badge/version-0.5.0_alpha2-lightgreen?style=for-the-badge&labelColor=darkgreen) - - - - - -
- - for Any GPU - -
- - NVIDIA / AMD / Intel - -
- - for NVIDIA GPU -
- - 8Gb VRAM or more - -
-
- Buy Me a Coffee at ko-fi.com
@@ -41,15 +18,9 @@ # ReActor for Stable Diffusion -
- -### The Fast and Simple FaceSwap Extension with a lot of improvements and without NSFW filter (uncensored, use it on your own [responsibility](#disclaimer)) - -> Ex "Roop-GE" (GE - Gourieff Edition, aka "NSFW-Roop"), the extension was renamed with the version 0.3.0
-> Repository old link: `https://github.com/Gourieff/sd-webui-roop-nsfw` +### The Fast and Simple FaceSwap Extension with a lot of improvements and without NSFW filter (uncensored, use it on your own [responsibility](#disclaimer)) --- -
Installation | Features | Usage | API | Troubleshooting | Updating | ComfyUI | Disclaimer @@ -83,7 +54,7 @@ -example +example ## Installation @@ -100,8 +71,8 @@ 2. In web-ui, go to the "Extensions" tab and use this URL `https://github.com/Gourieff/sd-webui-reactor` in the "Install from URL" tab and click "Install" 3. Please, wait for several minutes until the installation process will be finished 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 (*if you have any other Roop-based extension enabled - disable it, otherwise this extension won't work*), click "Apply and restart UI" -* If you see the message "Done!", just go to the "Installed" tab (*if you have any other Roop-based extension enabled - disable it, otherwise this extension won't work*), click "Apply and restart UI" - or you can just simply reload the 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 5. Enjoy! If you use [SD.Next](https://github.com/vladmandic/automatic): @@ -113,15 +84,15 @@ 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 7. Check the last message in your SD.Next 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 (*if you have any other Roop-based extension enabled - disable it, otherwise this extension won't work*), click "Restart the UI" -8. Stop SD.Next, 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 +* 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 9. Run your SD.Next WebUI and enjoy! If you use [Cagliostro Colab UI](https://github.com/Linaqruf/sd-notebook-collection): 1. In active WebUI, go to the "Extensions" tab and use this URL `https://github.com/Gourieff/sd-webui-reactor` in the "Install from URL" tab and click "Install" 2. Please, wait for several minutes until the installation process will be finished -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" (*if you have any other Roop-based extension enabled - disable it before restart, otherwise this extension won't work*) +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! ## Features @@ -135,6 +106,7 @@ - Ability to set the **Postprocessing order** - **100% compatibility** with different **SD WebUIs**: Automatic1111, SD.Next, Cagliostro Colab UI - **Fast performance** even with CPU, ReActor for SD WebUI is absolutely not picky about how powerful your GPU is +- **CUDA** acceleration support from the version 0.5.0 - **[API](/API.md) support**: both SD WebUI built-in and external (via POST/GET requests) - **ComfyUI [support](https://github.com/Gourieff/comfyui-reactor-node)** - **Mac M1/M2 [support](https://github.com/Gourieff/sd-webui-reactor/issues/42)** @@ -149,7 +121,7 @@ 2. Turn on the "Enable" checkbox; 3. That's it, now the generated result will have the face you selected. -example +example ### Face Indexes @@ -172,18 +144,18 @@ ReActor will swap a face only if it meets the given condition. ### The result face is blurry Use the "Restore Face" option. You can also try the "Upscaler" option or for more finer control, use an upscaler from the "Extras" tab. You can also set the postproduction order (from 0.1.0 version): -example +example *The old logic was the opposite (Upscale -> then Restore), resulting in worse face quality (and big texture differences) after upscaling.* ### There are multiple faces in result Select the face numbers you wish to swap using the "Comma separated face number(s)" option for swap-source and result images. You can use different index order. -example +example ### ~~The result is totally black~~ ~~This means NSFW filter detected that your image is NSFW.~~ -IamSFW +IamSFW ### Img2Img @@ -191,21 +163,11 @@ You can choose to activate the swap on the source image or on the generated imag ReActor works with Inpainting - but only the masked part will be swapped.
Please use with the "Only masked" option for "Inpaint area" if you enabled "Upscaler". Otherwise use the upscale option via the Extras tab or via the Script loader (below the screen) with "SD upscale" or "Ultimate SD upscale". -### ReActor + FaceSwapLab inside one enclosure +### Extras Tab -If you have troubles running both extensions together, try the following: +From the version 0.5.0 you can use ReActor via the Extras Tab. It gives a superfast perfomance and ability to swap face2image avoiding SD pipeline that can cause smushing of original image's details -**for NVIDIA GPU with VRAM > 6Gb:** -
    Just use [ReActor Force](https://github.com/Gourieff/sd-webui-reactor-force) - -**for any other GPU**: -1. Delete FaceSwapLab from the extensions folder -2. Delete `onnxruntime` and `onnxruntime-gpu` folders from the site-packages directory (inside VENV Lib) -3. Run SD WebUI and let ReActor to install `onnxruntime`, close SD WebUI -4. `git clone https://github.com/glucauze/sd-webui-faceswaplab` into the SD WebUI extensions folder -5. Edit `sd-webui-faceswaplab/install.py` file: - - Line 9: `use_gpu = True` -> `use_gpu = False` -6. Run SD WebUI, both extensions must work fine after that +IamSFW ## API @@ -236,7 +198,7 @@ Please, check the path where "inswapper_128.onnx" model is stored. It must be in 7. Then one-by-one: - `pip install insightface==0.7.3` - `pip install onnx` - - `pip install onnxruntime` + - `pip install onnxruntime-gpu>=1.16.1` if you have CUDA, otherwise `pip install onnxruntime` - `pip install opencv-python` - `pip install tqdm` 8. Type `deactivate`, you can close your Terminal or Console and start your SD WebUI, ReActor should start OK - if not, welcome to the Issues section. @@ -245,7 +207,7 @@ Please, check the path where "inswapper_128.onnx" model is stored. It must be in First of all - you need to disable any other Roop-based extensions: - Go to 'Extensions -> Installed' tab and uncheck any Roop-based extensions except this one - uncompatible-with-other-roop + uncompatible-with-other-roop - Click 'Apply and restart UI' Alternative solutions: @@ -254,7 +216,7 @@ Alternative solutions: ### **IV. "AttributeError: 'FaceSwapScript' object has no attribute 'enable'"** -You need to disable the "SD-CN-Animation" extension (or perhaps some another that causes the conflict) +Probably, you need to disable the "SD-CN-Animation" extension (or perhaps some another that causes the conflict) ### **V. "INVALID_PROTOBUF : Load model from <...>\models\insightface\inswapper_128.onnx failed:Protobuf parsing failed" OR "AttributeError: 'NoneType' object has no attribute 'get'" OR "AttributeError: 'FaceSwapScript' object has no attribute 'save_original'"** @@ -270,10 +232,10 @@ and put it to the `stable-diffusion-webui\models\insightface` replacing existing 3. Go to the (Windows)`venv\Scripts` or (MacOS/Linux)`venv/bin` run Terminal or Console (cmd) there and type `activate` 4. Then: - `python -m pip install -U pip` -- `pip uninstall -y onnx onnxruntime onnxruntime-gpu onnxruntime-silicon onnxruntime-extensions` -- `pip install onnx==1.14.1 onnxruntime` +- `pip uninstall -y onnxruntime onnxruntime-gpu onnxruntime-silicon onnxruntime-extensions` +- `pip install onnxruntime-gpu>=1.16.1` if you have CUDA, otherwise `pip install onnxruntime` -If it didn't help - it seems that you have another extension reinstalling `onnxruntime` when SD WebUI checks requirements. Please see your extensions list. If you find there "WD14 tagger" - try to disable it and then follow the steps above once again. This extension causes reinstalling of `onnxruntime` to `onnxruntime-gpu` or `onnxruntime==1.16.0` every time SD WebUI runs.
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` or `onnxruntime-gpu` to `onnxruntime<1.16.1` every time SD WebUI runs.
ORT 1.16.0 has a bug https://github.com/microsoft/onnxruntime/issues/17631 - don't install it! ### **VII. "ImportError: cannot import name 'builder' from 'google.protobuf.internal'"** diff --git a/README_RU.md b/README_RU.md index 7afd84f..e8cf87c 100644 --- a/README_RU.md +++ b/README_RU.md @@ -1,32 +1,9 @@
- logo + logo - ![Version](https://img.shields.io/badge/версия-0.5.0_alpha1-lightgreen?style=for-the-badge&labelColor=darkgreen) + ![Version](https://img.shields.io/badge/версия-0.5.0_alpha2-lightgreen?style=for-the-badge&labelColor=darkgreen) - - - - - -
- - для любых GPU - -
- - NVIDIA / AMD / Intel - -
- - для GPU NVIDIA -
- - 8Гб VRAM или более - -
-
-
Buy Me a Coffee at ko-fi.com
@@ -42,10 +19,7 @@ # ReActor для Stable Diffusion ### Расширение для быстрой и простой замены лиц на любых изображениях. Без фильтра цензуры, 18+, используйте под вашу собственную [ответственность](#disclaimer) -
- --- -
Установка | Возможности | Использование | API | Устранение проблем | Обновление | ComfyUI | Ответственность @@ -79,7 +53,7 @@ -example +example @@ -98,8 +72,8 @@ 2. Внутри SD Web-UI перейдите во вкладку "Extensions" и вставьте ссылку `https://github.com/Gourieff/sd-webui-reactor` в "Install from URL" и нажмите "Install" 3. Пожалуйста, подождите несколько минут, пока процесс установки полностью не завершится 4. Проверьте последнее сообщение в консоли SD-WebUI: -* Если вы видите "--- PLEASE, RESTART the Server! ---" - остановите Сервер (CTRL+C или CMD+C) и запустите его заново - ИЛИ же перейдите во вкладку "Installed" (*если у вас имееются какие-либо другие расширение, основанные на Roop или клонах ReActor - отключите их, иначе данное расширение может не работать*), нажмите "Apply and restart UI" -* Если вы видите "Done!", перейдите во вкладку "Installed" (*если у вас имееются какие-либо другие расширение, основанные на Roop или клонах ReActor - отключите их, иначе данное расширение может не работать*), нажмите "Apply and restart UI" - или же просто перезагрузите UI, нажав на "Reload UI" +* Если вы видите "--- PLEASE, RESTART the Server! ---" - остановите Сервер (CTRL+C или CMD+C) и запустите его заново - ИЛИ же перейдите во вкладку "Installed", нажмите "Apply and restart UI" +* Если вы видите "Done!", просто перезагрузите UI, нажав на "Reload UI" 5. Готово! Если вы используете [SD.Next](https://github.com/vladmandic/automatic): @@ -111,15 +85,15 @@ 5. Запустите SD.Next, перейдите во вкладку "Extensions", вставьте эту ссылку `https://github.com/Gourieff/sd-webui-reactor` в "Install from URL" и нажмите "Install" 6. Пожалуйста, подождите несколько минут, пока процесс установки полностью не завершится 7. Проверьте последнее сообщение в консоли SD.Next: -* Если вы видите "--- PLEASE, RESTART the Server! ---" - остановите Сервер (CTRL+C или CMD+C) и запустите его заново - ИЛИ же перейдите во вкладку "Installed" (*если у вас имееются какие-либо другие расширение, основанные на Roop или клонах ReActor - отключите их, иначе данное расширение может не работать*), нажмите "Restart the UI" -8. Остановите Сервер SD.Next, перейдите в директорию `automatic\extensions\sd-webui-reactor` - если вы видите там папку `models\insightface` с файлом `inswapper_128.onnx` внутри, переместите его в папку `automatic\models\insightface` +* Если вы видите "--- PLEASE, RESTART the Server! ---" - остановите Сервер (CTRL+C или CMD+C) или просто закройте консоль +8. Перейдите в директорию `automatic\extensions\sd-webui-reactor` - если вы видите там папку `models\insightface` с файлом `inswapper_128.onnx` внутри, переместите его в папку `automatic\models\insightface` 9. Готово, можете запустить SD.Next WebUI! Если вы используете [Cagliostro Colab UI](https://github.com/Linaqruf/sd-notebook-collection): 1. В активном WebUI, перейдите во вкладку "Extensions", вставьте ссылку `https://github.com/Gourieff/sd-webui-reactor` в "Install from URL" и нажмите "Install" 2. Пожалуйста, подождите некоторое время, пока процесс установки полностью не завершится -3. Когда вы увидите сообщение "--- PLEASE, RESTART the Server! ---" (в секции "Start UI" вашего ноутбука "Start Cagliostro Colab UI") - перейдите во вкладку "Installed" и нажмите "Apply and restart UI" (*если у вас имееются какие-либо другие расширение, основанные на Roop или клонах ReActor - отключите их, иначе данное расширение может не работать*) +3. Когда вы увидите сообщение "--- PLEASE, RESTART the Server! ---" (в секции "Start UI" вашего ноутбука "Start Cagliostro Colab UI") - перейдите во вкладку "Installed" и нажмите "Apply and restart UI" 4. Готово! @@ -135,6 +109,7 @@ - Возможность задать **порядок постобработки** - **100% совместимость** с разными **SD WebUI**: Automatic1111, SD.Next, Cagliostro Colab UI - **Отличная производительность** даже с использованием ЦПУ, ReActor для SD WebUI абсолютно не требователен к мощности вашей видеокарты +- **Поддержка CUDA**, начиная с версии 0.5.0 - **Поддержка [API](/API.md)**: как встроенного в SD WebUI, так и внешнего (через POST/GET запросы) - **[Поддержка](https://github.com/Gourieff/comfyui-reactor-node) ComfyUI** - **[Поддержка](https://github.com/Gourieff/sd-webui-reactor/issues/42) компьютеров Mac M1/M2** @@ -151,7 +126,7 @@ 2. Установите флажок "Enable"; 3. Готово, теперь результат будет иметь то лицо, которое вы выбрали. -example +example ### Индексы Лиц (Face Indexes) @@ -174,18 +149,18 @@ ReActor заменит только то лицо, которое удовлет ### Если лицо получилось нечётким Используйте опцию "Restore Face". Также можете попробовать опцию "Upscaler". Для более точного контроля параметров используйте Upscaler во вкладке "Extras". Также вы можете установить порядок постобработки (начиная с версии 0.1.0): -example +example *Прежняя логика была противоположенной (Upscale -> затем Restore), что приводило к более худшему качеству изображения лица (а также к значительной разнице текстур) после увеличения.* ### Результат имеет несколько лиц Выберите номера лиц, которые нужно поменять, используя поля "Comma separated face number(s)" для исходного изображения лица и для результата. Можно устанавливать любой, необходимый вам, порядок лиц. -example +example ### ~~Результат получился чёрным~~ ~~Это значит, что сработал NSFW фильтр.~~ -IamSFW +IamSFW ### Img2Img @@ -193,22 +168,11 @@ ReActor заменит только то лицо, которое удовлет Inpainting также работает, но замена лица происходит только в области маски.
Пожалуйста, используйте с опцией "Only masked" для "Inpaint area", если вы применяете "Upscaler". Иначе, используйте функцию увеличения (апскейла) через вкладку "Extras" или через опциональный загрузчик "Script" (внизу экрана), применив "SD upscale" или "Ultimate SD upscale". -### Использование ReActor + FaceSwapLab внутри одной среды +### Extras -Если вы столкнулись с трудностями запуска одновременно обоих расширений, попробуйте сделать следующее: +Начиная с версии 0.5.0, вы можете использовать ReActor через вкладку Extras, что даёт очень быструю производительность и возможность замены лиц в обход пайплайна SD, что иногда вызывает размытие или искажение деталей оригинального изображения -**для GPU NVIDIA с VRAM > 6Гб:** -
    Используйте [ReActor Force](https://github.com/Gourieff/sd-webui-reactor-force) - -**для любых других GPU**: - -1. Удалите FaceSwapLab из папки `extensions` -2. Удалите папки `onnxruntime` и `onnxruntime-gpu` из директории `site-packages` (внутри VENV Lib) -3. Запустите SD WebUI, чтобы ReActor установил библиотеку `onnxruntime`, закройте SD WebUI -4. `git clone https://github.com/glucauze/sd-webui-faceswaplab` в папку `extensions` -5. Отредактируйте файл `sd-webui-faceswaplab/install.py`: - - Строка 9: `use_gpu = True` -> `use_gpu = False` -6. Запустите SD WebUI, оба расширения теперь могут работать внутри одного окружения +IamSFW ## API @@ -241,7 +205,7 @@ Inpainting также работает, но замена лица происх 7. Далее: - `pip install insightface==0.7.3` - `pip install onnx` - - `pip install onnxruntime` + - `pip install onnxruntime-gpu>=1.16.1` если у весть есть GPU с CUDA, иначе `pip install onnxruntime` - `pip install opencv-python` - `pip install tqdm` 8. Выполните `deactivate`, закройте Терминал или Консоль и запустите SD WebUI, ReActor должен запуститься без к-л проблем - если же нет, добро пожаловать в раздел "Issues". @@ -250,7 +214,7 @@ Inpainting также работает, но замена лица происх Для начала отключите любые другие Roop-подобные расширения: - Перейдите в 'Extensions -> Installed' и снимите флажок с ненужных: - uncompatible-with-other-roop + uncompatible-with-other-roop - Нажмите 'Apply and restart UI' Альтернативные решения: @@ -275,10 +239,10 @@ Inpainting также работает, но замена лица происх 3. Перейдите в (Windows)`venv\Scripts` или (MacOS/Linux)`venv/bin`, откройте Терминал или Консоль (cmd) и выполните `activate` 4. Затем: - `python -m pip install -U pip` -- `pip uninstall -y onnx onnxruntime onnxruntime-gpu onnxruntime-silicon onnxruntime-extensions` -- `pip install onnx==1.14.1 onnxruntime` +- `pip uninstall -y onnxruntime onnxruntime-gpu onnxruntime-silicon onnxruntime-extensions` +- `pip install onnxruntime-gpu>=1.16.1` если у весть есть GPU с CUDA, иначе `pip install onnxruntime` -Если это не помогло - значит какое-то другое расширение переустанавливает `onnxruntime` всякий раз, когда SD WebUI проверяет требования пакетов. Внимательно посмотрите список активных расширений. Если видите там "WD14 tagger" - попробуйте отключить его и ещё раз выполнить шаги выше. Это расширение вызывает переустановку `onnxruntime` на `onnxruntime-gpu` или `onnxruntime==1.16.0` при каждом запуске SD WebUI.
ORT 1.16.0 выкатили с ошибкой https://github.com/microsoft/onnxruntime/issues/17631 - не устанавливайте её! +Если это не помогло - значит какое-то другое расширение переустанавливает `onnxruntime` всякий раз, когда SD WebUI проверяет требования пакетов. Внимательно посмотрите список активных расширений. Некоторые расширения могут вызывать переустановку `onnxruntime` или `onnxruntime-gpu` на версию `onnxruntime<1.16.1` при каждом запуске SD WebUI.
ORT 1.16.0 выкатили с ошибкой https://github.com/microsoft/onnxruntime/issues/17631 - не устанавливайте её! ### **VII. "ImportError: cannot import name 'builder' from 'google.protobuf.internal'"** diff --git a/example/ReActor_logo_red.png b/example/ReActor_logo_red.png deleted file mode 100644 index caf55bc..0000000 Binary files a/example/ReActor_logo_red.png and /dev/null differ diff --git a/example/api_example.py b/example/api_example.py index b073e55..72ec048 100644 --- a/example/api_example.py +++ b/example/api_example.py @@ -42,6 +42,7 @@ args=[ 0.8, #17 CodeFormer Weight (0 = maximum effect, 1 = minimum effect), 0.5 - by default False, #18 Source Image Hash Check, True - by default False, #19 Target Image Hash Check, False - by default + "CUDA", #20 CPU or CUDA (if you have it), CPU - by default ] # The args for ReActor can be found by diff --git a/example/api_external.curl b/example/api_external.curl index 751d34e..4fd0b1b 100644 --- a/example/api_external.curl +++ b/example/api_external.curl @@ -18,5 +18,6 @@ curl -X POST \ "gender_source": 0, "gender_target": 0, "save_to_file": 1, - "result_file_path": "" + "result_file_path": "", + "device": "CUDA" }' diff --git a/example/api_external.json b/example/api_external.json index 18e6e4a..67b4d9a 100644 --- a/example/api_external.json +++ b/example/api_external.json @@ -14,5 +14,6 @@ "gender_source": 0, "gender_target": 0, "save_to_file": 1, - "result_file_path": "" + "result_file_path": "", + "device": "CUDA" } \ No newline at end of file diff --git a/example/demo_crop.jpg b/example/demo_crop.jpg deleted file mode 100644 index a76dd62..0000000 Binary files a/example/demo_crop.jpg and /dev/null differ diff --git a/example/example.jpg b/example/example.jpg deleted file mode 100644 index 87421c5..0000000 Binary files a/example/example.jpg and /dev/null differ diff --git a/example/multiple-faces.png b/example/multiple-faces.png deleted file mode 100644 index b3cebf1..0000000 Binary files a/example/multiple-faces.png and /dev/null differ diff --git a/example/pp-order.png b/example/pp-order.png deleted file mode 100644 index ae64bc8..0000000 Binary files a/example/pp-order.png and /dev/null differ diff --git a/example/roop-off.png b/example/roop-off.png deleted file mode 100644 index a13ffed..0000000 Binary files a/example/roop-off.png and /dev/null differ diff --git a/install.py b/install.py index 157580a..e5dcf39 100644 --- a/install.py +++ b/install.py @@ -1,5 +1,6 @@ import subprocess import os, sys +from typing import Any import pkg_resources from tqdm import tqdm import urllib.request @@ -13,7 +14,10 @@ except: except: model_path = os.path.abspath("models") -req_file = os.path.join(os.path.dirname(os.path.realpath(__file__)), "requirements.txt") + +BASE_PATH = os.path.dirname(os.path.realpath(__file__)) + +req_file = os.path.join(BASE_PATH, "requirements.txt") models_dir_old = os.path.join(models_path, "roop") models_dir = os.path.join(models_path, "insightface") @@ -35,6 +39,10 @@ model_url = "https://github.com/facefusion/facefusion-assets/releases/download/m model_name = os.path.basename(model_url) model_path = os.path.join(models_dir, model_name) +def get_sd_option(name: str, default: Any) -> Any: + assert shared.opts.data is not None + return shared.opts.data.get(name, default) + def run_pip(*args): subprocess.run([sys.executable, "-m", "pip", "install", *args]) @@ -68,9 +76,46 @@ if not os.path.exists(models_dir): if not os.path.exists(model_path): download(model_url, model_path) -print("Checking ReActor requirements...", end=' ') +print("ReActor preheating...", end=' ') + +last_device = None +first_run = False + +try: + last_device_log = os.path.join(BASE_PATH, "last_device.txt") + with open(last_device_log) as f: + for el in f: + last_device = el.strip() +except: + last_device = "CPU" + first_run = True + with open(os.path.join(BASE_PATH, "last_device.txt"), "w") as txt: + txt.write(last_device) + with open(req_file) as file: install_count = 0 + try: + import torch.cuda as cuda + if cuda.is_available(): + ort = "onnxruntime-gpu" + if first_run: + last_device = "CUDA" + with open(os.path.join(BASE_PATH, "last_device.txt"), "w") as txt: + txt.write(last_device) + else: + ort = "onnxruntime" + if last_device == "CUDA": + last_device = "CPU" + with open("last_device.txt", "w") as txt: + txt.write(last_device) + if not is_installed(ort,"1.16.1",False): + install_count += 1 + run_pip(ort) + except Exception as e: + print(e) + print(f"\nERROR: Failed to install {ort} - ReActor won't start") + raise e + print(f"Device: {last_device}") strict = True for package in file: package_version = None @@ -89,6 +134,8 @@ with open(req_file) as file: print(f"\nERROR: Failed to install {package} - ReActor won't start") raise e if install_count > 0: - print(f'\n--- PLEASE, RESTART the Server! ---\n') - else: - print('Ok') + print(f""" + +---------------------------------+ + --- PLEASE, RESTART the Server! --- + +---------------------------------+ + """) diff --git a/requirements.txt b/requirements.txt index 6955258..c0b8c4f 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,4 +1,3 @@ insightface==0.7.3 onnx>=1.14.0 -onnxruntime>=1.15.1 opencv-python>=4.7.0.72 diff --git a/scripts/reactor_api.py b/scripts/reactor_api.py index 1ffb880..f0cc133 100644 --- a/scripts/reactor_api.py +++ b/scripts/reactor_api.py @@ -70,7 +70,8 @@ def reactor_api(_: gr.Blocks, app: FastAPI): gender_source: int = Body(0,title="Gender Detection (Source) (0 - No, 1 - Female Only, 2 - Male Only)"), gender_target: int = Body(0,title="Gender Detection (Target) (0 - No, 1 - Female Only, 2 - Male Only)"), save_to_file: int = Body(0,title="Save Result to file, 0 - No, 1 - Yes"), - 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)") ): s_image = api.decode_base64_to_image(source_image) t_image = api.decode_base64_to_image(target_image) @@ -83,7 +84,7 @@ def reactor_api(_: gr.Blocks, app: FastAPI): use_model = get_full_model(model) if use_model is None: Exception("Model not found") - result = swap_face(s_image, t_image, use_model, sf_index, f_index, up_options, gender_s, gender_t) + result = swap_face(s_image, t_image, use_model, sf_index, f_index, up_options, gender_s, gender_t, True, True, device) if save_to_file == 1: if result_file_path == "": result_file_path = default_file_path() diff --git a/scripts/reactor_faceswap.py b/scripts/reactor_faceswap.py index dcbcbb8..25fb944 100644 --- a/scripts/reactor_faceswap.py +++ b/scripts/reactor_faceswap.py @@ -1,6 +1,7 @@ import os, glob import gradio as gr from PIL import Image +import torch.cuda as cuda from typing import List @@ -26,7 +27,8 @@ from scripts.reactor_logger import logger from scripts.reactor_swapper import EnhancementOptions, swap_face, check_process_halt, reset_messaged from scripts.reactor_version import version_flag, app_title from scripts.console_log_patch import apply_logging_patch -from scripts.reactor_helpers import make_grid, get_image_path +from scripts.reactor_helpers import make_grid, get_image_path, set_Device +from scripts.reactor_globals import DEVICE, DEVICE_LIST MODELS_PATH = None @@ -134,6 +136,26 @@ class FaceSwapScript(scripts.Script): ) with gr.Tab("Settings"): models = get_models() + if cuda.is_available(): + with gr.Row(): + 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("") + setattr(device, "do_not_save_to_config", True) + save_device_btn.click( + set_Device, + inputs=[device], + outputs=[save], + ) + else: + device = "CPU" with gr.Row(): if len(models) == 0: logger.warning( @@ -163,7 +185,7 @@ class FaceSwapScript(scripts.Script): target_hash_check = gr.Checkbox( False, label="Target Image Hash Check", - info="Affects if you use img2img with only 'Swap in source image' option." + info="Affects if you use Extras tab or img2img with only 'Swap in source image' on." ) return [ @@ -187,6 +209,7 @@ class FaceSwapScript(scripts.Script): codeformer_weight, source_hash_check, target_hash_check, + device, ] @@ -239,6 +262,7 @@ class FaceSwapScript(scripts.Script): codeformer_weight, source_hash_check, target_hash_check, + device, ): self.enable = enable if self.enable: @@ -254,7 +278,8 @@ class FaceSwapScript(scripts.Script): self.upscaler_visibility = upscaler_visibility self.face_restorer_visibility = face_restorer_visibility self.restore_first = restore_first - self.upscaler_name = upscaler_name + self.upscaler_name = upscaler_name + self.swap_in_source = swap_in_source self.swap_in_generated = swap_in_generated self.model = os.path.join(MODELS_PATH,model) self.console_logging_level = console_logging_level @@ -264,6 +289,7 @@ class FaceSwapScript(scripts.Script): self.codeformer_weight = codeformer_weight self.source_hash_check = source_hash_check self.target_hash_check = target_hash_check + self.device = device if self.gender_source is None or self.gender_source == "No": self.gender_source = 0 if self.gender_target is None or self.gender_target == "No": @@ -285,9 +311,11 @@ class FaceSwapScript(scripts.Script): if self.target_hash_check is None: self.target_hash_check = False + set_Device(self.device) + if self.source is not None: apply_logging_patch(console_logging_level) - if isinstance(p, StableDiffusionProcessingImg2Img) and swap_in_source: + if isinstance(p, StableDiffusionProcessingImg2Img) and self.swap_in_source: logger.status("Working: source face index %s, target face index %s", self.source_faces_index, self.faces_index) for i in range(len(p.init_images)): @@ -304,6 +332,7 @@ class FaceSwapScript(scripts.Script): gender_target=self.gender_target, source_hash_check=self.source_hash_check, target_hash_check=self.target_hash_check, + device=self.device, ) 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") @@ -354,6 +383,7 @@ class FaceSwapScript(scripts.Script): gender_target=self.gender_target, source_hash_check=self.source_hash_check, target_hash_check=self.target_hash_check, + device=self.device, ) if result is not None and swapped > 0: result_images.append(result) @@ -410,6 +440,7 @@ class FaceSwapScript(scripts.Script): gender_target=self.gender_target, source_hash_check=self.source_hash_check, target_hash_check=self.target_hash_check, + device=self.device, ) try: pp = scripts_postprocessing.PostprocessedImage(result) @@ -436,7 +467,6 @@ class FaceSwapScriptExtras(scripts_postprocessing.ScriptPostprocessing): 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("
") gr.Markdown("Source Image (above):") with gr.Row(): source_faces_index = gr.Textbox( @@ -450,7 +480,6 @@ class FaceSwapScriptExtras(scripts_postprocessing.ScriptPostprocessing): label="Gender Detection (Source)", type="index", ) - gr.Markdown("
") gr.Markdown("Target Image (result):") with gr.Row(): faces_index = gr.Textbox( @@ -464,7 +493,6 @@ class FaceSwapScriptExtras(scripts_postprocessing.ScriptPostprocessing): label="Gender Detection (Target)", type="index", ) - gr.Markdown("
") with gr.Row(): face_restorer_name = gr.Radio( label="Restore Face", @@ -479,7 +507,6 @@ class FaceSwapScriptExtras(scripts_postprocessing.ScriptPostprocessing): codeformer_weight = gr.Slider( 0, 1, 0.5, step=0.1, label="CodeFormer Weight", info="0 = maximum effect, 1 = minimum effect" ) - gr.Markdown("
") with gr.Tab("Upscale"): restore_first = gr.Checkbox( @@ -493,7 +520,6 @@ class FaceSwapScriptExtras(scripts_postprocessing.ScriptPostprocessing): value="None", info="Won't scale if you choose -Swap in Source- via img2img, only 1x-postprocessing will affect (texturing, denoising, restyling etc.)" ) - gr.Markdown("
") with gr.Row(): upscaler_scale = gr.Slider(1, 8, 1, step=0.1, label="Scale by") upscaler_visibility = gr.Slider( @@ -501,6 +527,26 @@ class FaceSwapScriptExtras(scripts_postprocessing.ScriptPostprocessing): ) with gr.Tab("Settings"): models = get_models() + if cuda.is_available(): + with gr.Row(): + 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("") + setattr(device, "do_not_save_to_config", True) + save_device_btn.click( + set_Device, + inputs=[device], + outputs=[save], + ) + else: + device = "CPU" with gr.Row(): if len(models) == 0: logger.warning( @@ -520,18 +566,6 @@ class FaceSwapScriptExtras(scripts_postprocessing.ScriptPostprocessing): label="Console Log Level", type="index", ) - gr.Markdown("
") - with gr.Row(): - source_hash_check = gr.Checkbox( - True, - label="Source Image Hash Check", - info="Recommended to keep it ON. Processing is faster when Source Image is the same." - ) - target_hash_check = gr.Checkbox( - False, - label="Target Image Hash Check", - info="Affects if you use img2img with only 'Swap in source image' option." - ) args = { 'img': img, @@ -549,8 +583,7 @@ class FaceSwapScriptExtras(scripts_postprocessing.ScriptPostprocessing): 'gender_source': gender_source, 'gender_target': gender_target, 'codeformer_weight': codeformer_weight, - 'source_hash_check': source_hash_check, - 'target_hash_check': target_hash_check, + 'device': device, } return args @@ -599,8 +632,7 @@ class FaceSwapScriptExtras(scripts_postprocessing.ScriptPostprocessing): self.gender_source = args['gender_source'] self.gender_target = args['gender_target'] self.codeformer_weight = args['codeformer_weight'] - self.source_hash_check = args['source_hash_check'] - self.target_hash_check = args['target_hash_check'] + self.device = args['device'] if self.gender_source is None or self.gender_source == "No": self.gender_source = 0 if self.gender_target is None or self.gender_target == "No": @@ -615,17 +647,16 @@ class FaceSwapScriptExtras(scripts_postprocessing.ScriptPostprocessing): self.source_faces_index = [0] if len(self.faces_index) == 0: self.faces_index = [0] - if self.source_hash_check is None: - self.source_hash_check = True - if self.target_hash_check is None: - self.target_hash_check = False current_job_number = shared.state.job_no + 1 job_count = shared.state.job_count if current_job_number == job_count: reset_messaged() + set_Device(self.device) + if self.source is not None: + 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) image: Image.Image = pp.image result, output, swapped = swap_face( @@ -637,12 +668,14 @@ class FaceSwapScriptExtras(scripts_postprocessing.ScriptPostprocessing): enhancement_options=self.enhancement_options, gender_source=self.gender_source, gender_target=self.gender_target, - source_hash_check=self.source_hash_check, - target_hash_check=self.target_hash_check, + source_hash_check=True, + target_hash_check=True, + device=self.device, ) try: pp.info["ReActor"] = True pp.image = result + logger.status("---Done!---") except Exception: logger.error("Cannot create a result image") else: diff --git a/scripts/reactor_globals.py b/scripts/reactor_globals.py index d41b27e..96e6d7d 100644 --- a/scripts/reactor_globals.py +++ b/scripts/reactor_globals.py @@ -1 +1,18 @@ +import os +from pathlib import Path + IS_RUN: bool = False +BASE_PATH = os.path.join(Path(__file__).parents[1]) +DEVICE_LIST: list = ["CPU", "CUDA"] + +def updateDevice(): + try: + LAST_DEVICE_PATH = os.path.join(BASE_PATH, "last_device.txt") + with open(LAST_DEVICE_PATH) as f: + for el in f: + device = el.strip() + except: + device = "CPU" + return device + +DEVICE = updateDevice() diff --git a/scripts/reactor_helpers.py b/scripts/reactor_helpers.py index 54fe81c..21cc90f 100644 --- a/scripts/reactor_helpers.py +++ b/scripts/reactor_helpers.py @@ -8,6 +8,17 @@ import hashlib from modules.images import FilenameGenerator, get_next_sequence_number from modules import shared, script_callbacks +from scripts.reactor_globals import DEVICE, BASE_PATH + +def set_Device(value): + global DEVICE + DEVICE = value + with open(os.path.join(BASE_PATH, "last_device.txt"), "w") as txt: + txt.write(DEVICE) + +def get_Device(): + global DEVICE + return DEVICE def make_grid(image_list: List): diff --git a/scripts/reactor_swapper.py b/scripts/reactor_swapper.py index 276fd4b..065fc8f 100644 --- a/scripts/reactor_swapper.py +++ b/scripts/reactor_swapper.py @@ -1,7 +1,8 @@ import copy import os from dataclasses import dataclass -from typing import List, Union +from typing import List, Union, Tuple +from functools import lru_cache import cv2 import numpy as np @@ -9,7 +10,7 @@ from PIL import Image import insightface -from scripts.reactor_helpers import get_image_md5hash +from scripts.reactor_helpers import get_image_md5hash, get_Device from modules.face_restoration import FaceRestoration try: # A1111 from modules import codeformer_model @@ -31,7 +32,12 @@ import warnings np.warnings = warnings np.warnings.filterwarnings('ignore') -providers = ["CPUExecutionProvider"] + +DEVICE = get_Device() +if DEVICE == "CUDA": + PROVIDERS = ["CUDAExecutionProvider"] +else: + PROVIDERS = ["CPUExecutionProvider"] @dataclass @@ -80,21 +86,32 @@ SOURCE_IMAGE_HASH = None TARGET_FACES = None TARGET_IMAGE_HASH = None + def getAnalysisModel(): global ANALYSIS_MODEL if ANALYSIS_MODEL is None: ANALYSIS_MODEL = insightface.app.FaceAnalysis( - name="buffalo_l", providers=providers, root=os.path.join(models_path, "insightface") # note: allowed_modules=['detection', 'genderage'] + name="buffalo_l", providers=PROVIDERS, root=os.path.join(models_path, "insightface") # note: allowed_modules=['detection', 'genderage'] ) return ANALYSIS_MODEL +@lru_cache(maxsize=3) +def getAnalysisModel_CUDA(det_size: Tuple[int, int] = (640, 640)): + global ANALYSIS_MODEL + if ANALYSIS_MODEL is None: + ANALYSIS_MODEL = insightface.app.FaceAnalysis( + name="buffalo_l", providers=PROVIDERS, root=os.path.join(models_path, "insightface") # note: allowed_modules=['detection', 'genderage'] + ) + ANALYSIS_MODEL.prepare(ctx_id=0, det_size=det_size) + return ANALYSIS_MODEL +@lru_cache(maxsize=1) def getFaceSwapModel(model_path: str): global FS_MODEL global CURRENT_FS_MODEL_PATH if CURRENT_FS_MODEL_PATH is None or CURRENT_FS_MODEL_PATH != model_path: CURRENT_FS_MODEL_PATH = model_path - FS_MODEL = insightface.model_zoo.get_model(model_path, providers=providers) + FS_MODEL = insightface.model_zoo.get_model(model_path, providers=PROVIDERS) return FS_MODEL @@ -210,17 +227,17 @@ def get_face_age(face, face_index): return "None" return face_age -# def reget_face_single(img_data: np.ndarray, face, det_size, face_index, gender_source, gender_target): -# det_size_half = (det_size[0] // 2, det_size[1] // 2) -# return get_face_single(img_data, face, face_index=face_index, det_size=det_size_half, gender_source=gender_source, gender_target=gender_target) - def half_det_size(det_size): logger.status("Trying to halve 'det_size' parameter") return (det_size[0] // 2, det_size[1] // 2) def analyze_faces(img_data: np.ndarray, det_size=(640, 640)): - face_analyser = copy.deepcopy(getAnalysisModel()) - face_analyser.prepare(ctx_id=0, det_size=det_size) + logger.info("Applied Execution Provider: %s", PROVIDERS[0]) + if DEVICE == "CUDA": + face_analyser = getAnalysisModel_CUDA(det_size) + else: + face_analyser = copy.deepcopy(getAnalysisModel()) + face_analyser.prepare(ctx_id=0, det_size=det_size) return face_analyser.get(img_data) def get_face_single(img_data: np.ndarray, face, face_index=0, det_size=(640, 640), gender_source=0, gender_target=0): @@ -278,9 +295,12 @@ def swap_face( gender_target: int = 0, source_hash_check: bool = True, target_hash_check: bool = False, + device: str = "CPU", ): - global SOURCE_FACES, SOURCE_IMAGE_HASH, TARGET_FACES, TARGET_IMAGE_HASH + global SOURCE_FACES, SOURCE_IMAGE_HASH, TARGET_FACES, TARGET_IMAGE_HASH, PROVIDERS result_image = target_img + + PROVIDERS = ["CUDAExecutionProvider"] if device == "CUDA" else ["CPUExecutionProvider"] if check_process_halt(): return result_image, [], 0 diff --git a/scripts/reactor_version.py b/scripts/reactor_version.py index 4fc9f76..b7aec38 100644 --- a/scripts/reactor_version.py +++ b/scripts/reactor_version.py @@ -1,5 +1,5 @@ app_title = "ReActor" -version_flag = "v0.5.0-a1" +version_flag = "v0.5.0-a2" from scripts.reactor_logger import logger, get_Run, set_Run