Compare commits

..

143 Commits

Author SHA1 Message Date
dfb26ed781 Merge pull request #1702 from easydiffusion/beta
Beta
2023-12-12 18:10:46 +05:30
547febafba Autosave the VAE tiling setting 2023-12-12 18:10:04 +05:30
85eaa305cc Hotfix for #1701 - run disable VAE tiling only on pipelines that support it 2023-12-12 18:07:56 +05:30
25272ce083 Kofi only 2023-12-12 12:48:13 +05:30
212fa77b47 Merge pull request #1700 from easydiffusion/beta
Beta
2023-12-12 12:47:24 +05:30
e77629c525 Version and changelog 2023-12-11 22:31:12 +05:30
097780be26 Setting to enable/disable VAE tiling 2023-12-11 22:28:19 +05:30
6489cd785d Merge pull request #1648 from michaelachrisco/main
Fix Sampler learn more link
2023-11-05 19:16:14 +05:30
a4e651e27e Click to learn more about samplers should go to wiki page 2023-10-28 23:40:20 -07:00
bedf176e62 Merge pull request #1630 from easydiffusion/beta
Beta
2023-10-12 10:06:42 +05:30
398a0509d7 Banner change 2023-10-12 10:05:43 +05:30
52cc99bf1f Revert "Revert the support banner experiment"
This reverts commit 45a14a9be9.
2023-10-12 09:58:29 +05:30
824e057d7b Merge pull request #1624 from easydiffusion/beta
sdkit 2.0.15 - fix for gfgpan/realesrgan in parallel threads
2023-10-06 09:54:40 +05:30
9bd4b3a6d0 sdkit 2.0.15 - fix for gfgpan/realesrgan in parallel threads with Stable Diffusion 2023-10-05 19:04:19 +05:30
307b00cc05 Merge pull request #1622 from easydiffusion/beta
Beta
2023-10-03 19:38:11 +05:30
8a98df4673 Merge branch 'beta' of github.com:cmdr2/stable-diffusion-ui into beta 2023-10-03 19:35:36 +05:30
45a14a9be9 Revert the support banner experiment 2023-10-03 19:35:23 +05:30
e419276e34 Merge pull request #1621 from easydiffusion/main
Main
2023-10-03 12:45:36 +05:30
0a92b7b1d5 Merge pull request #1620 from easydiffusion/beta
Use sd 2.1.5
2023-10-03 12:42:11 +05:30
f110168366 Use sd 2.1.5 2023-10-03 12:41:51 +05:30
ce24a05909 Merge pull request #1619 from easydiffusion/beta
Beta
2023-10-03 12:39:29 +05:30
45facf64e5 sdkit 2.0.14 - pin transformers 4.33.2 (via sd 2.1.5) and acccelerate 0.23.0, and k-diffusion to 0.0.12 2023-10-02 12:08:58 +05:30
e999832c26 Prevent the user from changing the metadata format if the server has set force_save_metadata 2023-09-30 20:11:28 +05:30
4c8d5a7077 Allow setting the metadata field in the server settings, instead of forcing json whenever force_save_path is set 2023-09-29 20:23:24 +05:30
81643cb3af Merge pull request #1611 from easydiffusion/beta
Fix error if a user doesn't have any LoRA models in the folder
2023-09-28 10:06:23 +05:30
7a9bc883df Fix error if a user doesn't have any LoRA models in the folder 2023-09-27 19:32:21 +05:30
6280a80129 Merge pull request #1608 from easydiffusion/beta
LoRA Manager and Upload Thumbnails
2023-09-27 19:24:41 +05:30
a33908b6de Changelog for LoRA manager and 'Upload thumbnails' 2023-09-27 19:22:35 +05:30
0ea5620413 Multi-gpu GFPGAN not fixed yet 2023-09-27 19:03:45 +05:30
e23eb1fea8 Save metadata as json if using force_save_path 2023-09-26 20:58:27 +05:30
41f2c82eaf Save metadata if force_save_path is enabled. We can make this more flexible later 2023-09-26 20:54:16 +05:30
91e3bfe58f Merge pull request #1604 from flavioislima/fix/rocm_url
FIX: ROCM download URL
2023-09-25 14:06:45 +05:30
83d5519a31 Merge pull request #1605 from JeLuF/hover
Fix 'Swap width&height' tooltip
2023-09-25 14:06:11 +05:30
cc2666b9d6 Fix 'Swap w&h' tooltip 2023-09-24 22:06:30 +02:00
954493fef5 FIX: ROCM download URL 2023-09-23 20:08:19 +01:00
967c3681cd Merge pull request #1598 from JeLuF/loraman3
LoraManager: Remove old plugin file
2023-09-19 11:18:41 +05:30
87c9df5c0d Remove old plugin file 2023-09-18 18:55:34 +02:00
62136768d2 typo 2023-09-18 21:25:39 +05:30
b71b7804fc Merge branch 'beta' of github.com:cmdr2/stable-diffusion-ui into beta 2023-09-18 21:25:22 +05:30
e8b7751374 typo 2023-09-18 21:25:08 +05:30
54d4433141 Merge pull request #1596 from JeLuF/loraman2
LoraManager: Implement 'Upload thumbnail' button
2023-09-18 10:42:25 +05:30
14dbebbc35 LoraManager: Implement 'Upload thumbnail' button 2023-09-17 22:37:17 +02:00
d6a02a31a7 LoraManager: Implement 'Upload thumbnail' button 2023-09-17 22:36:50 +02:00
86e2ac40ae changelog 2023-09-15 19:09:45 +05:30
a12ed7533b Fix broken embeddings dialog when the lora info couldn't be fetched 2023-09-15 19:09:14 +05:30
9fb0ee2d1b Merge pull request #1588 from JeLuF/loraman1
Loramanager fixes
2023-09-15 19:02:17 +05:30
6311b80474 Loramanager fixes
- avoid console errors in python and JS code
- suppress localhost:9000/null links
2023-09-14 23:15:27 +02:00
c13d1093ee sdkit 2.0.12 - actually use the gfpgan fix. 2.0.11 was bad 2023-09-14 20:01:53 +05:30
dd7deeba53 v3.0.6 2023-09-14 19:53:44 +05:30
338aef3e95 sdkit 2.0.11 - fix for gfpgan when using multiple GPUs in parallel 2023-09-14 19:52:50 +05:30
134c98ccb5 Merge pull request #1565 from JeLuF/loramanager
Lora Manager
2023-09-14 19:05:17 +05:30
d12877987f Merge pull request #1584 from easydiffusion/beta
Beta
2023-09-13 18:14:26 +05:30
676316e5e4 Merge pull request #1583 from JeLuF/poor
🔥 FIX Linux installer: Don't use rich
2023-09-13 18:12:56 +05:30
52761ad88c Update check_modules.py 2023-09-13 13:45:21 +02:00
f5e489ba87 Don't use rich
During the first installation, rich is not yet installed
2023-09-13 13:39:34 +02:00
982af1fff3 Merge pull request #1581 from easydiffusion/main
Main
2023-09-13 13:12:32 +05:30
1cff398c20 Merge pull request #1580 from easydiffusion/beta
Beta
2023-09-13 13:12:14 +05:30
a6271d2c4e Merge pull request #1563 from JeLuF/amdperm
AMD/Linux: Warn about file permissions
2023-09-05 16:32:13 +05:30
60f8cc6883 Merge pull request #1567 from easydiffusion/beta
Beta
2023-09-05 16:31:34 +05:30
ffb8feba6b Merge pull request #1564 from JeLuF/wmic
Windows: Show GPU list and driver versions in log
2023-09-05 16:18:42 +05:30
4aca3c4639 Lora Manager 2023-09-04 01:36:32 +02:00
120f9e567c Windows: Show GPU list and driver versions in log 2023-09-03 13:49:30 +02:00
c0492511df AMD/Linux: Warn about file permissions 2023-09-03 13:44:06 +02:00
1075a5ed93 changelog 2023-09-02 19:30:56 +05:30
58d3507155 sdkit 2.0.10 - SDXL ControlNet support; upgrade to diffusers 0.20.2 2023-09-02 19:30:27 +05:30
ae0c9b6a6b Merge branch 'beta' of github.com:cmdr2/stable-diffusion-ui into beta 2023-09-02 18:34:33 +05:30
ad1374af1d bring back config print 2023-09-02 18:34:12 +05:30
8436e8a71e Merge pull request #1560 from JeLuF/mdir-err
Error handling for models_dir
2023-09-02 08:23:54 +05:30
ea07483465 Error handling for models_dir 2023-09-01 22:54:03 +02:00
51f857c3f3 Merge pull request #1559 from easydiffusion/beta
Beta
2023-09-01 20:30:54 +05:30
74c0ca0902 changelog 2023-09-01 19:53:51 +05:30
ad5641fa3e Fix incorrect metadata generation of embeddings, by removing duplicated logic. The UI already handles this 2023-09-01 19:52:20 +05:30
b0294f8cbd Support banner 2023-09-01 19:31:46 +05:30
5d4498ff85 changelog 2023-09-01 19:29:54 +05:30
d52fb15746 Merge pull request #1558 from easydiffusion/beta
Revert "Continue using uvicorn directly on windows"
2023-09-01 18:29:43 +05:30
ee6be74e72 Revert "Continue using uvicorn directly on windows"
This reverts commit 3a5e0cb2d2.
2023-09-01 18:29:19 +05:30
4cbc86f945 Merge pull request #1557 from easydiffusion/beta
Continue using uvicorn directly on windows
2023-09-01 17:52:19 +05:30
3a5e0cb2d2 Continue using uvicorn directly on windows 2023-09-01 17:51:15 +05:30
7916b8d26a Merge pull request #1556 from easydiffusion/beta
Ignore unknown AMD GPUs
2023-09-01 17:05:04 +05:30
a0842b4659 Ignore unknown AMD GPUs 2023-09-01 17:04:38 +05:30
14ee87ca80 Merge pull request #1555 from easydiffusion/beta
AMD on Linux
2023-09-01 15:58:35 +05:30
cec1d7d6c9 hide debug log 2023-09-01 13:30:46 +05:30
9aeae4d16e note to self 2023-09-01 13:25:00 +05:30
9c1b741d89 Relative path for src 2023-09-01 13:17:50 +05:30
c71a74f857 Merge pull request #1491 from JeLuF/launcher
Pythonize the uvicorn startup
2023-09-01 13:10:45 +05:30
524612cee5 Different PYTHONPATH for Windows and Linux/Mac 2023-09-01 13:09:01 +05:30
11e47b3871 Merge pull request #1554 from easydiffusion/main
Main
2023-09-01 12:51:47 +05:30
4a1b2be45c Merge pull request #1553 from easydiffusion/beta
Beta
2023-09-01 12:51:23 +05:30
d641aa2f6e Fix ordering of help topics 2023-09-01 11:08:11 +05:30
237c7a5348 3.0.4 2023-09-01 10:42:29 +05:30
19f37907d9 Allow changing the models directory via a setting, to share models with other locations on the disk 2023-09-01 10:40:18 +05:30
b8706da990 Merge pull request #1548 from easydiffusion/beta
Beta
2023-08-31 22:28:03 +05:30
b458d57355 Keep the old test_diffusers id around to prevent broken plugins 2023-08-31 22:24:27 +05:30
a5962dae33 Allow underscore in embeddings path 2023-08-31 22:19:04 +05:30
670768e5b3 Allow hyphens in embeddings 2023-08-31 22:16:48 +05:30
f02b915cd0 Fix typo when using force_save_path 2023-08-31 22:11:42 +05:30
71bbbeb936 Update help topics 2023-08-31 21:25:29 +05:30
e084b78b53 Update README.md 2023-08-31 20:14:06 +05:30
013860e3c0 Merge pull request #1546 from easydiffusion/beta
Use v3 for everyone
2023-08-31 20:03:57 +05:30
7a118eeb15 Rename the test_diffusers config key to upgrade all the existing users to the v3 engine. Users can now opt to disable v3. This upgrades existing users who had maybe tried diffusers many months ago (when it was still unstable) and decided against it (at that time). 2023-08-31 19:20:26 +05:30
df408b25e5 changelog 2023-08-31 15:59:23 +05:30
536082c1a6 Save filtered images to disk if required by the API, for e.g. when clicking 'Upscale' or 'Fix Faces on the image 2023-08-31 15:57:53 +05:30
b986ca3059 Update README.md 2023-08-31 12:44:52 +05:30
4bf9e577b9 Merge pull request #1541 from easydiffusion/beta
Beta
2023-08-31 09:57:11 +05:30
a7c12e61d8 Fix incorrect tiling message in the task info 2023-08-30 19:32:29 +05:30
847d27bffb sdkit 2.0.9 - another fix for torch 2.0 and onnx export 2023-08-30 19:32:09 +05:30
781e812f22 sdkit 2.0.8 - temp hack for allowing onnx export on pytorch 2.0 2023-08-30 18:51:34 +05:30
e49b5e0e6b changelog 2023-08-30 18:24:46 +05:30
8f1c1b128e sdkit 2.0.7 - Allow loading NovelAI-based models 2023-08-30 18:24:23 +05:30
04cbb052d7 bump version 2023-08-30 17:54:19 +05:30
16f0950ebd sdkit 2.0.6 - Fix broken VAE tiling 2023-08-30 17:42:50 +05:30
e959a3d7ab ui 2023-08-30 17:42:32 +05:30
fc9941abaa Merge pull request #1539 from easydiffusion/beta
Server-side setting to block_nsfw
2023-08-30 16:25:04 +05:30
f177011395 changelog 2023-08-30 16:22:08 +05:30
80e47be5a5 Prevent block_nsfw from getting edited via the HTTP api 2023-08-30 16:22:05 +05:30
9a9f6e3559 Server-side config to allow force-blocking of NSFW images 2023-08-30 16:13:10 +05:30
1a6e0234b3 Merge pull request #1538 from easydiffusion/beta
Beta
2023-08-30 15:35:21 +05:30
56bea46e3a Use absolute config path 2023-08-30 15:34:55 +05:30
a09441b2c8 Change the tensorrt installation commands to what NVIDIA suggested over chat 2023-08-30 15:14:05 +05:30
105994d96d Merge pull request #1536 from easydiffusion/beta
Beta
2023-08-30 14:58:24 +05:30
d641647b1e Merge branch 'beta' of github.com:cmdr2/stable-diffusion-ui into beta 2023-08-30 14:57:53 +05:30
672574d278 sdkit 2.0.5 - don't download the safety checker unless necessary 2023-08-30 14:57:37 +05:30
f1ded17399 Merge pull request #1535 from easydiffusion/beta
Beta
2023-08-30 14:44:41 +05:30
d254e3e2fd Merge pull request #1534 from easydiffusion/main
Main
2023-08-30 14:44:09 +05:30
ab5450bb27 Don't download codeformer and controlnet if not being used 2023-08-30 14:39:43 +05:30
a2e9e5eb57 Remove old files 2023-08-30 13:18:03 +05:30
8965f11ab4 Update CONTRIBUTING.md 2023-08-30 13:15:19 +05:30
1dd5644e7a Update build.bat and build.sh to create the installers for Windows and Mac/Linux (respectively) 2023-08-30 13:09:12 +05:30
37f813506e Merge pull request #1533 from easydiffusion/beta
Beta
2023-08-29 20:11:44 +05:30
a5d5ed90e6 Merge pull request #1532 from easydiffusion/main
Main
2023-08-29 20:10:26 +05:30
3792a1bc0d sdkit 2.0.4 - use sd 1.5 fp16 by default, if no model is present 2023-08-29 20:06:54 +05:30
fbafa56ecb Use torch 2.0.1 and torchvision 0.15.2 by default on Windows 2023-08-29 18:52:06 +05:30
2f910c69b8 unused file 2023-08-29 17:54:23 +05:30
bf06cc48bb Merge branch 'beta' of github.com:cmdr2/stable-diffusion-ui into beta 2023-08-29 17:52:11 +05:30
3ef67ebc73 NSIS - v3, create from an existing installation 2023-08-29 17:51:58 +05:30
0c4318fb31 Update README.md 2023-08-29 17:49:50 +05:30
c55ced93db Update FUNDING.yml 2023-08-29 17:48:50 +05:30
807d940001 Merge pull request #1528 from JeLuF/inputmode
inputmode=numeric/decimal for <input> fields
2023-08-29 15:09:13 +05:30
8c27fa136c inputmode=numeric/decimal for <input> fields 2023-08-29 10:02:06 +02:00
8e7a6077e5 Make it work on Windows 2023-08-13 15:01:32 +02:00
53a79c1a81 Automatically detect whether NAVI1/2 or NAVI3 ROCm versions are needed 2023-08-11 22:36:20 +02:00
e9f54c8bae Launch uvicorn from check_modules.py 2023-08-11 21:31:45 +02:00
c978863e5f Add uvicorn-launch to check_modules.py 2023-08-08 22:16:57 +02:00
36 changed files with 2365 additions and 832 deletions

2
.gitignore vendored
View File

@ -4,3 +4,5 @@ installer.tar
dist
.idea/*
node_modules/*
.tmp1
.tmp2

View File

@ -17,6 +17,18 @@
- **Major rewrite of the code** - We've switched to using diffusers under-the-hood, which allows us to release new features faster, and focus on making the UI and installer even easier to use.
### Detailed changelog
* 3.0.7 - 11 Dec 2023 - Setting to enable/disable VAE tiling (in the Image Settings panel). Sometimes VAE tiling reduces the quality of the image, so this setting will help control that.
* 3.0.6 - 18 Sep 2023 - Add thumbnails to embeddings from the UI, using the new `Upload Thumbnail` button in the Embeddings popup. Thanks @JeLuf.
* 3.0.6 - 15 Sep 2023 - Fix broken embeddings dialog when LoRA information couldn't be fetched.
* 3.0.6 - 14 Sep 2023 - UI for adding notes to LoRA files (to help you remember which prompts to use). Also added a button to automatically fetch prompts from Civitai for a LoRA file, using the `Import from Civitai` button. Thanks @JeLuf.
* 3.0.5 - 2 Sep 2023 - Support SDXL ControlNets.
* 3.0.4 - 1 Sep 2023 - Fix incorrect metadata generated for embeddings, when the exact word doesn't match the case, or is part of a larger word.
* 3.0.4 - 1 Sep 2023 - Simplify the installation for AMD users on Linux. Thanks @JeLuf.
* 3.0.4 - 1 Sep 2023 - Allow using a different folder for models. This is useful if you want to share a models folder across different software, or on a different drive. You can change this path in the Settings tab.
* 3.0.3 - 31 Aug 2023 - Auto-save images to disk (if enabled by the user) when upscaling/fixing using the buttons on the image.
* 3.0.3 - 30 Aug 2023 - Allow loading NovelAI-based custom models.
* 3.0.3 - 30 Aug 2023 - Fix broken VAE tiling. This allows you to create larger images with lesser VRAM usage.
* 3.0.3 - 30 Aug 2023 - Allow blocking NSFW images using a server-side config. This prevents the browser from generating NSFW images or changing the config. Open `config.yaml` in a text editor (e.g. Notepad), and add `block_nsfw: true` at the end, and save the file.
* 3.0.2 - 29 Aug 2023 - Fixed incorrect matching of embeddings from prompts.
* 3.0.2 - 24 Aug 2023 - Fix broken seamless tiling.
* 3.0.2 - 23 Aug 2023 - Fix styling on mobile devices.

View File

@ -47,3 +47,5 @@ Build the Windows installer using Windows, and the Linux installer using Linux.
1. Run `build.bat` or `./build.sh` depending on whether you're in Windows or Linux.
2. Make a new GitHub release and upload the Windows and Linux installer builds created inside the `dist` folder.
For NSIS (on Windows), you need to have these plugins in the `nsis/Plugins` folder: `amd64-unicode`, `x86-ansi`, `x86-unicode`

Binary file not shown.

Before

Width:  |  Height:  |  Size: 288 KiB

View File

@ -1 +0,0 @@
!define EXISTING_INSTALLATION_DIR "D:\path\to\installed\easy-diffusion"

Binary file not shown.

Before

Width:  |  Height:  |  Size: 200 KiB

View File

@ -7,9 +7,9 @@ RequestExecutionLevel user
!AddPluginDir /amd64-unicode "."
; HM NIS Edit Wizard helper defines
!define PRODUCT_NAME "Easy Diffusion"
!define PRODUCT_VERSION "2.5"
!define PRODUCT_VERSION "3.0"
!define PRODUCT_PUBLISHER "cmdr2 and contributors"
!define PRODUCT_WEB_SITE "https://stable-diffusion-ui.github.io"
!define PRODUCT_WEB_SITE "https://easydiffusion.github.io"
!define PRODUCT_DIR_REGKEY "Software\Microsoft\Easy Diffusion\App Paths\installer.exe"
; MUI 1.67 compatible ------
@ -165,9 +165,9 @@ FunctionEnd
; MUI Settings
;---------------------------------------------------------------------------------------------------------
!define MUI_ABORTWARNING
!define MUI_ICON "cyborg_flower_girl.ico"
!define MUI_ICON "${EXISTING_INSTALLATION_DIR}\installer_files\cyborg_flower_girl.ico"
!define MUI_WELCOMEFINISHPAGE_BITMAP "cyborg_flower_girl.bmp"
!define MUI_WELCOMEFINISHPAGE_BITMAP "${EXISTING_INSTALLATION_DIR}\installer_files\cyborg_flower_girl.bmp"
; Welcome page
!define MUI_WELCOMEPAGE_TEXT "This installer will guide you through the installation of Easy Diffusion.$\n$\n\
@ -176,8 +176,8 @@ Click Next to continue."
Page custom MediaPackDialog
; License page
!insertmacro MUI_PAGE_LICENSE "..\LICENSE"
!insertmacro MUI_PAGE_LICENSE "..\CreativeML Open RAIL-M License"
!insertmacro MUI_PAGE_LICENSE "${EXISTING_INSTALLATION_DIR}\LICENSE"
!insertmacro MUI_PAGE_LICENSE "${EXISTING_INSTALLATION_DIR}\CreativeML Open RAIL-M License"
; Directory page
!define MUI_PAGE_CUSTOMFUNCTION_LEAVE "DirectoryLeave"
!insertmacro MUI_PAGE_DIRECTORY
@ -210,29 +210,33 @@ ShowInstDetails show
; List of files to be installed
Section "MainSection" SEC01
SetOutPath "$INSTDIR"
File "..\CreativeML Open RAIL-M License"
File "..\How to install and run.txt"
File "..\LICENSE"
File "..\scripts\Start Stable Diffusion UI.cmd"
File "${EXISTING_INSTALLATION_DIR}\CreativeML Open RAIL-M License"
File "${EXISTING_INSTALLATION_DIR}\How to install and run.txt"
File "${EXISTING_INSTALLATION_DIR}\LICENSE"
File "${EXISTING_INSTALLATION_DIR}\Start Stable Diffusion UI.cmd"
File /r "${EXISTING_INSTALLATION_DIR}\installer_files"
File /r "${EXISTING_INSTALLATION_DIR}\profile"
File /r "${EXISTING_INSTALLATION_DIR}\sd-ui-files"
SetOutPath "$INSTDIR\installer_files"
File "cyborg_flower_girl.ico"
SetOutPath "$INSTDIR\scripts"
File "${EXISTING_INSTALLATION_DIR}\scripts\install_status.txt"
File "..\scripts\on_env_start.bat"
File "${EXISTING_INSTALLATION_DIR}\scripts\on_env_start.bat"
File "C:\windows\system32\curl.exe"
CreateDirectory "$INSTDIR\models"
File "${EXISTING_INSTALLATION_DIR}\scripts\config.yaml.sample"
CreateDirectory "$INSTDIR\models\stable-diffusion"
CreateDirectory "$INSTDIR\models\gfpgan"
CreateDirectory "$INSTDIR\models\realesrgan"
CreateDirectory "$INSTDIR\models\vae"
CreateDirectory "$INSTDIR\profile\.cache\huggingface\hub"
SetOutPath "$INSTDIR\profile\.cache\huggingface\hub"
File /r /x pytorch_model.bin "${EXISTING_INSTALLATION_DIR}\profile\.cache\huggingface\hub\models--openai--clip-vit-large-patch14"
CreateDirectory "$SMPROGRAMS\Easy Diffusion"
CreateShortCut "$SMPROGRAMS\Easy Diffusion\Easy Diffusion.lnk" "$INSTDIR\Start Stable Diffusion UI.cmd" "" "$INSTDIR\installer_files\cyborg_flower_girl.ico"
DetailPrint 'Downloading the Stable Diffusion 1.4 model...'
NScurl::http get "https://huggingface.co/CompVis/stable-diffusion-v-1-4-original/resolve/main/sd-v1-4.ckpt" "$INSTDIR\models\stable-diffusion\sd-v1-4.ckpt" /CANCEL /INSIST /END
DetailPrint 'Downloading the Stable Diffusion 1.5 model...'
NScurl::http get "https://github.com/easydiffusion/sdkit-test-data/releases/download/assets/sd-v1-5.safetensors" "$INSTDIR\models\stable-diffusion\sd-v1-5.safetensors" /CANCEL /INSIST /END
DetailPrint 'Downloading the GFPGAN model...'
NScurl::http get "https://github.com/TencentARC/GFPGAN/releases/download/v1.3.4/GFPGANv1.4.pth" "$INSTDIR\models\gfpgan\GFPGANv1.4.pth" /CANCEL /INSIST /END

View File

@ -3,7 +3,9 @@
Does not require technical knowledge, does not require pre-installed software. 1-click install, powerful features, friendly community.
[Installation guide](#installation) | [Troubleshooting guide](https://github.com/easydiffusion/easydiffusion/wiki/Troubleshooting) | <sub>[![Discord Server](https://img.shields.io/discord/1014774730907209781?label=Discord)](https://discord.com/invite/u9yhsFmEkB)</sub> <sup>(for support queries, and development discussions)</sup>
️‍🔥🎉 **New!** Support for SDXL, ControlNet, multiple LoRA files, embeddings (and a lot more) have been added!
[Installation guide](#installation) | [Troubleshooting guide](https://github.com/easydiffusion/easydiffusion/wiki/Troubleshooting) | [User guide](https://github.com/easydiffusion/easydiffusion/wiki) | <sub>[![Discord Server](https://img.shields.io/discord/1014774730907209781?label=Discord)](https://discord.com/invite/u9yhsFmEkB)</sub> <sup>(for support queries, and development discussions)</sup>
---
![262597678-11089485-2514-4a11-88fb-c3acc81fc9ec](https://github.com/easydiffusion/easydiffusion/assets/844287/050b5e15-e909-45bf-8162-a38234830e38)
@ -102,7 +104,7 @@ Just delete the `EasyDiffusion` folder to uninstall all the downloaded packages.
- **Auto scan for malicious models**: Uses picklescan to prevent malicious models.
- **Safetensors support**: Support loading models in the safetensor format, for improved safety.
- **Auto-updater**: Gets you the latest improvements and bug-fixes to a rapidly evolving project.
- **Developer Console**: A developer-mode for those who want to modify their Stable Diffusion code, and edit the conda environment.
- **Developer Console**: A developer-mode for those who want to modify their Stable Diffusion code, modify packages, and edit the conda environment.
**(and a lot more)**

View File

@ -1,48 +1,78 @@
@echo off
setlocal enabledelayedexpansion
@echo "Hi there, what you are running is meant for the developers of this project, not for users." & echo.
@echo "If you only want to use the Stable Diffusion UI, you've downloaded the wrong file."
@echo "If you only want to use Easy Diffusion, you've downloaded the wrong file."
@echo "Please download and follow the instructions at https://github.com/easydiffusion/easydiffusion#installation" & echo.
@echo "If you are actually a developer of this project, please type Y and press enter" & echo.
set /p answer=Are you a developer of this project (Y/N)?
if /i "%answer:~,1%" NEQ "Y" exit /b
mkdir dist\win\stable-diffusion-ui\scripts
@REM mkdir dist\linux-mac\stable-diffusion-ui\scripts
@rem verify dependencies
call makensis /VERSION >.tmp1 2>.tmp2
if "!ERRORLEVEL!" NEQ "0" (
echo makensis.exe not found! Download it from https://sourceforge.net/projects/nsisbi/files/ and set it on the PATH variable.
pause
exit
)
@rem copy the installer files for Windows
set /p OUT_DIR=Output folder path (will create the installer files inside this, e.g. F:\EasyDiffusion):
copy scripts\on_env_start.bat dist\win\stable-diffusion-ui\scripts\
copy scripts\bootstrap.bat dist\win\stable-diffusion-ui\scripts\
copy scripts\config.yaml.sample dist\win\stable-diffusion-ui\scripts\config.yaml
copy "scripts\Start Stable Diffusion UI.cmd" dist\win\stable-diffusion-ui\
copy LICENSE dist\win\stable-diffusion-ui\
copy "CreativeML Open RAIL-M License" dist\win\stable-diffusion-ui\
copy "How to install and run.txt" dist\win\stable-diffusion-ui\
echo. > dist\win\stable-diffusion-ui\scripts\install_status.txt
mkdir "%OUT_DIR%\scripts"
mkdir "%OUT_DIR%\installer_files"
@rem copy the installer files for Linux and Mac
set BASE_DIR=%cd%
@REM copy scripts\on_env_start.sh dist\linux-mac\stable-diffusion-ui\scripts\
@REM copy scripts\bootstrap.sh dist\linux-mac\stable-diffusion-ui\scripts\
@REM copy scripts\start.sh dist\linux-mac\stable-diffusion-ui\
@REM copy LICENSE dist\linux-mac\stable-diffusion-ui\
@REM copy "CreativeML Open RAIL-M License" dist\linux-mac\stable-diffusion-ui\
@REM copy "How to install and run.txt" dist\linux-mac\stable-diffusion-ui\
@REM echo. > dist\linux-mac\stable-diffusion-ui\scripts\install_status.txt
@rem STEP 1: copy the installer files for Windows
@rem make the zip
cd dist\win
call powershell Compress-Archive -Path stable-diffusion-ui -DestinationPath ..\stable-diffusion-ui-windows.zip
cd ..\..
@REM cd dist\linux-mac
@REM call powershell Compress-Archive -Path stable-diffusion-ui -DestinationPath ..\stable-diffusion-ui-linux.zip
@REM call powershell Compress-Archive -Path stable-diffusion-ui -DestinationPath ..\stable-diffusion-ui-mac.zip
@REM cd ..\..
echo "Build ready. Upload the zip files inside the 'dist' folder."
copy "%BASE_DIR%\scripts\on_env_start.bat" "%OUT_DIR%\scripts\"
copy "%BASE_DIR%\scripts\config.yaml.sample" "%OUT_DIR%\scripts\config.yaml.sample"
copy "%BASE_DIR%\scripts\Start Stable Diffusion UI.cmd" "%OUT_DIR%\"
copy "%BASE_DIR%\LICENSE" "%OUT_DIR%\"
copy "%BASE_DIR%\CreativeML Open RAIL-M License" "%OUT_DIR%\"
copy "%BASE_DIR%\How to install and run.txt" "%OUT_DIR%\"
copy "%BASE_DIR%\NSIS\cyborg_flower_girl.ico" "%OUT_DIR%\installer_files\"
copy "%BASE_DIR%\NSIS\cyborg_flower_girl.bmp" "%OUT_DIR%\installer_files\"
echo. > "%OUT_DIR%\scripts\install_status.txt"
echo ----
echo Basic files ready. Verify the files in %OUT_DIR%, then press Enter to initialize the environment, or close to quit.
echo ----
pause
@rem STEP 2: Initialize the environment with git, python and conda
cd /d "%OUT_DIR%\"
call "%BASE_DIR%\scripts\bootstrap.bat"
echo ----
echo Environment ready. Verify the environment, then press Enter to download the necessary packages, or close to quit.
echo ----
pause
@rem STEP 3: Download the packages and create a working installation
cd /d "%OUT_DIR%\"
start "Install Easy Diffusion" /D "%OUT_DIR%" "Start Stable Diffusion UI.cmd"
echo ----
echo Installation in progress (in a new window). Once complete, verify the installation, then press Enter to create an installer from these files, or close to quit.
echo ----
pause
@rem STEP 4: Build the installer from a working installation
cd /d "%OUT_DIR%\"
echo ^^!define EXISTING_INSTALLATION_DIR "%OUT_DIR%" > nsisconf.nsh
call makensis /NOCD /V4 "%BASE_DIR%\NSIS\sdui.nsi"
echo ----
if "!ERRORLEVEL!" EQU "0" (
echo Installer built successfully at %OUT_DIR%
) else (
echo Installer failed to build at %OUT_DIR%
)
echo ----
pause

View File

@ -1,7 +1,7 @@
#!/bin/bash
printf "Hi there, what you are running is meant for the developers of this project, not for users.\n\n"
printf "If you only want to use the Stable Diffusion UI, you've downloaded the wrong file.\n"
printf "If you only want to use Easy Diffusion, you've downloaded the wrong file.\n"
printf "Please download and follow the instructions at https://github.com/easydiffusion/easydiffusion#installation \n\n"
printf "If you are actually a developer of this project, please type Y and press enter\n\n"
@ -11,40 +11,30 @@ case $yn in
* ) exit;;
esac
# mkdir -p dist/win/stable-diffusion-ui/scripts
mkdir -p dist/linux-mac/stable-diffusion-ui/scripts
# copy the installer files for Windows
# cp scripts/on_env_start.bat dist/win/stable-diffusion-ui/scripts/
# cp scripts/bootstrap.bat dist/win/stable-diffusion-ui/scripts/
# cp "scripts/Start Stable Diffusion UI.cmd" dist/win/stable-diffusion-ui/
# cp LICENSE dist/win/stable-diffusion-ui/
# cp "CreativeML Open RAIL-M License" dist/win/stable-diffusion-ui/
# cp "How to install and run.txt" dist/win/stable-diffusion-ui/
# echo "" > dist/win/stable-diffusion-ui/scripts/install_status.txt
mkdir -p dist/linux-mac/easy-diffusion/scripts
# copy the installer files for Linux and Mac
cp scripts/on_env_start.sh dist/linux-mac/stable-diffusion-ui/scripts/
cp scripts/bootstrap.sh dist/linux-mac/stable-diffusion-ui/scripts/
cp scripts/functions.sh dist/linux-mac/stable-diffusion-ui/scripts/
cp scripts/config.yaml.sample dist/linux-mac/stable-diffusion-ui/scripts/config.yaml
cp scripts/start.sh dist/linux-mac/stable-diffusion-ui/
cp LICENSE dist/linux-mac/stable-diffusion-ui/
cp "CreativeML Open RAIL-M License" dist/linux-mac/stable-diffusion-ui/
cp "How to install and run.txt" dist/linux-mac/stable-diffusion-ui/
echo "" > dist/linux-mac/stable-diffusion-ui/scripts/install_status.txt
cp scripts/on_env_start.sh dist/linux-mac/easy-diffusion/scripts/
cp scripts/bootstrap.sh dist/linux-mac/easy-diffusion/scripts/
cp scripts/functions.sh dist/linux-mac/easy-diffusion/scripts/
cp scripts/config.yaml.sample dist/linux-mac/easy-diffusion/scripts/config.yaml.sample
cp scripts/start.sh dist/linux-mac/easy-diffusion/
cp LICENSE dist/linux-mac/easy-diffusion/
cp "CreativeML Open RAIL-M License" dist/linux-mac/easy-diffusion/
cp "How to install and run.txt" dist/linux-mac/easy-diffusion/
echo "" > dist/linux-mac/easy-diffusion/scripts/install_status.txt
# set the permissions
chmod u+x dist/linux-mac/easy-diffusion/scripts/on_env_start.sh
chmod u+x dist/linux-mac/easy-diffusion/scripts/bootstrap.sh
chmod u+x dist/linux-mac/easy-diffusion/start.sh
# make the zip
# cd dist/win
# zip -r ../stable-diffusion-ui-windows.zip stable-diffusion-ui
# cd ../..
cd dist/linux-mac
zip -r ../stable-diffusion-ui-linux.zip stable-diffusion-ui
zip -r ../stable-diffusion-ui-mac.zip stable-diffusion-ui
zip -r ../Easy-Diffusion-Linux.zip easy-diffusion
zip -r ../Easy-Diffusion-Mac.zip easy-diffusion
cd ../..
echo "Build ready. Upload the zip files inside the 'dist' folder."

Binary file not shown.

View File

@ -15,7 +15,7 @@ if exist "on_sd_start.bat" (
echo download. This will not work.
echo.
echo Recommended: Please close this window and download the installer from
echo https://stable-diffusion-ui.github.io/docs/installation/
echo https://easydiffusion.github.io/docs/installation/
echo.
echo ================================================================================
echo.
@ -39,6 +39,7 @@ call where conda
call conda --version
echo .
echo COMSPEC=%COMSPEC%
wmic path win32_VideoController get name,AdapterRAM,DriverDate,DriverVersion
@rem Download the rest of the installer and UI
call scripts\on_env_start.bat

View File

@ -14,6 +14,8 @@ set LEGACY_INSTALL_ENV_DIR=%cd%\installer
set MICROMAMBA_DOWNLOAD_URL=https://github.com/easydiffusion/easydiffusion/releases/download/v1.1/micromamba.exe
set umamba_exists=F
set PYTHONHOME=
set OLD_APPDATA=%APPDATA%
set OLD_USERPROFILE=%USERPROFILE%
set APPDATA=%cd%\installer_files\appdata
@ -22,15 +24,12 @@ set USERPROFILE=%cd%\profile
@rem figure out whether git and conda needs to be installed
if exist "%INSTALL_ENV_DIR%" set PATH=%INSTALL_ENV_DIR%;%INSTALL_ENV_DIR%\Library\bin;%INSTALL_ENV_DIR%\Scripts;%INSTALL_ENV_DIR%\Library\usr\bin;%PATH%
set PACKAGES_TO_INSTALL=
set PACKAGES_TO_INSTALL=git python=3.8.5
if not exist "%LEGACY_INSTALL_ENV_DIR%\etc\profile.d\conda.sh" (
if not exist "%INSTALL_ENV_DIR%\etc\profile.d\conda.sh" set PACKAGES_TO_INSTALL=%PACKAGES_TO_INSTALL% conda python=3.8.5
if not exist "%INSTALL_ENV_DIR%\etc\profile.d\conda.sh" set PACKAGES_TO_INSTALL=%PACKAGES_TO_INSTALL% conda
)
call git --version >.tmp1 2>.tmp2
if "!ERRORLEVEL!" NEQ "0" set PACKAGES_TO_INSTALL=%PACKAGES_TO_INSTALL% git
call "%MAMBA_ROOT_PREFIX%\micromamba.exe" --version >.tmp1 2>.tmp2
if "!ERRORLEVEL!" EQU "0" set umamba_exists=T

View File

@ -8,18 +8,21 @@ a custom index URL depending on the platform.
"""
import os
import os, sys
from importlib.metadata import version as pkg_version
import platform
import traceback
import shutil
from pathlib import Path
from pprint import pprint
os_name = platform.system()
modules_to_check = {
"torch": ("1.11.0", "1.13.1", "2.0.0"),
"torchvision": ("0.12.0", "0.14.1", "0.15.1"),
"sdkit": "2.0.3",
"stable-diffusion-sdkit": "2.1.4",
"torch": ("1.11.0", "1.13.1", "2.0.0", "2.0.1"),
"torchvision": ("0.12.0", "0.14.1", "0.15.1", "0.15.2"),
"sdkit": "2.0.15",
"stable-diffusion-sdkit": "2.1.5",
"rich": "12.6.0",
"uvicorn": "0.19.0",
"fastapi": "0.85.1",
@ -48,9 +51,20 @@ def install(module_name: str, module_version: str):
module_version, index_url = apply_torch_install_overrides(module_version)
if is_amd_on_linux(): # hack until AMD works properly on torch 2.0 (avoids black images on some cards)
amd_gpus = setup_amd_environment()
if module_name == "torch":
if "Navi 3" in amd_gpus:
# No AMD 7x00 support in rocm 5.2, needs nightly 5.5. build
module_version = "2.1.0.dev-20230614+rocm5.5"
index_url = "https://download.pytorch.org/whl/nightly/rocm5.5"
else:
module_version = "1.13.1+rocm5.2"
elif module_name == "torchvision":
if "Navi 3" in amd_gpus:
# No AMD 7x00 support in rocm 5.2, needs nightly 5.5. build
module_version = "0.16.0.dev-20230614+rocm5.5"
index_url = "https://download.pytorch.org/whl/nightly/rocm5.5"
else:
module_version = "0.14.1+rocm5.2"
elif os_name == "Darwin":
if module_name == "torch":
@ -69,9 +83,9 @@ def install(module_name: str, module_version: str):
os.system(install_cmd)
def init():
def update_modules():
for module_name, allowed_versions in modules_to_check.items():
if os.path.exists(f"../src/{module_name}"):
if os.path.exists(f"src/{module_name}"):
print(f"Skipping {module_name} update, since it's in developer/editable mode")
continue
@ -94,6 +108,10 @@ def init():
except:
traceback.print_exc()
fail(module_name)
else:
if version(module_name) != latest_version:
print(f"WARNING! Tried to install {module_name}=={latest_version}, but the version is still {version(module_name)}!")
if module_name in modules_to_log:
print(f"{module_name}: {version(module_name)}")
@ -159,7 +177,930 @@ Thanks!"""
)
exit(1)
### Launcher
### start
def get_config():
config_directory = os.path.dirname(__file__) # this will be "scripts"
config_yaml = os.path.join(config_directory, "..", "config.yaml")
config_json = os.path.join(config_directory, "config.json")
init()
config = None
# migrate the old config yaml location
config_legacy_yaml = os.path.join(config_directory, "config.yaml")
if os.path.isfile(config_legacy_yaml):
shutil.move(config_legacy_yaml, config_yaml)
if os.path.isfile(config_yaml):
from ruamel.yaml import YAML
yaml = YAML(typ='safe')
with open(config_yaml, 'r') as configfile:
try:
config = yaml.load(configfile)
except Exception as e:
print(e, file=sys.stderr)
elif os.path.isfile(config_json):
import json
with open(config_json, 'r') as configfile:
try:
config = json.load(configfile)
except Exception as e:
print(e, file=sys.stderr)
if config is None:
config = {}
return config
def setup_amd_environment():
if not os.access("/dev/kfd", os.W_OK):
print(
"#########################################################################\n"
+ "# EasyDiffusion has no write access to /dev/kfd. #\n"
+ "#########################################################################\n"
+ "\n"
+ "Without this, the ROCm driver will probably not be able to initialize the GPU and EasyDiffusion will use the CPU for rendering.\n"
+ "\n"
+ "Follow the instructions on this site to configure the access:\n"
+ "https://github.com/easydiffusion/easydiffusion/wiki/AMD-on-Linux#access-permissions\n"
+ "\n"
)
gpus = list(filter(lambda x: ("amdgpu" in x), open("/proc/bus/pci/devices", "r").readlines()))
gpus = [ x.split("\t")[1].upper() for x in gpus ]
gpus = [ AMD_PCI_IDs[x] for x in gpus if x in AMD_PCI_IDs ]
i=0
supported_gpus=[]
for gpu in gpus:
print(f"Found AMD GPU {gpu}.")
if gpu.startswith("Navi 1"):
print("--- Applying Navi 1 settings")
os.environ["HSA_OVERRIDE_GFX_VERSION"]="10.3.0"
os.environ["FORCE_FULL_PRECISION"]="yes"
os.environ["HIP_VISIBLE_DEVICES"]=str(i)
supported_gpus.append("Navi 1")
elif gpu.startswith("Navi 2"):
print("--- Applying Navi 2 settings")
os.environ["HSA_OVERRIDE_GFX_VERSION"]="10.3.0"
os.environ["HIP_VISIBLE_DEVICES"]=str(i)
supported_gpus.append("Navi 2")
elif gpu.startswith("Navi 3"):
print("--- Applying Navi 3 settings")
os.environ["HSA_OVERRIDE_GFX_VERSION"]="11.0.0"
os.environ["HIP_VISIBLE_DEVICES"]=str(i)
supported_gpus.append("Navi 3")
else:
print("--- This GPU is probably not supported by ROCm\n")
i+=1
return supported_gpus
def launch_uvicorn():
config = get_config()
pprint(config)
with open("scripts/install_status.txt","a") as f:
f.write("sd_weights_downloaded\n")
f.write("sd_install_complete\n")
print("\n\nEasy Diffusion installation complete, starting the server!\n\n")
os.environ["PYTORCH_ENABLE_MPS_FALLBACK"] = "1"
if os_name == "Windows":
os.environ["PYTHONPATH"] = str(Path( os.environ["INSTALL_ENV_DIR"], "lib", "site-packages"))
else:
os.environ["PYTHONPATH"] = str(Path( os.environ["INSTALL_ENV_DIR"], "lib", "python3.8", "site-packages"))
os.environ["SD_UI_PATH"] = str(Path(Path.cwd(), "ui"))
print(f"PYTHONPATH={os.environ['PYTHONPATH']}")
print(f"Python: {shutil.which('python')}")
print(f"Version: {platform. python_version()}")
bind_ip = "127.0.0.1"
listen_port = 9000
if "net" in config:
print("Checking network settings")
if "listen_port" in config["net"]:
listen_port = config["net"]["listen_port"]
print("Set listen port to ", listen_port)
if "listen_to_network" in config["net"] and config["net"]["listen_to_network"] == True:
if "bind_ip" in config["net"]:
bind_ip = config["net"]["bind_ip"]
else:
bind_ip = "0.0.0.0"
print("Set bind_ip to ", bind_ip)
os.chdir("stable-diffusion")
if is_amd_on_linux():
setup_amd_environment()
print("\nLaunching uvicorn\n")
os.system(f'python -m uvicorn main:server_api --app-dir "{os.environ["SD_UI_PATH"]}" --port {listen_port} --host {bind_ip} --log-level error')
### Start
# This list would probably be a good candidate for an import, but since PYTHONPATH and other settings
# have not been initialized yet, I keep the list here for the moment -- JeLuF
AMD_PCI_IDs = {
"1002AC0C": "Theater 506A USB",
"1002AC0D": "Theater 506A USB",
"1002AC0E": "Theater 506A External USB",
"1002AC0F": "Theater 506A External USB",
"1002AC12": "Theater HD T507 (DVB-T) TV tuner/capture device",
"1002AC02": "TV Wonder HD 600 PCIe",
"1002AC03": "Theater 506 PCIe",
"1002AC04": "Theater 506 USB",
"1002AC05": "Theater 506 USB",
"1002AC06": "Theater 506 External USB",
"1002AC07": "Theater 506 External USB",
"1002AC08": "Theater 506A World-Wide Analog Decoder + Demodulator",
"1002AC09": "Theater 506A World-Wide Analog Decoder + Demodulator",
"1002AC0A": "Theater 506A PCIe",
"1002AC0B": "Theater 506A PCIe",
"1002AC00": "Theater 506 World-Wide Analog Decoder",
"1002AC01": "Theater 506 World-Wide Analog Decoder",
"1002999D": "Richland [Radeon HD 8550D]",
"100299A0": "Trinity 2 [Radeon HD 7520G]",
"100299A2": "Trinity 2 [Radeon HD 7420G]",
"100299A4": "Trinity 2 [Radeon HD 7400G]",
"10029996": "Richland [Radeon HD 8470D]",
"10029997": "Richland [Radeon HD 8350G]",
"10029998": "Richland [Radeon HD 8370D]",
"10029999": "Richland [Radeon HD 8510G]",
"1002999A": "Richland [Radeon HD 8410G]",
"1002999B": "Richland [Radeon HD 8310G]",
"1002999C": "Richland [Radeon HD 8650D]",
"10029925": "Kingston/Clayton/Jupiter/Gladius/Montego HDMI Controller",
"10029926": "Jupiter",
"10029990": "Trinity 2 [Radeon HD 7520G]",
"10029991": "Trinity 2 [Radeon HD 7540D]",
"10029992": "Trinity 2 [Radeon HD 7420G]",
"10029993": "Trinity 2 [Radeon HD 7480D]",
"10029994": "Trinity 2 [Radeon HD 7400G]",
"10029995": "Richland [Radeon HD 8450G]",
"10029917": "Trinity [Radeon HD 7620G]",
"10029918": "Trinity [Radeon HD 7600G]",
"10029919": "Trinity [Radeon HD 7500G]",
"1002991E": "Bishop [Xbox One S APU]",
"10029920": "Liverpool [Playstation 4 APU]",
"10029922": "Starshp",
"10029923": "Starsha2 [Kingston/Clayton]",
"10029924": "Gladius",
"10029909": "Trinity [Radeon HD 7500G]",
"1002990A": "Trinity [Radeon HD 7500G]",
"1002990B": "Richland [Radeon HD 8650G]",
"1002990C": "Richland [Radeon HD 8670D]",
"1002990D": "Richland [Radeon HD 8550G]",
"1002990E": "Richland [Radeon HD 8570D]",
"1002990F": "Richland [Radeon HD 8610G]",
"10029910": "Trinity [Radeon HD 7660G]",
"10029913": "Trinity [Radeon HD 7640G]",
"10029901": "Trinity [Radeon HD 7660D]",
"10029903": "Trinity [Radeon HD 7640G]",
"10029904": "Trinity [Radeon HD 7560D]",
"10029905": "Trinity GL [FirePro A300]",
"10029906": "Trinity GL [FirePro A320]",
"10029907": "Trinity [Radeon HD 7620G]",
"10029908": "Trinity [Radeon HD 7600G]",
"1002985F": "Mullins",
"10029874": "Wani [Radeon R5/R6/R7 Graphics]",
"10029890": "Amur",
"100298C0": "Nolan",
"100298E4": "Stoney [Radeon R2/R3/R4/R5 Graphics]",
"10029900": "Trinity [Radeon HD 7660G]",
"10029858": "Mullins",
"10029859": "Mullins",
"1002985A": "Mullins",
"1002985B": "Mullins",
"1002985C": "Mullins",
"1002985D": "Mullins",
"1002985E": "Mullins",
"10029852": "Mullins [Radeon R2 Graphics]",
"10029853": "Mullins [Radeon R2 Graphics]",
"10029854": "Mullins [Radeon R3E Graphics]",
"10029855": "Mullins [Radeon R6 Graphics]",
"10029856": "Mullins [Radeon R1E/R2E Graphics]",
"10029857": "Mullins [Radeon APU XX-2200M with R2 Graphics]",
"10029836": "Kabini [Radeon HD 8280 / R3 Series]",
"10029837": "Kabini [Radeon HD 8280E]",
"10029838": "Kabini [Radeon HD 8240 / R3 Series]",
"10029839": "Kabini [Radeon HD 8180]",
"1002983D": "Temash [Radeon HD 8250/8280G]",
"10029850": "Mullins [Radeon R3 Graphics]",
"10029851": "Mullins [Radeon R4/R5 Graphics]",
"10029808": "Wrestler [Radeon HD 7340]",
"10029809": "Wrestler [Radeon HD 7310]",
"1002980A": "Wrestler [Radeon HD 7290]",
"10029830": "Kabini [Radeon HD 8400 / R3 Series]",
"10029831": "Kabini [Radeon HD 8400E]",
"10029832": "Kabini [Radeon HD 8330]",
"10029833": "Kabini [Radeon HD 8330E]",
"10029834": "Kabini [Radeon HD 8210]",
"10029835": "Kabini [Radeon HD 8310E]",
"10029714": "RS880 [Radeon HD 4290]",
"10029715": "RS880 [Radeon HD 4250]",
"10029802": "Wrestler [Radeon HD 6310]",
"10029803": "Wrestler [Radeon HD 6310]",
"10029804": "Wrestler [Radeon HD 6250]",
"10029805": "Wrestler [Radeon HD 6250]",
"10029806": "Wrestler [Radeon HD 6320]",
"10029807": "Wrestler [Radeon HD 6290]",
"1002964B": "Sumo",
"1002964C": "Sumo",
"1002964E": "Sumo",
"1002964F": "Sumo",
"10029710": "RS880 [Radeon HD 4200]",
"10029712": "RS880M [Mobility Radeon HD 4225/4250]",
"10029713": "RS880M [Mobility Radeon HD 4100]",
"10029643": "SuperSumo [Radeon HD 6380G]",
"10029644": "SuperSumo [Radeon HD 6410D]",
"10029645": "SuperSumo [Radeon HD 6410D]",
"10029647": "Sumo [Radeon HD 6520G]",
"10029648": "Sumo [Radeon HD 6480G]",
"10029649": "SuperSumo [Radeon HD 6480G]",
"1002964A": "Sumo [Radeon HD 6530D]",
"10029642": "SuperSumo [Radeon HD 6370D]",
"10029615": "RS780E [Radeon HD 3200]",
"10029610": "RS780 [Radeon HD 3200]",
"10029611": "RS780C [Radeon 3100]",
"10029612": "RS780M [Mobility Radeon HD 3200]",
"10029613": "RS780MC [Mobility Radeon HD 3100]",
"10029614": "RS780D [Radeon HD 3300]",
"10029616": "RS780L [Radeon 3000]",
"10029640": "Sumo [Radeon HD 6550D]",
"10029641": "Sumo [Radeon HD 6620G]",
"100295C4": "RV620/M82 [Mobility Radeon HD 3450/3470]",
"100295C5": "RV620 LE [Radeon HD 3450]",
"100295C6": "RV620 LE [Radeon HD 3450 AGP]",
"100295C9": "RV620 LE [Radeon HD 3450 PCI]",
"100295CC": "RV620 GL [FirePro V3700]",
"100295CD": "RV620 GL [FirePro 2450]",
"100295CF": "RV620 GL [FirePro 2260]",
"10029591": "RV635/M86 [Mobility Radeon HD 3650]",
"10029593": "RV635/M86 [Mobility Radeon HD 3670]",
"10029595": "RV635/M86 GL [Mobility FireGL V5700]",
"10029596": "RV635 PRO [Radeon HD 3650 AGP]",
"10029597": "RV635 PRO [Radeon HD 3650 AGP]",
"10029598": "RV635 [Radeon HD 3650/3750/4570/4580]",
"10029599": "RV635 PRO [Radeon HD 3650 AGP]",
"100295C0": "RV620 PRO [Radeon HD 3470]",
"100295C2": "RV620/M82 [Mobility Radeon HD 3410/3430]",
"10029586": "RV630 XT [Radeon HD 2600 XT AGP]",
"10029587": "RV630 PRO [Radeon HD 2600 PRO AGP]",
"10029588": "RV630 XT [Radeon HD 2600 XT]",
"10029589": "RV630 PRO [Radeon HD 2600 PRO]",
"1002958A": "RV630 [Radeon HD 2600 X2]",
"1002958B": "RV630/M76 [Mobility Radeon HD 2600 XT]",
"1002958C": "RV630 GL [FireGL V5600]",
"1002958D": "RV630 GL [FireGL V3600]",
"1002954F": "RV710 [Radeon HD 4350/4550]",
"10029552": "RV710/M92 [Mobility Radeon HD 4330/4350/4550]",
"10029553": "RV710/M92 [Mobility Radeon HD 4530/4570/5145/530v/540v/545v]",
"10029555": "RV711/M93 [Mobility Radeon HD 4350/4550/530v/540v/545v / FirePro RG220]",
"10029557": "RV711/M93 GL [FirePro RG220]",
"1002955F": "RV710/M92 [Mobility Radeon HD 4330]",
"10029580": "RV630 [Radeon HD 2600 PRO]",
"10029581": "RV630/M76 [Mobility Radeon HD 2600]",
"10029583": "RV630/M76 [Mobility Radeon HD 2600 XT/2700]",
"1002950F": "R680 [Radeon HD 3870 X2]",
"10029511": "RV670 GL [FireGL V7700]",
"10029513": "RV670 [Radeon HD 3850 X2]",
"10029515": "RV670 PRO [Radeon HD 3850 AGP]",
"10029519": "RV670 GL [FireStream 9170]",
"10029540": "RV710 [Radeon HD 4550]",
"10029500": "RV670 [Radeon HD 3850 X2]",
"10029501": "RV670 [Radeon HD 3870]",
"10029504": "RV670/M88 [Mobility Radeon HD 3850]",
"10029505": "RV670 [Radeon HD 3690/3850]",
"10029506": "RV670/M88 [Mobility Radeon HD 3850 X2]",
"10029507": "RV670 [Radeon HD 3830]",
"10029508": "RV670/M88-XT [Mobility Radeon HD 3870]",
"10029509": "RV670/M88 [Mobility Radeon HD 3870 X2]",
"100294C1": "RV610 [Radeon HD 2400 PRO/XT]",
"100294C3": "RV610 [Radeon HD 2400 PRO]",
"100294C4": "RV610 LE [Radeon HD 2400 PRO AGP]",
"100294C5": "RV610 [Radeon HD 2400 LE]",
"100294C7": "RV610 [Radeon HD 2350]",
"100294C8": "RV610/M74 [Mobility Radeon HD 2400 XT]",
"100294C9": "RV610/M72-S [Mobility Radeon HD 2400]",
"100294CB": "RV610 [Radeon E2400]",
"100294CC": "RV610 LE [Radeon HD 2400 PRO PCI]",
"10029498": "RV730 PRO [Radeon HD 4650]",
"1002949C": "RV730 GL [FirePro V7750]",
"1002949E": "RV730 GL [FirePro V5700]",
"1002949F": "RV730 GL [FirePro V3750]",
"100294A0": "RV740/M97 [Mobility Radeon HD 4830]",
"100294A1": "RV740/M97-XT [Mobility Radeon HD 4860]",
"100294A3": "RV740/M97 GL [FirePro M7740]",
"100294B3": "RV740 PRO [Radeon HD 4770]",
"100294B4": "RV740 PRO [Radeon HD 4750]",
"10029462": "RV790 [Radeon HD 4860]",
"1002946A": "RV770 GL [FirePro M7750]",
"10029480": "RV730/M96 [Mobility Radeon HD 4650/5165]",
"10029488": "RV730/M96-XT [Mobility Radeon HD 4670]",
"10029489": "RV730/M96 GL [Mobility FireGL V5725]",
"10029490": "RV730 XT [Radeon HD 4670]",
"10029491": "RV730/M96-CSP [Radeon E4690]",
"10029495": "RV730 [Radeon HD 4600 AGP Series]",
"1002944A": "RV770/M98L [Mobility Radeon HD 4850]",
"1002944B": "RV770/M98 [Mobility Radeon HD 4850 X2]",
"1002944C": "RV770 LE [Radeon HD 4830]",
"1002944E": "RV770 CE [Radeon HD 4710]",
"10029450": "RV770 GL [FireStream 9270]",
"10029452": "RV770 GL [FireStream 9250]",
"10029456": "RV770 GL [FirePro V8700]",
"1002945A": "RV770/M98-XT [Mobility Radeon HD 4870]",
"10029460": "RV790 [Radeon HD 4890]",
"1002940A": "R600 GL [FireGL V8650]",
"1002940B": "R600 GL [FireGL V8600]",
"1002940F": "R600 GL [FireGL V7600]",
"10029440": "RV770 [Radeon HD 4870]",
"10029441": "R700 [Radeon HD 4870 X2]",
"10029442": "RV770 [Radeon HD 4850]",
"10029443": "R700 [Radeon HD 4850 X2]",
"10029444": "RV770 GL [FirePro V8750]",
"10029446": "RV770 GL [FirePro V7760]",
"1002793F": "RS690M [Radeon Xpress 1200/1250/1270] (Secondary)",
"10027941": "RS600 [Radeon Xpress 1250]",
"10027942": "RS600M [Radeon Xpress 1250]",
"1002796E": "RS740 [Radeon 2100]",
"10029400": "R600 [Radeon HD 2900 PRO/XT]",
"10029401": "R600 [Radeon HD 2900 XT]",
"10029403": "R600 [Radeon HD 2900 PRO]",
"10029405": "R600 [Radeon HD 2900 GT]",
"1002791E": "RS690 [Radeon X1200]",
"1002791F": "RS690M [Radeon Xpress 1200/1250/1270]",
"10027835": "RS350M [Mobility Radeon 9000 IGP]",
"10027448": "Navi 31 [Radeon Pro W7900]",
"1002744C": "Navi 31 [Radeon RX 7900 XT/7900 XTX]",
"1002745E": "Navi 31 [Radeon Pro W7800]",
"10027480": "Navi 33 [Radeon RX 7700S/7600S/7600M XT]",
"10027483": "Navi 33 [Radeon RX 7600M/7600M XT]",
"10027489": "Navi 33",
"10027834": "RS350 [Radeon 9100 PRO/XT IGP]",
"10027446": "Navi 31 USB",
"1002743F": "Navi 24 [Radeon RX 6400/6500 XT/6500M]",
"10027421": "Navi 24 [Radeon PRO W6500M]",
"10027422": "Navi 24 [Radeon PRO W6400]",
"10027423": "Navi 24 [Radeon PRO W6300/W6300M]",
"10027424": "Navi 24 [Radeon RX 6300]",
"100273F0": "Navi 33 [Radeon RX 7600M XT]",
"100273EF": "Navi 23 [Radeon RX 6650 XT / 6700S / 6800S]",
"100273E1": "Navi 23 WKS-XM [Radeon PRO W6600M]",
"100273FF": "Navi 23 [Radeon RX 6600/6600 XT/6600M]",
"100273E3": "Navi 23 WKS-XL [Radeon PRO W6600]",
"100273E4": "Navi 23 USB",
"10027408": "Aldebaran/MI200 [Instinct MI250X]",
"1002740C": "Aldebaran/MI200 [Instinct MI250X/MI250]",
"1002740F": "Aldebaran/MI200 [Instinct MI210]",
"100273CE": "Navi 22-XL SRIOV MxGPU",
"100273AF": "Navi 21 [Radeon RX 6900 XT]",
"100273BF": "Navi 21 [Radeon RX 6800/6800 XT / 6900 XT]",
"100273C4": "Navi 22 USB",
"100273C3": "Navi 22",
"100273E0": "Navi 23",
"100273DF": "Navi 22 [Radeon RX 6700/6700 XT/6750 XT / 6800M/6850M XT]",
"100273A5": "Navi 21 [Radeon RX 6950 XT]",
"100273AE": "Navi 21 [Radeon Pro V620 MxGPU]",
"100273A2": "Navi 21 Pro-XTA [Radeon Pro W6900X]",
"100273A3": "Navi 21 GL-XL [Radeon PRO W6800]",
"100273A4": "Navi 21 USB",
"100273AB": "Navi 21 Pro-XLA [Radeon Pro W6800X/Radeon Pro W6800X Duo]",
"1002734F": "Navi 14 [Radeon Pro W5300M]",
"10027360": "Navi 12 [Radeon Pro 5600M/V520/BC-160]",
"100273A1": "Navi 21 [Radeon Pro V620]",
"10027362": "Navi 12 [Radeon Pro V520/V540]",
"10027388": "Arcturus GL-XL",
"1002738C": "Arcturus GL-XL [Instinct MI100]",
"1002738E": "Arcturus GL-XL [Instinct MI100]",
"10027319": "Navi 10 [Radeon Pro 5700 XT]",
"1002731B": "Navi 10 [Radeon Pro 5700]",
"1002731F": "Navi 10 [Radeon RX 5600 OEM/5600 XT / 5700/5700 XT]",
"10027340": "Navi 14 [Radeon RX 5500/5500M / Pro 5500M]",
"10027341": "Navi 14 [Radeon Pro W5500]",
"10027347": "Navi 14 [Radeon Pro W5500M]",
"1002731E": "TDC-150",
"100272A0": "RV570 [Radeon X1950 PRO] (Secondary)",
"100272A8": "RV570 [Radeon X1950 GT] (Secondary)",
"100272B1": "RV560 [Radeon X1650 XT] (Secondary)",
"100272B3": "RV560 [Radeon X1650 GT] (Secondary)",
"10027300": "Fiji [Radeon R9 FURY / NANO Series]",
"10027310": "Navi 10 [Radeon Pro W5700X]",
"10027312": "Navi 10 [Radeon Pro W5700]",
"10027314": "Navi 10 USB",
"1002724E": "R580 GL [FireGL V7350]",
"10027269": "R580 [Radeon X1900 XT] (Secondary)",
"1002726B": "R580 [Radeon X1900 GT] (Secondary)",
"1002726E": "R580 [AMD Stream Processor] (Secondary)",
"10027280": "RV570 [Radeon X1950 PRO]",
"10027288": "RV570 [Radeon X1950 GT]",
"10027291": "RV560 [Radeon X1650 XT]",
"10027293": "RV560 [Radeon X1650 GT]",
"100271F2": "RV530 GL [FireGL V3400] (Secondary)",
"10027210": "RV550/M71 [Mobility Radeon HD 2300]",
"10027211": "RV550/M71 [Mobility Radeon X2300 HD]",
"10027240": "R580+ [Radeon X1950 XTX]",
"10027244": "R580+ [Radeon X1950 XT]",
"10027248": "R580 [Radeon X1950]",
"10027249": "R580 [Radeon X1900 XT]",
"1002724B": "R580 [Radeon X1900 GT]",
"100271D6": "RV530/M66-XT [Mobility Radeon X1700]",
"100271DE": "RV530/M66 [Mobility Radeon X1700/X2500]",
"100271E0": "RV530 [Radeon X1600] (Secondary)",
"100271E1": "RV535 [Radeon X1650 PRO] (Secondary)",
"100271E2": "RV530 [Radeon X1600] (Secondary)",
"100271E6": "RV530 [Radeon X1650] (Secondary)",
"100271E7": "RV535 [Radeon X1650 PRO] (Secondary)",
"100271C4": "RV530/M56 GL [Mobility FireGL V5200]",
"100271C5": "RV530/M56-P [Mobility Radeon X1600]",
"100271C6": "RV530LE [Radeon X1600/X1650 PRO]",
"100271C7": "RV535 [Radeon X1650 PRO]",
"100271CE": "RV530 [Radeon X1300 XT/X1600 PRO]",
"100271D2": "RV530 GL [FireGL V3400]",
"100271D4": "RV530/M66 GL [Mobility FireGL V5250]",
"100271D5": "RV530/M66-P [Mobility Radeon X1700]",
"100271A7": "RV516 [Radeon X1300/X1550 Series] (Secondary)",
"100271BB": "RV516 GL [FireMV 2250] (Secondary)",
"100271C0": "RV530 [Radeon X1600 XT/X1650 GTO]",
"100271C1": "RV535 [Radeon X1650 PRO]",
"100271C2": "RV530 [Radeon X1600 PRO]",
"100271C3": "RV530 [Radeon X1600 PRO]",
"1002718D": "RV516/M64-CSP128 [Mobility Radeon X1450]",
"10027193": "RV516 [Radeon X1550 Series]",
"10027196": "RV516/M62-S [Mobility Radeon X1350]",
"1002719B": "RV516 GL [FireMV 2250]",
"1002719F": "RV516 [Radeon X1550 Series]",
"100271A0": "RV516 [Radeon X1300/X1550 Series] (Secondary)",
"100271A1": "RV516 [Radeon X1600/X1650 Series] (Secondary)",
"100271A3": "RV516 [Radeon X1300/X1550 Series] (Secondary)",
"10027186": "RV516/M64 [Mobility Radeon X1450]",
"10027187": "RV516 [Radeon X1300/X1550 Series]",
"10027188": "RV516/M64-S [Mobility Radeon X2300]",
"1002718A": "RV516/M64 [Mobility Radeon X2300]",
"1002718B": "RV516/M62 [Mobility Radeon X1350]",
"1002718C": "RV516/M62-CSP64 [Mobility Radeon X1350]",
"10027162": "RV515 PRO [Radeon X1300/X1550 Series] (Secondary)",
"10027163": "RV505 [Radeon X1550 Series] (Secondary)",
"10027166": "RV515 [Radeon X1300/X1550 Series] (Secondary)",
"10027167": "RV515 [Radeon X1550 64-bit] (Secondary)",
"10027172": "RV515 GL [FireGL V3300] (Secondary)",
"10027173": "RV515 GL [FireGL V3350] (Secondary)",
"10027181": "RV516 [Radeon X1600/X1650 Series]",
"10027183": "RV516 [Radeon X1300/X1550 Series]",
"10027145": "RV515/M54 [Mobility Radeon X1400]",
"10027146": "RV515 [Radeon X1300/X1550]",
"10027147": "RV505 [Radeon X1550 64-bit]",
"10027149": "RV515/M52 [Mobility Radeon X1300]",
"1002714A": "RV515/M52 [Mobility Radeon X1300]",
"10027152": "RV515 GL [FireGL V3300]",
"10027153": "RV515 GL [FireGL V3350]",
"1002715F": "RV505 CE [Radeon X1550 64-bit]",
"10027120": "R520 [Radeon X1800] (Secondary)",
"10027124": "R520 GL [FireGL V7200] (Secondary)",
"10027129": "R520 [Radeon X1800] (Secondary)",
"1002712E": "R520 GL [FireGL V7300] (Secondary)",
"1002712F": "R520 GL [FireGL V7350] (Secondary)",
"10027140": "RV515 [Radeon X1300/X1550/X1600 Series]",
"10027142": "RV515 PRO [Radeon X1300/X1550 Series]",
"10027143": "RV505 [Radeon X1300/X1550 Series]",
"10027102": "R520/M58 [Mobility Radeon X1800]",
"10027104": "R520 GL [FireGL V7200 / Barco MXTR-5100]",
"10027109": "R520 [Radeon X1800 XL]",
"1002710A": "R520 [Radeon X1800 GTO]",
"1002710B": "R520 [Radeon X1800 GTO]",
"1002710E": "R520 GL [FireGL V7300]",
"1002710F": "R520 GL [FireGL V7350]",
"100269A1": "Vega 12",
"100269A2": "Vega 12",
"100269A3": "Vega 12",
"100269AF": "Vega 12 [Radeon Pro Vega 20]",
"10026FDF": "Polaris 20 XL [Radeon RX 580 2048SP]",
"10027100": "R520 [Radeon X1800 XT]",
"10027101": "R520/M58 [Mobility Radeon X1800 XT]",
"10026980": "Polaris12",
"10026981": "Lexa XT [Radeon PRO WX 3200]",
"10026985": "Lexa XT [Radeon PRO WX 3100]",
"10026986": "Polaris12",
"10026987": "Lexa [Radeon 540X/550X/630 / RX 640 / E9171 MCM]",
"10026995": "Lexa XT [Radeon PRO WX 2100]",
"1002699F": "Lexa PRO [Radeon 540/540X/550/550X / RX 540X/550/550X]",
"100269A0": "Vega 12",
"1002698F": "Lexa XT [Radeon PRO WX 3100 / Barco MXRT 4700]",
"1002692F": "Tonga XTV GL [FirePro S7150V]",
"10026938": "Tonga XT / Amethyst XT [Radeon R9 380X / R9 M295X]",
"10026939": "Tonga PRO [Radeon R9 285/380]",
"1002694C": "Polaris 22 XT [Radeon RX Vega M GH]",
"1002694E": "Polaris 22 XL [Radeon RX Vega M GL]",
"1002694F": "Polaris 22 MGL XL [Radeon Pro WX Vega M GL]",
"10026930": "Tonga PRO [Radeon R9 380 4GB]",
"1002693B": "Tonga PRO GL [FirePro W7100 / Barco MXRT-7600]",
"100268FE": "Cedar LE",
"10026900": "Topaz XT [Radeon R7 M260/M265 / M340/M360 / M440/M445 / 530/535 / 620/625 Mobile]",
"10026901": "Topaz PRO [Radeon R5 M255]",
"10026907": "Meso XT [Radeon R5 M315]",
"10026920": "Amethyst [Radeon R9 M395/ M395X Mac Edition]",
"10026921": "Amethyst XT [Radeon R9 M295X / M390X]",
"10026929": "Tonga XT GL [FirePro S7150]",
"1002692B": "Tonga PRO GL [FirePro W7100]",
"100268FA": "Cedar [Radeon HD 7350/8350 / R5 220]",
"100268E4": "Robson CE [Radeon HD 6370M/7370M]",
"100268E5": "Robson LE [Radeon HD 6330M]",
"100268E8": "Cedar",
"100268E9": "Cedar [ATI FirePro (FireGL) Graphics Adapter]",
"100268F1": "Cedar GL [FirePro 2460]",
"100268F2": "Cedar GL [FirePro 2270]",
"100268F8": "Cedar [Radeon HD 7300 Series]",
"100268F9": "Cedar [Radeon HD 5000/6000/7350/8350 Series]",
"100268C8": "Redwood XT GL [FirePro V4800]",
"100268C9": "Redwood PRO GL [FirePro V3800]",
"100268D8": "Redwood XT [Radeon HD 5670/5690/5730]",
"100268D9": "Redwood PRO [Radeon HD 5550/5570/5630/6510/6610/7570]",
"100268DA": "Redwood LE [Radeon HD 5550/5570/5630/6390/6490/7570]",
"100268DE": "Redwood",
"100268E0": "Park [Mobility Radeon HD 5430/5450/5470]",
"100268E1": "Park [Mobility Radeon HD 5430]",
"100268A9": "Juniper XT [FirePro V5800]",
"100268B8": "Juniper XT [Radeon HD 5770]",
"100268B9": "Juniper LE [Radeon HD 5670 640SP Edition]",
"100268BA": "Juniper XT [Radeon HD 6770]",
"100268BE": "Juniper PRO [Radeon HD 5750]",
"100268BF": "Juniper PRO [Radeon HD 6750]",
"100268C0": "Madison [Mobility Radeon HD 5730 / 6570M]",
"100268C1": "Madison [Mobility Radeon HD 5650/5750 / 6530M/6550M]",
"100268C7": "Pinewood [Mobility Radeon HD 5570/6550A]",
"10026898": "Cypress XT [Radeon HD 5870]",
"10026899": "Cypress PRO [Radeon HD 5850]",
"1002689B": "Cypress PRO [Radeon HD 6800 Series]",
"1002689C": "Hemlock [Radeon HD 5970]",
"1002689D": "Hemlock [Radeon HD 5970]",
"1002689E": "Cypress LE [Radeon HD 5830]",
"100268A0": "Broadway XT [Mobility Radeon HD 5870]",
"100268A1": "Broadway PRO [Mobility Radeon HD 5850]",
"100268A8": "Granville [Radeon HD 6850M/6870M]",
"1002686D": "Vega 10 GLXTA",
"1002686E": "Vega 10 GLXLA",
"1002687F": "Vega 10 XL/XT [Radeon RX Vega 56/64]",
"10026880": "Lexington [Radeon HD 6550M]",
"10026888": "Cypress XT [FirePro V8800]",
"10026889": "Cypress PRO [FirePro V7800]",
"1002688A": "Cypress XT [FirePro V9800]",
"1002688C": "Cypress XT GL [FireStream 9370]",
"1002688D": "Cypress PRO GL [FireStream 9350]",
"10026869": "Vega 10 XGA [Radeon Pro Vega 48]",
"1002686A": "Vega 10 LEA",
"1002686B": "Vega 10 XTXA [Radeon Pro Vega 64X]",
"10026862": "Vega 10 XT [Radeon PRO SSG]",
"10026863": "Vega 10 XTX [Radeon Vega Frontier Edition]",
"10026864": "Vega 10 [Radeon Pro V340/Instinct MI25x2]",
"10026867": "Vega 10 XL [Radeon Pro Vega 56]",
"10026868": "Vega 10 [Radeon PRO WX 8100/8200]",
"1002686C": "Vega 10 [Instinct MI25 MxGPU/MI25x2 MxGPU/V340 MxGPU/V340L MxGPU]",
"1002683F": "Cape Verde PRO [Radeon HD 7750/8740 / R7 250E]",
"10026840": "Thames [Radeon HD 7500M/7600M Series]",
"10026841": "Thames [Radeon HD 7550M/7570M/7650M]",
"10026842": "Thames LE [Radeon HD 7000M Series]",
"10026843": "Thames [Radeon HD 7670M]",
"10026860": "Vega 10 [Instinct MI25/MI25x2/V340/V320]",
"10026861": "Vega 10 XT [Radeon PRO WX 9100]",
"10026828": "Cape Verde PRO [FirePro W600]",
"10026829": "Cape Verde",
"1002682A": "Venus PRO",
"1002682B": "Cape Verde PRO / Venus LE / Tropo PRO-L [Radeon HD 8830M / R7 250 / R7 M465X]",
"1002682C": "Cape Verde GL [FirePro W4100]",
"1002682D": "Chelsea XT GL [FirePro M4000]",
"1002682F": "Chelsea LP [Radeon HD 7730M]",
"10026835": "Cape Verde PRX [Radeon R9 255 OEM]",
"10026837": "Cape Verde LE [Radeon HD 7730/8730]",
"1002683D": "Cape Verde XT [Radeon HD 7770/8760 / R7 250X]",
"10026820": "Venus XTX [Radeon HD 8890M / R9 M275X/M375X]",
"10026821": "Venus XT [Radeon HD 8870M / R9 M270X/M370X]",
"10026822": "Venus PRO [Radeon E8860]",
"10026823": "Venus PRO [Radeon HD 8850M / R9 M265X]",
"10026825": "Heathrow XT [Radeon HD 7870M]",
"10026826": "Chelsea LP [Radeon HD 7700M Series]",
"10026827": "Heathrow PRO [Radeon HD 7850M/8850M]",
"10026808": "Pitcairn XT GL [FirePro W7000]",
"10026809": "Pitcairn LE GL [FirePro W5000]",
"10026810": "Curacao XT / Trinidad XT [Radeon R7 370 / R9 270X/370X]",
"10026811": "Curacao PRO [Radeon R7 370 / R9 270/370 OEM]",
"10026816": "Pitcairn",
"10026817": "Pitcairn",
"10026818": "Pitcairn XT [Radeon HD 7870 GHz Edition]",
"10026819": "Pitcairn PRO [Radeon HD 7850 / R7 265 / R9 270 1024SP]",
"100267EB": "Baffin [Radeon Pro V5300X]",
"100267EF": "Baffin [Radeon RX 460/560D / Pro 450/455/460/555/555X/560/560X]",
"100267FF": "Baffin [Radeon RX 550 640SP / RX 560/560X]",
"10026800": "Wimbledon XT [Radeon HD 7970M]",
"10026801": "Neptune XT [Radeon HD 8970M]",
"10026802": "Wimbledon",
"10026806": "Neptune",
"100267DF": "Ellesmere [Radeon RX 470/480/570/570X/580/580X/590]",
"100267E0": "Baffin [Radeon Pro WX 4170]",
"100267E1": "Baffin [Polaris11]",
"100267E3": "Baffin [Radeon Pro WX 4100]",
"100267E8": "Baffin [Radeon Pro WX 4130/4150]",
"100267E9": "Baffin [Polaris11]",
"100267D7": "Ellesmere [Radeon Pro WX 5100 / Barco MXRT-6700]",
"100267C7": "Ellesmere [Radeon Pro WX 5100]",
"100267CA": "Ellesmere [Polaris10]",
"100267CC": "Ellesmere [Polaris10]",
"100267CF": "Ellesmere [Polaris10]",
"100267D0": "Ellesmere [Radeon Pro V7300X / V7350x2]",
"100267D4": "Ellesmere [Radeon Pro WX 7100 / Barco MXRT-8700]",
"100267B9": "Vesuvius [Radeon R9 295X2]",
"100267BE": "Hawaii LE",
"100267C0": "Ellesmere [Radeon Pro WX 7100 Mobile]",
"100267C2": "Ellesmere [Radeon Pro V7300X / V7350x2]",
"100267C4": "Ellesmere [Radeon Pro WX 7100]",
"100267B8": "Hawaii XT [Radeon R9 290X Engineering Sample]",
"100267A2": "Hawaii GL",
"100267A8": "Hawaii",
"100267A9": "Hawaii",
"100267AA": "Hawaii",
"100267B0": "Hawaii XT / Grenada XT [Radeon R9 290X/390X]",
"100267B1": "Hawaii PRO [Radeon R9 290/390]",
"1002679A": "Tahiti PRO [Radeon HD 7950/8950 OEM / R9 280]",
"1002679B": "Malta [Radeon HD 7990/8990 OEM]",
"1002679E": "Tahiti LE [Radeon HD 7870 XT]",
"1002679F": "Tahiti",
"100267A0": "Hawaii XT GL [FirePro W9100]",
"100267A1": "Hawaii PRO GL [FirePro W8100]",
"10026780": "Tahiti XT GL [FirePro W9000]",
"10026784": "Tahiti [FirePro Series Graphics Adapter]",
"10026788": "Tahiti [FirePro Series Graphics Adapter]",
"1002678A": "Tahiti PRO GL [FirePro Series]",
"10026798": "Tahiti XT [Radeon HD 7970/8970 OEM / R9 280X]",
"10026770": "Caicos [Radeon HD 6450A/7450A]",
"10026771": "Caicos XTX [Radeon HD 8490 / R5 235X OEM]",
"10026772": "Caicos [Radeon HD 7450A]",
"10026778": "Caicos XT [Radeon HD 7470/8470 / R5 235/310 OEM]",
"10026779": "Caicos [Radeon HD 6450/7450/8450 / R5 230 OEM]",
"1002677B": "Caicos PRO [Radeon HD 7450]",
"10026761": "Seymour LP [Radeon HD 6430M]",
"10026763": "Seymour [Radeon E6460]",
"10026764": "Seymour [Radeon HD 6400M Series]",
"10026765": "Seymour [Radeon HD 6400M Series]",
"10026766": "Caicos",
"10026767": "Caicos",
"10026768": "Caicos",
"10026759": "Turks PRO [Radeon HD 6570/7570/8550 / R5 230]",
"1002675B": "Turks [Radeon HD 7600 Series]",
"1002675D": "Turks PRO [Radeon HD 7570]",
"1002675F": "Turks LE [Radeon HD 5570/6510/7510/8510]",
"10026760": "Seymour [Radeon HD 6400M/7400M Series]",
"10026743": "Whistler [Radeon E6760]",
"10026749": "Turks GL [FirePro V4900]",
"1002674A": "Turks GL [FirePro V3900]",
"10026750": "Onega [Radeon HD 6650A/7650A]",
"10026751": "Turks [Radeon HD 7650A/7670A]",
"10026758": "Turks XT [Radeon HD 6670/7670]",
"1002671F": "Cayman CE [Radeon HD 6930]",
"10026720": "Blackcomb [Radeon HD 6970M/6990M]",
"10026738": "Barts XT [Radeon HD 6870]",
"10026739": "Barts PRO [Radeon HD 6850]",
"1002673E": "Barts LE [Radeon HD 6790]",
"10026740": "Whistler [Radeon HD 6730M/6770M/7690M XT]",
"10026741": "Whistler [Radeon HD 6630M/6650M/6750M/7670M/7690M]",
"10026742": "Whistler LE [Radeon HD 6610M/7610M]",
"10026707": "Cayman LE GL [FirePro V5900]",
"10026718": "Cayman XT [Radeon HD 6970]",
"10026719": "Cayman PRO [Radeon HD 6950]",
"1002671C": "Antilles [Radeon HD 6990]",
"1002671D": "Antilles [Radeon HD 6990]",
"100266A2": "Vega 20",
"100266A3": "Vega 20 [Radeon Pro Vega II/Radeon Pro Vega II Duo]",
"100266A7": "Vega 20 [Radeon Pro Vega 20]",
"100266AF": "Vega 20 [Radeon VII]",
"10026704": "Cayman PRO GL [FirePro V7900]",
"10026665": "Jet PRO [Radeon R5 M230 / R7 M260DX / Radeon 520/610 Mobile]",
"10026667": "Jet ULT [Radeon R5 M230]",
"1002666F": "Sun LE [Radeon HD 8550M / R5 M230]",
"100266A0": "Vega 20 [Radeon Pro/Radeon Instinct]",
"100266A1": "Vega 20 [Radeon Pro VII/Radeon Instinct MI50 32GB]",
"10026658": "Bonaire XTX [Radeon R7 260X/360]",
"1002665C": "Bonaire XT [Radeon HD 7790/8770 / R7 360 / R9 260/360 OEM]",
"1002665D": "Bonaire [Radeon R7 200 Series]",
"1002665F": "Tobago PRO [Radeon R7 360 / R9 360 OEM]",
"10026660": "Sun XT [Radeon HD 8670A/8670M/8690M / R5 M330 / M430 / Radeon 520 Mobile]",
"10026663": "Sun PRO [Radeon HD 8570A/8570M]",
"10026664": "Jet XT [Radeon R5 M240]",
"10026631": "Oland",
"10026640": "Saturn XT [FirePro M6100]",
"10026641": "Saturn PRO [Radeon HD 8930M]",
"10026646": "Bonaire XT [Radeon R9 M280X / FirePro W6150M]",
"10026647": "Saturn PRO/XT [Radeon R9 M270X/M280X]",
"10026649": "Bonaire [FirePro W5100]",
"10026650": "Bonaire",
"10026651": "Bonaire",
"1002664D": "Bonaire [FirePro W5100 / Barco MXRT-5600]",
"10026606": "Mars XTX [Radeon HD 8790M]",
"10026607": "Mars LE [Radeon HD 8530M / R5 M240]",
"10026608": "Oland GL [FirePro W2100]",
"10026610": "Oland XT [Radeon HD 8670 / R5 340X OEM / R7 250/350/350X OEM]",
"10026611": "Oland [Radeon HD 8570 / R5 430 OEM / R7 240/340 / Radeon 520 OEM]",
"10026613": "Oland PRO [Radeon R7 240/340 / Radeon 520]",
"10026617": "Oland LE [Radeon R7 240]",
"10026609": "Oland GL [FirePro W2100 / Barco MXRT 2600]",
"10025E4F": "RV410 [Radeon X700]",
"10025E6B": "RV410 [Radeon X700 PRO] (Secondary)",
"10025E6D": "RV410 [Radeon X700] (Secondary)",
"10025F57": "R423 [Radeon X800 XT]",
"10026600": "Mars [Radeon HD 8670A/8670M/8750M / R7 M370]",
"10026601": "Mars [Radeon HD 8730M]",
"10026604": "Opal XT [Radeon R7 M265/M365X/M465]",
"10026605": "Opal PRO [Radeon R7 M260X]",
"10025D72": "R480 [Radeon X850 XT] (Secondary)",
"10025D77": "R423 [Radeon X800 XT] (Secondary)",
"10025E48": "RV410 GL [FireGL V5000]",
"10025E49": "RV410 [Radeon X700 Series]",
"10025E4A": "RV410 [Radeon X700 XT]",
"10025E4B": "RV410 [Radeon X700 PRO]",
"10025E4C": "RV410 [Radeon X700 SE]",
"10025E4D": "RV410 [Radeon X700]",
"10025D4D": "R480 [Radeon X850 XT Platinum Edition]",
"10025D4E": "R480 [Radeon X850 SE]",
"10025D4F": "R480 [Radeon X800 GTO]",
"10025D50": "R480 GL [FireGL V7200]",
"10025D52": "R480 [Radeon X850 XT]",
"10025D57": "R423 [Radeon X800 XT]",
"10025D6D": "R480 [Radeon X850 XT Platinum Edition] (Secondary)",
"10025D6F": "R480 [Radeon X800 GTO] (Secondary)",
"10025B73": "RV370 [Radeon X300/X550/X1050 Series] (Secondary)",
"10025B74": "RV370 GL [FireGL V3100] (Secondary)",
"10025B75": "RV370 GL [FireMV 2200] (Secondary)",
"10025C61": "RV280/M9+ [Mobility Radeon 9200 AGP]",
"10025C63": "RV280/M9+ [Mobility Radeon 9200 AGP]",
"10025D44": "RV280 [Radeon 9200 SE] (Secondary)",
"10025D45": "RV280 GL [FireMV 2200 PCI] (Secondary)",
"10025D48": "R423/M28 [Mobility Radeon X800 XT]",
"10025D49": "R423/M28 GL [Mobility FireGL V5100]",
"10025D4A": "R423/M28 [Mobility Radeon X800]",
"10025A62": "RC410M [Mobility Radeon Xpress 200M]",
"10025B60": "RV370 [Radeon X300]",
"10025B62": "RV370 [Radeon X600/X600 SE]",
"10025B63": "RV370 [Radeon X300/X550/X1050 Series]",
"10025B64": "RV370 GL [FireGL V3100]",
"10025B65": "RV370 GL [FireMV 2200]",
"10025B66": "RV370X",
"10025B70": "RV370 [Radeon X300 SE]",
"10025B72": "RV380 [Radeon X300/X550/X1050 Series] (Secondary)",
"10025A36": "RC4xx/RS4xx PCI Express Port 1",
"10025A37": "RC4xx/RS4xx PCI Express Port 2",
"10025A38": "RC4xx/RS4xx PCI Express Port 3",
"10025A39": "RC4xx/RS4xx PCI Express Port 4",
"10025A41": "RS400 [Radeon Xpress 200]",
"10025A42": "RS400M [Radeon Xpress 200M]",
"10025A61": "RC410 [Radeon Xpress 200/1100]",
"10025A23": "RD890S/RD990 I/O Memory Management Unit (IOMMU)",
"10025A34": "RS4xx PCI Express Port [ext gfx]",
"10025965": "RV280 GL [FireMV 2200 PCI]",
"10025974": "RS482/RS485 [Radeon Xpress 1100/1150]",
"10025975": "RS482M [Mobility Radeon Xpress 200]",
"10025960": "RV280 [Radeon 9200 PRO / 9250]",
"10025961": "RV280 [Radeon 9200]",
"10025962": "RV280 [Radeon 9200]",
"10025964": "RV280 [Radeon 9200 SE]",
"10025940": "RV280 [Radeon 9200 PRO] (Secondary)",
"10025941": "RV280 [Radeon 9200] (Secondary)",
"10025944": "RV280 [Radeon 9200 SE PCI]",
"10025954": "RS480 [Radeon Xpress 200 Series]",
"10025955": "RS480M [Mobility Radeon Xpress 200]",
"10025834": "RS300 [Radeon 9100 IGP]",
"10025835": "RS300M [Mobility Radeon 9100 IGP]",
"10025854": "RS480 [Radeon Xpress 200 Series] (Secondary)",
"10025874": "RS480 [Radeon Xpress 1150] (Secondary)",
"1002564B": "RV410/M26 GL [Mobility FireGL V5000]",
"1002564F": "RV410/M26 [Mobility Radeon X700 XL]",
"10025652": "RV410/M26 [Mobility Radeon X700]",
"10025653": "RV410/M26 [Mobility Radeon X700]",
"10025654": "Mach64 VT [Video Xpression]",
"10025655": "264VT3 [Mach64 VT3]",
"10025656": "Mach64 VT4 [Video Xpression+]",
"10025657": "RV410 [Radeon X550 XTX / X700]",
"1002554E": "R430 [All-In-Wonder X800 GT]",
"1002554F": "R430 [Radeon X800]",
"10025550": "R423 GL [FireGL V7100]",
"10025551": "R423 GL [FireGL V5100]",
"10025569": "R423 [Radeon X800 PRO] (Secondary)",
"1002556B": "R423 [Radeon X800 GT] (Secondary)",
"1002556D": "R480 [Radeon X800 GTO2/XL] (Secondary)",
"1002556F": "R430 [Radeon X800] (Secondary)",
"10025571": "R423 GL [FireGL V5100] (Secondary)",
"10025462": "RV380/M24C [Mobility Radeon X600 SE]",
"10025464": "RV370/M22 GL [Mobility FireGL V3100]",
"10025549": "R423 [Radeon X800 GTO]",
"1002554A": "R423 [Radeon X800 XT Platinum Edition]",
"1002554B": "R423 [Radeon X800 GT/SE]",
"1002554D": "R480 [Radeon X800 GTO2/XL]",
"1002524C": "Rage 128 VR AGP",
"1002534D": "Rage 128 4X AGP 4x",
"10025354": "Mach 64 VT",
"10025446": "Rage 128 PRO Ultra AGP 4x",
"10025452": "Rage 128 PRO Ultra4XL VR-R AGP",
"10025460": "RV370/M22 [Mobility Radeon X300]",
"10025461": "RV370/M22 [Mobility Radeon X300]",
"1002514C": "R200 [Radeon 8500/8500 LE]",
"1002514D": "R200 [Radeon 9100]",
"10025157": "RV200 [Radeon 7500/7500 LE]",
"10025159": "RV100 [Radeon 7000 / Radeon VE]",
"1002515E": "ES1000",
"10025245": "Rage 128 GL PCI",
"10025246": "Rage 128 (Rage 4) series",
"1002524B": "Rage 128 VR PCI",
"10024E6A": "RV350 [Radeon 9800 XT] (Secondary)",
"10024E71": "RV350/M10 [Mobility Radeon 9600] (Secondary)",
"10024F72": "RV250 [Radeon 9000 Series]",
"10024F73": "RV250 [Radeon 9000 Series] (Secondary)",
"10025044": "All-In-Wonder 128 PCI",
"10025046": "Rage 4 [Rage 128 PRO AGP 4X]",
"10025050": "Rage 4 [Rage 128 PRO PCI / Xpert 128 PCI]",
"10025052": "Rage 4 [Rage 128 PRO AGP 4X]",
"10025144": "R100 [Radeon 7200 / All-In-Wonder Radeon]",
"10025148": "R200 GL [FireGL 8800]",
"10024E54": "RV350/M10 GL [Mobility FireGL T2]",
"10024E56": "RV360/M12 [Mobility Radeon 9550]",
"10024E64": "R300 [Radeon 9700 PRO] (Secondary)",
"10024E65": "R300 [Radeon 9500 PRO] (Secondary)",
"10024E66": "RV350 [Radeon 9600] (Secondary)",
"10024E67": "R300 GL [FireGL X1] (Secondary)",
"10024E68": "R350 [Radeon 9800 PRO] (Secondary)",
"10024E69": "R350 [Radeon 9800] (Secondary)",
"10024E48": "R350 [Radeon 9800 Series]",
"10024E49": "R350 [Radeon 9800]",
"10024E4A": "R360 [Radeon 9800 XXL/XT]",
"10024E4B": "R350 GL [FireGL X2 AGP Pro]",
"10024E50": "RV350/M10 / RV360/M11 [Mobility Radeon 9600 (PRO) / 9700]",
"10024E51": "RV350 [Radeon 9550/9600/X1050 Series]",
"10024E52": "RV350/M10 [Mobility Radeon 9500/9700 SE]",
"10024C66": "RV250/M9 GL [Mobility FireGL 9000/Radeon 9000]",
"10024C6E": "RV250/M9 [Mobility Radeon 9000] (Secondary)",
"10024D46": "Rage Mobility 128 AGP 4X/Mobility M4",
"10024D52": "Theater 550 PRO PCI [ATI TV Wonder 550]",
"10024D53": "Theater 550 PRO PCIe",
"10024E44": "R300 [Radeon 9700/9700 PRO]",
"10024E45": "R300 [Radeon 9500 PRO/9700]",
"10024E46": "R300 [Radeon 9600 TX]",
"10024E47": "R300 GL [FireGL X1]",
"10024C4E": "Rage Mobility L AGP 2x",
"10024C46": "Rage Mobility 128 AGP 2X/Mobility M3",
"10024C47": "3D Rage IIC PCI / Mobility Radeon 7500/7500C",
"10024C49": "3D Rage LT PRO PCI",
"10024C4D": "Rage Mobility AGP 2x Series",
"10024C50": "Rage 3 LT [3D Rage LT PRO PCI]",
"10024C52": "M1 [Rage Mobility-M1 PCI]",
"10024C54": "264LT [Mach64 LT]",
"10024C57": "RV200/M7 [Mobility Radeon 7500]",
"10024C58": "RV200/M7 GL [Mobility FireGL 7800]",
"10024C59": "RV100/M6 [Rage/Radeon Mobility Series]",
"10024A6A": "R420 [Radeon X800] (Secondary)",
"10024A6B": "R420 [Radeon X800 XT AGP] (Secondary)",
"10024A70": "R420 [Radeon X800 XT Platinum Edition AGP] (Secondary)",
"10024A74": "R420 [Radeon X800 VE] (Secondary)",
"10024B49": "R481 [Radeon X850 XT AGP]",
"10024B4B": "R481 [Radeon X850 PRO AGP]",
"10024B4C": "R481 [Radeon X850 XT Platinum Edition AGP]",
"10024B69": "R481 [Radeon X850 XT AGP] (Secondary)",
"10024B6B": "R481 [Radeon X850 PRO AGP] (Secondary)",
"10024B6C": "R481 [Radeon X850 XT Platinum Edition AGP] (Secondary)",
"10024C42": "Mach64 LT [3D Rage LT PRO AGP]",
"1002496E": "RV250 [Radeon 9000] (Secondary)",
"10024A49": "R420 [Radeon X800 PRO/GTO AGP]",
"10024A4A": "R420 [Radeon X800 GT AGP]",
"10024A4B": "R420 [Radeon X800 AGP Series]",
"10024A4D": "R420 GL [FireGL X3-256]",
"10024A4E": "RV420/M18 [Mobility Radeon 9800]",
"10024A4F": "R420 [Radeon X850 AGP]",
"10024A50": "R420 [Radeon X800 XT Platinum Edition AGP]",
"10024A54": "R420 [Radeon X800 VE AGP]",
"10024A69": "R420 [Radeon X800 PRO/GTO] (Secondary)",
"10024966": "RV250 [Radeon 9000 Series]",
"10024753": "Rage XC",
"10024754": "Mach64 GT/GT-B [3D Rage I/II]",
"10024755": "Mach64 GT-B [3D Rage II+ DVD]",
"10024756": "Rage 2 [3D Rage IIC PCI]",
"10024757": "Rage 2 [3D Rage IIC AGP]",
"10024758": "Mach64 GX [WinTurbo]",
"10024759": "Rage 3 [3D Rage IIC PCI]",
"1002475A": "3D Rage IIC AGP",
"10024654": "Mach64 VT",
"10024742": "Rage 3 [3D Rage PRO AGP 2X]",
"10024744": "Rage 3 [3D Rage PRO AGP 1X]",
"10024749": "3D Rage PRO PCI",
"1002474D": "Rage XL AGP 2X",
"1002474E": "Rage XC AGP",
"1002474F": "Rage XL",
"10024750": "3D Rage Pro PCI",
"10024752": "Rage 3 [Rage XL PCI]",
"10024437": "RS250 [Mobility Radeon 7000 IGP]",
"10024554": "210888ET [Mach64 ET]",
"10024630": "XENOS Parent Die (XBOX 360)",
"10024631": "XENOS Daughter Die (XBOX 360)",
}
update_modules()
if len(sys.argv) > 1 and sys.argv[1]=="--launch-uvicorn":
launch_uvicorn()

View File

@ -34,6 +34,7 @@ call conda activate
@REM remove the old version of the dev console script, if it's still present
if exist "Open Developer Console.cmd" del "Open Developer Console.cmd"
if exist "ui\plugins\ui\merge.plugin.js" del "ui\plugins\ui\merge.plugin.js"
@rem create the stable-diffusion folder, to work with legacy installations
if not exist "stable-diffusion" mkdir stable-diffusion
@ -52,73 +53,24 @@ if exist ldm rename ldm ldm-old
if not exist "%INSTALL_ENV_DIR%\DLLs\libssl-1_1-x64.dll" copy "%INSTALL_ENV_DIR%\Library\bin\libssl-1_1-x64.dll" "%INSTALL_ENV_DIR%\DLLs\"
if not exist "%INSTALL_ENV_DIR%\DLLs\libcrypto-1_1-x64.dll" copy "%INSTALL_ENV_DIR%\Library\bin\libcrypto-1_1-x64.dll" "%INSTALL_ENV_DIR%\DLLs\"
cd ..
@rem set any overrides
set HF_HUB_DISABLE_SYMLINKS_WARNING=true
@rem install or upgrade the required modules
set PATH=C:\Windows\System32;%PATH%
@REM prevent from using packages from the user's home directory, to avoid conflicts
set PYTHONNOUSERSITE=1
set PYTHONPATH=%INSTALL_ENV_DIR%\lib\site-packages
@rem Download the required packages
call python ..\scripts\check_modules.py
if "%ERRORLEVEL%" NEQ "0" (
pause
exit /b
)
call WHERE uvicorn > .tmp
@>nul findstr /m "uvicorn" .tmp
@if "%ERRORLEVEL%" NEQ "0" (
@echo. & echo "UI packages not found! Sorry about that, please try to:" & echo " 1. Run this installer again." & echo " 2. If that doesn't fix it, please try the common troubleshooting steps at https://github.com/easydiffusion/easydiffusion/wiki/Troubleshooting" & echo " 3. If those steps don't help, please copy *all* the error messages in this window, and ask the community at https://discord.com/invite/u9yhsFmEkB" & echo " 4. If that doesn't solve the problem, please file an issue at https://github.com/easydiffusion/easydiffusion/issues" & echo "Thanks!" & echo.
pause
exit /b
)
@>nul findstr /m "conda_sd_ui_deps_installed" ..\scripts\install_status.txt
@if "%ERRORLEVEL%" NEQ "0" (
@echo conda_sd_ui_deps_installed >> ..\scripts\install_status.txt
)
@>nul findstr /m "sd_install_complete" ..\scripts\install_status.txt
@if "%ERRORLEVEL%" NEQ "0" (
@echo sd_weights_downloaded >> ..\scripts\install_status.txt
@echo sd_install_complete >> ..\scripts\install_status.txt
)
@echo. & echo "Easy Diffusion installation complete! Starting the server!" & echo.
@set SD_DIR=%cd%
set PYTHONPATH=%INSTALL_ENV_DIR%\lib\site-packages
echo PYTHONPATH=%PYTHONPATH%
@rem Download the required packages
call where python
call python --version
@cd ..
@set SD_UI_PATH=%cd%\ui
call python scripts\check_modules.py --launch-uvicorn
pause
exit /b
@FOR /F "tokens=* USEBACKQ" %%F IN (`python scripts\get_config.py --default=9000 net listen_port`) DO (
@SET ED_BIND_PORT=%%F
)
@FOR /F "tokens=* USEBACKQ" %%F IN (`python scripts\get_config.py --default=False net listen_to_network`) DO (
if "%%F" EQU "True" (
@FOR /F "tokens=* USEBACKQ" %%G IN (`python scripts\get_config.py --default=0.0.0.0 net bind_ip`) DO (
@SET ED_BIND_IP=%%G
)
) else (
@SET ED_BIND_IP=127.0.0.1
)
)
@cd stable-diffusion
@rem set any overrides
set HF_HUB_DISABLE_SYMLINKS_WARNING=true
@python -m uvicorn main:server_api --app-dir "%SD_UI_PATH%" --port %ED_BIND_PORT% --host %ED_BIND_IP% --log-level error
@pause

View File

@ -7,6 +7,7 @@ cp sd-ui-files/scripts/check_modules.py scripts/
cp sd-ui-files/scripts/get_config.py scripts/
cp sd-ui-files/scripts/config.yaml.sample scripts/
source ./scripts/functions.sh
# activate the installer env
@ -20,6 +21,10 @@ if [ -e "open_dev_console.sh" ]; then
rm "open_dev_console.sh"
fi
if [ -e "ui/plugins/ui/merge.plugin.js" ]; then
rm "ui/plugins/ui/merge.plugin.js"
fi
# set the correct installer path (current vs legacy)
if [ -e "installer_files/env" ]; then
export INSTALL_ENV_DIR="$(pwd)/installer_files/env"
@ -41,45 +46,8 @@ fi
if [ -e "src" ]; then mv src src-old; fi
if [ -e "ldm" ]; then mv ldm ldm-old; fi
# Download the required packages
if ! python ../scripts/check_modules.py; then
read -p "Press any key to continue"
exit 1
fi
if ! command -v uvicorn &> /dev/null; then
fail "UI packages not found!"
fi
if [ `grep -c sd_install_complete ../scripts/install_status.txt` -gt "0" ]; then
echo sd_weights_downloaded >> ../scripts/install_status.txt
echo sd_install_complete >> ../scripts/install_status.txt
fi
printf "\n\nEasy Diffusion installation complete, starting the server!\n\n"
SD_PATH=`pwd`
export PYTORCH_ENABLE_MPS_FALLBACK=1
export PYTHONPATH="$INSTALL_ENV_DIR/lib/python3.8/site-packages"
echo "PYTHONPATH=$PYTHONPATH"
which python
python --version
cd ..
export SD_UI_PATH=`pwd`/ui
export ED_BIND_PORT="$( python scripts/get_config.py --default=9000 net listen_port )"
case "$( python scripts/get_config.py --default=False net listen_to_network )" in
"True")
export ED_BIND_IP=$( python scripts/get_config.py --default=0.0.0.0 net bind_ip)
;;
"False")
export ED_BIND_IP=127.0.0.1
;;
esac
cd stable-diffusion
uvicorn main:server_api --app-dir "$SD_UI_PATH" --port "$ED_BIND_PORT" --host "$ED_BIND_IP" --log-level error
# Download the required packages
python scripts/check_modules.py --launch-uvicorn
read -p "Press any key to continue"

View File

@ -11,7 +11,7 @@ if [ -f "on_sd_start.bat" ]; then
echo download. This will not work.
echo
echo Recommended: Please close this window and download the installer from
echo https://stable-diffusion-ui.github.io/docs/installation/
echo https://easydiffusion.github.io/docs/installation/
echo
echo ================================================================================
echo

View File

@ -37,7 +37,6 @@ ROOT_DIR = os.path.abspath(os.path.join(SD_DIR, ".."))
SD_UI_DIR = os.getenv("SD_UI_PATH", None)
CONFIG_DIR = os.path.abspath(os.path.join(SD_UI_DIR, "..", "scripts"))
MODELS_DIR = os.path.abspath(os.path.join(SD_DIR, "..", "models"))
BUCKET_DIR = os.path.abspath(os.path.join(SD_DIR, "..", "bucket"))
USER_PLUGINS_DIR = os.path.abspath(os.path.join(SD_DIR, "..", "plugins"))
@ -61,7 +60,7 @@ APP_CONFIG_DEFAULTS = {
"ui": {
"open_browser_on_start": True,
},
"test_diffusers": True,
"use_v3_engine": True,
}
IMAGE_EXTENSIONS = [
@ -92,14 +91,23 @@ CUSTOM_MODIFIERS_LANDSCAPE_EXTENSIONS = [
"-landscape",
]
MODELS_DIR = os.path.abspath(os.path.join(SD_DIR, "..", "models"))
def init():
global MODELS_DIR
os.makedirs(USER_UI_PLUGINS_DIR, exist_ok=True)
os.makedirs(USER_SERVER_PLUGINS_DIR, exist_ok=True)
# https://pytorch.org/docs/stable/storage.html
warnings.filterwarnings("ignore", category=UserWarning, message="TypedStorage is deprecated")
config = getConfig()
config_models_dir = config.get("models_dir", None)
if (config_models_dir is not None and config_models_dir != ""):
MODELS_DIR = config_models_dir
def init_render_threads():
load_server_plugins()
@ -116,9 +124,9 @@ def getConfig(default_val=APP_CONFIG_DEFAULTS):
shutil.move(config_legacy_yaml, config_yaml_path)
def set_config_on_startup(config: dict):
if getConfig.__test_diffusers_on_startup is None:
getConfig.__test_diffusers_on_startup = config.get("test_diffusers", True)
config["config_on_startup"] = {"test_diffusers": getConfig.__test_diffusers_on_startup}
if getConfig.__use_v3_engine_on_startup is None:
getConfig.__use_v3_engine_on_startup = config.get("use_v3_engine", True)
config["config_on_startup"] = {"use_v3_engine": getConfig.__use_v3_engine_on_startup}
if os.path.isfile(config_yaml_path):
try:
@ -166,12 +174,15 @@ def getConfig(default_val=APP_CONFIG_DEFAULTS):
return default_val
getConfig.__test_diffusers_on_startup = None
getConfig.__use_v3_engine_on_startup = None
def setConfig(config):
global MODELS_DIR
try: # config.yaml
config_yaml_path = os.path.join(CONFIG_DIR, "..", "config.yaml")
config_yaml_path = os.path.abspath(config_yaml_path)
yaml = YAML()
if not hasattr(config, "_yaml_comment"):
@ -205,6 +216,9 @@ def setConfig(config):
except:
log.error(traceback.format_exc())
if config.get("models_dir"):
MODELS_DIR = config["models_dir"]
def save_to_config(ckpt_model_name, vae_model_name, hypernetwork_model_name, vram_usage_level):
config = getConfig()

View File

@ -55,8 +55,13 @@ def init():
return bucketfiles
else:
bucket_id = crud.get_bucket_by_path(db, path).id
bucket = crud.get_bucket_by_path(db, path)
if bucket == None:
raise HTTPException(status_code=404, detail="Bucket not found")
bucket_id = bucket.id
bucketfile = db.query(models.BucketFile).filter(models.BucketFile.bucket_id == bucket_id, models.BucketFile.filename == filename).first()
if bucketfile == None:
raise HTTPException(status_code=404, detail="File not found")
suffix = get_suffix_from_filename(filename)

View File

@ -37,7 +37,7 @@ MODEL_EXTENSIONS = {
}
DEFAULT_MODELS = {
"stable-diffusion": [
{"file_name": "sd-v1-4.ckpt", "model_id": "1.4"},
{"file_name": "sd-v1-5.safetensors", "model_id": "1.5-pruned-emaonly-fp16"},
],
"gfpgan": [
{"file_name": "GFPGANv1.4.pth", "model_id": "1.4"},
@ -193,9 +193,9 @@ def resolve_model_paths(models_data: ModelsData):
skip_models = cn_filters + ["latent_upscaler", "nsfw_checker"]
if model_type in skip_models: # doesn't use model paths
continue
if model_type == "codeformer":
if model_type == "codeformer" and model_paths[model_type]:
download_if_necessary("codeformer", "codeformer.pth", "codeformer-0.1.0")
elif model_type == "controlnet":
elif model_type == "controlnet" and model_paths[model_type]:
model_id = model_paths[model_type]
model_info = get_model_info_from_db(model_type=model_type, model_id=model_id)
if model_info:
@ -261,7 +261,24 @@ def make_model_folders():
for model_type in KNOWN_MODEL_TYPES:
model_dir_path = os.path.join(app.MODELS_DIR, model_type)
try:
os.makedirs(model_dir_path, exist_ok=True)
except Exception as e:
from rich.console import Console
from rich.panel import Panel
Console().print(
Panel(
"\n"
+ f"Error while creating the models directory: '{model_dir_path}'\n"
+ f"Error: {e}\n\n"
+ f"[white]Check the 'models_dir:' line in the file '{os.path.join(app.ROOT_DIR, 'config.yaml')}'.[/white]\n",
title="Fatal Error starting Easy Diffusion",
style="bold yellow on red",
)
)
input("Press Enter to terminate...")
exit(1)
help_file_name = f"Place your {model_type} model files here.txt"
help_file_contents = f'Supported extensions: {" or ".join(MODEL_EXTENSIONS.get(model_type))}'
@ -305,7 +322,7 @@ def is_malicious_model(file_path):
def getModels(scan_for_malicious: bool = True):
models = {
"options": {
"stable-diffusion": [{"sd-v1-4": "SD 1.4"}],
"stable-diffusion": [],
"vae": [],
"hypernetwork": [],
"lora": [],

View File

@ -12,9 +12,9 @@ from easydiffusion import app
manifest = {
"tensorrt": {
"install": [
"nvidia-cudnn --pre --extra-index-url=https://pypi.nvidia.com --trusted-host pypi.nvidia.com",
"tensorrt-libs --pre --extra-index-url=https://pypi.nvidia.com --trusted-host pypi.nvidia.com",
"tensorrt --pre --extra-index-url=https://pypi.nvidia.com --trusted-host pypi.nvidia.com",
"wheel",
"nvidia-cudnn-cu11==8.9.4.25",
"tensorrt==9.0.0.post11.dev1 --pre --extra-index-url=https://pypi.nvidia.com --trusted-host pypi.nvidia.com",
],
"uninstall": ["tensorrt"],
# TODO also uninstall tensorrt-libs and nvidia-cudnn, but do it upon restarting (avoid 'file in use' error)

View File

@ -30,7 +30,7 @@ def init(device):
from easydiffusion import app
app_config = app.getConfig()
context.test_diffusers = app_config.get("test_diffusers", True)
context.test_diffusers = app_config.get("use_v3_engine", True)
log.info("Device usage during initialization:")
get_device_usage(device, log_info=True, process_usage_only=False)

View File

@ -15,8 +15,10 @@ from easydiffusion.types import (
FilterImageRequest,
MergeRequest,
TaskData,
RenderTaskData,
ModelsData,
OutputFormatData,
SaveToDiskData,
convert_legacy_render_req_to_new,
)
from easydiffusion.utils import log
@ -36,6 +38,7 @@ NOCACHE_HEADERS = {
"Pragma": "no-cache",
"Expires": "0",
}
PROTECTED_CONFIG_KEYS = ("block_nsfw",) # can't change these via the HTTP API
class NoCacheStaticFiles(StaticFiles):
@ -63,7 +66,8 @@ class SetAppConfigRequest(BaseModel, extra=Extra.allow):
ui_open_browser_on_start: bool = None
listen_to_network: bool = None
listen_port: int = None
test_diffusers: bool = True
use_v3_engine: bool = True
models_dir: str = None
def init():
@ -172,10 +176,11 @@ def set_app_config_internal(req: SetAppConfigRequest):
config["net"] = {}
config["net"]["listen_port"] = int(req.listen_port)
config["test_diffusers"] = req.test_diffusers
config["use_v3_engine"] = req.use_v3_engine
config["models_dir"] = req.models_dir
for property, property_value in req.dict().items():
if property_value is not None and property not in req.__fields__:
if property_value is not None and property not in req.__fields__ and property not in PROTECTED_CONFIG_KEYS:
config[property] = property_value
try:
@ -204,7 +209,12 @@ def read_web_data_internal(key: str = None, **kwargs):
if not key: # /get without parameters, stable-diffusion easter egg.
raise HTTPException(status_code=418, detail="StableDiffusion is drawing a teapot!") # HTTP418 I'm a teapot
elif key == "app_config":
return JSONResponse(app.getConfig(), headers=NOCACHE_HEADERS)
config = app.getConfig()
if "models_dir" not in config:
config["models_dir"] = app.MODELS_DIR
return JSONResponse(config, headers=NOCACHE_HEADERS)
elif key == "system_info":
config = app.getConfig()
@ -215,6 +225,7 @@ def read_web_data_internal(key: str = None, **kwargs):
"hosts": app.getIPConfig(),
"default_output_dir": output_dir,
"enforce_output_dir": ("force_save_path" in config),
"enforce_output_metadata": ("force_save_metadata" in config),
}
system_info["devices"]["config"] = config.get("render_devices", "auto")
return JSONResponse(system_info, headers=NOCACHE_HEADERS)
@ -261,14 +272,15 @@ def render_internal(req: dict):
# separate out the request data into rendering and task-specific data
render_req: GenerateImageRequest = GenerateImageRequest.parse_obj(req)
task_data: TaskData = TaskData.parse_obj(req)
task_data: RenderTaskData = RenderTaskData.parse_obj(req)
models_data: ModelsData = ModelsData.parse_obj(req)
output_format: OutputFormatData = OutputFormatData.parse_obj(req)
save_data: SaveToDiskData = SaveToDiskData.parse_obj(req)
# Overwrite user specified save path
config = app.getConfig()
if "force_save_path" in config:
task_data.save_to_disk_path = config["force_save_path"]
save_data.save_to_disk_path = config["force_save_path"]
render_req.init_image_mask = req.get("mask") # hack: will rename this in the HTTP API in a future revision
@ -280,7 +292,7 @@ def render_internal(req: dict):
)
# enqueue the task
task = RenderTask(render_req, task_data, models_data, output_format)
task = RenderTask(render_req, task_data, models_data, output_format, save_data)
return enqueue_task(task)
except HTTPException as e:
raise e
@ -291,13 +303,14 @@ def render_internal(req: dict):
def filter_internal(req: dict):
try:
session_id = req.get("session_id", "session")
filter_req: FilterImageRequest = FilterImageRequest.parse_obj(req)
task_data: TaskData = TaskData.parse_obj(req)
models_data: ModelsData = ModelsData.parse_obj(req)
output_format: OutputFormatData = OutputFormatData.parse_obj(req)
save_data: SaveToDiskData = SaveToDiskData.parse_obj(req)
# enqueue the task
task = FilterTask(filter_req, session_id, models_data, output_format)
task = FilterTask(filter_req, task_data, models_data, output_format, save_data)
return enqueue_task(task)
except HTTPException as e:
raise e
@ -456,8 +469,8 @@ def modify_package_internal(package_name: str, req: dict):
log.error(traceback.format_exc())
return HTTPException(status_code=500, detail=str(e))
def get_sha256_internal(obj_path):
import hashlib
from easydiffusion.utils import sha256sum
path = obj_path.split("/")
@ -477,4 +490,3 @@ def get_sha256_internal(obj_path):
log.error(str(e))
log.error(traceback.format_exc())
return HTTPException(status_code=500, detail=str(e))

View File

@ -1,12 +1,25 @@
import os
import json
import pprint
import time
from numpy import base_repr
from sdkit.filter import apply_filters
from sdkit.models import load_model
from sdkit.utils import img_to_base64_str, get_image, log
from sdkit.utils import img_to_base64_str, get_image, log, save_images
from easydiffusion import model_manager, runtime
from easydiffusion.types import FilterImageRequest, FilterImageResponse, ModelsData, OutputFormatData
from easydiffusion.types import (
FilterImageRequest,
FilterImageResponse,
ModelsData,
OutputFormatData,
SaveToDiskData,
TaskData,
GenerateImageRequest,
)
from easydiffusion.utils.save_utils import format_folder_name
from .task import Task
@ -15,13 +28,22 @@ class FilterTask(Task):
"For applying filters to input images"
def __init__(
self, req: FilterImageRequest, session_id: str, models_data: ModelsData, output_format: OutputFormatData
self,
req: FilterImageRequest,
task_data: TaskData,
models_data: ModelsData,
output_format: OutputFormatData,
save_data: SaveToDiskData,
):
super().__init__(session_id)
super().__init__(task_data.session_id)
task_data.request_id = self.id
self.request = req
self.task_data = task_data
self.models_data = models_data
self.output_format = output_format
self.save_data = save_data
# convert to multi-filter format, if necessary
if isinstance(req.filter, str):
@ -34,13 +56,15 @@ class FilterTask(Task):
def run(self):
"Runs the image filtering task on the assigned thread"
from easydiffusion import app
context = runtime.context
model_manager.resolve_model_paths(self.models_data)
model_manager.reload_models_if_necessary(context, self.models_data)
model_manager.fail_if_models_did_not_load(context)
print_task_info(self.request, self.models_data, self.output_format)
print_task_info(self.request, self.models_data, self.output_format, self.save_data)
if isinstance(self.request.image, list):
images = [get_image(img) for img in self.request.image]
@ -50,6 +74,26 @@ class FilterTask(Task):
images = filter_images(context, images, self.request.filter, self.request.filter_params)
output_format = self.output_format
if self.save_data.save_to_disk_path is not None:
app_config = app.getConfig()
folder_format = app_config.get("folder_format", "$id")
dummy_req = GenerateImageRequest()
img_id = base_repr(int(time.time() * 10000), 36)[-7:] # Base 36 conversion, 0-9, A-Z
save_dir_path = os.path.join(
self.save_data.save_to_disk_path, format_folder_name(folder_format, dummy_req, self.task_data)
)
save_images(
images,
save_dir_path,
file_name=img_id,
output_format=output_format.output_format,
output_quality=output_format.output_quality,
output_lossless=output_format.output_lossless,
)
images = [
img_to_base64_str(
img, output_format.output_format, output_format.output_quality, output_format.output_lossless
@ -60,6 +104,7 @@ class FilterTask(Task):
res = FilterImageResponse(self.request, self.models_data, images=images)
res = res.json()
self.buffer_queue.put(json.dumps(res))
log.info("Filter task completed")
self.response = res
@ -105,11 +150,15 @@ def after_filter(context, filter_name, filter_params, previous_state):
load_model(context, "realesrgan")
def print_task_info(req: FilterImageRequest, models_data: ModelsData, output_format: OutputFormatData):
def print_task_info(
req: FilterImageRequest, models_data: ModelsData, output_format: OutputFormatData, save_data: SaveToDiskData
):
req_str = pprint.pformat({"filter": req.filter, "filter_params": req.filter_params}).replace("[", "\[")
models_data = pprint.pformat(models_data.dict()).replace("[", "\[")
output_format = pprint.pformat(output_format.dict()).replace("[", "\[")
save_data = pprint.pformat(save_data.dict()).replace("[", "\[")
log.info(f"request: {req_str}")
log.info(f"models data: {models_data}")
log.info(f"output format: {output_format}")
log.info(f"save data: {save_data}")

View File

@ -4,9 +4,9 @@ import queue
import time
from easydiffusion import model_manager, runtime
from easydiffusion.types import GenerateImageRequest, ModelsData, OutputFormatData
from easydiffusion.types import GenerateImageRequest, ModelsData, OutputFormatData, SaveToDiskData
from easydiffusion.types import Image as ResponseImage
from easydiffusion.types import GenerateImageResponse, TaskData, UserInitiatedStop
from easydiffusion.types import GenerateImageResponse, RenderTaskData, UserInitiatedStop
from easydiffusion.utils import get_printable_request, log, save_images_to_disk
from sdkit.generate import generate_images
from sdkit.utils import (
@ -28,23 +28,38 @@ class RenderTask(Task):
"For image generation"
def __init__(
self, req: GenerateImageRequest, task_data: TaskData, models_data: ModelsData, output_format: OutputFormatData
self,
req: GenerateImageRequest,
task_data: RenderTaskData,
models_data: ModelsData,
output_format: OutputFormatData,
save_data: SaveToDiskData,
):
super().__init__(task_data.session_id)
task_data.request_id = self.id
self.render_request: GenerateImageRequest = req # Initial Request
self.task_data: TaskData = task_data
self.render_request = req # Initial Request
self.task_data = task_data
self.models_data = models_data
self.output_format = output_format
self.save_data = save_data
self.temp_images: list = [None] * req.num_outputs * (1 if task_data.show_only_filtered_image else 2)
def run(self):
"Runs the image generation task on the assigned thread"
from easydiffusion import task_manager
from easydiffusion import task_manager, app
context = runtime.context
config = app.getConfig()
if config.get("block_nsfw", False): # override if set on the server
self.task_data.block_nsfw = True
if "nsfw_checker" not in self.task_data.filters:
self.task_data.filters.append("nsfw_checker")
self.models_data.model_paths["nsfw_checker"] = "nsfw_checker"
def step_callback():
task_manager.keep_task_alive(self)
@ -80,6 +95,7 @@ class RenderTask(Task):
self.task_data,
self.models_data,
self.output_format,
self.save_data,
self.buffer_queue,
self.temp_images,
step_callback,
@ -122,22 +138,23 @@ class RenderTask(Task):
def make_images(
context,
req: GenerateImageRequest,
task_data: TaskData,
task_data: RenderTaskData,
models_data: ModelsData,
output_format: OutputFormatData,
save_data: SaveToDiskData,
data_queue: queue.Queue,
task_temp_images: list,
step_callback,
):
context.stop_processing = False
print_task_info(req, task_data, models_data, output_format)
print_task_info(req, task_data, models_data, output_format, save_data)
images, seeds = make_images_internal(
context, req, task_data, models_data, output_format, data_queue, task_temp_images, step_callback
context, req, task_data, models_data, output_format, save_data, data_queue, task_temp_images, step_callback
)
res = GenerateImageResponse(
req, task_data, models_data, output_format, images=construct_response(images, seeds, output_format)
req, task_data, models_data, output_format, save_data, images=construct_response(images, seeds, output_format)
)
res = res.json()
data_queue.put(json.dumps(res))
@ -147,25 +164,32 @@ def make_images(
def print_task_info(
req: GenerateImageRequest, task_data: TaskData, models_data: ModelsData, output_format: OutputFormatData
req: GenerateImageRequest,
task_data: RenderTaskData,
models_data: ModelsData,
output_format: OutputFormatData,
save_data: SaveToDiskData,
):
req_str = pprint.pformat(get_printable_request(req, task_data, output_format)).replace("[", "\[")
req_str = pprint.pformat(get_printable_request(req, task_data, models_data, output_format, save_data)).replace("[", "\[")
task_str = pprint.pformat(task_data.dict()).replace("[", "\[")
models_data = pprint.pformat(models_data.dict()).replace("[", "\[")
output_format = pprint.pformat(output_format.dict()).replace("[", "\[")
save_data = pprint.pformat(save_data.dict()).replace("[", "\[")
log.info(f"request: {req_str}")
log.info(f"task data: {task_str}")
# log.info(f"models data: {models_data}")
log.info(f"output format: {output_format}")
log.info(f"save data: {save_data}")
def make_images_internal(
context,
req: GenerateImageRequest,
task_data: TaskData,
task_data: RenderTaskData,
models_data: ModelsData,
output_format: OutputFormatData,
save_data: SaveToDiskData,
data_queue: queue.Queue,
task_temp_images: list,
step_callback,
@ -187,8 +211,8 @@ def make_images_internal(
filters, filter_params = task_data.filters, task_data.filter_params
filtered_images = filter_images(context, images, filters, filter_params) if not user_stopped else images
if task_data.save_to_disk_path is not None:
save_images_to_disk(images, filtered_images, req, task_data, output_format)
if save_data.save_to_disk_path is not None:
save_images_to_disk(images, filtered_images, req, task_data, models_data, output_format, save_data)
seeds = [*range(req.seed, req.seed + len(images))]
if task_data.show_only_filtered_image or filtered_images is images:
@ -200,7 +224,7 @@ def make_images_internal(
def generate_images_internal(
context,
req: GenerateImageRequest,
task_data: TaskData,
task_data: RenderTaskData,
models_data: ModelsData,
data_queue: queue.Queue,
task_temp_images: list,
@ -254,6 +278,13 @@ def generate_images_internal(
setattr(pipe.unet, "_allocate_trt_buffers_backup", pipe.unet._allocate_trt_buffers)
delattr(pipe.unet, "_allocate_trt_buffers")
if task_data.enable_vae_tiling:
if hasattr(pipe, "enable_vae_tiling"):
pipe.enable_vae_tiling()
else:
if hasattr(pipe, "disable_vae_tiling"):
pipe.disable_vae_tiling()
images = generate_images(context, callback=callback, **req.dict())
user_stopped = False
except UserInitiatedStop:
@ -291,7 +322,7 @@ def construct_response(images: list, seeds: list, output_format: OutputFormatDat
def make_step_callback(
context,
req: GenerateImageRequest,
task_data: TaskData,
task_data: RenderTaskData,
data_queue: queue.Queue,
task_temp_images: list,
step_callback,

View File

@ -26,7 +26,7 @@ class GenerateImageRequest(BaseModel):
sampler_name: str = None # "ddim", "plms", "heun", "euler", "euler_a", "dpm2", "dpm2_a", "lms"
hypernetwork_strength: float = 0
lora_alpha: Union[float, List[float]] = 0
tiling: str = "none" # "none", "x", "y", "xy"
tiling: str = None # None, "x", "y", "xy"
class FilterImageRequest(BaseModel):
@ -58,10 +58,17 @@ class OutputFormatData(BaseModel):
output_lossless: bool = False
class SaveToDiskData(BaseModel):
save_to_disk_path: str = None
metadata_output_format: str = "txt" # or "json"
class TaskData(BaseModel):
request_id: str = None
session_id: str = "session"
save_to_disk_path: str = None
class RenderTaskData(TaskData):
vram_usage_level: str = "balanced" # or "low" or "medium"
use_face_correction: Union[str, List[str]] = None # or "GFPGANv1.3"
@ -77,10 +84,10 @@ class TaskData(BaseModel):
filters: List[str] = []
filter_params: Dict[str, Dict[str, Any]] = {}
control_filter_to_apply: Union[str, List[str]] = None
enable_vae_tiling: bool = True
show_only_filtered_image: bool = False
block_nsfw: bool = False
metadata_output_format: str = "txt" # or "json"
stream_image_progress: bool = False
stream_image_progress_interval: int = 5
clip_skip: bool = False
@ -126,12 +133,14 @@ class GenerateImageResponse:
task_data: TaskData,
models_data: ModelsData,
output_format: OutputFormatData,
save_data: SaveToDiskData,
images: list,
):
self.render_request = render_request
self.task_data = task_data
self.models_data = models_data
self.output_format = output_format
self.save_data = save_data
self.images = images
def json(self):
@ -141,6 +150,7 @@ class GenerateImageResponse:
task_data = self.task_data.dict()
task_data.update(self.output_format.dict())
task_data.update(self.save_data.dict())
res = {
"status": "succeeded",

View File

@ -1,4 +1,5 @@
import logging
import hashlib
log = logging.getLogger("easydiffusion")

View File

@ -7,7 +7,14 @@ from datetime import datetime
from functools import reduce
from easydiffusion import app
from easydiffusion.types import GenerateImageRequest, TaskData, OutputFormatData
from easydiffusion.types import (
GenerateImageRequest,
TaskData,
RenderTaskData,
OutputFormatData,
SaveToDiskData,
ModelsData,
)
from numpy import base_repr
from sdkit.utils import save_dicts, save_images
from sdkit.models.model_loader.embeddings import get_embedding_token
@ -95,7 +102,7 @@ def format_folder_name(format: str, req: GenerateImageRequest, task_data: TaskDa
def format_file_name(
format: str,
req: GenerateImageRequest,
task_data: TaskData,
task_data: RenderTaskData,
now: float,
batch_file_number: int,
folder_img_number: ImageNumber,
@ -118,13 +125,19 @@ def format_file_name(
def save_images_to_disk(
images: list, filtered_images: list, req: GenerateImageRequest, task_data: TaskData, output_format: OutputFormatData
images: list,
filtered_images: list,
req: GenerateImageRequest,
task_data: RenderTaskData,
models_data: ModelsData,
output_format: OutputFormatData,
save_data: SaveToDiskData,
):
now = time.time()
app_config = app.getConfig()
folder_format = app_config.get("folder_format", "$id")
save_dir_path = os.path.join(task_data.save_to_disk_path, format_folder_name(folder_format, req, task_data))
metadata_entries = get_metadata_entries_for_request(req, task_data, output_format)
save_dir_path = os.path.join(save_data.save_to_disk_path, format_folder_name(folder_format, req, task_data))
metadata_entries = get_metadata_entries_for_request(req, task_data, models_data, output_format, save_data)
file_number = calculate_img_number(save_dir_path, task_data)
make_filename = make_filename_callback(
app_config.get("filename_format", "$p_$tsb64"),
@ -143,8 +156,8 @@ def save_images_to_disk(
output_quality=output_format.output_quality,
output_lossless=output_format.output_lossless,
)
if task_data.metadata_output_format:
for metadata_output_format in task_data.metadata_output_format.split(","):
if save_data.metadata_output_format:
for metadata_output_format in save_data.metadata_output_format.split(","):
if metadata_output_format.lower() in ["json", "txt", "embed"]:
save_dicts(
metadata_entries,
@ -179,8 +192,8 @@ def save_images_to_disk(
output_quality=output_format.output_quality,
output_lossless=output_format.output_lossless,
)
if task_data.metadata_output_format:
for metadata_output_format in task_data.metadata_output_format.split(","):
if save_data.metadata_output_format:
for metadata_output_format in save_data.metadata_output_format.split(","):
if metadata_output_format.lower() in ["json", "txt", "embed"]:
save_dicts(
metadata_entries,
@ -191,11 +204,17 @@ def save_images_to_disk(
)
def get_metadata_entries_for_request(req: GenerateImageRequest, task_data: TaskData, output_format: OutputFormatData):
metadata = get_printable_request(req, task_data, output_format)
def get_metadata_entries_for_request(
req: GenerateImageRequest,
task_data: RenderTaskData,
models_data: ModelsData,
output_format: OutputFormatData,
save_data: SaveToDiskData,
):
metadata = get_printable_request(req, task_data, models_data, output_format, save_data)
# if text, format it in the text format expected by the UI
is_txt_format = task_data.metadata_output_format and "txt" in task_data.metadata_output_format.lower().split(",")
is_txt_format = save_data.metadata_output_format and "txt" in save_data.metadata_output_format.lower().split(",")
if is_txt_format:
def format_value(value):
@ -214,13 +233,20 @@ def get_metadata_entries_for_request(req: GenerateImageRequest, task_data: TaskD
return entries
def get_printable_request(req: GenerateImageRequest, task_data: TaskData, output_format: OutputFormatData):
def get_printable_request(
req: GenerateImageRequest,
task_data: RenderTaskData,
models_data: ModelsData,
output_format: OutputFormatData,
save_data: SaveToDiskData,
):
req_metadata = req.dict()
task_data_metadata = task_data.dict()
task_data_metadata.update(output_format.dict())
task_data_metadata.update(save_data.dict())
app_config = app.getConfig()
using_diffusers = app_config.get("test_diffusers", True)
using_diffusers = app_config.get("use_v3_engine", True)
# Save the metadata in the order defined in TASK_TEXT_MAPPING
metadata = {}
@ -230,25 +256,11 @@ def get_printable_request(req: GenerateImageRequest, task_data: TaskData, output
elif key in task_data_metadata:
metadata[key] = task_data_metadata[key]
if key == "use_embeddings_model" and using_diffusers:
embeddings_extensions = {".pt", ".bin", ".safetensors"}
if key == "use_embeddings_model" and task_data_metadata[key] and using_diffusers:
embeddings_used = models_data.model_paths["embeddings"]
embeddings_used = embeddings_used if isinstance(embeddings_used, list) else [embeddings_used]
def scan_directory(directory_path: str):
used_embeddings = []
for entry in os.scandir(directory_path):
if entry.is_file():
# Check if the filename has the right extension
if not any(map(lambda ext: entry.name.endswith(ext), embeddings_extensions)):
continue
embedding_name_regex = regex.compile(r"(^|[\s,])" + regex.escape(get_embedding_token(entry.name)) + r"([+-]*$|[\s,]|[+-]+[\s,])")
if embedding_name_regex.search(req.prompt) or embedding_name_regex.search(req.negative_prompt):
used_embeddings.append(entry.path)
elif entry.is_dir():
used_embeddings.extend(scan_directory(entry.path))
return used_embeddings
used_embeddings = scan_directory(os.path.join(app.MODELS_DIR, "embeddings"))
metadata["use_embeddings_model"] = used_embeddings if len(used_embeddings) > 0 else None
metadata["use_embeddings_model"] = embeddings_used if len(embeddings_used) > 0 else None
# Clean up the metadata
if req.init_image is None and "prompt_strength" in metadata:
@ -269,7 +281,17 @@ def get_printable_request(req: GenerateImageRequest, task_data: TaskData, output
del metadata[key]
else:
for key in (
x for x in ["use_lora_model", "lora_alpha", "clip_skip", "tiling", "latent_upscaler_steps", "use_controlnet_model", "control_filter_to_apply"] if x in metadata
x
for x in [
"use_lora_model",
"lora_alpha",
"clip_skip",
"tiling",
"latent_upscaler_steps",
"use_controlnet_model",
"control_filter_to_apply",
]
if x in metadata
):
del metadata[key]
@ -279,7 +301,7 @@ def get_printable_request(req: GenerateImageRequest, task_data: TaskData, output
def make_filename_callback(
filename_format: str,
req: GenerateImageRequest,
task_data: TaskData,
task_data: RenderTaskData,
folder_img_number: int,
suffix=None,
now=None,
@ -296,7 +318,7 @@ def make_filename_callback(
return make_filename
def _calculate_img_number(save_dir_path: str, task_data: TaskData):
def _calculate_img_number(save_dir_path: str, task_data: RenderTaskData):
def get_highest_img_number(accumulator: int, file: os.DirEntry) -> int:
if not file.is_file:
return accumulator
@ -340,5 +362,5 @@ def _calculate_img_number(save_dir_path: str, task_data: TaskData):
_calculate_img_number.session_img_numbers = {}
def calculate_img_number(save_dir_path: str, task_data: TaskData):
def calculate_img_number(save_dir_path: str, task_data: RenderTaskData):
return ImageNumber(lambda: _calculate_img_number(save_dir_path, task_data))

View File

@ -35,7 +35,7 @@
<h1>
<img id="logo_img" src="/media/images/icon-512x512.png" >
Easy Diffusion
<small><span id="version">v3.0.2</span> <span id="updateBranchLabel"></span></small>
<small><span id="version">v3.0.7</span> <span id="updateBranchLabel"></span></small>
</h1>
</div>
<div id="server-status">
@ -155,11 +155,11 @@
<div id="editor-settings-entries" class="collapsible-content">
<div><table>
<tr><b class="settings-subheader">Image Settings</b></tr>
<tr class="pl-5"><td><label for="seed">Seed:</label></td><td><input id="seed" name="seed" size="10" value="0" onkeypress="preventNonNumericalInput(event)"> <input id="random_seed" name="random_seed" type="checkbox" checked><label for="random_seed">Random</label></td></tr>
<tr class="pl-5"><td><label for="seed">Seed:</label></td><td><input id="seed" name="seed" size="10" value="0" onkeypress="preventNonNumericalInput(event)" inputmode="numeric"> <input id="random_seed" name="random_seed" type="checkbox" checked><label for="random_seed">Random</label></td></tr>
<tr class="pl-5"><td><label for="num_outputs_total">Number of Images:</label></td>
<td><input id="num_outputs_total" name="num_outputs_total" value="1" type="number" value="1" min="1" step="1" onkeypres"="preventNonNumericalInput(event)">
<td><input id="num_outputs_total" name="num_outputs_total" value="1" type="number" value="1" min="1" step="1" onkeypres"="preventNonNumericalInput(event)" inputmode="numeric">
<label><small>(total)</small></label>
<input id="num_outputs_parallel" name="num_outputs_parallel" value="1" type="number" value="1" min="1" step="1" onkeypress="preventNonNumericalInput(event)">
<input id="num_outputs_parallel" name="num_outputs_parallel" value="1" type="number" value="1" min="1" step="1" onkeypress="preventNonNumericalInput(event)" inputmode="numeric">
<label id="num_outputs_parallel_label" for="num_outputs_parallel"><small>(in parallel)</small></label></td>
</tr>
<tr class="pl-5"><td><label for="stable_diffusion_model">Model:</label></td><td class="model-input">
@ -266,7 +266,7 @@
<option value="unipc_tu_2" class="k_diffusion-only">UniPC TU 2</option>
<option value="unipc_tq" class="k_diffusion-only">UniPC TQ</option>
</select>
<a href="https://github.com/easydiffusion/easydiffusion/wiki/How-to-Use#samplers" target="_blank"><i class="fa-solid fa-circle-question help-btn"><span class="simple-tooltip top-left">Click to learn more about samplers</span></i></a>
<a href="https://github.com/easydiffusion/easydiffusion/wiki/Samplers" target="_blank"><i class="fa-solid fa-circle-question help-btn"><span class="simple-tooltip top-left">Click to learn more about samplers</span></i></a>
</td></tr>
<tr class="pl-5"><td><label>Image Size: </label></td><td id="image-size-options">
<select id="width" name="width" value="512">
@ -291,7 +291,9 @@
<option value="2048">2048</option>
</select>
<label id="widthLabel" for="width"><small><span>(width)</span></small></label>
<div class="tooltip-container">
<span id="swap-width-height" class="clickable smallButton" style="margin-left: 2px; margin-right:2px;"><i class="fa-solid fa-right-left"><span class="simple-tooltip top-left"> Swap width and height </span></i></span>
</div>
<select id="height" name="height" value="512">
<option value="128">128</option>
<option value="192">192</option>
@ -318,9 +320,9 @@
<span id="recent-resolutions-button" class="clickable"><i class="fa-solid fa-sliders"><span class="simple-tooltip top-left"> Advanced sizes </span></i></span>
<div id="recent-resolutions-popup" class="displayNone">
<small>Custom size:</small><br>
<input id="custom-width" name="custom-width" type="number" min="128" value="512" onkeypress="preventNonNumericalInput(event)">
<input id="custom-width" name="custom-width" type="number" min="128" value="512" onkeypress="preventNonNumericalInput(event)" inputmode="numeric">
&times;
<input id="custom-height" name="custom-height" type="number" min="128" value="512" onkeypress="preventNonNumericalInput(event)"><br>
<input id="custom-height" name="custom-height" type="number" min="128" value="512" onkeypress="preventNonNumericalInput(event)" inputmode="numeric"><br>
<small>Resize:</small><br>
<input id="resize-slider" name="resize-slider" class="editor-slider" value="1" type="range" min="0.4" max="2" step="0.005" style="width:100%;"><br>
<div id="enlarge-buttons"><button data-factor="0.5" class="tertiaryButton smallButton">×0.5</button>&nbsp;<button data-factor="1.2" class="tertiaryButton smallButton">×1.2</button>&nbsp;<button data-factor="1.5" class="tertiaryButton smallButton">×1.5</button>&nbsp;<button data-factor="2" class="tertiaryButton smallButton">×2</button>&nbsp;<button data-factor="3" class="tertiaryButton smallButton">×3</button></div>
@ -342,9 +344,9 @@
</div>
<div id="small_image_warning" class="displayNone">Small image sizes can cause bad image quality</div>
</td></tr>
<tr class="pl-5"><td><label for="num_inference_steps">Inference Steps:</label></td><td> <input id="num_inference_steps" name="num_inference_steps" type="number" min="1" step="1" style="width: 42pt" value="25" onkeypress="preventNonNumericalInput(event)"></td></tr>
<tr class="pl-5"><td><label for="guidance_scale_slider">Guidance Scale:</label></td><td> <input id="guidance_scale_slider" name="guidance_scale_slider" class="editor-slider" value="75" type="range" min="11" max="500"> <input id="guidance_scale" name="guidance_scale" size="4" pattern="^[0-9\.]+$" onkeypress="preventNonNumericalInput(event)"></td></tr>
<tr id="prompt_strength_container" class="pl-5"><td><label for="prompt_strength_slider">Prompt Strength:</label></td><td> <input id="prompt_strength_slider" name="prompt_strength_slider" class="editor-slider" value="80" type="range" min="0" max="99"> <input id="prompt_strength" name="prompt_strength" size="4" pattern="^[0-9\.]+$" onkeypress="preventNonNumericalInput(event)"><br/></td></tr>
<tr class="pl-5"><td><label for="num_inference_steps">Inference Steps:</label></td><td> <input id="num_inference_steps" name="num_inference_steps" type="number" min="1" step="1" style="width: 42pt" value="25" onkeypress="preventNonNumericalInput(event)" inputmode="numeric"></td></tr>
<tr class="pl-5"><td><label for="guidance_scale_slider">Guidance Scale:</label></td><td> <input id="guidance_scale_slider" name="guidance_scale_slider" class="editor-slider" value="75" type="range" min="11" max="500"> <input id="guidance_scale" name="guidance_scale" size="4" pattern="^[0-9\.]+$" onkeypress="preventNonNumericalInput(event)" inputmode="decimal"></td></tr>
<tr id="prompt_strength_container" class="pl-5"><td><label for="prompt_strength_slider">Prompt Strength:</label></td><td> <input id="prompt_strength_slider" name="prompt_strength_slider" class="editor-slider" value="80" type="range" min="0" max="99"> <input id="prompt_strength" name="prompt_strength" size="4" pattern="^[0-9\.]+$" onkeypress="preventNonNumericalInput(event)" inputmode="decimal"><br/></td></tr>
<tr id="lora_model_container" class="pl-5">
<td>
<label for="lora_model">LoRA:</label>
@ -358,7 +360,7 @@
</td></tr>
<tr id="hypernetwork_strength_container" class="pl-5">
<td><label for="hypernetwork_strength_slider">Hypernetwork Strength:</label></td>
<td> <input id="hypernetwork_strength_slider" name="hypernetwork_strength_slider" class="editor-slider" value="100" type="range" min="0" max="100"> <input id="hypernetwork_strength" name="hypernetwork_strength" size="4" pattern="^[0-9\.]+$" onkeypress="preventNonNumericalInput(event)"><br/></td>
<td> <input id="hypernetwork_strength_slider" name="hypernetwork_strength_slider" class="editor-slider" value="100" type="range" min="0" max="100"> <input id="hypernetwork_strength" name="hypernetwork_strength" size="4" pattern="^[0-9\.]+$" onkeypress="preventNonNumericalInput(event)" inputmode="decimal"><br/></td>
</tr>
<tr id="tiling_container" class="pl-5">
<td><label for="tiling">Seamless Tiling:</label></td>
@ -383,8 +385,15 @@
</span>
</td></tr>
<tr class="pl-5" id="output_quality_row"><td><label for="output_quality">Image Quality:</label></td><td>
<input id="output_quality_slider" name="output_quality" class="editor-slider" value="75" type="range" min="10" max="95"> <input id="output_quality" name="output_quality" size="4" pattern="^[0-9\.]+$" onkeypress="preventNonNumericalInput(event)">
<input id="output_quality_slider" name="output_quality" class="editor-slider" value="75" type="range" min="10" max="95"> <input id="output_quality" name="output_quality" size="4" pattern="^[0-9\.]+$" onkeypress="preventNonNumericalInput(event)" inputmode="numeric">
</td></tr>
<tr class="pl-5">
<td><label for="tiling">Enable VAE Tiling:</label></td>
<td class="diffusers-restart-needed">
<input id="enable_vae_tiling" name="enable_vae_tiling" type="checkbox" checked>
<label><small>Optimizes memory for larger images</small></label>
</td>
</tr>
</table></div>
<div><ul>
@ -393,7 +402,7 @@
<li class="pl-5" id="use_face_correction_container">
<input id="use_face_correction" name="use_face_correction" type="checkbox"> <label for="use_face_correction">Fix incorrect faces and eyes</label> <div style="display:inline-block;"><input id="gfpgan_model" type="text" spellcheck="false" autocomplete="off" class="model-filter" data-path="" /></div>
<table id="codeformer_settings" class="displayNone sub-settings">
<tr class="pl-5"><td><label for="codeformer_fidelity_slider">Strength:</label></td><td><input id="codeformer_fidelity_slider" name="codeformer_fidelity_slider" class="editor-slider" value="5" type="range" min="0" max="10"> <input id="codeformer_fidelity" name="codeformer_fidelity" size="4" pattern="^[0-9\.]+$" onkeypress="preventNonNumericalInput(event)"></td></tr>
<tr class="pl-5"><td><label for="codeformer_fidelity_slider">Strength:</label></td><td><input id="codeformer_fidelity_slider" name="codeformer_fidelity_slider" class="editor-slider" value="5" type="range" min="0" max="10"> <input id="codeformer_fidelity" name="codeformer_fidelity" size="4" pattern="^[0-9\.]+$" onkeypress="preventNonNumericalInput(event)" inputmode="decimal"></td></tr>
<tr class="pl-5"><td><label for="codeformer_upscale_faces">Upscale Faces:</label></td><td><input id="codeformer_upscale_faces" name="codeformer_upscale_faces" type="checkbox" checked> <label><small>(improves the resolution of faces)</small></label></td></tr>
</table>
</li>
@ -410,7 +419,7 @@
<option value="latent_upscaler">Latent Upscaler 2x</option>
</select>
<table id="latent_upscaler_settings" class="displayNone sub-settings">
<tr class="pl-5"><td><label for="latent_upscaler_steps_slider">Upscaling Steps:</label></td><td><input id="latent_upscaler_steps_slider" name="latent_upscaler_steps_slider" class="editor-slider" value="10" type="range" min="1" max="50"> <input id="latent_upscaler_steps" name="latent_upscaler_steps" size="4" pattern="^[0-9\.]+$" onkeypress="preventNonNumericalInput(event)"></td></tr>
<tr class="pl-5"><td><label for="latent_upscaler_steps_slider">Upscaling Steps:</label></td><td><input id="latent_upscaler_steps_slider" name="latent_upscaler_steps_slider" class="editor-slider" value="10" type="range" min="1" max="50"> <input id="latent_upscaler_steps" name="latent_upscaler_steps" size="4" pattern="^[0-9\.]+$" onkeypress="preventNonNumericalInput(event)" inputmode="numeric"></td></tr>
</table>
</li>
<li class="pl-5"><input id="show_only_filtered_image" name="show_only_filtered_image" type="checkbox" checked> <label for="show_only_filtered_image">Show only the corrected/upscaled image</label></li>
@ -455,14 +464,14 @@
<div class="dropdown-content">
<div class="dropdown-item">
<input id="thumbnail_size" name="thumbnail_size" class="editor-slider" type="range" value="70" min="5" max="200" oninput="sliderUpdate(event)">
<input id="thumbnail_size-input" name="thumbnail_size-input" size="3" value="70" pattern="^[0-9.]+$" onkeypress="preventNonNumericalInput(event)" oninput="sliderUpdate(event)">&nbsp;%
<input id="thumbnail_size-input" name="thumbnail_size-input" size="3" value="70" pattern="^[0-9.]+$" onkeypress="preventNonNumericalInput(event)" oninput="sliderUpdate(event)" inputmode="numeric">&nbsp;%
</div>
</div>
</div>
<div class="clearfix" style="clear: both;"></div>
</div>
<div id="supportBanner" class="displayNone">
If you found this project useful and want to help keep it alive, please consider <a href="https://ko-fi.com/easydiffusion" target="_blank">buying me a coffee</a> or <a href="https://www.patreon.com/EasyDiffusion" target="_blank">supporting me on Patreon</a> to help cover the cost of development and maintenance! Or even better, <a href="https://cmdr2.itch.io/easydiffusion" target="_blank">purchasing it at the full price</a>. Thank you for your support!
If you found this project useful and want to help keep it alive, please consider <a href="https://ko-fi.com/easydiffusion" target="_blank">buying me a coffee</a> to help cover the cost of development and maintenance! Thanks for your support!
</div>
</div>
</div>
@ -508,29 +517,45 @@
<div class="float-container">
<div class="float-child">
<h1>Help</h1>
<ul id="help-links">
<li><span class="help-section">Using the software</span>
<div id="help-links">
<h4><span class="help-section"><b>Basics</b></span></h4>
<ul>
<li> <a href="https://github.com/easydiffusion/easydiffusion/wiki/How-To-Use" target="_blank"><i class="fa-solid fa-book fa-fw"></i> How to use</a>
<li> <a href="https://github.com/easydiffusion/easydiffusion/wiki/UI-Overview" target="_blank"><i class="fa-solid fa-list fa-fw"></i> UI Overview</a>
<li> <a href="https://github.com/easydiffusion/easydiffusion/wiki/Writing-Prompts" target="_blank"><i class="fa-solid fa-pen-to-square fa-fw"></i> Writing prompts</a>
<li> <a href="https://github.com/easydiffusion/easydiffusion/wiki/Inpainting" target="_blank"><i class="fa-solid fa-paintbrush fa-fw"></i> Inpainting</a>
<li> <a href="https://github.com/easydiffusion/easydiffusion/wiki/Run-on-Multiple-GPUs" target="_blank"><i class="fa-solid fa-paintbrush fa-fw"></i> Run on Multiple GPUs</a>
<li> <a href="https://github.com/easydiffusion/easydiffusion/wiki/How-To-Use" target="_blank">How to use</a></li>
<li> <a href="https://github.com/easydiffusion/easydiffusion/wiki/Writing-Prompts" target="_blank">Writing prompts</a></li>
<li> <a href="https://github.com/easydiffusion/easydiffusion/wiki/Image-Modifiers" target="_blank">Image Modifiers</a></li>
<li> <a href="https://github.com/easydiffusion/easydiffusion/wiki/Inpainting" target="_blank">Inpainting</a></li>
<li> <a href="https://github.com/easydiffusion/easydiffusion/wiki/Samplers" target="_blank">Samplers</a></li>
<li> <a href="https://github.com/easydiffusion/easydiffusion/wiki/UI-Overview" target="_blank">Summary of every UI option</a></li>
<li> <a href="https://github.com/easydiffusion/easydiffusion/wiki/Troubleshooting" target="_blank">Common error messages (and solutions)</a></li>
</ul>
<li><span class="help-section">Installation</span>
<h4><span class="help-section"><b>Intermediate</b></span></h4>
<ul>
<li> <a href="https://github.com/easydiffusion/easydiffusion/wiki/Troubleshooting" target="_blank"><i class="fa-solid fa-circle-question fa-fw"></i> Troubleshooting</a>
<li> <a href="https://github.com/easydiffusion/easydiffusion/wiki/Custom-Models" target="_blank">Custom Models</a></li>
<li> <a href="https://github.com/easydiffusion/easydiffusion/wiki/Prompt-Syntax" target="_blank">Prompt Syntax (weights, emphasis etc)</a></li>
<li> <a href="https://github.com/easydiffusion/easydiffusion/wiki/UI-Plugins" target="_blank">UI Plugins</a></li>
<li> <a href="https://github.com/easydiffusion/easydiffusion/wiki/Embeddings" target="_blank">Embeddings</a></li>
<li> <a href="https://github.com/easydiffusion/easydiffusion/wiki/LoRA" target="_blank">LoRA</a></li>
<li> <a href="https://github.com/easydiffusion/easydiffusion/wiki/SDXL" target="_blank">SDXL</a></li>
<li> <a href="https://github.com/easydiffusion/easydiffusion/wiki/ControlNet" target="_blank">ControlNet</a></li>
<li> <a href="https://github.com/easydiffusion/easydiffusion/wiki/Seamless-Tiling" target="_blank">Seamless Tiling</a></li>
<li> <a href="https://github.com/easydiffusion/easydiffusion/wiki/xFormers" target="_blank">xFormers</a></li>
<li> <a href="https://github.com/easydiffusion/easydiffusion/wiki/The-beta-channel" target="_blank">The beta channel</a></li>
</ul>
<li><span class="help-section">Downloadable Content</span>
<h4><span class="help-section"><b>Advanced topics</b></span></h4>
<ul>
<li> <a href="https://github.com/easydiffusion/easydiffusion/wiki/Custom-Models" target="_blank"><i class="fa-solid fa-images fa-fw"></i> Custom Models</a>
<li> <a href="https://github.com/easydiffusion/easydiffusion/wiki/UI-Plugins" target="_blank"><i class="fa-solid fa-puzzle-piece fa-fw"></i> UI Plugins</a>
<li> <a href="https://github.com/easydiffusion/easydiffusion/wiki/VAE-Variational-Auto-Encoder" target="_blank"><i class="fa-solid fa-hand-sparkles fa-fw"></i> VAE Variational Auto Encoder</a>
<li> <a href="https://github.com/easydiffusion/easydiffusion/wiki/Run-on-Multiple-GPUs" target="_blank">Run on Multiple GPUs</a></li>
<li> <a href="https://github.com/easydiffusion/easydiffusion/wiki/Model-Merging" target="_blank">Model Merging</a></li>
<li> <a href="https://github.com/easydiffusion/easydiffusion/wiki/Custom-Modifiers" target="_blank">Custom Modifiers</a></li>
</ul>
<h4><span class="help-section"><b>Misc</b></span></h4>
<ul>
<li> <a href="https://theally.notion.site/The-Definitive-Stable-Diffusion-Glossary-1d1e6d15059c41e6a6b4306b4ecd9df9" target="_blank">Glossary of Stable Diffusion related terms</a></li>
</ul>
</div>
</div>
<div class="float-child">
<h1>Community</h1>
@ -712,7 +737,7 @@
<span class="embeddings-action-text">Expand Categories</span>
</button>
<i class="fa-solid fa-magnifying-glass"></i>
<input id="embeddings-search-box" type="text" spellcheck="false" autocomplete="off" placeholder="Search...">
<input id="embeddings-search-box" type="text" spellcheck="false" autocomplete="off" placeholder="Search..." inputmode="search">
<label for="embedding-card-size-selector"><small>Thumbnail Size:</small></label>
<select id="embedding-card-size-selector" name="embedding-card-size-selector">
<option value="-2">0</option>
@ -798,6 +823,7 @@
<p>This license of this software forbids you from sharing any content that violates any laws, produce any harm to a person, disseminate any personal information that would be meant for harm, <br/>spread misinformation and target vulnerable groups. For the full list of restrictions please read <a href="https://github.com/easydiffusion/easydiffusion/blob/main/LICENSE" target="_blank">the license</a>.</p>
<p>By using this software, you consent to the terms and conditions of the license.</p>
</div>
<input id="test_diffusers" type="checkbox" style="display: none" checked />
</div>
</div>
</body>

View File

@ -609,11 +609,18 @@ div.img-preview img {
margin: auto;
padding: 0px;
}
#help-links ul {
list-style-type: disc;
padding-left: 12pt;
}
#help-links li {
padding-bottom: 12pt;
padding-bottom: 6pt;
display: block;
font-size: 10pt;
}
#help-links ul li {
display: list-item;
}
#help-links li .fa-fw {
padding-right: 2pt;
}
@ -1207,6 +1214,12 @@ input::file-selector-button {
visibility: visible;
}
}
.tooltip-container {
display: inline-block;
position: relative;
}
.simple-tooltip.right {
right: 0px;
top: 50%;

View File

@ -56,6 +56,7 @@ const SETTINGS_IDS_LIST = [
"extract_lora_from_prompt",
"embedding-card-size-selector",
"lora_model",
"enable_vae_tiling",
]
const IGNORE_BY_DEFAULT = ["prompt"]

View File

@ -268,7 +268,11 @@ const TASK_MAPPING = {
tiling: {
name: "Tiling",
setUI: (val) => {
if (val === null || val === "None") {
tilingField.value = "none"
} else {
tilingField.value = val
}
},
readUI: () => tilingField.value,
parse: (val) => val,
@ -583,6 +587,7 @@ const TASK_TEXT_MAPPING = {
lora_alpha: "LoRA Strength",
use_controlnet_model: "ControlNet model",
control_filter_to_apply: "ControlNet Filter",
tiling: "Seamless Tiling",
}
function parseTaskFromText(str) {
const taskReqBody = {}

View File

@ -22,7 +22,8 @@ const taskConfigSetup = {
},
tiling: {
label: "Tiling",
visible: ({ reqBody }) => reqBody?.tiling != "none",
visible: ({ reqBody }) =>
reqBody?.tiling != "none" && reqBody?.tiling !== null && reqBody?.tiling !== undefined,
value: ({ reqBody }) => reqBody?.tiling,
},
use_vae_model: {
@ -128,6 +129,7 @@ let hypernetworkStrengthField = document.querySelector("#hypernetwork_strength")
let outputFormatField = document.querySelector("#output_format")
let outputLosslessField = document.querySelector("#output_lossless")
let outputLosslessContainer = document.querySelector("#output_lossless_container")
let enableVAETilingField = document.querySelector("#enable_vae_tiling")
let blockNSFWField = document.querySelector("#block_nsfw")
let showOnlyFilteredImageField = document.querySelector("#show_only_filtered_image")
let updateBranchLabel = document.querySelector("#updateBranchLabel")
@ -513,7 +515,7 @@ function showImages(reqBody, res, outputContainer, livePreview) {
{
text: "Use as Thumbnail",
on_click: onUseAsThumbnailClick,
filter: (req, img) => "use_embeddings_model" in req,
filter: (req, img) => "use_embeddings_model" in req || "use_lora_model" in req
},
]
@ -680,7 +682,7 @@ function getAllModelNames(type) {
// gets a flattened list of all models of a certain type. e.g. "path/subpath/modelname"
// use the filter to search for all models having a certain name.
function getAllModelPathes(type,filter="") {
function getAllModelPathes(type, filter = "") {
function f(tree, prefix) {
if (tree == undefined) {
return []
@ -690,7 +692,7 @@ function getAllModelPathes(type,filter="") {
if (typeof e == "object") {
result = result.concat(f(e[1], prefix + e[0] + "/"))
} else {
if (filter=="" || e==filter) {
if (filter == "" || e == filter) {
result.push(prefix + e)
}
}
@ -700,7 +702,6 @@ function getAllModelPathes(type,filter="") {
return f(modelsOptions[type], "")
}
function onUseAsThumbnailClick(req, img) {
let scale = 1
let targetWidth = img.naturalWidth
@ -748,16 +749,14 @@ function onUseAsThumbnailClick(req, img) {
onUseAsThumbnailClick.croppr.setImage(img.src)
}
useAsThumbSelect.innerHTML=""
if ("use_embeddings_model" in req) {
let embeddings = req.use_embeddings_model.map((e) => e.split("/").pop())
let LORA = []
if ("use_lora_model" in req) {
LORA = req.use_lora_model
}
let optgroup = document.createElement("optgroup")
optgroup.label = "Embeddings"
optgroup.replaceChildren(
let embOptions = document.createElement("optgroup")
embOptions.label = "Embeddings"
embOptions.replaceChildren(
...embeddings.map((e) => {
let option = document.createElement("option")
option.innerText = e
@ -765,8 +764,30 @@ function onUseAsThumbnailClick(req, img) {
return option
})
)
useAsThumbSelect.appendChild(embOptions)
}
if ("use_lora_model" in req) {
let LORA = req.use_lora_model
if (typeof LORA == "string") {
LORA = [LORA]
}
LORA = LORA.map((e) => e.split("/").pop())
let loraOptions = document.createElement("optgroup")
loraOptions.label = "LORA"
loraOptions.replaceChildren(
...LORA.map((e) => {
let option = document.createElement("option")
option.innerText = e
option.dataset["type"] = "lora"
return option
})
)
useAsThumbSelect.appendChild(loraOptions)
}
useAsThumbSelect.replaceChildren(optgroup)
useAsThumbDialog.showModal()
onUseAsThumbnailClick.scale = scale
}
@ -782,6 +803,50 @@ useAsThumbCancelBtn.addEventListener("click", () => {
useAsThumbDialog.close()
})
const Bucket = {
upload(path, blob) {
const formData = new FormData()
formData.append("file", blob)
return fetch(`bucket/${path}`, {
method: "POST",
body: formData,
})
},
getImageAsDataURL(path) {
return fetch(`bucket/${path}`)
.then((response) => {
if (response.status == 200) {
return response.blob()
} else {
throw new Error("Bucket error")
}
})
.then((blob) => {
return new Promise((resolve, reject) => {
const reader = new FileReader()
reader.onload = () => resolve(reader.result)
reader.onerror = reject
reader.readAsDataURL(blob)
})
})
},
getList(path) {
return fetch(`bucket/${path}`)
.then((response) => (response.status == 200 ? response.json() : []))
},
store(path, data) {
return Bucket.upload(`${path}.json`, JSON.stringify(data))
},
retrieve(path) {
return fetch(`bucket/${path}.json`)
.then((response) => (response.status == 200 ? response.json() : null))
},
}
useAsThumbSaveBtn.addEventListener("click", (e) => {
let scale = 1 / onUseAsThumbnailClick.scale
let crop = onUseAsThumbnailClick.croppr.getValue()
@ -793,22 +858,18 @@ useAsThumbSaveBtn.addEventListener("click", (e) => {
.then((thumb) => fetch(thumb))
.then((response) => response.blob())
.then(async function(blob) {
const formData = new FormData()
formData.append("file", blob)
let options = useAsThumbSelect.selectedOptions
let promises = []
for (let embedding of options) {
promises.push(
fetch(`bucket/${profileName}/${embedding.dataset["type"]}/${embedding.value}.png`, {
method: "POST",
body: formData,
})
Bucket.upload(`${profileName}/${embedding.dataset["type"]}/${embedding.value}.png`, blob)
)
}
return Promise.all(promises)
})
.then(() => {
useAsThumbDialog.close()
document.dispatchEvent(new CustomEvent("saveThumb", { detail: useAsThumbSelect.selectedOptions }))
})
.catch((error) => {
console.error(error)
@ -852,6 +913,10 @@ function applyInlineFilter(filterName, path, filterParams, img, statusText, tool
}
filterReq.model_paths[filterName] = path
if (saveToDiskField.checked && diskPathField.value.trim() !== "") {
filterReq.save_to_disk_path = diskPathField.value.trim()
}
tools.spinnerStatus.innerText = statusText
tools.spinner.classList.remove("displayNone")
@ -1237,7 +1302,6 @@ function getCurrentUserRequest() {
//render_device: undefined, // Set device affinity. Prefer this device, but wont activate.
use_stable_diffusion_model: stableDiffusionModelField.value,
clip_skip: clipSkipField.checked,
tiling: tilingField.value,
use_vae_model: vaeModelField.value,
stream_progress_updates: true,
stream_image_progress: numOutputsTotal > 50 ? false : streamImageProgressField.checked,
@ -1302,6 +1366,11 @@ function getCurrentUserRequest() {
newTask.reqBody.use_lora_model = modelNames
newTask.reqBody.lora_alpha = modelStrengths
}
if (tilingField.value !== "none") {
newTask.reqBody.tiling = tilingField.value
}
newTask.reqBody.enable_vae_tiling = enableVAETilingField.checked
}
if (testDiffusers.checked && document.getElementById("toggle-tensorrt-install").innerHTML == "Uninstall") {
// TRT is installed
@ -1338,7 +1407,7 @@ function setEmbeddings(task) {
let prompt = task.reqBody.prompt
let negativePrompt = task.reqBody.negative_prompt
let overallPrompt = (prompt + " " + negativePrompt).toLowerCase()
overallPrompt = overallPrompt.replaceAll(/[^a-z0-9\.]/g, " ") // only allow alpha-numeric and dots
overallPrompt = overallPrompt.replaceAll(/[^a-z0-9\-_\.]/g, " ") // only allow alpha-numeric, dots and hyphens
overallPrompt = overallPrompt.split(" ")
let embeddingsTree = modelsOptions["embeddings"]
@ -1562,7 +1631,7 @@ function updateInitialText() {
const countBeforeBanner = localStorage.getItem("countBeforeBanner") || 1
if (countBeforeBanner <= 0) {
// supportBanner.classList.remove("displayNone")
supportBanner.classList.remove("displayNone")
}
}
}
@ -2355,20 +2424,10 @@ function loadThumbnailImageFromFile() {
}
function updateEmbeddingsList(filter = "") {
function html(model, iconlist = [], prefix = "", filter = "") {
function html(model, iconMap = {}, prefix = "", filter = "") {
filter = filter.toLowerCase()
let toplevel = document.createElement("div")
let folders = document.createElement("div")
let embIcon = Object.assign(
{},
...iconlist.map((x) => ({
[x
.toLowerCase()
.split(".")
.slice(0, -1)
.join(".")]: x,
}))
)
let profileName = profileNameField.value
model?.forEach((m) => {
@ -2376,13 +2435,9 @@ function updateEmbeddingsList(filter = "") {
let token = m.toLowerCase()
if (token.search(filter) != -1) {
let button
// if (iconlist.length==0) {
// button = document.createElement("button")
// button.innerText = m
// } else {
let img = "/media/images/noimg.png"
if (token in embIcon) {
img = `/bucket/${profileName}/embeddings/${embIcon[token]}`
if (token in iconMap) {
img = `/bucket/${profileName}/${iconMap[token]}`
}
button = createModifierCard(m, [img, img], true)
// }
@ -2391,7 +2446,7 @@ function updateEmbeddingsList(filter = "") {
toplevel.appendChild(button)
}
} else {
let subdir = html(m[1], iconlist, prefix + m[0] + "/", filter)
let subdir = html(m[1], iconMap, prefix + m[0] + "/", filter)
if (typeof subdir == "object") {
let div1 = document.createElement("div")
let div2 = document.createElement("div")
@ -2450,11 +2505,44 @@ function updateEmbeddingsList(filter = "") {
</div>
`
let loraTokens = []
let profileName = profileNameField.value
fetch(`/bucket/${profileName}/embeddings/`)
.then((response) => (response.status == 200 ? response.json() : []))
.then(async function(iconlist) {
embeddingsList.replaceChildren(html(modelsOptions.embeddings, iconlist, "", filter))
let iconMap = {}
Bucket.getList(`${profileName}/embeddings/`)
.then((icons) => {
iconMap = Object.assign(
{},
...icons.map((x) => ({
[x
.toLowerCase()
.split(".")
.slice(0, -1)
.join(".")]: `embeddings/${x}`,
}))
)
return Bucket.getList(`${profileName}/lora/`)
})
.then(async function (icons) {
for (let lora of loraModelField.value.modelNames) {
let keywords = await getLoraKeywords(lora)
loraTokens = loraTokens.concat(keywords)
let loraname = lora.split("/").pop()
if (icons.includes(`${loraname}.png`)) {
keywords.forEach((kw) => {
iconMap[kw.toLowerCase()] = `lora/${loraname}.png`
})
}
}
let tokenList = [...modelsOptions.embeddings]
if (loraTokens.length != 0) {
tokenList.unshift(['LORA Keywords', loraTokens])
}
embeddingsList.replaceChildren(html(tokenList, iconMap, "", filter))
createCollapsibles(embeddingsList)
if (filter != "") {
embeddingsExpandAll()

View File

@ -97,6 +97,17 @@ var PARAMETERS = [
},
],
},
{
id: "models_dir",
type: ParameterType.custom,
icon: "fa-folder-tree",
label: "Models Folder",
note: "Path to the 'models' folder. Please save and refresh the page after changing this.",
saveInAppConfig: true,
render: (parameter) => {
return `<input id="${parameter.id}" name="${parameter.id}" size="30">`
},
},
{
id: "block_nsfw",
type: ParameterType.checkbox,
@ -238,7 +249,7 @@ var PARAMETERS = [
default: false,
},
{
id: "test_diffusers",
id: "use_v3_engine",
type: ParameterType.checkbox,
label: "Use the new v3 engine (diffusers)",
note:
@ -420,8 +431,9 @@ let listenPortField = document.querySelector("#listen_port")
let useBetaChannelField = document.querySelector("#use_beta_channel")
let uiOpenBrowserOnStartField = document.querySelector("#ui_open_browser_on_start")
let confirmDangerousActionsField = document.querySelector("#confirm_dangerous_actions")
let testDiffusers = document.querySelector("#test_diffusers")
let testDiffusers = document.querySelector("#use_v3_engine")
let profileNameField = document.querySelector("#profileName")
let modelsDirField = document.querySelector("#models_dir")
let saveSettingsBtn = document.querySelector("#save-system-settings-btn")
@ -463,15 +475,17 @@ async function getAppConfig() {
if (config.net && config.net.listen_port !== undefined) {
listenPortField.value = config.net.listen_port
}
modelsDirField.value = config.models_dir
let testDiffusersEnabled = true
if (config.test_diffusers === false) {
if (config.use_v3_engine === false) {
testDiffusersEnabled = false
}
testDiffusers.checked = testDiffusersEnabled
document.querySelector("#test_diffusers").checked = testDiffusers.checked // don't break plugins
if (config.config_on_startup) {
if (config.config_on_startup?.test_diffusers) {
if (config.config_on_startup?.use_v3_engine) {
document.body.classList.add("diffusers-enabled-on-startup")
document.body.classList.remove("diffusers-disabled-on-startup")
} else {
@ -511,6 +525,10 @@ async function getAppConfig() {
customHeightField.step = IMAGE_STEP_SIZE
}
if (config.force_save_metadata) {
metadataOutputFormatField.value = config.force_save_metadata
}
console.log("get config status response", config)
return config
@ -722,10 +740,13 @@ async function getSystemInfo() {
force = res["enforce_output_dir"]
if (force == true) {
saveToDiskField.checked = true
metadataOutputFormatField.disabled = false
metadataOutputFormatField.disabled = res["enforce_output_metadata"]
diskPathField.disabled = true
}
saveToDiskField.disabled = force
diskPathField.disabled = force
} else {
diskPathField.disabled = !saveToDiskField.checked
metadataOutputFormatField.disabled = !saveToDiskField.checked
}
setDiskPath(res["default_output_dir"], force)
} catch (e) {

View File

@ -1,454 +0,0 @@
;(function() {
"use strict"
///////////////////// Function section
function smoothstep(x) {
return x * x * (3 - 2 * x)
}
function smootherstep(x) {
return x * x * x * (x * (x * 6 - 15) + 10)
}
function smootheststep(x) {
let y = -20 * Math.pow(x, 7)
y += 70 * Math.pow(x, 6)
y -= 84 * Math.pow(x, 5)
y += 35 * Math.pow(x, 4)
return y
}
function getCurrentTime() {
const now = new Date()
let hours = now.getHours()
let minutes = now.getMinutes()
let seconds = now.getSeconds()
hours = hours < 10 ? `0${hours}` : hours
minutes = minutes < 10 ? `0${minutes}` : minutes
seconds = seconds < 10 ? `0${seconds}` : seconds
return `${hours}:${minutes}:${seconds}`
}
function addLogMessage(message) {
const logContainer = document.getElementById("merge-log")
logContainer.innerHTML += `<i>${getCurrentTime()}</i> ${message}<br>`
// Scroll to the bottom of the log
logContainer.scrollTop = logContainer.scrollHeight
document.querySelector("#merge-log-container").style.display = "block"
}
function addLogSeparator() {
const logContainer = document.getElementById("merge-log")
logContainer.innerHTML += "<hr>"
logContainer.scrollTop = logContainer.scrollHeight
}
function drawDiagram(fn) {
const SIZE = 300
const canvas = document.getElementById("merge-canvas")
canvas.height = canvas.width = SIZE
const ctx = canvas.getContext("2d")
// Draw coordinate system
ctx.scale(1, -1)
ctx.translate(0, -canvas.height)
ctx.lineWidth = 1
ctx.beginPath()
ctx.strokeStyle = "white"
ctx.moveTo(0, 0)
ctx.lineTo(0, SIZE)
ctx.lineTo(SIZE, SIZE)
ctx.lineTo(SIZE, 0)
ctx.lineTo(0, 0)
ctx.lineTo(SIZE, SIZE)
ctx.stroke()
ctx.beginPath()
ctx.setLineDash([1, 2])
const n = SIZE / 10
for (let i = n; i < SIZE; i += n) {
ctx.moveTo(0, i)
ctx.lineTo(SIZE, i)
ctx.moveTo(i, 0)
ctx.lineTo(i, SIZE)
}
ctx.stroke()
ctx.beginPath()
ctx.setLineDash([])
ctx.beginPath()
ctx.strokeStyle = "black"
ctx.lineWidth = 3
// Plot function
const numSamples = 20
for (let i = 0; i <= numSamples; i++) {
const x = i / numSamples
const y = fn(x)
const canvasX = x * SIZE
const canvasY = y * SIZE
if (i === 0) {
ctx.moveTo(canvasX, canvasY)
} else {
ctx.lineTo(canvasX, canvasY)
}
}
ctx.stroke()
// Plot alpha values (yellow boxes)
let start = parseFloat(document.querySelector("#merge-start").value)
let step = parseFloat(document.querySelector("#merge-step").value)
let iterations = document.querySelector("#merge-count").value >> 0
ctx.beginPath()
ctx.fillStyle = "yellow"
for (let i = 0; i < iterations; i++) {
const alpha = (start + i * step) / 100
const x = alpha * SIZE
const y = fn(alpha) * SIZE
if (x <= SIZE) {
ctx.rect(x - 3, y - 3, 6, 6)
ctx.fill()
} else {
ctx.strokeStyle = "red"
ctx.moveTo(0, 0)
ctx.lineTo(0, SIZE)
ctx.lineTo(SIZE, SIZE)
ctx.lineTo(SIZE, 0)
ctx.lineTo(0, 0)
ctx.lineTo(SIZE, SIZE)
ctx.stroke()
addLogMessage("<i>Warning: maximum ratio is &#8805; 100%</i>")
}
}
}
function updateChart() {
let fn = (x) => x
switch (document.querySelector("#merge-interpolation").value) {
case "SmoothStep":
fn = smoothstep
break
case "SmootherStep":
fn = smootherstep
break
case "SmoothestStep":
fn = smootheststep
break
}
drawDiagram(fn)
}
createTab({
id: "merge",
icon: "fa-code-merge",
label: "Merge models",
css: `
#tab-content-merge .tab-content-inner {
max-width: 100%;
padding: 10pt;
}
.merge-container {
margin-left: 15%;
margin-right: 15%;
text-align: left;
display: inline-grid;
grid-template-columns: 1fr 1fr;
grid-template-rows: auto auto auto;
gap: 0px 0px;
grid-auto-flow: row;
grid-template-areas:
"merge-input merge-config"
"merge-buttons merge-buttons";
}
.merge-container p {
margin-top: 3pt;
margin-bottom: 3pt;
}
.merge-config .tab-content {
background: var(--background-color1);
border-radius: 3pt;
}
.merge-config .tab-content-inner {
text-align: left;
}
.merge-input {
grid-area: merge-input;
padding-left:1em;
}
.merge-config {
grid-area: merge-config;
padding:1em;
}
.merge-config input {
margin-bottom: 3px;
}
.merge-config select {
margin-bottom: 3px;
}
.merge-buttons {
grid-area: merge-buttons;
padding:1em;
text-align: center;
}
#merge-button {
padding: 8px;
width:20em;
}
div#merge-log {
height:150px;
overflow-x:hidden;
overflow-y:scroll;
background:var(--background-color1);
border-radius: 3pt;
}
div#merge-log i {
color: hsl(var(--accent-hue), 100%, calc(2*var(--accent-lightness)));
font-family: monospace;
}
.disabled {
background: var(--background-color4);
color: var(--text-color);
}
#merge-type-tabs {
border-bottom: 1px solid black;
}
#merge-log-container {
display: none;
}
.merge-container #merge-warning {
color: rgb(153, 153, 153);
}`,
content: `
<div class="merge-container panel-box">
<div class="merge-input">
<p><label for="#mergeModelA">Select Model A:</label></p>
<input id="mergeModelA" type="text" spellcheck="false" autocomplete="off" class="model-filter" data-path="" />
<p><label for="#mergeModelB">Select Model B:</label></p>
<input id="mergeModelB" type="text" spellcheck="false" autocomplete="off" class="model-filter" data-path="" />
<br/><br/>
<p id="merge-warning"><small><b>Important:</b> Please merge models of similar type.<br/>For e.g. <code>SD 1.4</code> models with only <code>SD 1.4/1.5</code> models,<br/><code>SD 2.0</code> with <code>SD 2.0</code>-type, and <code>SD 2.1</code> with <code>SD 2.1</code>-type models.</small></p>
<br/>
<table>
<tr>
<td><label for="#merge-filename">Output file name:</label></td>
<td><input id="merge-filename" size=24> <i class="fa-solid fa-circle-question help-btn"><span class="simple-tooltip top-left">Base name of the output file.<br>Mix ratio and file suffix will be appended to this.</span></i></td>
</tr>
<tr>
<td><label for="#merge-fp">Output precision:</label></td>
<td><select id="merge-fp">
<option value="fp16">fp16 (smaller file size)</option>
<option value="fp32">fp32 (larger file size)</option>
</select>
<i class="fa-solid fa-circle-question help-btn"><span class="simple-tooltip top-left">Image generation uses fp16, so it's a good choice.<br>Use fp32 if you want to use the result models for more mixes</span></i>
</td>
</tr>
<tr>
<td><label for="#merge-format">Output file format:</label></td>
<td><select id="merge-format">
<option value="safetensors">Safetensors (recommended)</option>
<option value="ckpt">CKPT/Pickle (legacy format)</option>
</select>
</td>
</tr>
</table>
<br/>
<div id="merge-log-container">
<p><label for="#merge-log">Log messages:</label></p>
<div id="merge-log"></div>
</div>
</div>
<div class="merge-config">
<div class="tab-container">
<span id="tab-merge-opts-single" class="tab active">
<span>Make a single file</small></span>
</span>
<span id="tab-merge-opts-batch" class="tab">
<span>Make multiple variations</small></span>
</span>
</div>
<div>
<div id="tab-content-merge-opts-single" class="tab-content active">
<div class="tab-content-inner">
<small>Saves a single merged model file, at the specified merge ratio.</small><br/><br/>
<label for="#single-merge-ratio-slider">Merge ratio:</label>
<input id="single-merge-ratio-slider" name="single-merge-ratio-slider" class="editor-slider" value="50" type="range" min="1" max="1000">
<input id="single-merge-ratio" size=2 value="5">%
<i class="fa-solid fa-circle-question help-btn"><span class="simple-tooltip top-left">Model A's contribution to the mix. The rest will be from Model B.</span></i>
</div>
</div>
<div id="tab-content-merge-opts-batch" class="tab-content">
<div class="tab-content-inner">
<small>Saves multiple variations of the model, at different merge ratios.<br/>Each variation will be saved as a separate file.</small><br/><br/>
<table>
<tr><td><label for="#merge-count">Number of variations:</label></td>
<td> <input id="merge-count" size=2 value="5"></td>
<td> <i class="fa-solid fa-circle-question help-btn"><span class="simple-tooltip top-left">Number of models to create</span></i></td></tr>
<tr><td><label for="#merge-start">Starting merge ratio:</label></td>
<td> <input id="merge-start" size=2 value="5">%</td>
<td> <i class="fa-solid fa-circle-question help-btn"><span class="simple-tooltip top-left">Smallest share of model A in the mix</span></i></td></tr>
<tr><td><label for="#merge-step">Increment each step:</label></td>
<td> <input id="merge-step" size=2 value="10">%</td>
<td> <i class="fa-solid fa-circle-question help-btn"><span class="simple-tooltip top-left">Share of model A added into the mix per step</span></i></td></tr>
<tr><td><label for="#merge-interpolation">Interpolation model:</label></td>
<td> <select id="merge-interpolation">
<option>Exact</option>
<option>SmoothStep</option>
<option>SmootherStep</option>
<option>SmoothestStep</option>
</select></td>
<td> <i class="fa-solid fa-circle-question help-btn"><span class="simple-tooltip top-left">Sigmoid function to be applied to the model share before mixing</span></i></td></tr>
</table>
<br/>
<small>Preview of variation ratios:</small><br/>
<canvas id="merge-canvas" width="400" height="400"></canvas>
</div>
</div>
</div>
</div>
<div class="merge-buttons">
<button id="merge-button" class="primaryButton">Merge models</button>
</div>
</div>`,
onOpen: ({ firstOpen }) => {
if (!firstOpen) {
return
}
const tabSettingsSingle = document.querySelector("#tab-merge-opts-single")
const tabSettingsBatch = document.querySelector("#tab-merge-opts-batch")
linkTabContents(tabSettingsSingle)
linkTabContents(tabSettingsBatch)
console.log("Activate")
let mergeModelAField = new ModelDropdown(document.querySelector("#mergeModelA"), "stable-diffusion")
let mergeModelBField = new ModelDropdown(document.querySelector("#mergeModelB"), "stable-diffusion")
updateChart()
// slider
const singleMergeRatioField = document.querySelector("#single-merge-ratio")
const singleMergeRatioSlider = document.querySelector("#single-merge-ratio-slider")
function updateSingleMergeRatio() {
singleMergeRatioField.value = singleMergeRatioSlider.value / 10
singleMergeRatioField.dispatchEvent(new Event("change"))
}
function updateSingleMergeRatioSlider() {
if (singleMergeRatioField.value < 0) {
singleMergeRatioField.value = 0
} else if (singleMergeRatioField.value > 100) {
singleMergeRatioField.value = 100
}
singleMergeRatioSlider.value = singleMergeRatioField.value * 10
singleMergeRatioSlider.dispatchEvent(new Event("change"))
}
singleMergeRatioSlider.addEventListener("input", updateSingleMergeRatio)
singleMergeRatioField.addEventListener("input", updateSingleMergeRatioSlider)
updateSingleMergeRatio()
document.querySelector(".merge-config").addEventListener("change", updateChart)
document.querySelector("#merge-button").addEventListener("click", async function(e) {
// Build request template
let model0 = mergeModelAField.value
let model1 = mergeModelBField.value
let request = { model0: model0, model1: model1 }
request["use_fp16"] = document.querySelector("#merge-fp").value == "fp16"
let iterations = document.querySelector("#merge-count").value >> 0
let start = parseFloat(document.querySelector("#merge-start").value)
let step = parseFloat(document.querySelector("#merge-step").value)
if (isTabActive(tabSettingsSingle)) {
start = parseFloat(singleMergeRatioField.value)
step = 0
iterations = 1
addLogMessage(`merge ratio = ${start}%`)
} else {
addLogMessage(`start = ${start}%`)
addLogMessage(`step = ${step}%`)
}
if (start + (iterations - 1) * step >= 100) {
addLogMessage("<i>Aborting: maximum ratio is &#8805; 100%</i>")
addLogMessage("Reduce the number of variations or the step size")
addLogSeparator()
document.querySelector("#merge-count").focus()
return
}
if (document.querySelector("#merge-filename").value == "") {
addLogMessage("<i>Aborting: No output file name specified</i>")
addLogSeparator()
document.querySelector("#merge-filename").focus()
return
}
// Disable merge button
e.target.disabled = true
e.target.classList.add("disabled")
let cursor = $("body").css("cursor")
let label = document.querySelector("#merge-button").innerHTML
$("body").css("cursor", "progress")
document.querySelector("#merge-button").innerHTML = "Merging models ..."
addLogMessage("Merging models")
addLogMessage("Model A: " + model0)
addLogMessage("Model B: " + model1)
// Batch main loop
for (let i = 0; i < iterations; i++) {
let alpha = (start + i * step) / 100
if (isTabActive(tabSettingsBatch)) {
switch (document.querySelector("#merge-interpolation").value) {
case "SmoothStep":
alpha = smoothstep(alpha)
break
case "SmootherStep":
alpha = smootherstep(alpha)
break
case "SmoothestStep":
alpha = smootheststep(alpha)
break
}
}
addLogMessage(`merging batch job ${i + 1}/${iterations}, alpha = ${alpha.toFixed(5)}...`)
request["out_path"] = document.querySelector("#merge-filename").value
request["out_path"] += "-" + alpha.toFixed(5) + "." + document.querySelector("#merge-format").value
addLogMessage(`&nbsp;&nbsp;filename: ${request["out_path"]}`)
// sdkit documentation: "ratio - the ratio of the second model. 1 means only the second model will be used."
request["ratio"] = 1-alpha
let res = await fetch("/model/merge", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify(request),
})
const data = await res.json()
addLogMessage(JSON.stringify(data))
}
addLogMessage(
"<b>Done.</b> The models have been saved to your <tt>models/stable-diffusion</tt> folder."
)
addLogSeparator()
// Re-enable merge button
$("body").css("cursor", cursor)
document.querySelector("#merge-button").innerHTML = label
e.target.disabled = false
e.target.classList.remove("disabled")
// Update model list
stableDiffusionModelField.innerHTML = ""
vaeModelField.innerHTML = ""
hypernetworkModelField.innerHTML = ""
await getModels()
})
},
})
})()

View File

@ -0,0 +1,770 @@
;(function() {
"use strict"
let mergeCSS = `
/*********** Main tab ***********/
.tab-centered {
justify-content: center;
}
#model-tool-tab-content {
background-color: var(--background-color3);
}
#model-tool-tab-content .tab-content-inner {
text-align: initial;
}
#model-tool-tab-bar .tab {
margin-bottom: 0px;
border-top-left-radius: var(--input-border-radius);
background-color: var(--background-color3);
padding: 6px 6px 0.8em 6px;
}
#tab-content-merge .tab-content-inner {
max-width: 100%;
padding: 10pt;
}
/*********** Merge UI ***********/
.merge-model-container {
margin-left: 15%;
margin-right: 15%;
text-align: left;
display: inline-grid;
grid-template-columns: 1fr 1fr;
grid-template-rows: auto auto auto;
gap: 0px 0px;
grid-auto-flow: row;
grid-template-areas:
"merge-input merge-config"
"merge-buttons merge-buttons";
}
.merge-model-container p {
margin-top: 3pt;
margin-bottom: 3pt;
}
.merge-config .tab-content {
background: var(--background-color1);
border-radius: 3pt;
}
.merge-config .tab-content-inner {
text-align: left;
}
.merge-input {
grid-area: merge-input;
padding-left:1em;
}
.merge-config {
grid-area: merge-config;
padding:1em;
}
.merge-config input {
margin-bottom: 3px;
}
.merge-config select {
margin-bottom: 3px;
}
.merge-buttons {
grid-area: merge-buttons;
padding:1em;
text-align: center;
}
#merge-button {
padding: 8px;
width:20em;
}
div#merge-log {
height:150px;
overflow-x:hidden;
overflow-y:scroll;
background:var(--background-color1);
border-radius: 3pt;
}
div#merge-log i {
color: hsl(var(--accent-hue), 100%, calc(2*var(--accent-lightness)));
font-family: monospace;
}
.disabled {
background: var(--background-color4);
color: var(--text-color);
}
#merge-type-tabs {
border-bottom: 1px solid black;
}
#merge-log-container {
display: none;
}
.merge-model-container #merge-warning {
color: var(--small-label-color);
}
/*********** LORA UI ***********/
.lora-manager-grid {
display: grid;
gap: 0px 8px;
grid-auto-flow: row;
}
@media screen and (min-width: 1501px) {
.lora-manager-grid textarea {
height:350px;
}
.lora-manager-grid {
grid-template-columns: auto 1fr 1fr;
grid-template-rows: auto 1fr;
grid-template-areas:
"selector selector selector"
"thumbnail keywords notes";
}
}
@media screen and (min-width: 1001px) and (max-width: 1500px) {
.lora-manager-grid textarea {
height:250px;
}
.lora-manager-grid {
grid-template-columns: auto auto;
grid-template-rows: auto auto auto;
grid-template-areas:
"selector selector"
"thumbnail keywords"
"thumbnail notes";
}
}
@media screen and (max-width: 1000px) {
.lora-manager-grid textarea {
height:200px;
}
.lora-manager-grid {
grid-template-columns: auto;
grid-template-rows: auto auto auto auto;
grid-template-areas:
"selector"
"keywords"
"thumbnail"
"notes";
}
}
.lora-manager-grid-selector {
grid-area: selector;
justify-self: start;
}
.lora-manager-grid-thumbnail {
grid-area: thumbnail;
justify-self: center;
}
.lora-manager-grid-keywords {
grid-area: keywords;
}
.lora-manager-grid-notes {
grid-area: notes;
}
.lora-manager-grid p {
margin-bottom: 2px;
}
`
let mergeUI = `
<div class="merge-model-container panel-box">
<div class="merge-input">
<p><label for="#mergeModelA">Select Model A:</label></p>
<input id="mergeModelA" type="text" spellcheck="false" autocomplete="off" class="model-filter" data-path="" />
<p><label for="#mergeModelB">Select Model B:</label></p>
<input id="mergeModelB" type="text" spellcheck="false" autocomplete="off" class="model-filter" data-path="" />
<br/><br/>
<p id="merge-warning"><small><b>Important:</b> Please merge models of similar type.<br/>For e.g. <code>SD 1.4</code> models with only <code>SD 1.4/1.5</code> models,<br/><code>SD 2.0</code> with <code>SD 2.0</code>-type, and <code>SD 2.1</code> with <code>SD 2.1</code>-type models.</small></p>
<br/>
<table>
<tr>
<td><label for="#merge-filename">Output file name:</label></td>
<td><input id="merge-filename" size=24> <i class="fa-solid fa-circle-question help-btn"><span class="simple-tooltip top-left">Base name of the output file.<br>Mix ratio and file suffix will be appended to this.</span></i></td>
</tr>
<tr>
<td><label for="#merge-fp">Output precision:</label></td>
<td><select id="merge-fp">
<option value="fp16">fp16 (smaller file size)</option>
<option value="fp32">fp32 (larger file size)</option>
</select>
<i class="fa-solid fa-circle-question help-btn"><span class="simple-tooltip top-left">Image generation uses fp16, so it's a good choice.<br>Use fp32 if you want to use the result models for more mixes</span></i>
</td>
</tr>
<tr>
<td><label for="#merge-format">Output file format:</label></td>
<td><select id="merge-format">
<option value="safetensors">Safetensors (recommended)</option>
<option value="ckpt">CKPT/Pickle (legacy format)</option>
</select>
</td>
</tr>
</table>
<br/>
<div id="merge-log-container">
<p><label for="#merge-log">Log messages:</label></p>
<div id="merge-log"></div>
</div>
</div>
<div class="merge-config">
<div class="tab-container">
<span id="tab-merge-opts-single" class="tab active">
<span>Make a single file</small></span>
</span>
<span id="tab-merge-opts-batch" class="tab">
<span>Make multiple variations</small></span>
</span>
</div>
<div>
<div id="tab-content-merge-opts-single" class="tab-content active">
<div class="tab-content-inner">
<small>Saves a single merged model file, at the specified merge ratio.</small><br/><br/>
<label for="#single-merge-ratio-slider">Merge ratio:</label>
<input id="single-merge-ratio-slider" name="single-merge-ratio-slider" class="editor-slider" value="50" type="range" min="1" max="1000">
<input id="single-merge-ratio" size=2 value="5">%
<i class="fa-solid fa-circle-question help-btn"><span class="simple-tooltip top-left">Model A's contribution to the mix. The rest will be from Model B.</span></i>
</div>
</div>
<div id="tab-content-merge-opts-batch" class="tab-content">
<div class="tab-content-inner">
<small>Saves multiple variations of the model, at different merge ratios.<br/>Each variation will be saved as a separate file.</small><br/><br/>
<table>
<tr><td><label for="#merge-count">Number of variations:</label></td>
<td> <input id="merge-count" size=2 value="5"></td>
<td> <i class="fa-solid fa-circle-question help-btn"><span class="simple-tooltip top-left">Number of models to create</span></i></td></tr>
<tr><td><label for="#merge-start">Starting merge ratio:</label></td>
<td> <input id="merge-start" size=2 value="5">%</td>
<td> <i class="fa-solid fa-circle-question help-btn"><span class="simple-tooltip top-left">Smallest share of model A in the mix</span></i></td></tr>
<tr><td><label for="#merge-step">Increment each step:</label></td>
<td> <input id="merge-step" size=2 value="10">%</td>
<td> <i class="fa-solid fa-circle-question help-btn"><span class="simple-tooltip top-left">Share of model A added into the mix per step</span></i></td></tr>
<tr><td><label for="#merge-interpolation">Interpolation model:</label></td>
<td> <select id="merge-interpolation">
<option>Exact</option>
<option>SmoothStep</option>
<option>SmootherStep</option>
<option>SmoothestStep</option>
</select></td>
<td> <i class="fa-solid fa-circle-question help-btn"><span class="simple-tooltip top-left">Sigmoid function to be applied to the model share before mixing</span></i></td></tr>
</table>
<br/>
<small>Preview of variation ratios:</small><br/>
<canvas id="merge-canvas" width="400" height="400"></canvas>
</div>
</div>
</div>
</div>
<div class="merge-buttons">
<button id="merge-button" class="primaryButton">Merge models</button>
</div>
</div>`
let loraUI=`
<div class="panel-box lora-manager-grid">
<div class="lora-manager-grid-selector">
<label for="#loraModel">Select Lora:</label>
<input id="loraModel" type="text" spellcheck="false" autocomplete="off" class="model-filter" data-path="" />
</div>
<div class="lora-manager-grid-thumbnail">
<p style="height:2em;">Thumbnail:</p>
<div style="position:relative; height:256px; width:256px;background-color:#222;border-radius:1em;margin-bottom:1em;">
<i id="lora-manager-image-placeholder" class="fa-regular fa-image" style="font-size:500%;color:#555;position:absolute; top: 50%; left: 50%; transform: translate(-50%,-50%);"></i>
<img id="lora-manager-image" class="displayNone" style="border-radius:6px;max-height:256px;max-width:256px;"/>
</div>
<div style="text-align:center;">
<button class="tertiaryButton" id="lora-manager-upload-button"><i class="fa-solid fa-upload"></i> Upload new thumbnail</button>
<input id="lora-manager-upload-input" name="lora-manager-upload-input" type="file" class="displayNone">
<!-- button class="tertiaryButton"><i class="fa-solid fa-trash-can"></i> Remove</button -->
</div>
</div>
<div class="lora-manager-grid-keywords">
<p style="height:2em;">Keywords:
<span style="float:right;margin-bottom:4px;"><button id="lora-keyword-from-civitai" class="tertiaryButton smallButton">Import from Civitai</button></span></p>
<textarea style="width:100%;resize:vertical;" id="lora-manager-keywords" placeholder="Put LORA specific keywords here..."></textarea>
<p style="color:var(--small-label-color);">
<b>LORA model keywords</b> can be used via the <code>+&nbsp;Embeddings</code> button. They get added to the embedding
keyword menu when the LORA has been selected in the image settings.
</p>
</div>
<div class="lora-manager-grid-notes">
<p style="height:2em;">Notes:</p>
<textarea style="width:100%;resize:vertical;" id="lora-manager-notes" placeholder="Place for things you want to remember..."></textarea>
<p id="civitai-section" class="displayNone">
<b>Civitai model page:</b>
<a id="civitai-model-page" target="_blank"></a>
</p>
</div>
</div>`
let tabHTML=`
<div id="model-tool-tab-bar" class="tab-container tab-centered">
<span id="tab-model-loraUI" class="tab active">
<span><i class="fa-solid fa-key"></i> Lora Keywords</small></span>
</span>
<span id="tab-model-mergeUI" class="tab">
<span><i class="fa-solid fa-code-merge"></i> Merge Models</small></span>
</span>
</div>
<div id="model-tool-tab-content" class="panel-box">
<div id="tab-content-model-loraUI" class="tab-content active">
<div class="tab-content-inner">
${loraUI}
</div>
</div>
<div id="tab-content-model-mergeUI" class="tab-content">
<div class="tab-content-inner">
${mergeUI}
</div>
</div>
</div>`
///////////////////// Function section
function smoothstep(x) {
return x * x * (3 - 2 * x)
}
function smootherstep(x) {
return x * x * x * (x * (x * 6 - 15) + 10)
}
function smootheststep(x) {
let y = -20 * Math.pow(x, 7)
y += 70 * Math.pow(x, 6)
y -= 84 * Math.pow(x, 5)
y += 35 * Math.pow(x, 4)
return y
}
function getCurrentTime() {
const now = new Date()
let hours = now.getHours()
let minutes = now.getMinutes()
let seconds = now.getSeconds()
hours = hours < 10 ? `0${hours}` : hours
minutes = minutes < 10 ? `0${minutes}` : minutes
seconds = seconds < 10 ? `0${seconds}` : seconds
return `${hours}:${minutes}:${seconds}`
}
function addLogMessage(message) {
const logContainer = document.getElementById("merge-log")
logContainer.innerHTML += `<i>${getCurrentTime()}</i> ${message}<br>`
// Scroll to the bottom of the log
logContainer.scrollTop = logContainer.scrollHeight
document.querySelector("#merge-log-container").style.display = "block"
}
function addLogSeparator() {
const logContainer = document.getElementById("merge-log")
logContainer.innerHTML += "<hr>"
logContainer.scrollTop = logContainer.scrollHeight
}
function drawDiagram(fn) {
const SIZE = 300
const canvas = document.getElementById("merge-canvas")
canvas.height = canvas.width = SIZE
const ctx = canvas.getContext("2d")
// Draw coordinate system
ctx.scale(1, -1)
ctx.translate(0, -canvas.height)
ctx.lineWidth = 1
ctx.beginPath()
ctx.strokeStyle = "white"
ctx.moveTo(0, 0)
ctx.lineTo(0, SIZE)
ctx.lineTo(SIZE, SIZE)
ctx.lineTo(SIZE, 0)
ctx.lineTo(0, 0)
ctx.lineTo(SIZE, SIZE)
ctx.stroke()
ctx.beginPath()
ctx.setLineDash([1, 2])
const n = SIZE / 10
for (let i = n; i < SIZE; i += n) {
ctx.moveTo(0, i)
ctx.lineTo(SIZE, i)
ctx.moveTo(i, 0)
ctx.lineTo(i, SIZE)
}
ctx.stroke()
ctx.beginPath()
ctx.setLineDash([])
ctx.beginPath()
ctx.strokeStyle = "black"
ctx.lineWidth = 3
// Plot function
const numSamples = 20
for (let i = 0; i <= numSamples; i++) {
const x = i / numSamples
const y = fn(x)
const canvasX = x * SIZE
const canvasY = y * SIZE
if (i === 0) {
ctx.moveTo(canvasX, canvasY)
} else {
ctx.lineTo(canvasX, canvasY)
}
}
ctx.stroke()
// Plot alpha values (yellow boxes)
let start = parseFloat(document.querySelector("#merge-start").value)
let step = parseFloat(document.querySelector("#merge-step").value)
let iterations = document.querySelector("#merge-count").value >> 0
ctx.beginPath()
ctx.fillStyle = "yellow"
for (let i = 0; i < iterations; i++) {
const alpha = (start + i * step) / 100
const x = alpha * SIZE
const y = fn(alpha) * SIZE
if (x <= SIZE) {
ctx.rect(x - 3, y - 3, 6, 6)
ctx.fill()
} else {
ctx.strokeStyle = "red"
ctx.moveTo(0, 0)
ctx.lineTo(0, SIZE)
ctx.lineTo(SIZE, SIZE)
ctx.lineTo(SIZE, 0)
ctx.lineTo(0, 0)
ctx.lineTo(SIZE, SIZE)
ctx.stroke()
addLogMessage("<i>Warning: maximum ratio is &#8805; 100%</i>")
}
}
}
function updateChart() {
let fn = (x) => x
switch (document.querySelector("#merge-interpolation").value) {
case "SmoothStep":
fn = smoothstep
break
case "SmootherStep":
fn = smootherstep
break
case "SmoothestStep":
fn = smootheststep
break
}
drawDiagram(fn)
}
function initMergeUI() {
const tabSettingsSingle = document.querySelector("#tab-merge-opts-single")
const tabSettingsBatch = document.querySelector("#tab-merge-opts-batch")
linkTabContents(tabSettingsSingle)
linkTabContents(tabSettingsBatch)
let mergeModelAField = new ModelDropdown(document.querySelector("#mergeModelA"), "stable-diffusion")
let mergeModelBField = new ModelDropdown(document.querySelector("#mergeModelB"), "stable-diffusion")
updateChart()
// slider
const singleMergeRatioField = document.querySelector("#single-merge-ratio")
const singleMergeRatioSlider = document.querySelector("#single-merge-ratio-slider")
function updateSingleMergeRatio() {
singleMergeRatioField.value = singleMergeRatioSlider.value / 10
singleMergeRatioField.dispatchEvent(new Event("change"))
}
function updateSingleMergeRatioSlider() {
if (singleMergeRatioField.value < 0) {
singleMergeRatioField.value = 0
} else if (singleMergeRatioField.value > 100) {
singleMergeRatioField.value = 100
}
singleMergeRatioSlider.value = singleMergeRatioField.value * 10
singleMergeRatioSlider.dispatchEvent(new Event("change"))
}
singleMergeRatioSlider.addEventListener("input", updateSingleMergeRatio)
singleMergeRatioField.addEventListener("input", updateSingleMergeRatioSlider)
updateSingleMergeRatio()
document.querySelector(".merge-config").addEventListener("change", updateChart)
document.querySelector("#merge-button").addEventListener("click", async function(e) {
// Build request template
let model0 = mergeModelAField.value
let model1 = mergeModelBField.value
let request = { model0: model0, model1: model1 }
request["use_fp16"] = document.querySelector("#merge-fp").value == "fp16"
let iterations = document.querySelector("#merge-count").value >> 0
let start = parseFloat(document.querySelector("#merge-start").value)
let step = parseFloat(document.querySelector("#merge-step").value)
if (isTabActive(tabSettingsSingle)) {
start = parseFloat(singleMergeRatioField.value)
step = 0
iterations = 1
addLogMessage(`merge ratio = ${start}%`)
} else {
addLogMessage(`start = ${start}%`)
addLogMessage(`step = ${step}%`)
}
if (start + (iterations - 1) * step >= 100) {
addLogMessage("<i>Aborting: maximum ratio is &#8805; 100%</i>")
addLogMessage("Reduce the number of variations or the step size")
addLogSeparator()
document.querySelector("#merge-count").focus()
return
}
if (document.querySelector("#merge-filename").value == "") {
addLogMessage("<i>Aborting: No output file name specified</i>")
addLogSeparator()
document.querySelector("#merge-filename").focus()
return
}
// Disable merge button
e.target.disabled = true
e.target.classList.add("disabled")
let cursor = $("body").css("cursor")
let label = document.querySelector("#merge-button").innerHTML
$("body").css("cursor", "progress")
document.querySelector("#merge-button").innerHTML = "Merging models ..."
addLogMessage("Merging models")
addLogMessage("Model A: " + model0)
addLogMessage("Model B: " + model1)
// Batch main loop
for (let i = 0; i < iterations; i++) {
let alpha = (start + i * step) / 100
if (isTabActive(tabSettingsBatch)) {
switch (document.querySelector("#merge-interpolation").value) {
case "SmoothStep":
alpha = smoothstep(alpha)
break
case "SmootherStep":
alpha = smootherstep(alpha)
break
case "SmoothestStep":
alpha = smootheststep(alpha)
break
}
}
addLogMessage(`merging batch job ${i + 1}/${iterations}, alpha = ${alpha.toFixed(5)}...`)
request["out_path"] = document.querySelector("#merge-filename").value
request["out_path"] += "-" + alpha.toFixed(5) + "." + document.querySelector("#merge-format").value
addLogMessage(`&nbsp;&nbsp;filename: ${request["out_path"]}`)
// sdkit documentation: "ratio - the ratio of the second model. 1 means only the second model will be used."
request["ratio"] = 1-alpha
let res = await fetch("/model/merge", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify(request),
})
const data = await res.json()
addLogMessage(JSON.stringify(data))
}
addLogMessage(
"<b>Done.</b> The models have been saved to your <tt>models/stable-diffusion</tt> folder."
)
addLogSeparator()
// Re-enable merge button
$("body").css("cursor", cursor)
document.querySelector("#merge-button").innerHTML = label
e.target.disabled = false
e.target.classList.remove("disabled")
// Update model list
stableDiffusionModelField.innerHTML = ""
vaeModelField.innerHTML = ""
hypernetworkModelField.innerHTML = ""
await getModels()
})
}
const LoraUI = {
modelField: undefined,
keywordsField: undefined,
notesField: undefined,
civitaiImportBtn: undefined,
civitaiSecion: undefined,
civitaiAnchor: undefined,
image: undefined,
imagePlaceholder: undefined,
init() {
LoraUI.modelField = new ModelDropdown(document.querySelector("#loraModel"), "lora", "None")
LoraUI.keywordsField = document.querySelector("#lora-manager-keywords")
LoraUI.notesField = document.querySelector("#lora-manager-notes")
LoraUI.civitaiImportBtn = document.querySelector("#lora-keyword-from-civitai")
LoraUI.civitaiSection = document.querySelector("#civitai-section")
LoraUI.civitaiAnchor = document.querySelector("#civitai-model-page")
LoraUI.image = document.querySelector("#lora-manager-image")
LoraUI.imagePlaceholder = document.querySelector("#lora-manager-image-placeholder")
LoraUI.uploadBtn = document.querySelector("#lora-manager-upload-button")
LoraUI.uploadInput = document.querySelector("#lora-manager-upload-input")
LoraUI.modelField.addEventListener("change", LoraUI.updateFields)
LoraUI.keywordsField.addEventListener("focusout", LoraUI.saveInfos)
LoraUI.notesField.addEventListener("focusout", LoraUI.saveInfos)
LoraUI.civitaiImportBtn.addEventListener("click", LoraUI.importFromCivitai)
LoraUI.uploadBtn.addEventListener("click", (e) => LoraUI.uploadInput.click())
LoraUI.uploadInput.addEventListener("change", LoraUI.uploadLoraThumb)
document.addEventListener("saveThumb", LoraUI.updateFields)
LoraUI.updateFields()
},
uploadLoraThumb(e) {
console.log(e)
if (LoraUI.uploadInput.files.length === 0) {
return
}
let reader = new FileReader()
let file = LoraUI.uploadInput.files[0]
reader.addEventListener("load", (event) => {
let img = document.createElement("img")
img.src = reader.result
onUseAsThumbnailClick(
{
use_lora_model: LoraUI.modelField.value,
},
img
)
})
if (file) {
reader.readAsDataURL(file)
}
},
updateFields() {
document.getElementById("civitai-section").classList.add("displayNone")
Bucket.retrieve(`modelinfo/lora/${LoraUI.modelField.value}`)
.then((info) => {
if (info == null) {
LoraUI.keywordsField.value = ""
LoraUI.notesField.value = ""
LoraUI.hideCivitaiLink()
} else {
LoraUI.keywordsField.value = info.keywords.join("\n")
LoraUI.notesField.value = info.notes
if ("civitai" in info && info["civitai"] != null) {
LoraUI.showCivitaiLink(info.civitai)
} else {
LoraUI.hideCivitaiLink()
}
}
})
Bucket.getImageAsDataURL(`${profileNameField.value}/lora/${LoraUI.modelField.value}.png`)
.then((data) => {
LoraUI.image.src=data
LoraUI.image.classList.remove("displayNone")
LoraUI.imagePlaceholder.classList.add("displayNone")
})
.catch((error) => {
LoraUI.image.classList.add("displayNone")
LoraUI.imagePlaceholder.classList.remove("displayNone")
})
},
saveInfos() {
let info = {
keywords: LoraUI.keywordsField.value
.split("\n")
.filter((x) => (x != "")),
notes: LoraUI.notesField.value,
civitai: LoraUI.civitaiSection.checkVisibility() ? LoraUI.civitaiAnchor.href : null,
}
Bucket.store(`modelinfo/lora/${LoraUI.modelField.value}`, info)
},
importFromCivitai() {
document.body.style["cursor"] = "progress"
fetch("/sha256/lora/"+LoraUI.modelField.value)
.then((result) => result.json())
.then((json) => fetch("https://civitai.com/api/v1/model-versions/by-hash/" + json.digest))
.then((result) => result.json())
.then((json) => {
document.body.style["cursor"] = "default"
if (json == null) {
return
}
if ("trainedWords" in json) {
LoraUI.keywordsField.value = json["trainedWords"].join("\n")
} else {
showToast("No keyword info found.")
}
if ("modelId" in json) {
LoraUI.showCivitaiLink("https://civitai.com/models/" + json.modelId)
} else {
LoraUI.hideCivitaiLink()
}
LoraUI.saveInfos()
})
},
showCivitaiLink(href) {
LoraUI.civitaiSection.classList.remove("displayNone")
LoraUI.civitaiAnchor.href = href
LoraUI.civitaiAnchor.innerHTML = LoraUI.civitaiAnchor.href
},
hideCivitaiLink() {
LoraUI.civitaiSection.classList.add("displayNone")
}
}
createTab({
id: "merge",
icon: "fa-toolbox",
label: "Model tools",
css: mergeCSS,
content: tabHTML,
onOpen: ({ firstOpen }) => {
if (!firstOpen) {
return
}
initMergeUI()
LoraUI.init()
const tabMergeUI = document.querySelector("#tab-model-mergeUI")
const tabLoraUI = document.querySelector("#tab-model-loraUI")
linkTabContents(tabMergeUI)
linkTabContents(tabLoraUI)
},
})
})()
async function getLoraKeywords(model) {
return Bucket.retrieve(`modelinfo/lora/${model}`)
.then((info) => info ? info.keywords : [])
}