Compare commits

...

1406 Commits

Author SHA1 Message Date
w-e-w 08c68820cd gradio 3.41.2 attempt 2023-12-03 20:47:31 +09:00
AUTOMATIC1111 f0f100e67b add categories to settings 2023-11-26 17:56:22 +03:00
AUTOMATIC1111 500de919ed Merge pull request #14108 from AUTOMATIC1111/json.dump(ensure_ascii=False)
json.dump(ensure_ascii=False)
2023-11-26 16:15:56 +03:00
w-e-w a15dd151ff json.dump(ensure_ascii=False)
improve json readability
2023-11-26 21:56:21 +09:00
AUTOMATIC1111 2a40d3c603 compact prompt layout: preserve scroll when switching between lora tabs 2023-11-26 14:58:56 +03:00
AUTOMATIC1111 e44103264d Merge pull request #13936 from cabelo/compatibility
Compatibility
2023-11-26 11:57:13 +03:00
AUTOMATIC1111 6955c210b7 Merge pull request #14059 from akx/upruff
Update Ruff to 0.1.6
2023-11-26 11:54:36 +03:00
AUTOMATIC1111 d1750e5eca fix linter errors 2023-11-26 11:37:12 +03:00
AUTOMATIC1111 c5a0c59a83 do not save HTML explanations from options page to config 2023-11-26 11:36:17 +03:00
AUTOMATIC1111 f7f015e84b Merge pull request #14084 from wfjsw/move-from-sysinfo-to-errors
Move exception_records related methods to errors.py
2023-11-26 11:29:27 +03:00
AUTOMATIC1111 f85b74763d Merge branch 'hypertile-in-sample' into dev 2023-11-26 11:18:49 +03:00
AUTOMATIC1111 fd8674a4bc Merge pull request #13948 from aria1th/hypertile-in-sample
support HyperTile optimization
2023-11-26 11:18:25 +03:00
AUTOMATIC1111 d2e0c1ca13 rework hypertile into a built-in extension 2023-11-26 11:17:38 +03:00
AUTOMATIC1111 3a9bf4ac10 move file 2023-11-26 08:29:12 +03:00
Jabasukuriputo Wang 5cedc8f9b2 remove traceback in sysinfo 2023-11-24 11:30:30 -06:00
Jabasukuriputo Wang 86b99b1e98 Move exception_records related methods to errors.py 2023-11-24 11:28:54 -06:00
Aarni Koskela 066afda2f6 Simplify restart_sampler (suggested by ruff) 2023-11-22 18:05:12 +02:00
Aarni Koskela 8fe1e19522 Update ruff to 0.1.6 2023-11-22 18:05:12 +02:00
AUTOMATIC1111 8aa51f682c fix [Bug]: (Dev Branch) Placing "Dimensions" first in "ui_reorder_list" prevents start #14047 2023-11-21 08:32:07 +03:00
AUTOMATIC1111 5f36f6ab21 Merge pull request #14009 from AUTOMATIC1111/Option-to-show-batch-img2img-results-in-UI
Option to show batch img2img results in UI
2023-11-20 17:44:58 +03:00
AUTOMATIC1111 1463cea949 Merge branch 'dag' into dev 2023-11-20 14:50:01 +03:00
AUTOMATIC1111 73a0b4bba6 Merge pull request #13944 from wfjsw/dag
implementing script metadata and DAG sorting mechanism
2023-11-20 14:49:46 +03:00
AUTOMATIC1111 9b471436b2 rework extensions metadata: use custom sorter that doesn't mess the order as much and ignores cyclic errors, use classes with named fields instead of dictionaries, eliminate some duplicated code 2023-11-20 14:47:09 +03:00
AUTOMATIC1111 411da7c281 Merge pull request #14035 from AUTOMATIC1111/sysinfo-json
save sysinfo as .json
2023-11-20 08:56:45 +03:00
w-e-w 6d337bf23d save sysinfo as .json
GitHub now allows uploading of .json files in issues
2023-11-20 01:38:31 +09:00
w-e-w dea5e43c83 Option to show batch img2img results in UI
shared.opts.img2img_batch_show_results_limit
limit the number of images return to the UI for batch img2img
default limit 32
0 no images are shown
-1 unlimited, all images are shown
2023-11-19 17:37:32 +09:00
wfjsw bde439ef67 use metadata.ini for meta filename 2023-11-19 00:58:47 -06:00
AUTOMATIC1111 fc83af4432 Merge pull request #13931 from AUTOMATIC1111/style-hotkeys
Enable prompt hotkeys in style editor
2023-11-19 09:11:49 +03:00
AUTOMATIC1111 337bc4a2fb Merge pull request #13014 from AUTOMATIC1111/thread-safe-extranetworks-list_items
thread safe extra network list_items
2023-11-19 09:09:21 +03:00
AUTOMATIC1111 6fac65f334 Merge pull request #13929 from kingljl/fix-dependency-address-patch-1
Fix dependency address patch 1
2023-11-19 09:01:39 +03:00
AUTOMATIC1111 5a031d9233 Merge pull request #13962 from kaalibro/dev
Fixes generation restart not working for some users when 'Ctrl+Enter' is pressed
2023-11-19 09:01:11 +03:00
AUTOMATIC1111 e4e875fffe Merge pull request #13968 from kaalibro/extranetworks-path-sorting
Adds 'Path' sorting for Extra network cards
2023-11-19 09:00:05 +03:00
AUTOMATIC1111 b945ba716b Merge pull request #13977 from AUTOMATIC1111/hotfix-postprocessing-state-end
Hotfix: call shared.state.end() after postprocessing done
2023-11-19 08:59:32 +03:00
AUTOMATIC1111 2207ef363a Merge pull request #13692 from v0xie/network-oft
Support inference with OFT networks
2023-11-19 08:59:09 +03:00
AUTOMATIC1111 3a13b0e762 Merge pull request #13996 from Luxter77/patch-1
Adds tqdm handler to logging_config.py for progress bar integration
2023-11-19 08:57:14 +03:00
AUTOMATIC1111 6429c3db11 Merge pull request #13826 from ezxzeng/ui_mobile_optimizations
added accordion settings options
2023-11-19 08:42:58 +03:00
AUTOMATIC1111 5a9dc1c0ca Merge pull request #14004 from storyicon/master
feat: fix randn found element of type float at pos 2
2023-11-19 08:40:29 +03:00
storyicon 4f2a4a3615 feat: fix randn found element of type float at pos 2
Signed-off-by: storyicon <storyicon@foxmail.com>
2023-11-17 09:48:18 +00:00
aria1th 97431f29fe fix double gc and decoding with unet context 2023-11-17 10:05:28 +09:00
aria1th ffd0f8ddc3 set empty value for SD XL 3rd layer 2023-11-17 09:54:33 +09:00
aria1th c0725ba2d0 Fix inverted option issue
I'm pretty sure I was sleepy while implementing this
2023-11-17 09:34:50 +09:00
aria1th c40be2252a Fix critical issue - unet apply 2023-11-17 09:22:27 +09:00
Your Name 7021cdb1de actually adds handler to logging_config.py 2023-11-16 17:53:57 -03:00
Lucas Daniel Velazquez M cdb60a690d Take into account tqdm not being installed before first boot for logging 2023-11-16 16:49:59 -03:00
Lucas Daniel Velazquez M 236eb82c3a Adds tqdm handler to logging_config.py for progress bar integration 2023-11-16 13:20:33 -03:00
AngelBottomless 472c22cc8a fix ruff - add newline 2023-11-16 19:03:45 +09:00
AngelBottomless bcfaf3979a convert/add hypertile options 2023-11-16 18:43:16 +09:00
v0xie eb667e715a feat: LyCORIS/kohya OFT network support 2023-11-15 18:28:48 -08:00
v0xie d6d0b22e66 fix: ignore calc_scale() for COFT which has very small alpha 2023-11-15 03:08:50 -08:00
aria1th af45872fdb copy LDM VAE key from XL 2023-11-15 15:15:14 +09:00
aria1th b29fc6d4de Implement Hypertile
Co-Authored-By: Kieran Hunt <kph@hotmail.ca>
2023-11-15 15:13:39 +09:00
AngelBottomless a292d2c47f hotfix: call shared.state.end() after postprocessing done 2023-11-15 14:26:37 +09:00
kaalibro c1c816006e Adds 'Path' sorting for Extra network cards 2023-11-13 22:01:52 +06:00
kaalibro 94e9669566 Fixes generation restart not working for some users when 'Ctrl+Enter' is pressed 2023-11-13 14:51:06 +06:00
wfjsw 3bb32befe9 bug fix 2023-11-11 11:58:19 -06:00
wfjsw 48d6102b31 fix 2023-11-11 11:17:26 -06:00
wfjsw 520e52f846 allow comma and whitespace as separator 2023-11-11 10:58:26 -06:00
wfjsw 7af576e745 remove the assumption of same name 2023-11-11 10:46:47 -06:00
aria1th 294f8a514f add hyperTile
https://github.com/tfernd/HyperTile
2023-11-11 23:28:12 +09:00
wfjsw bc1a450124 reverse the extension load order so builtin extensions load earlier natively 2023-11-11 04:08:45 -06:00
wfjsw 0d1924c48b populate loaded_extensions from extension list instead 2023-11-11 04:03:55 -06:00
wfjsw 0fc7dc1c04 implementing script metadata and DAG sorting mechanism 2023-11-11 04:01:13 -06:00
Emily Zeng 3a4a6c43a4 ExitStack as alternative to suppress 2023-11-10 16:06:01 -05:00
w-e-w 5432d93013 fix added accordion settings options 2023-11-11 05:30:35 +09:00
Alessandro de Oliveira Faria (A.K.A. CABELO) 6a86b3ad9b Compatibility with Debian 11, Fedora 34+ and openSUSE 15.4+ 2023-11-10 14:15:34 -03:00
missionfloyd 7ff54005fe Enable prompt hotkeys in style editor 2023-11-09 23:47:53 -07:00
Alessandro de Oliveira Faria (A.K.A. CABELO) 66767e3876 - opensuse compatibility 2023-11-10 03:45:44 -03:00
fuchen.ljl 6d77a6e1c6 Update README.md
Modify the stablediffusion dependency address
2023-11-10 14:40:39 +08:00
fuchen.ljl 98fc525a2c Update README.md
Modify the stablediffusion dependency address
2023-11-10 14:37:30 +08:00
Emily Zeng ff2952f105 multiline with statement for readibility 2023-11-09 13:35:52 -05:00
Emily Zeng 9aa4d098f0 removed changes that weren't merged properly 2023-11-09 13:25:24 -05:00
Emily Zeng a625a7bb81 moved nested with to single line to remove extra tabs 2023-11-09 13:15:06 -05:00
ezxzeng f9c14a8c8c Merge branch 'dev' into ui_mobile_optimizations 2023-11-07 15:25:27 -05:00
AUTOMATIC1111 5e80d9ee99 fix pix2pix producing bad results 2023-11-07 11:33:33 +03:00
AUTOMATIC1111 47bccbebae Merge pull request #13884 from GerryDE/notification-sound-volume
Add option to set notification sound volume
2023-11-07 08:29:06 +03:00
GerryDE 9ba991cad8 Add option to set notification sound volume 2023-11-07 03:09:08 +01:00
AUTOMATIC1111 9c1c0da026 fix exception related to the pix2pix 2023-11-06 11:17:36 +03:00
AUTOMATIC1111 656437e0a5 fix img2img_tabs error 2023-11-06 10:32:21 +03:00
AUTOMATIC1111 6ad666e479 more changes for #13865: fix formatting, rename the function, add comment and add a readme entry 2023-11-05 19:46:20 +03:00
AUTOMATIC1111 80d639a440 linter 2023-11-05 19:32:21 +03:00
AUTOMATIC1111 96ee3eff6c Merge pull request #13865 from Gothos/master
Add support for SSD-1B
2023-11-05 19:31:44 +03:00
AUTOMATIC1111 ff805d8d0e Merge branch 'dev' into master 2023-11-05 19:30:57 +03:00
AUTOMATIC1111 c3699d4fd1 compact prompt option disabled by default 2023-11-05 19:23:48 +03:00
AUTOMATIC1111 4d4a9e7332 added compact prompt option 2023-11-05 19:19:55 +03:00
Ritesh Gangnani 44c5097375 Use devices.torch_gc() instead of empty_cache() 2023-11-05 20:31:57 +05:30
Ritesh Gangnani 44db35fb1a Added memory clearance after deletion 2023-11-05 19:15:38 +05:30
Ritesh Gangnani ff1609f91e Add SSD-1B as a supported model 2023-11-05 19:13:49 +05:30
AUTOMATIC1111 d9499f4301 properly apply sort order for extra network cards when selected from dropdown
allow selection of default sort order in settings
remove 'Default' sort order, replace with 'Name'
2023-11-05 10:12:50 +03:00
AUTOMATIC1111 16ab174290 eslint 2023-11-05 09:20:15 +03:00
AUTOMATIC1111 046c7b053a Merge pull request #13855 from gibiee/patch-1
Corrected a typo in `modules/cmd_args.py`
2023-11-05 08:57:59 +03:00
AUTOMATIC1111 6b8c661c49 add a visible checkbox to input accordion 2023-11-05 08:55:54 +03:00
gibiee 2b06cefe66 correct a typo
modify "defaul" to "default"
2023-11-05 11:37:23 +09:00
v0xie 7edd50f304 Merge pull request #2 from v0xie/network-oft-change-impl
Use same updown implementation for LyCORIS OFT as kohya-ss OFT
2023-11-04 15:06:04 -07:00
v0xie bbf00a96af refactor: remove unused function 2023-11-04 14:56:47 -07:00
v0xie 329c8bacce refactor: use same updown for both kohya OFT and LyCORIS diag-oft 2023-11-04 14:54:36 -07:00
v0xie 1dd25be037 Merge pull request #1 from v0xie/oft-faster
Support LyCORIS diag-oft OFT implementation (minus MultiheadAttention layer), maintains support for kohya-ss OFT
2023-11-03 19:47:27 -07:00
v0xie f6c8201e56 refactor: move factorization to lyco_helpers, separate calc_updown for kohya and kb 2023-11-03 19:35:15 -07:00
v0xie fe1967a4c4 skip multihead attn for now 2023-11-03 17:52:55 -07:00
AUTOMATIC1111 452ab8fe72 Merge pull request #13718 from avantcontra/bugfix_gfpgan_custom_path
fix bug when using --gfpgan-models-path
2023-11-03 20:19:58 +03:00
AUTOMATIC1111 399baa54c2 Merge pull request #13733 from dben/patch-1
Update prompts_from_file script to allow concatenating entries with the general prompt.
2023-11-03 20:19:04 +03:00
AUTOMATIC1111 21d561885e Merge pull request #13762 from wkpark/nextjob
call state.jobnext() before postproces*()
2023-11-03 20:16:58 +03:00
AUTOMATIC1111 73c74baa6a Merge pull request #13797 from Meerkov/master
Fix #13796
2023-11-03 20:11:54 +03:00
AUTOMATIC1111 1f373a2baa Merge pull request #13829 from AUTOMATIC1111/paren-fix
Fix parenthesis auto selection
2023-11-03 19:59:01 +03:00
AUTOMATIC1111 4afaaf8a02 add changelog entry 2023-11-03 19:50:14 +03:00
AUTOMATIC1111 bda2ecdbf5 Merge pull request #13839 from AUTOMATIC1111/httpx==0.24.1
requirements_versions httpx==0.24.1
2023-11-03 19:46:07 +03:00
AUTOMATIC1111 4c423f6d37 Merge pull request #13839 from AUTOMATIC1111/httpx==0.24.1
requirements_versions httpx==0.24.1
2023-11-03 19:44:57 +03:00
w-e-w cc80a09d82 Update requirements_versions.txt 2023-11-04 00:50:30 +09:00
missionfloyd 8052a4971e Fix parenthesis auto selection
Fixes #13813
2023-11-03 00:59:19 -06:00
Emily Zeng 759515316e added accordion settings options 2023-11-02 21:54:48 -04:00
v0xie d727ddfccd no idea what i'm doing, trying to support both type of OFT, kblueleaf diag_oft has MultiheadAttn which kohya's doesn't?, attempt create new module based off network_lora.py, errors about tensor dim mismatch 2023-11-02 00:13:11 -07:00
v0xie 65ccd6305f detect diag_oft type 2023-11-02 00:11:32 -07:00
v0xie a2fad6ee05 test implementation based on kohaku diag-oft implementation 2023-11-01 22:34:27 -07:00
Meerkov fbc5c531b9 Fix #13796
Fix comment error that makes understanding scheduling more confusing.
2023-10-29 15:37:08 -07:00
Won-Kyu Park 5121846d34 call state.jobnext() before postproces*() 2023-10-25 21:57:41 +09:00
David Benson dfc4c27b24 linting issue 2023-10-23 08:26:40 -04:00
David Benson 88b2ef3b04 Update prompts_from_file script to allow concatenating entries with the general prompt. 2023-10-23 08:16:26 -04:00
v0xie 6523edb8a4 style: conform style 2023-10-22 09:31:15 -07:00
v0xie 3b8515d2c9 fix: multiplier applied twice in finalize_updown 2023-10-22 09:27:48 -07:00
v0xie 4a50c9638c refactor: remove used OFT functions 2023-10-22 08:54:24 -07:00
v0xie de8ee92ed8 fix: use merge_weight to cache value 2023-10-21 17:37:17 -07:00
v0xie 76f5abdbdb style: cleanup oft 2023-10-21 16:07:45 -07:00
v0xie fce86ab7d7 fix: support multiplier, no forward pass hook 2023-10-21 16:03:54 -07:00
v0xie 7683547728 fix: return orig weights during updown, merge weights before forward 2023-10-21 14:42:24 -07:00
v0xie 2d8c894b27 refactor: use forward hook instead of custom forward 2023-10-21 13:43:31 -07:00
avantcontra 236dd55dbe fix Blank line contains whitespace 2023-10-22 04:32:13 +08:00
avantcontra 443ca983ad fix bug when using --gfpgan-models-path 2023-10-22 03:21:23 +08:00
AUTOMATIC1111 464fbcd921 fix the situation with emphasis editing (aaaa:1.1) bbbb (cccc:1.1) 2023-10-21 09:09:32 +03:00
AUTOMATIC1111 384fab9627 rework some of changes for emphasis editing keys, force conversion of old-style emphasis 2023-10-21 08:45:51 +03:00
v0xie 0550659ce6 style: fix ambiguous variable name 2023-10-19 13:13:02 -07:00
v0xie d10c4db57e style: formatting 2023-10-19 12:52:14 -07:00
v0xie 321680ccd0 refactor: fix constraint, re-use get_weight 2023-10-19 12:41:17 -07:00
v0xie eb01d7f0e0 faster by calculating R in updown and using cached R in forward 2023-10-18 04:56:53 -07:00
v0xie 853e21d98e faster by using cached R in forward 2023-10-18 04:27:44 -07:00
v0xie 1c6efdbba7 inference working but SLOW 2023-10-18 04:16:01 -07:00
v0xie ec718f76b5 wip incorrect OFT implementation 2023-10-17 23:35:50 -07:00
AUTOMATIC1111 861cbd5636 Merge pull request #13644 from XpucT/dev
Start / Restart generation by Ctrl (Alt) + Enter
2023-10-15 14:19:48 +03:00
Khachatur Avanesian d33cb2b812 Add files via upload
LF
2023-10-15 11:01:45 +03:00
Khachatur Avanesian 3e223523ce Update script.js 2023-10-15 10:48:50 +03:00
Khachatur Avanesian d295e97a0d Update script.js
LF instead CRLF
2023-10-15 10:37:48 +03:00
Khachatur Avanesian 77bd953da2 Update script.js
Exclude lambda
2023-10-15 10:25:36 +03:00
AUTOMATIC1111 2f6ea8b103 respect keyedit_precision_attention setting when converting from old (((attention))) syntax 2023-10-15 10:12:38 +03:00
AUTOMATIC1111 a3d9b011a3 Merge pull request #13533 from missionfloyd/edit-attention-fix
Edit-attention fixes
2023-10-15 10:08:52 +03:00
AUTOMATIC1111 282903bb67 repair unload sd checkpoint button 2023-10-15 09:41:02 +03:00
AUTOMATIC1111 0d65d0eabd add an option to not print stack traces on ctrl+c. 2023-10-15 08:45:38 +03:00
Khachatur Avanesian f00eaa4d00 Start / Restart generation by Ctrl (Alt) + Enter
Add ability to interrupt current generation and start generation again by Ctrl (Alt) + Enter
2023-10-15 02:34:03 +03:00
AUTOMATIC1111 d4255506ff Merge pull request #13638 from wkpark/user-settings-2
webui.settings.bat support
2023-10-14 23:00:35 +03:00
Won-Kyu Park 117ec71994 support webui.settings.bat 2023-10-15 04:36:27 +09:00
AUTOMATIC1111 4be7b620c2 Merge pull request #13568 from AUTOMATIC1111/lora_emb_bundle
Add lora-embedding bundle system
2023-10-14 12:18:55 +03:00
AUTOMATIC1111 a8cbe50c9f remove duplicated code 2023-10-14 12:17:59 +03:00
AUTOMATIC1111 19f5795c27 Merge pull request #13463 from FluttyProger/patch-1
Ability for extensions to return custom data via api in response.images
2023-10-14 08:37:45 +03:00
AUTOMATIC1111 6fe16a9e1a Merge pull request #12991 from AUTOMATIC1111/but-report-template
Update bug_report.yml
2023-10-14 08:36:43 +03:00
AUTOMATIC1111 eadef35512 Merge pull request #13567 from LeonZhao28/bugfix_key_error_in_processing
fix the key error exception when processing override_settings keys
2023-10-14 08:34:41 +03:00
AUTOMATIC1111 771dac9c5f Merge pull request #13459 from wkpark/preview-fix
show the preview image in the modalview if available
2023-10-14 08:21:53 +03:00
AUTOMATIC1111 0619df9835 use shallow copy for #13535 2023-10-14 08:01:04 +03:00
AUTOMATIC1111 7cc96429f2 Merge pull request #13535 from chu8129/dev
fix: checkpoints_loaded:{checkpoint:state_dict}, model.load_state_dict issue in dict value empty
2023-10-14 08:00:04 +03:00
AUTOMATIC1111 26500b8c1b Merge pull request #13610 from v0xie/network-glora
Support inference with LyCORIS GLora networks
2023-10-14 07:52:52 +03:00
AUTOMATIC1111 a109c7aeb8 more general case of adding an infotext when no images have been generated 2023-10-14 07:49:03 +03:00
AUTOMATIC1111 27fdc26a74 Merge pull request #13630 from wkpark/indexerror-fix
fix IndexError
2023-10-14 07:46:34 +03:00
AUTOMATIC1111 3a66c3c9e1 put notification.mp3 option at the end of the page 2023-10-14 07:35:06 +03:00
AUTOMATIC1111 499543cf1d Merge pull request #13631 from galekseev/master
added option to play notification sound or not
2023-10-14 07:30:31 +03:00
AUTOMATIC1111 902afa6b4c Merge pull request #13364 from superhero-7/master
Add altdiffusion-m18 support
2023-10-14 07:29:01 +03:00
missionfloyd fff1a0c74f Make attention conversion optional
Fix square brackets multiplier
2023-10-13 17:18:02 -06:00
missionfloyd 954499a494 Convert (emphasis) to (emphasis:1.1)
per @SirVeggie's suggestion
2023-10-13 16:46:05 -06:00
Gleb Alekseev 44d14bc32e added option to play notification sound or not 2023-10-13 15:08:59 -03:00
Won-Kyu Park fbc8d21354 fix IndexError: list index out of range error interrupted while postprocess 2023-10-14 02:45:09 +09:00
v0xie 906d1179e9 support inference with LyCORIS GLora networks 2023-10-11 21:26:58 -07:00
Won-Kyu Park dbb10fbd8c show the preview image in the modalview if available 2023-10-11 21:56:17 +09:00
Kohaku-Blueleaf 891ccb767c Fix lint 2023-10-10 15:07:25 +08:00
Kohaku-Blueleaf 81e94de318 Add warning when meet emb name conflicting
Choose standalone embedding (in /embeddings folder) first
2023-10-10 14:44:20 +08:00
Kohaku-Blueleaf 2282eb8dd5 Remove dev debug print 2023-10-10 12:11:00 +08:00
Kohaku-Blueleaf 3d8b1af6be Support string_to_param nested dict
format:
bundle_emb.EMBNAME.string_to_param.KEYNAME
2023-10-10 12:09:33 +08:00
Kohaku-Blueleaf 2aa485b5af add lora bundle system 2023-10-09 22:52:09 +08:00
Leon 9821625a76 fix the key error exception when adding an overwriting key which is defined in the extensions 2023-10-09 18:36:48 +08:00
missionfloyd 3562b0dc74 Fix negative values 2023-10-07 15:52:16 -06:00
missionfloyd fd51b8501e Fix multi-line selections 2023-10-07 15:28:25 -06:00
missionfloyd 09a2da835e Add brackets, vertical bar to default delimiters 2023-10-07 14:48:43 -06:00
wangqiuwen 770ee23f18 reverst 2023-10-07 15:38:50 +08:00
wangqiuwen 76010a51ef up 2023-10-07 15:36:01 +08:00
missionfloyd e34949be52 Edit-attention fixes 2023-10-06 22:49:33 -06:00
w-e-w 35fd24e857 Less placeholder bug_report template 2023-10-03 23:05:48 +09:00
AUTOMATIC1111 7d60076b8b case-insensitive search for settings 2023-10-03 16:22:32 +03:00
AUTOMATIC1111 77171923f8 Merge pull request #13475 from wkpark/regress-fix
fix regression
2023-10-03 12:38:11 +03:00
AUTOMATIC1111 c4ffeb857e Merge pull request #13480 from AUTOMATIC1111/popup-fix
Fix accidentally closing popup dialogs
2023-10-03 12:37:46 +03:00
missionfloyd e5381320b9 Lint 2023-10-02 22:33:03 -06:00
missionfloyd 86a46e8189 Fix accidentally closing popup dialogs 2023-10-02 22:22:15 -06:00
Won-Kyu Park c2279da522 fix re_param_code (regression bug PR #13458) 2023-10-03 01:16:41 +09:00
AUTOMATIC1111 dc2074c46d Merge pull request #13466 from AUTOMATIC1111/denoising-none
Change denoising_strength default to None.
2023-10-02 13:05:27 +03:00
AUTOMATIC1111 362675e75b Merge pull request #13469 from PermissionDenied7335/master
I found a code snippet in webui.sh that disables python venv and moved it to the appropriate location
2023-10-02 12:47:02 +03:00
PermissionDenied7335 6ab0b65ed1 Added an option not to enable venv 2023-10-02 15:43:59 +08:00
missionfloyd 3f763d41e8 Change denoising_strength default to None. 2023-10-01 22:38:27 -06:00
FluttyProger f71e919ecb Ability for extensions to return custom data via api in response.images 2023-10-01 18:06:48 +03:00
AUTOMATIC1111 e3c849da06 Merge pull request #13458 from wkpark/fieldname-regex
fix fieldname regex
2023-10-01 11:49:42 +03:00
AUTOMATIC1111 c0113872c5 add search field to settings 2023-10-01 11:48:41 +03:00
Won-Kyu Park deeec0b343 fix fieldname regex to accept additional [-/] chars 2023-10-01 16:19:59 +09:00
AUTOMATIC1111 c7e810a985 add onEdit function for js and rework token-counter.js to use it 2023-10-01 10:15:23 +03:00
superhero-7 2d947175b9 fix linter issues 2023-10-01 12:25:19 +08:00
AUTOMATIC1111 7026b96476 Merge pull request #13444 from AUTOMATIC1111/edit-attn-delimiters
edit-attention: Allow editing whitespace delimiters
2023-10-01 07:04:08 +03:00
missionfloyd 56ef5e9d48 Remove end parenthesis from weight 2023-09-30 21:44:05 -06:00
missionfloyd 0eb5fde2fd Remove unneeded code 2023-09-30 21:20:58 -06:00
missionfloyd 0935d2c304 Use checkboxes for whitespace delimiters 2023-09-30 18:37:44 -06:00
AUTOMATIC1111 b2f9709538 get #13121 to work without restart 2023-09-30 10:29:10 +03:00
AUTOMATIC1111 5cc7bf3876 reword sd_checkpoint_dropdown_use_short setting and add explanation 2023-09-30 10:10:57 +03:00
AUTOMATIC1111 416fbde726 Merge pull request #13121 from AUTOMATIC1111/consolidated-allowed-preview-formats
Consolidated allowed preview formats, Fix extra network `.gif` not woking as preview
2023-09-30 10:09:45 +03:00
missionfloyd 1cc7c4bfb3 Allow editing whitespace delimiters 2023-09-30 01:09:09 -06:00
AUTOMATIC1111 951842d785 Merge pull request #13139 from AUTOMATIC1111/ckpt-dir-path-separator
fix `--ckpt-dir` path separator and option use `short name` for checkpoint dropdown
2023-09-30 10:02:28 +03:00
AUTOMATIC1111 591ad1dbc3 Merge pull request #13170 from AUTOMATIC1111/re-fix-batch-img2img-output-dir-with-script
Re fix batch img2img output dir with script
2023-09-30 09:59:21 +03:00
AUTOMATIC1111 fcfe5c179b Merge pull request #12877 from zixaphir/removeExtraNetworksFromPrompt_fix
account for customizable extra network separators in remove code
2023-09-30 09:49:37 +03:00
AUTOMATIC1111 a0e979badb Merge pull request #13178 from wpdong0727/fix-lora-bias-backup-reset
fix: lora-bias-backup don't reset cache
2023-09-30 09:48:38 +03:00
AUTOMATIC1111 3aa9f01bdc Merge pull request #13077 from sdwebui-extensions/master
fix localization when there are multi same localization file in the extensions
2023-09-30 09:47:52 +03:00
AUTOMATIC1111 4e5d2526cb Merge pull request #13189 from AUTOMATIC1111/make-InputAccordion-work-with-ui-config
make InputAccordion work with ui-config
2023-09-30 09:46:55 +03:00
AUTOMATIC1111 ab63054f95 write infotext to gif image as comment 2023-09-30 09:34:50 +03:00
AUTOMATIC1111 0c71967a53 Merge pull request #13068 from JaredTherriault/master
Load comments from gif images to gather geninfo from gif outputs
2023-09-30 09:33:14 +03:00
AUTOMATIC1111 b20cd352d9 Merge pull request #13210 from AUTOMATIC1111/fetch-version-info-when-webui_dir-is-not-work_dir-
fix issues when webui_dir is not work_dir
2023-09-30 09:23:32 +03:00
AUTOMATIC1111 3a4290f833 Merge pull request #13229 from AUTOMATIC1111/initialize-state.time_start-befroe-state.job_count
initialize state.time_start befroe state.job_count
2023-09-30 09:21:47 +03:00
AUTOMATIC1111 df48222f3e Merge pull request #13231 from der3318/better-support-for-portable-git
Better Support for Portable Git
2023-09-30 09:21:08 +03:00
AUTOMATIC1111 ee8e98711b Merge pull request #13266 from wkpark/xyz-prepare
xyz_grid: add prepare
2023-09-30 09:17:24 +03:00
AUTOMATIC1111 87b50397a6 add missing import, simplify code, use patches module for #13276 2023-09-30 09:11:31 +03:00
AUTOMATIC1111 e309583f29 Merge pull request #13276 from woweenie/patch-1
patch DDPM.register_betas so that users can put given_betas in model yaml
2023-09-30 09:01:12 +03:00
AUTOMATIC1111 7ce1f3a142 Merge pull request #13281 from AUTOMATIC1111/Config-states-time-ISO-in-system-time-zone
Config states time ISO in system time zone
2023-09-30 08:59:28 +03:00
AUTOMATIC1111 db63cf7d24 Merge pull request #13282 from AUTOMATIC1111/XYZ-if-not-Include-Sub-Grids-do-not-save-Sub-Grid
XYZ if not include sub grids do not save sub grid
2023-09-30 08:58:07 +03:00
AUTOMATIC1111 cdafbcaad2 Merge pull request #13313 from chu8129/dev
use orderdict as lru cache:opt/bug
2023-09-30 08:55:54 +03:00
AUTOMATIC1111 34055f9d0c Merge pull request #13302 from Zolxys/patch-1
Fix: --sd_model in "Prompts from file or textbox" script is not working
2023-09-30 08:49:26 +03:00
AUTOMATIC1111 9b17416580 Merge pull request #13372 from ezt19/patch-1
Update dragdrop.js
2023-09-30 08:46:48 +03:00
AUTOMATIC1111 833b9b62b5 Merge pull request #13395 from AUTOMATIC1111/escape-names
Fix viewing/editing metadata when filename contains an apostrophe
2023-09-30 08:32:38 +03:00
AUTOMATIC1111 3b0be0f12f Merge pull request #13411 from AUTOMATIC1111/update-card-metadata
Update card on correct tab when editing metadata
2023-09-30 08:32:07 +03:00
AUTOMATIC1111 4083639c3c Merge pull request #13418 from akx/torchsde-bump
Bump to torchsde==0.2.6
2023-09-30 08:31:30 +03:00
AUTOMATIC1111 8a758383d2 Merge pull request #13412 from AUTOMATIC1111/data-sort-name-fix
Fix data-sort-name containing spaces
2023-09-30 08:24:37 +03:00
AUTOMATIC1111 ad3b8a1c41 alternative solution to #13434 2023-09-30 08:23:12 +03:00
AUTOMATIC1111 1b9ca01e4f Merge pull request #13253 from LeonZhao28/feature_skip_load_model_at_start
add --skip-load-model-at-start
2023-09-30 08:15:00 +03:00
Aarni Koskela 30f4f25b2e Bump to torchsde==0.2.6 2023-09-27 10:21:14 +03:00
missionfloyd a69daae012 Fix data-sort-name containing spaces 2023-09-26 22:02:52 -06:00
missionfloyd 99aa702015 Update card on correct tab 2023-09-26 21:08:55 -06:00
missionfloyd d00f6dca28 Escape item names 2023-09-25 22:08:24 -06:00
ezt19 fdecf813b6 Update dragdrop.js
Fixing a problem when u cannot put two images and they are going into two different places for images.
2023-09-23 20:41:28 +00:00
superhero-7 f8f4ff2bb8 support altdiffusion-m18 2023-09-23 17:55:19 +08:00
superhero-7 702a1e1cc7 support m18 2023-09-23 17:51:41 +08:00
王秋文/qwwang 8e355fbd75 fix 2023-09-18 16:45:42 +08:00
Zolxys 701feabf49 Fix: --sd_model in "Promts from file or textbox" script is not working
Fix for bug report #8079
2023-09-17 11:37:15 -05:00
w-e-w d2878a8b0b XYZ if not Include Sub Grids do not save Sub Grid 2023-09-16 09:54:14 +09:00
w-e-w 663fb87976 Config states time ISO in system time zone 2023-09-16 09:11:54 +09:00
woweenie d9d94141dc patch DDPM.register_betas so that users can put given_betas in model yaml 2023-09-15 18:59:44 +02:00
qiuwen.wang 813535d38b use dict[key]=model; did not update orderdict order, should use move to end 2023-09-15 18:23:23 +08:00
Won-Kyu Park afd0624587 xyz_grid: add prepare option to AxisOption 2023-09-15 17:30:36 +09:00
Leon ab3d3528a1 add --skip-load-model-at-start 2023-09-14 18:42:56 +08:00
Der Chien 0ad38a9b87 20230913 setup GIT_PYTHON_GIT_EXECUTABLE for GitPython 2023-09-13 20:20:01 +08:00
w-e-w cf1edc2b54 initialize state.time_start befroe state.job_count 2023-09-13 16:27:02 +09:00
w-e-w 5b761b49ad correct webpath when webui_dir is not work_dir 2023-09-13 16:05:55 +09:00
AUTOMATIC1111 102b6617da Merge pull request #13213 from AUTOMATIC1111/fix-add_option-overriding-config-with-default
Fix major issue add_option overriding config with default
2023-09-12 17:50:44 +03:00
w-e-w 93015964c7 fix add_option overriding config with default 2023-09-12 22:53:09 +09:00
w-e-w 6fb2194d9c fetch version info when webui_dir is not work_dir 2023-09-12 16:50:56 +09:00
w-e-w 74b80e7211 add comment 2023-09-12 09:29:07 +09:00
AUTOMATIC1111 59544321aa initial work on sd_unet for SDXL 2023-09-11 21:17:40 +03:00
w-e-w e785402b6a return nothing if not found 2023-09-11 19:37:55 +09:00
w-e-w c485a7d12e make InputAccordion work with ui-config 2023-09-11 13:47:44 +09:00
liubo0902 413123f08a Update localization.py 2023-09-11 09:22:27 +08:00
dongwenpu 7d4d871d46 fix: lora-bias-backup don't reset cache 2023-09-10 17:53:42 +08:00
zixaphir 26d0d87f5b Remove extra spaces 2023-09-09 17:26:46 -07:00
zixaphir d6478a60aa Remove extra network separator without regex 2023-09-09 17:22:10 -07:00
w-e-w ab57417175 prevent accessing non-existing keys 2023-09-09 22:35:50 +09:00
w-e-w f8042cb323 Ensure not override images with script enabled 2023-09-09 22:35:07 +09:00
w-e-w f5959c1c30 thread safe extra network using list 2023-09-09 17:05:50 +09:00
w-e-w 25de9a785c Revert "thread safe extra network list_items"
This reverts commit aab385d01b.
2023-09-09 16:56:19 +09:00
AUTOMATIC1111 924642331b Merge pull request #12846 from a666/deprecated-types
Fix some deprecated types
2023-09-09 10:31:56 +03:00
AUTOMATIC1111 c9c457eda8 stylistic changes for #13118 2023-09-09 10:27:16 +03:00
AUTOMATIC1111 73c2a03d49 Merge pull request #13118 from ljleb/fix-counter
Don't use multicond parser for negative prompt counter
2023-09-09 10:24:07 +03:00
AUTOMATIC1111 06af73bd1d linter 2023-09-09 10:23:53 +03:00
AUTOMATIC1111 9cebe308e9 return apply styles to main UI 2023-09-09 10:20:06 +03:00
AUTOMATIC1111 558808c748 Merge pull request #13119 from AUTOMATIC1111/enable_console_prompts-in-settings
enable console prompts in settings
2023-09-09 10:02:02 +03:00
w-e-w c68aabc852 lint 2023-09-09 15:59:22 +09:00
w-e-w 46ef185709 deprecate --enable-console-prompts
use --enable-console-prompts as the default value for shared.opts.enable_console_prompts
2023-09-09 15:53:10 +09:00
AUTOMATIC1111 46375f0592 fix for crash when running #12924 without --device-id 2023-09-09 09:39:37 +03:00
AUTOMATIC1111 558baffa2c Merge pull request #12924 from catboxanon/fix/cudnn
More accurate check for enabling cuDNN benchmark on 16XX cards
2023-09-09 09:33:37 +03:00
AUTOMATIC1111 4ebed495ed Merge pull request #12880 from AUTOMATIC1111/dropdown-padding-mobile
Use default dropdown padding on mobile
2023-09-09 09:29:42 +03:00
AUTOMATIC1111 e6d41b54cd Merge pull request #12976 from AUTOMATIC1111/toolbutton-tooltips
Restore missing tooltips
2023-09-09 09:29:11 +03:00
AUTOMATIC1111 e06c16e884 Merge pull request #12957 from AnyISalIn/dev
fix: update shared.opts.data when add_option
2023-09-09 09:28:33 +03:00
AUTOMATIC1111 72bc69e741 Merge pull request #12986 from AUTOMATIC1111/update-cmd-arg-description
update cmd arg description
2023-09-09 09:26:29 +03:00
AUTOMATIC1111 b33ffc11aa Merge pull request #12975 from AUTOMATIC1111/styles-copy-prompt
Add button to copy prompt to style editor
2023-09-09 09:26:03 +03:00
AUTOMATIC1111 0a2c24003c Merge pull request #12995 from uservar/patch-2
Fix bug with sigma min/max overrides.
2023-09-09 09:25:21 +03:00
AUTOMATIC1111 9e58e11ad4 Merge pull request #13028 from AUTOMATIC1111/fallback-invalid-exif
Add Fallback at images.read_info_from_image if exif data was invalid
2023-09-09 09:21:18 +03:00
AUTOMATIC1111 4c4d7dd01f fix whitespace for #13084 2023-09-09 09:15:09 +03:00
AUTOMATIC1111 adb3f2bcdd Merge pull request #13084 from AUTOMATIC1111/fix-preview-while-generation
Fix #13080 - Hypernetwork/TI preview generation
2023-09-09 09:14:01 +03:00
AUTOMATIC1111 8afabae67d Merge pull request #12929 from Beinsezii/dev
WEBUI.SH - Use torch 2.1.0 release candidate for Navi 3
2023-09-09 09:10:07 +03:00
AUTOMATIC1111 fccde0c1f7 Merge pull request #12909 from AUTOMATIC1111/Action-to-calculate-all-SD-checkpoint-hashes
Action to calculate all SD checkpoint hashes
2023-09-09 09:09:29 +03:00
AUTOMATIC1111 3ca4655a18 update for #12926 2023-09-09 09:08:31 +03:00
ljleb 349f893024 Merge branch 'dev' of https://github.com/AUTOMATIC1111/stable-diffusion-webui into fix-counter 2023-09-09 02:06:04 -04:00
ljleb 7b44b85730 refact 2023-09-09 02:01:12 -04:00
AUTOMATIC1111 329c8ab932 Merge pull request #12926 from AUTOMATIC1111/fix-batch-img2img-output-dir-with-script
fix batch img2img output dir with script
2023-09-09 08:56:32 +03:00
AUTOMATIC1111 259768f27f fix the bug in script-info API 2023-09-09 08:38:49 +03:00
AUTOMATIC1111 741e8ecb7d Merge pull request #13135 from ibrainventures/patch-2
(feat) Include Program Version in info response. Update processing.py
2023-09-09 08:18:51 +03:00
w-e-w 63485b2c55 option use short name for checkpoint dropdown 2023-09-08 10:00:27 +09:00
w-e-w e4726cccf9 parsing string to path 2023-09-08 09:46:34 +09:00
ibrainventures f11eec81e3 (feat) Include Program Version in info response. Update processing.py
This would help to organize / memorize the program version for the creation process. (as it is also unformated included inside the infotext).
2023-09-07 23:19:52 +02:00
w-e-w c3d51fc696 Update bug_report.yml 2023-09-07 19:35:55 +09:00
w-e-w 45881703c5 consolidated allowed preview formats 2023-09-07 12:11:36 +09:00
w-e-w 340fce2113 enable console prompts in settings 2023-09-07 10:01:16 +09:00
w-e-w 657404b75b use original filename batch img2img with scripts 2023-09-06 20:33:43 +09:00
w-e-w 35d1c94549 save_images_add_number_suffix 2023-09-06 20:24:26 +09:00
catboxanon 25189b29af Grammar fixes 2023-09-05 22:13:36 -04:00
AngelBottomless 47033afa5c Fix preview for textual inversion training 2023-09-05 22:38:02 +09:00
AngelBottomless de5bb4ca88 Fix #13080 - Hypernetwork/TI preview generation
Fixes sampler name reference

Same patch will be done for TI.
2023-09-05 22:35:17 +09:00
liubo0902 ff7027ffc0 Update localization.py 2023-09-05 15:08:59 +08:00
liubo0902 0c1c9e74cd Update localization.py 2023-09-05 15:06:47 +08:00
JaredTherriault 022639a145 Load comments from gif images to gather geninfo from gif outputs 2023-09-04 17:37:48 -07:00
JaredTherriault 5e16914a4e Merge branch 'AUTOMATIC1111:master' into master 2023-09-04 17:29:33 -07:00
JaredTherriault 8f3b02f095 Revert "Offloading custom work"
This reverts commit f3d1631aab.

This work has been offloaded now into an extension called Prompt Control.
2023-09-03 13:32:56 -07:00
AngelBottomless f593cbfec4 fallback if exif data was invalid 2023-09-03 21:07:36 +09:00
w-e-w aab385d01b thread safe extra network list_items 2023-09-03 11:56:02 +09:00
uservar a51721cb09 Fix bug with sigma min/max overrides. 2023-09-02 11:35:30 +00:00
w-e-w 061a4a295d Update bug_report.yml 2023-09-02 18:11:08 +09:00
w-e-w ba05e32789 update cmd arg description 2023-09-02 14:12:59 +09:00
missionfloyd 3e67017dfb Restore missing tooltips 2023-09-01 17:01:08 -06:00
missionfloyd d7e3ea68b3 Remove whitespace 2023-09-01 16:24:35 -06:00
missionfloyd bf0b083216 Add button to copy prompt to style editor 2023-09-01 16:14:33 -06:00
AnyISalIn 317d00b2a6 fix: update shared.opts.data when add_option
Signed-off-by: AnyISalIn <anyisalin@gmail.com>
2023-09-01 21:56:17 +08:00
Beinsezii 737a013377 WEBUI.SH Navi 3 torch 2.1.0 rc instead of nightly
With the release candidates being out for both torch and vision,
webui should default to these over nightly for a more stable experience.

Stable release isn't excpected until October 4th:
https://dev-discuss.pytorch.org/c/release-announcements/27
2023-08-31 15:03:08 -07:00
zixaphir 78c1a74660 Account for edge case where user deleted leading separator. 2023-08-31 14:18:35 -07:00
w-e-w bd9b3d15e8 fix batch img2img output dir with script 2023-09-01 04:05:58 +09:00
catboxanon 5681bf8016 More accurate check for enabling cuDNN benchmark on 16XX cards 2023-08-31 14:57:16 -04:00
w-e-w 348c6022f3 Action to calculate all SD checkpoint hashes 2023-09-01 00:56:55 +09:00
missionfloyd 76b1ad7daf Use default dropdown padding on mobile 2023-08-30 23:07:18 -06:00
AUTOMATIC1111 d39440bfb9 Merge branch 'master' into dev 2023-08-31 07:39:14 +03:00
AUTOMATIC1111 5ef669de08 Merge branch 'release_candidate' 2023-08-31 07:38:34 +03:00
AUTOMATIC1111 20158d77d9 Merge branch 'release_candidate' into dev 2023-08-31 07:37:36 +03:00
AUTOMATIC1111 e7965a5eb8 Merge pull request #12876 from ljleb/fix-re
Fix generation params regex
2023-08-31 07:34:01 +03:00
AUTOMATIC1111 3bff988f1e Merge pull request #12876 from ljleb/fix-re
Fix generation params regex
2023-08-31 07:30:03 +03:00
zixaphir 41196ccbf7 account for customizable extra network separators in remove code
previous behavior only searched for leading spaces
2023-08-30 20:20:19 -07:00
ljleb 541a3db05b fix generation params regex 2023-08-30 21:38:21 -04:00
AUTOMATIC1111 ae7291fb49 fix an issue where using hires fix with refiner on first pass with medvram would cause an exception when generating 2023-08-30 21:34:17 +03:00
AUTOMATIC1111 d43333ff71 fix an issue where VAE would remain in fp16 after an auto-switch to fp32 2023-08-30 21:13:24 +03:00
AUTOMATIC1111 0cdbd90d6b update bug report template to include sysinfo and not include all other fields that are already covered by sysinfo 2023-08-30 19:50:47 +03:00
AUTOMATIC1111 d0026da483 add --dump-sysinfo, a cmd arg to dump limited sysinfo file at startup 2023-08-30 19:48:47 +03:00
AUTOMATIC1111 8d54739de5 add information about Restore faces and Tiling into the changelog 2023-08-30 19:17:27 +03:00
AUTOMATIC1111 135b61bc0b fix inpainting models in txt2img creating black pictures 2023-08-30 19:08:17 +03:00
AUTOMATIC1111 6adf2b71c2 fix inpainting models in txt2img creating black pictures 2023-08-30 19:08:04 +03:00
AUTOMATIC1111 87cca029d7 add an option to choose how to combine hires fix and refiner 2023-08-30 18:24:21 +03:00
AUTOMATIC1111 ae0b2cc196 add an option to choose how to combine hires fix and refiner 2023-08-30 18:22:50 +03:00
AUTOMATIC1111 1ac11b3dae Merge pull request #12865 from AUTOMATIC1111/another-convert-to-system-time-zone
extension update time, convert to system time zone
2023-08-30 11:00:38 +03:00
AUTOMATIC1111 0ff8b8fb54 Merge pull request #12865 from AUTOMATIC1111/another-convert-to-system-time-zone
extension update time, convert to system time zone
2023-08-30 11:00:29 +03:00
w-e-w c985d23c52 extension update time, convert to system time zone 2023-08-30 16:18:31 +09:00
AUTOMATIC1111 87a083d1b2 Merge pull request #12864 from AUTOMATIC1111/extension-time-format-time-zone
patch Extension time format in systme time zone
2023-08-30 09:45:23 +03:00
AUTOMATIC1111 644b537014 Merge pull request #12864 from AUTOMATIC1111/extension-time-format-time-zone
patch Extension time format in systme time zone
2023-08-30 09:45:12 +03:00
w-e-w 67cd4ec0aa lint 2023-08-30 15:37:13 +09:00
w-e-w 28b084ca25 extension time format in system time zone 2023-08-30 15:28:46 +09:00
AUTOMATIC1111 503bd3fc0f keep order in list of checkpoints when loading model that doesn't have a checksum 2023-08-30 08:54:41 +03:00
AUTOMATIC1111 f874b1bcad keep order in list of checkpoints when loading model that doesn't have a checksum 2023-08-30 08:54:31 +03:00
AUTOMATIC1111 9e7de49fc5 update changelog 2023-08-30 08:28:46 +03:00
AUTOMATIC1111 06bc1f4f67 Merge pull request #12851 from bluelovers/pr/extension-time-001
chore: change extension time format
2023-08-30 08:24:08 +03:00
AUTOMATIC1111 338d0b6103 go back to single path for filenames in extra networks metadata dialog 2023-08-30 08:23:59 +03:00
AUTOMATIC1111 3989d7e88b Merge pull request #12838 from bluelovers/pr/file-metadata-path-001
display file metadata `path` , `ss_output_name`
2023-08-30 08:23:50 +03:00
AUTOMATIC1111 afea99a72b get progressbar to display correctly in extensions tab 2023-08-30 08:23:47 +03:00
AUTOMATIC1111 965c728914 Merge pull request #12839 from ibrainventures/patch-1
[RC 1.6.0 - zoom is partly hidden] Update style.css
2023-08-30 08:23:44 +03:00
AUTOMATIC1111 46f3ee9594 Merge pull request #12854 from catboxanon/fix/quicksettings-dropdown-unfocus
Do not change quicksettings dropdown option when value returned is `None`
2023-08-30 08:23:42 +03:00
AUTOMATIC1111 323dcadea2 Merge pull request #12855 from dhwz/dev
don't print empty lines
2023-08-30 08:23:40 +03:00
AUTOMATIC1111 642faa1f65 Merge pull request #12856 from catboxanon/extra-noise-noisy-latent
Add noisy latent to `ExtraNoiseParams` for callback
2023-08-30 08:23:37 +03:00
AUTOMATIC1111 d156d5bffd Merge pull request #12851 from bluelovers/pr/extension-time-001
chore: change extension time format
2023-08-30 08:23:11 +03:00
AUTOMATIC1111 edf3ad5aed go back to single path for filenames in extra networks metadata dialog 2023-08-30 08:22:06 +03:00
AUTOMATIC1111 4aaae3dc65 Merge pull request #12838 from bluelovers/pr/file-metadata-path-001
display file metadata `path` , `ss_output_name`
2023-08-30 08:07:15 +03:00
AUTOMATIC1111 9a4a1aac81 get progressbar to display correctly in extensions tab 2023-08-30 08:05:18 +03:00
AUTOMATIC1111 ee373a737c Merge pull request #12839 from ibrainventures/patch-1
[RC 1.6.0 - zoom is partly hidden] Update style.css
2023-08-30 07:43:38 +03:00
AUTOMATIC1111 9e248fb24e Merge pull request #12854 from catboxanon/fix/quicksettings-dropdown-unfocus
Do not change quicksettings dropdown option when value returned is `None`
2023-08-30 07:41:46 +03:00
AUTOMATIC1111 08603378e8 Merge pull request #12855 from dhwz/dev
don't print empty lines
2023-08-30 07:27:45 +03:00
AUTOMATIC1111 834f4c7cd3 Merge pull request #12856 from catboxanon/extra-noise-noisy-latent
Add noisy latent to `ExtraNoiseParams` for callback
2023-08-30 07:27:13 +03:00
catboxanon 549b475be9 Add noisy latent to ExtraNoiseParams for callback 2023-08-29 14:22:04 -04:00
dhwz 7e5fcdaf69 don't print empty lines 2023-08-29 18:49:42 +02:00
catboxanon e3939f3339 Do not change quicksettings value when value returned is None 2023-08-29 12:19:10 -04:00
bluelovers cb2a4f2424 chore: change extension time format 2023-08-29 22:47:10 +08:00
bluelovers f564d8ed2c refactor: refactor function 2023-08-29 22:11:18 +08:00
ibrainventures ba7d0d225a Update style.css 2023-08-29 15:31:01 +02:00
AUTOMATIC1111 04b90328c0 revert SGM noise multiplier change for img2img because it breaks hires fix 2023-08-29 15:38:33 +03:00
AUTOMATIC1111 a0af2852b6 revert SGM noise multiplier change for img2img because it breaks hires fix 2023-08-29 15:38:05 +03:00
a666 b6c1a1bbbf Fix some deprecated types 2023-08-29 00:54:57 -06:00
AUTOMATIC1111 00e393ce10 Merge pull request #12833 from catboxanon/fix/dont-print-blank-stdout
Don't print blank stdout in extension installers
2023-08-29 09:02:11 +03:00
AUTOMATIC1111 84d41e49b3 Merge pull request #12833 from catboxanon/fix/dont-print-blank-stdout
Don't print blank stdout in extension installers
2023-08-29 09:00:34 +03:00
AUTOMATIC1111 0c9282b84d Merge pull request #12832 from catboxanon/fix/skip-install-extensions
Honor `--skip-install` for extension installers
2023-08-29 08:58:10 +03:00
AUTOMATIC1111 18ba89863d Merge pull request #12832 from catboxanon/fix/skip-install-extensions
Honor `--skip-install` for extension installers
2023-08-29 08:58:01 +03:00
AUTOMATIC1111 444f102964 Merge pull request #12834 from catboxanon/fix/notification-tab-switch
Fix notification not playing when built-in webui tab is inactive
2023-08-29 08:55:58 +03:00
AUTOMATIC1111 9e8464db1e Merge pull request #12834 from catboxanon/fix/notification-tab-switch
Fix notification not playing when built-in webui tab is inactive
2023-08-29 08:55:45 +03:00
AUTOMATIC1111 738e133b24 Merge pull request #12818 from catboxanon/sgm
Add option to align with sgm repo's sampling implementation
2023-08-29 08:54:32 +03:00
AUTOMATIC1111 01a257eb07 Merge pull request #12818 from catboxanon/sgm
Add option to align with sgm repo's sampling implementation
2023-08-29 08:54:09 +03:00
AUTOMATIC1111 6558716018 Merge pull request #12837 from bluelovers/pr/file-metadata-break-001
style: file-metadata word-break
2023-08-29 08:53:37 +03:00
AUTOMATIC1111 9c87ae0d9d Merge pull request #12837 from bluelovers/pr/file-metadata-break-001
style: file-metadata word-break
2023-08-29 08:52:58 +03:00
catboxanon 7ab16e99ee Add option to align with sgm repo sampling implementation 2023-08-29 01:51:13 -04:00
AUTOMATIC1111 8a7a4275a8 Merge pull request #12842 from dhwz/dev
remove xformers Python version check
2023-08-29 08:44:11 +03:00
AUTOMATIC1111 3269572753 Merge pull request #12842 from dhwz/dev
remove xformers Python version check
2023-08-29 08:32:48 +03:00
dhwz 5070ab8004 remove xformers Python version check 2023-08-29 07:16:32 +02:00
ibrainventures 02e7824e6a [RC 1.6.1 - zoom is partly hidden] Update style.css
If a image / batch result image is higher or wider than the current viewport, and is zoomed (left corner zoom icon) it is cutted off  on the top and also to the left. This new rule seems to be the culprit.
2023-08-29 02:04:07 +02:00
bluelovers d83a1ba65b feat: display file metadata ss_output_name
https://github.com/AUTOMATIC1111/stable-diffusion-webui/issues/12289
2023-08-29 06:33:00 +08:00
bluelovers 1bb21f3510 feat: display file metadata path
https://github.com/AUTOMATIC1111/stable-diffusion-webui/issues/12289
2023-08-29 06:25:16 +08:00
bluelovers 739686b1c5 style: file-metadata word-break 2023-08-29 06:19:22 +08:00
AUTOMATIC1111 c0f9821c35 always show NV as RNG source in infotext 2023-08-28 22:23:29 +03:00
AUTOMATIC1111 cd48308a2a always show NV as RNG source in infotext 2023-08-28 22:22:35 +03:00
catboxanon 592b0dcfa7 Fix notification not playing when built-in webui tab is inactive 2023-08-28 12:09:37 -04:00
catboxanon 20df81b0cc Honor --skip-install for extension installers 2023-08-28 11:26:50 -04:00
catboxanon 99acbd5ebe Don't print blank stdout in extension installers 2023-08-28 11:17:47 -04:00
AUTOMATIC1111 d1c93c3822 Merge pull request #12827 from omahs/patch-1
Fix minor typos
2023-08-28 15:04:07 +03:00
AUTOMATIC1111 9e14cac318 Merge branch 'dev' into patch-1 2023-08-28 15:03:46 +03:00
omahs f898833ea3 fix typos 2023-08-28 10:43:13 +02:00
JaredTherriault f3d1631aab Offloading custom work
-custom_statics works to do mass replace strings, intended for copy-pasting gen info from internet generations and replacing unsavory prompts with safer prompts for my own sanity
-tried to implement this into generation_parameters_copypaste but it didn't work out this iteration, presumably because we return a string and the calling method is looking for an object type
-updated webui-user.bat to set a custom temp directory (for disk space concerns) and to apply xformers (for generation speed)

I probably won't be merging any of this work into the main repo since I don't want to mess with anyone else's prompts, this is just intended to keep my workspace safe from anything I don't want to see. Eventually this should be done in an extension which I could then publish, but I need to learn a lot more about the extension and callback systems in the main repo first. just uploading this to my fork for now so i don't lose the current progress.
2023-08-27 21:54:05 -07:00
AUTOMATIC1111 8632452627 Merge pull request #12815 from AUTOMATIC1111/consolidate-local-check
consolidate local check
2023-08-28 07:53:37 +03:00
AUTOMATIC1111 86708463f1 Merge pull request #12819 from catboxanon/fix/rng-infotext
Add missing infotext for RNG in options
2023-08-28 07:20:48 +03:00
AUTOMATIC1111 66146ed72b Merge pull request #12819 from catboxanon/fix/rng-infotext
Add missing infotext for RNG in options
2023-08-28 07:20:33 +03:00
catboxanon 2b8484a29d Add missing infotext for RNG 2023-08-27 16:25:26 -04:00
w-e-w 18e3e6d6ab consolidate local check 2023-08-28 03:43:27 +09:00
AUTOMATIC1111 bfc5c08109 Merge pull request #12814 from AUTOMATIC1111/non-local-condition
non-local condition
2023-08-27 21:29:59 +03:00
AUTOMATIC1111 ad266d795e Merge pull request #12814 from AUTOMATIC1111/non-local-condition
non-local condition
2023-08-27 21:29:48 +03:00
w-e-w e422f19ee9 non-local condition 2023-08-28 03:27:07 +09:00
AUTOMATIC1111 d0d5075914 update changelog 2023-08-27 20:24:25 +03:00
AUTOMATIC1111 896fde789e hide --gradio-auth and --api-auth values from /internal/sysinfo report 2023-08-27 20:17:01 +03:00
AUTOMATIC1111 d63117ace5 hide --gradio-auth and --api-auth values from /internal/sysinfo report 2023-08-27 20:16:50 +03:00
AUTOMATIC1111 66d7630705 lint 2023-08-27 10:11:22 +03:00
AUTOMATIC1111 63d3150dc4 lint 2023-08-27 10:11:14 +03:00
AUTOMATIC1111 cb81087b59 update changelog 2023-08-27 09:45:12 +03:00
AUTOMATIC1111 6139b145f0 fix style editing dialog breaking if it's opened in both img2img and txt2img tabs 2023-08-27 09:45:08 +03:00
AUTOMATIC1111 f331821b27 Merge pull request #12780 from catboxanon/xyz-hide-samplers
Don't show hidden samplers in dropdown for XYZ script
2023-08-27 09:45:06 +03:00
AUTOMATIC1111 5359dc0a10 Merge pull request #12792 from catboxanon/image-cropper-hide
Hide broken image crop tool
2023-08-27 09:45:03 +03:00
AUTOMATIC1111 7989765faa Merge pull request #12797 from Madrawn/vae_resolve_bug
Small typo: vae resolve bug
2023-08-27 09:45:00 +03:00
AUTOMATIC1111 783a5754d5 Merge pull request #12795 from catboxanon/prevent-duplicate-resize-handler-mk2
Prevent duplicate resize handler
2023-08-27 09:44:56 +03:00
AUTOMATIC1111 897312de46 update changelog 2023-08-27 09:44:13 +03:00
AUTOMATIC1111 23c6b5f124 fix style editing dialog breaking if it's opened in both img2img and txt2img tabs 2023-08-27 09:39:49 +03:00
AUTOMATIC1111 c2463b5323 Merge pull request #12780 from catboxanon/xyz-hide-samplers
Don't show hidden samplers in dropdown for XYZ script
2023-08-27 09:28:12 +03:00
AUTOMATIC1111 ed2a05fc3f Merge pull request #12792 from catboxanon/image-cropper-hide
Hide broken image crop tool
2023-08-27 09:26:50 +03:00
AUTOMATIC1111 e3174a1a42 Merge pull request #12797 from Madrawn/vae_resolve_bug
Small typo: vae resolve bug
2023-08-27 09:26:18 +03:00
AUTOMATIC1111 07878c6ca8 Merge pull request #12795 from catboxanon/prevent-duplicate-resize-handler-mk2
Prevent duplicate resize handler
2023-08-27 09:24:42 +03:00
AUTOMATIC1111 5e30f737b0 fix for Reload UI function: if you reload UI on one tab, other opened tabs will no longer stop working 2023-08-27 09:19:13 +03:00
AUTOMATIC1111 bd5c16e8da fix for Reload UI function: if you reload UI on one tab, other opened tabs will no longer stop working 2023-08-27 09:19:02 +03:00
AUTOMATIC1111 f2c55523c0 update changelog 2023-08-27 09:17:51 +03:00
AUTOMATIC1111 cb5f0823c6 update gradio to 3.41.2 2023-08-27 08:45:40 +03:00
AUTOMATIC1111 9dd0c4add5 update changelog 2023-08-27 08:45:25 +03:00
AUTOMATIC1111 1b46863f24 update gradio to 3.41.2 2023-08-27 08:45:16 +03:00
AUTOMATIC1111 3d83683a28 fix error that causes some extra networks to be disabled if both <lora:> and <lyco:> are present in the prompt 2023-08-27 08:41:48 +03:00
AUTOMATIC1111 b7f0e81562 fix error that causes some extra networks to be disabled if both <lora:> and <lyco:> are present in the prompt 2023-08-27 08:41:26 +03:00
catboxanon 9d8d279d0d Prevent duplicate resize handler 2023-08-26 17:30:09 -04:00
Daniel Dengler d888490f85 Merge remote-tracking branch 'origin/dev' into vae_resolve_bug 2023-08-26 23:23:11 +02:00
Daniel Dengler 168eac319d is_automatic is missing () for call 2023-08-26 23:22:57 +02:00
catboxanon 73f69a7453 Fix CSS whitespace 2023-08-26 07:04:11 -04:00
catboxanon ec54257cb2 Hide broken image crop tool for now 2023-08-26 07:00:09 -04:00
AUTOMATIC1111 72ee347eab update pnginfo checkpoint to return dict with parsed values 2023-08-26 06:52:18 +03:00
AUTOMATIC1111 ac1abf3de6 fix defaults settings page breaking when any of main UI tabs are hidden 2023-08-26 06:34:23 +03:00
AUTOMATIC1111 bb90b0ff42 fix defaults settings page breaking when any of main UI tabs are hidden 2023-08-26 06:34:00 +03:00
catboxanon db56bdce33 Don't show hidden samplers in dropdown for XYZ script 2023-08-25 16:04:06 -04:00
AUTOMATIC1111 f3a1027869 Merge pull request #12774 from SpenserCai/extensions_api
support installed extensions list api
2023-08-25 19:03:12 +03:00
SpenserCai dd07b5193e fix format error 2023-08-25 22:23:17 +08:00
SpenserCai 3369fb27df support installed extensions list api 2023-08-25 22:15:35 +08:00
AUTOMATIC1111 4c6788644a Merge branch 'release_candidate' into dev 2023-08-25 16:24:45 +03:00
AUTOMATIC1111 a6cedafb27 Merge pull request #12767 from AUTOMATIC1111/img2img-batch-PNG_info-model_hash
img2img batch PNG info model hash
2023-08-25 11:41:31 +03:00
AUTOMATIC1111 e004384e46 Merge branch 'dev' into release_candidate 2023-08-25 11:40:49 +03:00
AUTOMATIC1111 e835e61f3a Merge pull request #12754 from daswer123/improve_integration
Zoom and Pan: Resize handler
2023-08-25 11:40:13 +03:00
w-e-w 4130e5db3d img2img batch PNG info model hash 2023-08-25 10:12:19 +09:00
AUTOMATIC1111 c8c73eae59 fix incorrect save/display of new values in Defaults page in settings 2023-08-24 22:03:24 +03:00
Danil Boldyrev c39efa6ba6 Zoom and Pan: Resize handler 2023-08-24 17:30:35 +03:00
AUTOMATIC1111 935d9d899c update info about gradio in changelog file 2023-08-24 11:16:29 +03:00
AUTOMATIC1111 189229bbf9 Merge branch 'dev' into release_candidate 2023-08-24 11:09:04 +03:00
AUTOMATIC1111 b6c0217405 update changelog 2023-08-24 11:06:23 +03:00
AUTOMATIC1111 995ff5902f add infotext for use_old_scheduling option 2023-08-24 10:07:54 +03:00
AUTOMATIC1111 b0211ff7f8 bump gradio version 2023-08-24 09:41:30 +03:00
AUTOMATIC1111 0027ce1f6e Merge pull request #12457 from rubberbaron/shared-hires-prompt-test
prompt editing timeline has separate range for first pass and hires-fix pass
2023-08-24 09:41:16 +03:00
AUTOMATIC1111 06f18186dc Merge pull request #12745 from AUTOMATIC1111/draw-extra-network-buttons-above-description
draw extra network buttons above description
2023-08-24 09:37:17 +03:00
AUTOMATIC1111 2c570f641c Merge pull request #12749 from daswer123/improve_integration
Zoom and pan: Improve integration
2023-08-24 09:36:53 +03:00
Danil Boldyrev fa68d66c98 remove console.log 2023-08-24 01:42:37 +03:00
Danil Boldyrev 32e790a47e Fixing and improving integration 2023-08-24 01:40:06 +03:00
w-e-w ddf3d1a7ac draw extra network buttons above description 2023-08-24 00:34:28 +09:00
AUTOMATIC1111 c9c8485bc1 Merge branch 'release_candidate' 2023-08-23 15:48:09 +03:00
AUTOMATIC1111 31f2be3dce update changelog 2023-08-23 15:47:11 +03:00
AUTOMATIC1111 250c416474 update doggettx cross attention optimization to not use an unreasonable amount of memory in some edge cases -- suggestion by MorkTheOrk 2023-08-23 15:44:38 +03:00
AUTOMATIC1111 12171ca961 fix memory leak when generation fails 2023-08-23 15:40:31 +03:00
AUTOMATIC1111 bae91855f5 Merge pull request #12737 from yajunzhng/master
tell RealESRGANer which device to run on, could be cuda, M1, or other…
2023-08-23 12:30:17 +03:00
yajun f29b4cd7cb tell RealESRGANer which device to run on, could be cuda, M1, or other GPU 2023-08-23 14:31:38 +08:00
AUTOMATIC1111 0232a987bb set devices.dtype_unet correctly 2023-08-23 07:10:43 +03:00
Danil Boldyrev 6a87e35bef lint 2023-08-23 03:35:09 +03:00
Danil Boldyrev 8fd1558179 Removed the old code 2023-08-23 03:21:28 +03:00
AUTOMATIC1111 04cfcf91d9 fix endless progress requests 2023-08-22 21:05:25 +03:00
AUTOMATIC1111 3ec5ce9416 add type annotations for extra fields of shared.sd_model 2023-08-22 19:05:03 +03:00
AUTOMATIC1111 016554e437 add --medvram-sdxl 2023-08-22 18:49:08 +03:00
AUTOMATIC1111 bb7dd7b646 use an atomic operation to replace the cache with the new version 2023-08-22 17:45:47 +03:00
AUTOMATIC1111 9c82b34be7 Merge pull request #12727 from daswer123/improve_integration
Zoom and pan: Improved integration
2023-08-22 17:19:15 +03:00
Danil Boldyrev 54fbdcf467 Improve integration, fix for new gradio 2023-08-22 16:43:23 +03:00
AUTOMATIC1111 2e9289bcbf Merge pull request #12722 from ravi9/intel-readme
Update README.md with install instructions on Intel CPUs, GPUs
2023-08-22 15:26:23 +03:00
AUTOMATIC1111 7fd0ccdffc Merge pull request #12723 from MMP0/dev-resize-handle-fix
Resize handle improvements and bug fixes
2023-08-22 15:25:28 +03:00
MMP0 ed49c7c246 Fix double click event not firing 2023-08-22 21:21:06 +09:00
AUTOMATIC1111 0d90064e9e eslint 2023-08-22 13:57:05 +03:00
AUTOMATIC1111 9158d0fd12 fix broken generate button if not using live previews 2023-08-22 13:54:45 +03:00
MMP0 c4b11ec54e Replace tabs with spaces 2023-08-22 18:48:17 +09:00
AUTOMATIC1111 9e4019c5ff make it possible to localize tooltips and placeholders 2023-08-22 12:00:29 +03:00
MMP0 96edfb560b Limit mouse detection to primary button only 2023-08-22 17:19:26 +09:00
AUTOMATIC1111 f6c52f4f41 for live previews, only hide gallery after at least one live previews pic has been received
fix blinking for live previews
fix a clientside live previews exception that happens when you kill serverside during sampling
match the size of live preview image to gallery image
2023-08-22 11:02:14 +03:00
Ravi Panchumarthy 7d94e5f33b Update README.md with Intel install instructions 2023-08-22 00:54:01 -07:00
AUTOMATIC1111 e8a9d213e4 dump current stack traces when exiting with SIGINT 2023-08-22 10:49:52 +03:00
MMP0 0998256fc5 Prevent text selection and cursor changes 2023-08-22 16:45:34 +09:00
AUTOMATIC1111 a459075d26 actual solution to the uncommon hanging problem that is seemingly caused by multiple progress requests working on same tensor 2023-08-22 10:41:10 +03:00
MMP0 70283a9f4a Expand the hit area of resize handle 2023-08-22 16:40:50 +09:00
MMP0 e1b37a066d Fix resize handle overflowing in Safari 2023-08-22 16:35:49 +09:00
AUTOMATIC1111 d7c9c61420 attemped solution to the uncommon hanging problem that is seemingly caused by live previews working on the tensor as denoising 2023-08-22 09:55:20 +03:00
AUTOMATIC1111 79fd17ee63 remove unneeded example_inputs from gradio config 2023-08-22 08:18:01 +03:00
AUTOMATIC1111 7a3a6e3855 Merge pull request #12713 from AUTOMATIC1111/XYZ-RNG
add RNG source to XYZ
2023-08-22 07:31:26 +03:00
AUTOMATIC1111 f83996cd9f Merge pull request #12714 from catboxanon/resize-handle-reset
Reset columns on resize handle double click
2023-08-22 07:30:52 +03:00
AUTOMATIC1111 7da73cbcca Merge pull request #12717 from brkirch/make-temp-directory
Create Gradio temp directory if necessary
2023-08-22 07:30:25 +03:00
brkirch 299b8096bc Make Gradio temp directory if it doesn't exist
Gradio normally creates the temp directory in `pil_to_temp_file()` (https://github.com/gradio-app/gradio/blob/861d752a83da0f95e9f79173069b69eababeed39/gradio/components/base.py#L313) but since the Gradio implementation of `pil_to_temp_file()` is replaced with `save_pil_to_file()`, the Gradio temp directory should also be created by `save_pil_to_file()` when necessary.
2023-08-21 17:36:17 -04:00
catboxanon aed52d1632 Reset columns on resize handle dblclick 2023-08-21 12:40:27 -04:00
w-e-w 9dce2aa735 add RNG source to XYZ 2023-08-21 23:08:47 +09:00
AUTOMATIC1111 953c3eab7b forbid Full live preview method for medvram and add a setting to undo the forbidding 2023-08-21 15:54:30 +03:00
AUTOMATIC1111 18fb522660 citation mk2 2023-08-21 15:27:04 +03:00
AUTOMATIC1111 bd6f070882 add citation 2023-08-21 15:22:47 +03:00
AUTOMATIC1111 a3fdef4ed4 Merge pull request #12707 from AnyISalIn/dev
feat: replace threading.Lock() to FIFOLock
2023-08-21 15:09:26 +03:00
AUTOMATIC1111 dfd6ea3fca ditch --always-batch-cond-uncond in favor of an UI setting 2023-08-21 15:07:10 +03:00
AnyISalIn 71a0f6ef85 feat: replace threading.Lock() to FIFOLock
Signed-off-by: AnyISalIn <anyisalin@gmail.com>
2023-08-21 17:49:58 +08:00
AUTOMATIC1111 d02c4da483 also prevent changing API options via override_settings 2023-08-21 08:58:15 +03:00
AUTOMATIC1111 df595ae313 make resize handle available to extensions 2023-08-21 08:48:46 +03:00
AUTOMATIC1111 b4d21e7113 prevent API options from being changed via API 2023-08-21 08:48:45 +03:00
AUTOMATIC1111 d722d6de36 Merge pull request #12667 from AUTOMATIC1111/switch-to-PNG-when-images-too-large
switch to PNG when images too large
2023-08-21 07:50:50 +03:00
AUTOMATIC1111 76ae1019b9 add settings for http/https URLs in source images in api 2023-08-21 07:38:07 +03:00
AUTOMATIC1111 a7f18b2297 Merge pull request #12698 from Akegarasu/fix-ssrf-in-api
fix potential ssrf attack in #12663
2023-08-21 07:19:48 +03:00
AUTOMATIC1111 d3632368e6 Merge pull request #12704 from fraz0815/master
Update torch for Navi 31 (7900 XT/XTX)
2023-08-21 07:11:17 +03:00
AUTOMATIC1111 5a3fe7a8d1 Merge pull request #12685 from Uminosachi/fix-vae-mismatch
Fix SD VAE switch error after model reuse
2023-08-21 07:10:19 +03:00
Uminosachi be301f224d Fix for consistency with shared.opts.sd_vae of UI 2023-08-21 11:28:53 +09:00
fraz0815 db6c7ff084 Update torch for Navi 31 (7900 XT/XTX)
Navi 3 needs at least 5.5 which is only on the nightly chain, previous versions are no longer online (torch==2.1.0.dev-20230614+rocm5.5 torchvision==0.16.0.dev-20230614+rocm5.5 torchaudio==2.1.0.dev-20230614+rocm5.5).
so switch to nightly rocm5.6 without explicit versions this time
2023-08-20 22:59:30 +02:00
akiba 268dc9b308 fix potential ssrf attack in #12663 2023-08-20 23:17:50 +08:00
Uminosachi 549b0fc526 Change where VAE state are stored in model 2023-08-20 23:06:51 +09:00
AUTOMATIC1111 42b72fe246 fix for small images in live previews not being scaled up 2023-08-20 14:57:48 +03:00
AUTOMATIC1111 f65d0dc081 Merge pull request #12689 from AUTOMATIC1111/patch-config-status
Patch config status handle corrupted files
2023-08-20 14:20:27 +03:00
Uminosachi af5d2e8e5f Change to access sd_model attribute with dot 2023-08-20 20:08:22 +09:00
Uminosachi 5159edbf0e Store base_vae and loaded_vae_file in sd_model 2023-08-20 19:44:37 +09:00
AUTOMATIC1111 4a2bf65fea make mobile built-in extension actually do something 2023-08-20 13:40:11 +03:00
AUTOMATIC1111 db5c304e29 make live previews play nice with window/slider resizes 2023-08-20 13:38:35 +03:00
AUTOMATIC1111 a0d721e109 make live preview display work independently from progress bar 2023-08-20 13:00:59 +03:00
w-e-w 2c10fda399 make it obvious that a config_status is corrupted
also format HTML removing unnecessary text blocks
2023-08-20 18:48:23 +09:00
w-e-w 7ca20adc6d no need to use OrderedDict 2023-08-20 18:48:23 +09:00
w-e-w e0e64bcdf6 assert key created_at exist in config_states 2023-08-20 18:48:23 +09:00
AUTOMATIC1111 499cef3c2b Merge pull request #12684 from AUTOMATIC1111/fix-xyz-swap-axes
fix xyz swap axes
2023-08-20 12:46:34 +03:00
AUTOMATIC1111 2571767204 Merge pull request #12687 from catboxanon/resize-handle
Add resize-handle (built-in extension)
2023-08-20 12:42:12 +03:00
w-e-w 36ecff71ae catch error when loading config_states
and save config_states with indent
2023-08-20 15:36:39 +09:00
catboxanon a3c8510c05 Add resize-handler extension 2023-08-20 02:31:32 -04:00
Uminosachi 042e1d5d0b Fix SD VAE switch error after model reuse 2023-08-20 15:00:14 +09:00
w-e-w ae17c775dc fix xyz swap axes
make csv_string_to_list_strip function
2023-08-20 14:29:26 +09:00
w-e-w 8ce613bb3a switch to PNG when images too large 2023-08-19 16:50:43 +09:00
AUTOMATIC1111 9d2299ed0b implement undo hijack for SDXL 2023-08-19 10:16:27 +03:00
AUTOMATIC1111 35db3665b3 possible fix for dictionary changed size during iteration 2023-08-19 08:39:48 +03:00
AUTOMATIC1111 5a5913828c Merge pull request #12616 from catboxanon/extra-noise-callback
Add extra noise callback
2023-08-19 08:36:44 +03:00
AUTOMATIC1111 448d6bef37 Merge pull request #12599 from AUTOMATIC1111/ram_optim
RAM optimization round 2
2023-08-19 08:36:20 +03:00
AUTOMATIC1111 7056fdf2be Merge pull request #12630 from catboxanon/fix/nans-mk2
Attempt to resolve NaN issue with unstable VAEs in fp32 mk2
2023-08-19 08:34:46 +03:00
AUTOMATIC1111 3d81fd714b Merge pull request #12633 from catboxanon/fix/img2img-bg-color
Fix img2img background color for transparent images option not being used
2023-08-19 08:33:22 +03:00
AUTOMATIC1111 58a9082411 Merge pull request #12635 from catboxanon/fix/full-page-img
Make image viewer actually fit the whole page
2023-08-19 08:32:45 +03:00
AUTOMATIC1111 99a64edea8 do not assign to vae_dict 2023-08-19 08:31:06 +03:00
AUTOMATIC1111 d75b521af8 Merge pull request #12638 from Cschlaefli/fix-api-vae-model-refresh
fix issues with api model-refresh and vae-refresh
2023-08-19 08:28:47 +03:00
AUTOMATIC1111 296c8f6a4a Merge pull request #12639 from AUTOMATIC1111/more-hash
More hash filename patterns
2023-08-19 08:28:00 +03:00
AUTOMATIC1111 99cd8de234 Merge pull request #12645 from catboxanon/css/sticky-column
Make results column sticky
2023-08-19 08:27:28 +03:00
AUTOMATIC1111 5590be7a8c Merge pull request #12644 from AUTOMATIC1111/fix-model-override-logic
fix model override logic
2023-08-19 08:26:39 +03:00
AUTOMATIC1111 f084e6bbd0 revert xformers back to 0.0.20 2023-08-19 08:22:12 +03:00
AUTOMATIC1111 cd719b08bd Merge pull request #12663 from SpenserCai/get_image_from_url
api support get image from url
2023-08-19 08:08:19 +03:00
AUTOMATIC1111 90e560bb75 Merge pull request #12648 from catboxanon/feat/gallery-tweaks
Gallery: Set preview to `True`, allow custom height
2023-08-19 08:06:13 +03:00
AUTOMATIC1111 9182dd7e5d Merge pull request #12634 from catboxanon/feat/live-preview-fast-interrupt
Improve interrupt speed
2023-08-19 08:05:36 +03:00
AUTOMATIC1111 f739e3e05d second appearance 2023-08-19 08:04:48 +03:00
AUTOMATIC1111 e7a044a2d1 Merge pull request #12653 from S-Del/fix/typo
fix typo `txt2txt` -> `txt2img`
2023-08-19 08:03:40 +03:00
AUTOMATIC1111 ca72db23d2 Merge pull request #12660 from dansgithubuser/fork
Get python print statements to show up in docker logs
2023-08-19 08:03:19 +03:00
AUTOMATIC1111 e4a2a705ad Merge pull request #12661 from XDOneDude/master
update xformers to 0.0.21 and some fixes
2023-08-19 08:02:18 +03:00
AUTOMATIC1111 bb91bb5e83 Merge pull request #12662 from bluelovers/bluelovers-patch-1-1
refactor: Update ui.js
2023-08-19 08:01:05 +03:00
SpenserCai 4760c3c0b5 api support get image from url 2023-08-19 12:19:21 +08:00
bluelovers 1631e96a98 refactor: Update ui.js 2023-08-19 10:38:43 +08:00
XDOneDude 61c1261e4e more grammar fixes 2023-08-18 21:56:15 -04:00
XDOneDude 956e1d8d90 xformers update 2023-08-18 21:25:59 -04:00
Dan 453a5ac1d0 run python unbuffered so output shows up in docker logs 2023-08-18 21:09:27 -04:00
S-Del 64d5fa1efd fix typo txt2txt -> txt2img 2023-08-18 22:32:20 +09:00
catboxanon 9d1d63afca Exit out of hires fix if interrupted earlier 2023-08-18 05:55:10 -04:00
catboxanon 44d4e7c500 Gallery: Set preview to True, allow custom height 2023-08-18 05:15:30 -04:00
catboxanon f89f01f9d8 Make results column sticky 2023-08-18 04:18:22 -04:00
w-e-w 640cb1bb8d fix model override logic
do not need extra logic to unload refine model
2023-08-18 17:14:02 +09:00
w-e-w a81dc43fcd negative_prompt full_prompt hash 2023-08-18 15:13:12 +09:00
w-e-w 8a1f32b6a5 image hash 2023-08-18 14:04:46 +09:00
Cade Schlaefli f9c2216ffa remove unused import 2023-08-17 21:14:14 -05:00
Cade Schlaefli 959f8b32d5 fix issues with model refresh 2023-08-17 20:48:17 -05:00
catboxanon 13f1357b7f Make image viewer actually fit the whole page 2023-08-17 20:21:46 -04:00
catboxanon 3ce5fb8e5c Add option for faster live interrupt 2023-08-17 20:03:26 -04:00
catboxanon 46e8898f65 Fix img2img background color not being used 2023-08-17 19:35:34 -04:00
catboxanon 3003b10e0a Attempt to resolve NaN issue with unstable VAEs in fp32 mk2 2023-08-17 18:10:55 -04:00
AUTOMATIC1111 0dc74545c0 resolve the issue with loading fp16 checkpoints while using --no-half 2023-08-17 07:54:07 +03:00
catboxanon 254be4eeb2 Add extra noise callback 2023-08-16 21:45:19 -04:00
AUTOMATIC1111 541ef9247c Merge pull request #12607 from AUTOMATIC1111/return-empty-list-if-extensions_dir-not-exist-
fix Return empty list if extensions dir not exist
2023-08-16 18:41:02 +03:00
w-e-w e1a29266b2 return empty list if extensions_dir not exist 2023-08-17 00:24:24 +09:00
AUTOMATIC1111 fc3a57ff96 Merge pull request #12603 from AUTOMATIC1111/auto-add-data-dir-to-gradio-allowed-path
auto add data-dir to gradio-allowed-path
2023-08-16 14:48:37 +03:00
w-e-w 0cf85b24df auto add data-dir to gradio-allowed-path 2023-08-16 20:18:46 +09:00
AUTOMATIC1111 eaba3d7349 send weights to target device instead of CPU memory 2023-08-16 12:11:01 +03:00
AUTOMATIC1111 57e59c14c8 Revert "send weights to target device instead of CPU memory"
This reverts commit 0815c45bcd.
2023-08-16 11:28:00 +03:00
AUTOMATIC1111 0815c45bcd send weights to target device instead of CPU memory 2023-08-16 10:44:17 +03:00
AUTOMATIC1111 023a3a98a1 Merge pull request #12596 from AUTOMATIC1111/fix-taesd-scale
Remove wrong TAESD Latent scale
2023-08-16 09:56:12 +03:00
AUTOMATIC1111 86221269f9 RAM optimization round 2 2023-08-16 09:55:35 +03:00
Kohaku-Blueleaf d9ddc5d4cd Remove wrong scale 2023-08-16 11:21:12 +08:00
AUTOMATIC1111 a7f7701b64 Merge pull request #12589 from catboxanon/fix/css-overflow
CSS: Remove forced visible overflow for Gradio group child divs
2023-08-15 21:47:49 +03:00
AUTOMATIC1111 fd563e3274 Merge pull request #12586 from catboxanon/fix/rng-shape
RNG: Make all elements of shape `int`s
2023-08-15 21:47:02 +03:00
AUTOMATIC1111 d09d33bc2d Merge pull request #12588 from catboxanon/fix/inpaint-upload
Fix inpaint upload for alpha masks
2023-08-15 21:46:19 +03:00
catboxanon 7083391931 CSS: Remove forced visible overflow for Gradio group child divs 2023-08-15 14:44:13 -04:00
catboxanon 0f77139253 Fix inpaint upload for alpha masks, create reusable function 2023-08-15 14:24:55 -04:00
catboxanon 5b28b7dbc7 RNG: Make all elements of shape ints 2023-08-15 13:38:37 -04:00
AUTOMATIC1111 85fcb7b8df lint 2023-08-15 19:25:03 +03:00
AUTOMATIC1111 8b181c812f Merge pull request #12584 from AUTOMATIC1111/full-module-with-bias
Add ex_bias into full module
2023-08-15 19:24:15 +03:00
AUTOMATIC1111 f01682ee01 store patches for Lora in a specialized module 2023-08-15 19:23:40 +03:00
Kohaku-Blueleaf aa57a89a21 full module with ex_bias 2023-08-15 23:41:46 +08:00
AUTOMATIC1111 7327be97aa Merge pull request #12570 from NoCrypt/add-miku-theme
Add NoCrypt/miku gradio theme
2023-08-15 16:31:12 +03:00
AUTOMATIC1111 63f881a5f0 Merge pull request #12577 from brkirch/fix-vae-near-checkpoint-exception
Fix `sd_vae_as_default` being accessed instead of `sd_vae_overrides_per_model_preferences`
2023-08-15 15:29:48 +03:00
AUTOMATIC1111 dc0e63a48a Merge pull request #12578 from AUTOMATIC1111/changelog-fix
Changelog minor correction
2023-08-15 15:29:15 +03:00
w-e-w f117bb64fc Update CHANGELOG.md 2023-08-15 20:19:13 +09:00
brkirch 54209c1639 Use the new SD VAE override setting 2023-08-15 06:29:39 -04:00
AUTOMATIC1111 ec505bac41 Merge pull request #12573 from catboxanon/changelog
Add PR refs to changelog
2023-08-15 11:47:20 +03:00
catboxanon 2154662826 Add PR refs to changelog 2023-08-15 03:23:44 -04:00
AUTOMATIC1111 9ab52caf02 update changelog file 2023-08-15 09:50:57 +03:00
AUTOMATIC1111 bc61ad9ec8 Merge pull request #12564 from catboxanon/feat/img2img-noise
Add extra noise param for img2img operations
2023-08-15 09:50:20 +03:00
NoCrypt b0a6d61d73 Add NoCrypt/miku gradio theme 2023-08-15 13:22:44 +07:00
catboxanon 371b24b17c Add extra img2img noise 2023-08-15 02:19:19 -04:00
AUTOMATIC1111 79d4e81984 fix processing error that happens if batch_size is not a multiple of how many prompts/negative prompts there are #12509 2023-08-15 08:46:17 +03:00
AUTOMATIC1111 7e77a38cbc get XYZ plot to work with recent changes to refined specified in fields of p rather than in settings 2023-08-15 08:27:50 +03:00
AUTOMATIC1111 d6b79b9963 Merge pull request #12476 from AnyISalIn/dev
xyz_grid: support refiner_checkpoint and refiner_switch_at
2023-08-15 08:26:38 +03:00
AUTOMATIC1111 6f86573247 Merge pull request #12552 from brkirch/update-sdxl-commit-hash
Update SD XL commit hash
2023-08-15 08:12:21 +03:00
AUTOMATIC1111 45be87afc6 correctly add Eta DDIM to infotext when it's 1.0 and do not add it when it's 0.0. 2023-08-14 21:48:05 +03:00
AUTOMATIC1111 5daf7983d1 when refreshing cards in extra networks UI, do not discard user's custom resolution 2023-08-14 19:27:04 +03:00
AUTOMATIC1111 f23e5ce2da revert changed inpainting mask conditioning calculation after #12311 2023-08-14 17:59:03 +03:00
AUTOMATIC1111 e56b7c8419 Merge pull request #12547 from whitebell/fix-typo
Fix typo in shared_options.py
2023-08-14 13:36:10 +03:00
AUTOMATIC1111 2359c07ddf Merge pull request #12551 from AUTOMATIC1111/separate-Extra-options
separate Extra options
2023-08-14 13:35:41 +03:00
brkirch bc63339df3 Update hash for SD XL Repo 2023-08-14 06:26:36 -04:00
w-e-w a2e213bc7b separate Extra options 2023-08-14 18:50:22 +09:00
AUTOMATIC1111 6bfd4dfecf add second_order to samplers that mistakenly didn't have it 2023-08-14 12:07:38 +03:00
Robert Barron 99ab3d43a7 hires prompt timeline: merge to latests, slightly simplify diff 2023-08-14 00:43:27 -07:00
AUTOMATIC1111 353c876172 fix API always using -1 as seed 2023-08-14 10:43:18 +03:00
Robert Barron d61e31bae6 Merge remote-tracking branch 'auto1111/dev' into shared-hires-prompt-test 2023-08-14 00:35:17 -07:00
AUTOMATIC1111 f3b96d4998 return seed controls UI to how it was before 2023-08-14 10:22:52 +03:00
AUTOMATIC1111 abbecb3e73 further repair the /docs page to not break styles with the attempted fix 2023-08-14 10:15:10 +03:00
whitebell b39d9364d8 Fix typo in shared_options.py
unperdictable -> unpredictable
2023-08-14 15:58:38 +09:00
AUTOMATIC1111 c7c16f805c repair /docs page 2023-08-14 09:49:51 +03:00
AUTOMATIC1111 f37cc5f5e1 Merge pull request #12542 from AUTOMATIC1111/res-sampler
Add RES sampler and reorder the sampler list
2023-08-14 09:02:10 +03:00
AUTOMATIC1111 3a4bee1096 Merge pull request #12543 from AUTOMATIC1111/extra-norm-module
Fix MHA error with ex_bias and support ex_bias for layers which don't have bias
2023-08-14 09:01:34 +03:00
AUTOMATIC1111 c1a31ec9f7 revert to applying mask before denoising for k-diffusion, like it was before 2023-08-14 08:59:15 +03:00
Kohaku-Blueleaf f70ded8936 remove "if bias exist" check 2023-08-14 13:53:40 +08:00
Kohaku-Blueleaf aa26f8eb40 Put frequently used sampler back 2023-08-14 13:50:53 +08:00
AUTOMATIC1111 cda2f0a162 make on_before_component/on_after_component possible earlier 2023-08-14 08:49:39 +03:00
AUTOMATIC1111 aeb76ef174 repair DDIM/PLMS/UniPC batches 2023-08-14 08:49:02 +03:00
Kohaku-Blueleaf e7c03ccdce Merge branch 'dev' into extra-norm-module 2023-08-14 13:34:51 +08:00
Kohaku-Blueleaf d9cc27cb29 Fix MHA updown err and support ex-bias for no-bias layer 2023-08-14 13:32:51 +08:00
Kohaku-Blueleaf 0ea61a74be add res(dpmdd 2m sde heun) and reorder the sampler list 2023-08-14 11:46:36 +08:00
AUTOMATIC1111 007ecfbb29 also use setup callback for the refiner instead of before_process 2023-08-13 21:01:13 +03:00
AUTOMATIC1111 9cd0475c08 Merge pull request #12526 from brkirch/mps-adjust-sub-quad
Fixes for `git checkout`, MPS/macOS fixes and optimizations
2023-08-13 20:28:49 +03:00
AUTOMATIC1111 8452708560 Merge pull request #12530 from eltociear/eltociear-patch-1
Fix typo in launch_utils.py
2023-08-13 20:27:17 +03:00
AUTOMATIC1111 16781ba09a fix 2 for git code botched by previous PRs 2023-08-13 20:15:20 +03:00
Ikko Eltociear Ashimine 09ff5b5416 Fix typo in launch_utils.py
existance -> existence
2023-08-14 01:03:49 +09:00
AUTOMATIC1111 f093c9d39d fix broken XYZ plot seeds
add new callback for scripts to be used before processing
2023-08-13 17:31:10 +03:00
brkirch 2035cbbd5d Fix DDIM and PLMS samplers on MPS 2023-08-13 10:07:52 -04:00
brkirch 5df535b7c2 Remove duplicate code for torchsde randn 2023-08-13 10:07:52 -04:00
brkirch 232c931f40 Mac k-diffusion workarounds are no longer needed 2023-08-13 10:07:52 -04:00
brkirch f4dbb0c820 Change the repositories origin URLs when necessary 2023-08-13 10:07:52 -04:00
brkirch 9058620cec git checkout with commit hash 2023-08-13 10:07:14 -04:00
brkirch 2489252099 torch.empty can create issues; use torch.zeros
For MPS, using a tensor created with `torch.empty()` can cause `torch.baddbmm()` to include NaNs in the tensor it returns, even though `beta=0`. However, with a tensor of shape [1,1,1], there should be a negligible performance difference between `torch.empty()` and `torch.zeros()` anyway, so it's better to just use `torch.zeros()` for this and avoid unnecessarily creating issues.
2023-08-13 10:06:25 -04:00
brkirch 87dd685224 Make sub-quadratic the default for MPS 2023-08-13 10:06:25 -04:00
brkirch abfa4ad8bc Use fixed size for sub-quadratic chunking on MPS
Even if this causes chunks to be much smaller, performance isn't significantly impacted. This will usually reduce memory usage but should also help with poor performance when free memory is low.
2023-08-13 10:06:25 -04:00
AUTOMATIC1111 3163d1269a fix for the broken run_git calls 2023-08-13 16:51:21 +03:00
AUTOMATIC1111 1c6ca09992 Merge pull request #12510 from catboxanon/feat/extnet/hashes
Support search and display of hashes for all extra network items
2023-08-13 16:46:32 +03:00
AUTOMATIC1111 d73db17ee3 Merge pull request #12515 from catboxanon/fix/gc1
Clear sampler and garbage collect before decoding images to reduce VRAM
2023-08-13 16:45:38 +03:00
AUTOMATIC1111 127ab9114f Merge pull request #12514 from catboxanon/feat/batch-encode
Encode batch items individually to significantly reduce VRAM
2023-08-13 16:41:07 +03:00
AUTOMATIC1111 d53f3b5596 Merge pull request #12520 from catboxanon/eta
Update description of eta setting
2023-08-13 16:40:17 +03:00
AUTOMATIC1111 d41a5bb97d Merge pull request #12521 from catboxanon/feat/more-s-noise
Add `s_noise` param to more samplers
2023-08-13 16:39:25 +03:00
AUTOMATIC1111 551d2fabcc Merge pull request #12522 from catboxanon/fix/extra_params
Restore `extra_params` that was lost in merge
2023-08-13 16:38:27 +03:00
AUTOMATIC1111 db40d26d08 linter 2023-08-13 16:38:10 +03:00
catboxanon 525b55b1e9 Restore extra_params that was lost in merge 2023-08-13 09:08:34 -04:00
catboxanon ce0829d711 Merge branch 'feat/dpmpp3msde' into feat/more-s-noise 2023-08-13 08:46:58 -04:00
catboxanon ac790fc49b Discard penultimate sigma for DPM-Solver++(3M) SDE 2023-08-13 08:46:07 -04:00
catboxanon f4757032e7 Fix s_noise description 2023-08-13 08:24:28 -04:00
catboxanon d1a70c3f05 Add s_noise param to more samplers 2023-08-13 08:22:24 -04:00
AUTOMATIC1111 d8419762c1 Lora: output warnings in UI rather than fail for unfitting loras; switch to logging for error output in console 2023-08-13 15:07:37 +03:00
catboxanon 60a7405165 Update description of eta setting 2023-08-13 08:06:40 -04:00
catboxanon 1ae9dacb4b Add DPM-Solver++(3M) SDE 2023-08-13 07:57:29 -04:00
catboxanon 69f49c8d39 Clear sampler before decoding images
More significant VRAM reduction.
2023-08-13 04:40:34 -04:00
catboxanon 822597db49 Encode batches separately
Significantly reduces VRAM.
This makes encoding more inline with how decoding currently functions.
2023-08-13 04:16:48 -04:00
catboxanon 7fa5ee54b1 Support search and display of hashes for all extra network items 2023-08-13 02:32:54 -04:00
AUTOMATIC1111 da80d649fd Merge pull request #12503 from AUTOMATIC1111/extra-norm-module
Add Norm Module to lora ext and add "bias" support
2023-08-13 08:28:48 +03:00
AUTOMATIC1111 61673451ff Merge pull request #12491 from AUTOMATIC1111/xyz-csv-and-dropdown-mode
Bring back CSV mode for XYZ grid
2023-08-13 08:25:15 +03:00
AUTOMATIC1111 599f61a1e0 use dataclass for StableDiffusionProcessing 2023-08-13 08:24:16 +03:00
w-e-w 0e3bac8132 rephrase and move 2023-08-13 14:09:38 +09:00
AUTOMATIC1111 fa9370b741 add refiner to StableDiffusionProcessing class
write out correct model name in infotext, rather than the refiner model
2023-08-13 06:07:30 +03:00
Kohaku-Blueleaf 5881dcb887 remove debug print 2023-08-13 02:36:02 +08:00
Kohaku-Blueleaf a2b8305096 return None if no ex_bias 2023-08-13 02:35:04 +08:00
Kohaku-Blueleaf bd4da4474b Add extra norm module into built-in lora ext
refer to LyCORIS 1.9.0.dev6
add new option and module for training norm layer
(Which is reported to be good for style)
2023-08-13 02:27:39 +08:00
w-e-w dc5b5ee9c6 properly convert this into CSV string 2023-08-13 02:21:04 +09:00
w-e-w 299eb54308 pass csv_mode 2023-08-13 02:17:13 +09:00
w-e-w 8d9ca46e0a convert value when switching mode 2023-08-13 02:05:20 +09:00
AUTOMATIC1111 b2080756fc make "send to" buttons into small tool buttons 2023-08-12 19:03:33 +03:00
AUTOMATIC1111 9d0ec13596 fix quicksettings on Chrome 2023-08-12 18:42:59 +03:00
AUTOMATIC1111 6816ad5ed8 fix broken reuse seed 2023-08-12 18:36:30 +03:00
AUTOMATIC1111 4e8690906c update seed/subseed HTML widths 2023-08-12 18:00:30 +03:00
AUTOMATIC1111 f0b72b8121 move seed, variation seed and variation seed strength to a single row, dump resize seed from UI
add a way for scripts to register a callback for before/after just a single component's creation
2023-08-12 17:46:13 +03:00
w-e-w 7a68ac6615 rename to csv mode 2023-08-12 23:40:05 +09:00
w-e-w f131f84e13 dropdown mode chackbox 2023-08-12 23:26:25 +09:00
AUTOMATIC1111 6aa26a26d5 change quicksettings items to have variable width 2023-08-12 16:47:39 +03:00
w-e-w fd617fad00 Redundant character escape '\]' in RegExp 2023-08-12 22:24:59 +09:00
w-e-w d20eb11c9e format 2023-08-12 22:24:00 +09:00
w-e-w c8d453e915 bring back csv mode 2023-08-12 22:20:34 +09:00
AUTOMATIC1111 b293ed3061 make it possible to use hires fix together with refiner 2023-08-12 12:54:32 +03:00
AUTOMATIC1111 64311faa68 put refiner into main UI, into the new accordions section
add VAE from main model into infotext, not from refiner model
option to make scripts UI without gr.Group
fix inconsistencies with refiner when usings samplers that do more denoising than steps
2023-08-12 12:39:59 +03:00
AUTOMATIC1111 26c92f056a Merge pull request #12480 from catboxanon/fix/cc
Fix color correction by converting image to RGB
2023-08-12 09:12:30 +03:00
AUTOMATIC1111 ebc1bafb03 Merge pull request #12479 from catboxanon/fix/extras-generator
Refactor postprocessing/extras tab to use generator to resolve OOM issues
2023-08-12 08:58:14 +03:00
AUTOMATIC1111 9dae70da79 Merge pull request #12487 from AUTOMATIC1111/disable-extensions-installer-with-arg
pathc: also disable extensions installer with arg
2023-08-12 08:57:35 +03:00
w-e-w f57bc1a21b disable extensions installer with arg 2023-08-12 12:06:31 +09:00
catboxanon af27b716e5 Fix color correction by converting image to RGB 2023-08-11 12:22:11 -04:00
catboxanon 7c9c19b2a2 Refactor postprocessing to use generator to resolve OOM issues 2023-08-11 11:32:12 -04:00
AnyISalIn 3b2f51602d xyz_grid: support refiner_checkpoint and refiner_switch_at
Signed-off-by: AnyISalIn <anyisalin@gmail.com>
2023-08-11 21:40:33 +08:00
AUTOMATIC1111 ae6b30907d Merge pull request #12470 from Splendide-Imaginarius/mask-blur-property+kernel
Make `StableDiffusionProcessingImg2Img.mask_blur` a property, make more inline with PIL `GaussianBlur`
2023-08-11 15:03:18 +03:00
AUTOMATIC1111 77c52ea701 fix accordion style on img2img 2023-08-11 11:59:11 +03:00
AUTOMATIC1111 3c00e41ec0 Merge pull request #12458 from daswer123/auto-expand
Zoom and pan: Some fixes for the auto-expand
2023-08-11 07:56:31 +03:00
AUTOMATIC1111 340c1cc68d Merge pull request #12463 from catboxanon/fix/vae-hash
Properly return `None` for VAE hash when using `--no-hashing`
2023-08-11 07:55:42 +03:00
AUTOMATIC1111 2c79f2af6e Merge pull request #12466 from catboxanon/fix/lora-old-mk2
Fix broken `Lora/Networks: use old method` option
2023-08-11 07:53:12 +03:00
catboxanon 4fafc34e49 Fix to make LoRA old method setting work 2023-08-10 23:42:58 -04:00
catboxanon d456fb797a fix: Properly return None when VAE hash is None 2023-08-10 16:04:49 -04:00
AUTOMATIC1111 458eda1321 Merge pull request #12456 from AUTOMATIC1111/patch-#12453
Patch #12453
2023-08-10 17:55:31 +03:00
Robert Barron 54f926b11d fix bad merge 2023-08-10 07:48:04 -07:00
w-e-w a75d756a6f use default value if value error 2023-08-10 23:47:28 +09:00
Robert Barron 863613293e Merge branch 'shared-hires-prompt-raw' into shared-hires-prompt-test 2023-08-10 07:45:35 -07:00
AUTOMATIC1111 9af5cce4c7 Merge pull request #12454 from wfjsw/no-autofix-on-fetch
rm dir on failed clone, disable autofix for fetch
2023-08-10 17:28:29 +03:00
AUTOMATIC1111 e0906096c5 remove unnecessary GFPGAN_PACKAGE (we install GFPGAN from the requirements file) 2023-08-10 17:22:08 +03:00
AUTOMATIC1111 4549f2a9cc lint 2023-08-10 17:21:01 +03:00
AUTOMATIC1111 f4979422dd return the line lost during the merge 2023-08-10 17:18:33 +03:00
Jabasukuriputo Wang 5a705c2468 rm dir on failed clone, disable autofix for fetch 2023-08-10 09:18:10 -05:00
AUTOMATIC1111 36762f0eaf Merge pull request #12371 from AUTOMATIC1111/refiner
initial refiner support
2023-08-10 17:05:32 +03:00
AUTOMATIC1111 ac8a5d18d3 resolve merge issues 2023-08-10 17:04:59 +03:00
AUTOMATIC1111 70a01cd444 Merge branch 'dev' into refiner 2023-08-10 17:04:38 +03:00
AUTOMATIC1111 959404e0e2 Merge pull request #12453 from AUTOMATIC1111/catch-float-ValueError-default-to--1
Catch float value error default to -1
2023-08-10 16:46:40 +03:00
AUTOMATIC1111 887bcfdf65 Merge pull request #12447 from AUTOMATIC1111/extra-networks-metadata-indent-
save extra networks metadata with indent
2023-08-10 16:46:08 +03:00
AUTOMATIC1111 40ccd26b19 Merge pull request #12450 from catboxanon/cache-file
Add env var for cache file
2023-08-10 16:45:44 +03:00
w-e-w 4412398c4b catch float ValueError default -1 2023-08-10 22:44:33 +09:00
AUTOMATIC1111 942d7a118a Merge pull request #12452 from AUTOMATIC1111/use-new-style-constructor
use new style constructor
2023-08-10 16:43:27 +03:00
AUTOMATIC1111 070b034cd5 put infotext label for setting into OptionInfo definition rather than in a separate list 2023-08-10 16:42:26 +03:00
AUTOMATIC1111 9d78d317ae add VAE to infotext 2023-08-10 16:22:10 +03:00
Danil Boldyrev 045f740892 Height fix 2023-08-10 16:17:52 +03:00
AUTOMATIC1111 b13806c150 fix a bug preventing normal operation if a string is added to a gr.Number component via ui-config.json 2023-08-10 16:15:34 +03:00
AUTOMATIC1111 4f6582cb66 add precision=0 to gr.Number seed 2023-08-10 16:10:42 +03:00
AUTOMATIC1111 1b3093fe3a fix --use-textbox-seed 2023-08-10 15:58:53 +03:00
w-e-w 237b704172 use new style constructor 2023-08-10 21:42:26 +09:00
AUTOMATIC1111 4d93f48f09 fix for multiple input accordions 2023-08-10 15:32:54 +03:00
Danil Boldyrev ed01d2ee3b a another fix, a different approach 2023-08-10 13:45:25 +03:00
catboxanon 386202895f Add env var for cache file 2023-08-10 06:17:45 -04:00
AUTOMATIC1111 0883810592 comment for InputAccordion 2023-08-10 13:02:50 +03:00
AUTOMATIC1111 faca86620d linter fixes 2023-08-10 12:58:00 +03:00
AUTOMATIC1111 6c23061a7d avoid importing gradio in tests because it spams warnings 2023-08-10 12:50:03 +03:00
AUTOMATIC1111 33446acf47 face restoration and tiling moved to settings - use "Options in main UI" setting if you want them back 2023-08-10 12:41:41 +03:00
w-e-w 0a0a9d4fe9 extra networks metadata indent 2023-08-10 18:05:17 +09:00
AUTOMATIC1111 9199b6b7eb add a custom UI element that combines accordion and checkbox
rework hires fix UI to use accordion
prevent bogus progress output in console when calculating hires fix dimensions
2023-08-10 11:20:46 +03:00
AUTOMATIC1111 2c5106ed06 additional work on gradio styles;
make the accordion change affect all accordions, not just inside scripts div
2023-08-10 07:57:52 +03:00
AUTOMATIC1111 6ed1541ef5 Merge pull request #12312 from catboxanon/script-accordion-style
Add styling for script components
2023-08-10 07:05:44 +03:00
AUTOMATIC1111 736aaf348b Merge pull request #12440 from catboxanon/dev
Use better symbol for extra networks sort
2023-08-10 06:39:38 +03:00
AUTOMATIC1111 f0edd26998 Merge pull request #12439 from catboxanon/fix/slerp-import
Add slerp import for extension backwards compat
2023-08-10 06:37:44 +03:00
catboxanon ff1bfd01ba Remove up down symbol 2023-08-09 14:41:25 -04:00
catboxanon 2ceb4f81e2 Use better symbol for extra networks sort 2023-08-09 14:40:18 -04:00
catboxanon 259805947e Add slerp import for extension backwards compat 2023-08-09 14:24:16 -04:00
AUTOMATIC1111 66c32e40e8 fix gradio themes not applying 2023-08-09 21:19:33 +03:00
AUTOMATIC1111 edfae9e78a add --loglevel commandline argument for logging
remove the progressbar for extension installation in favor of logging output
2023-08-09 20:49:33 +03:00
Robert Barron d1ba46b6e1 allow first pass and hires pass to use a single prompt to do different prompt editing, hires is 1.0..2.0:
relative time range is [1..2]
  absolute time range is [steps+1..steps+hire_steps], e.g. with 30 steps and 20 hires steps, '20' is 2/3rds through first pass, and 40 is halfway through hires pass
2023-08-09 10:38:47 -07:00
AUTOMATIC1111 c7b9394daf Merge pull request #12435 from daswer123/auto-expand
Zoom and pan: fix auto-expand
2023-08-09 20:04:44 +03:00
AUTOMATIC1111 ab42f81c75 Merge pull request #12436 from catboxanon/fix/tqdm
Only import `tqdm` when needed in `launch_utils`
2023-08-09 20:03:55 +03:00
catboxanon 8b7b99f8d5 fix: Only import tqdm when needed 2023-08-09 12:18:03 -04:00
Danil Boldyrev 4a64d34001 fix auto-expand 2023-08-09 18:40:45 +03:00
AUTOMATIC1111 95821f0132 split webui.py's initialization and utility functions into separate files 2023-08-09 18:11:13 +03:00
AUTOMATIC1111 a2a97e57f0 simplify 2023-08-09 17:08:36 +03:00
AUTOMATIC1111 f2ebcee7c4 Merge pull request #11925 from wfjsw/ext-inst-pbar
Progressbar for extension installers
2023-08-09 17:03:24 +03:00
AUTOMATIC1111 eed963e972 Lora cache in memory 2023-08-09 16:54:49 +03:00
AUTOMATIC1111 7ba8f11688 fix missing restricted_opts from shared 2023-08-09 15:06:03 +03:00
AUTOMATIC1111 aa10faa591 fix checkpoint name jumping around in the list of checkpoints for no good reason 2023-08-09 14:47:44 +03:00
AUTOMATIC1111 358f55db6a Merge pull request #12424 from AUTOMATIC1111/extra-network-metadata-inherit-old-description
extra network metadata inherit old description
2023-08-09 14:41:30 +03:00
AUTOMATIC1111 c8c48640e6 Merge pull request #12426 from AUTOMATIC1111/split_shared
Split shared.py into multiple files
2023-08-09 14:40:06 +03:00
w-e-w 0cac6ab615 extra network metadata inherit old description 2023-08-09 20:35:06 +09:00
AUTOMATIC1111 2617598b7a Merge pull request #12392 from olivierlacan/fix/fastapi
Pin fastapi to > 0.90.1 to fix crash
2023-08-09 14:25:50 +03:00
AUTOMATIC1111 8eea891718 Merge pull request #12396 from Uminosachi/fix-mismatch-shared
Fix mismatch between shared.sd_model & shared.opts
2023-08-09 14:20:12 +03:00
AUTOMATIC1111 386245a264 split shared.py into multiple files; should resolve all circular reference import errors related to shared.py 2023-08-09 10:25:35 +03:00
AUTOMATIC1111 7d81ecbea6 Split history: mv temp modules/shared.py 2023-08-09 08:47:53 +03:00
AUTOMATIC1111 8cf8fc6794 Split history: merge 2023-08-09 08:47:53 +03:00
AUTOMATIC1111 da0712ee7d Split history: mv modules/shared.py temp 2023-08-09 08:47:53 +03:00
AUTOMATIC1111 a6f840b4dc Split history: mv modules/shared.py modules/shared_options.py 2023-08-09 08:47:52 +03:00
AUTOMATIC1111 0d5dc9a6e7 rework RNG to use generators instead of generating noises beforehand 2023-08-09 08:43:31 +03:00
AUTOMATIC1111 d81d3fa8cd fix styles missing from the prompt in infotext when making a grid of batch of multiplie images 2023-08-09 07:45:06 +03:00
w-e-w c102780693 extra network metadata inherit old description 2023-08-09 13:38:53 +09:00
AUTOMATIC1111 7f9dbc45b1 Merge pull request #12413 from daswer123/auto-expand
Zoom and pan: option to auto-expand a wide image
2023-08-09 07:03:30 +03:00
AUTOMATIC1111 08e538e2e6 Merge pull request #12422 from catboxanon/fix/hr-same-sampler
Fix HR `Use same sampler` option
2023-08-09 07:00:48 +03:00
catboxanon bd4b4292ef Fix hr use same sampler 2023-08-08 20:55:08 -04:00
Danil Boldyrev e12a1be1ca auto-expand enable by default for js 2023-08-09 00:14:19 +03:00
Danil Boldyrev a74c014425 auto-expand enable by default 2023-08-09 00:06:51 +03:00
AUTOMATIC1111 a2360de3f3 Merge pull request #12412 from dhwz/dev
fix typo
2023-08-08 23:30:57 +03:00
AUTOMATIC1111 0e83c67525 by request: fix tiled vae extension 2023-08-08 22:27:32 +03:00
AUTOMATIC1111 1aefb50259 add None refiner option 2023-08-08 22:17:25 +03:00
AUTOMATIC1111 ec194b6374 fix webui not switching back to original model from refiner when batch count is greater than 1 2023-08-08 22:14:02 +03:00
AUTOMATIC1111 f8ff8c0638 merge errors 2023-08-08 22:09:51 +03:00
AUTOMATIC1111 54c3e5c913 Merge branch 'dev' into refiner 2023-08-08 21:49:47 +03:00
AUTOMATIC1111 70c63c1208 pass samplers from UI by name, make it possible to use a sampler from infotext even if it's hidden in the dropdown 2023-08-08 21:28:34 +03:00
Danil Boldyrev bc7906e6d6 Ability to automatically expand a picture that does not fit in the screen 2023-08-08 21:28:16 +03:00
AUTOMATIC1111 ae1bde1aa1 put commonly used samplers on top, make DPM++ 2M Karras the default choice 2023-08-08 21:10:12 +03:00
AUTOMATIC1111 a8a256f9b5 REMOVE 2023-08-08 21:08:50 +03:00
AUTOMATIC1111 8285a149d8 add CFG denoiser implementation for DDIM, PLMS and UniPC (this is the commit when you can run both old and new implementations to compare them) 2023-08-08 21:04:44 +03:00
dhwz 2a72d76d6f fix typo 2023-08-08 19:08:37 +02:00
AUTOMATIC1111 2d8e4a6544 split sd_samplers_kdiffusion into two 2023-08-08 18:35:31 +03:00
AUTOMATIC1111 c721884cf5 Split history: mv temp modules/sd_samplers_kdiffusion.py 2023-08-08 18:32:18 +03:00
AUTOMATIC1111 ee2b8f2e1b Split history: merge 2023-08-08 18:32:18 +03:00
AUTOMATIC1111 a3e27019e4 Split history: mv modules/sd_samplers_kdiffusion.py temp 2023-08-08 18:32:17 +03:00
AUTOMATIC1111 7e88f57aaa Split history: mv modules/sd_samplers_kdiffusion.py modules/sd_samplers_cfg_denoiser.py 2023-08-08 18:32:17 +03:00
AUTOMATIC1111 902f8cf292 Merge pull request #12254 from AUTOMATIC1111/auro-autolaunch
Automatically open webui in browser when running "locally"
2023-08-08 06:44:49 +03:00
w-e-w f17c8c2eff Merge branch 'dev' into auro-autolaunch 2023-08-08 11:39:34 +09:00
w-e-w c75bda867b setting: Automatically open webui in browser on startup 2023-08-08 11:29:33 +09:00
Uminosachi 8c200c2156 Fix mismatch between shared.sd_model & shared.opts 2023-08-08 10:48:03 +09:00
Olivier Lacan b0f7f4a991 Pin fastapi to > 0.90.1 to fix crash
See https://github.com/AUTOMATIC1111/stable-diffusion-webui/issues/11642#issuecomment-1643298659

This resolves a crashing bug for me on Python 3.10 and it appears to do so
as well for others.
2023-08-07 12:46:02 -07:00
AUTOMATIC1111 01997f45ba fix extra_options_section misbehaving when there's just one extra_options element 2023-08-07 18:49:23 +03:00
AUTOMATIC1111 251140fc88 Merge pull request #12379 from diegocr/dev
Allow to open images in new browser tab by MMB.
2023-08-07 17:48:13 +03:00
Diego Casorran aea0fa9fd5 Allow to open images in new browser tab by MMB.
Signed-off-by: Diego Casorran <dcasorran@gmail.com>
2023-08-07 14:53:42 +02:00
AUTOMATIC1111 912356133a Merge pull request #12387 from huaizong/feature/whz/fix-api-only-mode-lora-nowork
Feature/whz/fix api only mode lora nowork
2023-08-07 13:36:26 +03:00
王怀宗 250a95b6fe fix: enable before_ui_callback when api only mode (fixes #7984) 2023-08-07 18:08:07 +08:00
AUTOMATIC1111 fd67eafc65 Merge pull request #12385 from catboxanon/dev
Remove deprecated style method
2023-08-07 09:43:59 +03:00
AUTOMATIC1111 4c72377bbf Options in main UI update
- correctly read values from pasted infotext
- setting for column count
- infotext paste: do not add a field to override settings if some other component is already handling it
2023-08-07 09:42:13 +03:00
catboxanon 7d8f55ec7c Remove style method 2023-08-07 01:45:10 -04:00
AUTOMATIC1111 0ea20a0d52 rework #12230 to not have duplicate code 2023-08-07 08:38:18 +03:00
AUTOMATIC1111 5cf37ca89f Merge pull request #12230 from wfjsw/git-clone-autofix
Git autofix
2023-08-07 08:27:27 +03:00
AUTOMATIC1111 3453710d10 Merge pull request #12375 from catboxanon/k-diffusion-sigma
Clean up k-diffusion sigma params
2023-08-07 08:20:05 +03:00
AUTOMATIC1111 6e7828e1d2 apply unet overrides after switching model 2023-08-07 08:16:20 +03:00
AUTOMATIC1111 c96e4750d8 SD VAE rework 2
- the setting for preferring opts.sd_vae has been inverted and reworded
- resolve_vae function made easier to read and now returns an object rather than a tuple
- if the checkbox for overriding per-model preferences is checked, opts.sd_vae overrides checkpoint user metadata
- changing VAE in user metadata  for currently loaded model immediately applies the selection
2023-08-07 08:07:20 +03:00
catboxanon 7bcfb4654f Add info to k-diffusion sigma params 2023-08-06 12:41:21 -04:00
catboxanon 976963ab6d Clean up k-diffusion sigma params 2023-08-06 12:30:23 -04:00
AUTOMATIC1111 5a0db84b6c add infotext
add proper support for recalculating conds in k-diffusion samplers
remove support for compvis samplers
2023-08-06 17:53:33 +03:00
AUTOMATIC1111 5a38a9c0ee Merge pull request #12369 from diegocr/dev
add explicit content-type header for image/webp
2023-08-06 17:52:28 +03:00
AUTOMATIC1111 956e69bf3a lint! 2023-08-06 17:07:08 +03:00
AUTOMATIC1111 f1975b0213 initial refiner support 2023-08-06 17:01:07 +03:00
Diego Casorran e866c35462 add explicit content-type header for image/webp 2023-08-06 12:25:04 +00:00
AUTOMATIC1111 57e8a11d17 enable cond cache by default 2023-08-06 13:25:51 +03:00
AUTOMATIC1111 f9950da3e3 create dir for gradio themes cache if it's missing 2023-08-06 12:39:28 +03:00
AUTOMATIC1111 aa42c0ff8e repair broken live previews if using VAE with half 2023-08-06 07:41:24 +03:00
AUTOMATIC1111 06da34d47a Merge pull request #12358 from catboxanon/sigma-infotext
Add missing k-diffusion sigma params to infotext
2023-08-06 06:56:07 +03:00
AUTOMATIC1111 5cae08f2c3 fix rework saving incomplete images 2023-08-06 06:55:19 +03:00
catboxanon 8f31b139b8 Assume 0 = inf for s_tmax 2023-08-05 23:50:33 -04:00
catboxanon ce4be668fe Read kdiffusion sigma params from opts 2023-08-05 23:42:20 -04:00
AUTOMATIC1111 2e8b40004e Merge pull request #12355 from AUTOMATIC1111/gradio-theme-cache
Gradio theme cache
2023-08-06 06:37:48 +03:00
catboxanon 1e8482356c Merge branch 'dev' into sigma-infotext 2023-08-05 23:37:38 -04:00
w-e-w e9c591b101 Gradio theme cache 2023-08-06 12:33:20 +09:00
AUTOMATIC1111 ee96a6a588 do the same for s_tmax #12345 2023-08-06 06:32:41 +03:00
AUTOMATIC1111 92b99f3273 Merge pull request #12354 from catboxanon/fix/s-noise
Allow `s_noise` override to actually be used
2023-08-06 06:26:57 +03:00
AUTOMATIC1111 ee75416e3e Merge branch 'dev' into fix/s-noise 2023-08-06 06:25:35 +03:00
AUTOMATIC1111 d86d12e911 rework saving incomplete images 2023-08-06 06:21:36 +03:00
AUTOMATIC1111 2844d9597b Merge pull request #12338 from AUTOMATIC1111/dont-save-incomplete-images
don't save incomplete images
2023-08-06 06:05:47 +03:00
AUTOMATIC1111 dd1e2726f3 Merge pull request #12352 from bannsec/bannsec-patch-1
Update README.md
2023-08-06 06:05:28 +03:00
catboxanon f18a032190 Correct s_noise fix 2023-08-05 23:05:25 -04:00
AUTOMATIC1111 9cbde6c9fd Merge pull request #12356 from catboxanon/fix/s-churn-max
Increase `s_churn` max value
2023-08-06 05:56:05 +03:00
AUTOMATIC1111 f4e4992a4a Merge pull request #12357 from catboxanon/s-tmax
Add option for `s_tmax`
2023-08-06 05:55:20 +03:00
catboxanon 31506f0771 Add sigma params to infotext 2023-08-05 22:37:25 -04:00
catboxanon 85c2c138d2 Attempt to read s_tmax from arg first if option not found 2023-08-05 21:51:46 -04:00
catboxanon c11104fed5 Add s_tmax 2023-08-05 21:42:03 -04:00
catboxanon dfc01c68cd Increase s_churn max value 2023-08-05 21:23:58 -04:00
catboxanon 496cef956b Allow s_noise override to actually be used 2023-08-05 21:14:13 -04:00
bannsec b315c20756 Update README.md
Correct install instructions on linux and provide additional required apt packages
Fixes #12351
2023-08-05 14:07:35 -04:00
AUTOMATIC1111 c6278c15a8 add explanation for gradio themes 2023-08-05 17:11:37 +03:00
AUTOMATIC1111 0a0a6b2a4d Merge pull request #12346 from dhwz/dev
add new gradio themes
2023-08-05 17:08:38 +03:00
dhwz 1f7fc4d7a3 fix whitespace 2023-08-05 16:07:57 +02:00
dhwz 8ece321df3 add new gradio themes 2023-08-05 16:03:06 +02:00
w-e-w 1d7dcdb6c3 Option to not save incomplete images 2023-08-05 19:07:53 +09:00
AUTOMATIC1111 60183eebc3 add description to VAE setting page 2023-08-05 11:18:13 +03:00
AUTOMATIC1111 36ca80d004 put VAE into a separate settings page 2023-08-05 10:43:06 +03:00
AUTOMATIC1111 3f451f3042 do not add VAE Encoder/Decoder to infotext if it's the default 2023-08-05 10:36:26 +03:00
AUTOMATIC1111 c980dca234 Merge pull request #12331 from AUTOMATIC1111/need_Reload-UI_not_Restart
only need Reload UI not Restart
2023-08-05 09:37:18 +03:00
AUTOMATIC1111 f879cac1e7 Merge pull request #12311 from AUTOMATIC1111/efficient-vae-methods
Add TAESD(or more) options for all the VAE encode/decode operation
2023-08-05 09:24:26 +03:00
AUTOMATIC1111 ad510b2cd3 fix refresh button for styles 2023-08-05 09:17:36 +03:00
AUTOMATIC1111 c74c708ed8 add checkbox to show/hide dirs for extra networks 2023-08-05 09:15:18 +03:00
AUTOMATIC1111 e053e21af6 put localStorage stuff into its own file 2023-08-05 08:48:03 +03:00
w-e-w 7a64601428 need Reload UI not Restart 2023-08-05 14:21:28 +09:00
Kohaku-Blueleaf b85ec2b9b6 Fix some merge mistakes 2023-08-05 13:14:00 +08:00
Kohaku-Blueleaf d56a9cfe6a Merge branch 'dev' into efficient-vae-methods 2023-08-05 13:12:37 +08:00
AUTOMATIC1111 a32f270a47 Merge pull request #11808 from AUTOMATIC1111/extra-networks-always-visible
Always show extra networks tabs in the UI
2023-08-05 08:07:26 +03:00
AUTOMATIC1111 8197f24dbc remove the extra networks button 2023-08-05 08:07:13 +03:00
AUTOMATIC1111 ef1698fd6d Merge branch 'dev' into extra-networks-always-visible 2023-08-05 08:01:38 +03:00
Splendide Imaginarius 56888644a6 Reduce mask blur kernel size to 2.5 sigmas
This more closely matches the old behavior of PIL's Gaussian blur, and
fixes breakage when tiling.

See https://github.com/Coyote-A/ultimate-upscale-for-automatic1111/issues/111#issuecomment-1663504109

Thanks to Алексей Трофимов and eunnone for reporting the issue.
2023-08-05 04:54:23 +00:00
AUTOMATIC1111 c613416af3 Merge pull request #12227 from AUTOMATIC1111/multiple_loaded_models
option to keep multiple models in memory
2023-08-05 07:52:50 +03:00
AUTOMATIC1111 22ecb78b51 Merge branch 'dev' into multiple_loaded_models 2023-08-05 07:52:29 +03:00
Kohaku-Blueleaf a6b245e46f dix 2023-08-05 12:49:35 +08:00
AUTOMATIC1111 0ae2767ae6 Merge pull request #12181 from AUTOMATIC1111/hires_checkpoint
Hires fix change checkpoint
2023-08-05 07:47:34 +03:00
AUTOMATIC1111 e64263653a Merge pull request #12327 from catboxanon/fix/filename-invalid-chars
Add tab and carriage return to invalid filename chars
2023-08-05 07:47:07 +03:00
AUTOMATIC1111 d2b842ce07 move img2img settings to their own section 2023-08-05 07:46:22 +03:00
Kohaku-Blueleaf d8371d0b3c update info 2023-08-05 12:37:46 +08:00
AUTOMATIC1111 e7140a36c0 change default color to white 2023-08-05 07:36:25 +03:00
Kohaku-Blueleaf aa744cadc8 add infotext 2023-08-05 12:35:40 +08:00
AUTOMATIC1111 63cac3c3cc Merge pull request #12326 from AUTOMATIC1111/configurable-masks-color-and-default-brush-color-
configurable masks color and default brush color
2023-08-05 07:34:22 +03:00
catboxanon bcff763b6e Add tab and carriage return to invalid filename chars 2023-08-04 22:59:47 -04:00
Kohaku-Blueleaf 9ac2989edd Merge branch 'dev' into efficient-vae-methods 2023-08-05 10:43:17 +08:00
w-e-w 1d60a609a9 configurable masks color and default brush color 2023-08-05 09:34:26 +09:00
AUTOMATIC1111 4560176640 added VAE selection to checkpoint user metadata 2023-08-04 22:05:50 +03:00
AUTOMATIC1111 31a9966b9d Merge pull request #12319 from catboxanon/fix/alternating-words-empty
Prompt parser: Account for empty field in alternating words syntax
2023-08-04 20:35:25 +03:00
AUTOMATIC1111 c57cb6e89c Merge pull request #12318 from catboxanon/sysinfo-new-page
Open raw sysinfo link in new page
2023-08-04 20:31:10 +03:00
catboxanon b6596cdb19 Prompt parser: account for empty field in alternating words syntax 2023-08-04 13:26:37 -04:00
catboxanon 9213d5cb3b Open raw sysinfo link in new page 2023-08-04 12:26:37 -04:00
AUTOMATIC1111 682ff8936d glorious, glorious wonderful clear milky white butter smooth color for inpainting
you are the best, gradio
how I yearned for this day
i always believed in you
i knew you had it in you
this day marks a new beginning
thank you, everyone
thank you
2023-08-04 18:51:25 +03:00
AUTOMATIC1111 f08a69e629 Merge pull request #12310 from catboxanon/fix/gradio-3-39-0-textbox-overflow
Fix Gradio 3.39.0 textbox overflow
2023-08-04 15:55:25 +03:00
AUTOMATIC1111 fadbab3781 Curse you, gradio!!! fixes broken refresh button #12309 2023-08-04 14:56:39 +03:00
catboxanon 3ca3c7f1c6 Add styling for script components 2023-08-04 07:20:32 -04:00
catboxanon daee41e0d6 Fix Gradio 3.39.0 textbox overflow 2023-08-04 06:45:12 -04:00
Kohaku-Blueleaf 21000f13a1 replace get_first_stage_encoding 2023-08-04 18:23:14 +08:00
AUTOMATIC1111 a0e74c4db4 Merge pull request #12308 from catboxanon/fix/gradio-3-39-0-inpaint-mask
Fix inpaint mask for Gradio 3.39.0
2023-08-04 13:16:50 +03:00
Kohaku-Blueleaf 073342c887 remove noneed scale 2023-08-04 17:55:52 +08:00
Kohaku-Blueleaf 6346d8eeaa Revert "change all encode"
This reverts commit 094c416a80.
2023-08-04 17:53:30 +08:00
Kohaku-Blueleaf 094c416a80 change all encode 2023-08-04 17:53:16 +08:00
catboxanon 99f5f8e76b Fix string quotes 2023-08-04 05:47:25 -04:00
catboxanon cd4e053e5e Simply img2img mask conversion, fix threshold 2023-08-04 05:43:53 -04:00
catboxanon 2dc2bc4ab5 Fix string quotes 2023-08-04 05:40:13 -04:00
catboxanon e219211ff6 Remove unused import in img2img 2023-08-04 05:35:47 -04:00
catboxanon df9fd1d3ae Fix inpaint mask for Gradio 3.39.0 2023-08-04 05:31:38 -04:00
AUTOMATIC1111 2e613a6ffc Merge pull request #12304 from catboxanon/fix/extras-infotext-paste
Correctly toggle extras checkbox for infotext paste
2023-08-04 12:04:11 +03:00
catboxanon f5994e84a2 Cleanup extras checkbox infotext paste check 2023-08-04 04:57:01 -04:00
AUTOMATIC1111 c93857922a Merge pull request #12201 from AnyISalIn/dev
fix: sdxl model invalid configuration after the hijack
2023-08-04 11:53:19 +03:00
AUTOMATIC1111 6391128b41 Merge pull request #12306 from catboxanon/fix/hires-infotext-paste
Only enable hires fix if hires scale or upscaler found in params for infotext paste
2023-08-04 11:52:17 +03:00
catboxanon 7c5480eb96 Cleanup hr infotext paste check mk2 2023-08-04 04:42:35 -04:00
catboxanon 67312653d7 Cleanup hr infotext paste check 2023-08-04 04:40:56 -04:00
AUTOMATIC1111 e81b431701 Merge pull request #12307 from daxijiu/dev
fix some content are ignore by localization
2023-08-04 11:33:34 +03:00
daxijiu 695300929a Merge pull request #1 from daxijiu/fix-some-content-are-ignore-by-localization
fix some content  are ignore by localization
2023-08-04 16:12:41 +08:00
daxijiu 82b415c9c1 fix some content are ignore by localization
in setting "Face restoration model" and "Select which Real-ESRGAN models" and in extras "upscaler 1 & 2" are ignore by localization
2023-08-04 16:03:49 +08:00
catboxanon d89a915b74 Only enable hr fix if hr scale or upscale in infotext on paste 2023-08-04 04:03:37 -04:00
catboxanon ac8dfd9386 Toggle extras checkbox for infotext paste 2023-08-04 03:52:22 -04:00
Kohaku-Blueleaf 1f6bfdea80 move the modified decode into smapler_common 2023-08-04 14:38:52 +08:00
Kohaku-Blueleaf 70e66e81e5 Merge branch 'dev' into efficient-vae-methods 2023-08-04 14:38:16 +08:00
AUTOMATIC1111 f0c1063a70 resolve some of circular import issues for kohaku 2023-08-04 09:13:46 +03:00
AUTOMATIC1111 09165916fa Merge pull request #12297 from AUTOMATIC1111/sort-VAE
sort VAE
2023-08-04 08:53:47 +03:00
Kohaku-Blueleaf c134a48016 Fix code style 2023-08-04 13:40:20 +08:00
Kohaku-Blueleaf 75336dfc84 add TAESD for i2i and t2i 2023-08-04 13:38:52 +08:00
AUTOMATIC1111 3f9e09a615 Merge pull request #11831 from wzgrx/dev
Dev The requirements.txt installation version is required to be updated. I have tested the latest version and SD can be used normally
2023-08-04 08:12:33 +03:00
AUTOMATIC1111 01486f6896 Merge pull request #12300 from catboxanon/dev
Add exponential scheduler variant to sampler selection for DPM-Solver++(2M) SDE sampler
2023-08-04 08:11:13 +03:00
AUTOMATIC1111 56c3f94ba3 Merge branch 'dev' into dev 2023-08-04 08:05:21 +03:00
AUTOMATIC1111 073c0ebba3 add gradio version warning 2023-08-04 08:04:23 +03:00
AUTOMATIC1111 362789a379 gradio 3.39 2023-08-04 08:04:23 +03:00
w-e-w 7f1d087cba sort VAE 2023-08-04 14:01:22 +09:00
catboxanon 3bd2c68eb4 Add exponential scheduler for DPM-Solver++(2M) SDE
Better quality results than Karras.
Related discussion: https://gist.github.com/crowsonkb/3ed16fba35c73ece7cf4b9a2095f2b78
2023-08-04 00:51:49 -04:00
AUTOMATIC1111 71efc5bda8 Merge pull request #12298 from catboxanon/xyz-sampler
XYZ: Support hires sampler
2023-08-04 07:47:35 +03:00
w-e-w f4d9297127 use samplers_for_img2img for Hires sampler 2023-08-04 13:27:25 +09:00
AUTOMATIC1111 220e298417 Merge pull request #12294 from AUTOMATIC1111/cmd_arg-disable-extensions
add cmd_arg --disable-extensions all extra
2023-08-04 07:26:34 +03:00
catboxanon f7813fad1c XYZ: Use default label format for hires sampler
If both sampler and hires sampler are used this makes the distinction more clear.
2023-08-04 00:19:30 -04:00
catboxanon 8b37734244 XYZ: Support hires sampler, cleanup 2023-08-04 00:10:14 -04:00
w-e-w bbfff771d7 --disable-all-extensions --disable-extra-extensions 2023-08-04 12:44:52 +09:00
AnyISalIn 24f21583cd fix: prevent cache model.state_dict() after model hijack
Signed-off-by: AnyISalIn <anyisalin@gmail.com>
2023-08-04 11:43:27 +08:00
AUTOMATIC1111 09c1be9674 put some of the shared functionality into toprow
write a comment for the toprow
2023-08-03 23:31:14 +03:00
AUTOMATIC1111 af528552d6 fix linter issues 2023-08-03 23:31:14 +03:00
AUTOMATIC1111 20549a50cb add style editor dialog
rework toprow for img2img and txt2img to use a class with fields
fix the console error when editing checkpoint user metadata
2023-08-03 23:31:13 +03:00
AUTOMATIC1111 8e840e1519 Merge pull request #12269 from AUTOMATIC1111/TI-Hash-fix
fix missing TI hash
2023-08-03 12:56:19 +03:00
w-e-w f56a309432 fix missing TI hash 2023-08-03 18:46:49 +09:00
AUTOMATIC1111 0904df84e2 minor performance improvements for philox 2023-08-03 07:53:03 +03:00
AUTOMATIC1111 fca42949a3 rework torchsde._brownian.brownian_interval replacement to use device.randn_local and respect the NV setting. 2023-08-03 07:18:55 +03:00
Splendide Imaginarius a1825ee741 Make StableDiffusionProcessingImg2Img.mask_blur a property
Fixes breakage when mask_blur is set after construction.

See https://github.com/Coyote-A/ultimate-upscale-for-automatic1111/issues/111#issuecomment-1652091424

Thanks to Алексей Трофимов and eunnone for reporting the issue.
2023-08-03 02:07:00 +00:00
AUTOMATIC1111 84b6fcd02c add NV option for Random number generator source setting, which allows to generate same pictures on CPU/AMD/Mac as on NVidia videocards. 2023-08-03 00:00:23 +03:00
AUTOMATIC1111 ccb9233934 add yet another torch_gc to reclaim some of VRAM after the initial stage of img2img 2023-08-02 18:53:09 +03:00
AUTOMATIC1111 10ff071e33 update doggettx cross attention optimization to not use an unreasonable amount of memory in some edge cases -- suggestion by MorkTheOrk 2023-08-02 18:37:16 +03:00
AUTOMATIC1111 390bffa81b repair merge error 2023-08-01 17:13:15 +03:00
AUTOMATIC1111 0c9b1e7969 Merge branch 'dev' into multiple_loaded_models 2023-08-01 16:55:55 +03:00
AUTOMATIC1111 6a0d498c8e support tooltip kwarg for gradio elements 2023-08-01 12:50:23 +03:00
AUTOMATIC1111 401ba1b879 XYZ plot do not fail if an exception occurs 2023-08-01 09:22:53 +03:00
AUTOMATIC1111 07be13caa3 add metadata to checkpoint merger 2023-08-01 08:27:54 +03:00
AUTOMATIC1111 6d3a0c9506 move checkpoint merger UI to its own file 2023-08-01 07:43:43 +03:00
AUTOMATIC1111 0042954490 Split history: mv temp modules/ui.py 2023-08-01 07:15:16 +03:00
AUTOMATIC1111 8a4149accc Split history: merge 2023-08-01 07:15:16 +03:00
AUTOMATIC1111 b98fa1c397 Split history: mv modules/ui.py temp 2023-08-01 07:15:15 +03:00
AUTOMATIC1111 c6b826d796 Split history: mv modules/ui.py modules/ui_checkpoint_merger.py 2023-08-01 07:15:15 +03:00
AUTOMATIC1111 2860c3be3e add filename to to the table in user metadata editor 2023-08-01 07:10:42 +03:00
AUTOMATIC1111 4b43480fe8 show metadata for SD checkpoints in the extra networks UI 2023-08-01 07:08:11 +03:00
Jabasukuriputo Wang 8b036d8a82 fix 2023-08-01 11:26:59 +08:00
Jabasukuriputo Wang c46525b70b fix exception 2023-08-01 11:26:17 +08:00
Jabasukuriputo Wang 955542a654 also check on rev-parse 2023-08-01 11:24:54 +08:00
Jabasukuriputo Wang 2f1d5b6b04 attempt to fix workspace status when doing git clone 2023-08-01 11:20:59 +08:00
AUTOMATIC1111 151b8ed3a6 repair PLMS 2023-08-01 00:38:34 +03:00
AUTOMATIC1111 b235022c61 option to keep multiple models in memory 2023-08-01 00:24:48 +03:00
AUTOMATIC1111 c10633f93a fix memory leak when generation fails 2023-07-31 22:03:05 +03:00
AUTOMATIC1111 0d577aba26 Merge pull request #12207 from akx/local-storage-guard
Don't crash if out of local storage quota
2023-07-31 14:00:49 +03:00
AUTOMATIC1111 c09bc2c608 fix "clamp_scalar_cpu" not implemented for 'Half' 2023-07-31 13:20:26 +03:00
Aarni Koskela fb87a05fe8 Don't crash if out of local storage quota
Fixes #12206 (works around it)
2023-07-31 11:23:26 +02:00
AUTOMATIC1111 4d9b096663 additional memory improvements when switching between models of different types 2023-07-31 10:43:31 +03:00
AUTOMATIC1111 29d7e31d89 repair AttributeError: 'NoneType' object has no attribute 'conditioning_key' 2023-07-31 10:43:26 +03:00
AUTOMATIC1111 dca121e903 set the field to None instead 2023-07-31 09:13:07 +03:00
AUTOMATIC1111 0af4127fd1 delete the field that is preventing the model from being unloaded and is causing increased RAM usage 2023-07-30 19:36:24 +03:00
AUTOMATIC1111 a1eb49627a Merge pull request #12177 from rubberbaron/prompt-parse-whitespace-around-numbers
add support for whitespace after the number in constructions like [fo…
2023-07-30 17:23:19 +03:00
AUTOMATIC1111 02038036ff make it so that VAE NaNs autodetection also works during first pass of hires fix 2023-07-30 16:16:31 +03:00
AUTOMATIC1111 f60d9fbe29 Merge pull request #12178 from rubberbaron/xyz-grid-remove-dir
xyz_grid: in the axis labels, remove pathnames from model filenames
2023-07-30 15:32:34 +03:00
AUTOMATIC1111 cc53db6652 this time for sure 2023-07-30 15:30:33 +03:00
AUTOMATIC1111 a64fbe8928 make it possible to use checkpoints of different types (SD1, SDXL) in first and second pass of hires fix 2023-07-30 15:12:09 +03:00
AUTOMATIC1111 eec540b227 repair non-latent upscaling broken for SDXL 2023-07-30 15:04:12 +03:00
AUTOMATIC1111 77761e7bad linter 2023-07-30 14:10:33 +03:00
AUTOMATIC1111 40cd59207b make it work with SDXL 2023-07-30 14:10:26 +03:00
AUTOMATIC1111 3bca90b249 hires fix checkpoint selection 2023-07-30 13:48:27 +03:00
Robert Barron 085c903229 xyz_grid: in the legend, remove pathnames from model filenames 2023-07-30 03:35:32 -07:00
Robert Barron 8a40e30d08 add support for whitespace after the number in constructions like [foo:bar: 0.5 ] and (foo : 0.5 ) 2023-07-30 01:46:25 -07:00
AUTOMATIC1111 63a8861c19 Merge pull request #12164 from AUTOMATIC1111/rework-img2img-batch-image-save
Rework img2img batch image save
2023-07-30 11:45:33 +03:00
w-e-w fb44838176 strip output_dir 2023-07-30 14:47:24 +09:00
w-e-w 53ccdefc01 don't override default if output_dir is blank 2023-07-30 00:34:04 +09:00
w-e-w 9857537053 lint 2023-07-30 00:06:25 +09:00
w-e-w b95a41ad72 rework img2img batch image save 2023-07-30 00:02:31 +09:00
AUTOMATIC1111 6f0abbb71a textual inversion support for SDXL 2023-07-29 15:15:06 +03:00
AUTOMATIC1111 4ca9f70b59 Merge pull request #11950 from AnyISalIn/dev
feat: add refresh vae api
2023-07-29 09:37:02 +03:00
AUTOMATIC1111 e18fc29bbf put the entry for the sampler in the readme section in order of addition 2023-07-29 08:40:43 +03:00
AUTOMATIC1111 79d6e9cd32 some stylistic changes for the sampler code 2023-07-29 08:38:00 +03:00
AUTOMATIC1111 aefe1325df split the new sampler into a different file 2023-07-29 08:11:59 +03:00
AUTOMATIC1111 11dc92dc0a Split history: mv temp modules/sd_samplers_kdiffusion.py 2023-07-29 08:06:04 +03:00
AUTOMATIC1111 bdeb44aeb2 Split history: merge 2023-07-29 08:06:03 +03:00
AUTOMATIC1111 e1323fc1b7 Split history: mv modules/sd_samplers_kdiffusion.py temp 2023-07-29 08:06:03 +03:00
AUTOMATIC1111 3ac950248d Split history: mv modules/sd_samplers_kdiffusion.py modules/sd_samplers_extra.py 2023-07-29 08:06:03 +03:00
AUTOMATIC1111 bef40851af Merge pull request #11850 from lambertae/restart_sampling
Restart sampling
2023-07-29 08:03:32 +03:00
AUTOMATIC1111 9a52a30d2f Merge pull request #12107 from JetVarimax/patch-2
Fix typo
2023-07-29 07:49:22 +03:00
AUTOMATIC1111 fc163218c4 Merge pull request #12120 from DiabolicDiabetic/patch-2
IMG2IMG TIF batch fix img2img.py
2023-07-29 07:48:44 +03:00
AUTOMATIC1111 19ac0adf03 Merge pull request #12124 from Xstephen/master
Add total_tqdm clear in the end of txt2img & img2img api.
2023-07-29 07:44:00 +03:00
AUTOMATIC1111 ac81c1dd1f Merge pull request #11958 from AUTOMATIC1111/conserve-ram
Use less RAM when creating models
2023-07-29 07:43:04 +03:00
caoxipeng 6cc5a886ae Add total_tqdm clear in the end of txt2img & img2img api. 2023-07-28 11:40:10 +08:00
DiabolicDiabetic 9cbf3461f7 IMG2IMG TIF batch fix img2img.py
IMG2IMG batch tab wouldn't process tif images
2023-07-27 20:15:50 -05:00
AUTOMATIC1111 25004d4eee Merge branch 'master' into dev 2023-07-27 09:03:44 +03:00
AUTOMATIC1111 56236dfd3f Merge branch 'master' into release_candidate 2023-07-27 09:03:26 +03:00
AUTOMATIC1111 68f336bd99 Merge branch 'release_candidate' 2023-07-27 09:02:22 +03:00
AUTOMATIC1111 50973ec77c update the changelog 2023-07-27 09:02:02 +03:00
AUTOMATIC1111 f82e08cf45 update lora extension to work with python 3.8 2023-07-27 09:00:59 +03:00
AUTOMATIC1111 91a131aa6c update lora extension to work with python 3.8 2023-07-27 09:00:47 +03:00
AUTOMATIC1111 3039925b27 update readme 2023-07-26 15:19:02 +03:00
AUTOMATIC1111 8220cf37da Merge pull request #12020 from Littleor/dev
Fix the error in rendering the name and description in the extra network UI.
2023-07-26 15:18:04 +03:00
AUTOMATIC1111 0cb9711a15 Merge pull request #12020 from Littleor/dev
Fix the error in rendering the name and description in the extra network UI.
2023-07-26 15:17:37 +03:00
AUTOMATIC1111 055461ae41 repair SDXL 2023-07-26 15:08:12 +03:00
AUTOMATIC1111 89e6dfff71 repair SDXL 2023-07-26 15:07:56 +03:00
AUTOMATIC1111 5c8f91b229 fix autograd which i broke for no good reason when implementing SDXL 2023-07-26 13:04:10 +03:00
AUTOMATIC1111 8284ebd94c fix autograd which i broke for no good reason when implementing SDXL 2023-07-26 13:03:52 +03:00
Littleor 187323a606 fix: extra network ui description allow HTML tags 2023-07-26 17:23:57 +08:00
AUTOMATIC1111 6b877c35da Merge pull request #12032 from AUTOMATIC1111/fix-api-get-options-sd_model_checkpoint
api /sdapi/v1/options use "Any" type when default type is None
2023-07-26 11:52:58 +03:00
AUTOMATIC1111 deed8439d5 Merge pull request #12032 from AUTOMATIC1111/fix-api-get-options-sd_model_checkpoint
api /sdapi/v1/options use "Any" type when default type is None
2023-07-26 11:52:42 +03:00
w-e-w 6305632493 use "Any" type when type is None 2023-07-26 17:20:04 +09:00
AUTOMATIC1111 eb6d330bb7 delete scale checker script due to user demand 2023-07-26 09:20:02 +03:00
AUTOMATIC1111 246d1f1f70 delete scale checker script due to user demand 2023-07-26 09:19:46 +03:00
AUTOMATIC1111 5360ae2cc5 Merge pull request #12023 from AUTOMATIC1111/create_infotext_fix
Create infotext fix
2023-07-26 08:10:21 +03:00
AUTOMATIC1111 e16eb3d0cb Merge pull request #12024 from AUTOMATIC1111/fix-check-for-updates-status-always-unknown-
fix check for updates status always "unknown"
2023-07-26 08:10:12 +03:00
AUTOMATIC1111 ca6f90dc6d Merge pull request #12023 from AUTOMATIC1111/create_infotext_fix
Create infotext fix
2023-07-26 08:07:07 +03:00
AUTOMATIC1111 835a7dbf0e simplify PostprocessBatchListArgs 2023-07-26 07:49:57 +03:00
AUTOMATIC1111 225eb1b1a0 Merge pull request #12024 from AUTOMATIC1111/fix-check-for-updates-status-always-unknown-
fix check for updates status always "unknown"
2023-07-26 07:45:48 +03:00
w-e-w b8a903efbe fix check for updates status always "unknown" 2023-07-26 13:43:38 +09:00
AUTOMATIC1111 7c22bbd3ad attempt 2 2023-07-26 07:04:07 +03:00
AUTOMATIC1111 13e371af73 doc update 2023-07-26 06:37:13 +03:00
AUTOMATIC1111 ae36e0899f alternative solution for infotext issue 2023-07-26 06:36:06 +03:00
Littleor b73c405013 fix: error rendering name and description in extra network ui 2023-07-26 11:02:34 +08:00
lambertae 8de6d3ff77 fix progress bar & torchHijack 2023-07-25 22:35:43 -04:00
JetVarimax fd43558586 Fix typo 2023-07-25 20:31:15 +01:00
AUTOMATIC1111 99ef3b6c52 update readme 2023-07-25 16:31:01 +03:00
AUTOMATIC1111 65b6f8d3d5 fix for #11963 2023-07-25 16:20:55 +03:00
AUTOMATIC1111 b57a816038 Merge pull request #11963 from catboxanon/fix/lora-te
Fix parsing text encoder blocks in some LoRAs
2023-07-25 16:20:52 +03:00
AUTOMATIC1111 11f996a096 Merge pull request #11979 from AUTOMATIC1111/catch-exception-for-non-git-extensions
catch exception for non git extensions
2023-07-25 16:20:49 +03:00
AUTOMATIC1111 ce0aab3643 Merge pull request #11984 from AUTOMATIC1111/api-only-subpath-(root_path)
api only subpath (rootpath)
2023-07-25 16:20:46 +03:00
AUTOMATIC1111 c251e8db8d Merge pull request #11957 from ljleb/pp-batch-list
Add postprocess_batch_list script callback
2023-07-25 16:20:33 +03:00
AUTOMATIC1111 284822323a restyle Startup profile for black users 2023-07-25 16:20:16 +03:00
AUTOMATIC1111 1f59be5188 Merge pull request #11926 from wfjsw/fix-env-get-1
fix 11291#issuecomment-1646547908
2023-07-25 16:20:07 +03:00
AUTOMATIC1111 cad87bf4e3 Merge pull request #11927 from ljleb/fix-AND
Fix composable diffusion weight parsing
2023-07-25 16:20:01 +03:00
AUTOMATIC1111 704628b903 Merge pull request #11923 from AnyISalIn/dev
[bug] If txt2img/img2img raises an exception, finally call state.end()
2023-07-25 16:19:36 +03:00
AUTOMATIC1111 636ff513b0 Merge pull request #11920 from wfjsw/typo-fix-1
typo fix
2023-07-25 16:19:22 +03:00
AUTOMATIC1111 51206edb62 Merge pull request #11921 from wfjsw/prepend-pythonpath
prepend the pythonpath instead of overriding it
2023-07-25 16:19:08 +03:00
AUTOMATIC1111 c5934fb6e3 Merge pull request #11878 from Bourne-M/patch-1
【bug】reload altclip model error
2023-07-25 16:18:55 +03:00
AUTOMATIC1111 d0bf509fa1 fix for #11963 2023-07-25 16:18:10 +03:00
AUTOMATIC1111 d6ec08ba89 Merge pull request #11963 from catboxanon/fix/lora-te
Fix parsing text encoder blocks in some LoRAs
2023-07-25 16:17:41 +03:00
AUTOMATIC1111 65bf3ba260 Merge pull request #11979 from AUTOMATIC1111/catch-exception-for-non-git-extensions
catch exception for non git extensions
2023-07-25 15:23:35 +03:00
AUTOMATIC1111 bed598ce7f Merge pull request #11984 from AUTOMATIC1111/api-only-subpath-(root_path)
api only subpath (rootpath)
2023-07-25 15:19:10 +03:00
w-e-w b1a16a298c api only subpath (rootpath)
Co-Authored-By: 陈杰 <pythias@gmail.com>
2023-07-25 20:51:27 +09:00
w-e-w fee593a07f catch exception for non git extensions 2023-07-25 20:01:10 +09:00
AUTOMATIC1111 fc8e23dec5 Merge branch 'master' into dev 2023-07-25 08:20:42 +03:00
AUTOMATIC1111 a3ddf464a2 Merge branch 'release_candidate' 2023-07-25 08:18:02 +03:00
catboxanon a68f469030 Fix to parse TE in some LoRAs 2023-07-24 17:54:59 -04:00
AUTOMATIC1111 f7c0a963f1 Merge pull request #11957 from ljleb/pp-batch-list
Add postprocess_batch_list script callback
2023-07-24 23:18:16 +03:00
ljleb 5b06607476 simplify 2023-07-24 15:43:06 -04:00
ljleb 6b68b59032 use local vars 2023-07-24 15:38:52 -04:00
AUTOMATIC1111 0a89cd1a58 Use less RAM when creating models 2023-07-24 22:08:08 +03:00
ljleb ca45ff1ae6 add postprocess_batch_list callback 2023-07-24 13:52:24 -04:00
AnyISalIn 1cbfafafd2 feat: add refresh vae api
Signed-off-by: AnyISalIn <anyisalin@gmail.com>
2023-07-24 19:45:08 +08:00
AUTOMATIC1111 f451994053 Merge branch 'release_candidate' into dev 2023-07-24 11:58:15 +03:00
AUTOMATIC1111 2c11e9009e repair --medvram for SD2.x too after SDXL update 2023-07-24 11:57:59 +03:00
Jabasukuriputo Wang f2a4073aea Merge branch 'dev' into ext-inst-pbar 2023-07-23 23:32:13 +08:00
AUTOMATIC1111 ec83db8978 restyle Startup profile for black users 2023-07-22 17:15:38 +03:00
AUTOMATIC1111 a8d4213317 add --log-startup option to print detailed startup progress 2023-07-22 17:15:38 +03:00
Jabasukuriputo Wang 9421c11346 Merge branch 'dev' into ext-inst-pbar 2023-07-22 21:58:59 +08:00
AUTOMATIC1111 0615b3c532 Merge pull request #11926 from wfjsw/fix-env-get-1
fix 11291#issuecomment-1646547908
2023-07-22 16:37:03 +03:00
AUTOMATIC1111 2d635c0192 Merge pull request #11927 from ljleb/fix-AND
Fix composable diffusion weight parsing
2023-07-22 16:36:40 +03:00
ljleb 88a3e1d306 fix AND linebreaks 2023-07-22 07:40:30 -04:00
ljleb 0674fabd0d fix AND linebreaks 2023-07-22 07:10:20 -04:00
AUTOMATIC1111 c76a30af41 more info for startup timings 2023-07-22 13:49:29 +03:00
Jabasukuriputo Wang 3c26734d60 nop 2023-07-22 18:33:59 +08:00
Jabasukuriputo Wang 2a7e34fe79 fix https://github.com/AUTOMATIC1111/stable-diffusion-webui/pull/11921#issuecomment-1646547908 2023-07-22 18:09:00 +08:00
Jabasukuriputo Wang b2f0040da7 fix tqdm not found on new instance 2023-07-22 17:51:15 +08:00
Jabasukuriputo Wang 7afe7375e1 display a progressbar for extension installer 2023-07-22 17:46:50 +08:00
AUTOMATIC1111 90eb731ff1 start timer early anyway 2023-07-22 12:21:05 +03:00
AUTOMATIC1111 491d42bb1c Merge pull request #11856 from wfjsw/move-start-timer
Only start timer when actually starting
2023-07-22 12:19:36 +03:00
AUTOMATIC1111 45c0f58dc6 Merge pull request #11923 from AnyISalIn/dev
[bug] If txt2img/img2img raises an exception, finally call state.end()
2023-07-22 07:03:21 +03:00
AnyISalIn 1fe2dcaa2a [bug] If txt2img/img2img raises an exception, finally call state.end()
Signed-off-by: AnyISalIn <anyisalin@gmail.com>
2023-07-22 10:00:27 +08:00
AUTOMATIC1111 075934a944 Merge pull request #11920 from wfjsw/typo-fix-1
typo fix
2023-07-21 18:01:20 +03:00
AUTOMATIC1111 ed4d7912c7 Merge pull request #11921 from wfjsw/prepend-pythonpath
prepend the pythonpath instead of overriding it
2023-07-21 18:00:03 +03:00
Jabasukuriputo Wang 16eddc622e prepend the pythonpath instead of overriding it 2023-07-21 22:00:03 +08:00
w-e-w bc91f15ed3 typo fix 2023-07-21 22:56:41 +09:00
Jabasukuriputo Wang 118529a6dc typo fix 2023-07-21 21:49:33 +08:00
Jabasukuriputo Wang 33694baea1 avoid importing timer when it is not strictly needed 2023-07-21 17:15:44 +08:00
lambertae f873890298 new restart scheme 2023-07-20 21:27:43 -04:00
lambertae 128d59c9cc fix ruff 2023-07-20 20:36:40 -04:00
lambertae 2f57a559ac allow choise of restart_list & use karras from kdiffusion 2023-07-20 20:34:41 -04:00
AUTOMATIC1111 2f98f7c924 Merge branch 'release_candidate' into dev 2023-07-20 19:16:55 +03:00
AUTOMATIC1111 1f26815dd3 Merge pull request #11898 from janekm/janekm-patch-1
Update sd_models_xl.py
2023-07-20 19:16:40 +03:00
Janek Mann 8218f6cd37 Update sd_models_xl.py
Fix width/height not getting fed into the conditioning
2023-07-20 16:22:52 +01:00
lambertae 6233268964 add credit 2023-07-20 02:27:28 -04:00
lambertae ddbf4a73f5 restart-sampler with correct steps 2023-07-20 02:24:18 -04:00
AUTOMATIC1111 4bf64976c1 Merge branch 'release_candidate' into dev 2023-07-19 20:23:48 +03:00
AUTOMATIC1111 23c947ab03 automatically switch to 32-bit float VAE if the generated picture has NaNs. 2023-07-19 20:23:30 +03:00
AUTOMATIC1111 5677296d1b Merge pull request #11878 from Bourne-M/patch-1
【bug】reload altclip model error
2023-07-19 16:26:12 +03:00
AUTOMATIC1111 0e47c36a28 Merge branch 'dev' into release_candidate 2023-07-19 15:50:49 +03:00
AUTOMATIC1111 4334d25978 bugfix: model name was added together with directory name to infotext and to [model_name] filename pattern 2023-07-19 15:49:54 +03:00
AUTOMATIC1111 05ccb4d0e3 bugfix: model name was added together with directory name to infotext and to [model_name] filename pattern 2023-07-19 15:49:31 +03:00
yfzhou cb75734896 【bug】reload altclip model error
When using BertSeriesModelWithTransformation as the cond_stage_model, the undo_hijack should be performed using the FrozenXLMREmbedderWithCustomWords type; otherwise, it will result in a failed model reload.
2023-07-19 17:53:28 +08:00
AUTOMATIC1111 d5c850aab5 Merge pull request #11866 from kopyl/allow-no-venv-install
Make possible to install web ui without venv with venv_dir=- env variable for Linux
2023-07-19 08:00:05 +03:00
AUTOMATIC1111 0a334b447f Merge branch 'dev' into allow-no-venv-install 2023-07-19 07:59:39 +03:00
AUTOMATIC1111 c2b9754857 Merge pull request #11869 from AUTOMATIC1111/missing-p-save_image-before-highres-fix
Fix missing p save_image before-highres-fix
2023-07-19 07:58:34 +03:00
Jabasukuriputo Wang fc3bdf8c11 Merge branch 'dev' into move-start-timer 2023-07-19 10:33:31 +08:00
w-e-w c8b55f29e2 missing p save_image before-highres-fix 2023-07-19 08:27:19 +09:00
kopyl 6094310704 improve var naming 2023-07-19 01:48:21 +03:00
kopyl 0c4ca5f43e Replace argument with env variable 2023-07-19 01:47:39 +03:00
AUTOMATIC1111 b010eea520 fix incorrect multiplier for Loras 2023-07-19 00:41:00 +03:00
AUTOMATIC1111 0fae47e974 Merge pull request #11867 from AUTOMATIC1111/add-dropdown-extra_sort_order-lable
add dropdown extra_sort_order lable
2023-07-18 23:23:26 +03:00
w-e-w c278e60131 add dropdown extra_sort_order lable 2023-07-19 04:58:30 +09:00
kopyl 2b42f73e3d Make possible to install web ui without venv with --novenv flag
When passing `--novenv` flag to webui.sh it can skip venv.
Might be useful for installing in Docker since messing with venv in Docker might be a bit complicated.

Example usage:
`webui.sh --novenv`

Hope this gets approved and pushed into future versions of Web UI
2023-07-18 22:43:18 +03:00
AUTOMATIC1111 136c8859a4 add backwards compatibility --lyco-dir-backcompat option, use that for LyCORIS directory instead of hardcoded value
prevent running preload.py for disabled extensions
2023-07-18 20:11:30 +03:00
AUTOMATIC1111 eb7c9b58fc Merge branch 'dev' into release_candidate 2023-07-18 18:20:22 +03:00
AUTOMATIC1111 7f7db1700b linter fix 2023-07-18 18:16:23 +03:00
AUTOMATIC1111 b270ded268 fix the issue with /sdapi/v1/options failing (this time for sure!)
fix automated tests downloading CLIP model
2023-07-18 18:10:04 +03:00
AUTOMATIC1111 be16d274f8 changelog for 1.5.0 2023-07-18 17:44:56 +03:00
AUTOMATIC1111 66c5f1bb15 return sd_model_checkpoint to None 2023-07-18 17:41:37 +03:00
AUTOMATIC1111 4b5a63aa11 add a bit more metadata info for the lora user metadata page 2023-07-18 17:32:46 +03:00
AUTOMATIC1111 ed82f1c5f1 lint 2023-07-18 15:55:23 +03:00
wfjsw 3c570421d3 move start timer 2023-07-18 19:00:16 +08:00
AUTOMATIC1111 420cc8f68e also make None a valid option for options API for #11854 2023-07-18 11:48:40 +03:00
AUTOMATIC1111 6be5ccb530 Merge pull request #11854 from leon0707/fix-11805
Fix #11805
2023-07-18 11:48:01 +03:00
Leon Feng a3730bd9be Merge branch 'dev' into fix-11805 2023-07-18 04:24:14 -04:00
Leon Feng d6668347c8 remove duplicate 2023-07-18 04:19:58 -04:00
AUTOMATIC1111 871b8687a8 Merge pull request #11846 from brkirch/sd-xl-upcast-sampling-fix
Add support for using `--upcast-sampling` with SD XL
2023-07-18 08:08:19 +03:00
AUTOMATIC1111 20c41364cc Merge pull request #11843 from KohakuBlueleaf/fix-lyco-support
Fix wrong key name in lokr module
2023-07-18 08:05:28 +03:00
lambertae 7bb0fbed13 code styling 2023-07-18 01:02:04 -04:00
lambertae 37e048a7e2 fix floating error 2023-07-18 00:55:02 -04:00
brkirch f0e2098f1a Add support for --upcast-sampling with SD XL 2023-07-18 00:39:50 -04:00
lambertae 15a94d6cf7 remove useless header 2023-07-18 00:39:26 -04:00
lambertae 40a18d38a8 add restart sampler 2023-07-18 00:32:01 -04:00
Kohaku-Blueleaf 3d31caf4a5 use "is not None" for Tensor 2023-07-18 10:45:42 +08:00
Kohaku-Blueleaf 17e14ed2d9 Fix wrong key name in lokr module 2023-07-18 10:23:41 +08:00
AUTOMATIC1111 a99d5708e6 skip installing packages with pip if theyare already installed
record time it took to launch
2023-07-17 20:10:24 +03:00
AUTOMATIC1111 699108bfbb hide cards for networks of incompatible stable diffusion version in Lora extra networks interface 2023-07-17 18:56:22 +03:00
AUTOMATIC1111 f97e35929b Merge pull request #11824 from AUTOMATIC1111/XYZ-always_discard_next_to_last_sigma
XYZ always_discard_next_to_last_sigma
2023-07-17 15:56:34 +03:00
AUTOMATIC1111 2164578738 Merge pull request #11821 from AUTOMATIC1111/lora_lyco
lora extension rework to include other types of networks
2023-07-17 15:51:59 +03:00
wzgrx 952effa8b1 Update requirements_versions.txt 2023-07-17 18:50:29 +08:00
wzgrx 0dcf6436a8 Update requirements.txt 2023-07-17 18:49:53 +08:00
AUTOMATIC1111 05d23c7837 move generate button below the picture for mobile clients 2023-07-17 11:44:29 +03:00
AUTOMATIC1111 95c5c4d64e fix tabs height on small screens 2023-07-17 11:18:08 +03:00
w-e-w 543ea5730b fix extra search button 2023-07-17 16:35:41 +09:00
AUTOMATIC1111 35510f7529 add alias to lyco network
read networks from LyCORIS dir if it exists
add credits
2023-07-17 10:06:02 +03:00
AUTOMATIC1111 9251ae3bc7 delay writing cache to prevent writing the same thing over and over 2023-07-17 09:29:36 +03:00
AUTOMATIC1111 2e07a8ae6b some backwards compatibility
linter
2023-07-17 09:05:18 +03:00
AUTOMATIC1111 238adeaffb support specifying te and unet weights separately
update lora code
support full module
2023-07-17 09:00:47 +03:00
w-e-w 8941297ceb lowercase 2023-07-17 12:45:38 +09:00
w-e-w c03856bfdf reversible boolean_choice order 2023-07-17 12:45:10 +09:00
w-e-w 7870937c77 XYZ always_discard_next_to_last_sigma
Co-authored-by: Franck Mahon <franck.mahon@gmail.com>
2023-07-17 12:25:29 +09:00
AUTOMATIC1111 46466f09d0 Lokr support 2023-07-17 00:29:07 +03:00
AUTOMATIC1111 58c3df32f3 IA3 support 2023-07-17 00:12:18 +03:00
AUTOMATIC1111 ef5dac7786 fix 2023-07-17 00:01:17 +03:00
AUTOMATIC1111 c2297b89d3 linter 2023-07-16 23:14:57 +03:00
AUTOMATIC1111 b75b004fe6 lora extension rework to include other types of networks 2023-07-16 23:13:55 +03:00
AUTOMATIC1111 643836007f more tweaking for cards section height 2023-07-16 14:46:05 +03:00
AUTOMATIC1111 7d26c479ee changelog for future 1.5.0 2023-07-16 14:39:47 +03:00
AUTOMATIC1111 24bad5dc7b change extra networks list to have constant height and scrolling 2023-07-16 13:59:15 +03:00
AUTOMATIC1111 67ea4eabc3 fix cache loading wrong entries from old cache files 2023-07-16 13:46:33 +03:00
AUTOMATIC1111 ace0c78373 Merge pull request #11669 from gitama2023/patch-1
Added a prompt for users using poor scaling
2023-07-16 13:12:18 +03:00
AUTOMATIC1111 570f42afd1 possible fix for FP16 VAE failing in img2img SDXL 2023-07-16 12:28:50 +03:00
AUTOMATIC1111 0198eaec45 Merge pull request #11757 from AUTOMATIC1111/sdxl
SD XL support
2023-07-16 12:04:53 +03:00
AUTOMATIC1111 57d61de25c fix unneded reload from disk 2023-07-16 11:52:29 +03:00
AUTOMATIC1111 5ef7590324 always show extra networks tabs in the UI 2023-07-16 11:38:59 +03:00
AUTOMATIC1111 9d3dd64fe9 minor restyling for extra networks 2023-07-16 10:44:04 +03:00
AUTOMATIC1111 690d56f3c1 nuke thumbs extra networks view mode (use settings tab to change width/height/scale to get thumbs) 2023-07-16 10:25:34 +03:00
AUTOMATIC1111 7b052eb70e add resolution calculation from buckets for lora user metadata page 2023-07-16 10:07:02 +03:00
AUTOMATIC1111 ccd97886da fix bogus metadata for extra networks appearing out of cache
fix description editing for checkpoint not immediately appearing on cards
2023-07-16 09:49:34 +03:00
AUTOMATIC1111 f71630edb3 Merge pull request #11794 from MarcusAdams/none-filename-token
Added [none] filename token.
2023-07-16 09:27:28 +03:00
AUTOMATIC1111 89c3e17c65 Merge pull request #11797 from wfjsw/ext-index-env
allow replacing extensions index with environment variable
2023-07-16 09:27:07 +03:00
AUTOMATIC1111 d2e64e26e5 Merge pull request #11802 from AUTOMATIC1111/warns-merge-into-master
Warns merge into master
2023-07-16 09:26:47 +03:00
AUTOMATIC1111 57e4422bdb Merge pull request #11806 from huchenlei/file_500
404 when thumb file not found
2023-07-16 09:26:07 +03:00
AUTOMATIC1111 47d9dd0240 speedup extra networks listing 2023-07-16 09:25:32 +03:00
AUTOMATIC1111 a1d6ada69a allow refreshing single card after editing user metadata instead of all cards 2023-07-16 08:38:23 +03:00
huchenlei 8c11b126e5 404 when thumb file not found 2023-07-15 23:51:18 -04:00
Leon Feng d380f939b5 Update shared.py 2023-07-15 23:31:59 -04:00
AUTOMATIC1111 efceed8c7f fix styles for dark people 2023-07-16 01:09:19 +03:00
AUTOMATIC1111 11f339733d add lora user metadata editor dialog inspired by MrKuenning's mockup from #7458 2023-07-16 00:57:45 +03:00
AUTOMATIC1111 5decbf184b eslint 2023-07-15 21:05:33 +03:00
AUTOMATIC1111 e5d3ae2bf4 user metadata system for custom networks 2023-07-15 20:39:10 +03:00
w-e-w 2970d712ee Warns merge into master 2023-07-16 02:29:20 +09:00
Jabasukuriputo Wang 2d9d53be21 allow replacing extensions index with environment variable 2023-07-15 17:09:51 +08:00
AUTOMATIC1111 c58cf73c80 remove "## " from changelog.md version 2023-07-15 09:33:21 +03:00
AUTOMATIC1111 0aa8d538e1 suppress printing TI embedding into console by default 2023-07-15 09:24:22 +03:00
AUTOMATIC1111 510e5fc8c6 cache git extension repo information 2023-07-15 09:20:43 +03:00
AUTOMATIC1111 2b1bae0d75 add textual inversion hashes to infotext 2023-07-15 08:41:22 +03:00
AUTOMATIC1111 127635409a add padding and identification to generation log section (Failed to find Loras, Used embeddings, etc...) 2023-07-15 08:07:25 +03:00
AUTOMATIC1111 b8bd8ce4cf disable rich exception output in console for API by default, use WEBUI_RICH_EXCEPTIONS env var to enable 2023-07-15 07:44:37 +03:00
AUTOMATIC1111 14cf434bc3 fix an issue in live previews that happens when you use SDXL with fp16 VAE 2023-07-15 07:33:16 +03:00
Marcus Adams 5d94088eac Added [none] filename token. 2023-07-14 21:52:00 -04:00
AUTOMATIC1111 95ee0cb188 restyle time taken/VRAM display 2023-07-14 22:51:58 +03:00
AUTOMATIC1111 5dee0fa1f8 add a message about unsupported samplers 2023-07-14 21:41:21 +03:00
AUTOMATIC1111 ac2d47ff4c add cheap VAE approximation coeffs for SDXL 2023-07-14 20:27:41 +03:00
AUTOMATIC1111 471a5a66b7 add more relevant fields to caching conds 2023-07-14 17:54:09 +03:00
AUTOMATIC1111 92a3236161 Merge branch 'dev' into sdxl 2023-07-14 10:12:48 +03:00
AUTOMATIC1111 9893d09b43 Merge pull request #11779 from AUTOMATIC1111/do-not-run-twice
Do not run git workflows twice for PRs from this repo
2023-07-14 10:09:20 +03:00
AUTOMATIC1111 62e3263467 edit names more 2023-07-14 10:07:08 +03:00
AUTOMATIC1111 9a3f35b028 repair medvram and lowvram 2023-07-14 09:56:01 +03:00
AUTOMATIC1111 714c920c20 do not run workflow items twice for PRs from this repo
update names
2023-07-14 09:47:44 +03:00
AUTOMATIC1111 abb948dab0 raise maximum Negative Guidance minimum sigma due to request in PR discussion 2023-07-14 09:28:01 +03:00
AUTOMATIC1111 b7dbeda0d9 linter 2023-07-14 09:19:08 +03:00
AUTOMATIC1111 6d8dcdefa0 initial SDXL refiner support 2023-07-14 09:16:01 +03:00
AUTOMATIC1111 073e30ee15 Merge pull request #11775 from AUTOMATIC1111/handles-model-hash-cache.json-error
handles model hash cache.json error
2023-07-14 00:18:17 +03:00
w-e-w a3db187e4f handles model hash cache.json error 2023-07-14 05:48:14 +09:00
AUTOMATIC1111 dc39061856 thank you linter 2023-07-13 21:19:41 +03:00
AUTOMATIC1111 6c5f83b19b add support for SDXL loras with te1/te2 modules 2023-07-13 21:17:50 +03:00
AUTOMATIC1111 ff73841c60 mute SDXL imports in the place there SDXL is imported for the first time instead of launch.py 2023-07-13 17:42:16 +03:00
AUTOMATIC1111 e16ebc917d repair --no-half for SDXL 2023-07-13 17:32:35 +03:00
AUTOMATIC1111 b8159d0919 add XL support for live previews: approx and TAESD 2023-07-13 17:24:54 +03:00
AUTOMATIC1111 6f23da603d fix broken img2img 2023-07-13 16:18:39 +03:00
AUTOMATIC1111 066d5edf17 Merge pull request #11730 from tangjicheng46/master
fix: timeout_keep_alive_handler error
2023-07-13 15:21:50 +03:00
AUTOMATIC1111 b7c5b30f14 Merge branch 'dev' into master 2023-07-13 15:21:39 +03:00
AUTOMATIC1111 262ec8ecda Merge pull request #11707 from wfjsw/revert-11244
Revert #11244
2023-07-13 14:51:04 +03:00
AUTOMATIC1111 ed0512c76f Merge pull request #11747 from AUTOMATIC1111/img2img-save
Save img2img batch with images.save_image()
2023-07-13 14:50:08 +03:00
AUTOMATIC1111 cc0a3cc492 Merge pull request #11750 from AUTOMATIC1111/quick-settings-textbox
Use submit and blur for quick settings textbox
2023-07-13 14:49:48 +03:00
AUTOMATIC1111 e93f582a78 Merge pull request #11748 from huaizong/fix/x/resize-less-than-two-pixel-error
fix: check fill size none zero when resize  (fixes #11425)
2023-07-13 14:48:19 +03:00
AUTOMATIC1111 76ebb175ca lora support 2023-07-13 12:59:31 +03:00
AUTOMATIC1111 594c8e7b26 fix CLIP doing the unneeded normalization
revert SD2.1 back to use the original repo
add SDXL's force_zero_embeddings to negative prompt
2023-07-13 11:35:52 +03:00
AUTOMATIC1111 21aec6f567 lint 2023-07-13 09:38:54 +03:00
AUTOMATIC1111 ac4ccfa136 get attention optimizations to work 2023-07-13 09:30:33 +03:00
AUTOMATIC1111 b717eb7e56 mute unneeded SDXL imports for tests too 2023-07-13 08:29:37 +03:00
AUTOMATIC1111 a04c955121 fix importlib.machinery issue on github's autotests #yolo 2023-07-13 00:12:25 +03:00
AUTOMATIC1111 5cf623c58e linter 2023-07-13 00:08:19 +03:00
AUTOMATIC1111 60397a7800 Merge branch 'dev' into sdxl 2023-07-12 23:53:26 +03:00
AUTOMATIC1111 da464a3fb3 SDXL support 2023-07-12 23:52:43 +03:00
w-e-w ea49bb0612 use submit blur for quick settings textbox 2023-07-12 23:30:22 +09:00
AUTOMATIC1111 e5ca987778 Merge pull request #11749 from akx/mps-gc-fix-2
Don't do MPS GC when there's a latent
2023-07-12 16:57:07 +03:00
Aarni Koskela 3d524fd3f1 Don't do MPS GC when there's a latent that could still be sampled 2023-07-12 15:17:30 +03:00
Aarni Koskela 8f6b24ce59 Add correct logger name 2023-07-12 15:16:42 +03:00
missionfloyd e0218c4f22 Merge branch 'dev' into img2img-save 2023-07-12 02:57:57 -06:00
王怀宗 6c0d5d1198 fix: check fill size none zero when resize (fixes #11425) 2023-07-12 16:51:50 +08:00
missionfloyd 3fee3c34f1 Save img2img batch with images.save_image() 2023-07-12 02:45:03 -06:00
AUTOMATIC1111 af081211ee getting SD2.1 to run on SDXL repo 2023-07-11 21:16:43 +03:00
AUTOMATIC1111 15adff3d6d Merge pull request #11733 from akx/brace-for-impact
Allow using alt in the prompt fields again
2023-07-11 15:25:59 +03:00
Aarni Koskela 3636c2c6ed Allow using alt in the prompt fields again 2023-07-11 15:05:20 +03:00
AUTOMATIC1111 799760ab95 Merge pull request #11722 from akx/mps-gc-fix
Fix MPS cache cleanup
2023-07-11 13:49:02 +03:00
Aarni Koskela b85fc7187d Fix MPS cache cleanup
Importing torch does not import torch.mps so the call failed.
2023-07-11 12:51:05 +03:00
TangJicheng 14501f56aa set timeout_keep_alive 2023-07-11 17:32:04 +09:00
TangJicheng 10d4e4ace2 add cmd_args: --timeout-keep-alive 2023-07-11 17:30:57 +09:00
AUTOMATIC1111 7b833291b3 Merge branch 'master' into dev 2023-07-11 06:25:50 +03:00
AUTOMATIC1111 f865d3e116 add changelog for 1.4.1 2023-07-11 06:23:52 +03:00
AUTOMATIC1111 910d4f61e5 Merge pull request #11720 from akx/closing
Use closing() with processing classes everywhere
2023-07-10 20:41:09 +03:00
AUTOMATIC1111 8d0078b6ef Merge pull request #11718 from tangjicheng46/master
fix: add queue lock for refresh-checkpoints
2023-07-10 20:40:58 +03:00
Aarni Koskela 44c27ebc73 Use closing() with processing classes everywhere
Follows up on #11569
2023-07-10 20:08:23 +03:00
tangjicheng 089a0022ae add queue lock for refresh-checkpoints 2023-07-10 23:10:14 +09:00
wfjsw 75f56406ce Revert Pull Request #11244
Revert "Add github mirror for the download extension"

This reverts commit 9ec2ba2d28.

Revert "Update code style"

This reverts commit de022c4c80.

Revert "Update call method"

This reverts commit e9bd18c57b.

Revert "move github proxy to settings, System page."

This reverts commit 4981c7d370.
2023-07-09 22:42:00 +08:00
AUTOMATIC1111 bcb6ad5fab Merge pull request #11696 from WuSiYu/feat_SWIN_torch_compile
feat: add option SWIN_torch_compile to accelerate SwinIR upscale
2023-07-08 23:05:17 +03:00
SiYu Wu 44d66daaad add option SWIN_torch_compile to accelerate SwinIR upscale using torch.compile() 2023-07-09 03:27:33 +08:00
AUTOMATIC1111 7dcdf81b84 Merge pull request #11595 from akx/alisases
Fix typo: checkpoint_alisases
2023-07-08 17:53:55 +03:00
AUTOMATIC1111 e3507a1be4 fix for eslint 2023-07-08 17:53:17 +03:00
AUTOMATIC1111 4981c7d370 move github proxy to settings, System page. 2023-07-08 17:52:03 +03:00
AUTOMATIC1111 ee642a2ff4 Merge pull request #11244 from MaiXiaoMeng/dev
Add github mirror for the download extension
2023-07-08 17:38:29 +03:00
AUTOMATIC1111 4da92281f6 pin version for torch for Navi3 according to comment from #11228 2023-07-08 17:29:28 +03:00
Aarni Koskela da468a585b Fix typo: checkpoint_alisases 2023-07-08 17:28:42 +03:00
AUTOMATIC1111 ed855783ed Merge pull request #11228 from Beinsezii/dev
WEBUI.SH Navi 3 Support
2023-07-08 17:28:04 +03:00
AUTOMATIC1111 386f78035b Merge pull request #11672 from nelsonjchen/patch-1
Add a link to an index-able/crawl-able wiki mirroring service of the wiki
2023-07-08 17:21:05 +03:00
AUTOMATIC1111 da8916f926 added torch.mps.empty_cache() to torch_gc()
changed a bunch of places that use torch.cuda.empty_cache() to use torch_gc() instead
2023-07-08 17:13:18 +03:00
AUTOMATIC1111 e161b5a025 rework #10436 to use shared.walk_files 2023-07-08 16:54:03 +03:00
AUTOMATIC1111 353031a014 Merge pull request #10436 from lenankamp/patch-1
Recursive batch img2img.py
2023-07-08 16:50:54 +03:00
AUTOMATIC1111 993dd9a892 Merge branch 'dev' into patch-1 2023-07-08 16:50:23 +03:00
AUTOMATIC1111 d7d6e8cfc8 use natural sort for shared.walk_files and shared.listfiles, as well as for dirs in extra networks 2023-07-08 16:45:59 +03:00
AUTOMATIC1111 7a6abc59ea for #10650: change key to alt+arrows, enable by default 2023-07-08 16:15:28 +03:00
AUTOMATIC1111 12a29a677a Merge pull request #10650 from missionfloyd/reorder-hotkeys
Hotkeys to move prompt elements
2023-07-08 16:12:01 +03:00
AUTOMATIC1111 274a3e21ba small rework for img2img PNG info 2023-07-08 15:42:00 +03:00
AUTOMATIC1111 1d71c36de2 third time's the charm 2023-07-08 15:21:29 +03:00
AUTOMATIC1111 9043b91649 additional changes for merge conflict for #11337 2023-07-08 15:14:24 +03:00
AUTOMATIC1111 b88645d9eb additional changes for merge conflict for #11337 2023-07-08 15:14:14 +03:00
AUTOMATIC1111 b0419b60a0 Merge pull request #11337 from FWeynschenk/img2img-batch-png-info
Img2img batch png info
2023-07-08 15:10:33 +03:00
AUTOMATIC1111 ec9bbda3da Merge branch 'dev' into img2img-batch-png-info 2023-07-08 15:10:10 +03:00
AUTOMATIC1111 18256c5f01 fix for #11478 2023-07-08 14:58:33 +03:00
AUTOMATIC1111 211c3398f6 Merge pull request #11478 from catalpaaa/subpath
Fixing --subpath on newer gradio version
2023-07-08 14:53:42 +03:00
AUTOMATIC1111 539518292e Merge pull request #11468 from NoCrypt/grid-colors-options
Add options to change colors in grid
2023-07-08 14:51:50 +03:00
AUTOMATIC1111 f0c62688d2 Merge pull request #11488 from AUTOMATIC1111/callback-after_extra_networks_activate
add callback after_extra_networks_activate
2023-07-08 14:50:11 +03:00
AUTOMATIC1111 3602602260 whitespace for #11477 2023-07-08 14:44:02 +03:00
AUTOMATIC1111 53924aeaf0 Merge pull request #11477 from hako-mikan/master
add `before_hr` script callback
2023-07-08 14:43:06 +03:00
AUTOMATIC1111 953147bf6b Merge pull request #11495 from missionfloyd/end-paren-fix
Correctly remove end parenthesis with ctrl+up/down
2023-07-08 14:41:33 +03:00
AUTOMATIC1111 eb51acb89e Merge pull request #11503 from AUTOMATIC1111/rename---add-stop-route-to---api-server-stop
Rename --add-stop-route to --api-server-stop
2023-07-08 14:40:21 +03:00
AUTOMATIC1111 6acc4cd7e1 Merge pull request #11513 from Akegarasu/dev
fix can't get current hash
2023-07-08 14:39:52 +03:00
AUTOMATIC1111 b25925c95b Merge pull request #11520 from AUTOMATIC1111/extension-metadata
Extension metadata
2023-07-08 14:30:17 +03:00
AUTOMATIC1111 b74f661ed9 Merge pull request #11529 from hunshcn/sync-weight
sync default value of process_focal_crop_entropy_weight between ui and api
2023-07-08 14:24:48 +03:00
AUTOMATIC1111 7a7fa25d02 lint fix for #11492 2023-07-08 14:21:40 +03:00
AUTOMATIC1111 d78377ea5d Merge pull request #11593 from akx/better-status-reporting-1
Better status reporting, part 1
2023-07-08 14:20:28 +03:00
AUTOMATIC1111 fc049a2fd3 Merge branch 'dev' into better-status-reporting-1 2023-07-08 14:19:34 +03:00
AUTOMATIC1111 ae74b44c69 Merge pull request #11596 from akx/use-read-info
postprocessing: use read_info_from_image
2023-07-08 14:14:12 +03:00
AUTOMATIC1111 9be8903ca9 Merge pull request #11567 from AUTOMATIC1111/seed_resize_to_0
Don't add "Seed Resize: -1x-1" to API image metadata
2023-07-08 13:58:31 +03:00
AUTOMATIC1111 e338f4142f Merge pull request #11592 from onyasumi/launchscript-directory
Fixed launch script to be runnable from any directory
2023-07-08 13:57:01 +03:00
AUTOMATIC1111 3a294a08bc Merge pull request #11535 from gshawn3/bugfix/11534
fix for #11534: canvas zoom and pan extension hijacking shortcut keys
2023-07-08 13:48:58 +03:00
AUTOMATIC1111 d12ccb91a8 Merge pull request #11631 from AUTOMATIC1111/gif-preview
Allow gif for extra network previews
2023-07-08 13:47:57 +03:00
AUTOMATIC1111 2151a9881f Merge pull request #11492 from semjon00/dev
Fix throwing exception when trying to resize image with I;16 mode
2023-07-08 13:46:08 +03:00
AUTOMATIC1111 19772c3c97 fix problem with extra network saving images as previews losing generation info
add a description for save_image_with_geninfo
2023-07-08 13:43:42 +03:00
AUTOMATIC1111 16045d0877 Merge pull request #11637 from Hao-Wu/fix-has-mps-deprecated
Fix warning of 'has_mps' deprecated from PyTorch
2023-07-08 13:11:52 +03:00
AUTOMATIC1111 5ed1ae5003 Merge pull request #11656 from jovijovi/dev
fix(api): convert to "RGB" if image mode is "RGBA" #11655
2023-07-08 13:10:50 +03:00
AUTOMATIC1111 46c2b1e202 Merge pull request #11660 from neilmahaseth/patch-1
Fix UnicodeEncodeError when writing to file CLIP Interrogator Batch Mode
2023-07-08 13:10:03 +03:00
AUTOMATIC1111 7348440524 Merge pull request #11569 from ramyma/hotfix-api-cache
Hotfix: API cache cleanup
2023-07-08 13:09:20 +03:00
Nelson Chen a369a0cf65 Add a link to an index-able/crawl-able wiki mirroring service of the wiki
At the moment, the wiki is editable by GitHub users, so it is blocked from indexing. If you are searching for something related to this repo, Google and other search engines will not use the content for it.

This link hack just sticks a link on the README so search engines may prioritize it. At the moment, 0 pages from GitHub are index and only 7 pages from my proxy service are. If you add this, the rest should get indexed.

An indexable page looks like this: https://github-wiki-see.page/m/AUTOMATIC1111/stable-diffusion-webui/wiki/Command-Line-Arguments-and-Settings. It is not meant to be read, just indexed, and users are expected to click through to the GitHub copy.

https://github-wiki-see.page/ has more information about the situation. I built the tool and I'm happy to answer any questions I can.

Similar: https://github.com/MiSTer-devel/Main_MiSTer#main_mister-main-binary-and-wiki-repo:~:text=For%20the%20purposes%20of%20getting%20google%20to%20crawl%20the%20wiki%2C%20here%27s%20a%20link%20to%20the%20(not%20for%20humans)%20crawlable%20wiki
2023-07-07 09:04:49 -07:00
gitama2023 f439179641 Added a prompt for users using poor scaling
Added a JavaScript file that detects browser scaling and prompts users when scale is not 100%
2023-07-07 16:18:01 +08:00
Neil Mahseth c258dd34a8 Fix UnicodeEncodeError when writing to file CLIP Interrogator Batch Mode
The code snippet print(interrogation_function(img), file=open(os.path.join(ii_output_dir, f"{left}.txt"), 'a')) raises a UnicodeEncodeError with the message "'charmap' codec can't encode character '\u016b' in position 129". This error occurs because the default encoding used by the open() function cannot handle certain Unicode characters.

To fix this issue, the encoding parameter needs to be explicitly specified when opening the file. By using an appropriate encoding, such as 'utf-8', we can ensure that Unicode characters are properly encoded and written to the file.

The updated code should be modified as follows:

python
Copy code
print(interrogation_function(img), file=open(os.path.join(ii_output_dir, f"{left}.txt"), 'a', encoding='utf-8'))
By making this change, the code will no longer raise the UnicodeEncodeError and will correctly handle Unicode characters during the file write operation.
2023-07-06 22:02:47 +05:30
jovijovi 259967b7c6 fix(api): convert to "RGB" if image mode is "RGBA" 2023-07-06 18:43:17 +08:00
Hao-Wu daf41a2734 Fix warning of 'has_mps' is deprecated from PyTorch 2023-07-06 15:37:10 +08:00
semjon00 fb661e089f Fix throwing exception when trying to resize image with I;16 mode 2023-07-05 15:39:04 +03:00
missionfloyd c602471b85 Allow gif for extra network previews 2023-07-05 03:19:26 -06:00
Danil Boldyrev f325783abd made the blur function optional, added exclusion buttons 2023-07-04 22:26:43 +03:00
missionfloyd f731a728c6 Check seed_resize_from <= 0 2023-07-03 11:41:10 -06:00
ramyma c1c0492859 Use contextlib for closing the generation process 2023-07-03 20:17:47 +03:00
ramyma 3278887317 Handle cleanup in case there's an exception thrown 2023-07-03 20:02:30 +03:00
Aarni Koskela 5c6a33b3e1 read_info_from_image: don't mutate info in passed-in image 2023-07-03 13:10:42 +03:00
Aarni Koskela 96f0593c8f read_info_from_image: add type 2023-07-03 13:10:20 +03:00
Aarni Koskela b2c574891f read_info_from_image: add photoshop to ignored 2023-07-03 13:09:37 +03:00
Aarni Koskela 08f9b705cd Use read_info_from_image in postprocessing
Avoids bad keys such as `exif` ending up in the "PNG info" passed forward
2023-07-03 13:08:28 +03:00
Aarni Koskela 522a8b9f62 Add a status logger in modules.shared 2023-07-03 11:07:57 +03:00
Aarni Koskela e430344347 API: use finally: for state.end() 2023-07-03 11:03:41 +03:00
Aarni Koskela f44feb6a10 Add job argument to State.begin() 2023-07-03 11:03:41 +03:00
Aarni Koskela b70001e618 Add SD_WEBUI_LOG_LEVEL envvar 2023-07-03 11:03:41 +03:00
Frank Tao e33e2c5175 Update webui.sh 2023-07-03 03:17:27 -04:00
onyasumi 5a32d4fcb1 Fix launch script to be runnable from any directory 2023-07-03 07:15:19 +00:00
Danil Boldyrev 8519d52ef5 fixing the copy/paste function, correct code 2023-07-02 19:20:49 +03:00
ramyma 74d001bc68 Hotfix: call processing close to cleanup API generation calls 2023-07-02 04:59:59 +03:00
missionfloyd 7f46f81dd7 Change default seed_resize to 0 2023-07-01 17:20:56 -06:00
gshawn3 8a07c59baa fix for #11534: canvas zoom and pan extension hijacking shortcut keys 2023-06-30 03:49:26 -07:00
w-e-w 2ccc832b33 add extensions Update Created dates with sorting 2023-06-29 22:46:59 +09:00
Akiba 0416a7bfba fix can't get current hash 2023-06-29 18:46:52 +08:00
w-e-w b1c6e39620 starts left 2023-06-29 19:25:34 +09:00
w-e-w d47324b898 add stars 2023-06-29 19:25:18 +09:00
hunshcn 0bc0e652a3 sync default value of process_focal_crop_entropy_weight between ui and api 2023-06-29 18:12:55 +08:00
w-e-w cc9c171978 rename --add-stop-route to --api-server-stop 2023-06-29 14:21:28 +09:00
missionfloyd 0b0767939d Correctly remove end parenthesis with ctrl+up/down 2023-06-28 17:51:27 -06:00
w-e-w 9c2a7f1e8b add callback after_extra_networks_activate 2023-06-29 02:08:21 +09:00
NoCrypt f74fb50495 Move change colors options to Saving images/grids 2023-06-28 20:24:57 +07:00
NoCrypt d22eb8a17f Fix lint 2023-06-28 17:57:34 +07:00
NoCrypt 45ab7475d6 Revision 2023-06-28 17:55:58 +07:00
catalpaaa 24d4475bdb fixing --subpath on newer gradio version 2023-06-28 03:15:01 -07:00
hako-mikan b0ec69b360 add 'before_hr callback' script callback 2023-06-28 18:37:08 +09:00
NoCrypt da14f6a663 Add options to change colors in grid 2023-06-28 10:16:44 +07:00
Beinsezii 9d8af4bd6a Merge branch 'AUTOMATIC1111:dev' into dev 2023-06-27 15:29:47 -07:00
AUTOMATIC1111 fab73f2e7d Merge pull request #11325 from stablegeniusdiffuser/dev-batch-grid-metadata
Add parameter to differentiate between batch run grids or ordinary images to write proper metadata
2023-06-27 14:23:39 +03:00
AUTOMATIC1111 1bf01b73f4 Merge pull request #11046 from akx/ded-code
Remove a bunch of unused/vestigial code
2023-06-27 11:25:55 +03:00
AUTOMATIC d06af4e517 fix and rework #11113 2023-06-27 09:26:18 +03:00
AUTOMATIC1111 a96687682a Merge pull request #11113 from stevensu1977/master
add model exists status check /sdapi/v1/options  #11112
2023-06-27 09:24:12 +03:00
AUTOMATIC1111 0b97ae2832 Merge branch 'dev' into master 2023-06-27 09:23:15 +03:00
AUTOMATIC1111 3cd4fd51ef Merge pull request #10823 from akx/model-loady
Upscaler model loading cleanup
2023-06-27 09:20:49 +03:00
AUTOMATIC1111 d4f9250c5a Merge pull request #11201 from akx/ruff-upg
Upgrade Ruff to 0.0.272
2023-06-27 09:19:55 +03:00
AUTOMATIC 24129368f1 send tensors to the correct device when loading from safetensors file with memmap disabled for #11260 2023-06-27 09:19:04 +03:00
AUTOMATIC1111 14196548c5 Merge pull request #11260 from dhwz/dev
fix very slow loading speed of .safetensors files
2023-06-27 09:11:08 +03:00
AUTOMATIC1111 d35e246111 Merge pull request #11227 from deckar01/10141-gradio-user-exif
Add Gradio User to Metadata
2023-06-27 09:06:03 +03:00
AUTOMATIC1111 4147fd6b2f Merge branch 'dev' into 10141-gradio-user-exif 2023-06-27 09:05:53 +03:00
AUTOMATIC1111 bedcd2f377 Merge pull request #11264 from huchenlei/meta_class
🐛 Allow Script to have custom metaclass
2023-06-27 09:02:51 +03:00
AUTOMATIC1111 58a9a261c4 Merge branch 'dev' into meta_class 2023-06-27 09:02:38 +03:00
AUTOMATIC1111 2c43dd766d Merge pull request #11226 from AUTOMATIC1111/git-clone-progress
show Git clone progress
2023-06-27 09:01:04 +03:00
AUTOMATIC 9bb1fcfad4 alternate fix for catch errors when retrieving extension index #11290 2023-06-27 08:59:35 +03:00
AUTOMATIC1111 fa31dd80f5 Merge pull request #11315 from guming3d/master
fix: adding elem_id for img2img resize to and resize by tabs
2023-06-27 08:53:10 +03:00
AUTOMATIC1111 2b247f3533 Merge pull request #11415 from netux/extensions-toggle-all
Add checkbox to check/uncheck all extensions in the Installed tab
2023-06-27 08:44:37 +03:00
AUTOMATIC1111 3e76ae5f50 Merge pull request #11146 from AUTOMATIC1111/api-quit-restart
api quit restart
2023-06-27 08:41:36 +03:00
AUTOMATIC f005efae72 Merge branch 'master' into dev 2023-06-27 08:39:34 +03:00
AUTOMATIC 394ffa7b0a Merge branch 'release_candidate' 2023-06-27 08:38:14 +03:00
AUTOMATIC 6ac247317d Merge branch 'release_candidate' into dev 2023-06-27 08:37:46 +03:00
AUTOMATIC1111 dbc88c9645 Merge pull request #11189 from daswer123/dev
Zoom and pan: More options in the settings and improved error output
2023-06-27 08:34:51 +03:00
AUTOMATIC1111 cd7c03e1f6 Merge pull request #11136 from arch-fan/typo
fixed typos
2023-06-27 06:40:43 +03:00
AUTOMATIC1111 a9e7a3db3e Merge pull request #11199 from akx/makedirs
Use os.makedirs(..., exist_ok=True)
2023-06-27 06:39:51 +03:00
AUTOMATIC1111 001cbd369d Merge pull request #11294 from zhtttylz/Fix_Typo_of_hints.js
Fix Typo of hints.js
2023-06-27 06:35:22 +03:00
AUTOMATIC1111 820bbb5b7b Merge pull request #11408 from wfjsw/patch-1
Strip whitespaces from URL and dirname prior to extension installation
2023-06-27 06:20:59 +03:00
AUTOMATIC 4bd490c28d add missing infotext entry for the pad cond/uncond option 2023-06-27 06:18:43 +03:00
Martín (Netux) Rodríguez dd268c48c9 feat(extensions): add toggle all checkbox to Installed tab
Small QoL addition.

While there is the option to disable all extensions with the radio buttons at the top, that only acts as an added flag and doesn't really change the state of the extensions in the UI.

An use case for this checkbox is to disable all extensions except for a few, which is important for debugging extensions.
You could do that before, but you'd have to uncheck and recheck every extension one by one.
2023-06-25 00:48:46 -03:00
Jabasukuriputo Wang d5a5f2f29f Strip whitespaces from URL and dirname prior to extension installation
This avoid some cryptic errors brought by accidental spaces around urls
2023-06-25 01:31:02 +08:00
Ferdinand Weynschenk c4c63dd5e4 resolve linter 2023-06-20 14:03:42 +02:00
Ferdinand Weynschenk 7ad48120d4 use ui params when retreiving png info fails
Don't want to interrupt the process since batches can be huge. This makes more sense to me than using the previous images parameters
2023-06-20 13:50:02 +02:00
Ferdinand Weynschenk 928bd42da4 PNG info support at img2img batch 2023-06-20 13:33:36 +02:00
stablegeniusdiffuser 27e9e3f6fa Add use_main_prompt parameter to use proper metadata for batch run grids or individual images 2023-06-19 20:36:44 +02:00
George Gu d2ccdcdc97 fix: adding elem_id for img2img resize to and resize by tabs 2023-06-19 10:16:18 +08:00
zhtttylz f7ae0e68c9 Fix Typo of hints.js 2023-06-18 16:42:39 +08:00
w-e-w 2e1710d88e update the description of --add-stop-rout 2023-06-18 14:07:41 +09:00
huchenlei 373ff5a217 🐛 Allow Script to have metaclass 2023-06-16 15:17:17 -04:00
dhwz 41363e0d27 fix very slow loading speed of .safetensors files 2023-06-16 18:10:15 +02:00
XiaoMeng Mai e9bd18c57b Update call method 2023-06-16 00:09:54 +08:00
Jared Deckard f603275d84 Add an opt-in infotext user name setting 2023-06-15 11:00:20 -05:00
Jared Deckard 8f18e67243 Add a user pattern to the filename generator 2023-06-15 11:00:11 -05:00
XiaoMeng Mai de022c4c80 Update code style 2023-06-15 22:59:46 +08:00
XiaoMeng Mai 9ec2ba2d28 Add github mirror for the download extension 2023-06-15 22:43:09 +08:00
Jared Deckard d3c86e5178 Note the Gradio user in the Exif data 2023-06-14 17:15:52 -05:00
Beinsezii 1d7c51fb9f WEBUI.SH Navi 3 Support
Navi 3 card now defaults to nightly torch to utilize rocm 5.5
for out-of-the-box support.

https://download.pytorch.org/whl/nightly/

While its not yet on the main pytorch "get started" site,
it still seems perfectly indexable via pip which is all we need.

With this I'm able to clone a fresh repo and immediately run ./webui.sh
on my 7900 XTX without any problems.
2023-06-14 13:07:22 -07:00
w-e-w 376f793bde git clone show progress 2023-06-15 04:23:52 +09:00
Jared Deckard fa9d2ac2ff Fix gradio special args in the call queue 2023-06-14 13:53:13 -05:00
w-e-w 6091c4e4aa terminate -> stop 2023-06-14 19:53:08 +09:00
w-e-w 49fb2a3376 response 501 if not a able to restart 2023-06-14 19:52:12 +09:00
w-e-w 6387f0e85d update workflow kill test server 2023-06-14 18:51:54 +09:00
w-e-w 5be6c026f5 rename routes 2023-06-14 18:51:47 +09:00
Danil Boldyrev 3a41d7c551 Formatting code with Prettier 2023-06-14 00:31:36 +03:00
Danil Boldyrev 9b687f013d Reworked the disabling of functions, refactored part of the code 2023-06-14 00:24:25 +03:00
Aarni Koskela d807164776 textual_inversion/logging.py: clean up duplicate key from sets (and sort them) (Ruff B033) 2023-06-13 13:07:39 +03:00
Aarni Koskela 8ce9b36e0f Upgrade ruff to 272 2023-06-13 13:07:06 +03:00
Aarni Koskela 2667f47ffb Remove stray space from SwinIR model URL 2023-06-13 13:00:05 +03:00
Aarni Koskela bf67a5dcf4 Upscaler.load_model: don't return None, just use exceptions 2023-06-13 12:44:25 +03:00
Aarni Koskela e3a973a68d Add TODO comments to sus model loads 2023-06-13 12:38:29 +03:00
Aarni Koskela 0afbc0c235 Fix up if "http" in ...: to be more sensible startswiths 2023-06-13 12:38:29 +03:00
Aarni Koskela 89352a2f52 Move load_file_from_url to modelloader 2023-06-13 12:38:28 +03:00
Aarni Koskela 165ab44f03 Use os.makedirs(..., exist_ok=True) 2023-06-13 12:35:43 +03:00
Danil Boldyrev 9a2da597c5 remove console.log 2023-06-12 22:21:42 +03:00
Danil Boldyrev ee029a8cad Improved error output, improved settings menu 2023-06-12 22:19:22 +03:00
w-e-w d80962681a remove fastapi.Response 2023-06-12 18:21:01 +09:00
w-e-w b9664ab615 move _stop route to api 2023-06-12 18:15:27 +09:00
Su Wei 7e2d39a2d1 update model checkpoint switch code 2023-06-12 15:22:49 +08:00
w-e-w 9142be0a0d quit restart 2023-06-10 23:36:34 +09:00
arch-fan 5576a72322 fixed typos 2023-06-09 19:59:27 +00:00
AUTOMATIC 3b11f17a37 Merge branch 'dev' into release_candidate 2023-06-09 22:48:18 +03:00
Su Wei 8ca34ad6d8 add model exists status check to modeuls/api/api.py , /sdapi/v1/options [POST] 2023-06-09 13:14:20 +08:00
Aarni Koskela ba70a220e3 Remove a bunch of unused/vestigial code
As found by Vulture and some eyes
2023-06-05 22:43:57 +03:00
missionfloyd 6645f23c4c Merge branch 'dev' into reorder-hotkeys 2023-05-25 18:53:33 -06:00
missionfloyd 43bdaa2f0e Make ctrl+left/right optional 2023-05-25 18:49:28 -06:00
missionfloyd dafe519363 Fix lint errors 2023-05-22 21:23:39 -06:00
missionfloyd 468056958b Add reorder hotkeys
Shifts selected items with ctrl+left/right
2023-05-22 20:46:25 -06:00
lenankamp ff6acd35d0 Update img2img.py
Hopefully corrected the white space issue
2023-05-19 03:20:19 -04:00
lenankamp bbce167305 Recursive batch img2img.py
Searches sub directories and performs img2img batch processing, also limits inputs to jpg, webp, and png. Then saves to putput directory with relative paths.
2023-05-16 14:37:45 -04:00
179 changed files with 12974 additions and 5340 deletions
+7
View File
@@ -74,6 +74,7 @@ module.exports = {
create_submit_args: "readonly", create_submit_args: "readonly",
restart_reload: "readonly", restart_reload: "readonly",
updateInput: "readonly", updateInput: "readonly",
onEdit: "readonly",
//extraNetworks.js //extraNetworks.js
requestGet: "readonly", requestGet: "readonly",
popup: "readonly", popup: "readonly",
@@ -87,5 +88,11 @@ module.exports = {
modalNextImage: "readonly", modalNextImage: "readonly",
// token-counters.js // token-counters.js
setupTokenCounters: "readonly", setupTokenCounters: "readonly",
// localStorage.js
localSet: "readonly",
localGet: "readonly",
localRemove: "readonly",
// resizeHandle.js
setupResizeHandle: "writable"
} }
}; };
+51 -84
View File
@@ -1,35 +1,55 @@
name: Bug Report name: Bug Report
description: You think somethings is broken in the UI description: You think something is broken in the UI
title: "[Bug]: " title: "[Bug]: "
labels: ["bug-report"] labels: ["bug-report"]
body: body:
- type: checkboxes
attributes:
label: Is there an existing issue for this?
description: Please search to see if an issue already exists for the bug you encountered, and that it hasn't been fixed in a recent build/commit.
options:
- label: I have searched the existing issues and checked the recent builds/commits
required: true
- type: markdown - type: markdown
attributes: attributes:
value: | value: |
*Please fill this form with as much information as possible, don't forget to fill "What OS..." and "What browsers" and *provide screenshots if possible** > The title of the bug report should be short and descriptive.
> Use relevant keywords for searchability.
> Do not leave it blank, but also do not put an entire error log in it.
- type: checkboxes
attributes:
label: Checklist
description: |
Please perform basic debugging to see if extensions or configuration is the cause of the issue.
Basic debug procedure
 1. Disable all third-party extensions - check if extension is the cause
 2. Update extensions and webui - sometimes things just need to be updated
 3. Backup and remove your config.json and ui-config.json - check if the issue is caused by bad configuration
 4. Delete venv with third-party extensions disabled - sometimes extensions might cause wrong libraries to be installed
 5. Try a fresh installation webui in a different directory - see if a clean installation solves the issue
Before making a issue report please, check that the issue hasn't been reported recently.
options:
- label: The issue exists after disabling all extensions
- label: The issue exists on a clean installation of webui
- label: The issue is caused by an extension, but I believe it is caused by a bug in the webui
- label: The issue exists in the current version of the webui
- label: The issue has not been reported before recently
- label: The issue has been reported before but has not been fixed yet
- type: markdown
attributes:
value: |
> Please fill this form with as much information as possible. Don't forget to "Upload Sysinfo" and "What browsers" and provide screenshots if possible
- type: textarea - type: textarea
id: what-did id: what-did
attributes: attributes:
label: What happened? label: What happened?
description: Tell us what happened in a very clear and simple way description: Tell us what happened in a very clear and simple way
placeholder: |
txt2img is not working as intended.
validations: validations:
required: true required: true
- type: textarea - type: textarea
id: steps id: steps
attributes: attributes:
label: Steps to reproduce the problem label: Steps to reproduce the problem
description: Please provide us with precise step by step information on how to reproduce the bug description: Please provide us with precise step by step instructions on how to reproduce the bug
value: | placeholder: |
1. Go to .... 1. Go to ...
2. Press .... 2. Press ...
3. ... 3. ...
validations: validations:
required: true required: true
@@ -37,64 +57,9 @@ body:
id: what-should id: what-should
attributes: attributes:
label: What should have happened? label: What should have happened?
description: Tell what you think the normal behavior should be description: Tell us what you think the normal behavior should be
validations: placeholder: |
required: true WebUI should ...
- type: input
id: commit
attributes:
label: Version or Commit where the problem happens
description: "Which webui version or commit are you running ? (Do not write *Latest Version/repo/commit*, as this means nothing and will have changed by the time we read your issue. Rather, copy the **Version: v1.2.3** link at the bottom of the UI, or from the cmd/terminal if you can't launch it.)"
validations:
required: true
- type: dropdown
id: py-version
attributes:
label: What Python version are you running on ?
multiple: false
options:
- Python 3.10.x
- Python 3.11.x (above, no supported yet)
- Python 3.9.x (below, no recommended)
- type: dropdown
id: platforms
attributes:
label: What platforms do you use to access the UI ?
multiple: true
options:
- Windows
- Linux
- MacOS
- iOS
- Android
- Other/Cloud
- type: dropdown
id: device
attributes:
label: What device are you running WebUI on?
multiple: true
options:
- Nvidia GPUs (RTX 20 above)
- Nvidia GPUs (GTX 16 below)
- AMD GPUs (RX 6000 above)
- AMD GPUs (RX 5000 below)
- CPU
- Other GPUs
- type: dropdown
id: cross_attention_opt
attributes:
label: Cross attention optimization
description: What cross attention optimization are you using, Settings -> Optimizations -> Cross attention optimization
multiple: false
options:
- Automatic
- xformers
- sdp-no-mem
- sdp
- Doggettx
- V1
- InvokeAI
- "None "
validations: validations:
required: true required: true
- type: dropdown - type: dropdown
@@ -108,26 +73,25 @@ body:
- Brave - Brave
- Apple Safari - Apple Safari
- Microsoft Edge - Microsoft Edge
- Android
- iOS
- Other
- type: textarea - type: textarea
id: cmdargs id: sysinfo
attributes: attributes:
label: Command Line Arguments label: Sysinfo
description: Are you using any launching parameters/command line arguments (modified webui-user .bat/.sh) ? If yes, please write them below. Write "No" otherwise. description: System info file, generated by WebUI. You can generate it in settings, on the Sysinfo page. Drag the file into the field to upload it. If you submit your report without including the sysinfo file, the report will be closed. If needed, review the report to make sure it includes no personal information you don't want to share. If you can't start WebUI, you can use --dump-sysinfo commandline argument to generate the file.
render: Shell placeholder: |
validations: 1. Go to WebUI Settings -> Sysinfo -> Download system info.
required: true If WebUI fails to launch, use --dump-sysinfo commandline argument to generate the file
- type: textarea 2. Upload the Sysinfo as a attached file, Do NOT paste it in as plain text.
id: extensions
attributes:
label: List of extensions
description: Are you using any extensions other than built-ins? If yes, provide a list, you can copy it at "Extensions" tab. Write "No" otherwise.
validations: validations:
required: true required: true
- type: textarea - type: textarea
id: logs id: logs
attributes: attributes:
label: Console logs label: Console logs
description: Please provide **full** cmd/terminal logs from the moment you started UI to the end of it, after your bug happened. If it's very long, provide a link to pastebin or similar service. description: Please provide **full** cmd/terminal logs from the moment you started UI to the end of it, after the bug occured. If it's very long, provide a link to pastebin or similar service.
render: Shell render: Shell
validations: validations:
required: true required: true
@@ -135,4 +99,7 @@ body:
id: misc id: misc
attributes: attributes:
label: Additional information label: Additional information
description: Please provide us with any relevant additional info or context. description: |
Please provide us with any relevant additional info or context.
Examples:
 I have updated my GPU driver recently.
+6 -2
View File
@@ -1,4 +1,4 @@
name: Run Linting/Formatting on Pull Requests name: Linter
on: on:
- push - push
@@ -6,7 +6,9 @@ on:
jobs: jobs:
lint-python: lint-python:
name: ruff
runs-on: ubuntu-latest runs-on: ubuntu-latest
if: github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name != github.event.pull_request.base.repo.full_name
steps: steps:
- name: Checkout Code - name: Checkout Code
uses: actions/checkout@v3 uses: actions/checkout@v3
@@ -18,11 +20,13 @@ jobs:
# not to have GHA download an (at the time of writing) 4 GB cache # not to have GHA download an (at the time of writing) 4 GB cache
# of PyTorch and other dependencies. # of PyTorch and other dependencies.
- name: Install Ruff - name: Install Ruff
run: pip install ruff==0.0.265 run: pip install ruff==0.1.6
- name: Run Ruff - name: Run Ruff
run: ruff . run: ruff .
lint-js: lint-js:
name: eslint
runs-on: ubuntu-latest runs-on: ubuntu-latest
if: github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name != github.event.pull_request.base.repo.full_name
steps: steps:
- name: Checkout Code - name: Checkout Code
uses: actions/checkout@v3 uses: actions/checkout@v3
+6 -3
View File
@@ -1,4 +1,4 @@
name: Run basic features tests on CPU with empty SD model name: Tests
on: on:
- push - push
@@ -6,7 +6,9 @@ on:
jobs: jobs:
test: test:
name: tests on CPU with empty model
runs-on: ubuntu-latest runs-on: ubuntu-latest
if: github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name != github.event.pull_request.base.repo.full_name
steps: steps:
- name: Checkout Code - name: Checkout Code
uses: actions/checkout@v3 uses: actions/checkout@v3
@@ -39,10 +41,11 @@ jobs:
--skip-prepare-environment --skip-prepare-environment
--skip-torch-cuda-test --skip-torch-cuda-test
--test-server --test-server
--do-not-download-clip
--no-half --no-half
--disable-opt-split-attention --disable-opt-split-attention
--use-cpu all --use-cpu all
--add-stop-route --api-server-stop
2>&1 | tee output.txt & 2>&1 | tee output.txt &
- name: Run tests - name: Run tests
run: | run: |
@@ -50,7 +53,7 @@ jobs:
python -m pytest -vv --junitxml=test/results.xml --cov . --cov-report=xml --verify-base-url test python -m pytest -vv --junitxml=test/results.xml --cov . --cov-report=xml --verify-base-url test
- name: Kill test server - name: Kill test server
if: always() if: always()
run: curl -vv -XPOST http://127.0.0.1:7860/_stop && sleep 10 run: curl -vv -XPOST http://127.0.0.1:7860/sdapi/v1/server-stop && sleep 10
- name: Show coverage - name: Show coverage
run: | run: |
python -m coverage combine .coverage* python -m coverage combine .coverage*
+19
View File
@@ -0,0 +1,19 @@
name: Pull requests can't target master branch
"on":
pull_request:
types:
- opened
- synchronize
- reopened
branches:
- master
jobs:
check:
runs-on: ubuntu-latest
steps:
- name: Warning marge into master
run: |
echo -e "::warning::This pull request directly merge into \"master\" branch, normally development happens on \"dev\" branch."
exit 1
+255
View File
@@ -1,3 +1,258 @@
## 1.6.1
### Bug Fixes:
* fix an error causing the webui to fail to start ([#13839](https://github.com/AUTOMATIC1111/stable-diffusion-webui/pull/13839))
## 1.6.0
### Features:
* refiner support [#12371](https://github.com/AUTOMATIC1111/stable-diffusion-webui/pull/12371)
* add NV option for Random number generator source setting, which allows to generate same pictures on CPU/AMD/Mac as on NVidia videocards
* add style editor dialog
* hires fix: add an option to use a different checkpoint for second pass ([#12181](https://github.com/AUTOMATIC1111/stable-diffusion-webui/pull/12181))
* option to keep multiple loaded models in memory ([#12227](https://github.com/AUTOMATIC1111/stable-diffusion-webui/pull/12227))
* new samplers: Restart, DPM++ 2M SDE Exponential, DPM++ 2M SDE Heun, DPM++ 2M SDE Heun Karras, DPM++ 2M SDE Heun Exponential, DPM++ 3M SDE, DPM++ 3M SDE Karras, DPM++ 3M SDE Exponential ([#12300](https://github.com/AUTOMATIC1111/stable-diffusion-webui/pull/12300), [#12519](https://github.com/AUTOMATIC1111/stable-diffusion-webui/pull/12519), [#12542](https://github.com/AUTOMATIC1111/stable-diffusion-webui/pull/12542))
* rework DDIM, PLMS, UniPC to use CFG denoiser same as in k-diffusion samplers:
* makes all of them work with img2img
* makes prompt composition posssible (AND)
* makes them available for SDXL
* always show extra networks tabs in the UI ([#11808](https://github.com/AUTOMATIC1111/stable-diffusion-webui/pull/11808))
* use less RAM when creating models ([#11958](https://github.com/AUTOMATIC1111/stable-diffusion-webui/pull/11958), [#12599](https://github.com/AUTOMATIC1111/stable-diffusion-webui/pull/12599))
* textual inversion inference support for SDXL
* extra networks UI: show metadata for SD checkpoints
* checkpoint merger: add metadata support
* prompt editing and attention: add support for whitespace after the number ([ red : green : 0.5 ]) (seed breaking change) ([#12177](https://github.com/AUTOMATIC1111/stable-diffusion-webui/pull/12177))
* VAE: allow selecting own VAE for each checkpoint (in user metadata editor)
* VAE: add selected VAE to infotext
* options in main UI: add own separate setting for txt2img and img2img, correctly read values from pasted infotext, add setting for column count ([#12551](https://github.com/AUTOMATIC1111/stable-diffusion-webui/pull/12551))
* add resize handle to txt2img and img2img tabs, allowing to change the amount of horizontable space given to generation parameters and resulting image gallery ([#12687](https://github.com/AUTOMATIC1111/stable-diffusion-webui/pull/12687), [#12723](https://github.com/AUTOMATIC1111/stable-diffusion-webui/pull/12723))
* change default behavior for batching cond/uncond -- now it's on by default, and is disabled by an UI setting (Optimizatios -> Batch cond/uncond) - if you are on lowvram/medvram and are getting OOM exceptions, you will need to enable it
* show current position in queue and make it so that requests are processed in the order of arrival ([#12707](https://github.com/AUTOMATIC1111/stable-diffusion-webui/pull/12707))
* add `--medvram-sdxl` flag that only enables `--medvram` for SDXL models
* prompt editing timeline has separate range for first pass and hires-fix pass (seed breaking change) ([#12457](https://github.com/AUTOMATIC1111/stable-diffusion-webui/pull/12457))
### Minor:
* img2img batch: RAM savings, VRAM savings, .tif, .tiff in img2img batch ([#12120](https://github.com/AUTOMATIC1111/stable-diffusion-webui/pull/12120), [#12514](https://github.com/AUTOMATIC1111/stable-diffusion-webui/pull/12514), [#12515](https://github.com/AUTOMATIC1111/stable-diffusion-webui/pull/12515))
* postprocessing/extras: RAM savings ([#12479](https://github.com/AUTOMATIC1111/stable-diffusion-webui/pull/12479))
* XYZ: in the axis labels, remove pathnames from model filenames
* XYZ: support hires sampler ([#12298](https://github.com/AUTOMATIC1111/stable-diffusion-webui/pull/12298))
* XYZ: new option: use text inputs instead of dropdowns ([#12491](https://github.com/AUTOMATIC1111/stable-diffusion-webui/pull/12491))
* add gradio version warning
* sort list of VAE checkpoints ([#12297](https://github.com/AUTOMATIC1111/stable-diffusion-webui/pull/12297))
* use transparent white for mask in inpainting, along with an option to select the color ([#12326](https://github.com/AUTOMATIC1111/stable-diffusion-webui/pull/12326))
* move some settings to their own section: img2img, VAE
* add checkbox to show/hide dirs for extra networks
* Add TAESD(or more) options for all the VAE encode/decode operation ([#12311](https://github.com/AUTOMATIC1111/stable-diffusion-webui/pull/12311))
* gradio theme cache, new gradio themes, along with explanation that the user can input his own values ([#12346](https://github.com/AUTOMATIC1111/stable-diffusion-webui/pull/12346), [#12355](https://github.com/AUTOMATIC1111/stable-diffusion-webui/pull/12355))
* sampler fixes/tweaks: s_tmax, s_churn, s_noise, s_tmax ([#12354](https://github.com/AUTOMATIC1111/stable-diffusion-webui/pull/12354), [#12356](https://github.com/AUTOMATIC1111/stable-diffusion-webui/pull/12356), [#12357](https://github.com/AUTOMATIC1111/stable-diffusion-webui/pull/12357), [#12358](https://github.com/AUTOMATIC1111/stable-diffusion-webui/pull/12358), [#12375](https://github.com/AUTOMATIC1111/stable-diffusion-webui/pull/12375), [#12521](https://github.com/AUTOMATIC1111/stable-diffusion-webui/pull/12521))
* update README.md with correct instructions for Linux installation ([#12352](https://github.com/AUTOMATIC1111/stable-diffusion-webui/pull/12352))
* option to not save incomplete images, on by default ([#12338](https://github.com/AUTOMATIC1111/stable-diffusion-webui/pull/12338))
* enable cond cache by default
* git autofix for repos that are corrupted ([#12230](https://github.com/AUTOMATIC1111/stable-diffusion-webui/pull/12230))
* allow to open images in new browser tab by middle mouse button ([#12379](https://github.com/AUTOMATIC1111/stable-diffusion-webui/pull/12379))
* automatically open webui in browser when running "locally" ([#12254](https://github.com/AUTOMATIC1111/stable-diffusion-webui/pull/12254))
* put commonly used samplers on top, make DPM++ 2M Karras the default choice
* zoom and pan: option to auto-expand a wide image, improved integration ([#12413](https://github.com/AUTOMATIC1111/stable-diffusion-webui/pull/12413), [#12727](https://github.com/AUTOMATIC1111/stable-diffusion-webui/pull/12727))
* option to cache Lora networks in memory
* rework hires fix UI to use accordion
* face restoration and tiling moved to settings - use "Options in main UI" setting if you want them back
* change quicksettings items to have variable width
* Lora: add Norm module, add support for bias ([#12503](https://github.com/AUTOMATIC1111/stable-diffusion-webui/pull/12503))
* Lora: output warnings in UI rather than fail for unfitting loras; switch to logging for error output in console
* support search and display of hashes for all extra network items ([#12510](https://github.com/AUTOMATIC1111/stable-diffusion-webui/pull/12510))
* add extra noise param for img2img operations ([#12564](https://github.com/AUTOMATIC1111/stable-diffusion-webui/pull/12564))
* support for Lora with bias ([#12584](https://github.com/AUTOMATIC1111/stable-diffusion-webui/pull/12584))
* make interrupt quicker ([#12634](https://github.com/AUTOMATIC1111/stable-diffusion-webui/pull/12634))
* configurable gallery height ([#12648](https://github.com/AUTOMATIC1111/stable-diffusion-webui/pull/12648))
* make results column sticky ([#12645](https://github.com/AUTOMATIC1111/stable-diffusion-webui/pull/12645))
* more hash filename patterns ([#12639](https://github.com/AUTOMATIC1111/stable-diffusion-webui/pull/12639))
* make image viewer actually fit the whole page ([#12635](https://github.com/AUTOMATIC1111/stable-diffusion-webui/pull/12635))
* make progress bar work independently from live preview display which results in it being updated a lot more often
* forbid Full live preview method for medvram and add a setting to undo the forbidding
* make it possible to localize tooltips and placeholders
* add option to align with sgm repo's sampling implementation ([#12818](https://github.com/AUTOMATIC1111/stable-diffusion-webui/pull/12818))
* Restore faces and Tiling generation parameters have been moved to settings out of main UI
* if you want to put them back into main UI, use `Options in main UI` setting on the UI page.
### Extensions and API:
* gradio 3.41.2
* also bump versions for packages: transformers, GitPython, accelerate, scikit-image, timm, tomesd
* support tooltip kwarg for gradio elements: gr.Textbox(label='hello', tooltip='world')
* properly clear the total console progressbar when using txt2img and img2img from API
* add cmd_arg --disable-extra-extensions and --disable-all-extensions ([#12294](https://github.com/AUTOMATIC1111/stable-diffusion-webui/pull/12294))
* shared.py and webui.py split into many files
* add --loglevel commandline argument for logging
* add a custom UI element that combines accordion and checkbox
* avoid importing gradio in tests because it spams warnings
* put infotext label for setting into OptionInfo definition rather than in a separate list
* make `StableDiffusionProcessingImg2Img.mask_blur` a property, make more inline with PIL `GaussianBlur` ([#12470](https://github.com/AUTOMATIC1111/stable-diffusion-webui/pull/12470))
* option to make scripts UI without gr.Group
* add a way for scripts to register a callback for before/after just a single component's creation
* use dataclass for StableDiffusionProcessing
* store patches for Lora in a specialized module instead of inside torch
* support http/https URLs in API ([#12663](https://github.com/AUTOMATIC1111/stable-diffusion-webui/pull/12663), [#12698](https://github.com/AUTOMATIC1111/stable-diffusion-webui/pull/12698))
* add extra noise callback ([#12616](https://github.com/AUTOMATIC1111/stable-diffusion-webui/pull/12616))
* dump current stack traces when exiting with SIGINT
* add type annotations for extra fields of shared.sd_model
### Bug Fixes:
* Don't crash if out of local storage quota for javascriot localStorage
* XYZ plot do not fail if an exception occurs
* fix missing TI hash in infotext if generation uses both negative and positive TI ([#12269](https://github.com/AUTOMATIC1111/stable-diffusion-webui/pull/12269))
* localization fixes ([#12307](https://github.com/AUTOMATIC1111/stable-diffusion-webui/pull/12307))
* fix sdxl model invalid configuration after the hijack
* correctly toggle extras checkbox for infotext paste ([#12304](https://github.com/AUTOMATIC1111/stable-diffusion-webui/pull/12304))
* open raw sysinfo link in new page ([#12318](https://github.com/AUTOMATIC1111/stable-diffusion-webui/pull/12318))
* prompt parser: Account for empty field in alternating words syntax ([#12319](https://github.com/AUTOMATIC1111/stable-diffusion-webui/pull/12319))
* add tab and carriage return to invalid filename chars ([#12327](https://github.com/AUTOMATIC1111/stable-diffusion-webui/pull/12327))
* fix api only Lora not working ([#12387](https://github.com/AUTOMATIC1111/stable-diffusion-webui/pull/12387))
* fix options in main UI misbehaving when there's just one element
* make it possible to use a sampler from infotext even if it's hidden in the dropdown
* fix styles missing from the prompt in infotext when making a grid of batch of multiplie images
* prevent bogus progress output in console when calculating hires fix dimensions
* fix --use-textbox-seed
* fix broken `Lora/Networks: use old method` option ([#12466](https://github.com/AUTOMATIC1111/stable-diffusion-webui/pull/12466))
* properly return `None` for VAE hash when using `--no-hashing` ([#12463](https://github.com/AUTOMATIC1111/stable-diffusion-webui/pull/12463))
* MPS/macOS fixes and optimizations ([#12526](https://github.com/AUTOMATIC1111/stable-diffusion-webui/pull/12526))
* add second_order to samplers that mistakenly didn't have it
* when refreshing cards in extra networks UI, do not discard user's custom resolution
* fix processing error that happens if batch_size is not a multiple of how many prompts/negative prompts there are ([#12509](https://github.com/AUTOMATIC1111/stable-diffusion-webui/pull/12509))
* fix inpaint upload for alpha masks ([#12588](https://github.com/AUTOMATIC1111/stable-diffusion-webui/pull/12588))
* fix exception when image sizes are not integers ([#12586](https://github.com/AUTOMATIC1111/stable-diffusion-webui/pull/12586))
* fix incorrect TAESD Latent scale ([#12596](https://github.com/AUTOMATIC1111/stable-diffusion-webui/pull/12596))
* auto add data-dir to gradio-allowed-path ([#12603](https://github.com/AUTOMATIC1111/stable-diffusion-webui/pull/12603))
* fix exception if extensuions dir is missing ([#12607](https://github.com/AUTOMATIC1111/stable-diffusion-webui/pull/12607))
* fix issues with api model-refresh and vae-refresh ([#12638](https://github.com/AUTOMATIC1111/stable-diffusion-webui/pull/12638))
* fix img2img background color for transparent images option not being used ([#12633](https://github.com/AUTOMATIC1111/stable-diffusion-webui/pull/12633))
* attempt to resolve NaN issue with unstable VAEs in fp32 mk2 ([#12630](https://github.com/AUTOMATIC1111/stable-diffusion-webui/pull/12630))
* implement missing undo hijack for SDXL
* fix xyz swap axes ([#12684](https://github.com/AUTOMATIC1111/stable-diffusion-webui/pull/12684))
* fix errors in backup/restore tab if any of config files are broken ([#12689](https://github.com/AUTOMATIC1111/stable-diffusion-webui/pull/12689))
* fix SD VAE switch error after model reuse ([#12685](https://github.com/AUTOMATIC1111/stable-diffusion-webui/pull/12685))
* fix trying to create images too large for the chosen format ([#12667](https://github.com/AUTOMATIC1111/stable-diffusion-webui/pull/12667))
* create Gradio temp directory if necessary ([#12717](https://github.com/AUTOMATIC1111/stable-diffusion-webui/pull/12717))
* prevent possible cache loss if exiting as it's being written by using an atomic operation to replace the cache with the new version
* set devices.dtype_unet correctly
* run RealESRGAN on GPU for non-CUDA devices ([#12737](https://github.com/AUTOMATIC1111/stable-diffusion-webui/pull/12737))
* prevent extra network buttons being obscured by description for very small card sizes ([#12745](https://github.com/AUTOMATIC1111/stable-diffusion-webui/pull/12745))
* fix error that causes some extra networks to be disabled if both <lora:> and <lyco:> are present in the prompt
* fix defaults settings page breaking when any of main UI tabs are hidden
* fix incorrect save/display of new values in Defaults page in settings
* fix for Reload UI function: if you reload UI on one tab, other opened tabs will no longer stop working
* fix an error that prevents VAE being reloaded after an option change if a VAE near the checkpoint exists ([#12797](https://github.com/AUTOMATIC1111/stable-diffusion-webui/pull/12737))
* hide broken image crop tool ([#12792](https://github.com/AUTOMATIC1111/stable-diffusion-webui/pull/12737))
* don't show hidden samplers in dropdown for XYZ script ([#12780](https://github.com/AUTOMATIC1111/stable-diffusion-webui/pull/12737))
* fix style editing dialog breaking if it's opened in both img2img and txt2img tabs
* fix a bug allowing users to bypass gradio and API authentication (reported by vysecurity)
* fix notification not playing when built-in webui tab is inactive ([#12834](https://github.com/AUTOMATIC1111/stable-diffusion-webui/pull/12834))
* honor `--skip-install` for extension installers ([#12832](https://github.com/AUTOMATIC1111/stable-diffusion-webui/pull/12832))
* don't print blank stdout in extension installers ([#12833](https://github.com/AUTOMATIC1111/stable-diffusion-webui/pull/12832), [#12855](https://github.com/AUTOMATIC1111/stable-diffusion-webui/pull/12855))
* do not change quicksettings dropdown option when value returned is `None` ([#12854](https://github.com/AUTOMATIC1111/stable-diffusion-webui/pull/12854))
* get progressbar to display correctly in extensions tab
## 1.5.2
### Bug Fixes:
* fix memory leak when generation fails
* update doggettx cross attention optimization to not use an unreasonable amount of memory in some edge cases -- suggestion by MorkTheOrk
## 1.5.1
### Minor:
* support parsing text encoder blocks in some new LoRAs
* delete scale checker script due to user demand
### Extensions and API:
* add postprocess_batch_list script callback
### Bug Fixes:
* fix TI training for SD1
* fix reload altclip model error
* prepend the pythonpath instead of overriding it
* fix typo in SD_WEBUI_RESTARTING
* if txt2img/img2img raises an exception, finally call state.end()
* fix composable diffusion weight parsing
* restyle Startup profile for black users
* fix webui not launching with --nowebui
* catch exception for non git extensions
* fix some options missing from /sdapi/v1/options
* fix for extension update status always saying "unknown"
* fix display of extra network cards that have `<>` in the name
* update lora extension to work with python 3.8
## 1.5.0
### Features:
* SD XL support
* user metadata system for custom networks
* extended Lora metadata editor: set activation text, default weight, view tags, training info
* Lora extension rework to include other types of networks (all that were previously handled by LyCORIS extension)
* show github stars for extenstions
* img2img batch mode can read extra stuff from png info
* img2img batch works with subdirectories
* hotkeys to move prompt elements: alt+left/right
* restyle time taken/VRAM display
* add textual inversion hashes to infotext
* optimization: cache git extension repo information
* move generate button next to the generated picture for mobile clients
* hide cards for networks of incompatible Stable Diffusion version in Lora extra networks interface
* skip installing packages with pip if they all are already installed - startup speedup of about 2 seconds
### Minor:
* checkbox to check/uncheck all extensions in the Installed tab
* add gradio user to infotext and to filename patterns
* allow gif for extra network previews
* add options to change colors in grid
* use natural sort for items in extra networks
* Mac: use empty_cache() from torch 2 to clear VRAM
* added automatic support for installing the right libraries for Navi3 (AMD)
* add option SWIN_torch_compile to accelerate SwinIR upscale
* suppress printing TI embedding info at start to console by default
* speedup extra networks listing
* added `[none]` filename token.
* removed thumbs extra networks view mode (use settings tab to change width/height/scale to get thumbs)
* add always_discard_next_to_last_sigma option to XYZ plot
* automatically switch to 32-bit float VAE if the generated picture has NaNs without the need for `--no-half-vae` commandline flag.
### Extensions and API:
* api endpoints: /sdapi/v1/server-kill, /sdapi/v1/server-restart, /sdapi/v1/server-stop
* allow Script to have custom metaclass
* add model exists status check /sdapi/v1/options
* rename --add-stop-route to --api-server-stop
* add `before_hr` script callback
* add callback `after_extra_networks_activate`
* disable rich exception output in console for API by default, use WEBUI_RICH_EXCEPTIONS env var to enable
* return http 404 when thumb file not found
* allow replacing extensions index with environment variable
### Bug Fixes:
* fix for catch errors when retrieving extension index #11290
* fix very slow loading speed of .safetensors files when reading from network drives
* API cache cleanup
* fix UnicodeEncodeError when writing to file CLIP Interrogator batch mode
* fix warning of 'has_mps' deprecated from PyTorch
* fix problem with extra network saving images as previews losing generation info
* fix throwing exception when trying to resize image with I;16 mode
* fix for #11534: canvas zoom and pan extension hijacking shortcut keys
* fixed launch script to be runnable from any directory
* don't add "Seed Resize: -1x-1" to API image metadata
* correctly remove end parenthesis with ctrl+up/down
* fixing --subpath on newer gradio version
* fix: check fill size none zero when resize (fixes #11425)
* use submit and blur for quick settings textbox
* save img2img batch with images.save_image()
* prevent running preload.py for disabled extensions
* fix: previously, model name was added together with directory name to infotext and to [model_name] filename pattern; directory name is now not included
## 1.4.1
### Bug Fixes:
* add queue lock for refresh-checkpoints
## 1.4.0 ## 1.4.0
### Features: ### Features:
+7
View File
@@ -0,0 +1,7 @@
cff-version: 1.2.0
message: "If you use this software, please cite it as below."
authors:
- given-names: AUTOMATIC1111
title: "Stable Diffusion Web UI"
date-released: 2022-08-22
url: "https://github.com/AUTOMATIC1111/stable-diffusion-webui"
+20 -8
View File
@@ -78,7 +78,7 @@ A browser interface based on Gradio library for Stable Diffusion.
- Clip skip - Clip skip
- Hypernetworks - Hypernetworks
- Loras (same as Hypernetworks but more pretty) - Loras (same as Hypernetworks but more pretty)
- A sparate UI where you can choose, with preview, which embeddings, hypernetworks or Loras to add to your prompt - A separate UI where you can choose, with preview, which embeddings, hypernetworks or Loras to add to your prompt
- Can select to load a different VAE from settings screen - Can select to load a different VAE from settings screen
- Estimated completion time in progress bar - Estimated completion time in progress bar
- API - API
@@ -88,19 +88,23 @@ A browser interface based on Gradio library for Stable Diffusion.
- [Alt-Diffusion](https://arxiv.org/abs/2211.06679) support - see [wiki](https://github.com/AUTOMATIC1111/stable-diffusion-webui/wiki/Features#alt-diffusion) for instructions - [Alt-Diffusion](https://arxiv.org/abs/2211.06679) support - see [wiki](https://github.com/AUTOMATIC1111/stable-diffusion-webui/wiki/Features#alt-diffusion) for instructions
- Now without any bad letters! - Now without any bad letters!
- Load checkpoints in safetensors format - Load checkpoints in safetensors format
- Eased resolution restriction: generated image's domension must be a multiple of 8 rather than 64 - Eased resolution restriction: generated image's dimensions must be a multiple of 8 rather than 64
- Now with a license! - Now with a license!
- Reorder elements in the UI from settings screen - Reorder elements in the UI from settings screen
- [Segmind Stable Diffusion](https://huggingface.co/segmind/SSD-1B) support
## Installation and Running ## Installation and Running
Make sure the required [dependencies](https://github.com/AUTOMATIC1111/stable-diffusion-webui/wiki/Dependencies) are met and follow the instructions available for both [NVidia](https://github.com/AUTOMATIC1111/stable-diffusion-webui/wiki/Install-and-Run-on-NVidia-GPUs) (recommended) and [AMD](https://github.com/AUTOMATIC1111/stable-diffusion-webui/wiki/Install-and-Run-on-AMD-GPUs) GPUs. Make sure the required [dependencies](https://github.com/AUTOMATIC1111/stable-diffusion-webui/wiki/Dependencies) are met and follow the instructions available for:
- [NVidia](https://github.com/AUTOMATIC1111/stable-diffusion-webui/wiki/Install-and-Run-on-NVidia-GPUs) (recommended)
- [AMD](https://github.com/AUTOMATIC1111/stable-diffusion-webui/wiki/Install-and-Run-on-AMD-GPUs) GPUs.
- [Intel CPUs, Intel GPUs (both integrated and discrete)](https://github.com/openvinotoolkit/stable-diffusion-webui/wiki/Installation-on-Intel-Silicon) (external wiki page)
Alternatively, use online services (like Google Colab): Alternatively, use online services (like Google Colab):
- [List of Online Services](https://github.com/AUTOMATIC1111/stable-diffusion-webui/wiki/Online-Services) - [List of Online Services](https://github.com/AUTOMATIC1111/stable-diffusion-webui/wiki/Online-Services)
### Installation on Windows 10/11 with NVidia-GPUs using release package ### Installation on Windows 10/11 with NVidia-GPUs using release package
1. Download `sd.webui.zip` from [v1.0.0-pre](https://github.com/AUTOMATIC1111/stable-diffusion-webui/releases/tag/v1.0.0-pre) and extract it's contents. 1. Download `sd.webui.zip` from [v1.0.0-pre](https://github.com/AUTOMATIC1111/stable-diffusion-webui/releases/tag/v1.0.0-pre) and extract its contents.
2. Run `update.bat`. 2. Run `update.bat`.
3. Run `run.bat`. 3. Run `run.bat`.
> For more details see [Install-and-Run-on-NVidia-GPUs](https://github.com/AUTOMATIC1111/stable-diffusion-webui/wiki/Install-and-Run-on-NVidia-GPUs) > For more details see [Install-and-Run-on-NVidia-GPUs](https://github.com/AUTOMATIC1111/stable-diffusion-webui/wiki/Install-and-Run-on-NVidia-GPUs)
@@ -115,15 +119,17 @@ Alternatively, use online services (like Google Colab):
1. Install the dependencies: 1. Install the dependencies:
```bash ```bash
# Debian-based: # Debian-based:
sudo apt install wget git python3 python3-venv sudo apt install wget git python3 python3-venv libgl1 libglib2.0-0
# Red Hat-based: # Red Hat-based:
sudo dnf install wget git python3 sudo dnf install wget git python3 gperftools-libs libglvnd-glx
# openSUSE-based:
sudo zypper install wget git python3 libtcmalloc4 libglvnd
# Arch-based: # Arch-based:
sudo pacman -S wget git python3 sudo pacman -S wget git python3
``` ```
2. Navigate to the directory you would like the webui to be installed and execute the following command: 2. Navigate to the directory you would like the webui to be installed and execute the following command:
```bash ```bash
bash <(wget -qO- https://raw.githubusercontent.com/AUTOMATIC1111/stable-diffusion-webui/master/webui.sh) wget -q https://raw.githubusercontent.com/AUTOMATIC1111/stable-diffusion-webui/master/webui.sh
``` ```
3. Run `webui.sh`. 3. Run `webui.sh`.
4. Check `webui-user.sh` for options. 4. Check `webui-user.sh` for options.
@@ -135,12 +141,15 @@ Find the instructions [here](https://github.com/AUTOMATIC1111/stable-diffusion-w
Here's how to add code to this repo: [Contributing](https://github.com/AUTOMATIC1111/stable-diffusion-webui/wiki/Contributing) Here's how to add code to this repo: [Contributing](https://github.com/AUTOMATIC1111/stable-diffusion-webui/wiki/Contributing)
## Documentation ## Documentation
The documentation was moved from this README over to the project's [wiki](https://github.com/AUTOMATIC1111/stable-diffusion-webui/wiki). The documentation was moved from this README over to the project's [wiki](https://github.com/AUTOMATIC1111/stable-diffusion-webui/wiki).
For the purposes of getting Google and other search engines to crawl the wiki, here's a link to the (not for humans) [crawlable wiki](https://github-wiki-see.page/m/AUTOMATIC1111/stable-diffusion-webui/wiki).
## Credits ## Credits
Licenses for borrowed code can be found in `Settings -> Licenses` screen, and also in `html/licenses.html` file. Licenses for borrowed code can be found in `Settings -> Licenses` screen, and also in `html/licenses.html` file.
- Stable Diffusion - https://github.com/CompVis/stable-diffusion, https://github.com/CompVis/taming-transformers - Stable Diffusion - https://github.com/Stability-AI/stablediffusion, https://github.com/CompVis/taming-transformers
- k-diffusion - https://github.com/crowsonkb/k-diffusion.git - k-diffusion - https://github.com/crowsonkb/k-diffusion.git
- GFPGAN - https://github.com/TencentARC/GFPGAN.git - GFPGAN - https://github.com/TencentARC/GFPGAN.git
- CodeFormer - https://github.com/sczhou/CodeFormer - CodeFormer - https://github.com/sczhou/CodeFormer
@@ -165,5 +174,8 @@ Licenses for borrowed code can be found in `Settings -> Licenses` screen, and al
- Security advice - RyotaK - Security advice - RyotaK
- UniPC sampler - Wenliang Zhao - https://github.com/wl-zhao/UniPC - UniPC sampler - Wenliang Zhao - https://github.com/wl-zhao/UniPC
- TAESD - Ollin Boer Bohan - https://github.com/madebyollin/taesd - TAESD - Ollin Boer Bohan - https://github.com/madebyollin/taesd
- LyCORIS - KohakuBlueleaf
- Restart sampling - lambertae - https://github.com/Newbeeer/diffusion_restart_sampling
- Hypertile - tfernd - https://github.com/tfernd/HyperTile
- Initial Gradio script - posted on 4chan by an Anonymous user. Thank you Anonymous user. - Initial Gradio script - posted on 4chan by an Anonymous user. Thank you Anonymous user.
- (You) - (You)
+73
View File
@@ -0,0 +1,73 @@
model:
base_learning_rate: 1.0e-04
target: ldm.models.diffusion.ddpm.LatentDiffusion
params:
linear_start: 0.00085
linear_end: 0.0120
num_timesteps_cond: 1
log_every_t: 200
timesteps: 1000
first_stage_key: "jpg"
cond_stage_key: "txt"
image_size: 64
channels: 4
cond_stage_trainable: false # Note: different from the one we trained before
conditioning_key: crossattn
monitor: val/loss_simple_ema
scale_factor: 0.18215
use_ema: False
scheduler_config: # 10000 warmup steps
target: ldm.lr_scheduler.LambdaLinearScheduler
params:
warm_up_steps: [ 10000 ]
cycle_lengths: [ 10000000000000 ] # incredibly large number to prevent corner cases
f_start: [ 1.e-6 ]
f_max: [ 1. ]
f_min: [ 1. ]
unet_config:
target: ldm.modules.diffusionmodules.openaimodel.UNetModel
params:
image_size: 32 # unused
in_channels: 4
out_channels: 4
model_channels: 320
attention_resolutions: [ 4, 2, 1 ]
num_res_blocks: 2
channel_mult: [ 1, 2, 4, 4 ]
num_head_channels: 64
use_spatial_transformer: True
use_linear_in_transformer: True
transformer_depth: 1
context_dim: 1024
use_checkpoint: True
legacy: False
first_stage_config:
target: ldm.models.autoencoder.AutoencoderKL
params:
embed_dim: 4
monitor: val/rec_loss
ddconfig:
double_z: true
z_channels: 4
resolution: 256
in_channels: 3
out_ch: 3
ch: 128
ch_mult:
- 1
- 2
- 4
- 4
num_res_blocks: 2
attn_resolutions: []
dropout: 0.0
lossconfig:
target: torch.nn.Identity
cond_stage_config:
target: modules.xlmr_m18.BertSeriesModelWithTransformation
params:
name: "XLMR-Large"
+3 -5
View File
@@ -12,7 +12,7 @@ import safetensors.torch
from ldm.models.diffusion.ddim import DDIMSampler from ldm.models.diffusion.ddim import DDIMSampler
from ldm.util import instantiate_from_config, ismap from ldm.util import instantiate_from_config, ismap
from modules import shared, sd_hijack from modules import shared, sd_hijack, devices
cached_ldsr_model: torch.nn.Module = None cached_ldsr_model: torch.nn.Module = None
@@ -112,8 +112,7 @@ class LDSR:
gc.collect() gc.collect()
if torch.cuda.is_available: devices.torch_gc()
torch.cuda.empty_cache()
im_og = image im_og = image
width_og, height_og = im_og.size width_og, height_og = im_og.size
@@ -150,8 +149,7 @@ class LDSR:
del model del model
gc.collect() gc.collect()
if torch.cuda.is_available: devices.torch_gc()
torch.cuda.empty_cache()
return a return a
+6 -10
View File
@@ -1,7 +1,6 @@
import os import os
from basicsr.utils.download_util import load_file_from_url from modules.modelloader import load_file_from_url
from modules.upscaler import Upscaler, UpscalerData from modules.upscaler import Upscaler, UpscalerData
from ldsr_model_arch import LDSR from ldsr_model_arch import LDSR
from modules import shared, script_callbacks, errors from modules import shared, script_callbacks, errors
@@ -43,20 +42,17 @@ class UpscalerLDSR(Upscaler):
if local_safetensors_path is not None and os.path.exists(local_safetensors_path): if local_safetensors_path is not None and os.path.exists(local_safetensors_path):
model = local_safetensors_path model = local_safetensors_path
else: else:
model = local_ckpt_path if local_ckpt_path is not None else load_file_from_url(url=self.model_url, model_dir=self.model_download_path, file_name="model.ckpt", progress=True) model = local_ckpt_path or load_file_from_url(self.model_url, model_dir=self.model_download_path, file_name="model.ckpt")
yaml = local_yaml_path if local_yaml_path is not None else load_file_from_url(url=self.yaml_url, model_dir=self.model_download_path, file_name="project.yaml", progress=True) yaml = local_yaml_path or load_file_from_url(self.yaml_url, model_dir=self.model_download_path, file_name="project.yaml")
try:
return LDSR(model, yaml) return LDSR(model, yaml)
except Exception:
errors.report("Error importing LDSR", exc_info=True)
return None
def do_upscale(self, img, path): def do_upscale(self, img, path):
try:
ldsr = self.load_model(path) ldsr = self.load_model(path)
if ldsr is None: except Exception:
print("NO LDSR!") errors.report(f"Failed loading LDSR model {path}", exc_info=True)
return img return img
ddim_steps = shared.opts.ldsr_steps ddim_steps = shared.opts.ldsr_steps
return ldsr.super_resolution(img, ddim_steps, self.scale) return ldsr.super_resolution(img, ddim_steps, self.scale)
+35 -13
View File
@@ -1,32 +1,51 @@
from modules import extra_networks, shared from modules import extra_networks, shared
import lora import networks
class ExtraNetworkLora(extra_networks.ExtraNetwork): class ExtraNetworkLora(extra_networks.ExtraNetwork):
def __init__(self): def __init__(self):
super().__init__('lora') super().__init__('lora')
self.errors = {}
"""mapping of network names to the number of errors the network had during operation"""
def activate(self, p, params_list): def activate(self, p, params_list):
additional = shared.opts.sd_lora additional = shared.opts.sd_lora
if additional != "None" and additional in lora.available_loras and not any(x for x in params_list if x.items[0] == additional): self.errors.clear()
if additional != "None" and additional in networks.available_networks and not any(x for x in params_list if x.items[0] == additional):
p.all_prompts = [x + f"<lora:{additional}:{shared.opts.extra_networks_default_multiplier}>" for x in p.all_prompts] p.all_prompts = [x + f"<lora:{additional}:{shared.opts.extra_networks_default_multiplier}>" for x in p.all_prompts]
params_list.append(extra_networks.ExtraNetworkParams(items=[additional, shared.opts.extra_networks_default_multiplier])) params_list.append(extra_networks.ExtraNetworkParams(items=[additional, shared.opts.extra_networks_default_multiplier]))
names = [] names = []
multipliers = [] te_multipliers = []
unet_multipliers = []
dyn_dims = []
for params in params_list: for params in params_list:
assert params.items assert params.items
names.append(params.items[0]) names.append(params.positional[0])
multipliers.append(float(params.items[1]) if len(params.items) > 1 else 1.0)
lora.load_loras(names, multipliers) te_multiplier = float(params.positional[1]) if len(params.positional) > 1 else 1.0
te_multiplier = float(params.named.get("te", te_multiplier))
unet_multiplier = float(params.positional[2]) if len(params.positional) > 2 else te_multiplier
unet_multiplier = float(params.named.get("unet", unet_multiplier))
dyn_dim = int(params.positional[3]) if len(params.positional) > 3 else None
dyn_dim = int(params.named["dyn"]) if "dyn" in params.named else dyn_dim
te_multipliers.append(te_multiplier)
unet_multipliers.append(unet_multiplier)
dyn_dims.append(dyn_dim)
networks.load_networks(names, te_multipliers, unet_multipliers, dyn_dims)
if shared.opts.lora_add_hashes_to_infotext: if shared.opts.lora_add_hashes_to_infotext:
lora_hashes = [] network_hashes = []
for item in lora.loaded_loras: for item in networks.loaded_networks:
shorthash = item.lora_on_disk.shorthash shorthash = item.network_on_disk.shorthash
if not shorthash: if not shorthash:
continue continue
@@ -36,10 +55,13 @@ class ExtraNetworkLora(extra_networks.ExtraNetwork):
alias = alias.replace(":", "").replace(",", "") alias = alias.replace(":", "").replace(",", "")
lora_hashes.append(f"{alias}: {shorthash}") network_hashes.append(f"{alias}: {shorthash}")
if lora_hashes: if network_hashes:
p.extra_generation_params["Lora hashes"] = ", ".join(lora_hashes) p.extra_generation_params["Lora hashes"] = ", ".join(network_hashes)
def deactivate(self, p): def deactivate(self, p):
pass if self.errors:
p.comment("Networks with errors: " + ", ".join(f"{k} ({v})" for k, v in self.errors.items()))
self.errors.clear()
+7 -504
View File
@@ -1,506 +1,9 @@
import os import networks
import re
import torch
from typing import Union
from modules import shared, devices, sd_models, errors, scripts, sd_hijack, hashes list_available_loras = networks.list_available_networks
metadata_tags_order = {"ss_sd_model_name": 1, "ss_resolution": 2, "ss_clip_skip": 3, "ss_num_train_images": 10, "ss_tag_frequency": 20} available_loras = networks.available_networks
available_lora_aliases = networks.available_network_aliases
re_digits = re.compile(r"\d+") available_lora_hash_lookup = networks.available_network_hash_lookup
re_x_proj = re.compile(r"(.*)_([qkv]_proj)$") forbidden_lora_aliases = networks.forbidden_network_aliases
re_compiled = {} loaded_loras = networks.loaded_networks
suffix_conversion = {
"attentions": {},
"resnets": {
"conv1": "in_layers_2",
"conv2": "out_layers_3",
"time_emb_proj": "emb_layers_1",
"conv_shortcut": "skip_connection",
}
}
def convert_diffusers_name_to_compvis(key, is_sd2):
def match(match_list, regex_text):
regex = re_compiled.get(regex_text)
if regex is None:
regex = re.compile(regex_text)
re_compiled[regex_text] = regex
r = re.match(regex, key)
if not r:
return False
match_list.clear()
match_list.extend([int(x) if re.match(re_digits, x) else x for x in r.groups()])
return True
m = []
if match(m, r"lora_unet_down_blocks_(\d+)_(attentions|resnets)_(\d+)_(.+)"):
suffix = suffix_conversion.get(m[1], {}).get(m[3], m[3])
return f"diffusion_model_input_blocks_{1 + m[0] * 3 + m[2]}_{1 if m[1] == 'attentions' else 0}_{suffix}"
if match(m, r"lora_unet_mid_block_(attentions|resnets)_(\d+)_(.+)"):
suffix = suffix_conversion.get(m[0], {}).get(m[2], m[2])
return f"diffusion_model_middle_block_{1 if m[0] == 'attentions' else m[1] * 2}_{suffix}"
if match(m, r"lora_unet_up_blocks_(\d+)_(attentions|resnets)_(\d+)_(.+)"):
suffix = suffix_conversion.get(m[1], {}).get(m[3], m[3])
return f"diffusion_model_output_blocks_{m[0] * 3 + m[2]}_{1 if m[1] == 'attentions' else 0}_{suffix}"
if match(m, r"lora_unet_down_blocks_(\d+)_downsamplers_0_conv"):
return f"diffusion_model_input_blocks_{3 + m[0] * 3}_0_op"
if match(m, r"lora_unet_up_blocks_(\d+)_upsamplers_0_conv"):
return f"diffusion_model_output_blocks_{2 + m[0] * 3}_{2 if m[0]>0 else 1}_conv"
if match(m, r"lora_te_text_model_encoder_layers_(\d+)_(.+)"):
if is_sd2:
if 'mlp_fc1' in m[1]:
return f"model_transformer_resblocks_{m[0]}_{m[1].replace('mlp_fc1', 'mlp_c_fc')}"
elif 'mlp_fc2' in m[1]:
return f"model_transformer_resblocks_{m[0]}_{m[1].replace('mlp_fc2', 'mlp_c_proj')}"
else:
return f"model_transformer_resblocks_{m[0]}_{m[1].replace('self_attn', 'attn')}"
return f"transformer_text_model_encoder_layers_{m[0]}_{m[1]}"
return key
class LoraOnDisk:
def __init__(self, name, filename):
self.name = name
self.filename = filename
self.metadata = {}
self.is_safetensors = os.path.splitext(filename)[1].lower() == ".safetensors"
if self.is_safetensors:
try:
self.metadata = sd_models.read_metadata_from_safetensors(filename)
except Exception as e:
errors.display(e, f"reading lora {filename}")
if self.metadata:
m = {}
for k, v in sorted(self.metadata.items(), key=lambda x: metadata_tags_order.get(x[0], 999)):
m[k] = v
self.metadata = m
self.ssmd_cover_images = self.metadata.pop('ssmd_cover_images', None) # those are cover images and they are too big to display in UI as text
self.alias = self.metadata.get('ss_output_name', self.name)
self.hash = None
self.shorthash = None
self.set_hash(
self.metadata.get('sshs_model_hash') or
hashes.sha256_from_cache(self.filename, "lora/" + self.name, use_addnet_hash=self.is_safetensors) or
''
)
def set_hash(self, v):
self.hash = v
self.shorthash = self.hash[0:12]
if self.shorthash:
available_lora_hash_lookup[self.shorthash] = self
def read_hash(self):
if not self.hash:
self.set_hash(hashes.sha256(self.filename, "lora/" + self.name, use_addnet_hash=self.is_safetensors) or '')
def get_alias(self):
if shared.opts.lora_preferred_name == "Filename" or self.alias.lower() in forbidden_lora_aliases:
return self.name
else:
return self.alias
class LoraModule:
def __init__(self, name, lora_on_disk: LoraOnDisk):
self.name = name
self.lora_on_disk = lora_on_disk
self.multiplier = 1.0
self.modules = {}
self.mtime = None
self.mentioned_name = None
"""the text that was used to add lora to prompt - can be either name or an alias"""
class LoraUpDownModule:
def __init__(self):
self.up = None
self.down = None
self.alpha = None
def assign_lora_names_to_compvis_modules(sd_model):
lora_layer_mapping = {}
for name, module in shared.sd_model.cond_stage_model.wrapped.named_modules():
lora_name = name.replace(".", "_")
lora_layer_mapping[lora_name] = module
module.lora_layer_name = lora_name
for name, module in shared.sd_model.model.named_modules():
lora_name = name.replace(".", "_")
lora_layer_mapping[lora_name] = module
module.lora_layer_name = lora_name
sd_model.lora_layer_mapping = lora_layer_mapping
def load_lora(name, lora_on_disk):
lora = LoraModule(name, lora_on_disk)
lora.mtime = os.path.getmtime(lora_on_disk.filename)
sd = sd_models.read_state_dict(lora_on_disk.filename)
# this should not be needed but is here as an emergency fix for an unknown error people are experiencing in 1.2.0
if not hasattr(shared.sd_model, 'lora_layer_mapping'):
assign_lora_names_to_compvis_modules(shared.sd_model)
keys_failed_to_match = {}
is_sd2 = 'model_transformer_resblocks' in shared.sd_model.lora_layer_mapping
for key_diffusers, weight in sd.items():
key_diffusers_without_lora_parts, lora_key = key_diffusers.split(".", 1)
key = convert_diffusers_name_to_compvis(key_diffusers_without_lora_parts, is_sd2)
sd_module = shared.sd_model.lora_layer_mapping.get(key, None)
if sd_module is None:
m = re_x_proj.match(key)
if m:
sd_module = shared.sd_model.lora_layer_mapping.get(m.group(1), None)
if sd_module is None:
keys_failed_to_match[key_diffusers] = key
continue
lora_module = lora.modules.get(key, None)
if lora_module is None:
lora_module = LoraUpDownModule()
lora.modules[key] = lora_module
if lora_key == "alpha":
lora_module.alpha = weight.item()
continue
if type(sd_module) == torch.nn.Linear:
module = torch.nn.Linear(weight.shape[1], weight.shape[0], bias=False)
elif type(sd_module) == torch.nn.modules.linear.NonDynamicallyQuantizableLinear:
module = torch.nn.Linear(weight.shape[1], weight.shape[0], bias=False)
elif type(sd_module) == torch.nn.MultiheadAttention:
module = torch.nn.Linear(weight.shape[1], weight.shape[0], bias=False)
elif type(sd_module) == torch.nn.Conv2d and weight.shape[2:] == (1, 1):
module = torch.nn.Conv2d(weight.shape[1], weight.shape[0], (1, 1), bias=False)
elif type(sd_module) == torch.nn.Conv2d and weight.shape[2:] == (3, 3):
module = torch.nn.Conv2d(weight.shape[1], weight.shape[0], (3, 3), bias=False)
else:
print(f'Lora layer {key_diffusers} matched a layer with unsupported type: {type(sd_module).__name__}')
continue
raise AssertionError(f"Lora layer {key_diffusers} matched a layer with unsupported type: {type(sd_module).__name__}")
with torch.no_grad():
module.weight.copy_(weight)
module.to(device=devices.cpu, dtype=devices.dtype)
if lora_key == "lora_up.weight":
lora_module.up = module
elif lora_key == "lora_down.weight":
lora_module.down = module
else:
raise AssertionError(f"Bad Lora layer name: {key_diffusers} - must end in lora_up.weight, lora_down.weight or alpha")
if keys_failed_to_match:
print(f"Failed to match keys when loading Lora {lora_on_disk.filename}: {keys_failed_to_match}")
return lora
def load_loras(names, multipliers=None):
already_loaded = {}
for lora in loaded_loras:
if lora.name in names:
already_loaded[lora.name] = lora
loaded_loras.clear()
loras_on_disk = [available_lora_aliases.get(name, None) for name in names]
if any(x is None for x in loras_on_disk):
list_available_loras()
loras_on_disk = [available_lora_aliases.get(name, None) for name in names]
failed_to_load_loras = []
for i, name in enumerate(names):
lora = already_loaded.get(name, None)
lora_on_disk = loras_on_disk[i]
if lora_on_disk is not None:
if lora is None or os.path.getmtime(lora_on_disk.filename) > lora.mtime:
try:
lora = load_lora(name, lora_on_disk)
except Exception as e:
errors.display(e, f"loading Lora {lora_on_disk.filename}")
continue
lora.mentioned_name = name
lora_on_disk.read_hash()
if lora is None:
failed_to_load_loras.append(name)
print(f"Couldn't find Lora with name {name}")
continue
lora.multiplier = multipliers[i] if multipliers else 1.0
loaded_loras.append(lora)
if failed_to_load_loras:
sd_hijack.model_hijack.comments.append("Failed to find Loras: " + ", ".join(failed_to_load_loras))
def lora_calc_updown(lora, module, target):
with torch.no_grad():
up = module.up.weight.to(target.device, dtype=target.dtype)
down = module.down.weight.to(target.device, dtype=target.dtype)
if up.shape[2:] == (1, 1) and down.shape[2:] == (1, 1):
updown = (up.squeeze(2).squeeze(2) @ down.squeeze(2).squeeze(2)).unsqueeze(2).unsqueeze(3)
elif up.shape[2:] == (3, 3) or down.shape[2:] == (3, 3):
updown = torch.nn.functional.conv2d(down.permute(1, 0, 2, 3), up).permute(1, 0, 2, 3)
else:
updown = up @ down
updown = updown * lora.multiplier * (module.alpha / module.up.weight.shape[1] if module.alpha else 1.0)
return updown
def lora_restore_weights_from_backup(self: Union[torch.nn.Conv2d, torch.nn.Linear, torch.nn.MultiheadAttention]):
weights_backup = getattr(self, "lora_weights_backup", None)
if weights_backup is None:
return
if isinstance(self, torch.nn.MultiheadAttention):
self.in_proj_weight.copy_(weights_backup[0])
self.out_proj.weight.copy_(weights_backup[1])
else:
self.weight.copy_(weights_backup)
def lora_apply_weights(self: Union[torch.nn.Conv2d, torch.nn.Linear, torch.nn.MultiheadAttention]):
"""
Applies the currently selected set of Loras to the weights of torch layer self.
If weights already have this particular set of loras applied, does nothing.
If not, restores orginal weights from backup and alters weights according to loras.
"""
lora_layer_name = getattr(self, 'lora_layer_name', None)
if lora_layer_name is None:
return
current_names = getattr(self, "lora_current_names", ())
wanted_names = tuple((x.name, x.multiplier) for x in loaded_loras)
weights_backup = getattr(self, "lora_weights_backup", None)
if weights_backup is None:
if isinstance(self, torch.nn.MultiheadAttention):
weights_backup = (self.in_proj_weight.to(devices.cpu, copy=True), self.out_proj.weight.to(devices.cpu, copy=True))
else:
weights_backup = self.weight.to(devices.cpu, copy=True)
self.lora_weights_backup = weights_backup
if current_names != wanted_names:
lora_restore_weights_from_backup(self)
for lora in loaded_loras:
module = lora.modules.get(lora_layer_name, None)
if module is not None and hasattr(self, 'weight'):
self.weight += lora_calc_updown(lora, module, self.weight)
continue
module_q = lora.modules.get(lora_layer_name + "_q_proj", None)
module_k = lora.modules.get(lora_layer_name + "_k_proj", None)
module_v = lora.modules.get(lora_layer_name + "_v_proj", None)
module_out = lora.modules.get(lora_layer_name + "_out_proj", None)
if isinstance(self, torch.nn.MultiheadAttention) and module_q and module_k and module_v and module_out:
updown_q = lora_calc_updown(lora, module_q, self.in_proj_weight)
updown_k = lora_calc_updown(lora, module_k, self.in_proj_weight)
updown_v = lora_calc_updown(lora, module_v, self.in_proj_weight)
updown_qkv = torch.vstack([updown_q, updown_k, updown_v])
self.in_proj_weight += updown_qkv
self.out_proj.weight += lora_calc_updown(lora, module_out, self.out_proj.weight)
continue
if module is None:
continue
print(f'failed to calculate lora weights for layer {lora_layer_name}')
self.lora_current_names = wanted_names
def lora_forward(module, input, original_forward):
"""
Old way of applying Lora by executing operations during layer's forward.
Stacking many loras this way results in big performance degradation.
"""
if len(loaded_loras) == 0:
return original_forward(module, input)
input = devices.cond_cast_unet(input)
lora_restore_weights_from_backup(module)
lora_reset_cached_weight(module)
res = original_forward(module, input)
lora_layer_name = getattr(module, 'lora_layer_name', None)
for lora in loaded_loras:
module = lora.modules.get(lora_layer_name, None)
if module is None:
continue
module.up.to(device=devices.device)
module.down.to(device=devices.device)
res = res + module.up(module.down(input)) * lora.multiplier * (module.alpha / module.up.weight.shape[1] if module.alpha else 1.0)
return res
def lora_reset_cached_weight(self: Union[torch.nn.Conv2d, torch.nn.Linear]):
self.lora_current_names = ()
self.lora_weights_backup = None
def lora_Linear_forward(self, input):
if shared.opts.lora_functional:
return lora_forward(self, input, torch.nn.Linear_forward_before_lora)
lora_apply_weights(self)
return torch.nn.Linear_forward_before_lora(self, input)
def lora_Linear_load_state_dict(self, *args, **kwargs):
lora_reset_cached_weight(self)
return torch.nn.Linear_load_state_dict_before_lora(self, *args, **kwargs)
def lora_Conv2d_forward(self, input):
if shared.opts.lora_functional:
return lora_forward(self, input, torch.nn.Conv2d_forward_before_lora)
lora_apply_weights(self)
return torch.nn.Conv2d_forward_before_lora(self, input)
def lora_Conv2d_load_state_dict(self, *args, **kwargs):
lora_reset_cached_weight(self)
return torch.nn.Conv2d_load_state_dict_before_lora(self, *args, **kwargs)
def lora_MultiheadAttention_forward(self, *args, **kwargs):
lora_apply_weights(self)
return torch.nn.MultiheadAttention_forward_before_lora(self, *args, **kwargs)
def lora_MultiheadAttention_load_state_dict(self, *args, **kwargs):
lora_reset_cached_weight(self)
return torch.nn.MultiheadAttention_load_state_dict_before_lora(self, *args, **kwargs)
def list_available_loras():
available_loras.clear()
available_lora_aliases.clear()
forbidden_lora_aliases.clear()
available_lora_hash_lookup.clear()
forbidden_lora_aliases.update({"none": 1, "Addams": 1})
os.makedirs(shared.cmd_opts.lora_dir, exist_ok=True)
candidates = list(shared.walk_files(shared.cmd_opts.lora_dir, allowed_extensions=[".pt", ".ckpt", ".safetensors"]))
for filename in sorted(candidates, key=str.lower):
if os.path.isdir(filename):
continue
name = os.path.splitext(os.path.basename(filename))[0]
try:
entry = LoraOnDisk(name, filename)
except OSError: # should catch FileNotFoundError and PermissionError etc.
errors.report(f"Failed to load LoRA {name} from {filename}", exc_info=True)
continue
available_loras[name] = entry
if entry.alias in available_lora_aliases:
forbidden_lora_aliases[entry.alias.lower()] = 1
available_lora_aliases[name] = entry
available_lora_aliases[entry.alias] = entry
re_lora_name = re.compile(r"(.*)\s*\([0-9a-fA-F]+\)")
def infotext_pasted(infotext, params):
if "AddNet Module 1" in [x[1] for x in scripts.scripts_txt2img.infotext_fields]:
return # if the other extension is active, it will handle those fields, no need to do anything
added = []
for k in params:
if not k.startswith("AddNet Model "):
continue
num = k[13:]
if params.get("AddNet Module " + num) != "LoRA":
continue
name = params.get("AddNet Model " + num)
if name is None:
continue
m = re_lora_name.match(name)
if m:
name = m.group(1)
multiplier = params.get("AddNet Weight A " + num, "1.0")
added.append(f"<lora:{name}:{multiplier}>")
if added:
params["Prompt"] += "\n" + "".join(added)
available_loras = {}
available_lora_aliases = {}
available_lora_hash_lookup = {}
forbidden_lora_aliases = {}
loaded_loras = []
list_available_loras()
+33
View File
@@ -0,0 +1,33 @@
import sys
import copy
import logging
class ColoredFormatter(logging.Formatter):
COLORS = {
"DEBUG": "\033[0;36m", # CYAN
"INFO": "\033[0;32m", # GREEN
"WARNING": "\033[0;33m", # YELLOW
"ERROR": "\033[0;31m", # RED
"CRITICAL": "\033[0;37;41m", # WHITE ON RED
"RESET": "\033[0m", # RESET COLOR
}
def format(self, record):
colored_record = copy.copy(record)
levelname = colored_record.levelname
seq = self.COLORS.get(levelname, self.COLORS["RESET"])
colored_record.levelname = f"{seq}{levelname}{self.COLORS['RESET']}"
return super().format(colored_record)
logger = logging.getLogger("lora")
logger.propagate = False
if not logger.handlers:
handler = logging.StreamHandler(sys.stdout)
handler.setFormatter(
ColoredFormatter("[%(name)s]-%(levelname)s: %(message)s")
)
logger.addHandler(handler)
+31
View File
@@ -0,0 +1,31 @@
import torch
import networks
from modules import patches
class LoraPatches:
def __init__(self):
self.Linear_forward = patches.patch(__name__, torch.nn.Linear, 'forward', networks.network_Linear_forward)
self.Linear_load_state_dict = patches.patch(__name__, torch.nn.Linear, '_load_from_state_dict', networks.network_Linear_load_state_dict)
self.Conv2d_forward = patches.patch(__name__, torch.nn.Conv2d, 'forward', networks.network_Conv2d_forward)
self.Conv2d_load_state_dict = patches.patch(__name__, torch.nn.Conv2d, '_load_from_state_dict', networks.network_Conv2d_load_state_dict)
self.GroupNorm_forward = patches.patch(__name__, torch.nn.GroupNorm, 'forward', networks.network_GroupNorm_forward)
self.GroupNorm_load_state_dict = patches.patch(__name__, torch.nn.GroupNorm, '_load_from_state_dict', networks.network_GroupNorm_load_state_dict)
self.LayerNorm_forward = patches.patch(__name__, torch.nn.LayerNorm, 'forward', networks.network_LayerNorm_forward)
self.LayerNorm_load_state_dict = patches.patch(__name__, torch.nn.LayerNorm, '_load_from_state_dict', networks.network_LayerNorm_load_state_dict)
self.MultiheadAttention_forward = patches.patch(__name__, torch.nn.MultiheadAttention, 'forward', networks.network_MultiheadAttention_forward)
self.MultiheadAttention_load_state_dict = patches.patch(__name__, torch.nn.MultiheadAttention, '_load_from_state_dict', networks.network_MultiheadAttention_load_state_dict)
def undo(self):
self.Linear_forward = patches.undo(__name__, torch.nn.Linear, 'forward')
self.Linear_load_state_dict = patches.undo(__name__, torch.nn.Linear, '_load_from_state_dict')
self.Conv2d_forward = patches.undo(__name__, torch.nn.Conv2d, 'forward')
self.Conv2d_load_state_dict = patches.undo(__name__, torch.nn.Conv2d, '_load_from_state_dict')
self.GroupNorm_forward = patches.undo(__name__, torch.nn.GroupNorm, 'forward')
self.GroupNorm_load_state_dict = patches.undo(__name__, torch.nn.GroupNorm, '_load_from_state_dict')
self.LayerNorm_forward = patches.undo(__name__, torch.nn.LayerNorm, 'forward')
self.LayerNorm_load_state_dict = patches.undo(__name__, torch.nn.LayerNorm, '_load_from_state_dict')
self.MultiheadAttention_forward = patches.undo(__name__, torch.nn.MultiheadAttention, 'forward')
self.MultiheadAttention_load_state_dict = patches.undo(__name__, torch.nn.MultiheadAttention, '_load_from_state_dict')
+68
View File
@@ -0,0 +1,68 @@
import torch
def make_weight_cp(t, wa, wb):
temp = torch.einsum('i j k l, j r -> i r k l', t, wb)
return torch.einsum('i j k l, i r -> r j k l', temp, wa)
def rebuild_conventional(up, down, shape, dyn_dim=None):
up = up.reshape(up.size(0), -1)
down = down.reshape(down.size(0), -1)
if dyn_dim is not None:
up = up[:, :dyn_dim]
down = down[:dyn_dim, :]
return (up @ down).reshape(shape)
def rebuild_cp_decomposition(up, down, mid):
up = up.reshape(up.size(0), -1)
down = down.reshape(down.size(0), -1)
return torch.einsum('n m k l, i n, m j -> i j k l', mid, up, down)
# copied from https://github.com/KohakuBlueleaf/LyCORIS/blob/dev/lycoris/modules/lokr.py
def factorization(dimension: int, factor:int=-1) -> tuple[int, int]:
'''
return a tuple of two value of input dimension decomposed by the number closest to factor
second value is higher or equal than first value.
In LoRA with Kroneckor Product, first value is a value for weight scale.
secon value is a value for weight.
Becuase of non-commutative property, A⊗B ≠ B⊗A. Meaning of two matrices is slightly different.
examples)
factor
-1 2 4 8 16 ...
127 -> 1, 127 127 -> 1, 127 127 -> 1, 127 127 -> 1, 127 127 -> 1, 127
128 -> 8, 16 128 -> 2, 64 128 -> 4, 32 128 -> 8, 16 128 -> 8, 16
250 -> 10, 25 250 -> 2, 125 250 -> 2, 125 250 -> 5, 50 250 -> 10, 25
360 -> 8, 45 360 -> 2, 180 360 -> 4, 90 360 -> 8, 45 360 -> 12, 30
512 -> 16, 32 512 -> 2, 256 512 -> 4, 128 512 -> 8, 64 512 -> 16, 32
1024 -> 32, 32 1024 -> 2, 512 1024 -> 4, 256 1024 -> 8, 128 1024 -> 16, 64
'''
if factor > 0 and (dimension % factor) == 0:
m = factor
n = dimension // factor
if m > n:
n, m = m, n
return m, n
if factor < 0:
factor = dimension
m, n = 1, dimension
length = m + n
while m<n:
new_m = m + 1
while dimension%new_m != 0:
new_m += 1
new_n = dimension // new_m
if new_m + new_n > length or new_m>factor:
break
else:
m, n = new_m, new_n
if m > n:
n, m = m, n
return m, n
+159
View File
@@ -0,0 +1,159 @@
from __future__ import annotations
import os
from collections import namedtuple
import enum
from modules import sd_models, cache, errors, hashes, shared
NetworkWeights = namedtuple('NetworkWeights', ['network_key', 'sd_key', 'w', 'sd_module'])
metadata_tags_order = {"ss_sd_model_name": 1, "ss_resolution": 2, "ss_clip_skip": 3, "ss_num_train_images": 10, "ss_tag_frequency": 20}
class SdVersion(enum.Enum):
Unknown = 1
SD1 = 2
SD2 = 3
SDXL = 4
class NetworkOnDisk:
def __init__(self, name, filename):
self.name = name
self.filename = filename
self.metadata = {}
self.is_safetensors = os.path.splitext(filename)[1].lower() == ".safetensors"
def read_metadata():
metadata = sd_models.read_metadata_from_safetensors(filename)
metadata.pop('ssmd_cover_images', None) # those are cover images, and they are too big to display in UI as text
return metadata
if self.is_safetensors:
try:
self.metadata = cache.cached_data_for_file('safetensors-metadata', "lora/" + self.name, filename, read_metadata)
except Exception as e:
errors.display(e, f"reading lora {filename}")
if self.metadata:
m = {}
for k, v in sorted(self.metadata.items(), key=lambda x: metadata_tags_order.get(x[0], 999)):
m[k] = v
self.metadata = m
self.alias = self.metadata.get('ss_output_name', self.name)
self.hash = None
self.shorthash = None
self.set_hash(
self.metadata.get('sshs_model_hash') or
hashes.sha256_from_cache(self.filename, "lora/" + self.name, use_addnet_hash=self.is_safetensors) or
''
)
self.sd_version = self.detect_version()
def detect_version(self):
if str(self.metadata.get('ss_base_model_version', "")).startswith("sdxl_"):
return SdVersion.SDXL
elif str(self.metadata.get('ss_v2', "")) == "True":
return SdVersion.SD2
elif len(self.metadata):
return SdVersion.SD1
return SdVersion.Unknown
def set_hash(self, v):
self.hash = v
self.shorthash = self.hash[0:12]
if self.shorthash:
import networks
networks.available_network_hash_lookup[self.shorthash] = self
def read_hash(self):
if not self.hash:
self.set_hash(hashes.sha256(self.filename, "lora/" + self.name, use_addnet_hash=self.is_safetensors) or '')
def get_alias(self):
import networks
if shared.opts.lora_preferred_name == "Filename" or self.alias.lower() in networks.forbidden_network_aliases:
return self.name
else:
return self.alias
class Network: # LoraModule
def __init__(self, name, network_on_disk: NetworkOnDisk):
self.name = name
self.network_on_disk = network_on_disk
self.te_multiplier = 1.0
self.unet_multiplier = 1.0
self.dyn_dim = None
self.modules = {}
self.bundle_embeddings = {}
self.mtime = None
self.mentioned_name = None
"""the text that was used to add the network to prompt - can be either name or an alias"""
class ModuleType:
def create_module(self, net: Network, weights: NetworkWeights) -> Network | None:
return None
class NetworkModule:
def __init__(self, net: Network, weights: NetworkWeights):
self.network = net
self.network_key = weights.network_key
self.sd_key = weights.sd_key
self.sd_module = weights.sd_module
if hasattr(self.sd_module, 'weight'):
self.shape = self.sd_module.weight.shape
self.dim = None
self.bias = weights.w.get("bias")
self.alpha = weights.w["alpha"].item() if "alpha" in weights.w else None
self.scale = weights.w["scale"].item() if "scale" in weights.w else None
def multiplier(self):
if 'transformer' in self.sd_key[:20]:
return self.network.te_multiplier
else:
return self.network.unet_multiplier
def calc_scale(self):
if self.scale is not None:
return self.scale
if self.dim is not None and self.alpha is not None:
return self.alpha / self.dim
return 1.0
def finalize_updown(self, updown, orig_weight, output_shape, ex_bias=None):
if self.bias is not None:
updown = updown.reshape(self.bias.shape)
updown += self.bias.to(orig_weight.device, dtype=orig_weight.dtype)
updown = updown.reshape(output_shape)
if len(output_shape) == 4:
updown = updown.reshape(output_shape)
if orig_weight.size().numel() == updown.size().numel():
updown = updown.reshape(orig_weight.shape)
if ex_bias is not None:
ex_bias = ex_bias * self.multiplier()
return updown * self.calc_scale() * self.multiplier(), ex_bias
def calc_updown(self, target):
raise NotImplementedError()
def forward(self, x, y):
raise NotImplementedError()
+27
View File
@@ -0,0 +1,27 @@
import network
class ModuleTypeFull(network.ModuleType):
def create_module(self, net: network.Network, weights: network.NetworkWeights):
if all(x in weights.w for x in ["diff"]):
return NetworkModuleFull(net, weights)
return None
class NetworkModuleFull(network.NetworkModule):
def __init__(self, net: network.Network, weights: network.NetworkWeights):
super().__init__(net, weights)
self.weight = weights.w.get("diff")
self.ex_bias = weights.w.get("diff_b")
def calc_updown(self, orig_weight):
output_shape = self.weight.shape
updown = self.weight.to(orig_weight.device, dtype=orig_weight.dtype)
if self.ex_bias is not None:
ex_bias = self.ex_bias.to(orig_weight.device, dtype=orig_weight.dtype)
else:
ex_bias = None
return self.finalize_updown(updown, orig_weight, output_shape, ex_bias)
+33
View File
@@ -0,0 +1,33 @@
import network
class ModuleTypeGLora(network.ModuleType):
def create_module(self, net: network.Network, weights: network.NetworkWeights):
if all(x in weights.w for x in ["a1.weight", "a2.weight", "alpha", "b1.weight", "b2.weight"]):
return NetworkModuleGLora(net, weights)
return None
# adapted from https://github.com/KohakuBlueleaf/LyCORIS
class NetworkModuleGLora(network.NetworkModule):
def __init__(self, net: network.Network, weights: network.NetworkWeights):
super().__init__(net, weights)
if hasattr(self.sd_module, 'weight'):
self.shape = self.sd_module.weight.shape
self.w1a = weights.w["a1.weight"]
self.w1b = weights.w["b1.weight"]
self.w2a = weights.w["a2.weight"]
self.w2b = weights.w["b2.weight"]
def calc_updown(self, orig_weight):
w1a = self.w1a.to(orig_weight.device, dtype=orig_weight.dtype)
w1b = self.w1b.to(orig_weight.device, dtype=orig_weight.dtype)
w2a = self.w2a.to(orig_weight.device, dtype=orig_weight.dtype)
w2b = self.w2b.to(orig_weight.device, dtype=orig_weight.dtype)
output_shape = [w1a.size(0), w1b.size(1)]
updown = ((w2b @ w1b) + ((orig_weight @ w2a) @ w1a))
return self.finalize_updown(updown, orig_weight, output_shape)
+55
View File
@@ -0,0 +1,55 @@
import lyco_helpers
import network
class ModuleTypeHada(network.ModuleType):
def create_module(self, net: network.Network, weights: network.NetworkWeights):
if all(x in weights.w for x in ["hada_w1_a", "hada_w1_b", "hada_w2_a", "hada_w2_b"]):
return NetworkModuleHada(net, weights)
return None
class NetworkModuleHada(network.NetworkModule):
def __init__(self, net: network.Network, weights: network.NetworkWeights):
super().__init__(net, weights)
if hasattr(self.sd_module, 'weight'):
self.shape = self.sd_module.weight.shape
self.w1a = weights.w["hada_w1_a"]
self.w1b = weights.w["hada_w1_b"]
self.dim = self.w1b.shape[0]
self.w2a = weights.w["hada_w2_a"]
self.w2b = weights.w["hada_w2_b"]
self.t1 = weights.w.get("hada_t1")
self.t2 = weights.w.get("hada_t2")
def calc_updown(self, orig_weight):
w1a = self.w1a.to(orig_weight.device, dtype=orig_weight.dtype)
w1b = self.w1b.to(orig_weight.device, dtype=orig_weight.dtype)
w2a = self.w2a.to(orig_weight.device, dtype=orig_weight.dtype)
w2b = self.w2b.to(orig_weight.device, dtype=orig_weight.dtype)
output_shape = [w1a.size(0), w1b.size(1)]
if self.t1 is not None:
output_shape = [w1a.size(1), w1b.size(1)]
t1 = self.t1.to(orig_weight.device, dtype=orig_weight.dtype)
updown1 = lyco_helpers.make_weight_cp(t1, w1a, w1b)
output_shape += t1.shape[2:]
else:
if len(w1b.shape) == 4:
output_shape += w1b.shape[2:]
updown1 = lyco_helpers.rebuild_conventional(w1a, w1b, output_shape)
if self.t2 is not None:
t2 = self.t2.to(orig_weight.device, dtype=orig_weight.dtype)
updown2 = lyco_helpers.make_weight_cp(t2, w2a, w2b)
else:
updown2 = lyco_helpers.rebuild_conventional(w2a, w2b, output_shape)
updown = updown1 * updown2
return self.finalize_updown(updown, orig_weight, output_shape)
+30
View File
@@ -0,0 +1,30 @@
import network
class ModuleTypeIa3(network.ModuleType):
def create_module(self, net: network.Network, weights: network.NetworkWeights):
if all(x in weights.w for x in ["weight"]):
return NetworkModuleIa3(net, weights)
return None
class NetworkModuleIa3(network.NetworkModule):
def __init__(self, net: network.Network, weights: network.NetworkWeights):
super().__init__(net, weights)
self.w = weights.w["weight"]
self.on_input = weights.w["on_input"].item()
def calc_updown(self, orig_weight):
w = self.w.to(orig_weight.device, dtype=orig_weight.dtype)
output_shape = [w.size(0), orig_weight.size(1)]
if self.on_input:
output_shape.reverse()
else:
w = w.reshape(-1, 1)
updown = orig_weight * w
return self.finalize_updown(updown, orig_weight, output_shape)
+64
View File
@@ -0,0 +1,64 @@
import torch
import lyco_helpers
import network
class ModuleTypeLokr(network.ModuleType):
def create_module(self, net: network.Network, weights: network.NetworkWeights):
has_1 = "lokr_w1" in weights.w or ("lokr_w1_a" in weights.w and "lokr_w1_b" in weights.w)
has_2 = "lokr_w2" in weights.w or ("lokr_w2_a" in weights.w and "lokr_w2_b" in weights.w)
if has_1 and has_2:
return NetworkModuleLokr(net, weights)
return None
def make_kron(orig_shape, w1, w2):
if len(w2.shape) == 4:
w1 = w1.unsqueeze(2).unsqueeze(2)
w2 = w2.contiguous()
return torch.kron(w1, w2).reshape(orig_shape)
class NetworkModuleLokr(network.NetworkModule):
def __init__(self, net: network.Network, weights: network.NetworkWeights):
super().__init__(net, weights)
self.w1 = weights.w.get("lokr_w1")
self.w1a = weights.w.get("lokr_w1_a")
self.w1b = weights.w.get("lokr_w1_b")
self.dim = self.w1b.shape[0] if self.w1b is not None else self.dim
self.w2 = weights.w.get("lokr_w2")
self.w2a = weights.w.get("lokr_w2_a")
self.w2b = weights.w.get("lokr_w2_b")
self.dim = self.w2b.shape[0] if self.w2b is not None else self.dim
self.t2 = weights.w.get("lokr_t2")
def calc_updown(self, orig_weight):
if self.w1 is not None:
w1 = self.w1.to(orig_weight.device, dtype=orig_weight.dtype)
else:
w1a = self.w1a.to(orig_weight.device, dtype=orig_weight.dtype)
w1b = self.w1b.to(orig_weight.device, dtype=orig_weight.dtype)
w1 = w1a @ w1b
if self.w2 is not None:
w2 = self.w2.to(orig_weight.device, dtype=orig_weight.dtype)
elif self.t2 is None:
w2a = self.w2a.to(orig_weight.device, dtype=orig_weight.dtype)
w2b = self.w2b.to(orig_weight.device, dtype=orig_weight.dtype)
w2 = w2a @ w2b
else:
t2 = self.t2.to(orig_weight.device, dtype=orig_weight.dtype)
w2a = self.w2a.to(orig_weight.device, dtype=orig_weight.dtype)
w2b = self.w2b.to(orig_weight.device, dtype=orig_weight.dtype)
w2 = lyco_helpers.make_weight_cp(t2, w2a, w2b)
output_shape = [w1.size(0) * w2.size(0), w1.size(1) * w2.size(1)]
if len(orig_weight.shape) == 4:
output_shape = orig_weight.shape
updown = make_kron(output_shape, w1, w2)
return self.finalize_updown(updown, orig_weight, output_shape)
+86
View File
@@ -0,0 +1,86 @@
import torch
import lyco_helpers
import network
from modules import devices
class ModuleTypeLora(network.ModuleType):
def create_module(self, net: network.Network, weights: network.NetworkWeights):
if all(x in weights.w for x in ["lora_up.weight", "lora_down.weight"]):
return NetworkModuleLora(net, weights)
return None
class NetworkModuleLora(network.NetworkModule):
def __init__(self, net: network.Network, weights: network.NetworkWeights):
super().__init__(net, weights)
self.up_model = self.create_module(weights.w, "lora_up.weight")
self.down_model = self.create_module(weights.w, "lora_down.weight")
self.mid_model = self.create_module(weights.w, "lora_mid.weight", none_ok=True)
self.dim = weights.w["lora_down.weight"].shape[0]
def create_module(self, weights, key, none_ok=False):
weight = weights.get(key)
if weight is None and none_ok:
return None
is_linear = type(self.sd_module) in [torch.nn.Linear, torch.nn.modules.linear.NonDynamicallyQuantizableLinear, torch.nn.MultiheadAttention]
is_conv = type(self.sd_module) in [torch.nn.Conv2d]
if is_linear:
weight = weight.reshape(weight.shape[0], -1)
module = torch.nn.Linear(weight.shape[1], weight.shape[0], bias=False)
elif is_conv and key == "lora_down.weight" or key == "dyn_up":
if len(weight.shape) == 2:
weight = weight.reshape(weight.shape[0], -1, 1, 1)
if weight.shape[2] != 1 or weight.shape[3] != 1:
module = torch.nn.Conv2d(weight.shape[1], weight.shape[0], self.sd_module.kernel_size, self.sd_module.stride, self.sd_module.padding, bias=False)
else:
module = torch.nn.Conv2d(weight.shape[1], weight.shape[0], (1, 1), bias=False)
elif is_conv and key == "lora_mid.weight":
module = torch.nn.Conv2d(weight.shape[1], weight.shape[0], self.sd_module.kernel_size, self.sd_module.stride, self.sd_module.padding, bias=False)
elif is_conv and key == "lora_up.weight" or key == "dyn_down":
module = torch.nn.Conv2d(weight.shape[1], weight.shape[0], (1, 1), bias=False)
else:
raise AssertionError(f'Lora layer {self.network_key} matched a layer with unsupported type: {type(self.sd_module).__name__}')
with torch.no_grad():
if weight.shape != module.weight.shape:
weight = weight.reshape(module.weight.shape)
module.weight.copy_(weight)
module.to(device=devices.cpu, dtype=devices.dtype)
module.weight.requires_grad_(False)
return module
def calc_updown(self, orig_weight):
up = self.up_model.weight.to(orig_weight.device, dtype=orig_weight.dtype)
down = self.down_model.weight.to(orig_weight.device, dtype=orig_weight.dtype)
output_shape = [up.size(0), down.size(1)]
if self.mid_model is not None:
# cp-decomposition
mid = self.mid_model.weight.to(orig_weight.device, dtype=orig_weight.dtype)
updown = lyco_helpers.rebuild_cp_decomposition(up, down, mid)
output_shape += mid.shape[2:]
else:
if len(down.shape) == 4:
output_shape += down.shape[2:]
updown = lyco_helpers.rebuild_conventional(up, down, output_shape, self.network.dyn_dim)
return self.finalize_updown(updown, orig_weight, output_shape)
def forward(self, x, y):
self.up_model.to(device=devices.device)
self.down_model.to(device=devices.device)
return y + self.up_model(self.down_model(x)) * self.multiplier() * self.calc_scale()
+28
View File
@@ -0,0 +1,28 @@
import network
class ModuleTypeNorm(network.ModuleType):
def create_module(self, net: network.Network, weights: network.NetworkWeights):
if all(x in weights.w for x in ["w_norm", "b_norm"]):
return NetworkModuleNorm(net, weights)
return None
class NetworkModuleNorm(network.NetworkModule):
def __init__(self, net: network.Network, weights: network.NetworkWeights):
super().__init__(net, weights)
self.w_norm = weights.w.get("w_norm")
self.b_norm = weights.w.get("b_norm")
def calc_updown(self, orig_weight):
output_shape = self.w_norm.shape
updown = self.w_norm.to(orig_weight.device, dtype=orig_weight.dtype)
if self.b_norm is not None:
ex_bias = self.b_norm.to(orig_weight.device, dtype=orig_weight.dtype)
else:
ex_bias = None
return self.finalize_updown(updown, orig_weight, output_shape, ex_bias)
+97
View File
@@ -0,0 +1,97 @@
import torch
import network
from lyco_helpers import factorization
from einops import rearrange
class ModuleTypeOFT(network.ModuleType):
def create_module(self, net: network.Network, weights: network.NetworkWeights):
if all(x in weights.w for x in ["oft_blocks"]) or all(x in weights.w for x in ["oft_diag"]):
return NetworkModuleOFT(net, weights)
return None
# Supports both kohya-ss' implementation of COFT https://github.com/kohya-ss/sd-scripts/blob/main/networks/oft.py
# and KohakuBlueleaf's implementation of OFT/COFT https://github.com/KohakuBlueleaf/LyCORIS/blob/dev/lycoris/modules/diag_oft.py
class NetworkModuleOFT(network.NetworkModule):
def __init__(self, net: network.Network, weights: network.NetworkWeights):
super().__init__(net, weights)
self.lin_module = None
self.org_module: list[torch.Module] = [self.sd_module]
# kohya-ss
if "oft_blocks" in weights.w.keys():
self.is_kohya = True
self.oft_blocks = weights.w["oft_blocks"] # (num_blocks, block_size, block_size)
self.alpha = weights.w["alpha"] # alpha is constraint
self.dim = self.oft_blocks.shape[0] # lora dim
# LyCORIS
elif "oft_diag" in weights.w.keys():
self.is_kohya = False
self.oft_blocks = weights.w["oft_diag"]
# self.alpha is unused
self.dim = self.oft_blocks.shape[1] # (num_blocks, block_size, block_size)
is_linear = type(self.sd_module) in [torch.nn.Linear, torch.nn.modules.linear.NonDynamicallyQuantizableLinear]
is_conv = type(self.sd_module) in [torch.nn.Conv2d]
is_other_linear = type(self.sd_module) in [torch.nn.MultiheadAttention] # unsupported
if is_linear:
self.out_dim = self.sd_module.out_features
elif is_conv:
self.out_dim = self.sd_module.out_channels
elif is_other_linear:
self.out_dim = self.sd_module.embed_dim
if self.is_kohya:
self.constraint = self.alpha * self.out_dim
self.num_blocks = self.dim
self.block_size = self.out_dim // self.dim
else:
self.constraint = None
self.block_size, self.num_blocks = factorization(self.out_dim, self.dim)
def calc_updown_kb(self, orig_weight, multiplier):
oft_blocks = self.oft_blocks.to(orig_weight.device, dtype=orig_weight.dtype)
oft_blocks = oft_blocks - oft_blocks.transpose(1, 2) # ensure skew-symmetric orthogonal matrix
R = oft_blocks.to(orig_weight.device, dtype=orig_weight.dtype)
R = R * multiplier + torch.eye(self.block_size, device=orig_weight.device)
# This errors out for MultiheadAttention, might need to be handled up-stream
merged_weight = rearrange(orig_weight, '(k n) ... -> k n ...', k=self.num_blocks, n=self.block_size)
merged_weight = torch.einsum(
'k n m, k n ... -> k m ...',
R,
merged_weight
)
merged_weight = rearrange(merged_weight, 'k m ... -> (k m) ...')
updown = merged_weight.to(orig_weight.device, dtype=orig_weight.dtype) - orig_weight
output_shape = orig_weight.shape
return self.finalize_updown(updown, orig_weight, output_shape)
def calc_updown(self, orig_weight):
# if alpha is a very small number as in coft, calc_scale() will return a almost zero number so we ignore it
multiplier = self.multiplier()
return self.calc_updown_kb(orig_weight, multiplier)
# override to remove the multiplier/scale factor; it's already multiplied in get_weight
def finalize_updown(self, updown, orig_weight, output_shape, ex_bias=None):
if self.bias is not None:
updown = updown.reshape(self.bias.shape)
updown += self.bias.to(orig_weight.device, dtype=orig_weight.dtype)
updown = updown.reshape(output_shape)
if len(output_shape) == 4:
updown = updown.reshape(output_shape)
if orig_weight.size().numel() == updown.size().numel():
updown = updown.reshape(orig_weight.shape)
if ex_bias is not None:
ex_bias = ex_bias * self.multiplier()
return updown, ex_bias
+628
View File
@@ -0,0 +1,628 @@
import logging
import os
import re
import lora_patches
import network
import network_lora
import network_glora
import network_hada
import network_ia3
import network_lokr
import network_full
import network_norm
import network_oft
import torch
from typing import Union
from modules import shared, devices, sd_models, errors, scripts, sd_hijack
import modules.textual_inversion.textual_inversion as textual_inversion
from lora_logger import logger
module_types = [
network_lora.ModuleTypeLora(),
network_hada.ModuleTypeHada(),
network_ia3.ModuleTypeIa3(),
network_lokr.ModuleTypeLokr(),
network_full.ModuleTypeFull(),
network_norm.ModuleTypeNorm(),
network_glora.ModuleTypeGLora(),
network_oft.ModuleTypeOFT(),
]
re_digits = re.compile(r"\d+")
re_x_proj = re.compile(r"(.*)_([qkv]_proj)$")
re_compiled = {}
suffix_conversion = {
"attentions": {},
"resnets": {
"conv1": "in_layers_2",
"conv2": "out_layers_3",
"norm1": "in_layers_0",
"norm2": "out_layers_0",
"time_emb_proj": "emb_layers_1",
"conv_shortcut": "skip_connection",
}
}
def convert_diffusers_name_to_compvis(key, is_sd2):
def match(match_list, regex_text):
regex = re_compiled.get(regex_text)
if regex is None:
regex = re.compile(regex_text)
re_compiled[regex_text] = regex
r = re.match(regex, key)
if not r:
return False
match_list.clear()
match_list.extend([int(x) if re.match(re_digits, x) else x for x in r.groups()])
return True
m = []
if match(m, r"lora_unet_conv_in(.*)"):
return f'diffusion_model_input_blocks_0_0{m[0]}'
if match(m, r"lora_unet_conv_out(.*)"):
return f'diffusion_model_out_2{m[0]}'
if match(m, r"lora_unet_time_embedding_linear_(\d+)(.*)"):
return f"diffusion_model_time_embed_{m[0] * 2 - 2}{m[1]}"
if match(m, r"lora_unet_down_blocks_(\d+)_(attentions|resnets)_(\d+)_(.+)"):
suffix = suffix_conversion.get(m[1], {}).get(m[3], m[3])
return f"diffusion_model_input_blocks_{1 + m[0] * 3 + m[2]}_{1 if m[1] == 'attentions' else 0}_{suffix}"
if match(m, r"lora_unet_mid_block_(attentions|resnets)_(\d+)_(.+)"):
suffix = suffix_conversion.get(m[0], {}).get(m[2], m[2])
return f"diffusion_model_middle_block_{1 if m[0] == 'attentions' else m[1] * 2}_{suffix}"
if match(m, r"lora_unet_up_blocks_(\d+)_(attentions|resnets)_(\d+)_(.+)"):
suffix = suffix_conversion.get(m[1], {}).get(m[3], m[3])
return f"diffusion_model_output_blocks_{m[0] * 3 + m[2]}_{1 if m[1] == 'attentions' else 0}_{suffix}"
if match(m, r"lora_unet_down_blocks_(\d+)_downsamplers_0_conv"):
return f"diffusion_model_input_blocks_{3 + m[0] * 3}_0_op"
if match(m, r"lora_unet_up_blocks_(\d+)_upsamplers_0_conv"):
return f"diffusion_model_output_blocks_{2 + m[0] * 3}_{2 if m[0]>0 else 1}_conv"
if match(m, r"lora_te_text_model_encoder_layers_(\d+)_(.+)"):
if is_sd2:
if 'mlp_fc1' in m[1]:
return f"model_transformer_resblocks_{m[0]}_{m[1].replace('mlp_fc1', 'mlp_c_fc')}"
elif 'mlp_fc2' in m[1]:
return f"model_transformer_resblocks_{m[0]}_{m[1].replace('mlp_fc2', 'mlp_c_proj')}"
else:
return f"model_transformer_resblocks_{m[0]}_{m[1].replace('self_attn', 'attn')}"
return f"transformer_text_model_encoder_layers_{m[0]}_{m[1]}"
if match(m, r"lora_te2_text_model_encoder_layers_(\d+)_(.+)"):
if 'mlp_fc1' in m[1]:
return f"1_model_transformer_resblocks_{m[0]}_{m[1].replace('mlp_fc1', 'mlp_c_fc')}"
elif 'mlp_fc2' in m[1]:
return f"1_model_transformer_resblocks_{m[0]}_{m[1].replace('mlp_fc2', 'mlp_c_proj')}"
else:
return f"1_model_transformer_resblocks_{m[0]}_{m[1].replace('self_attn', 'attn')}"
return key
def assign_network_names_to_compvis_modules(sd_model):
network_layer_mapping = {}
if shared.sd_model.is_sdxl:
for i, embedder in enumerate(shared.sd_model.conditioner.embedders):
if not hasattr(embedder, 'wrapped'):
continue
for name, module in embedder.wrapped.named_modules():
network_name = f'{i}_{name.replace(".", "_")}'
network_layer_mapping[network_name] = module
module.network_layer_name = network_name
else:
for name, module in shared.sd_model.cond_stage_model.wrapped.named_modules():
network_name = name.replace(".", "_")
network_layer_mapping[network_name] = module
module.network_layer_name = network_name
for name, module in shared.sd_model.model.named_modules():
network_name = name.replace(".", "_")
network_layer_mapping[network_name] = module
module.network_layer_name = network_name
sd_model.network_layer_mapping = network_layer_mapping
def load_network(name, network_on_disk):
net = network.Network(name, network_on_disk)
net.mtime = os.path.getmtime(network_on_disk.filename)
sd = sd_models.read_state_dict(network_on_disk.filename)
# this should not be needed but is here as an emergency fix for an unknown error people are experiencing in 1.2.0
if not hasattr(shared.sd_model, 'network_layer_mapping'):
assign_network_names_to_compvis_modules(shared.sd_model)
keys_failed_to_match = {}
is_sd2 = 'model_transformer_resblocks' in shared.sd_model.network_layer_mapping
matched_networks = {}
bundle_embeddings = {}
for key_network, weight in sd.items():
key_network_without_network_parts, network_part = key_network.split(".", 1)
if key_network_without_network_parts == "bundle_emb":
emb_name, vec_name = network_part.split(".", 1)
emb_dict = bundle_embeddings.get(emb_name, {})
if vec_name.split('.')[0] == 'string_to_param':
_, k2 = vec_name.split('.', 1)
emb_dict['string_to_param'] = {k2: weight}
else:
emb_dict[vec_name] = weight
bundle_embeddings[emb_name] = emb_dict
key = convert_diffusers_name_to_compvis(key_network_without_network_parts, is_sd2)
sd_module = shared.sd_model.network_layer_mapping.get(key, None)
if sd_module is None:
m = re_x_proj.match(key)
if m:
sd_module = shared.sd_model.network_layer_mapping.get(m.group(1), None)
# SDXL loras seem to already have correct compvis keys, so only need to replace "lora_unet" with "diffusion_model"
if sd_module is None and "lora_unet" in key_network_without_network_parts:
key = key_network_without_network_parts.replace("lora_unet", "diffusion_model")
sd_module = shared.sd_model.network_layer_mapping.get(key, None)
elif sd_module is None and "lora_te1_text_model" in key_network_without_network_parts:
key = key_network_without_network_parts.replace("lora_te1_text_model", "0_transformer_text_model")
sd_module = shared.sd_model.network_layer_mapping.get(key, None)
# some SD1 Loras also have correct compvis keys
if sd_module is None:
key = key_network_without_network_parts.replace("lora_te1_text_model", "transformer_text_model")
sd_module = shared.sd_model.network_layer_mapping.get(key, None)
# kohya_ss OFT module
elif sd_module is None and "oft_unet" in key_network_without_network_parts:
key = key_network_without_network_parts.replace("oft_unet", "diffusion_model")
sd_module = shared.sd_model.network_layer_mapping.get(key, None)
# KohakuBlueLeaf OFT module
if sd_module is None and "oft_diag" in key:
key = key_network_without_network_parts.replace("lora_unet", "diffusion_model")
key = key_network_without_network_parts.replace("lora_te1_text_model", "0_transformer_text_model")
sd_module = shared.sd_model.network_layer_mapping.get(key, None)
if sd_module is None:
keys_failed_to_match[key_network] = key
continue
if key not in matched_networks:
matched_networks[key] = network.NetworkWeights(network_key=key_network, sd_key=key, w={}, sd_module=sd_module)
matched_networks[key].w[network_part] = weight
for key, weights in matched_networks.items():
net_module = None
for nettype in module_types:
net_module = nettype.create_module(net, weights)
if net_module is not None:
break
if net_module is None:
raise AssertionError(f"Could not find a module type (out of {', '.join([x.__class__.__name__ for x in module_types])}) that would accept those keys: {', '.join(weights.w)}")
net.modules[key] = net_module
embeddings = {}
for emb_name, data in bundle_embeddings.items():
embedding = textual_inversion.create_embedding_from_data(data, emb_name, filename=network_on_disk.filename + "/" + emb_name)
embedding.loaded = None
embeddings[emb_name] = embedding
net.bundle_embeddings = embeddings
if keys_failed_to_match:
logging.debug(f"Network {network_on_disk.filename} didn't match keys: {keys_failed_to_match}")
return net
def purge_networks_from_memory():
while len(networks_in_memory) > shared.opts.lora_in_memory_limit and len(networks_in_memory) > 0:
name = next(iter(networks_in_memory))
networks_in_memory.pop(name, None)
devices.torch_gc()
def load_networks(names, te_multipliers=None, unet_multipliers=None, dyn_dims=None):
emb_db = sd_hijack.model_hijack.embedding_db
already_loaded = {}
for net in loaded_networks:
if net.name in names:
already_loaded[net.name] = net
for emb_name, embedding in net.bundle_embeddings.items():
if embedding.loaded:
emb_db.register_embedding_by_name(None, shared.sd_model, emb_name)
loaded_networks.clear()
networks_on_disk = [available_network_aliases.get(name, None) for name in names]
if any(x is None for x in networks_on_disk):
list_available_networks()
networks_on_disk = [available_network_aliases.get(name, None) for name in names]
failed_to_load_networks = []
for i, (network_on_disk, name) in enumerate(zip(networks_on_disk, names)):
net = already_loaded.get(name, None)
if network_on_disk is not None:
if net is None:
net = networks_in_memory.get(name)
if net is None or os.path.getmtime(network_on_disk.filename) > net.mtime:
try:
net = load_network(name, network_on_disk)
networks_in_memory.pop(name, None)
networks_in_memory[name] = net
except Exception as e:
errors.display(e, f"loading network {network_on_disk.filename}")
continue
net.mentioned_name = name
network_on_disk.read_hash()
if net is None:
failed_to_load_networks.append(name)
logging.info(f"Couldn't find network with name {name}")
continue
net.te_multiplier = te_multipliers[i] if te_multipliers else 1.0
net.unet_multiplier = unet_multipliers[i] if unet_multipliers else 1.0
net.dyn_dim = dyn_dims[i] if dyn_dims else 1.0
loaded_networks.append(net)
for emb_name, embedding in net.bundle_embeddings.items():
if embedding.loaded is None and emb_name in emb_db.word_embeddings:
logger.warning(
f'Skip bundle embedding: "{emb_name}"'
' as it was already loaded from embeddings folder'
)
continue
embedding.loaded = False
if emb_db.expected_shape == -1 or emb_db.expected_shape == embedding.shape:
embedding.loaded = True
emb_db.register_embedding(embedding, shared.sd_model)
else:
emb_db.skipped_embeddings[name] = embedding
if failed_to_load_networks:
sd_hijack.model_hijack.comments.append("Networks not found: " + ", ".join(failed_to_load_networks))
purge_networks_from_memory()
def network_restore_weights_from_backup(self: Union[torch.nn.Conv2d, torch.nn.Linear, torch.nn.GroupNorm, torch.nn.LayerNorm, torch.nn.MultiheadAttention]):
weights_backup = getattr(self, "network_weights_backup", None)
bias_backup = getattr(self, "network_bias_backup", None)
if weights_backup is None and bias_backup is None:
return
if weights_backup is not None:
if isinstance(self, torch.nn.MultiheadAttention):
self.in_proj_weight.copy_(weights_backup[0])
self.out_proj.weight.copy_(weights_backup[1])
else:
self.weight.copy_(weights_backup)
if bias_backup is not None:
if isinstance(self, torch.nn.MultiheadAttention):
self.out_proj.bias.copy_(bias_backup)
else:
self.bias.copy_(bias_backup)
else:
if isinstance(self, torch.nn.MultiheadAttention):
self.out_proj.bias = None
else:
self.bias = None
def network_apply_weights(self: Union[torch.nn.Conv2d, torch.nn.Linear, torch.nn.GroupNorm, torch.nn.LayerNorm, torch.nn.MultiheadAttention]):
"""
Applies the currently selected set of networks to the weights of torch layer self.
If weights already have this particular set of networks applied, does nothing.
If not, restores orginal weights from backup and alters weights according to networks.
"""
network_layer_name = getattr(self, 'network_layer_name', None)
if network_layer_name is None:
return
current_names = getattr(self, "network_current_names", ())
wanted_names = tuple((x.name, x.te_multiplier, x.unet_multiplier, x.dyn_dim) for x in loaded_networks)
weights_backup = getattr(self, "network_weights_backup", None)
if weights_backup is None and wanted_names != ():
if current_names != ():
raise RuntimeError("no backup weights found and current weights are not unchanged")
if isinstance(self, torch.nn.MultiheadAttention):
weights_backup = (self.in_proj_weight.to(devices.cpu, copy=True), self.out_proj.weight.to(devices.cpu, copy=True))
else:
weights_backup = self.weight.to(devices.cpu, copy=True)
self.network_weights_backup = weights_backup
bias_backup = getattr(self, "network_bias_backup", None)
if bias_backup is None:
if isinstance(self, torch.nn.MultiheadAttention) and self.out_proj.bias is not None:
bias_backup = self.out_proj.bias.to(devices.cpu, copy=True)
elif getattr(self, 'bias', None) is not None:
bias_backup = self.bias.to(devices.cpu, copy=True)
else:
bias_backup = None
self.network_bias_backup = bias_backup
if current_names != wanted_names:
network_restore_weights_from_backup(self)
for net in loaded_networks:
module = net.modules.get(network_layer_name, None)
if module is not None and hasattr(self, 'weight'):
try:
with torch.no_grad():
updown, ex_bias = module.calc_updown(self.weight)
if len(self.weight.shape) == 4 and self.weight.shape[1] == 9:
# inpainting model. zero pad updown to make channel[1] 4 to 9
updown = torch.nn.functional.pad(updown, (0, 0, 0, 0, 0, 5))
self.weight += updown
if ex_bias is not None and hasattr(self, 'bias'):
if self.bias is None:
self.bias = torch.nn.Parameter(ex_bias)
else:
self.bias += ex_bias
except RuntimeError as e:
logging.debug(f"Network {net.name} layer {network_layer_name}: {e}")
extra_network_lora.errors[net.name] = extra_network_lora.errors.get(net.name, 0) + 1
continue
module_q = net.modules.get(network_layer_name + "_q_proj", None)
module_k = net.modules.get(network_layer_name + "_k_proj", None)
module_v = net.modules.get(network_layer_name + "_v_proj", None)
module_out = net.modules.get(network_layer_name + "_out_proj", None)
if isinstance(self, torch.nn.MultiheadAttention) and module_q and module_k and module_v and module_out:
try:
with torch.no_grad():
updown_q, _ = module_q.calc_updown(self.in_proj_weight)
updown_k, _ = module_k.calc_updown(self.in_proj_weight)
updown_v, _ = module_v.calc_updown(self.in_proj_weight)
updown_qkv = torch.vstack([updown_q, updown_k, updown_v])
updown_out, ex_bias = module_out.calc_updown(self.out_proj.weight)
self.in_proj_weight += updown_qkv
self.out_proj.weight += updown_out
if ex_bias is not None:
if self.out_proj.bias is None:
self.out_proj.bias = torch.nn.Parameter(ex_bias)
else:
self.out_proj.bias += ex_bias
except RuntimeError as e:
logging.debug(f"Network {net.name} layer {network_layer_name}: {e}")
extra_network_lora.errors[net.name] = extra_network_lora.errors.get(net.name, 0) + 1
continue
if module is None:
continue
logging.debug(f"Network {net.name} layer {network_layer_name}: couldn't find supported operation")
extra_network_lora.errors[net.name] = extra_network_lora.errors.get(net.name, 0) + 1
self.network_current_names = wanted_names
def network_forward(module, input, original_forward):
"""
Old way of applying Lora by executing operations during layer's forward.
Stacking many loras this way results in big performance degradation.
"""
if len(loaded_networks) == 0:
return original_forward(module, input)
input = devices.cond_cast_unet(input)
network_restore_weights_from_backup(module)
network_reset_cached_weight(module)
y = original_forward(module, input)
network_layer_name = getattr(module, 'network_layer_name', None)
for lora in loaded_networks:
module = lora.modules.get(network_layer_name, None)
if module is None:
continue
y = module.forward(input, y)
return y
def network_reset_cached_weight(self: Union[torch.nn.Conv2d, torch.nn.Linear]):
self.network_current_names = ()
self.network_weights_backup = None
self.network_bias_backup = None
def network_Linear_forward(self, input):
if shared.opts.lora_functional:
return network_forward(self, input, originals.Linear_forward)
network_apply_weights(self)
return originals.Linear_forward(self, input)
def network_Linear_load_state_dict(self, *args, **kwargs):
network_reset_cached_weight(self)
return originals.Linear_load_state_dict(self, *args, **kwargs)
def network_Conv2d_forward(self, input):
if shared.opts.lora_functional:
return network_forward(self, input, originals.Conv2d_forward)
network_apply_weights(self)
return originals.Conv2d_forward(self, input)
def network_Conv2d_load_state_dict(self, *args, **kwargs):
network_reset_cached_weight(self)
return originals.Conv2d_load_state_dict(self, *args, **kwargs)
def network_GroupNorm_forward(self, input):
if shared.opts.lora_functional:
return network_forward(self, input, originals.GroupNorm_forward)
network_apply_weights(self)
return originals.GroupNorm_forward(self, input)
def network_GroupNorm_load_state_dict(self, *args, **kwargs):
network_reset_cached_weight(self)
return originals.GroupNorm_load_state_dict(self, *args, **kwargs)
def network_LayerNorm_forward(self, input):
if shared.opts.lora_functional:
return network_forward(self, input, originals.LayerNorm_forward)
network_apply_weights(self)
return originals.LayerNorm_forward(self, input)
def network_LayerNorm_load_state_dict(self, *args, **kwargs):
network_reset_cached_weight(self)
return originals.LayerNorm_load_state_dict(self, *args, **kwargs)
def network_MultiheadAttention_forward(self, *args, **kwargs):
network_apply_weights(self)
return originals.MultiheadAttention_forward(self, *args, **kwargs)
def network_MultiheadAttention_load_state_dict(self, *args, **kwargs):
network_reset_cached_weight(self)
return originals.MultiheadAttention_load_state_dict(self, *args, **kwargs)
def list_available_networks():
available_networks.clear()
available_network_aliases.clear()
forbidden_network_aliases.clear()
available_network_hash_lookup.clear()
forbidden_network_aliases.update({"none": 1, "Addams": 1})
os.makedirs(shared.cmd_opts.lora_dir, exist_ok=True)
candidates = list(shared.walk_files(shared.cmd_opts.lora_dir, allowed_extensions=[".pt", ".ckpt", ".safetensors"]))
candidates += list(shared.walk_files(shared.cmd_opts.lyco_dir_backcompat, allowed_extensions=[".pt", ".ckpt", ".safetensors"]))
for filename in candidates:
if os.path.isdir(filename):
continue
name = os.path.splitext(os.path.basename(filename))[0]
try:
entry = network.NetworkOnDisk(name, filename)
except OSError: # should catch FileNotFoundError and PermissionError etc.
errors.report(f"Failed to load network {name} from {filename}", exc_info=True)
continue
available_networks[name] = entry
if entry.alias in available_network_aliases:
forbidden_network_aliases[entry.alias.lower()] = 1
available_network_aliases[name] = entry
available_network_aliases[entry.alias] = entry
re_network_name = re.compile(r"(.*)\s*\([0-9a-fA-F]+\)")
def infotext_pasted(infotext, params):
if "AddNet Module 1" in [x[1] for x in scripts.scripts_txt2img.infotext_fields]:
return # if the other extension is active, it will handle those fields, no need to do anything
added = []
for k in params:
if not k.startswith("AddNet Model "):
continue
num = k[13:]
if params.get("AddNet Module " + num) != "LoRA":
continue
name = params.get("AddNet Model " + num)
if name is None:
continue
m = re_network_name.match(name)
if m:
name = m.group(1)
multiplier = params.get("AddNet Weight A " + num, "1.0")
added.append(f"<lora:{name}:{multiplier}>")
if added:
params["Prompt"] += "\n" + "".join(added)
originals: lora_patches.LoraPatches = None
extra_network_lora = None
available_networks = {}
available_network_aliases = {}
loaded_networks = []
loaded_bundle_embeddings = {}
networks_in_memory = {}
available_network_hash_lookup = {}
forbidden_network_aliases = {}
list_available_networks()
+1
View File
@@ -4,3 +4,4 @@ from modules import paths
def preload(parser): def preload(parser):
parser.add_argument("--lora-dir", type=str, help="Path to directory with Lora networks.", default=os.path.join(paths.models_path, 'Lora')) parser.add_argument("--lora-dir", type=str, help="Path to directory with Lora networks.", default=os.path.join(paths.models_path, 'Lora'))
parser.add_argument("--lyco-dir-backcompat", type=str, help="Path to directory with LyCORIS networks (for backawards compatibility; can also use --lyco-dir).", default=os.path.join(paths.models_path, 'LyCORIS'))
+30 -47
View File
@@ -1,72 +1,53 @@
import re import re
import torch
import gradio as gr import gradio as gr
from fastapi import FastAPI from fastapi import FastAPI
import lora import network
import networks
import lora # noqa:F401
import lora_patches
import extra_networks_lora import extra_networks_lora
import ui_extra_networks_lora import ui_extra_networks_lora
from modules import script_callbacks, ui_extra_networks, extra_networks, shared from modules import script_callbacks, ui_extra_networks, extra_networks, shared
def unload(): def unload():
torch.nn.Linear.forward = torch.nn.Linear_forward_before_lora networks.originals.undo()
torch.nn.Linear._load_from_state_dict = torch.nn.Linear_load_state_dict_before_lora
torch.nn.Conv2d.forward = torch.nn.Conv2d_forward_before_lora
torch.nn.Conv2d._load_from_state_dict = torch.nn.Conv2d_load_state_dict_before_lora
torch.nn.MultiheadAttention.forward = torch.nn.MultiheadAttention_forward_before_lora
torch.nn.MultiheadAttention._load_from_state_dict = torch.nn.MultiheadAttention_load_state_dict_before_lora
def before_ui(): def before_ui():
ui_extra_networks.register_page(ui_extra_networks_lora.ExtraNetworksPageLora()) ui_extra_networks.register_page(ui_extra_networks_lora.ExtraNetworksPageLora())
extra_networks.register_extra_network(extra_networks_lora.ExtraNetworkLora())
networks.extra_network_lora = extra_networks_lora.ExtraNetworkLora()
extra_networks.register_extra_network(networks.extra_network_lora)
extra_networks.register_extra_network_alias(networks.extra_network_lora, "lyco")
if not hasattr(torch.nn, 'Linear_forward_before_lora'): networks.originals = lora_patches.LoraPatches()
torch.nn.Linear_forward_before_lora = torch.nn.Linear.forward
if not hasattr(torch.nn, 'Linear_load_state_dict_before_lora'): script_callbacks.on_model_loaded(networks.assign_network_names_to_compvis_modules)
torch.nn.Linear_load_state_dict_before_lora = torch.nn.Linear._load_from_state_dict
if not hasattr(torch.nn, 'Conv2d_forward_before_lora'):
torch.nn.Conv2d_forward_before_lora = torch.nn.Conv2d.forward
if not hasattr(torch.nn, 'Conv2d_load_state_dict_before_lora'):
torch.nn.Conv2d_load_state_dict_before_lora = torch.nn.Conv2d._load_from_state_dict
if not hasattr(torch.nn, 'MultiheadAttention_forward_before_lora'):
torch.nn.MultiheadAttention_forward_before_lora = torch.nn.MultiheadAttention.forward
if not hasattr(torch.nn, 'MultiheadAttention_load_state_dict_before_lora'):
torch.nn.MultiheadAttention_load_state_dict_before_lora = torch.nn.MultiheadAttention._load_from_state_dict
torch.nn.Linear.forward = lora.lora_Linear_forward
torch.nn.Linear._load_from_state_dict = lora.lora_Linear_load_state_dict
torch.nn.Conv2d.forward = lora.lora_Conv2d_forward
torch.nn.Conv2d._load_from_state_dict = lora.lora_Conv2d_load_state_dict
torch.nn.MultiheadAttention.forward = lora.lora_MultiheadAttention_forward
torch.nn.MultiheadAttention._load_from_state_dict = lora.lora_MultiheadAttention_load_state_dict
script_callbacks.on_model_loaded(lora.assign_lora_names_to_compvis_modules)
script_callbacks.on_script_unloaded(unload) script_callbacks.on_script_unloaded(unload)
script_callbacks.on_before_ui(before_ui) script_callbacks.on_before_ui(before_ui)
script_callbacks.on_infotext_pasted(lora.infotext_pasted) script_callbacks.on_infotext_pasted(networks.infotext_pasted)
shared.options_templates.update(shared.options_section(('extra_networks', "Extra Networks"), { shared.options_templates.update(shared.options_section(('extra_networks', "Extra Networks"), {
"sd_lora": shared.OptionInfo("None", "Add Lora to prompt", gr.Dropdown, lambda: {"choices": ["None", *lora.available_loras]}, refresh=lora.list_available_loras), "sd_lora": shared.OptionInfo("None", "Add network to prompt", gr.Dropdown, lambda: {"choices": ["None", *networks.available_networks]}, refresh=networks.list_available_networks),
"lora_preferred_name": shared.OptionInfo("Alias from file", "When adding to prompt, refer to Lora by", gr.Radio, {"choices": ["Alias from file", "Filename"]}), "lora_preferred_name": shared.OptionInfo("Alias from file", "When adding to prompt, refer to Lora by", gr.Radio, {"choices": ["Alias from file", "Filename"]}),
"lora_add_hashes_to_infotext": shared.OptionInfo(True, "Add Lora hashes to infotext"), "lora_add_hashes_to_infotext": shared.OptionInfo(True, "Add Lora hashes to infotext"),
"lora_show_all": shared.OptionInfo(False, "Always show all networks on the Lora page").info("otherwise, those detected as for incompatible version of Stable Diffusion will be hidden"),
"lora_hide_unknown_for_versions": shared.OptionInfo([], "Hide networks of unknown versions for model versions", gr.CheckboxGroup, {"choices": ["SD1", "SD2", "SDXL"]}),
"lora_in_memory_limit": shared.OptionInfo(0, "Number of Lora networks to keep cached in memory", gr.Number, {"precision": 0}),
})) }))
shared.options_templates.update(shared.options_section(('compatibility', "Compatibility"), { shared.options_templates.update(shared.options_section(('compatibility', "Compatibility"), {
"lora_functional": shared.OptionInfo(False, "Lora: use old method that takes longer when you have multiple Loras active and produces same results as kohya-ss/sd-webui-additional-networks extension"), "lora_functional": shared.OptionInfo(False, "Lora/Networks: use old method that takes longer when you have multiple Loras active and produces same results as kohya-ss/sd-webui-additional-networks extension"),
})) }))
def create_lora_json(obj: lora.LoraOnDisk): def create_lora_json(obj: network.NetworkOnDisk):
return { return {
"name": obj.name, "name": obj.name,
"alias": obj.alias, "alias": obj.alias,
@@ -75,17 +56,17 @@ def create_lora_json(obj: lora.LoraOnDisk):
} }
def api_loras(_: gr.Blocks, app: FastAPI): def api_networks(_: gr.Blocks, app: FastAPI):
@app.get("/sdapi/v1/loras") @app.get("/sdapi/v1/loras")
async def get_loras(): async def get_loras():
return [create_lora_json(obj) for obj in lora.available_loras.values()] return [create_lora_json(obj) for obj in networks.available_networks.values()]
@app.post("/sdapi/v1/refresh-loras") @app.post("/sdapi/v1/refresh-loras")
async def refresh_loras(): async def refresh_loras():
return lora.list_available_loras() return networks.list_available_networks()
script_callbacks.on_app_started(api_loras) script_callbacks.on_app_started(api_networks)
re_lora = re.compile("<lora:([^:]+):") re_lora = re.compile("<lora:([^:]+):")
@@ -98,19 +79,21 @@ def infotext_pasted(infotext, d):
hashes = [x.strip().split(':', 1) for x in hashes.split(",")] hashes = [x.strip().split(':', 1) for x in hashes.split(",")]
hashes = {x[0].strip().replace(",", ""): x[1].strip() for x in hashes} hashes = {x[0].strip().replace(",", ""): x[1].strip() for x in hashes}
def lora_replacement(m): def network_replacement(m):
alias = m.group(1) alias = m.group(1)
shorthash = hashes.get(alias) shorthash = hashes.get(alias)
if shorthash is None: if shorthash is None:
return m.group(0) return m.group(0)
lora_on_disk = lora.available_lora_hash_lookup.get(shorthash) network_on_disk = networks.available_network_hash_lookup.get(shorthash)
if lora_on_disk is None: if network_on_disk is None:
return m.group(0) return m.group(0)
return f'<lora:{lora_on_disk.get_alias()}:' return f'<lora:{network_on_disk.get_alias()}:'
d["Prompt"] = re.sub(re_lora, lora_replacement, d["Prompt"]) d["Prompt"] = re.sub(re_lora, network_replacement, d["Prompt"])
script_callbacks.on_infotext_pasted(infotext_pasted) script_callbacks.on_infotext_pasted(infotext_pasted)
shared.opts.onchange("lora_in_memory_limit", networks.purge_networks_from_memory)
@@ -0,0 +1,217 @@
import datetime
import html
import random
import gradio as gr
import re
from modules import ui_extra_networks_user_metadata
def is_non_comma_tagset(tags):
average_tag_length = sum(len(x) for x in tags.keys()) / len(tags)
return average_tag_length >= 16
re_word = re.compile(r"[-_\w']+")
re_comma = re.compile(r" *, *")
def build_tags(metadata):
tags = {}
for _, tags_dict in metadata.get("ss_tag_frequency", {}).items():
for tag, tag_count in tags_dict.items():
tag = tag.strip()
tags[tag] = tags.get(tag, 0) + int(tag_count)
if tags and is_non_comma_tagset(tags):
new_tags = {}
for text, text_count in tags.items():
for word in re.findall(re_word, text):
if len(word) < 3:
continue
new_tags[word] = new_tags.get(word, 0) + text_count
tags = new_tags
ordered_tags = sorted(tags.keys(), key=tags.get, reverse=True)
return [(tag, tags[tag]) for tag in ordered_tags]
class LoraUserMetadataEditor(ui_extra_networks_user_metadata.UserMetadataEditor):
def __init__(self, ui, tabname, page):
super().__init__(ui, tabname, page)
self.select_sd_version = None
self.taginfo = None
self.edit_activation_text = None
self.slider_preferred_weight = None
self.edit_notes = None
def save_lora_user_metadata(self, name, desc, sd_version, activation_text, preferred_weight, notes):
user_metadata = self.get_user_metadata(name)
user_metadata["description"] = desc
user_metadata["sd version"] = sd_version
user_metadata["activation text"] = activation_text
user_metadata["preferred weight"] = preferred_weight
user_metadata["notes"] = notes
self.write_user_metadata(name, user_metadata)
def get_metadata_table(self, name):
table = super().get_metadata_table(name)
item = self.page.items.get(name, {})
metadata = item.get("metadata") or {}
keys = {
'ss_output_name': "Output name:",
'ss_sd_model_name': "Model:",
'ss_clip_skip': "Clip skip:",
'ss_network_module': "Kohya module:",
}
for key, label in keys.items():
value = metadata.get(key, None)
if value is not None and str(value) != "None":
table.append((label, html.escape(value)))
ss_training_started_at = metadata.get('ss_training_started_at')
if ss_training_started_at:
table.append(("Date trained:", datetime.datetime.utcfromtimestamp(float(ss_training_started_at)).strftime('%Y-%m-%d %H:%M')))
ss_bucket_info = metadata.get("ss_bucket_info")
if ss_bucket_info and "buckets" in ss_bucket_info:
resolutions = {}
for _, bucket in ss_bucket_info["buckets"].items():
resolution = bucket["resolution"]
resolution = f'{resolution[1]}x{resolution[0]}'
resolutions[resolution] = resolutions.get(resolution, 0) + int(bucket["count"])
resolutions_list = sorted(resolutions.keys(), key=resolutions.get, reverse=True)
resolutions_text = html.escape(", ".join(resolutions_list[0:4]))
if len(resolutions) > 4:
resolutions_text += ", ..."
resolutions_text = f"<span title='{html.escape(', '.join(resolutions_list))}'>{resolutions_text}</span>"
table.append(('Resolutions:' if len(resolutions_list) > 1 else 'Resolution:', resolutions_text))
image_count = 0
for _, params in metadata.get("ss_dataset_dirs", {}).items():
image_count += int(params.get("img_count", 0))
if image_count:
table.append(("Dataset size:", image_count))
return table
def put_values_into_components(self, name):
user_metadata = self.get_user_metadata(name)
values = super().put_values_into_components(name)
item = self.page.items.get(name, {})
metadata = item.get("metadata") or {}
tags = build_tags(metadata)
gradio_tags = [(tag, str(count)) for tag, count in tags[0:24]]
return [
*values[0:5],
item.get("sd_version", "Unknown"),
gr.HighlightedText.update(value=gradio_tags, visible=True if tags else False),
user_metadata.get('activation text', ''),
float(user_metadata.get('preferred weight', 0.0)),
gr.update(visible=True if tags else False),
gr.update(value=self.generate_random_prompt_from_tags(tags), visible=True if tags else False),
]
def generate_random_prompt(self, name):
item = self.page.items.get(name, {})
metadata = item.get("metadata") or {}
tags = build_tags(metadata)
return self.generate_random_prompt_from_tags(tags)
def generate_random_prompt_from_tags(self, tags):
max_count = None
res = []
for tag, count in tags:
if not max_count:
max_count = count
v = random.random() * max_count
if count > v:
res.append(tag)
return ", ".join(sorted(res))
def create_extra_default_items_in_left_column(self):
# this would be a lot better as gr.Radio but I can't make it work
self.select_sd_version = gr.Dropdown(['SD1', 'SD2', 'SDXL', 'Unknown'], value='Unknown', label='Stable Diffusion version', interactive=True)
def create_editor(self):
self.create_default_editor_elems()
self.taginfo = gr.HighlightedText(label="Training dataset tags")
self.edit_activation_text = gr.Text(label='Activation text', info="Will be added to prompt along with Lora")
self.slider_preferred_weight = gr.Slider(label='Preferred weight', info="Set to 0 to disable", minimum=0.0, maximum=2.0, step=0.01)
with gr.Row() as row_random_prompt:
with gr.Column(scale=8):
random_prompt = gr.Textbox(label='Random prompt', lines=4, max_lines=4, interactive=False)
with gr.Column(scale=1, min_width=120):
generate_random_prompt = gr.Button('Generate', size="lg", scale=1)
self.edit_notes = gr.TextArea(label='Notes', lines=4)
generate_random_prompt.click(fn=self.generate_random_prompt, inputs=[self.edit_name_input], outputs=[random_prompt], show_progress=False)
def select_tag(activation_text, evt: gr.SelectData):
tag = evt.value[0]
words = re.split(re_comma, activation_text)
if tag in words:
words = [x for x in words if x != tag and x.strip()]
return ", ".join(words)
return activation_text + ", " + tag if activation_text else tag
self.taginfo.select(fn=select_tag, inputs=[self.edit_activation_text], outputs=[self.edit_activation_text], show_progress=False)
self.create_default_buttons()
viewed_components = [
self.edit_name,
self.edit_description,
self.html_filedata,
self.html_preview,
self.edit_notes,
self.select_sd_version,
self.taginfo,
self.edit_activation_text,
self.slider_preferred_weight,
row_random_prompt,
random_prompt,
]
self.button_edit\
.click(fn=self.put_values_into_components, inputs=[self.edit_name_input], outputs=viewed_components)\
.then(fn=lambda: gr.update(visible=True), inputs=[], outputs=[self.box])
edited_components = [
self.edit_description,
self.select_sd_version,
self.edit_activation_text,
self.slider_preferred_weight,
self.edit_notes,
]
self.setup_save_handler(self.button_save, self.save_lora_user_metadata, edited_components)
@@ -1,8 +1,11 @@
import json
import os import os
import lora
import network
import networks
from modules import shared, ui_extra_networks from modules import shared, ui_extra_networks
from modules.ui_extra_networks import quote_js
from ui_edit_user_metadata import LoraUserMetadataEditor
class ExtraNetworksPageLora(ui_extra_networks.ExtraNetworksPage): class ExtraNetworksPageLora(ui_extra_networks.ExtraNetworksPage):
@@ -10,27 +13,70 @@ class ExtraNetworksPageLora(ui_extra_networks.ExtraNetworksPage):
super().__init__('Lora') super().__init__('Lora')
def refresh(self): def refresh(self):
lora.list_available_loras() networks.list_available_networks()
def create_item(self, name, index=None, enable_filter=True):
lora_on_disk = networks.available_networks.get(name)
if lora_on_disk is None:
return
def list_items(self):
for index, (name, lora_on_disk) in enumerate(lora.available_loras.items()):
path, ext = os.path.splitext(lora_on_disk.filename) path, ext = os.path.splitext(lora_on_disk.filename)
alias = lora_on_disk.get_alias() alias = lora_on_disk.get_alias()
yield { item = {
"name": name, "name": name,
"filename": path, "filename": lora_on_disk.filename,
"shorthash": lora_on_disk.shorthash,
"preview": self.find_preview(path), "preview": self.find_preview(path),
"description": self.find_description(path), "description": self.find_description(path),
"search_term": self.search_terms_from_path(lora_on_disk.filename), "search_term": self.search_terms_from_path(lora_on_disk.filename) + " " + (lora_on_disk.hash or ""),
"prompt": json.dumps(f"<lora:{alias}:") + " + opts.extra_networks_default_multiplier + " + json.dumps(">"),
"local_preview": f"{path}.{shared.opts.samples_format}", "local_preview": f"{path}.{shared.opts.samples_format}",
"metadata": json.dumps(lora_on_disk.metadata, indent=4) if lora_on_disk.metadata else None, "metadata": lora_on_disk.metadata,
"sort_keys": {'default': index, **self.get_sort_keys(lora_on_disk.filename)}, "sort_keys": {'default': index, **self.get_sort_keys(lora_on_disk.filename)},
"sd_version": lora_on_disk.sd_version.name,
} }
def allowed_directories_for_previews(self): self.read_user_metadata(item)
return [shared.cmd_opts.lora_dir] activation_text = item["user_metadata"].get("activation text")
preferred_weight = item["user_metadata"].get("preferred weight", 0.0)
item["prompt"] = quote_js(f"<lora:{alias}:") + " + " + (str(preferred_weight) if preferred_weight else "opts.extra_networks_default_multiplier") + " + " + quote_js(">")
if activation_text:
item["prompt"] += " + " + quote_js(" " + activation_text)
sd_version = item["user_metadata"].get("sd version")
if sd_version in network.SdVersion.__members__:
item["sd_version"] = sd_version
sd_version = network.SdVersion[sd_version]
else:
sd_version = lora_on_disk.sd_version
if shared.opts.lora_show_all or not enable_filter:
pass
elif sd_version == network.SdVersion.Unknown:
model_version = network.SdVersion.SDXL if shared.sd_model.is_sdxl else network.SdVersion.SD2 if shared.sd_model.is_sd2 else network.SdVersion.SD1
if model_version.name in shared.opts.lora_hide_unknown_for_versions:
return None
elif shared.sd_model.is_sdxl and sd_version != network.SdVersion.SDXL:
return None
elif shared.sd_model.is_sd2 and sd_version != network.SdVersion.SD2:
return None
elif shared.sd_model.is_sd1 and sd_version != network.SdVersion.SD1:
return None
return item
def list_items(self):
# instantiate a list to protect against concurrent modification
names = list(networks.available_networks)
for index, name in enumerate(names):
item = self.create_item(name, index)
if item is not None:
yield item
def allowed_directories_for_previews(self):
return [shared.cmd_opts.lora_dir, shared.cmd_opts.lyco_dir_backcompat]
def create_user_metadata_editor(self, ui, tabname):
return LoraUserMetadataEditor(ui, tabname, self)
@@ -1,4 +1,3 @@
import os.path
import sys import sys
import PIL.Image import PIL.Image
@@ -6,12 +5,11 @@ import numpy as np
import torch import torch
from tqdm import tqdm from tqdm import tqdm
from basicsr.utils.download_util import load_file_from_url
import modules.upscaler import modules.upscaler
from modules import devices, modelloader, script_callbacks, errors from modules import devices, modelloader, script_callbacks, errors
from scunet_model_arch import SCUNet as net from scunet_model_arch import SCUNet
from modules.modelloader import load_file_from_url
from modules.shared import opts from modules.shared import opts
@@ -28,7 +26,7 @@ class UpscalerScuNET(modules.upscaler.Upscaler):
scalers = [] scalers = []
add_model2 = True add_model2 = True
for file in model_paths: for file in model_paths:
if "http" in file: if file.startswith("http"):
name = self.model_name name = self.model_name
else: else:
name = modelloader.friendly_name(file) name = modelloader.friendly_name(file)
@@ -87,11 +85,12 @@ class UpscalerScuNET(modules.upscaler.Upscaler):
def do_upscale(self, img: PIL.Image.Image, selected_file): def do_upscale(self, img: PIL.Image.Image, selected_file):
torch.cuda.empty_cache() devices.torch_gc()
try:
model = self.load_model(selected_file) model = self.load_model(selected_file)
if model is None: except Exception as e:
print(f"ScuNET: Unable to load model from {selected_file}", file=sys.stderr) print(f"ScuNET: Unable to load model from {selected_file}: {e}", file=sys.stderr)
return img return img
device = devices.get_device_for('scunet') device = devices.get_device_for('scunet')
@@ -111,7 +110,7 @@ class UpscalerScuNET(modules.upscaler.Upscaler):
torch_output = torch_output[:, :h * 1, :w * 1] # remove padding, if any torch_output = torch_output[:, :h * 1, :w * 1] # remove padding, if any
np_output: np.ndarray = torch_output.float().cpu().clamp_(0, 1).numpy() np_output: np.ndarray = torch_output.float().cpu().clamp_(0, 1).numpy()
del torch_img, torch_output del torch_img, torch_output
torch.cuda.empty_cache() devices.torch_gc()
output = np_output.transpose((1, 2, 0)) # CHW to HWC output = np_output.transpose((1, 2, 0)) # CHW to HWC
output = output[:, :, ::-1] # BGR to RGB output = output[:, :, ::-1] # BGR to RGB
@@ -119,15 +118,12 @@ class UpscalerScuNET(modules.upscaler.Upscaler):
def load_model(self, path: str): def load_model(self, path: str):
device = devices.get_device_for('scunet') device = devices.get_device_for('scunet')
if "http" in path: if path.startswith("http"):
filename = load_file_from_url(url=self.model_url, model_dir=self.model_download_path, file_name="%s.pth" % self.name, progress=True) # TODO: this doesn't use `path` at all?
filename = load_file_from_url(self.model_url, model_dir=self.model_download_path, file_name=f"{self.name}.pth")
else: else:
filename = path filename = path
if not os.path.exists(os.path.join(self.model_path, filename)) or filename is None: model = SCUNet(in_nc=3, config=[4, 4, 4, 4, 4, 4, 4], dim=64)
print(f"ScuNET: Unable to load model from {filename}", file=sys.stderr)
return None
model = net(in_nc=3, config=[4, 4, 4, 4, 4, 4, 4], dim=64)
model.load_state_dict(torch.load(filename), strict=True) model.load_state_dict(torch.load(filename), strict=True)
model.eval() model.eval()
for _, v in model.named_parameters(): for _, v in model.named_parameters():
@@ -1,34 +1,35 @@
import os import sys
import platform
import numpy as np import numpy as np
import torch import torch
from PIL import Image from PIL import Image
from basicsr.utils.download_util import load_file_from_url
from tqdm import tqdm from tqdm import tqdm
from modules import modelloader, devices, script_callbacks, shared from modules import modelloader, devices, script_callbacks, shared
from modules.shared import opts, state from modules.shared import opts, state
from swinir_model_arch import SwinIR as net from swinir_model_arch import SwinIR
from swinir_model_arch_v2 import Swin2SR as net2 from swinir_model_arch_v2 import Swin2SR
from modules.upscaler import Upscaler, UpscalerData from modules.upscaler import Upscaler, UpscalerData
SWINIR_MODEL_URL = "https://github.com/JingyunLiang/SwinIR/releases/download/v0.0/003_realSR_BSRGAN_DFOWMFC_s64w8_SwinIR-L_x4_GAN.pth"
device_swinir = devices.get_device_for('swinir') device_swinir = devices.get_device_for('swinir')
class UpscalerSwinIR(Upscaler): class UpscalerSwinIR(Upscaler):
def __init__(self, dirname): def __init__(self, dirname):
self._cached_model = None # keep the model when SWIN_torch_compile is on to prevent re-compile every runs
self._cached_model_config = None # to clear '_cached_model' when changing model (v1/v2) or settings
self.name = "SwinIR" self.name = "SwinIR"
self.model_url = "https://github.com/JingyunLiang/SwinIR/releases/download/v0.0" \ self.model_url = SWINIR_MODEL_URL
"/003_realSR_BSRGAN_DFOWMFC_s64w8_SwinIR" \
"-L_x4_GAN.pth "
self.model_name = "SwinIR 4x" self.model_name = "SwinIR 4x"
self.user_path = dirname self.user_path = dirname
super().__init__() super().__init__()
scalers = [] scalers = []
model_files = self.find_models(ext_filter=[".pt", ".pth"]) model_files = self.find_models(ext_filter=[".pt", ".pth"])
for model in model_files: for model in model_files:
if "http" in model: if model.startswith("http"):
name = self.model_name name = self.model_name
else: else:
name = modelloader.friendly_name(model) name = modelloader.friendly_name(model)
@@ -37,27 +38,39 @@ class UpscalerSwinIR(Upscaler):
self.scalers = scalers self.scalers = scalers
def do_upscale(self, img, model_file): def do_upscale(self, img, model_file):
use_compile = hasattr(opts, 'SWIN_torch_compile') and opts.SWIN_torch_compile \
and int(torch.__version__.split('.')[0]) >= 2 and platform.system() != "Windows"
current_config = (model_file, opts.SWIN_tile)
if use_compile and self._cached_model_config == current_config:
model = self._cached_model
else:
self._cached_model = None
try:
model = self.load_model(model_file) model = self.load_model(model_file)
if model is None: except Exception as e:
print(f"Failed loading SwinIR model {model_file}: {e}", file=sys.stderr)
return img return img
model = model.to(device_swinir, dtype=devices.dtype) model = model.to(device_swinir, dtype=devices.dtype)
if use_compile:
model = torch.compile(model)
self._cached_model = model
self._cached_model_config = current_config
img = upscale(img, model) img = upscale(img, model)
try: devices.torch_gc()
torch.cuda.empty_cache()
except Exception:
pass
return img return img
def load_model(self, path, scale=4): def load_model(self, path, scale=4):
if "http" in path: if path.startswith("http"):
dl_name = "%s%s" % (self.model_name.replace(" ", "_"), ".pth") filename = modelloader.load_file_from_url(
filename = load_file_from_url(url=path, model_dir=self.model_download_path, file_name=dl_name, progress=True) url=path,
model_dir=self.model_download_path,
file_name=f"{self.model_name.replace(' ', '_')}.pth",
)
else: else:
filename = path filename = path
if filename is None or not os.path.exists(filename):
return None
if filename.endswith(".v2.pth"): if filename.endswith(".v2.pth"):
model = net2( model = Swin2SR(
upscale=scale, upscale=scale,
in_chans=3, in_chans=3,
img_size=64, img_size=64,
@@ -72,7 +85,7 @@ class UpscalerSwinIR(Upscaler):
) )
params = None params = None
else: else:
model = net( model = SwinIR(
upscale=scale, upscale=scale,
in_chans=3, in_chans=3,
img_size=64, img_size=64,
@@ -172,6 +185,8 @@ def on_ui_settings():
shared.opts.add_option("SWIN_tile", shared.OptionInfo(192, "Tile size for all SwinIR.", gr.Slider, {"minimum": 16, "maximum": 512, "step": 16}, section=('upscaling', "Upscaling"))) shared.opts.add_option("SWIN_tile", shared.OptionInfo(192, "Tile size for all SwinIR.", gr.Slider, {"minimum": 16, "maximum": 512, "step": 16}, section=('upscaling', "Upscaling")))
shared.opts.add_option("SWIN_tile_overlap", shared.OptionInfo(8, "Tile overlap, in pixels for SwinIR. Low values = visible seam.", gr.Slider, {"minimum": 0, "maximum": 48, "step": 1}, section=('upscaling', "Upscaling"))) shared.opts.add_option("SWIN_tile_overlap", shared.OptionInfo(8, "Tile overlap, in pixels for SwinIR. Low values = visible seam.", gr.Slider, {"minimum": 0, "maximum": 48, "step": 1}, section=('upscaling', "Upscaling")))
if int(torch.__version__.split('.')[0]) >= 2 and platform.system() != "Windows": # torch.compile() require pytorch 2.0 or above, and not on Windows
shared.opts.add_option("SWIN_torch_compile", shared.OptionInfo(False, "Use torch.compile to accelerate SwinIR.", gr.Checkbox, {"interactive": True}, section=('upscaling', "Upscaling")).info("Takes longer on first run"))
script_callbacks.on_ui_settings(on_ui_settings) script_callbacks.on_ui_settings(on_ui_settings)
@@ -4,16 +4,30 @@ onUiLoaded(async() => {
inpaint: "#img2maskimg", inpaint: "#img2maskimg",
inpaintSketch: "#inpaint_sketch", inpaintSketch: "#inpaint_sketch",
rangeGroup: "#img2img_column_size", rangeGroup: "#img2img_column_size",
sketch: "#img2img_sketch", sketch: "#img2img_sketch"
}; };
const tabNameToElementId = { const tabNameToElementId = {
"Inpaint sketch": elementIDs.inpaintSketch, "Inpaint sketch": elementIDs.inpaintSketch,
"Inpaint": elementIDs.inpaint, "Inpaint": elementIDs.inpaint,
"Sketch": elementIDs.sketch, "Sketch": elementIDs.sketch
}; };
// Helper functions // Helper functions
// Get active tab // Get active tab
/**
* Waits for an element to be present in the DOM.
*/
const waitForElement = (id) => new Promise(resolve => {
const checkForElement = () => {
const element = document.querySelector(id);
if (element) return resolve(element);
setTimeout(checkForElement, 100);
};
checkForElement();
});
function getActiveTab(elements, all = false) { function getActiveTab(elements, all = false) {
const tabs = elements.img2imgTabs.querySelectorAll("button"); const tabs = elements.img2imgTabs.querySelectorAll("button");
@@ -42,43 +56,115 @@ onUiLoaded(async() => {
} }
} }
// Check is hotkey valid // Detect whether the element has a horizontal scroll bar
function isSingleLetter(value) { function hasHorizontalScrollbar(element) {
return element.scrollWidth > element.clientWidth;
}
// Function for defining the "Ctrl", "Shift" and "Alt" keys
function isModifierKey(event, key) {
switch (key) {
case "Ctrl":
return event.ctrlKey;
case "Shift":
return event.shiftKey;
case "Alt":
return event.altKey;
default:
return false;
}
}
// Check if hotkey is valid
function isValidHotkey(value) {
const specialKeys = ["Ctrl", "Alt", "Shift", "Disable"];
return ( return (
typeof value === "string" && value.length === 1 && /[a-z]/i.test(value) (typeof value === "string" &&
value.length === 1 &&
/[a-z]/i.test(value)) ||
specialKeys.includes(value)
); );
} }
// Create hotkeyConfig from opts // Normalize hotkey
function normalizeHotkey(hotkey) {
return hotkey.length === 1 ? "Key" + hotkey.toUpperCase() : hotkey;
}
// Format hotkey for display
function formatHotkeyForDisplay(hotkey) {
return hotkey.startsWith("Key") ? hotkey.slice(3) : hotkey;
}
// Create hotkey configuration with the provided options
function createHotkeyConfig(defaultHotkeysConfig, hotkeysConfigOpts) { function createHotkeyConfig(defaultHotkeysConfig, hotkeysConfigOpts) {
const result = {}; const result = {}; // Resulting hotkey configuration
const usedKeys = new Set(); const usedKeys = new Set(); // Set of used hotkeys
// Iterate through defaultHotkeysConfig keys
for (const key in defaultHotkeysConfig) { for (const key in defaultHotkeysConfig) {
if (typeof hotkeysConfigOpts[key] === "boolean") { const userValue = hotkeysConfigOpts[key]; // User-provided hotkey value
result[key] = hotkeysConfigOpts[key]; const defaultValue = defaultHotkeysConfig[key]; // Default hotkey value
continue;
} // Apply appropriate value for undefined, boolean, or object userValue
if ( if (
hotkeysConfigOpts[key] && userValue === undefined ||
isSingleLetter(hotkeysConfigOpts[key]) && typeof userValue === "boolean" ||
!usedKeys.has(hotkeysConfigOpts[key].toUpperCase()) typeof userValue === "object" ||
userValue === "disable"
) { ) {
// If the property passed the test and has not yet been used, add 'Key' before it and save it result[key] =
result[key] = "Key" + hotkeysConfigOpts[key].toUpperCase(); userValue === undefined ? defaultValue : userValue;
usedKeys.add(hotkeysConfigOpts[key].toUpperCase()); } else if (isValidHotkey(userValue)) {
const normalizedUserValue = normalizeHotkey(userValue);
// Check for conflicting hotkeys
if (!usedKeys.has(normalizedUserValue)) {
usedKeys.add(normalizedUserValue);
result[key] = normalizedUserValue;
} else { } else {
// If the property does not pass the test or has already been used, we keep the default value
console.error( console.error(
`Hotkey: ${hotkeysConfigOpts[key]} for ${key} is repeated and conflicts with another hotkey or is not 1 letter. The default hotkey is used: ${defaultHotkeysConfig[key][3]}` `Hotkey: ${formatHotkeyForDisplay(
userValue
)} for ${key} is repeated and conflicts with another hotkey. The default hotkey is used: ${formatHotkeyForDisplay(
defaultValue
)}`
); );
result[key] = defaultHotkeysConfig[key]; result[key] = defaultValue;
}
} else {
console.error(
`Hotkey: ${formatHotkeyForDisplay(
userValue
)} for ${key} is not valid. The default hotkey is used: ${formatHotkeyForDisplay(
defaultValue
)}`
);
result[key] = defaultValue;
} }
} }
return result; return result;
} }
// Disables functions in the config object based on the provided list of function names
function disableFunctions(config, disabledFunctions) {
// Bind the hasOwnProperty method to the functionMap object to avoid errors
const hasOwnProperty =
Object.prototype.hasOwnProperty.bind(functionMap);
// Loop through the disabledFunctions array and disable the corresponding functions in the config object
disabledFunctions.forEach(funcName => {
if (hasOwnProperty(funcName)) {
const key = functionMap[funcName];
config[key] = "disable";
}
});
// Return the updated config object
return config;
}
/** /**
* The restoreImgRedMask function displays a red mask around an image to indicate the aspect ratio. * The restoreImgRedMask function displays a red mask around an image to indicate the aspect ratio.
* If the image display property is set to 'none', the mask breaks. To fix this, the function * If the image display property is set to 'none', the mask breaks. To fix this, the function
@@ -100,7 +186,9 @@ onUiLoaded(async() => {
imageARPreview.style.transform = ""; imageARPreview.style.transform = "";
if (parseFloat(mainTab.style.width) > 865) { if (parseFloat(mainTab.style.width) > 865) {
const transformString = mainTab.style.transform; const transformString = mainTab.style.transform;
const scaleMatch = transformString.match(/scale\(([-+]?[0-9]*\.?[0-9]+)\)/); const scaleMatch = transformString.match(
/scale\(([-+]?[0-9]*\.?[0-9]+)\)/
);
let zoom = 1; // default zoom let zoom = 1; // default zoom
if (scaleMatch && scaleMatch[1]) { if (scaleMatch && scaleMatch[1]) {
@@ -124,31 +212,54 @@ onUiLoaded(async() => {
// Default config // Default config
const defaultHotkeysConfig = { const defaultHotkeysConfig = {
canvas_hotkey_zoom: "Alt",
canvas_hotkey_adjust: "Ctrl",
canvas_hotkey_reset: "KeyR", canvas_hotkey_reset: "KeyR",
canvas_hotkey_fullscreen: "KeyS", canvas_hotkey_fullscreen: "KeyS",
canvas_hotkey_move: "KeyF", canvas_hotkey_move: "KeyF",
canvas_hotkey_overlap: "KeyO", canvas_hotkey_overlap: "KeyO",
canvas_disabled_functions: [],
canvas_show_tooltip: true, canvas_show_tooltip: true,
canvas_swap_controls: false canvas_auto_expand: true,
canvas_blur_prompt: false,
}; };
// swap the actions for ctr + wheel and shift + wheel
const hotkeysConfig = createHotkeyConfig( const functionMap = {
"Zoom": "canvas_hotkey_zoom",
"Adjust brush size": "canvas_hotkey_adjust",
"Moving canvas": "canvas_hotkey_move",
"Fullscreen": "canvas_hotkey_fullscreen",
"Reset Zoom": "canvas_hotkey_reset",
"Overlap": "canvas_hotkey_overlap"
};
// Loading the configuration from opts
const preHotkeysConfig = createHotkeyConfig(
defaultHotkeysConfig, defaultHotkeysConfig,
hotkeysConfigOpts hotkeysConfigOpts
); );
// Disable functions that are not needed by the user
const hotkeysConfig = disableFunctions(
preHotkeysConfig,
preHotkeysConfig.canvas_disabled_functions
);
let isMoving = false; let isMoving = false;
let mouseX, mouseY; let mouseX, mouseY;
let activeElement; let activeElement;
const elements = Object.fromEntries(Object.keys(elementIDs).map((id) => [ const elements = Object.fromEntries(
Object.keys(elementIDs).map(id => [
id, id,
gradioApp().querySelector(elementIDs[id]), gradioApp().querySelector(elementIDs[id])
])); ])
);
const elemData = {}; const elemData = {};
// Apply functionality to the range inputs. Restore redmask and correct for long images. // Apply functionality to the range inputs. Restore redmask and correct for long images.
const rangeInputs = elements.rangeGroup ? Array.from(elements.rangeGroup.querySelectorAll("input")) : const rangeInputs = elements.rangeGroup ?
Array.from(elements.rangeGroup.querySelectorAll("input")) :
[ [
gradioApp().querySelector("#img2img_width input[type='range']"), gradioApp().querySelector("#img2img_width input[type='range']"),
gradioApp().querySelector("#img2img_height input[type='range']") gradioApp().querySelector("#img2img_height input[type='range']")
@@ -158,7 +269,7 @@ onUiLoaded(async() => {
input?.addEventListener("input", () => restoreImgRedMask(elements)); input?.addEventListener("input", () => restoreImgRedMask(elements));
} }
function applyZoomAndPan(elemId) { function applyZoomAndPan(elemId, isExtension = true) {
const targetElement = gradioApp().querySelector(elemId); const targetElement = gradioApp().querySelector(elemId);
if (!targetElement) { if (!targetElement) {
@@ -180,38 +291,56 @@ onUiLoaded(async() => {
const toolTipElemnt = const toolTipElemnt =
targetElement.querySelector(".image-container"); targetElement.querySelector(".image-container");
const tooltip = document.createElement("div"); const tooltip = document.createElement("div");
tooltip.className = "tooltip"; tooltip.className = "canvas-tooltip";
// Creating an item of information // Creating an item of information
const info = document.createElement("i"); const info = document.createElement("i");
info.className = "tooltip-info"; info.className = "canvas-tooltip-info";
info.textContent = ""; info.textContent = "";
// Create a container for the contents of the tooltip // Create a container for the contents of the tooltip
const tooltipContent = document.createElement("div"); const tooltipContent = document.createElement("div");
tooltipContent.className = "tooltip-content"; tooltipContent.className = "canvas-tooltip-content";
// Add info about hotkeys // Define an array with hotkey information and their actions
const zoomKey = hotkeysConfig.canvas_swap_controls ? "Ctrl" : "Shift"; const hotkeysInfo = [
const adjustKey = hotkeysConfig.canvas_swap_controls ? "Shift" : "Ctrl";
const hotkeys = [
{key: `${zoomKey} + wheel`, action: "Zoom canvas"},
{key: `${adjustKey} + wheel`, action: "Adjust brush size"},
{ {
key: hotkeysConfig.canvas_hotkey_reset.charAt(hotkeysConfig.canvas_hotkey_reset.length - 1), configKey: "canvas_hotkey_zoom",
action: "Reset zoom" action: "Zoom canvas",
keySuffix: " + wheel"
}, },
{ {
key: hotkeysConfig.canvas_hotkey_fullscreen.charAt(hotkeysConfig.canvas_hotkey_fullscreen.length - 1), configKey: "canvas_hotkey_adjust",
action: "Adjust brush size",
keySuffix: " + wheel"
},
{configKey: "canvas_hotkey_reset", action: "Reset zoom"},
{
configKey: "canvas_hotkey_fullscreen",
action: "Fullscreen mode" action: "Fullscreen mode"
}, },
{ {configKey: "canvas_hotkey_move", action: "Move canvas"},
key: hotkeysConfig.canvas_hotkey_move.charAt(hotkeysConfig.canvas_hotkey_move.length - 1), {configKey: "canvas_hotkey_overlap", action: "Overlap"}
action: "Move canvas"
}
]; ];
// Create hotkeys array with disabled property based on the config values
const hotkeys = hotkeysInfo.map(info => {
const configValue = hotkeysConfig[info.configKey];
const key = info.keySuffix ?
`${configValue}${info.keySuffix}` :
configValue.charAt(configValue.length - 1);
return {
key,
action: info.action,
disabled: configValue === "disable"
};
});
for (const hotkey of hotkeys) { for (const hotkey of hotkeys) {
if (hotkey.disabled) {
continue;
}
const p = document.createElement("p"); const p = document.createElement("p");
p.innerHTML = `<b>${hotkey.key}</b> - ${hotkey.action}`; p.innerHTML = `<b>${hotkey.key}</b> - ${hotkey.action}`;
tooltipContent.appendChild(p); tooltipContent.appendChild(p);
@@ -252,6 +381,12 @@ onUiLoaded(async() => {
panY: 0 panY: 0
}; };
if (isExtension) {
targetElement.style.overflow = "hidden";
}
targetElement.isZoomed = false;
fixCanvas(); fixCanvas();
targetElement.style.transform = `scale(${elemData[elemId].zoomLevel}) translate(${elemData[elemId].panX}px, ${elemData[elemId].panY}px)`; targetElement.style.transform = `scale(${elemData[elemId].zoomLevel}) translate(${elemData[elemId].panX}px, ${elemData[elemId].panY}px)`;
@@ -262,8 +397,27 @@ onUiLoaded(async() => {
toggleOverlap("off"); toggleOverlap("off");
fullScreenMode = false; fullScreenMode = false;
const closeBtn = targetElement.querySelector("button[aria-label='Remove Image']");
if (closeBtn) {
closeBtn.addEventListener("click", resetZoom);
}
if (canvas && isExtension) {
const parentElement = targetElement.closest('[id^="component-"]');
if ( if (
canvas && canvas &&
parseFloat(canvas.style.width) > parentElement.offsetWidth &&
parseFloat(targetElement.style.width) > parentElement.offsetWidth
) {
fitToElement();
return;
}
}
if (
canvas &&
!isExtension &&
parseFloat(canvas.style.width) > 865 && parseFloat(canvas.style.width) > 865 &&
parseFloat(targetElement.style.width) > 865 parseFloat(targetElement.style.width) > 865
) { ) {
@@ -272,9 +426,6 @@ onUiLoaded(async() => {
} }
targetElement.style.width = ""; targetElement.style.width = "";
if (canvas) {
targetElement.style.height = canvas.style.height;
}
} }
// Toggle the zIndex of the target element between two values, allowing it to overlap or be overlapped by other elements // Toggle the zIndex of the target element between two values, allowing it to overlap or be overlapped by other elements
@@ -330,7 +481,7 @@ onUiLoaded(async() => {
// Update the zoom level and pan position of the target element based on the values of the zoomLevel, panX and panY variables // Update the zoom level and pan position of the target element based on the values of the zoomLevel, panX and panY variables
function updateZoom(newZoomLevel, mouseX, mouseY) { function updateZoom(newZoomLevel, mouseX, mouseY) {
newZoomLevel = Math.max(0.5, Math.min(newZoomLevel, 15)); newZoomLevel = Math.max(0.1, Math.min(newZoomLevel, 15));
elemData[elemId].panX += elemData[elemId].panX +=
mouseX - (mouseX * newZoomLevel) / elemData[elemId].zoomLevel; mouseX - (mouseX * newZoomLevel) / elemData[elemId].zoomLevel;
@@ -341,15 +492,16 @@ onUiLoaded(async() => {
targetElement.style.transform = `translate(${elemData[elemId].panX}px, ${elemData[elemId].panY}px) scale(${newZoomLevel})`; targetElement.style.transform = `translate(${elemData[elemId].panX}px, ${elemData[elemId].panY}px) scale(${newZoomLevel})`;
toggleOverlap("on"); toggleOverlap("on");
if (isExtension) {
targetElement.style.overflow = "visible";
}
return newZoomLevel; return newZoomLevel;
} }
// Change the zoom level based on user interaction // Change the zoom level based on user interaction
function changeZoomLevel(operation, e) { function changeZoomLevel(operation, e) {
if ( if (isModifierKey(e, hotkeysConfig.canvas_hotkey_zoom)) {
(!hotkeysConfig.canvas_swap_controls && e.shiftKey) ||
(hotkeysConfig.canvas_swap_controls && e.ctrlKey)
) {
e.preventDefault(); e.preventDefault();
let zoomPosX, zoomPosY; let zoomPosX, zoomPosY;
@@ -370,6 +522,8 @@ onUiLoaded(async() => {
zoomPosX - targetElement.getBoundingClientRect().left, zoomPosX - targetElement.getBoundingClientRect().left,
zoomPosY - targetElement.getBoundingClientRect().top zoomPosY - targetElement.getBoundingClientRect().top
); );
targetElement.isZoomed = true;
} }
} }
@@ -383,10 +537,19 @@ onUiLoaded(async() => {
//Reset Zoom //Reset Zoom
targetElement.style.transform = `translate(${0}px, ${0}px) scale(${1})`; targetElement.style.transform = `translate(${0}px, ${0}px) scale(${1})`;
let parentElement;
if (isExtension) {
parentElement = targetElement.closest('[id^="component-"]');
} else {
parentElement = targetElement.parentElement;
}
// Get element and screen dimensions // Get element and screen dimensions
const elementWidth = targetElement.offsetWidth; const elementWidth = targetElement.offsetWidth;
const elementHeight = targetElement.offsetHeight; const elementHeight = targetElement.offsetHeight;
const parentElement = targetElement.parentElement;
const screenWidth = parentElement.clientWidth; const screenWidth = parentElement.clientWidth;
const screenHeight = parentElement.clientHeight; const screenHeight = parentElement.clientHeight;
@@ -439,8 +602,12 @@ onUiLoaded(async() => {
if (!canvas) return; if (!canvas) return;
if (canvas.offsetWidth > 862) { if (canvas.offsetWidth > 862 || isExtension) {
targetElement.style.width = canvas.offsetWidth + "px"; targetElement.style.width = (canvas.offsetWidth + 2) + "px";
}
if (isExtension) {
targetElement.style.overflow = "visible";
} }
if (fullScreenMode) { if (fullScreenMode) {
@@ -503,6 +670,19 @@ onUiLoaded(async() => {
// Handle keydown events // Handle keydown events
function handleKeyDown(event) { function handleKeyDown(event) {
// Disable key locks to make pasting from the buffer work correctly
if ((event.ctrlKey && event.code === 'KeyV') || (event.ctrlKey && event.code === 'KeyC') || event.code === "F5") {
return;
}
// before activating shortcut, ensure user is not actively typing in an input field
if (!hotkeysConfig.canvas_blur_prompt) {
if (event.target.nodeName === 'TEXTAREA' || event.target.nodeName === 'INPUT') {
return;
}
}
const hotkeyActions = { const hotkeyActions = {
[hotkeysConfig.canvas_hotkey_reset]: resetZoom, [hotkeysConfig.canvas_hotkey_reset]: resetZoom,
[hotkeysConfig.canvas_hotkey_overlap]: toggleOverlap, [hotkeysConfig.canvas_hotkey_overlap]: toggleOverlap,
@@ -514,6 +694,13 @@ onUiLoaded(async() => {
event.preventDefault(); event.preventDefault();
action(event); action(event);
} }
if (
isModifierKey(event, hotkeysConfig.canvas_hotkey_zoom) ||
isModifierKey(event, hotkeysConfig.canvas_hotkey_adjust)
) {
event.preventDefault();
}
} }
// Get Mouse position // Get Mouse position
@@ -522,8 +709,48 @@ onUiLoaded(async() => {
mouseY = e.offsetY; mouseY = e.offsetY;
} }
// Simulation of the function to put a long image into the screen.
// We detect if an image has a scroll bar or not, make a fullscreen to reveal the image, then reduce it to fit into the element.
// We hide the image and show it to the user when it is ready.
targetElement.isExpanded = false;
function autoExpand() {
const canvas = document.querySelector(`${elemId} canvas[key="interface"]`);
if (canvas) {
if (hasHorizontalScrollbar(targetElement) && targetElement.isExpanded === false) {
targetElement.style.visibility = "hidden";
setTimeout(() => {
fitToScreen();
resetZoom();
targetElement.style.visibility = "visible";
targetElement.isExpanded = true;
}, 10);
}
}
}
targetElement.addEventListener("mousemove", getMousePosition); targetElement.addEventListener("mousemove", getMousePosition);
//observers
// Creating an observer with a callback function to handle DOM changes
const observer = new MutationObserver((mutationsList, observer) => {
for (let mutation of mutationsList) {
// If the style attribute of the canvas has changed, by observation it happens only when the picture changes
if (mutation.type === 'attributes' && mutation.attributeName === 'style' &&
mutation.target.tagName.toLowerCase() === 'canvas') {
targetElement.isExpanded = false;
setTimeout(resetZoom, 10);
}
}
});
// Apply auto expand if enabled
if (hotkeysConfig.canvas_auto_expand) {
targetElement.addEventListener("mousemove", autoExpand);
// Set up an observer to track attribute changes
observer.observe(targetElement, {attributes: true, childList: true, subtree: true});
}
// Handle events only inside the targetElement // Handle events only inside the targetElement
let isKeyDownHandlerAttached = false; let isKeyDownHandlerAttached = false;
@@ -564,11 +791,7 @@ onUiLoaded(async() => {
changeZoomLevel(operation, e); changeZoomLevel(operation, e);
// Handle brush size adjustment with ctrl key pressed // Handle brush size adjustment with ctrl key pressed
if ( if (isModifierKey(e, hotkeysConfig.canvas_hotkey_adjust)) {
(hotkeysConfig.canvas_swap_controls && e.shiftKey) ||
(!hotkeysConfig.canvas_swap_controls &&
(e.ctrlKey || e.metaKey))
) {
e.preventDefault(); e.preventDefault();
// Increase or decrease brush size based on scroll direction // Increase or decrease brush size based on scroll direction
@@ -578,6 +801,20 @@ onUiLoaded(async() => {
// Handle the move event for pan functionality. Updates the panX and panY variables and applies the new transform to the target element. // Handle the move event for pan functionality. Updates the panX and panY variables and applies the new transform to the target element.
function handleMoveKeyDown(e) { function handleMoveKeyDown(e) {
// Disable key locks to make pasting from the buffer work correctly
if ((e.ctrlKey && e.code === 'KeyV') || (e.ctrlKey && event.code === 'KeyC') || e.code === "F5") {
return;
}
// before activating shortcut, ensure user is not actively typing in an input field
if (!hotkeysConfig.canvas_blur_prompt) {
if (e.target.nodeName === 'TEXTAREA' || e.target.nodeName === 'INPUT') {
return;
}
}
if (e.code === hotkeysConfig.canvas_hotkey_move) { if (e.code === hotkeysConfig.canvas_hotkey_move) {
if (!e.ctrlKey && !e.metaKey && isKeyDownHandlerAttached) { if (!e.ctrlKey && !e.metaKey && isKeyDownHandlerAttached) {
e.preventDefault(); e.preventDefault();
@@ -618,6 +855,11 @@ onUiLoaded(async() => {
if (isMoving && elemId === activeElement) { if (isMoving && elemId === activeElement) {
updatePanPosition(e.movementX, e.movementY); updatePanPosition(e.movementX, e.movementY);
targetElement.style.pointerEvents = "none"; targetElement.style.pointerEvents = "none";
if (isExtension) {
targetElement.style.overflow = "visible";
}
} else { } else {
targetElement.style.pointerEvents = "auto"; targetElement.style.pointerEvents = "auto";
} }
@@ -628,13 +870,93 @@ onUiLoaded(async() => {
isMoving = false; isMoving = false;
}; };
gradioApp().addEventListener("mousemove", handleMoveByKey); // Checks for extension
function checkForOutBox() {
const parentElement = targetElement.closest('[id^="component-"]');
if (parentElement.offsetWidth < targetElement.offsetWidth && !targetElement.isExpanded) {
resetZoom();
targetElement.isExpanded = true;
} }
applyZoomAndPan(elementIDs.sketch); if (parentElement.offsetWidth < targetElement.offsetWidth && elemData[elemId].zoomLevel == 1) {
applyZoomAndPan(elementIDs.inpaint); resetZoom();
applyZoomAndPan(elementIDs.inpaintSketch); }
if (parentElement.offsetWidth < targetElement.offsetWidth && targetElement.offsetWidth * elemData[elemId].zoomLevel > parentElement.offsetWidth && elemData[elemId].zoomLevel < 1 && !targetElement.isZoomed) {
resetZoom();
}
}
if (isExtension) {
targetElement.addEventListener("mousemove", checkForOutBox);
}
window.addEventListener('resize', (e) => {
resetZoom();
if (isExtension) {
targetElement.isExpanded = false;
targetElement.isZoomed = false;
}
});
gradioApp().addEventListener("mousemove", handleMoveByKey);
}
applyZoomAndPan(elementIDs.sketch, false);
applyZoomAndPan(elementIDs.inpaint, false);
applyZoomAndPan(elementIDs.inpaintSketch, false);
// Make the function global so that other extensions can take advantage of this solution // Make the function global so that other extensions can take advantage of this solution
window.applyZoomAndPan = applyZoomAndPan; const applyZoomAndPanIntegration = async(id, elementIDs) => {
const mainEl = document.querySelector(id);
if (id.toLocaleLowerCase() === "none") {
for (const elementID of elementIDs) {
const el = await waitForElement(elementID);
if (!el) break;
applyZoomAndPan(elementID);
}
return;
}
if (!mainEl) return;
mainEl.addEventListener("click", async() => {
for (const elementID of elementIDs) {
const el = await waitForElement(elementID);
if (!el) break;
applyZoomAndPan(elementID);
}
}, {once: true});
};
window.applyZoomAndPan = applyZoomAndPan; // Only 1 elements, argument elementID, for example applyZoomAndPan("#txt2img_controlnet_ControlNet_input_image")
window.applyZoomAndPanIntegration = applyZoomAndPanIntegration; // for any extension
/*
The function `applyZoomAndPanIntegration` takes two arguments:
1. `id`: A string identifier for the element to which zoom and pan functionality will be applied on click.
If the `id` value is "none", the functionality will be applied to all elements specified in the second argument without a click event.
2. `elementIDs`: An array of string identifiers for elements. Zoom and pan functionality will be applied to each of these elements on click of the element specified by the first argument.
If "none" is specified in the first argument, the functionality will be applied to each of these elements without a click event.
Example usage:
applyZoomAndPanIntegration("#txt2img_controlnet", ["#txt2img_controlnet_ControlNet_input_image"]);
In this example, zoom and pan functionality will be applied to the element with the identifier "txt2img_controlnet_ControlNet_input_image" upon clicking the element with the identifier "txt2img_controlnet".
*/
// More examples
// Add integration with ControlNet txt2img One TAB
// applyZoomAndPanIntegration("#txt2img_controlnet", ["#txt2img_controlnet_ControlNet_input_image"]);
// Add integration with ControlNet txt2img Tabs
// applyZoomAndPanIntegration("#txt2img_controlnet",Array.from({ length: 10 }, (_, i) => `#txt2img_controlnet_ControlNet-${i}_input_image`));
// Add integration with Inpaint Anything
// applyZoomAndPanIntegration("None", ["#ia_sam_image", "#ia_sel_mask"]);
}); });
@@ -1,10 +1,15 @@
import gradio as gr
from modules import shared from modules import shared
shared.options_templates.update(shared.options_section(('canvas_hotkey', "Canvas Hotkeys"), { shared.options_templates.update(shared.options_section(('canvas_hotkey', "Canvas Hotkeys"), {
"canvas_hotkey_move": shared.OptionInfo("F", "Moving the canvas"), "canvas_hotkey_zoom": shared.OptionInfo("Alt", "Zoom canvas", gr.Radio, {"choices": ["Shift","Ctrl", "Alt"]}).info("If you choose 'Shift' you cannot scroll horizontally, 'Alt' can cause a little trouble in firefox"),
"canvas_hotkey_adjust": shared.OptionInfo("Ctrl", "Adjust brush size", gr.Radio, {"choices": ["Shift","Ctrl", "Alt"]}).info("If you choose 'Shift' you cannot scroll horizontally, 'Alt' can cause a little trouble in firefox"),
"canvas_hotkey_move": shared.OptionInfo("F", "Moving the canvas").info("To work correctly in firefox, turn off 'Automatically search the page text when typing' in the browser settings"),
"canvas_hotkey_fullscreen": shared.OptionInfo("S", "Fullscreen Mode, maximizes the picture so that it fits into the screen and stretches it to its full width "), "canvas_hotkey_fullscreen": shared.OptionInfo("S", "Fullscreen Mode, maximizes the picture so that it fits into the screen and stretches it to its full width "),
"canvas_hotkey_reset": shared.OptionInfo("R", "Reset zoom and canvas positon"), "canvas_hotkey_reset": shared.OptionInfo("R", "Reset zoom and canvas positon"),
"canvas_hotkey_overlap": shared.OptionInfo("O", "Toggle overlap ( Technical button, neededs for testing )"), "canvas_hotkey_overlap": shared.OptionInfo("O", "Toggle overlap").info("Technical button, neededs for testing"),
"canvas_show_tooltip": shared.OptionInfo(True, "Enable tooltip on the canvas"), "canvas_show_tooltip": shared.OptionInfo(True, "Enable tooltip on the canvas"),
"canvas_swap_controls": shared.OptionInfo(False, "Swap hotkey combinations for Zoom and Adjust brush resize"), "canvas_auto_expand": shared.OptionInfo(True, "Automatically expands an image that does not fit completely in the canvas area, similar to manually pressing the S and R buttons"),
"canvas_blur_prompt": shared.OptionInfo(False, "Take the focus off the prompt when working with a canvas"),
"canvas_disabled_functions": shared.OptionInfo(["Overlap"], "Disable function that you don't use", gr.CheckboxGroup, {"choices": ["Zoom","Adjust brush size", "Moving canvas","Fullscreen","Reset Zoom","Overlap"]}),
})) }))
@@ -1,4 +1,4 @@
.tooltip-info { .canvas-tooltip-info {
position: absolute; position: absolute;
top: 10px; top: 10px;
left: 10px; left: 10px;
@@ -15,7 +15,7 @@
z-index: 100; z-index: 100;
} }
.tooltip-info::after { .canvas-tooltip-info::after {
content: ''; content: '';
display: block; display: block;
width: 2px; width: 2px;
@@ -24,7 +24,7 @@
margin-top: 2px; margin-top: 2px;
} }
.tooltip-info::before { .canvas-tooltip-info::before {
content: ''; content: '';
display: block; display: block;
width: 2px; width: 2px;
@@ -32,7 +32,7 @@
background-color: white; background-color: white;
} }
.tooltip-content { .canvas-tooltip-content {
display: none; display: none;
background-color: #f9f9f9; background-color: #f9f9f9;
color: #333; color: #333;
@@ -50,7 +50,7 @@
z-index: 100; z-index: 100;
} }
.tooltip:hover .tooltip-content { .canvas-tooltip:hover .canvas-tooltip-content {
display: block; display: block;
animation: fadeIn 0.5s; animation: fadeIn 0.5s;
opacity: 1; opacity: 1;
@@ -61,3 +61,6 @@
to {opacity: 1;} to {opacity: 1;}
} }
.styler {
overflow:inherit !important;
}
@@ -1,5 +1,7 @@
import math
import gradio as gr import gradio as gr
from modules import scripts, shared, ui_components, ui_settings from modules import scripts, shared, ui_components, ui_settings, generation_parameters_copypaste
from modules.ui_components import FormColumn from modules.ui_components import FormColumn
@@ -19,18 +21,38 @@ class ExtraOptionsSection(scripts.Script):
def ui(self, is_img2img): def ui(self, is_img2img):
self.comps = [] self.comps = []
self.setting_names = [] self.setting_names = []
self.infotext_fields = []
extra_options = shared.opts.extra_options_img2img if is_img2img else shared.opts.extra_options_txt2img
mapping = {k: v for v, k in generation_parameters_copypaste.infotext_to_setting_name_mapping}
with gr.Blocks() as interface: with gr.Blocks() as interface:
with gr.Accordion("Options", open=False) if shared.opts.extra_options_accordion and shared.opts.extra_options else gr.Group(), gr.Row(): with gr.Accordion("Options", open=False) if shared.opts.extra_options_accordion and extra_options else gr.Group():
for setting_name in shared.opts.extra_options:
row_count = math.ceil(len(extra_options) / shared.opts.extra_options_cols)
for row in range(row_count):
with gr.Row():
for col in range(shared.opts.extra_options_cols):
index = row * shared.opts.extra_options_cols + col
if index >= len(extra_options):
break
setting_name = extra_options[index]
with FormColumn(): with FormColumn():
comp = ui_settings.create_setting_component(setting_name) comp = ui_settings.create_setting_component(setting_name)
self.comps.append(comp) self.comps.append(comp)
self.setting_names.append(setting_name) self.setting_names.append(setting_name)
setting_infotext_name = mapping.get(setting_name)
if setting_infotext_name is not None:
self.infotext_fields.append((comp, setting_infotext_name))
def get_settings_values(): def get_settings_values():
return [ui_settings.get_value_for_setting(key) for key in self.setting_names] res = [ui_settings.get_value_for_setting(key) for key in self.setting_names]
return res[0] if len(res) == 1 else res
interface.load(fn=get_settings_values, inputs=[], outputs=self.comps, queue=False, show_progress=False) interface.load(fn=get_settings_values, inputs=[], outputs=self.comps, queue=False, show_progress=False)
@@ -43,6 +65,10 @@ class ExtraOptionsSection(scripts.Script):
shared.options_templates.update(shared.options_section(('ui', "User interface"), { shared.options_templates.update(shared.options_section(('ui', "User interface"), {
"extra_options": shared.OptionInfo([], "Options in main UI", ui_components.DropdownMulti, lambda: {"choices": list(shared.opts.data_labels.keys())}).js("info", "settingsHintsShowQuicksettings").info("setting entries that also appear in txt2img/img2img interfaces").needs_restart(), "extra_options_txt2img": shared.OptionInfo([], "Options in main UI - txt2img", ui_components.DropdownMulti, lambda: {"choices": list(shared.opts.data_labels.keys())}).js("info", "settingsHintsShowQuicksettings").info("setting entries that also appear in txt2img interfaces").needs_reload_ui(),
"extra_options_accordion": shared.OptionInfo(False, "Place options in main UI into an accordion") "extra_options_img2img": shared.OptionInfo([], "Options in main UI - img2img", ui_components.DropdownMulti, lambda: {"choices": list(shared.opts.data_labels.keys())}).js("info", "settingsHintsShowQuicksettings").info("setting entries that also appear in img2img interfaces").needs_reload_ui(),
"extra_options_cols": shared.OptionInfo(1, "Options in main UI - number of columns", gr.Number, {"precision": 0}).needs_reload_ui(),
"extra_options_accordion": shared.OptionInfo(False, "Options in main UI - place into an accordion").needs_reload_ui()
})) }))
+345
View File
@@ -0,0 +1,345 @@
"""
Hypertile module for splitting attention layers in SD-1.5 U-Net and SD-1.5 VAE
Warn: The patch works well only if the input image has a width and height that are multiples of 128
Original author: @tfernd Github: https://github.com/tfernd/HyperTile
"""
from __future__ import annotations
import functools
from dataclasses import dataclass
from typing import Callable
from functools import wraps, cache
import math
import torch.nn as nn
import random
from einops import rearrange
@dataclass
class HypertileParams:
depth = 0
layer_name = ""
tile_size: int = 0
swap_size: int = 0
aspect_ratio: float = 1.0
forward = None
enabled = False
# TODO add SD-XL layers
DEPTH_LAYERS = {
0: [
# SD 1.5 U-Net (diffusers)
"down_blocks.0.attentions.0.transformer_blocks.0.attn1",
"down_blocks.0.attentions.1.transformer_blocks.0.attn1",
"up_blocks.3.attentions.0.transformer_blocks.0.attn1",
"up_blocks.3.attentions.1.transformer_blocks.0.attn1",
"up_blocks.3.attentions.2.transformer_blocks.0.attn1",
# SD 1.5 U-Net (ldm)
"input_blocks.1.1.transformer_blocks.0.attn1",
"input_blocks.2.1.transformer_blocks.0.attn1",
"output_blocks.9.1.transformer_blocks.0.attn1",
"output_blocks.10.1.transformer_blocks.0.attn1",
"output_blocks.11.1.transformer_blocks.0.attn1",
# SD 1.5 VAE
"decoder.mid_block.attentions.0",
"decoder.mid.attn_1",
],
1: [
# SD 1.5 U-Net (diffusers)
"down_blocks.1.attentions.0.transformer_blocks.0.attn1",
"down_blocks.1.attentions.1.transformer_blocks.0.attn1",
"up_blocks.2.attentions.0.transformer_blocks.0.attn1",
"up_blocks.2.attentions.1.transformer_blocks.0.attn1",
"up_blocks.2.attentions.2.transformer_blocks.0.attn1",
# SD 1.5 U-Net (ldm)
"input_blocks.4.1.transformer_blocks.0.attn1",
"input_blocks.5.1.transformer_blocks.0.attn1",
"output_blocks.6.1.transformer_blocks.0.attn1",
"output_blocks.7.1.transformer_blocks.0.attn1",
"output_blocks.8.1.transformer_blocks.0.attn1",
],
2: [
# SD 1.5 U-Net (diffusers)
"down_blocks.2.attentions.0.transformer_blocks.0.attn1",
"down_blocks.2.attentions.1.transformer_blocks.0.attn1",
"up_blocks.1.attentions.0.transformer_blocks.0.attn1",
"up_blocks.1.attentions.1.transformer_blocks.0.attn1",
"up_blocks.1.attentions.2.transformer_blocks.0.attn1",
# SD 1.5 U-Net (ldm)
"input_blocks.7.1.transformer_blocks.0.attn1",
"input_blocks.8.1.transformer_blocks.0.attn1",
"output_blocks.3.1.transformer_blocks.0.attn1",
"output_blocks.4.1.transformer_blocks.0.attn1",
"output_blocks.5.1.transformer_blocks.0.attn1",
],
3: [
# SD 1.5 U-Net (diffusers)
"mid_block.attentions.0.transformer_blocks.0.attn1",
# SD 1.5 U-Net (ldm)
"middle_block.1.transformer_blocks.0.attn1",
],
}
# XL layers, thanks for GitHub@gel-crabs for the help
DEPTH_LAYERS_XL = {
0: [
# SD 1.5 U-Net (diffusers)
"down_blocks.0.attentions.0.transformer_blocks.0.attn1",
"down_blocks.0.attentions.1.transformer_blocks.0.attn1",
"up_blocks.3.attentions.0.transformer_blocks.0.attn1",
"up_blocks.3.attentions.1.transformer_blocks.0.attn1",
"up_blocks.3.attentions.2.transformer_blocks.0.attn1",
# SD 1.5 U-Net (ldm)
"input_blocks.4.1.transformer_blocks.0.attn1",
"input_blocks.5.1.transformer_blocks.0.attn1",
"output_blocks.3.1.transformer_blocks.0.attn1",
"output_blocks.4.1.transformer_blocks.0.attn1",
"output_blocks.5.1.transformer_blocks.0.attn1",
# SD 1.5 VAE
"decoder.mid_block.attentions.0",
"decoder.mid.attn_1",
],
1: [
# SD 1.5 U-Net (diffusers)
#"down_blocks.1.attentions.0.transformer_blocks.0.attn1",
#"down_blocks.1.attentions.1.transformer_blocks.0.attn1",
#"up_blocks.2.attentions.0.transformer_blocks.0.attn1",
#"up_blocks.2.attentions.1.transformer_blocks.0.attn1",
#"up_blocks.2.attentions.2.transformer_blocks.0.attn1",
# SD 1.5 U-Net (ldm)
"input_blocks.4.1.transformer_blocks.1.attn1",
"input_blocks.5.1.transformer_blocks.1.attn1",
"output_blocks.3.1.transformer_blocks.1.attn1",
"output_blocks.4.1.transformer_blocks.1.attn1",
"output_blocks.5.1.transformer_blocks.1.attn1",
"input_blocks.7.1.transformer_blocks.0.attn1",
"input_blocks.8.1.transformer_blocks.0.attn1",
"output_blocks.0.1.transformer_blocks.0.attn1",
"output_blocks.1.1.transformer_blocks.0.attn1",
"output_blocks.2.1.transformer_blocks.0.attn1",
"input_blocks.7.1.transformer_blocks.1.attn1",
"input_blocks.8.1.transformer_blocks.1.attn1",
"output_blocks.0.1.transformer_blocks.1.attn1",
"output_blocks.1.1.transformer_blocks.1.attn1",
"output_blocks.2.1.transformer_blocks.1.attn1",
"input_blocks.7.1.transformer_blocks.2.attn1",
"input_blocks.8.1.transformer_blocks.2.attn1",
"output_blocks.0.1.transformer_blocks.2.attn1",
"output_blocks.1.1.transformer_blocks.2.attn1",
"output_blocks.2.1.transformer_blocks.2.attn1",
"input_blocks.7.1.transformer_blocks.3.attn1",
"input_blocks.8.1.transformer_blocks.3.attn1",
"output_blocks.0.1.transformer_blocks.3.attn1",
"output_blocks.1.1.transformer_blocks.3.attn1",
"output_blocks.2.1.transformer_blocks.3.attn1",
"input_blocks.7.1.transformer_blocks.4.attn1",
"input_blocks.8.1.transformer_blocks.4.attn1",
"output_blocks.0.1.transformer_blocks.4.attn1",
"output_blocks.1.1.transformer_blocks.4.attn1",
"output_blocks.2.1.transformer_blocks.4.attn1",
"input_blocks.7.1.transformer_blocks.5.attn1",
"input_blocks.8.1.transformer_blocks.5.attn1",
"output_blocks.0.1.transformer_blocks.5.attn1",
"output_blocks.1.1.transformer_blocks.5.attn1",
"output_blocks.2.1.transformer_blocks.5.attn1",
"input_blocks.7.1.transformer_blocks.6.attn1",
"input_blocks.8.1.transformer_blocks.6.attn1",
"output_blocks.0.1.transformer_blocks.6.attn1",
"output_blocks.1.1.transformer_blocks.6.attn1",
"output_blocks.2.1.transformer_blocks.6.attn1",
"input_blocks.7.1.transformer_blocks.7.attn1",
"input_blocks.8.1.transformer_blocks.7.attn1",
"output_blocks.0.1.transformer_blocks.7.attn1",
"output_blocks.1.1.transformer_blocks.7.attn1",
"output_blocks.2.1.transformer_blocks.7.attn1",
"input_blocks.7.1.transformer_blocks.8.attn1",
"input_blocks.8.1.transformer_blocks.8.attn1",
"output_blocks.0.1.transformer_blocks.8.attn1",
"output_blocks.1.1.transformer_blocks.8.attn1",
"output_blocks.2.1.transformer_blocks.8.attn1",
"input_blocks.7.1.transformer_blocks.9.attn1",
"input_blocks.8.1.transformer_blocks.9.attn1",
"output_blocks.0.1.transformer_blocks.9.attn1",
"output_blocks.1.1.transformer_blocks.9.attn1",
"output_blocks.2.1.transformer_blocks.9.attn1",
],
2: [
# SD 1.5 U-Net (diffusers)
"mid_block.attentions.0.transformer_blocks.0.attn1",
# SD 1.5 U-Net (ldm)
"middle_block.1.transformer_blocks.0.attn1",
"middle_block.1.transformer_blocks.1.attn1",
"middle_block.1.transformer_blocks.2.attn1",
"middle_block.1.transformer_blocks.3.attn1",
"middle_block.1.transformer_blocks.4.attn1",
"middle_block.1.transformer_blocks.5.attn1",
"middle_block.1.transformer_blocks.6.attn1",
"middle_block.1.transformer_blocks.7.attn1",
"middle_block.1.transformer_blocks.8.attn1",
"middle_block.1.transformer_blocks.9.attn1",
],
3 : [] # TODO - separate layers for SD-XL
}
RNG_INSTANCE = random.Random()
def random_divisor(value: int, min_value: int, /, max_options: int = 1) -> int:
"""
Returns a random divisor of value that
x * min_value <= value
if max_options is 1, the behavior is deterministic
"""
min_value = min(min_value, value)
# All big divisors of value (inclusive)
divisors = [i for i in range(min_value, value + 1) if value % i == 0] # divisors in small -> big order
ns = [value // i for i in divisors[:max_options]] # has at least 1 element # big -> small order
idx = RNG_INSTANCE.randint(0, len(ns) - 1)
return ns[idx]
def set_hypertile_seed(seed: int) -> None:
RNG_INSTANCE.seed(seed)
@functools.cache
def largest_tile_size_available(width: int, height: int) -> int:
"""
Calculates the largest tile size available for a given width and height
Tile size is always a power of 2
"""
gcd = math.gcd(width, height)
largest_tile_size_available = 1
while gcd % (largest_tile_size_available * 2) == 0:
largest_tile_size_available *= 2
return largest_tile_size_available
def iterative_closest_divisors(hw:int, aspect_ratio:float) -> tuple[int, int]:
"""
Finds h and w such that h*w = hw and h/w = aspect_ratio
We check all possible divisors of hw and return the closest to the aspect ratio
"""
divisors = [i for i in range(2, hw + 1) if hw % i == 0] # all divisors of hw
pairs = [(i, hw // i) for i in divisors] # all pairs of divisors of hw
ratios = [w/h for h, w in pairs] # all ratios of pairs of divisors of hw
closest_ratio = min(ratios, key=lambda x: abs(x - aspect_ratio)) # closest ratio to aspect_ratio
closest_pair = pairs[ratios.index(closest_ratio)] # closest pair of divisors to aspect_ratio
return closest_pair
@cache
def find_hw_candidates(hw:int, aspect_ratio:float) -> tuple[int, int]:
"""
Finds h and w such that h*w = hw and h/w = aspect_ratio
"""
h, w = round(math.sqrt(hw * aspect_ratio)), round(math.sqrt(hw / aspect_ratio))
# find h and w such that h*w = hw and h/w = aspect_ratio
if h * w != hw:
w_candidate = hw / h
# check if w is an integer
if not w_candidate.is_integer():
h_candidate = hw / w
# check if h is an integer
if not h_candidate.is_integer():
return iterative_closest_divisors(hw, aspect_ratio)
else:
h = int(h_candidate)
else:
w = int(w_candidate)
return h, w
def self_attn_forward(params: HypertileParams, scale_depth=True) -> Callable:
@wraps(params.forward)
def wrapper(*args, **kwargs):
if not params.enabled:
return params.forward(*args, **kwargs)
latent_tile_size = max(128, params.tile_size) // 8
x = args[0]
# VAE
if x.ndim == 4:
b, c, h, w = x.shape
nh = random_divisor(h, latent_tile_size, params.swap_size)
nw = random_divisor(w, latent_tile_size, params.swap_size)
if nh * nw > 1:
x = rearrange(x, "b c (nh h) (nw w) -> (b nh nw) c h w", nh=nh, nw=nw) # split into nh * nw tiles
out = params.forward(x, *args[1:], **kwargs)
if nh * nw > 1:
out = rearrange(out, "(b nh nw) c h w -> b c (nh h) (nw w)", nh=nh, nw=nw)
# U-Net
else:
hw: int = x.size(1)
h, w = find_hw_candidates(hw, params.aspect_ratio)
assert h * w == hw, f"Invalid aspect ratio {params.aspect_ratio} for input of shape {x.shape}, hw={hw}, h={h}, w={w}"
factor = 2 ** params.depth if scale_depth else 1
nh = random_divisor(h, latent_tile_size * factor, params.swap_size)
nw = random_divisor(w, latent_tile_size * factor, params.swap_size)
if nh * nw > 1:
x = rearrange(x, "b (nh h nw w) c -> (b nh nw) (h w) c", h=h // nh, w=w // nw, nh=nh, nw=nw)
out = params.forward(x, *args[1:], **kwargs)
if nh * nw > 1:
out = rearrange(out, "(b nh nw) hw c -> b nh nw hw c", nh=nh, nw=nw)
out = rearrange(out, "b nh nw (h w) c -> b (nh h nw w) c", h=h // nh, w=w // nw)
return out
return wrapper
def hypertile_hook_model(model: nn.Module, width, height, *, enable=False, tile_size_max=128, swap_size=1, max_depth=3, is_sdxl=False):
hypertile_layers = getattr(model, "__webui_hypertile_layers", None)
if hypertile_layers is None:
if not enable:
return
hypertile_layers = {}
layers = DEPTH_LAYERS_XL if is_sdxl else DEPTH_LAYERS
for depth in range(4):
for layer_name, module in model.named_modules():
if any(layer_name.endswith(try_name) for try_name in layers[depth]):
params = HypertileParams()
module.__webui_hypertile_params = params
params.forward = module.forward
params.depth = depth
params.layer_name = layer_name
module.forward = self_attn_forward(params)
hypertile_layers[layer_name] = 1
model.__webui_hypertile_layers = hypertile_layers
aspect_ratio = width / height
tile_size = min(largest_tile_size_available(width, height), tile_size_max)
for layer_name, module in model.named_modules():
if layer_name in hypertile_layers:
params = module.__webui_hypertile_params
params.tile_size = tile_size
params.swap_size = swap_size
params.aspect_ratio = aspect_ratio
params.enabled = enable and params.depth <= max_depth
@@ -0,0 +1,73 @@
import hypertile
from modules import scripts, script_callbacks, shared
class ScriptHypertile(scripts.Script):
name = "Hypertile"
def title(self):
return self.name
def show(self, is_img2img):
return scripts.AlwaysVisible
def process(self, p, *args):
hypertile.set_hypertile_seed(p.all_seeds[0])
configure_hypertile(p.width, p.height, enable_unet=shared.opts.hypertile_enable_unet)
def before_hr(self, p, *args):
configure_hypertile(p.hr_upscale_to_x, p.hr_upscale_to_y, enable_unet=shared.opts.hypertile_enable_unet_secondpass or shared.opts.hypertile_enable_unet)
def configure_hypertile(width, height, enable_unet=True):
hypertile.hypertile_hook_model(
shared.sd_model.first_stage_model,
width,
height,
swap_size=shared.opts.hypertile_swap_size_vae,
max_depth=shared.opts.hypertile_max_depth_vae,
tile_size_max=shared.opts.hypertile_max_tile_vae,
enable=shared.opts.hypertile_enable_vae,
)
hypertile.hypertile_hook_model(
shared.sd_model.model,
width,
height,
swap_size=shared.opts.hypertile_swap_size_unet,
max_depth=shared.opts.hypertile_max_depth_unet,
tile_size_max=shared.opts.hypertile_max_tile_unet,
enable=enable_unet,
is_sdxl=shared.sd_model.is_sdxl
)
def on_ui_settings():
import gradio as gr
options = {
"hypertile_explanation": shared.OptionHTML("""
<a href='https://github.com/tfernd/HyperTile'>Hypertile</a> optimizes the self-attention layer within U-Net and VAE models,
resulting in a reduction in computation time ranging from 1 to 4 times. The larger the generated image is, the greater the
benefit.
"""),
"hypertile_enable_unet": shared.OptionInfo(False, "Enable Hypertile U-Net").info("noticeable change in details of the generated picture; if enabled, overrides the setting below"),
"hypertile_enable_unet_secondpass": shared.OptionInfo(False, "Enable Hypertile U-Net for hires fix second pass"),
"hypertile_max_depth_unet": shared.OptionInfo(3, "Hypertile U-Net max depth", gr.Slider, {"minimum": 0, "maximum": 3, "step": 1}),
"hypertile_max_tile_unet": shared.OptionInfo(256, "Hypertile U-net max tile size", gr.Slider, {"minimum": 0, "maximum": 512, "step": 16}),
"hypertile_swap_size_unet": shared.OptionInfo(3, "Hypertile U-net swap size", gr.Slider, {"minimum": 0, "maximum": 6, "step": 1}),
"hypertile_enable_vae": shared.OptionInfo(False, "Enable Hypertile VAE").info("minimal change in the generated picture"),
"hypertile_max_depth_vae": shared.OptionInfo(3, "Hypertile VAE max depth", gr.Slider, {"minimum": 0, "maximum": 3, "step": 1}),
"hypertile_max_tile_vae": shared.OptionInfo(128, "Hypertile VAE max tile size", gr.Slider, {"minimum": 0, "maximum": 512, "step": 16}),
"hypertile_swap_size_vae": shared.OptionInfo(3, "Hypertile VAE swap size ", gr.Slider, {"minimum": 0, "maximum": 6, "step": 1}),
}
for name, opt in options.items():
opt.section = ('hypertile', "Hypertile")
shared.opts.add_option(name, opt)
script_callbacks.on_ui_settings(on_ui_settings)
@@ -0,0 +1,34 @@
var isSetupForMobile = false;
function isMobile() {
for (var tab of ["txt2img", "img2img"]) {
var imageTab = gradioApp().getElementById(tab + '_results');
if (imageTab && imageTab.offsetParent && imageTab.offsetLeft == 0) {
return true;
}
}
return false;
}
function reportWindowSize() {
if (gradioApp().querySelector('.toprow-compact-tools')) return; // not applicable for compact prompt layout
var currentlyMobile = isMobile();
if (currentlyMobile == isSetupForMobile) return;
isSetupForMobile = currentlyMobile;
for (var tab of ["txt2img", "img2img"]) {
var button = gradioApp().getElementById(tab + '_generate_box');
var target = gradioApp().getElementById(currentlyMobile ? tab + '_results' : tab + '_actions_column');
target.insertBefore(button, target.firstElementChild);
gradioApp().getElementById(tab + '_results').classList.toggle('mobile', currentlyMobile);
}
}
window.addEventListener("resize", reportWindowSize);
onUiLoaded(function() {
reportWindowSize();
});
+4 -4
View File
@@ -1,11 +1,11 @@
<div class='card' style={style} onclick={card_clicked} {sort_keys}> <div class='card' style={style} onclick={card_clicked} data-name="{name}" {sort_keys}>
{background_image} {background_image}
<div class="button-row">
{metadata_button} {metadata_button}
{edit_button}
</div>
<div class='actions'> <div class='actions'>
<div class='additional'> <div class='additional'>
<ul>
<a href="#" title="replace preview image with currently selected in gallery" onclick={save_card_preview}>replace preview</a>
</ul>
<span style="display:none" class='search_term{search_only}'>{search_term}</span> <span style="display:none" class='search_term{search_only}'>{search_term}</span>
</div> </div>
<span class='name'>{name}</span> <span class='name'>{name}</span>
-7
View File
@@ -1,7 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
<filter id='shadow' color-interpolation-filters="sRGB">
<feDropShadow flood-color="black" dx="0" dy="0" flood-opacity="0.9" stdDeviation="0.5"/>
<feDropShadow flood-color="black" dx="0" dy="0" flood-opacity="0.9" stdDeviation="0.5"/>
</filter>
<path style="filter:url(#shadow);" fill="#FFFFFF" d="M13.18 19C13.35 19.72 13.64 20.39 14.03 21H5C3.9 21 3 20.11 3 19V5C3 3.9 3.9 3 5 3H19C20.11 3 21 3.9 21 5V11.18C20.5 11.07 20 11 19.5 11C19.33 11 19.17 11 19 11.03V5H5V19H13.18M11.21 15.83L9.25 13.47L6.5 17H13.03C13.14 15.54 13.73 14.22 14.64 13.19L13.96 12.29L11.21 15.83M19 13.5V12L16.75 14.25L19 16.5V15C20.38 15 21.5 16.12 21.5 17.5C21.5 17.9 21.41 18.28 21.24 18.62L22.33 19.71C22.75 19.08 23 18.32 23 17.5C23 15.29 21.21 13.5 19 13.5M19 20C17.62 20 16.5 18.88 16.5 17.5C16.5 17.1 16.59 16.72 16.76 16.38L15.67 15.29C15.25 15.92 15 16.68 15 17.5C15 19.71 16.79 21.5 19 21.5V23L21.25 20.75L19 18.5V20Z" />
</svg>

Before

Width:  |  Height:  |  Size: 989 B

+1 -1
View File
@@ -119,7 +119,7 @@ window.addEventListener('paste', e => {
} }
const firstFreeImageField = visibleImageFields const firstFreeImageField = visibleImageFields
.filter(el => el.querySelector('input[type=file]'))?.[0]; .filter(el => !el.querySelector('img'))?.[0];
dropReplaceImage( dropReplaceImage(
firstFreeImageField ? firstFreeImageField ?
+52 -24
View File
@@ -18,37 +18,43 @@ function keyupEditAttention(event) {
const before = text.substring(0, selectionStart); const before = text.substring(0, selectionStart);
let beforeParen = before.lastIndexOf(OPEN); let beforeParen = before.lastIndexOf(OPEN);
if (beforeParen == -1) return false; if (beforeParen == -1) return false;
let beforeParenClose = before.lastIndexOf(CLOSE);
while (beforeParenClose !== -1 && beforeParenClose > beforeParen) { let beforeClosingParen = before.lastIndexOf(CLOSE);
beforeParen = before.lastIndexOf(OPEN, beforeParen - 1); if (beforeClosingParen != -1 && beforeClosingParen > beforeParen) return false;
beforeParenClose = before.lastIndexOf(CLOSE, beforeParenClose - 1);
}
// Find closing parenthesis around current cursor // Find closing parenthesis around current cursor
const after = text.substring(selectionStart); const after = text.substring(selectionStart);
let afterParen = after.indexOf(CLOSE); let afterParen = after.indexOf(CLOSE);
if (afterParen == -1) return false; if (afterParen == -1) return false;
let afterParenOpen = after.indexOf(OPEN);
while (afterParenOpen !== -1 && afterParen > afterParenOpen) { let afterOpeningParen = after.indexOf(OPEN);
afterParen = after.indexOf(CLOSE, afterParen + 1); if (afterOpeningParen != -1 && afterOpeningParen < afterParen) return false;
afterParenOpen = after.indexOf(OPEN, afterParenOpen + 1);
}
if (beforeParen === -1 || afterParen === -1) return false;
// Set the selection to the text between the parenthesis // Set the selection to the text between the parenthesis
const parenContent = text.substring(beforeParen + 1, selectionStart + afterParen); const parenContent = text.substring(beforeParen + 1, selectionStart + afterParen);
if (/.*:-?[\d.]+/s.test(parenContent)) {
const lastColon = parenContent.lastIndexOf(":"); const lastColon = parenContent.lastIndexOf(":");
selectionStart = beforeParen + 1; selectionStart = beforeParen + 1;
selectionEnd = selectionStart + lastColon; selectionEnd = selectionStart + lastColon;
} else {
selectionStart = beforeParen + 1;
selectionEnd = selectionStart + parenContent.length;
}
target.setSelectionRange(selectionStart, selectionEnd); target.setSelectionRange(selectionStart, selectionEnd);
return true; return true;
} }
function selectCurrentWord() { function selectCurrentWord() {
if (selectionStart !== selectionEnd) return false; if (selectionStart !== selectionEnd) return false;
const delimiters = opts.keyedit_delimiters + " \r\n\t"; const whitespace_delimiters = {"Tab": "\t", "Carriage Return": "\r", "Line Feed": "\n"};
let delimiters = opts.keyedit_delimiters;
// seek backward until to find beggining for (let i of opts.keyedit_delimiters_whitespace) {
delimiters += whitespace_delimiters[i];
}
// seek backward to find beginning
while (!delimiters.includes(text[selectionStart - 1]) && selectionStart > 0) { while (!delimiters.includes(text[selectionStart - 1]) && selectionStart > 0) {
selectionStart--; selectionStart--;
} }
@@ -63,7 +69,7 @@ function keyupEditAttention(event) {
} }
// If the user hasn't selected anything, let's select their current parenthesis block or word // If the user hasn't selected anything, let's select their current parenthesis block or word
if (!selectCurrentParenthesisBlock('<', '>') && !selectCurrentParenthesisBlock('(', ')')) { if (!selectCurrentParenthesisBlock('<', '>') && !selectCurrentParenthesisBlock('(', ')') && !selectCurrentParenthesisBlock('[', ']')) {
selectCurrentWord(); selectCurrentWord();
} }
@@ -71,40 +77,62 @@ function keyupEditAttention(event) {
var closeCharacter = ')'; var closeCharacter = ')';
var delta = opts.keyedit_precision_attention; var delta = opts.keyedit_precision_attention;
var start = selectionStart > 0 ? text[selectionStart - 1] : "";
var end = text[selectionEnd];
if (selectionStart > 0 && text[selectionStart - 1] == '<') { if (start == '<') {
closeCharacter = '>'; closeCharacter = '>';
delta = opts.keyedit_precision_extra; delta = opts.keyedit_precision_extra;
} else if (selectionStart == 0 || text[selectionStart - 1] != "(") { } else if (start == '(' && end == ')' || start == '[' && end == ']') { // convert old-style (((emphasis)))
let numParen = 0;
while (text[selectionStart - numParen - 1] == start && text[selectionEnd + numParen] == end) {
numParen++;
}
if (start == "[") {
weight = (1 / 1.1) ** numParen;
} else {
weight = 1.1 ** numParen;
}
weight = Math.round(weight / opts.keyedit_precision_attention) * opts.keyedit_precision_attention;
text = text.slice(0, selectionStart - numParen) + "(" + text.slice(selectionStart, selectionEnd) + ":" + weight + ")" + text.slice(selectionEnd + numParen);
selectionStart -= numParen - 1;
selectionEnd -= numParen - 1;
} else if (start != '(') {
// do not include spaces at the end // do not include spaces at the end
while (selectionEnd > selectionStart && text[selectionEnd - 1] == ' ') { while (selectionEnd > selectionStart && text[selectionEnd - 1] == ' ') {
selectionEnd -= 1; selectionEnd--;
} }
if (selectionStart == selectionEnd) { if (selectionStart == selectionEnd) {
return; return;
} }
text = text.slice(0, selectionStart) + "(" + text.slice(selectionStart, selectionEnd) + ":1.0)" + text.slice(selectionEnd); text = text.slice(0, selectionStart) + "(" + text.slice(selectionStart, selectionEnd) + ":1.0)" + text.slice(selectionEnd);
selectionStart += 1; selectionStart++;
selectionEnd += 1; selectionEnd++;
} }
var end = text.slice(selectionEnd + 1).indexOf(closeCharacter) + 1; if (text[selectionEnd] != ':') return;
var weight = parseFloat(text.slice(selectionEnd + 1, selectionEnd + 1 + end)); var weightLength = text.slice(selectionEnd + 1).indexOf(closeCharacter) + 1;
var weight = parseFloat(text.slice(selectionEnd + 1, selectionEnd + weightLength));
if (isNaN(weight)) return; if (isNaN(weight)) return;
weight += isPlus ? delta : -delta; weight += isPlus ? delta : -delta;
weight = parseFloat(weight.toPrecision(12)); weight = parseFloat(weight.toPrecision(12));
if (String(weight).length == 1) weight += ".0"; if (Number.isInteger(weight)) weight += ".0";
if (closeCharacter == ')' && weight == 1) { if (closeCharacter == ')' && weight == 1) {
text = text.slice(0, selectionStart - 1) + text.slice(selectionStart, selectionEnd) + text.slice(selectionEnd + 5); var endParenPos = text.substring(selectionEnd).indexOf(')');
text = text.slice(0, selectionStart - 1) + text.slice(selectionStart, selectionEnd) + text.slice(selectionEnd + endParenPos + 1);
selectionStart--; selectionStart--;
selectionEnd--; selectionEnd--;
} else { } else {
text = text.slice(0, selectionEnd + 1) + weight + text.slice(selectionEnd + 1 + end - 1); text = text.slice(0, selectionEnd + 1) + weight + text.slice(selectionEnd + weightLength);
} }
target.focus(); target.focus();
+41
View File
@@ -0,0 +1,41 @@
/* alt+left/right moves text in prompt */
function keyupEditOrder(event) {
if (!opts.keyedit_move) return;
let target = event.originalTarget || event.composedPath()[0];
if (!target.matches("*:is([id*='_toprow'] [id*='_prompt'], .prompt) textarea")) return;
if (!event.altKey) return;
let isLeft = event.key == "ArrowLeft";
let isRight = event.key == "ArrowRight";
if (!isLeft && !isRight) return;
event.preventDefault();
let selectionStart = target.selectionStart;
let selectionEnd = target.selectionEnd;
let text = target.value;
let items = text.split(",");
let indexStart = (text.slice(0, selectionStart).match(/,/g) || []).length;
let indexEnd = (text.slice(0, selectionEnd).match(/,/g) || []).length;
let range = indexEnd - indexStart + 1;
if (isLeft && indexStart > 0) {
items.splice(indexStart - 1, 0, ...items.splice(indexStart, range));
target.value = items.join();
target.selectionStart = items.slice(0, indexStart - 1).join().length + (indexStart == 1 ? 0 : 1);
target.selectionEnd = items.slice(0, indexEnd).join().length;
} else if (isRight && indexEnd < items.length - 1) {
items.splice(indexStart + 1, 0, ...items.splice(indexStart, range));
target.value = items.join();
target.selectionStart = items.slice(0, indexStart + 1).join().length + 1;
target.selectionEnd = items.slice(0, indexEnd + 2).join().length;
}
event.preventDefault();
updateInput(target);
}
addEventListener('keydown', (event) => {
keyupEditOrder(event);
});
+19 -1
View File
@@ -33,7 +33,7 @@ function extensions_check() {
var id = randomId(); var id = randomId();
requestProgress(id, gradioApp().getElementById('extensions_installed_top'), null, function() { requestProgress(id, gradioApp().getElementById('extensions_installed_html'), null, function() {
}); });
@@ -72,3 +72,21 @@ function config_state_confirm_restore(_, config_state_name, config_restore_type)
} }
return [confirmed, config_state_name, config_restore_type]; return [confirmed, config_state_name, config_restore_type];
} }
function toggle_all_extensions(event) {
gradioApp().querySelectorAll('#extensions .extension_toggle').forEach(function(checkbox_el) {
checkbox_el.checked = event.target.checked;
});
}
function toggle_extension() {
let all_extensions_toggled = true;
for (const checkbox_el of gradioApp().querySelectorAll('#extensions .extension_toggle')) {
if (!checkbox_el.checked) {
all_extensions_toggled = false;
break;
}
}
gradioApp().querySelector('#extensions .all_extensions_toggle').checked = all_extensions_toggled;
}
+159 -30
View File
@@ -1,20 +1,39 @@
function toggleCss(key, css, enable) {
var style = document.getElementById(key);
if (enable && !style) {
style = document.createElement('style');
style.id = key;
style.type = 'text/css';
document.head.appendChild(style);
}
if (style && !enable) {
document.head.removeChild(style);
}
if (style) {
style.innerHTML == '';
style.appendChild(document.createTextNode(css));
}
}
function setupExtraNetworksForTab(tabname) { function setupExtraNetworksForTab(tabname) {
gradioApp().querySelector('#' + tabname + '_extra_tabs').classList.add('extra-networks'); gradioApp().querySelector('#' + tabname + '_extra_tabs').classList.add('extra-networks');
var tabs = gradioApp().querySelector('#' + tabname + '_extra_tabs > div'); var tabs = gradioApp().querySelector('#' + tabname + '_extra_tabs > div');
var search = gradioApp().querySelector('#' + tabname + '_extra_search textarea'); var searchDiv = gradioApp().getElementById(tabname + '_extra_search');
var search = searchDiv.querySelector('textarea');
var sort = gradioApp().getElementById(tabname + '_extra_sort'); var sort = gradioApp().getElementById(tabname + '_extra_sort');
var sortOrder = gradioApp().getElementById(tabname + '_extra_sortorder'); var sortOrder = gradioApp().getElementById(tabname + '_extra_sortorder');
var refresh = gradioApp().getElementById(tabname + '_extra_refresh'); var refresh = gradioApp().getElementById(tabname + '_extra_refresh');
var showDirsDiv = gradioApp().getElementById(tabname + '_extra_show_dirs');
var showDirs = gradioApp().querySelector('#' + tabname + '_extra_show_dirs input');
var promptContainer = gradioApp().querySelector('.prompt-container-compact#' + tabname + '_prompt_container');
var negativePrompt = gradioApp().querySelector('#' + tabname + '_neg_prompt');
search.classList.add('search'); tabs.appendChild(searchDiv);
sort.classList.add('sort');
sortOrder.classList.add('sortorder');
sort.dataset.sortkey = 'sortDefault';
tabs.appendChild(search);
tabs.appendChild(sort); tabs.appendChild(sort);
tabs.appendChild(sortOrder); tabs.appendChild(sortOrder);
tabs.appendChild(refresh); tabs.appendChild(refresh);
tabs.appendChild(showDirsDiv);
var applyFilter = function() { var applyFilter = function() {
var searchTerm = search.value.toLowerCase(); var searchTerm = search.value.toLowerCase();
@@ -31,20 +50,23 @@ function setupExtraNetworksForTab(tabname) {
elem.style.display = visible ? "" : "none"; elem.style.display = visible ? "" : "none";
}); });
applySort();
}; };
var applySort = function() { var applySort = function() {
var cards = gradioApp().querySelectorAll('#' + tabname + '_extra_tabs div.card');
var reverse = sortOrder.classList.contains("sortReverse"); var reverse = sortOrder.classList.contains("sortReverse");
var sortKey = sort.querySelector("input").value.toLowerCase().replace("sort", "").replaceAll(" ", "_").replace(/_+$/, "").trim(); var sortKey = sort.querySelector("input").value.toLowerCase().replace("sort", "").replaceAll(" ", "_").replace(/_+$/, "").trim() || "name";
sortKey = sortKey ? "sort" + sortKey.charAt(0).toUpperCase() + sortKey.slice(1) : ""; sortKey = "sort" + sortKey.charAt(0).toUpperCase() + sortKey.slice(1);
var sortKeyStore = sortKey ? sortKey + (reverse ? "Reverse" : "") : ""; var sortKeyStore = sortKey + "-" + (reverse ? "Descending" : "Ascending") + "-" + cards.length;
if (!sortKey || sortKeyStore == sort.dataset.sortkey) {
if (sortKeyStore == sort.dataset.sortkey) {
return; return;
} }
sort.dataset.sortkey = sortKeyStore; sort.dataset.sortkey = sortKeyStore;
var cards = gradioApp().querySelectorAll('#' + tabname + '_extra_tabs div.card');
cards.forEach(function(card) { cards.forEach(function(card) {
card.originalParentElement = card.parentElement; card.originalParentElement = card.parentElement;
}); });
@@ -70,23 +92,70 @@ function setupExtraNetworksForTab(tabname) {
}; };
search.addEventListener("input", applyFilter); search.addEventListener("input", applyFilter);
applyFilter();
["change", "blur", "click"].forEach(function(evt) {
sort.querySelector("input").addEventListener(evt, applySort);
});
sortOrder.addEventListener("click", function() { sortOrder.addEventListener("click", function() {
sortOrder.classList.toggle("sortReverse"); sortOrder.classList.toggle("sortReverse");
applySort(); applySort();
}); });
applyFilter();
extraNetworksApplySort[tabname] = applySort;
extraNetworksApplyFilter[tabname] = applyFilter; extraNetworksApplyFilter[tabname] = applyFilter;
var showDirsUpdate = function() {
var css = '#' + tabname + '_extra_tabs .extra-network-subdirs { display: none; }';
toggleCss(tabname + '_extra_show_dirs_style', css, !showDirs.checked);
localSet('extra-networks-show-dirs', showDirs.checked ? 1 : 0);
};
showDirs.checked = localGet('extra-networks-show-dirs', 1) == 1;
showDirs.addEventListener("change", showDirsUpdate);
showDirsUpdate();
}
function extraNetworksMovePromptToTab(tabname, id, showPrompt, showNegativePrompt) {
if (!gradioApp().querySelector('.toprow-compact-tools')) return; // only applicable for compact prompt layout
var promptContainer = gradioApp().getElementById(tabname + '_prompt_container');
var prompt = gradioApp().getElementById(tabname + '_prompt_row');
var negPrompt = gradioApp().getElementById(tabname + '_neg_prompt_row');
var elem = id ? gradioApp().getElementById(id) : null;
if (showNegativePrompt && elem) {
elem.insertBefore(negPrompt, elem.firstChild);
} else {
promptContainer.insertBefore(negPrompt, promptContainer.firstChild);
}
if (showPrompt && elem) {
elem.insertBefore(prompt, elem.firstChild);
} else {
promptContainer.insertBefore(prompt, promptContainer.firstChild);
}
if (elem) {
elem.classList.toggle('extra-page-prompts-active', showNegativePrompt || showPrompt);
}
}
function extraNetworksUrelatedTabSelected(tabname) { // called from python when user selects an unrelated tab (generate)
extraNetworksMovePromptToTab(tabname, '', false, false);
}
function extraNetworksTabSelected(tabname, id, showPrompt, showNegativePrompt) { // called from python when user selects an extra networks tab
extraNetworksMovePromptToTab(tabname, id, showPrompt, showNegativePrompt);
} }
function applyExtraNetworkFilter(tabname) { function applyExtraNetworkFilter(tabname) {
setTimeout(extraNetworksApplyFilter[tabname], 1); setTimeout(extraNetworksApplyFilter[tabname], 1);
} }
function applyExtraNetworkSort(tabname) {
setTimeout(extraNetworksApplySort[tabname], 1);
}
var extraNetworksApplyFilter = {}; var extraNetworksApplyFilter = {};
var extraNetworksApplySort = {};
var activePromptTextarea = {}; var activePromptTextarea = {};
function setupExtraNetworks() { function setupExtraNetworks() {
@@ -113,23 +182,36 @@ function setupExtraNetworks() {
onUiLoaded(setupExtraNetworks); onUiLoaded(setupExtraNetworks);
var re_extranet = /<([^:]+:[^:]+):[\d.]+>/; var re_extranet = /<([^:^>]+:[^:]+):[\d.]+>(.*)/;
var re_extranet_g = /\s+<([^:]+:[^:]+):[\d.]+>/g; var re_extranet_g = /<([^:^>]+:[^:]+):[\d.]+>/g;
function tryToRemoveExtraNetworkFromPrompt(textarea, text) { function tryToRemoveExtraNetworkFromPrompt(textarea, text) {
var m = text.match(re_extranet); var m = text.match(re_extranet);
var replaced = false; var replaced = false;
var newTextareaText; var newTextareaText;
if (m) { if (m) {
var extraTextBeforeNet = opts.extra_networks_add_text_separator;
var extraTextAfterNet = m[2];
var partToSearch = m[1]; var partToSearch = m[1];
newTextareaText = textarea.value.replaceAll(re_extranet_g, function(found) { var foundAtPosition = -1;
newTextareaText = textarea.value.replaceAll(re_extranet_g, function(found, net, pos) {
m = found.match(re_extranet); m = found.match(re_extranet);
if (m[1] == partToSearch) { if (m[1] == partToSearch) {
replaced = true; replaced = true;
foundAtPosition = pos;
return ""; return "";
} }
return found; return found;
}); });
if (foundAtPosition >= 0) {
if (newTextareaText.substr(foundAtPosition, extraTextAfterNet.length) == extraTextAfterNet) {
newTextareaText = newTextareaText.substr(0, foundAtPosition) + newTextareaText.substr(foundAtPosition + extraTextAfterNet.length);
}
if (newTextareaText.substr(foundAtPosition - extraTextBeforeNet.length, extraTextBeforeNet.length) == extraTextBeforeNet) {
newTextareaText = newTextareaText.substr(0, foundAtPosition - extraTextBeforeNet.length) + newTextareaText.substr(foundAtPosition);
}
}
} else { } else {
newTextareaText = textarea.value.replaceAll(new RegExp(text, "g"), function(found) { newTextareaText = textarea.value.replaceAll(new RegExp(text, "g"), function(found) {
if (found == text) { if (found == text) {
@@ -172,7 +254,7 @@ function saveCardPreview(event, tabname, filename) {
} }
function extraNetworksSearchButton(tabs_id, event) { function extraNetworksSearchButton(tabs_id, event) {
var searchTextarea = gradioApp().querySelector("#" + tabs_id + ' > div > textarea'); var searchTextarea = gradioApp().querySelector("#" + tabs_id + ' > label > textarea');
var button = event.target; var button = event.target;
var text = button.classList.contains("search-all") ? "" : button.textContent.trim(); var text = button.classList.contains("search-all") ? "" : button.textContent.trim();
@@ -182,30 +264,28 @@ function extraNetworksSearchButton(tabs_id, event) {
var globalPopup = null; var globalPopup = null;
var globalPopupInner = null; var globalPopupInner = null;
function closePopup() {
if (!globalPopup) return;
globalPopup.style.display = "none";
}
function popup(contents) { function popup(contents) {
if (!globalPopup) { if (!globalPopup) {
globalPopup = document.createElement('div'); globalPopup = document.createElement('div');
globalPopup.onclick = function() {
globalPopup.style.display = "none";
};
globalPopup.classList.add('global-popup'); globalPopup.classList.add('global-popup');
var close = document.createElement('div'); var close = document.createElement('div');
close.classList.add('global-popup-close'); close.classList.add('global-popup-close');
close.onclick = function() { close.addEventListener("click", closePopup);
globalPopup.style.display = "none";
};
close.title = "Close"; close.title = "Close";
globalPopup.appendChild(close); globalPopup.appendChild(close);
globalPopupInner = document.createElement('div'); globalPopupInner = document.createElement('div');
globalPopupInner.onclick = function(event) {
event.stopPropagation(); return false;
};
globalPopupInner.classList.add('global-popup-inner'); globalPopupInner.classList.add('global-popup-inner');
globalPopup.appendChild(globalPopupInner); globalPopup.appendChild(globalPopupInner);
gradioApp().appendChild(globalPopup); gradioApp().querySelector('.main').appendChild(globalPopup);
} }
globalPopupInner.innerHTML = ''; globalPopupInner.innerHTML = '';
@@ -214,6 +294,15 @@ function popup(contents) {
globalPopup.style.display = "flex"; globalPopup.style.display = "flex";
} }
var storedPopupIds = {};
function popupId(id) {
if (!storedPopupIds[id]) {
storedPopupIds[id] = gradioApp().getElementById(id);
}
popup(storedPopupIds[id]);
}
function extraNetworksShowMetadata(text) { function extraNetworksShowMetadata(text) {
var elem = document.createElement('pre'); var elem = document.createElement('pre');
elem.classList.add('popup-metadata'); elem.classList.add('popup-metadata');
@@ -263,3 +352,43 @@ function extraNetworksRequestMetadata(event, extraPage, cardName) {
event.stopPropagation(); event.stopPropagation();
} }
var extraPageUserMetadataEditors = {};
function extraNetworksEditUserMetadata(event, tabname, extraPage, cardName) {
var id = tabname + '_' + extraPage + '_edit_user_metadata';
var editor = extraPageUserMetadataEditors[id];
if (!editor) {
editor = {};
editor.page = gradioApp().getElementById(id);
editor.nameTextarea = gradioApp().querySelector("#" + id + "_name" + ' textarea');
editor.button = gradioApp().querySelector("#" + id + "_button");
extraPageUserMetadataEditors[id] = editor;
}
editor.nameTextarea.value = cardName;
updateInput(editor.nameTextarea);
editor.button.click();
popup(editor.page);
event.stopPropagation();
}
function extraNetworksRefreshSingleCard(page, tabname, name) {
requestGet("./sd_extra_networks/get-single-card", {page: page, tabname: tabname, name: name}, function(data) {
if (data && data.html) {
var card = gradioApp().querySelector(`#${tabname}_${page.replace(" ", "_")}_cards > .card[data-name="${name}"]`);
var newDiv = document.createElement('DIV');
newDiv.innerHTML = data.html;
var newCard = newDiv.firstElementChild;
newCard.style.display = '';
card.parentElement.insertBefore(newCard, card);
card.parentElement.removeChild(card);
}
});
}
+13 -5
View File
@@ -15,7 +15,7 @@ var titles = {
"CFG Scale": "Classifier Free Guidance Scale - how strongly the image should conform to prompt - lower values produce more creative results", "CFG Scale": "Classifier Free Guidance Scale - how strongly the image should conform to prompt - lower values produce more creative results",
"Seed": "A value that determines the output of random number generator - if you create an image with same parameters and seed as another image, you'll get the same result", "Seed": "A value that determines the output of random number generator - if you create an image with same parameters and seed as another image, you'll get the same result",
"\u{1f3b2}\ufe0f": "Set seed to -1, which will cause a new random number to be used every time", "\u{1f3b2}\ufe0f": "Set seed to -1, which will cause a new random number to be used every time",
"\u267b\ufe0f": "Reuse seed from last generation, mostly useful if it was randomed", "\u267b\ufe0f": "Reuse seed from last generation, mostly useful if it was randomized",
"\u2199\ufe0f": "Read generation parameters from prompt or last generation if prompt is empty into user interface.", "\u2199\ufe0f": "Read generation parameters from prompt or last generation if prompt is empty into user interface.",
"\u{1f4c2}": "Open images output directory", "\u{1f4c2}": "Open images output directory",
"\u{1f4be}": "Save style", "\u{1f4be}": "Save style",
@@ -84,8 +84,6 @@ var titles = {
"Checkpoint name": "Loads weights from checkpoint before making images. You can either use hash or a part of filename (as seen in settings) for checkpoint name. Recommended to use with Y axis for less switching.", "Checkpoint name": "Loads weights from checkpoint before making images. You can either use hash or a part of filename (as seen in settings) for checkpoint name. Recommended to use with Y axis for less switching.",
"Inpainting conditioning mask strength": "Only applies to inpainting models. Determines how strongly to mask off the original image for inpainting and img2img. 1.0 means fully masked, which is the default behaviour. 0.0 means a fully unmasked conditioning. Lower values will help preserve the overall composition of the image, but will struggle with large changes.", "Inpainting conditioning mask strength": "Only applies to inpainting models. Determines how strongly to mask off the original image for inpainting and img2img. 1.0 means fully masked, which is the default behaviour. 0.0 means a fully unmasked conditioning. Lower values will help preserve the overall composition of the image, but will struggle with large changes.",
"vram": "Torch active: Peak amount of VRAM used by Torch during generation, excluding cached data.\nTorch reserved: Peak amount of VRAM allocated by Torch, including all active and cached data.\nSys VRAM: Peak amount of VRAM allocation across all applications / total GPU VRAM (peak utilization%).",
"Eta noise seed delta": "If this values is non-zero, it will be added to seed and used to initialize RNG for noises when using samplers with Eta. You can use this to produce even more variation of images, or you can use this to match images of other software if you know what you are doing.", "Eta noise seed delta": "If this values is non-zero, it will be added to seed and used to initialize RNG for noises when using samplers with Eta. You can use this to produce even more variation of images, or you can use this to match images of other software if you know what you are doing.",
"Filename word regex": "This regular expression will be used extract words from filename, and they will be joined using the option below into label text used for training. Leave empty to keep filename text as it is.", "Filename word regex": "This regular expression will be used extract words from filename, and they will be joined using the option below into label text used for training. Leave empty to keep filename text as it is.",
@@ -110,9 +108,8 @@ var titles = {
"Upscale by": "Adjusts the size of the image by multiplying the original width and height by the selected value. Ignored if either Resize width to or Resize height to are non-zero.", "Upscale by": "Adjusts the size of the image by multiplying the original width and height by the selected value. Ignored if either Resize width to or Resize height to are non-zero.",
"Resize width to": "Resizes image to this width. If 0, width is inferred from either of two nearby sliders.", "Resize width to": "Resizes image to this width. If 0, width is inferred from either of two nearby sliders.",
"Resize height to": "Resizes image to this height. If 0, height is inferred from either of two nearby sliders.", "Resize height to": "Resizes image to this height. If 0, height is inferred from either of two nearby sliders.",
"Multiplier for extra networks": "When adding extra network such as Hypernetwork or Lora to prompt, use this multiplier for it.",
"Discard weights with matching name": "Regular expression; if weights's name matches it, the weights is not written to the resulting checkpoint. Use ^model_ema to discard EMA weights.", "Discard weights with matching name": "Regular expression; if weights's name matches it, the weights is not written to the resulting checkpoint. Use ^model_ema to discard EMA weights.",
"Extra networks tab order": "Comma-separated list of tab names; tabs listed here will appear in the extra networks UI first and in order lsited.", "Extra networks tab order": "Comma-separated list of tab names; tabs listed here will appear in the extra networks UI first and in order listed.",
"Negative Guidance minimum sigma": "Skip negative prompt for steps where image is already mostly denoised; the higher this value, the more skips there will be; provides increased performance in exchange for minor quality reduction." "Negative Guidance minimum sigma": "Skip negative prompt for steps where image is already mostly denoised; the higher this value, the more skips there will be; provides increased performance in exchange for minor quality reduction."
}; };
@@ -193,3 +190,14 @@ onUiUpdate(function(mutationRecords) {
tooltipCheckTimer = setTimeout(processTooltipCheckNodes, 1000); tooltipCheckTimer = setTimeout(processTooltipCheckNodes, 1000);
} }
}); });
onUiLoaded(function() {
for (var comp of window.gradio_config.components) {
if (comp.props.webui_tooltip && comp.props.elem_id) {
var elem = gradioApp().getElementById(comp.props.elem_id);
if (elem) {
elem.title = comp.props.webui_tooltip;
}
}
}
});
+10 -2
View File
@@ -33,8 +33,11 @@ function updateOnBackgroundChange() {
const modalImage = gradioApp().getElementById("modalImage"); const modalImage = gradioApp().getElementById("modalImage");
if (modalImage && modalImage.offsetParent) { if (modalImage && modalImage.offsetParent) {
let currentButton = selected_gallery_button(); let currentButton = selected_gallery_button();
let preview = gradioApp().querySelectorAll('.livePreview > img');
if (currentButton?.children?.length > 0 && modalImage.src != currentButton.children[0].src) { if (preview.length > 0) {
// show preview image if available
modalImage.src = preview[preview.length - 1].src;
} else if (currentButton?.children?.length > 0 && modalImage.src != currentButton.children[0].src) {
modalImage.src = currentButton.children[0].src; modalImage.src = currentButton.children[0].src;
if (modalImage.style.display === 'none') { if (modalImage.style.display === 'none') {
const modal = gradioApp().getElementById("lightboxModal"); const modal = gradioApp().getElementById("lightboxModal");
@@ -136,6 +139,11 @@ function setupImageForLightbox(e) {
var event = isFirefox ? 'mousedown' : 'click'; var event = isFirefox ? 'mousedown' : 'click';
e.addEventListener(event, function(evt) { e.addEventListener(event, function(evt) {
if (evt.button == 1) {
open(evt.target.src);
evt.preventDefault();
return;
}
if (!opts.js_modal_lightbox || evt.button != 0) return; if (!opts.js_modal_lightbox || evt.button != 0) return;
modalZoomSet(gradioApp().getElementById('modalImage'), opts.js_modal_lightbox_initially_zoomed); modalZoomSet(gradioApp().getElementById('modalImage'), opts.js_modal_lightbox_initially_zoomed);
+68
View File
@@ -0,0 +1,68 @@
function inputAccordionChecked(id, checked) {
var accordion = gradioApp().getElementById(id);
accordion.visibleCheckbox.checked = checked;
accordion.onVisibleCheckboxChange();
}
function setupAccordion(accordion) {
var labelWrap = accordion.querySelector('.label-wrap');
var gradioCheckbox = gradioApp().querySelector('#' + accordion.id + "-checkbox input");
var extra = gradioApp().querySelector('#' + accordion.id + "-extra");
var span = labelWrap.querySelector('span');
var linked = true;
var isOpen = function() {
return labelWrap.classList.contains('open');
};
var observerAccordionOpen = new MutationObserver(function(mutations) {
mutations.forEach(function(mutationRecord) {
accordion.classList.toggle('input-accordion-open', isOpen());
if (linked) {
accordion.visibleCheckbox.checked = isOpen();
accordion.onVisibleCheckboxChange();
}
});
});
observerAccordionOpen.observe(labelWrap, {attributes: true, attributeFilter: ['class']});
if (extra) {
labelWrap.insertBefore(extra, labelWrap.lastElementChild);
}
accordion.onChecked = function(checked) {
if (isOpen() != checked) {
labelWrap.click();
}
};
var visibleCheckbox = document.createElement('INPUT');
visibleCheckbox.type = 'checkbox';
visibleCheckbox.checked = isOpen();
visibleCheckbox.id = accordion.id + "-visible-checkbox";
visibleCheckbox.className = gradioCheckbox.className + " input-accordion-checkbox";
span.insertBefore(visibleCheckbox, span.firstChild);
accordion.visibleCheckbox = visibleCheckbox;
accordion.onVisibleCheckboxChange = function() {
if (linked && isOpen() != visibleCheckbox.checked) {
labelWrap.click();
}
gradioCheckbox.checked = visibleCheckbox.checked;
updateInput(gradioCheckbox);
};
visibleCheckbox.addEventListener('click', function(event) {
linked = false;
event.stopPropagation();
});
visibleCheckbox.addEventListener('input', accordion.onVisibleCheckboxChange);
}
onUiLoaded(function() {
for (var accordion of gradioApp().querySelectorAll('.input-accordion')) {
setupAccordion(accordion);
}
});
+26
View File
@@ -0,0 +1,26 @@
function localSet(k, v) {
try {
localStorage.setItem(k, v);
} catch (e) {
console.warn(`Failed to save ${k} to localStorage: ${e}`);
}
}
function localGet(k, def) {
try {
return localStorage.getItem(k);
} catch (e) {
console.warn(`Failed to load ${k} from localStorage: ${e}`);
}
return def;
}
function localRemove(k) {
try {
return localStorage.removeItem(k);
} catch (e) {
console.warn(`Failed to remove ${k} from localStorage: ${e}`);
}
}
+36 -7
View File
@@ -11,11 +11,11 @@ var ignore_ids_for_localization = {
train_hypernetwork: 'OPTION', train_hypernetwork: 'OPTION',
txt2img_styles: 'OPTION', txt2img_styles: 'OPTION',
img2img_styles: 'OPTION', img2img_styles: 'OPTION',
setting_random_artist_categories: 'SPAN', setting_random_artist_categories: 'OPTION',
setting_face_restoration_model: 'SPAN', setting_face_restoration_model: 'OPTION',
setting_realesrgan_enabled_models: 'SPAN', setting_realesrgan_enabled_models: 'OPTION',
extras_upscaler_1: 'SPAN', extras_upscaler_1: 'OPTION',
extras_upscaler_2: 'SPAN', extras_upscaler_2: 'OPTION',
}; };
var re_num = /^[.\d]+$/; var re_num = /^[.\d]+$/;
@@ -107,12 +107,41 @@ function processNode(node) {
}); });
} }
function localizeWholePage() {
processNode(gradioApp());
function elem(comp) {
var elem_id = comp.props.elem_id ? comp.props.elem_id : "component-" + comp.id;
return gradioApp().getElementById(elem_id);
}
for (var comp of window.gradio_config.components) {
if (comp.props.webui_tooltip) {
let e = elem(comp);
let tl = e ? getTranslation(e.title) : undefined;
if (tl !== undefined) {
e.title = tl;
}
}
if (comp.props.placeholder) {
let e = elem(comp);
let textbox = e ? e.querySelector('[placeholder]') : null;
let tl = textbox ? getTranslation(textbox.placeholder) : undefined;
if (tl !== undefined) {
textbox.placeholder = tl;
}
}
}
}
function dumpTranslations() { function dumpTranslations() {
if (!hasLocalization()) { if (!hasLocalization()) {
// If we don't have any localization, // If we don't have any localization,
// we will not have traversed the app to find // we will not have traversed the app to find
// original_lines, so do that now. // original_lines, so do that now.
processNode(gradioApp()); localizeWholePage();
} }
var dumped = {}; var dumped = {};
if (localization.rtl) { if (localization.rtl) {
@@ -154,7 +183,7 @@ document.addEventListener("DOMContentLoaded", function() {
}); });
}); });
processNode(gradioApp()); localizeWholePage();
if (localization.rtl) { // if the language is from right to left, if (localization.rtl) { // if the language is from right to left,
(new MutationObserver((mutations, observer) => { // wait for the style to load (new MutationObserver((mutations, observer) => { // wait for the style to load
+6 -2
View File
@@ -15,7 +15,7 @@ onAfterUiUpdate(function() {
} }
} }
const galleryPreviews = gradioApp().querySelectorAll('div[id^="tab_"][style*="display: block"] div[id$="_results"] .thumbnail-item > img'); const galleryPreviews = gradioApp().querySelectorAll('div[id^="tab_"] div[id$="_results"] .thumbnail-item > img');
if (galleryPreviews == null) return; if (galleryPreviews == null) return;
@@ -26,7 +26,11 @@ onAfterUiUpdate(function() {
lastHeadImg = headImg; lastHeadImg = headImg;
// play notification sound if available // play notification sound if available
gradioApp().querySelector('#audio_notification audio')?.play(); const notificationAudio = gradioApp().querySelector('#audio_notification audio');
if (notificationAudio) {
notificationAudio.volume = opts.notification_volume / 100.0 || 1.0;
notificationAudio.play();
}
if (document.hasFocus()) return; if (document.hasFocus()) return;
+38 -29
View File
@@ -69,7 +69,6 @@ function requestProgress(id_task, progressbarContainer, gallery, atEnd, onProgre
var dateStart = new Date(); var dateStart = new Date();
var wasEverActive = false; var wasEverActive = false;
var parentProgressbar = progressbarContainer.parentNode; var parentProgressbar = progressbarContainer.parentNode;
var parentGallery = gallery ? gallery.parentNode : null;
var divProgress = document.createElement('div'); var divProgress = document.createElement('div');
divProgress.className = 'progressDiv'; divProgress.className = 'progressDiv';
@@ -80,32 +79,26 @@ function requestProgress(id_task, progressbarContainer, gallery, atEnd, onProgre
divProgress.appendChild(divInner); divProgress.appendChild(divInner);
parentProgressbar.insertBefore(divProgress, progressbarContainer); parentProgressbar.insertBefore(divProgress, progressbarContainer);
if (parentGallery) { var livePreview = null;
var livePreview = document.createElement('div');
livePreview.className = 'livePreview';
parentGallery.insertBefore(livePreview, gallery);
}
var removeProgressBar = function() { var removeProgressBar = function() {
if (!divProgress) return;
setTitle(""); setTitle("");
parentProgressbar.removeChild(divProgress); parentProgressbar.removeChild(divProgress);
if (parentGallery) parentGallery.removeChild(livePreview); if (gallery && livePreview) gallery.removeChild(livePreview);
atEnd(); atEnd();
divProgress = null;
}; };
var fun = function(id_task, id_live_preview) { var funProgress = function(id_task) {
request("./internal/progress", {id_task: id_task, id_live_preview: id_live_preview}, function(res) { request("./internal/progress", {id_task: id_task, live_preview: false}, function(res) {
if (res.completed) { if (res.completed) {
removeProgressBar(); removeProgressBar();
return; return;
} }
var rect = progressbarContainer.getBoundingClientRect();
if (rect.width) {
divProgress.style.width = rect.width + "px";
}
let progressText = ""; let progressText = "";
divInner.style.width = ((res.progress || 0) * 100.0) + '%'; divInner.style.width = ((res.progress || 0) * 100.0) + '%';
@@ -119,7 +112,6 @@ function requestProgress(id_task, progressbarContainer, gallery, atEnd, onProgre
progressText += " ETA: " + formatTime(res.eta); progressText += " ETA: " + formatTime(res.eta);
} }
setTitle(progressText); setTitle(progressText);
if (res.textinfo && res.textinfo.indexOf("\n") == -1) { if (res.textinfo && res.textinfo.indexOf("\n") == -1) {
@@ -142,16 +134,33 @@ function requestProgress(id_task, progressbarContainer, gallery, atEnd, onProgre
return; return;
} }
if (onProgress) {
if (res.live_preview && gallery) { onProgress(res);
rect = gallery.getBoundingClientRect();
if (rect.width) {
livePreview.style.width = rect.width + "px";
livePreview.style.height = rect.height + "px";
} }
setTimeout(() => {
funProgress(id_task, res.id_live_preview);
}, opts.live_preview_refresh_period || 500);
}, function() {
removeProgressBar();
});
};
var funLivePreview = function(id_task, id_live_preview) {
request("./internal/progress", {id_task: id_task, id_live_preview: id_live_preview}, function(res) {
if (!divProgress) {
return;
}
if (res.live_preview && gallery) {
var img = new Image(); var img = new Image();
img.onload = function() { img.onload = function() {
if (!livePreview) {
livePreview = document.createElement('div');
livePreview.className = 'livePreview';
gallery.insertBefore(livePreview, gallery.firstElementChild);
}
livePreview.appendChild(img); livePreview.appendChild(img);
if (livePreview.childElementCount > 2) { if (livePreview.childElementCount > 2) {
livePreview.removeChild(livePreview.firstElementChild); livePreview.removeChild(livePreview.firstElementChild);
@@ -160,18 +169,18 @@ function requestProgress(id_task, progressbarContainer, gallery, atEnd, onProgre
img.src = res.live_preview; img.src = res.live_preview;
} }
if (onProgress) {
onProgress(res);
}
setTimeout(() => { setTimeout(() => {
fun(id_task, res.id_live_preview); funLivePreview(id_task, res.id_live_preview);
}, opts.live_preview_refresh_period || 500); }, opts.live_preview_refresh_period || 500);
}, function() { }, function() {
removeProgressBar(); removeProgressBar();
}); });
}; };
fun(id_task, 0); funProgress(id_task, 0);
if (gallery) {
funLivePreview(id_task, 0);
}
} }
+141
View File
@@ -0,0 +1,141 @@
(function() {
const GRADIO_MIN_WIDTH = 320;
const GRID_TEMPLATE_COLUMNS = '1fr 16px 1fr';
const PAD = 16;
const DEBOUNCE_TIME = 100;
const R = {
tracking: false,
parent: null,
parentWidth: null,
leftCol: null,
leftColStartWidth: null,
screenX: null,
};
let resizeTimer;
let parents = [];
function setLeftColGridTemplate(el, width) {
el.style.gridTemplateColumns = `${width}px 16px 1fr`;
}
function displayResizeHandle(parent) {
if (window.innerWidth < GRADIO_MIN_WIDTH * 2 + PAD * 4) {
parent.style.display = 'flex';
if (R.handle != null) {
R.handle.style.opacity = '0';
}
return false;
} else {
parent.style.display = 'grid';
if (R.handle != null) {
R.handle.style.opacity = '100';
}
return true;
}
}
function afterResize(parent) {
if (displayResizeHandle(parent) && parent.style.gridTemplateColumns != GRID_TEMPLATE_COLUMNS) {
const oldParentWidth = R.parentWidth;
const newParentWidth = parent.offsetWidth;
const widthL = parseInt(parent.style.gridTemplateColumns.split(' ')[0]);
const ratio = newParentWidth / oldParentWidth;
const newWidthL = Math.max(Math.floor(ratio * widthL), GRADIO_MIN_WIDTH);
setLeftColGridTemplate(parent, newWidthL);
R.parentWidth = newParentWidth;
}
}
function setup(parent) {
const leftCol = parent.firstElementChild;
const rightCol = parent.lastElementChild;
parents.push(parent);
parent.style.display = 'grid';
parent.style.gap = '0';
parent.style.gridTemplateColumns = GRID_TEMPLATE_COLUMNS;
const resizeHandle = document.createElement('div');
resizeHandle.classList.add('resize-handle');
parent.insertBefore(resizeHandle, rightCol);
resizeHandle.addEventListener('mousedown', (evt) => {
if (evt.button !== 0) return;
evt.preventDefault();
evt.stopPropagation();
document.body.classList.add('resizing');
R.tracking = true;
R.parent = parent;
R.parentWidth = parent.offsetWidth;
R.handle = resizeHandle;
R.leftCol = leftCol;
R.leftColStartWidth = leftCol.offsetWidth;
R.screenX = evt.screenX;
});
resizeHandle.addEventListener('dblclick', (evt) => {
evt.preventDefault();
evt.stopPropagation();
parent.style.gridTemplateColumns = GRID_TEMPLATE_COLUMNS;
});
afterResize(parent);
}
window.addEventListener('mousemove', (evt) => {
if (evt.button !== 0) return;
if (R.tracking) {
evt.preventDefault();
evt.stopPropagation();
const delta = R.screenX - evt.screenX;
const leftColWidth = Math.max(Math.min(R.leftColStartWidth - delta, R.parent.offsetWidth - GRADIO_MIN_WIDTH - PAD), GRADIO_MIN_WIDTH);
setLeftColGridTemplate(R.parent, leftColWidth);
}
});
window.addEventListener('mouseup', (evt) => {
if (evt.button !== 0) return;
if (R.tracking) {
evt.preventDefault();
evt.stopPropagation();
R.tracking = false;
document.body.classList.remove('resizing');
}
});
window.addEventListener('resize', () => {
clearTimeout(resizeTimer);
resizeTimer = setTimeout(function() {
for (const parent of parents) {
afterResize(parent);
}
}, DEBOUNCE_TIME);
});
setupResizeHandle = setup;
})();
onUiLoaded(function() {
for (var elem of gradioApp().querySelectorAll('.resize-handle-row')) {
if (!elem.querySelector('.resize-handle')) {
setupResizeHandle(elem);
}
}
});
+71
View File
@@ -0,0 +1,71 @@
let settingsExcludeTabsFromShowAll = {
settings_tab_defaults: 1,
settings_tab_sysinfo: 1,
settings_tab_actions: 1,
settings_tab_licenses: 1,
};
function settingsShowAllTabs() {
gradioApp().querySelectorAll('#settings > div').forEach(function(elem) {
if (settingsExcludeTabsFromShowAll[elem.id]) return;
elem.style.display = "block";
});
}
function settingsShowOneTab() {
gradioApp().querySelector('#settings_show_one_page').click();
}
onUiLoaded(function() {
var edit = gradioApp().querySelector('#settings_search');
var editTextarea = gradioApp().querySelector('#settings_search > label > input');
var buttonShowAllPages = gradioApp().getElementById('settings_show_all_pages');
var settings_tabs = gradioApp().querySelector('#settings div');
onEdit('settingsSearch', editTextarea, 250, function() {
var searchText = (editTextarea.value || "").trim().toLowerCase();
gradioApp().querySelectorAll('#settings > div[id^=settings_] div[id^=column_settings_] > *').forEach(function(elem) {
var visible = elem.textContent.trim().toLowerCase().indexOf(searchText) != -1;
elem.style.display = visible ? "" : "none";
});
if (searchText != "") {
settingsShowAllTabs();
} else {
settingsShowOneTab();
}
});
settings_tabs.insertBefore(edit, settings_tabs.firstChild);
settings_tabs.appendChild(buttonShowAllPages);
buttonShowAllPages.addEventListener("click", settingsShowAllTabs);
});
onOptionsChanged(function() {
if (gradioApp().querySelector('#settings .settings-category')) return;
var sectionMap = {};
gradioApp().querySelectorAll('#settings > div > button').forEach(function(x) {
sectionMap[x.textContent.trim()] = x;
});
opts._categories.forEach(function(x) {
var section = x[0];
var category = x[1];
var span = document.createElement('SPAN');
span.textContent = category;
span.className = 'settings-category';
var sectionElem = sectionMap[section];
if (!sectionElem) return;
sectionElem.parentElement.insertBefore(span, sectionElem);
});
});
+9 -17
View File
@@ -1,10 +1,9 @@
let promptTokenCountDebounceTime = 800; let promptTokenCountUpdateFunctions = {};
let promptTokenCountTimeouts = {};
var promptTokenCountUpdateFunctions = {};
function update_txt2img_tokens(...args) { function update_txt2img_tokens(...args) {
// Called from Gradio // Called from Gradio
update_token_counter("txt2img_token_button"); update_token_counter("txt2img_token_button");
update_token_counter("txt2img_negative_token_button");
if (args.length == 2) { if (args.length == 2) {
return args[0]; return args[0];
} }
@@ -14,6 +13,7 @@ function update_txt2img_tokens(...args) {
function update_img2img_tokens(...args) { function update_img2img_tokens(...args) {
// Called from Gradio // Called from Gradio
update_token_counter("img2img_token_button"); update_token_counter("img2img_token_button");
update_token_counter("img2img_negative_token_button");
if (args.length == 2) { if (args.length == 2) {
return args[0]; return args[0];
} }
@@ -21,16 +21,7 @@ function update_img2img_tokens(...args) {
} }
function update_token_counter(button_id) { function update_token_counter(button_id) {
if (opts.disable_token_counters) { promptTokenCountUpdateFunctions[button_id]?.();
return;
}
if (promptTokenCountTimeouts[button_id]) {
clearTimeout(promptTokenCountTimeouts[button_id]);
}
promptTokenCountTimeouts[button_id] = setTimeout(
() => gradioApp().getElementById(button_id)?.click(),
promptTokenCountDebounceTime,
);
} }
@@ -69,10 +60,11 @@ function setupTokenCounting(id, id_counter, id_button) {
prompt.parentElement.insertBefore(counter, prompt); prompt.parentElement.insertBefore(counter, prompt);
prompt.parentElement.style.position = "relative"; prompt.parentElement.style.position = "relative";
promptTokenCountUpdateFunctions[id] = function() { var func = onEdit(id, textarea, 800, function() {
update_token_counter(id_button); gradioApp().getElementById(id_button)?.click();
}; });
textarea.addEventListener("input", promptTokenCountUpdateFunctions[id]); promptTokenCountUpdateFunctions[id] = func;
promptTokenCountUpdateFunctions[id_button] = func;
} }
function setupTokenCounters() { function setupTokenCounters() {
+27 -44
View File
@@ -19,28 +19,11 @@ function all_gallery_buttons() {
} }
function selected_gallery_button() { function selected_gallery_button() {
var allCurrentButtons = gradioApp().querySelectorAll('[style="display: block;"].tabitem div[id$=_gallery].gradio-gallery .thumbnail-item.thumbnail-small.selected'); return all_gallery_buttons().find(elem => elem.classList.contains('selected')) ?? null;
var visibleCurrentButton = null;
allCurrentButtons.forEach(function(elem) {
if (elem.parentElement.offsetParent) {
visibleCurrentButton = elem;
}
});
return visibleCurrentButton;
} }
function selected_gallery_index() { function selected_gallery_index() {
var buttons = all_gallery_buttons(); return all_gallery_buttons().findIndex(elem => elem.classList.contains('selected'));
var button = selected_gallery_button();
var result = -1;
buttons.forEach(function(v, i) {
if (v == button) {
result = i;
}
});
return result;
} }
function extract_image_from_gallery(gallery) { function extract_image_from_gallery(gallery) {
@@ -152,11 +135,11 @@ function submit() {
showSubmitButtons('txt2img', false); showSubmitButtons('txt2img', false);
var id = randomId(); var id = randomId();
localStorage.setItem("txt2img_task_id", id); localSet("txt2img_task_id", id);
requestProgress(id, gradioApp().getElementById('txt2img_gallery_container'), gradioApp().getElementById('txt2img_gallery'), function() { requestProgress(id, gradioApp().getElementById('txt2img_gallery_container'), gradioApp().getElementById('txt2img_gallery'), function() {
showSubmitButtons('txt2img', true); showSubmitButtons('txt2img', true);
localStorage.removeItem("txt2img_task_id"); localRemove("txt2img_task_id");
showRestoreProgressButton('txt2img', false); showRestoreProgressButton('txt2img', false);
}); });
@@ -171,11 +154,11 @@ function submit_img2img() {
showSubmitButtons('img2img', false); showSubmitButtons('img2img', false);
var id = randomId(); var id = randomId();
localStorage.setItem("img2img_task_id", id); localSet("img2img_task_id", id);
requestProgress(id, gradioApp().getElementById('img2img_gallery_container'), gradioApp().getElementById('img2img_gallery'), function() { requestProgress(id, gradioApp().getElementById('img2img_gallery_container'), gradioApp().getElementById('img2img_gallery'), function() {
showSubmitButtons('img2img', true); showSubmitButtons('img2img', true);
localStorage.removeItem("img2img_task_id"); localRemove("img2img_task_id");
showRestoreProgressButton('img2img', false); showRestoreProgressButton('img2img', false);
}); });
@@ -189,9 +172,7 @@ function submit_img2img() {
function restoreProgressTxt2img() { function restoreProgressTxt2img() {
showRestoreProgressButton("txt2img", false); showRestoreProgressButton("txt2img", false);
var id = localStorage.getItem("txt2img_task_id"); var id = localGet("txt2img_task_id");
id = localStorage.getItem("txt2img_task_id");
if (id) { if (id) {
requestProgress(id, gradioApp().getElementById('txt2img_gallery_container'), gradioApp().getElementById('txt2img_gallery'), function() { requestProgress(id, gradioApp().getElementById('txt2img_gallery_container'), gradioApp().getElementById('txt2img_gallery'), function() {
@@ -205,7 +186,7 @@ function restoreProgressTxt2img() {
function restoreProgressImg2img() { function restoreProgressImg2img() {
showRestoreProgressButton("img2img", false); showRestoreProgressButton("img2img", false);
var id = localStorage.getItem("img2img_task_id"); var id = localGet("img2img_task_id");
if (id) { if (id) {
requestProgress(id, gradioApp().getElementById('img2img_gallery_container'), gradioApp().getElementById('img2img_gallery'), function() { requestProgress(id, gradioApp().getElementById('img2img_gallery_container'), gradioApp().getElementById('img2img_gallery'), function() {
@@ -218,8 +199,8 @@ function restoreProgressImg2img() {
onUiLoaded(function() { onUiLoaded(function() {
showRestoreProgressButton('txt2img', localStorage.getItem("txt2img_task_id")); showRestoreProgressButton('txt2img', localGet("txt2img_task_id"));
showRestoreProgressButton('img2img', localStorage.getItem("img2img_task_id")); showRestoreProgressButton('img2img', localGet("img2img_task_id"));
}); });
@@ -282,21 +263,6 @@ onAfterUiUpdate(function() {
json_elem.parentElement.style.display = "none"; json_elem.parentElement.style.display = "none";
setupTokenCounters(); setupTokenCounters();
var show_all_pages = gradioApp().getElementById('settings_show_all_pages');
var settings_tabs = gradioApp().querySelector('#settings div');
if (show_all_pages && settings_tabs) {
settings_tabs.appendChild(show_all_pages);
show_all_pages.onclick = function() {
gradioApp().querySelectorAll('#settings > div').forEach(function(elem) {
if (elem.id == "settings_tab_licenses") {
return;
}
elem.style.display = "block";
});
};
}
}); });
onOptionsChanged(function() { onOptionsChanged(function() {
@@ -385,3 +351,20 @@ function switchWidthHeight(tabname) {
updateInput(height); updateInput(height);
return []; return [];
} }
var onEditTimers = {};
// calls func after afterMs milliseconds has passed since the input elem has beed enited by user
function onEdit(editId, elem, afterMs, func) {
var edited = function() {
var existingTimer = onEditTimers[editId];
if (existingTimer) clearTimeout(existingTimer);
onEditTimers[editId] = setTimeout(func, afterMs);
};
elem.addEventListener("input", edited);
return edited;
}
+11 -1
View File
@@ -1,6 +1,5 @@
from modules import launch_utils from modules import launch_utils
args = launch_utils.args args = launch_utils.args
python = launch_utils.python python = launch_utils.python
git = launch_utils.git git = launch_utils.git
@@ -18,6 +17,7 @@ run_pip = launch_utils.run_pip
check_run_python = launch_utils.check_run_python check_run_python = launch_utils.check_run_python
git_clone = launch_utils.git_clone git_clone = launch_utils.git_clone
git_pull_recursive = launch_utils.git_pull_recursive git_pull_recursive = launch_utils.git_pull_recursive
list_extensions = launch_utils.list_extensions
run_extension_installer = launch_utils.run_extension_installer run_extension_installer = launch_utils.run_extension_installer
prepare_environment = launch_utils.prepare_environment prepare_environment = launch_utils.prepare_environment
configure_for_tests = launch_utils.configure_for_tests configure_for_tests = launch_utils.configure_for_tests
@@ -25,6 +25,16 @@ start = launch_utils.start
def main(): def main():
if args.dump_sysinfo:
filename = launch_utils.dump_sysinfo()
print(f"Sysinfo saved as {filename}. Exiting...")
exit(0)
launch_utils.startup_timer.record("initial startup")
with launch_utils.startup_timer.subcategory("prepare environment"):
if not args.skip_prepare_environment: if not args.skip_prepare_environment:
prepare_environment() prepare_environment()
+149 -60
View File
@@ -1,8 +1,11 @@
import base64 import base64
import io import io
import os
import time import time
import datetime import datetime
import uvicorn import uvicorn
import ipaddress
import requests
import gradio as gr import gradio as gr
from threading import Lock from threading import Lock
from io import BytesIO from io import BytesIO
@@ -14,7 +17,7 @@ from fastapi.encoders import jsonable_encoder
from secrets import compare_digest from secrets import compare_digest
import modules.shared as shared import modules.shared as shared
from modules import sd_samplers, deepbooru, sd_hijack, images, scripts, ui, postprocessing, errors from modules import sd_samplers, deepbooru, sd_hijack, images, scripts, ui, postprocessing, errors, restart, shared_items, script_callbacks, generation_parameters_copypaste, sd_models
from modules.api import models from modules.api import models
from modules.shared import opts from modules.shared import opts
from modules.processing import StableDiffusionProcessingTxt2Img, StableDiffusionProcessingImg2Img, process_images from modules.processing import StableDiffusionProcessingTxt2Img, StableDiffusionProcessingImg2Img, process_images
@@ -22,21 +25,13 @@ from modules.textual_inversion.textual_inversion import create_embedding, train_
from modules.textual_inversion.preprocess import preprocess from modules.textual_inversion.preprocess import preprocess
from modules.hypernetworks.hypernetwork import create_hypernetwork, train_hypernetwork from modules.hypernetworks.hypernetwork import create_hypernetwork, train_hypernetwork
from PIL import PngImagePlugin, Image from PIL import PngImagePlugin, Image
from modules.sd_models import checkpoints_list, unload_model_weights, reload_model_weights
from modules.sd_vae import vae_dict
from modules.sd_models_config import find_checkpoint_config_near_filename from modules.sd_models_config import find_checkpoint_config_near_filename
from modules.realesrgan_model import get_realesrgan_models from modules.realesrgan_model import get_realesrgan_models
from modules import devices from modules import devices
from typing import Dict, List, Any from typing import Any
import piexif import piexif
import piexif.helper import piexif.helper
from contextlib import closing
def upscaler_to_index(name: str):
try:
return [x.name.lower() for x in shared.sd_upscalers].index(name.lower())
except Exception as e:
raise HTTPException(status_code=400, detail=f"Invalid upscaler, needs to be one of these: {' , '.join([x.name for x in shared.sd_upscalers])}") from e
def script_name_to_index(name, scripts): def script_name_to_index(name, scripts):
@@ -61,7 +56,41 @@ def setUpscalers(req: dict):
return reqDict return reqDict
def verify_url(url):
"""Returns True if the url refers to a global resource."""
import socket
from urllib.parse import urlparse
try:
parsed_url = urlparse(url)
domain_name = parsed_url.netloc
host = socket.gethostbyname_ex(domain_name)
for ip in host[2]:
ip_addr = ipaddress.ip_address(ip)
if not ip_addr.is_global:
return False
except Exception:
return False
return True
def decode_base64_to_image(encoding): def decode_base64_to_image(encoding):
if encoding.startswith("http://") or encoding.startswith("https://"):
if not opts.api_enable_requests:
raise HTTPException(status_code=500, detail="Requests not allowed")
if opts.api_forbid_local_requests and not verify_url(encoding):
raise HTTPException(status_code=500, detail="Request to local resource not allowed")
headers = {'user-agent': opts.api_useragent} if opts.api_useragent else {}
response = requests.get(encoding, timeout=30, headers=headers)
try:
image = Image.open(BytesIO(response.content))
return image
except Exception as e:
raise HTTPException(status_code=500, detail="Invalid image url") from e
if encoding.startswith("data:image/"): if encoding.startswith("data:image/"):
encoding = encoding.split(";")[1].split(",")[1] encoding = encoding.split(";")[1].split(",")[1]
try: try:
@@ -73,7 +102,8 @@ def decode_base64_to_image(encoding):
def encode_pil_to_base64(image): def encode_pil_to_base64(image):
with io.BytesIO() as output_bytes: with io.BytesIO() as output_bytes:
if isinstance(image, str):
return image
if opts.samples_format.lower() == 'png': if opts.samples_format.lower() == 'png':
use_metadata = False use_metadata = False
metadata = PngImagePlugin.PngInfo() metadata = PngImagePlugin.PngInfo()
@@ -84,6 +114,8 @@ def encode_pil_to_base64(image):
image.save(output_bytes, format="PNG", pnginfo=(metadata if use_metadata else None), quality=opts.jpeg_quality) image.save(output_bytes, format="PNG", pnginfo=(metadata if use_metadata else None), quality=opts.jpeg_quality)
elif opts.samples_format.lower() in ("jpg", "jpeg", "webp"): elif opts.samples_format.lower() in ("jpg", "jpeg", "webp"):
if image.mode == "RGBA":
image = image.convert("RGB")
parameters = image.info.get('parameters', None) parameters = image.info.get('parameters', None)
exif_bytes = piexif.dump({ exif_bytes = piexif.dump({
"Exif": { piexif.ExifIFD.UserComment: piexif.helper.UserComment.dump(parameters or "", encoding="unicode") } "Exif": { piexif.ExifIFD.UserComment: piexif.helper.UserComment.dump(parameters or "", encoding="unicode") }
@@ -102,14 +134,16 @@ def encode_pil_to_base64(image):
def api_middleware(app: FastAPI): def api_middleware(app: FastAPI):
rich_available = True rich_available = False
try: try:
if os.environ.get('WEBUI_RICH_EXCEPTIONS', None) is not None:
import anyio # importing just so it can be placed on silent list import anyio # importing just so it can be placed on silent list
import starlette # importing just so it can be placed on silent list import starlette # importing just so it can be placed on silent list
from rich.console import Console from rich.console import Console
console = Console() console = Console()
rich_available = True
except Exception: except Exception:
rich_available = False pass
@app.middleware("http") @app.middleware("http")
async def log_and_time(req: Request, call_next): async def log_and_time(req: Request, call_next):
@@ -187,17 +221,18 @@ class Api:
self.add_api_route("/sdapi/v1/options", self.get_config, methods=["GET"], response_model=models.OptionsModel) self.add_api_route("/sdapi/v1/options", self.get_config, methods=["GET"], response_model=models.OptionsModel)
self.add_api_route("/sdapi/v1/options", self.set_config, methods=["POST"]) self.add_api_route("/sdapi/v1/options", self.set_config, methods=["POST"])
self.add_api_route("/sdapi/v1/cmd-flags", self.get_cmd_flags, methods=["GET"], response_model=models.FlagsModel) self.add_api_route("/sdapi/v1/cmd-flags", self.get_cmd_flags, methods=["GET"], response_model=models.FlagsModel)
self.add_api_route("/sdapi/v1/samplers", self.get_samplers, methods=["GET"], response_model=List[models.SamplerItem]) self.add_api_route("/sdapi/v1/samplers", self.get_samplers, methods=["GET"], response_model=list[models.SamplerItem])
self.add_api_route("/sdapi/v1/upscalers", self.get_upscalers, methods=["GET"], response_model=List[models.UpscalerItem]) self.add_api_route("/sdapi/v1/upscalers", self.get_upscalers, methods=["GET"], response_model=list[models.UpscalerItem])
self.add_api_route("/sdapi/v1/latent-upscale-modes", self.get_latent_upscale_modes, methods=["GET"], response_model=List[models.LatentUpscalerModeItem]) self.add_api_route("/sdapi/v1/latent-upscale-modes", self.get_latent_upscale_modes, methods=["GET"], response_model=list[models.LatentUpscalerModeItem])
self.add_api_route("/sdapi/v1/sd-models", self.get_sd_models, methods=["GET"], response_model=List[models.SDModelItem]) self.add_api_route("/sdapi/v1/sd-models", self.get_sd_models, methods=["GET"], response_model=list[models.SDModelItem])
self.add_api_route("/sdapi/v1/sd-vae", self.get_sd_vaes, methods=["GET"], response_model=List[models.SDVaeItem]) self.add_api_route("/sdapi/v1/sd-vae", self.get_sd_vaes, methods=["GET"], response_model=list[models.SDVaeItem])
self.add_api_route("/sdapi/v1/hypernetworks", self.get_hypernetworks, methods=["GET"], response_model=List[models.HypernetworkItem]) self.add_api_route("/sdapi/v1/hypernetworks", self.get_hypernetworks, methods=["GET"], response_model=list[models.HypernetworkItem])
self.add_api_route("/sdapi/v1/face-restorers", self.get_face_restorers, methods=["GET"], response_model=List[models.FaceRestorerItem]) self.add_api_route("/sdapi/v1/face-restorers", self.get_face_restorers, methods=["GET"], response_model=list[models.FaceRestorerItem])
self.add_api_route("/sdapi/v1/realesrgan-models", self.get_realesrgan_models, methods=["GET"], response_model=List[models.RealesrganItem]) self.add_api_route("/sdapi/v1/realesrgan-models", self.get_realesrgan_models, methods=["GET"], response_model=list[models.RealesrganItem])
self.add_api_route("/sdapi/v1/prompt-styles", self.get_prompt_styles, methods=["GET"], response_model=List[models.PromptStyleItem]) self.add_api_route("/sdapi/v1/prompt-styles", self.get_prompt_styles, methods=["GET"], response_model=list[models.PromptStyleItem])
self.add_api_route("/sdapi/v1/embeddings", self.get_embeddings, methods=["GET"], response_model=models.EmbeddingsResponse) self.add_api_route("/sdapi/v1/embeddings", self.get_embeddings, methods=["GET"], response_model=models.EmbeddingsResponse)
self.add_api_route("/sdapi/v1/refresh-checkpoints", self.refresh_checkpoints, methods=["POST"]) self.add_api_route("/sdapi/v1/refresh-checkpoints", self.refresh_checkpoints, methods=["POST"])
self.add_api_route("/sdapi/v1/refresh-vae", self.refresh_vae, methods=["POST"])
self.add_api_route("/sdapi/v1/create/embedding", self.create_embedding, methods=["POST"], response_model=models.CreateResponse) self.add_api_route("/sdapi/v1/create/embedding", self.create_embedding, methods=["POST"], response_model=models.CreateResponse)
self.add_api_route("/sdapi/v1/create/hypernetwork", self.create_hypernetwork, methods=["POST"], response_model=models.CreateResponse) self.add_api_route("/sdapi/v1/create/hypernetwork", self.create_hypernetwork, methods=["POST"], response_model=models.CreateResponse)
self.add_api_route("/sdapi/v1/preprocess", self.preprocess, methods=["POST"], response_model=models.PreprocessResponse) self.add_api_route("/sdapi/v1/preprocess", self.preprocess, methods=["POST"], response_model=models.PreprocessResponse)
@@ -207,7 +242,13 @@ class Api:
self.add_api_route("/sdapi/v1/unload-checkpoint", self.unloadapi, methods=["POST"]) self.add_api_route("/sdapi/v1/unload-checkpoint", self.unloadapi, methods=["POST"])
self.add_api_route("/sdapi/v1/reload-checkpoint", self.reloadapi, methods=["POST"]) self.add_api_route("/sdapi/v1/reload-checkpoint", self.reloadapi, methods=["POST"])
self.add_api_route("/sdapi/v1/scripts", self.get_scripts_list, methods=["GET"], response_model=models.ScriptsList) self.add_api_route("/sdapi/v1/scripts", self.get_scripts_list, methods=["GET"], response_model=models.ScriptsList)
self.add_api_route("/sdapi/v1/script-info", self.get_script_info, methods=["GET"], response_model=List[models.ScriptInfo]) self.add_api_route("/sdapi/v1/script-info", self.get_script_info, methods=["GET"], response_model=list[models.ScriptInfo])
self.add_api_route("/sdapi/v1/extensions", self.get_extensions_list, methods=["GET"], response_model=list[models.ExtensionItem])
if shared.cmd_opts.api_server_stop:
self.add_api_route("/sdapi/v1/server-kill", self.kill_webui, methods=["POST"])
self.add_api_route("/sdapi/v1/server-restart", self.restart_webui, methods=["POST"])
self.add_api_route("/sdapi/v1/server-stop", self.stop_webui, methods=["POST"])
self.default_script_arg_txt2img = [] self.default_script_arg_txt2img = []
self.default_script_arg_img2img = [] self.default_script_arg_img2img = []
@@ -324,19 +365,23 @@ class Api:
args.pop('save_images', None) args.pop('save_images', None)
with self.queue_lock: with self.queue_lock:
p = StableDiffusionProcessingTxt2Img(sd_model=shared.sd_model, **args) with closing(StableDiffusionProcessingTxt2Img(sd_model=shared.sd_model, **args)) as p:
p.is_api = True
p.scripts = script_runner p.scripts = script_runner
p.outpath_grids = opts.outdir_txt2img_grids p.outpath_grids = opts.outdir_txt2img_grids
p.outpath_samples = opts.outdir_txt2img_samples p.outpath_samples = opts.outdir_txt2img_samples
shared.state.begin() try:
shared.state.begin(job="scripts_txt2img")
if selectable_scripts is not None: if selectable_scripts is not None:
p.script_args = script_args p.script_args = script_args
processed = scripts.scripts_txt2img.run(p, *p.script_args) # Need to pass args as list here processed = scripts.scripts_txt2img.run(p, *p.script_args) # Need to pass args as list here
else: else:
p.script_args = tuple(script_args) # Need to pass args as tuple here p.script_args = tuple(script_args) # Need to pass args as tuple here
processed = process_images(p) processed = process_images(p)
finally:
shared.state.end() shared.state.end()
shared.total_tqdm.clear()
b64images = list(map(encode_pil_to_base64, processed.images)) if send_images else [] b64images = list(map(encode_pil_to_base64, processed.images)) if send_images else []
@@ -380,20 +425,24 @@ class Api:
args.pop('save_images', None) args.pop('save_images', None)
with self.queue_lock: with self.queue_lock:
p = StableDiffusionProcessingImg2Img(sd_model=shared.sd_model, **args) with closing(StableDiffusionProcessingImg2Img(sd_model=shared.sd_model, **args)) as p:
p.init_images = [decode_base64_to_image(x) for x in init_images] p.init_images = [decode_base64_to_image(x) for x in init_images]
p.is_api = True
p.scripts = script_runner p.scripts = script_runner
p.outpath_grids = opts.outdir_img2img_grids p.outpath_grids = opts.outdir_img2img_grids
p.outpath_samples = opts.outdir_img2img_samples p.outpath_samples = opts.outdir_img2img_samples
shared.state.begin() try:
shared.state.begin(job="scripts_img2img")
if selectable_scripts is not None: if selectable_scripts is not None:
p.script_args = script_args p.script_args = script_args
processed = scripts.scripts_img2img.run(p, *p.script_args) # Need to pass args as list here processed = scripts.scripts_img2img.run(p, *p.script_args) # Need to pass args as list here
else: else:
p.script_args = tuple(script_args) # Need to pass args as tuple here p.script_args = tuple(script_args) # Need to pass args as tuple here
processed = process_images(p) processed = process_images(p)
finally:
shared.state.end() shared.state.end()
shared.total_tqdm.clear()
b64images = list(map(encode_pil_to_base64, processed.images)) if send_images else [] b64images = list(map(encode_pil_to_base64, processed.images)) if send_images else []
@@ -425,9 +474,6 @@ class Api:
return models.ExtrasBatchImagesResponse(images=list(map(encode_pil_to_base64, result[0])), html_info=result[1]) return models.ExtrasBatchImagesResponse(images=list(map(encode_pil_to_base64, result[0])), html_info=result[1])
def pnginfoapi(self, req: models.PNGInfoRequest): def pnginfoapi(self, req: models.PNGInfoRequest):
if(not req.image.strip()):
return models.PNGInfoResponse(info="")
image = decode_base64_to_image(req.image.strip()) image = decode_base64_to_image(req.image.strip())
if image is None: if image is None:
return models.PNGInfoResponse(info="") return models.PNGInfoResponse(info="")
@@ -436,9 +482,10 @@ class Api:
if geninfo is None: if geninfo is None:
geninfo = "" geninfo = ""
items = {**{'parameters': geninfo}, **items} params = generation_parameters_copypaste.parse_generation_parameters(geninfo)
script_callbacks.infotext_pasted_callback(geninfo, params)
return models.PNGInfoResponse(info=geninfo, items=items) return models.PNGInfoResponse(info=geninfo, items=items, parameters=params)
def progressapi(self, req: models.ProgressRequest = Depends()): def progressapi(self, req: models.ProgressRequest = Depends()):
# copy from check_progress_call of ui.py # copy from check_progress_call of ui.py
@@ -493,12 +540,12 @@ class Api:
return {} return {}
def unloadapi(self): def unloadapi(self):
unload_model_weights() sd_models.unload_model_weights()
return {} return {}
def reloadapi(self): def reloadapi(self):
reload_model_weights() sd_models.send_model_to_device(shared.sd_model)
return {} return {}
@@ -516,9 +563,13 @@ class Api:
return options return options
def set_config(self, req: Dict[str, Any]): def set_config(self, req: dict[str, Any]):
checkpoint_name = req.get("sd_model_checkpoint", None)
if checkpoint_name is not None and checkpoint_name not in sd_models.checkpoint_aliases:
raise RuntimeError(f"model {checkpoint_name!r} not found")
for k, v in req.items(): for k, v in req.items():
shared.opts.set(k, v) shared.opts.set(k, v, is_api=True)
shared.opts.save(shared.config_filename) shared.opts.save(shared.config_filename)
return return
@@ -550,10 +601,12 @@ class Api:
] ]
def get_sd_models(self): def get_sd_models(self):
return [{"title": x.title, "model_name": x.model_name, "hash": x.shorthash, "sha256": x.sha256, "filename": x.filename, "config": find_checkpoint_config_near_filename(x)} for x in checkpoints_list.values()] import modules.sd_models as sd_models
return [{"title": x.title, "model_name": x.model_name, "hash": x.shorthash, "sha256": x.sha256, "filename": x.filename, "config": find_checkpoint_config_near_filename(x)} for x in sd_models.checkpoints_list.values()]
def get_sd_vaes(self): def get_sd_vaes(self):
return [{"model_name": x, "filename": vae_dict[x]} for x in vae_dict.keys()] import modules.sd_vae as sd_vae
return [{"model_name": x, "filename": sd_vae.vae_dict[x]} for x in sd_vae.vae_dict.keys()]
def get_hypernetworks(self): def get_hypernetworks(self):
return [{"name": name, "path": shared.hypernetworks[name]} for name in shared.hypernetworks] return [{"name": name, "path": shared.hypernetworks[name]} for name in shared.hypernetworks]
@@ -593,48 +646,51 @@ class Api:
} }
def refresh_checkpoints(self): def refresh_checkpoints(self):
with self.queue_lock:
shared.refresh_checkpoints() shared.refresh_checkpoints()
def refresh_vae(self):
with self.queue_lock:
shared_items.refresh_vae_list()
def create_embedding(self, args: dict): def create_embedding(self, args: dict):
try: try:
shared.state.begin() shared.state.begin(job="create_embedding")
filename = create_embedding(**args) # create empty embedding filename = create_embedding(**args) # create empty embedding
sd_hijack.model_hijack.embedding_db.load_textual_inversion_embeddings() # reload embeddings so new one can be immediately used sd_hijack.model_hijack.embedding_db.load_textual_inversion_embeddings() # reload embeddings so new one can be immediately used
shared.state.end()
return models.CreateResponse(info=f"create embedding filename: {filename}") return models.CreateResponse(info=f"create embedding filename: {filename}")
except AssertionError as e: except AssertionError as e:
shared.state.end()
return models.TrainResponse(info=f"create embedding error: {e}") return models.TrainResponse(info=f"create embedding error: {e}")
finally:
shared.state.end()
def create_hypernetwork(self, args: dict): def create_hypernetwork(self, args: dict):
try: try:
shared.state.begin() shared.state.begin(job="create_hypernetwork")
filename = create_hypernetwork(**args) # create empty embedding filename = create_hypernetwork(**args) # create empty embedding
shared.state.end()
return models.CreateResponse(info=f"create hypernetwork filename: {filename}") return models.CreateResponse(info=f"create hypernetwork filename: {filename}")
except AssertionError as e: except AssertionError as e:
shared.state.end()
return models.TrainResponse(info=f"create hypernetwork error: {e}") return models.TrainResponse(info=f"create hypernetwork error: {e}")
finally:
shared.state.end()
def preprocess(self, args: dict): def preprocess(self, args: dict):
try: try:
shared.state.begin() shared.state.begin(job="preprocess")
preprocess(**args) # quick operation unless blip/booru interrogation is enabled preprocess(**args) # quick operation unless blip/booru interrogation is enabled
shared.state.end() shared.state.end()
return models.PreprocessResponse(info='preprocess complete') return models.PreprocessResponse(info='preprocess complete')
except KeyError as e: except KeyError as e:
shared.state.end()
return models.PreprocessResponse(info=f"preprocess error: invalid token: {e}") return models.PreprocessResponse(info=f"preprocess error: invalid token: {e}")
except AssertionError as e: except Exception as e:
shared.state.end()
return models.PreprocessResponse(info=f"preprocess error: {e}") return models.PreprocessResponse(info=f"preprocess error: {e}")
except FileNotFoundError as e: finally:
shared.state.end() shared.state.end()
return models.PreprocessResponse(info=f'preprocess error: {e}')
def train_embedding(self, args: dict): def train_embedding(self, args: dict):
try: try:
shared.state.begin() shared.state.begin(job="train_embedding")
apply_optimizations = shared.opts.training_xattention_optimizations apply_optimizations = shared.opts.training_xattention_optimizations
error = None error = None
filename = '' filename = ''
@@ -647,15 +703,15 @@ class Api:
finally: finally:
if not apply_optimizations: if not apply_optimizations:
sd_hijack.apply_optimizations() sd_hijack.apply_optimizations()
shared.state.end()
return models.TrainResponse(info=f"train embedding complete: filename: {filename} error: {error}") return models.TrainResponse(info=f"train embedding complete: filename: {filename} error: {error}")
except AssertionError as msg: except Exception as msg:
shared.state.end()
return models.TrainResponse(info=f"train embedding error: {msg}") return models.TrainResponse(info=f"train embedding error: {msg}")
finally:
shared.state.end()
def train_hypernetwork(self, args: dict): def train_hypernetwork(self, args: dict):
try: try:
shared.state.begin() shared.state.begin(job="train_hypernetwork")
shared.loaded_hypernetworks = [] shared.loaded_hypernetworks = []
apply_optimizations = shared.opts.training_xattention_optimizations apply_optimizations = shared.opts.training_xattention_optimizations
error = None error = None
@@ -673,9 +729,10 @@ class Api:
sd_hijack.apply_optimizations() sd_hijack.apply_optimizations()
shared.state.end() shared.state.end()
return models.TrainResponse(info=f"train embedding complete: filename: {filename} error: {error}") return models.TrainResponse(info=f"train embedding complete: filename: {filename} error: {error}")
except AssertionError: except Exception as exc:
return models.TrainResponse(info=f"train embedding error: {exc}")
finally:
shared.state.end() shared.state.end()
return models.TrainResponse(info=f"train embedding error: {error}")
def get_memory(self): def get_memory(self):
try: try:
@@ -712,6 +769,38 @@ class Api:
cuda = {'error': f'{err}'} cuda = {'error': f'{err}'}
return models.MemoryResponse(ram=ram, cuda=cuda) return models.MemoryResponse(ram=ram, cuda=cuda)
def launch(self, server_name, port): def get_extensions_list(self):
from modules import extensions
extensions.list_extensions()
ext_list = []
for ext in extensions.extensions:
ext: extensions.Extension
ext.read_info_from_repo()
if ext.remote is not None:
ext_list.append({
"name": ext.name,
"remote": ext.remote,
"branch": ext.branch,
"commit_hash":ext.commit_hash,
"commit_date":ext.commit_date,
"version":ext.version,
"enabled":ext.enabled
})
return ext_list
def launch(self, server_name, port, root_path):
self.app.include_router(self.router) self.app.include_router(self.router)
uvicorn.run(self.app, host=server_name, port=port, timeout_keep_alive=0) uvicorn.run(self.app, host=server_name, port=port, timeout_keep_alive=shared.cmd_opts.timeout_keep_alive, root_path=root_path)
def kill_webui(self):
restart.stop_program()
def restart_webui(self):
if restart.is_restartable():
restart.restart_program()
return Response(status_code=501)
def stop_webui(request):
shared.state.server_command = "stop"
return Response("Stopping.")
+33 -28
View File
@@ -1,11 +1,10 @@
import inspect import inspect
from pydantic import BaseModel, Field, create_model from pydantic import BaseModel, Field, create_model
from typing import Any, Optional from typing import Any, Optional, Literal
from typing_extensions import Literal
from inflection import underscore from inflection import underscore
from modules.processing import StableDiffusionProcessingTxt2Img, StableDiffusionProcessingImg2Img from modules.processing import StableDiffusionProcessingTxt2Img, StableDiffusionProcessingImg2Img
from modules.shared import sd_upscalers, opts, parser from modules.shared import sd_upscalers, opts, parser
from typing import Dict, List
API_NOT_ALLOWED = [ API_NOT_ALLOWED = [
"self", "self",
@@ -49,10 +48,12 @@ class PydanticModelGenerator:
additional_fields = None, additional_fields = None,
): ):
def field_type_generator(k, v): def field_type_generator(k, v):
# field_type = str if not overrides.get(k) else overrides[k]["type"]
# print(k, v.annotation, v.default)
field_type = v.annotation field_type = v.annotation
if field_type == 'Image':
# images are sent as base64 strings via API
field_type = 'str'
return Optional[field_type] return Optional[field_type]
def merge_class_params(class_): def merge_class_params(class_):
@@ -62,7 +63,6 @@ class PydanticModelGenerator:
parameters = {**parameters, **inspect.signature(classes.__init__).parameters} parameters = {**parameters, **inspect.signature(classes.__init__).parameters}
return parameters return parameters
self._model_name = model_name self._model_name = model_name
self._class_data = merge_class_params(class_instance) self._class_data = merge_class_params(class_instance)
@@ -71,7 +71,7 @@ class PydanticModelGenerator:
field=underscore(k), field=underscore(k),
field_alias=k, field_alias=k,
field_type=field_type_generator(k, v), field_type=field_type_generator(k, v),
field_value=v.default field_value=None if isinstance(v.default, property) else v.default
) )
for (k,v) in self._class_data.items() if k not in API_NOT_ALLOWED for (k,v) in self._class_data.items() if k not in API_NOT_ALLOWED
] ]
@@ -93,8 +93,8 @@ class PydanticModelGenerator:
d.field: (d.field_type, Field(default=d.field_value, alias=d.field_alias, exclude=d.field_exclude)) for d in self._model_def d.field: (d.field_type, Field(default=d.field_value, alias=d.field_alias, exclude=d.field_exclude)) for d in self._model_def
} }
DynamicModel = create_model(self._model_name, **fields) DynamicModel = create_model(self._model_name, **fields)
DynamicModel.__config__.allow_population_by_field_name = True DynamicModel.model_config['populate_by_name'] = True
DynamicModel.__config__.allow_mutation = True DynamicModel.model_config['frozen'] = True
return DynamicModel return DynamicModel
StableDiffusionTxt2ImgProcessingAPI = PydanticModelGenerator( StableDiffusionTxt2ImgProcessingAPI = PydanticModelGenerator(
@@ -128,12 +128,12 @@ StableDiffusionImg2ImgProcessingAPI = PydanticModelGenerator(
).generate_model() ).generate_model()
class TextToImageResponse(BaseModel): class TextToImageResponse(BaseModel):
images: List[str] = Field(default=None, title="Image", description="The generated image in base64 format.") images: list[str] = Field(default=None, title="Image", description="The generated image in base64 format.")
parameters: dict parameters: dict
info: str info: str
class ImageToImageResponse(BaseModel): class ImageToImageResponse(BaseModel):
images: List[str] = Field(default=None, title="Image", description="The generated image in base64 format.") images: list[str] = Field(default=None, title="Image", description="The generated image in base64 format.")
parameters: dict parameters: dict
info: str info: str
@@ -166,17 +166,18 @@ class FileData(BaseModel):
name: str = Field(title="File name") name: str = Field(title="File name")
class ExtrasBatchImagesRequest(ExtrasBaseRequest): class ExtrasBatchImagesRequest(ExtrasBaseRequest):
imageList: List[FileData] = Field(title="Images", description="List of images to work on. Must be Base64 strings") imageList: list[FileData] = Field(title="Images", description="List of images to work on. Must be Base64 strings")
class ExtrasBatchImagesResponse(ExtraBaseResponse): class ExtrasBatchImagesResponse(ExtraBaseResponse):
images: List[str] = Field(title="Images", description="The generated images in base64 format.") images: list[str] = Field(title="Images", description="The generated images in base64 format.")
class PNGInfoRequest(BaseModel): class PNGInfoRequest(BaseModel):
image: str = Field(title="Image", description="The base64 encoded PNG image") image: str = Field(title="Image", description="The base64 encoded PNG image")
class PNGInfoResponse(BaseModel): class PNGInfoResponse(BaseModel):
info: str = Field(title="Image info", description="A string with the parameters used to generate the image") info: str = Field(title="Image info", description="A string with the parameters used to generate the image")
items: dict = Field(title="Items", description="An object containing all the info the image had") items: dict = Field(title="Items", description="A dictionary containing all the other fields the image had")
parameters: dict = Field(title="Parameters", description="A dictionary with parsed generation info fields")
class ProgressRequest(BaseModel): class ProgressRequest(BaseModel):
skip_current_image: bool = Field(default=False, title="Skip current image", description="Skip current image serialization") skip_current_image: bool = Field(default=False, title="Skip current image", description="Skip current image serialization")
@@ -207,11 +208,10 @@ class PreprocessResponse(BaseModel):
fields = {} fields = {}
for key, metadata in opts.data_labels.items(): for key, metadata in opts.data_labels.items():
value = opts.data.get(key) value = opts.data.get(key)
optType = opts.typemap.get(type(metadata.default), type(value)) optType = opts.typemap.get(type(metadata.default), type(metadata.default)) if metadata.default else Any
if (metadata is not None): if metadata is not None:
fields.update({key: (Optional[optType], Field( fields.update({key: (Optional[optType], Field(default=metadata.default, description=metadata.label))})
default=metadata.default ,description=metadata.label))})
else: else:
fields.update({key: (Optional[optType], Field())}) fields.update({key: (Optional[optType], Field())})
@@ -231,8 +231,8 @@ FlagsModel = create_model("Flags", **flags)
class SamplerItem(BaseModel): class SamplerItem(BaseModel):
name: str = Field(title="Name") name: str = Field(title="Name")
aliases: List[str] = Field(title="Aliases") aliases: list[str] = Field(title="Aliases")
options: Dict[str, str] = Field(title="Options") options: dict[str, str] = Field(title="Options")
class UpscalerItem(BaseModel): class UpscalerItem(BaseModel):
name: str = Field(title="Name") name: str = Field(title="Name")
@@ -274,10 +274,6 @@ class PromptStyleItem(BaseModel):
prompt: Optional[str] = Field(title="Prompt") prompt: Optional[str] = Field(title="Prompt")
negative_prompt: Optional[str] = Field(title="Negative Prompt") negative_prompt: Optional[str] = Field(title="Negative Prompt")
class ArtistItem(BaseModel):
name: str = Field(title="Name")
score: float = Field(title="Score")
category: str = Field(title="Category")
class EmbeddingItem(BaseModel): class EmbeddingItem(BaseModel):
step: Optional[int] = Field(title="Step", description="The number of steps that were used to train this embedding, if available") step: Optional[int] = Field(title="Step", description="The number of steps that were used to train this embedding, if available")
@@ -287,8 +283,8 @@ class EmbeddingItem(BaseModel):
vectors: int = Field(title="Vectors", description="The number of vectors in the embedding") vectors: int = Field(title="Vectors", description="The number of vectors in the embedding")
class EmbeddingsResponse(BaseModel): class EmbeddingsResponse(BaseModel):
loaded: Dict[str, EmbeddingItem] = Field(title="Loaded", description="Embeddings loaded for the current model") loaded: dict[str, EmbeddingItem] = Field(title="Loaded", description="Embeddings loaded for the current model")
skipped: Dict[str, EmbeddingItem] = Field(title="Skipped", description="Embeddings skipped for the current model (likely due to architecture incompatibility)") skipped: dict[str, EmbeddingItem] = Field(title="Skipped", description="Embeddings skipped for the current model (likely due to architecture incompatibility)")
class MemoryResponse(BaseModel): class MemoryResponse(BaseModel):
ram: dict = Field(title="RAM", description="System memory stats") ram: dict = Field(title="RAM", description="System memory stats")
@@ -306,11 +302,20 @@ class ScriptArg(BaseModel):
minimum: Optional[Any] = Field(default=None, title="Minimum", description="Minimum allowed value for the argumentin UI") minimum: Optional[Any] = Field(default=None, title="Minimum", description="Minimum allowed value for the argumentin UI")
maximum: Optional[Any] = Field(default=None, title="Minimum", description="Maximum allowed value for the argumentin UI") maximum: Optional[Any] = Field(default=None, title="Minimum", description="Maximum allowed value for the argumentin UI")
step: Optional[Any] = Field(default=None, title="Minimum", description="Step for changing value of the argumentin UI") step: Optional[Any] = Field(default=None, title="Minimum", description="Step for changing value of the argumentin UI")
choices: Optional[List[str]] = Field(default=None, title="Choices", description="Possible values for the argument") choices: Optional[list[str]] = Field(default=None, title="Choices", description="Possible values for the argument")
class ScriptInfo(BaseModel): class ScriptInfo(BaseModel):
name: str = Field(default=None, title="Name", description="Script name") name: str = Field(default=None, title="Name", description="Script name")
is_alwayson: bool = Field(default=None, title="IsAlwayson", description="Flag specifying whether this script is an alwayson script") is_alwayson: bool = Field(default=None, title="IsAlwayson", description="Flag specifying whether this script is an alwayson script")
is_img2img: bool = Field(default=None, title="IsImg2img", description="Flag specifying whether this script is an img2img script") is_img2img: bool = Field(default=None, title="IsImg2img", description="Flag specifying whether this script is an img2img script")
args: List[ScriptArg] = Field(title="Arguments", description="List of script's arguments") args: list[ScriptArg] = Field(title="Arguments", description="List of script's arguments")
class ExtensionItem(BaseModel):
name: str = Field(title="Name", description="Extension name")
remote: str = Field(title="Remote", description="Extension Repository URL")
branch: str = Field(title="Branch", description="Extension Repository Branch")
commit_hash: str = Field(title="Commit Hash", description="Extension Repository Commit Hash")
version: str = Field(title="Version", description="Extension Version")
commit_date: str = Field(title="Commit Date", description="Extension Repository Commit Date")
enabled: bool = Field(title="Enabled", description="Flag specifying whether this extension is enabled")
+124
View File
@@ -0,0 +1,124 @@
import json
import os
import os.path
import threading
import time
from modules.paths import data_path, script_path
cache_filename = os.environ.get('SD_WEBUI_CACHE_FILE', os.path.join(data_path, "cache.json"))
cache_data = None
cache_lock = threading.Lock()
dump_cache_after = None
dump_cache_thread = None
def dump_cache():
"""
Marks cache for writing to disk. 5 seconds after no one else flags the cache for writing, it is written.
"""
global dump_cache_after
global dump_cache_thread
def thread_func():
global dump_cache_after
global dump_cache_thread
while dump_cache_after is not None and time.time() < dump_cache_after:
time.sleep(1)
with cache_lock:
cache_filename_tmp = cache_filename + "-"
with open(cache_filename_tmp, "w", encoding="utf8") as file:
json.dump(cache_data, file, indent=4, ensure_ascii=False)
os.replace(cache_filename_tmp, cache_filename)
dump_cache_after = None
dump_cache_thread = None
with cache_lock:
dump_cache_after = time.time() + 5
if dump_cache_thread is None:
dump_cache_thread = threading.Thread(name='cache-writer', target=thread_func)
dump_cache_thread.start()
def cache(subsection):
"""
Retrieves or initializes a cache for a specific subsection.
Parameters:
subsection (str): The subsection identifier for the cache.
Returns:
dict: The cache data for the specified subsection.
"""
global cache_data
if cache_data is None:
with cache_lock:
if cache_data is None:
if not os.path.isfile(cache_filename):
cache_data = {}
else:
try:
with open(cache_filename, "r", encoding="utf8") as file:
cache_data = json.load(file)
except Exception:
os.replace(cache_filename, os.path.join(script_path, "tmp", "cache.json"))
print('[ERROR] issue occurred while trying to read cache.json, move current cache to tmp/cache.json and create new cache')
cache_data = {}
s = cache_data.get(subsection, {})
cache_data[subsection] = s
return s
def cached_data_for_file(subsection, title, filename, func):
"""
Retrieves or generates data for a specific file, using a caching mechanism.
Parameters:
subsection (str): The subsection of the cache to use.
title (str): The title of the data entry in the subsection of the cache.
filename (str): The path to the file to be checked for modifications.
func (callable): A function that generates the data if it is not available in the cache.
Returns:
dict or None: The cached or generated data, or None if data generation fails.
The `cached_data_for_file` function implements a caching mechanism for data stored in files.
It checks if the data associated with the given `title` is present in the cache and compares the
modification time of the file with the cached modification time. If the file has been modified,
the cache is considered invalid and the data is regenerated using the provided `func`.
Otherwise, the cached data is returned.
If the data generation fails, None is returned to indicate the failure. Otherwise, the generated
or cached data is returned as a dictionary.
"""
existing_cache = cache(subsection)
ondisk_mtime = os.path.getmtime(filename)
entry = existing_cache.get(title)
if entry:
cached_mtime = entry.get("mtime", 0)
if ondisk_mtime > cached_mtime:
entry = None
if not entry or 'value' not in entry:
value = func()
if value is None:
return None
entry = {'mtime': ondisk_mtime, 'value': value}
existing_cache[title] = entry
dump_cache()
return entry['value']
+21 -9
View File
@@ -1,10 +1,10 @@
from functools import wraps
import html import html
import threading
import time import time
from modules import shared, progress, errors from modules import shared, progress, errors, devices, fifo_lock
queue_lock = threading.Lock() queue_lock = fifo_lock.FIFOLock()
def wrap_queued_call(func): def wrap_queued_call(func):
@@ -18,6 +18,7 @@ def wrap_queued_call(func):
def wrap_gradio_gpu_call(func, extra_outputs=None): def wrap_gradio_gpu_call(func, extra_outputs=None):
@wraps(func)
def f(*args, **kwargs): def f(*args, **kwargs):
# if the first argument is a string that says "task(...)", it is treated as a job id # if the first argument is a string that says "task(...)", it is treated as a job id
@@ -28,7 +29,7 @@ def wrap_gradio_gpu_call(func, extra_outputs=None):
id_task = None id_task = None
with queue_lock: with queue_lock:
shared.state.begin() shared.state.begin(job=id_task)
progress.start_task(id_task) progress.start_task(id_task)
try: try:
@@ -45,6 +46,7 @@ def wrap_gradio_gpu_call(func, extra_outputs=None):
def wrap_gradio_call(func, extra_outputs=None, add_stats=False): def wrap_gradio_call(func, extra_outputs=None, add_stats=False):
@wraps(func)
def f(*args, extra_outputs_array=extra_outputs, **kwargs): def f(*args, extra_outputs_array=extra_outputs, **kwargs):
run_memmon = shared.opts.memmon_poll_rate > 0 and not shared.mem_mon.disabled and add_stats run_memmon = shared.opts.memmon_poll_rate > 0 and not shared.mem_mon.disabled and add_stats
if run_memmon: if run_memmon:
@@ -72,6 +74,8 @@ def wrap_gradio_call(func, extra_outputs=None, add_stats=False):
error_message = f'{type(e).__name__}: {e}' error_message = f'{type(e).__name__}: {e}'
res = extra_outputs_array + [f"<div class='error'>{html.escape(error_message)}</div>"] res = extra_outputs_array + [f"<div class='error'>{html.escape(error_message)}</div>"]
devices.torch_gc()
shared.state.skipped = False shared.state.skipped = False
shared.state.interrupted = False shared.state.interrupted = False
shared.state.job_count = 0 shared.state.job_count = 0
@@ -82,9 +86,9 @@ def wrap_gradio_call(func, extra_outputs=None, add_stats=False):
elapsed = time.perf_counter() - t elapsed = time.perf_counter() - t
elapsed_m = int(elapsed // 60) elapsed_m = int(elapsed // 60)
elapsed_s = elapsed % 60 elapsed_s = elapsed % 60
elapsed_text = f"{elapsed_s:.2f}s" elapsed_text = f"{elapsed_s:.1f} sec."
if elapsed_m > 0: if elapsed_m > 0:
elapsed_text = f"{elapsed_m}m "+elapsed_text elapsed_text = f"{elapsed_m} min. "+elapsed_text
if run_memmon: if run_memmon:
mem_stats = {k: -(v//-(1024*1024)) for k, v in shared.mem_mon.stop().items()} mem_stats = {k: -(v//-(1024*1024)) for k, v in shared.mem_mon.stop().items()}
@@ -92,14 +96,22 @@ def wrap_gradio_call(func, extra_outputs=None, add_stats=False):
reserved_peak = mem_stats['reserved_peak'] reserved_peak = mem_stats['reserved_peak']
sys_peak = mem_stats['system_peak'] sys_peak = mem_stats['system_peak']
sys_total = mem_stats['total'] sys_total = mem_stats['total']
sys_pct = round(sys_peak/max(sys_total, 1) * 100, 2) sys_pct = sys_peak/max(sys_total, 1) * 100
vram_html = f"<p class='vram'>Torch active/reserved: {active_peak}/{reserved_peak} MiB, <wbr>Sys VRAM: {sys_peak}/{sys_total} MiB ({sys_pct}%)</p>" toltip_a = "Active: peak amount of video memory used during generation (excluding cached data)"
toltip_r = "Reserved: total amout of video memory allocated by the Torch library "
toltip_sys = "System: peak amout of video memory allocated by all running programs, out of total capacity"
text_a = f"<abbr title='{toltip_a}'>A</abbr>: <span class='measurement'>{active_peak/1024:.2f} GB</span>"
text_r = f"<abbr title='{toltip_r}'>R</abbr>: <span class='measurement'>{reserved_peak/1024:.2f} GB</span>"
text_sys = f"<abbr title='{toltip_sys}'>Sys</abbr>: <span class='measurement'>{sys_peak/1024:.1f}/{sys_total/1024:g} GB</span> ({sys_pct:.1f}%)"
vram_html = f"<p class='vram'>{text_a}, <wbr>{text_r}, <wbr>{text_sys}</p>"
else: else:
vram_html = '' vram_html = ''
# last item is always HTML # last item is always HTML
res[-1] += f"<div class='performance'><p class='time'>Time taken: <wbr>{elapsed_text}</p>{vram_html}</div>" res[-1] += f"<div class='performance'><p class='time'>Time taken: <wbr><span class='measurement'>{elapsed_text}</span></p>{vram_html}</div>"
return tuple(res) return tuple(res)
+16 -5
View File
@@ -13,8 +13,12 @@ parser.add_argument("--reinstall-xformers", action='store_true', help="launch.py
parser.add_argument("--reinstall-torch", action='store_true', help="launch.py argument: install the appropriate version of torch even if you have some version already installed") parser.add_argument("--reinstall-torch", action='store_true', help="launch.py argument: install the appropriate version of torch even if you have some version already installed")
parser.add_argument("--update-check", action='store_true', help="launch.py argument: check for updates at startup") parser.add_argument("--update-check", action='store_true', help="launch.py argument: check for updates at startup")
parser.add_argument("--test-server", action='store_true', help="launch.py argument: configure server for testing") parser.add_argument("--test-server", action='store_true', help="launch.py argument: configure server for testing")
parser.add_argument("--log-startup", action='store_true', help="launch.py argument: print a detailed log of what's happening at startup")
parser.add_argument("--skip-prepare-environment", action='store_true', help="launch.py argument: skip all environment preparation") parser.add_argument("--skip-prepare-environment", action='store_true', help="launch.py argument: skip all environment preparation")
parser.add_argument("--skip-install", action='store_true', help="launch.py argument: skip installation of packages") parser.add_argument("--skip-install", action='store_true', help="launch.py argument: skip installation of packages")
parser.add_argument("--dump-sysinfo", action='store_true', help="launch.py argument: dump limited sysinfo file (without information about extensions, options) to disk and quit")
parser.add_argument("--loglevel", type=str, help="log level; one of: CRITICAL, ERROR, WARNING, INFO, DEBUG", default=None)
parser.add_argument("--do-not-download-clip", action='store_true', help="do not download CLIP model even if it's not included in the checkpoint")
parser.add_argument("--data-dir", type=str, default=os.path.dirname(os.path.dirname(os.path.realpath(__file__))), help="base path where all user data is stored") parser.add_argument("--data-dir", type=str, default=os.path.dirname(os.path.dirname(os.path.realpath(__file__))), help="base path where all user data is stored")
parser.add_argument("--config", type=str, default=sd_default_config, help="path to config which constructs model",) parser.add_argument("--config", type=str, default=sd_default_config, help="path to config which constructs model",)
parser.add_argument("--ckpt", type=str, default=sd_model_file, help="path to checkpoint of stable diffusion model; if specified, this checkpoint will be added to the list of checkpoints and loaded",) parser.add_argument("--ckpt", type=str, default=sd_model_file, help="path to checkpoint of stable diffusion model; if specified, this checkpoint will be added to the list of checkpoints and loaded",)
@@ -32,9 +36,10 @@ parser.add_argument("--hypernetwork-dir", type=str, default=os.path.join(models_
parser.add_argument("--localizations-dir", type=str, default=os.path.join(script_path, 'localizations'), help="localizations directory") parser.add_argument("--localizations-dir", type=str, default=os.path.join(script_path, 'localizations'), help="localizations directory")
parser.add_argument("--allow-code", action='store_true', help="allow custom script execution from webui") parser.add_argument("--allow-code", action='store_true', help="allow custom script execution from webui")
parser.add_argument("--medvram", action='store_true', help="enable stable diffusion model optimizations for sacrificing a little speed for low VRM usage") parser.add_argument("--medvram", action='store_true', help="enable stable diffusion model optimizations for sacrificing a little speed for low VRM usage")
parser.add_argument("--medvram-sdxl", action='store_true', help="enable --medvram optimization just for SDXL models")
parser.add_argument("--lowvram", action='store_true', help="enable stable diffusion model optimizations for sacrificing a lot of speed for very low VRM usage") parser.add_argument("--lowvram", action='store_true', help="enable stable diffusion model optimizations for sacrificing a lot of speed for very low VRM usage")
parser.add_argument("--lowram", action='store_true', help="load stable diffusion checkpoint weights to VRAM instead of RAM") parser.add_argument("--lowram", action='store_true', help="load stable diffusion checkpoint weights to VRAM instead of RAM")
parser.add_argument("--always-batch-cond-uncond", action='store_true', help="disables cond/uncond batching that is enabled to save memory with --medvram or --lowvram") parser.add_argument("--always-batch-cond-uncond", action='store_true', help="does not do anything")
parser.add_argument("--unload-gfpgan", action='store_true', help="does not do anything.") parser.add_argument("--unload-gfpgan", action='store_true', help="does not do anything.")
parser.add_argument("--precision", type=str, help="evaluate at this precision", choices=["full", "autocast"], default="autocast") parser.add_argument("--precision", type=str, help="evaluate at this precision", choices=["full", "autocast"], default="autocast")
parser.add_argument("--upcast-sampling", action='store_true', help="upcast sampling. No effect with --no-half. Usually produces similar results to --no-half with better performance while using less memory.") parser.add_argument("--upcast-sampling", action='store_true', help="upcast sampling. No effect with --no-half. Usually produces similar results to --no-half with better performance while using less memory.")
@@ -65,6 +70,7 @@ parser.add_argument("--opt-sdp-no-mem-attention", action='store_true', help="pre
parser.add_argument("--disable-opt-split-attention", action='store_true', help="prefer no cross-attention layer optimization for automatic choice of optimization") parser.add_argument("--disable-opt-split-attention", action='store_true', help="prefer no cross-attention layer optimization for automatic choice of optimization")
parser.add_argument("--disable-nan-check", action='store_true', help="do not check if produced images/latent spaces have nans; useful for running without a checkpoint in CI") parser.add_argument("--disable-nan-check", action='store_true', help="do not check if produced images/latent spaces have nans; useful for running without a checkpoint in CI")
parser.add_argument("--use-cpu", nargs='+', help="use CPU as torch device for specified modules", default=[], type=str.lower) parser.add_argument("--use-cpu", nargs='+', help="use CPU as torch device for specified modules", default=[], type=str.lower)
parser.add_argument("--disable-model-loading-ram-optimization", action='store_true', help="disable an optimization that reduces RAM use when loading a model")
parser.add_argument("--listen", action='store_true', help="launch gradio with 0.0.0.0 as server name, allowing to respond to network requests") parser.add_argument("--listen", action='store_true', help="launch gradio with 0.0.0.0 as server name, allowing to respond to network requests")
parser.add_argument("--port", type=int, help="launch gradio with given server port, you need root/admin rights for ports < 1024, defaults to 7860 if available", default=None) parser.add_argument("--port", type=int, help="launch gradio with given server port, you need root/admin rights for ports < 1024, defaults to 7860 if available", default=None)
parser.add_argument("--show-negative-prompt", action='store_true', help="does not do anything", default=False) parser.add_argument("--show-negative-prompt", action='store_true', help="does not do anything", default=False)
@@ -77,14 +83,14 @@ parser.add_argument("--gradio-auth", type=str, help='set gradio authentication l
parser.add_argument("--gradio-auth-path", type=str, help='set gradio authentication file path ex. "/path/to/auth/file" same auth format as --gradio-auth', default=None) parser.add_argument("--gradio-auth-path", type=str, help='set gradio authentication file path ex. "/path/to/auth/file" same auth format as --gradio-auth', default=None)
parser.add_argument("--gradio-img2img-tool", type=str, help='does not do anything') parser.add_argument("--gradio-img2img-tool", type=str, help='does not do anything')
parser.add_argument("--gradio-inpaint-tool", type=str, help="does not do anything") parser.add_argument("--gradio-inpaint-tool", type=str, help="does not do anything")
parser.add_argument("--gradio-allowed-path", action='append', help="add path to gradio's allowed_paths, make it possible to serve files from it") parser.add_argument("--gradio-allowed-path", action='append', help="add path to gradio's allowed_paths, make it possible to serve files from it", default=[data_path])
parser.add_argument("--opt-channelslast", action='store_true', help="change memory type for stable diffusion to channels last") parser.add_argument("--opt-channelslast", action='store_true', help="change memory type for stable diffusion to channels last")
parser.add_argument("--styles-file", type=str, help="filename to use for styles", default=os.path.join(data_path, 'styles.csv')) parser.add_argument("--styles-file", type=str, help="filename to use for styles", default=os.path.join(data_path, 'styles.csv'))
parser.add_argument("--autolaunch", action='store_true', help="open the webui URL in the system's default browser upon launch", default=False) parser.add_argument("--autolaunch", action='store_true', help="open the webui URL in the system's default browser upon launch", default=False)
parser.add_argument("--theme", type=str, help="launches the UI with light or dark theme", default=None) parser.add_argument("--theme", type=str, help="launches the UI with light or dark theme", default=None)
parser.add_argument("--use-textbox-seed", action='store_true', help="use textbox for seeds in UI (no up/down, but possible to input long seeds)", default=False) parser.add_argument("--use-textbox-seed", action='store_true', help="use textbox for seeds in UI (no up/down, but possible to input long seeds)", default=False)
parser.add_argument("--disable-console-progressbars", action='store_true', help="do not output progressbars to console", default=False) parser.add_argument("--disable-console-progressbars", action='store_true', help="do not output progressbars to console", default=False)
parser.add_argument("--enable-console-prompts", action='store_true', help="print prompts to console when generating with txt2img and img2img", default=False) parser.add_argument("--enable-console-prompts", action='store_true', help="does not do anything", default=False) # Legacy compatibility, use as default value shared.opts.enable_console_prompts
parser.add_argument('--vae-path', type=str, help='Checkpoint to use as VAE; setting this argument disables all settings related to VAE', default=None) parser.add_argument('--vae-path', type=str, help='Checkpoint to use as VAE; setting this argument disables all settings related to VAE', default=None)
parser.add_argument("--disable-safe-unpickle", action='store_true', help="disable checking pytorch models for malicious code", default=False) parser.add_argument("--disable-safe-unpickle", action='store_true', help="disable checking pytorch models for malicious code", default=False)
parser.add_argument("--api", action='store_true', help="use api=True to launch the API together with the webui (use --nowebui instead for only the API)") parser.add_argument("--api", action='store_true', help="use api=True to launch the API together with the webui (use --nowebui instead for only the API)")
@@ -101,9 +107,14 @@ parser.add_argument("--tls-certfile", type=str, help="Partially enables TLS, req
parser.add_argument("--disable-tls-verify", action="store_false", help="When passed, enables the use of self-signed certificates.", default=None) parser.add_argument("--disable-tls-verify", action="store_false", help="When passed, enables the use of self-signed certificates.", default=None)
parser.add_argument("--server-name", type=str, help="Sets hostname of server", default=None) parser.add_argument("--server-name", type=str, help="Sets hostname of server", default=None)
parser.add_argument("--gradio-queue", action='store_true', help="does not do anything", default=True) parser.add_argument("--gradio-queue", action='store_true', help="does not do anything", default=True)
parser.add_argument("--no-gradio-queue", action='store_true', help="Disables gradio queue; causes the webpage to use http requests instead of websockets; was the defaul in earlier versions") parser.add_argument("--no-gradio-queue", action='store_true', help="Disables gradio queue; causes the webpage to use http requests instead of websockets; was the default in earlier versions")
parser.add_argument("--skip-version-check", action='store_true', help="Do not check versions of torch and xformers") parser.add_argument("--skip-version-check", action='store_true', help="Do not check versions of torch and xformers")
parser.add_argument("--no-hashing", action='store_true', help="disable sha256 hashing of checkpoints to help loading performance", default=False) parser.add_argument("--no-hashing", action='store_true', help="disable sha256 hashing of checkpoints to help loading performance", default=False)
parser.add_argument("--no-download-sd-model", action='store_true', help="don't download SD1.5 model even if no model is found in --ckpt-dir", default=False) parser.add_argument("--no-download-sd-model", action='store_true', help="don't download SD1.5 model even if no model is found in --ckpt-dir", default=False)
parser.add_argument('--subpath', type=str, help='customize the subpath for gradio, use with reverse proxy') parser.add_argument('--subpath', type=str, help='customize the subpath for gradio, use with reverse proxy')
parser.add_argument('--add-stop-route', action='store_true', help='add /_stop route to stop server') parser.add_argument('--add-stop-route', action='store_true', help='does not do anything')
parser.add_argument('--api-server-stop', action='store_true', help='enable server stop/restart/kill via api')
parser.add_argument('--timeout-keep-alive', type=int, default=30, help='set timeout_keep_alive for uvicorn')
parser.add_argument("--disable-all-extensions", action='store_true', help="prevent all extensions from running regardless of any other settings", default=False)
parser.add_argument("--disable-extra-extensions", action='store_true', help="prevent all extensions except built-in from running regardless of any other settings", default=False)
parser.add_argument("--skip-load-model-at-start", action='store_true', help="if load a model at web start, only take effect when --nowebui", )
+2 -8
View File
@@ -15,14 +15,11 @@ model_dir = "Codeformer"
model_path = os.path.join(models_path, model_dir) model_path = os.path.join(models_path, model_dir)
model_url = 'https://github.com/sczhou/CodeFormer/releases/download/v0.1.0/codeformer.pth' model_url = 'https://github.com/sczhou/CodeFormer/releases/download/v0.1.0/codeformer.pth'
have_codeformer = False
codeformer = None codeformer = None
def setup_model(dirname): def setup_model(dirname):
global model_path os.makedirs(model_path, exist_ok=True)
if not os.path.exists(model_path):
os.makedirs(model_path)
path = modules.paths.paths.get("CodeFormer", None) path = modules.paths.paths.get("CodeFormer", None)
if path is None: if path is None:
@@ -102,7 +99,7 @@ def setup_model(dirname):
output = self.net(cropped_face_t, w=w if w is not None else shared.opts.code_former_weight, adain=True)[0] output = self.net(cropped_face_t, w=w if w is not None else shared.opts.code_former_weight, adain=True)[0]
restored_face = tensor2img(output, rgb2bgr=True, min_max=(-1, 1)) restored_face = tensor2img(output, rgb2bgr=True, min_max=(-1, 1))
del output del output
torch.cuda.empty_cache() devices.torch_gc()
except Exception: except Exception:
errors.report('Failed inference for CodeFormer', exc_info=True) errors.report('Failed inference for CodeFormer', exc_info=True)
restored_face = tensor2img(cropped_face_t, rgb2bgr=True, min_max=(-1, 1)) restored_face = tensor2img(cropped_face_t, rgb2bgr=True, min_max=(-1, 1))
@@ -125,9 +122,6 @@ def setup_model(dirname):
return restored_img return restored_img
global have_codeformer
have_codeformer = True
global codeformer global codeformer
codeformer = FaceRestorerCodeFormer(dirname) codeformer = FaceRestorerCodeFormer(dirname)
shared.face_restorers.append(codeformer) shared.face_restorers.append(codeformer)
+6 -5
View File
@@ -4,18 +4,15 @@ Supports saving and restoring webui and extensions from a known working set of c
import os import os
import json import json
import time
import tqdm import tqdm
from datetime import datetime from datetime import datetime
from collections import OrderedDict
import git import git
from modules import shared, extensions, errors from modules import shared, extensions, errors
from modules.paths_internal import script_path, config_states_dir from modules.paths_internal import script_path, config_states_dir
all_config_states = {}
all_config_states = OrderedDict()
def list_config_states(): def list_config_states():
@@ -28,15 +25,19 @@ def list_config_states():
for filename in os.listdir(config_states_dir): for filename in os.listdir(config_states_dir):
if filename.endswith(".json"): if filename.endswith(".json"):
path = os.path.join(config_states_dir, filename) path = os.path.join(config_states_dir, filename)
try:
with open(path, "r", encoding="utf-8") as f: with open(path, "r", encoding="utf-8") as f:
j = json.load(f) j = json.load(f)
assert "created_at" in j, '"created_at" does not exist'
j["filepath"] = path j["filepath"] = path
config_states.append(j) config_states.append(j)
except Exception as e:
print(f'[ERROR]: Config states {path}, {e}')
config_states = sorted(config_states, key=lambda cs: cs["created_at"], reverse=True) config_states = sorted(config_states, key=lambda cs: cs["created_at"], reverse=True)
for cs in config_states: for cs in config_states:
timestamp = time.asctime(time.gmtime(cs["created_at"])) timestamp = datetime.fromtimestamp(cs["created_at"]).strftime('%Y-%m-%d %H:%M:%S')
name = cs.get("name", "Config") name = cs.get("name", "Config")
full_name = f"{name}: {timestamp}" full_name = f"{name}: {timestamp}"
all_config_states[full_name] = cs all_config_states[full_name] = cs
+18 -38
View File
@@ -3,7 +3,7 @@ import contextlib
from functools import lru_cache from functools import lru_cache
import torch import torch
from modules import errors from modules import errors, shared
if sys.platform == "darwin": if sys.platform == "darwin":
from modules import mac_specific from modules import mac_specific
@@ -15,17 +15,8 @@ def has_mps() -> bool:
else: else:
return mac_specific.has_mps return mac_specific.has_mps
def extract_device_id(args, name):
for x in range(len(args)):
if name in args[x]:
return args[x + 1]
return None
def get_cuda_device_string(): def get_cuda_device_string():
from modules import shared
if shared.cmd_opts.device_id is not None: if shared.cmd_opts.device_id is not None:
return f"cuda:{shared.cmd_opts.device_id}" return f"cuda:{shared.cmd_opts.device_id}"
@@ -47,8 +38,6 @@ def get_optimal_device():
def get_device_for(task): def get_device_for(task):
from modules import shared
if task in shared.cmd_opts.use_cpu: if task in shared.cmd_opts.use_cpu:
return cpu return cpu
@@ -56,32 +45,40 @@ def get_device_for(task):
def torch_gc(): def torch_gc():
if torch.cuda.is_available(): if torch.cuda.is_available():
with torch.cuda.device(get_cuda_device_string()): with torch.cuda.device(get_cuda_device_string()):
torch.cuda.empty_cache() torch.cuda.empty_cache()
torch.cuda.ipc_collect() torch.cuda.ipc_collect()
if has_mps():
mac_specific.torch_mps_gc()
def enable_tf32(): def enable_tf32():
if torch.cuda.is_available(): if torch.cuda.is_available():
# enabling benchmark option seems to enable a range of cards to do fp16 when they otherwise can't # enabling benchmark option seems to enable a range of cards to do fp16 when they otherwise can't
# see https://github.com/AUTOMATIC1111/stable-diffusion-webui/pull/4407 # see https://github.com/AUTOMATIC1111/stable-diffusion-webui/pull/4407
if any(torch.cuda.get_device_capability(devid) == (7, 5) for devid in range(0, torch.cuda.device_count())): device_id = (int(shared.cmd_opts.device_id) if shared.cmd_opts.device_id is not None and shared.cmd_opts.device_id.isdigit() else 0) or torch.cuda.current_device()
if torch.cuda.get_device_capability(device_id) == (7, 5) and torch.cuda.get_device_name(device_id).startswith("NVIDIA GeForce GTX 16"):
torch.backends.cudnn.benchmark = True torch.backends.cudnn.benchmark = True
torch.backends.cuda.matmul.allow_tf32 = True torch.backends.cuda.matmul.allow_tf32 = True
torch.backends.cudnn.allow_tf32 = True torch.backends.cudnn.allow_tf32 = True
errors.run(enable_tf32, "Enabling TF32") errors.run(enable_tf32, "Enabling TF32")
cpu = torch.device("cpu") cpu: torch.device = torch.device("cpu")
device = device_interrogate = device_gfpgan = device_esrgan = device_codeformer = None device: torch.device = None
dtype = torch.float16 device_interrogate: torch.device = None
dtype_vae = torch.float16 device_gfpgan: torch.device = None
dtype_unet = torch.float16 device_esrgan: torch.device = None
device_codeformer: torch.device = None
dtype: torch.dtype = torch.float16
dtype_vae: torch.dtype = torch.float16
dtype_unet: torch.dtype = torch.float16
unet_needs_upcast = False unet_needs_upcast = False
@@ -93,26 +90,10 @@ def cond_cast_float(input):
return input.float() if unet_needs_upcast else input return input.float() if unet_needs_upcast else input
def randn(seed, shape): nv_rng = None
from modules.shared import opts
torch.manual_seed(seed)
if opts.randn_source == "CPU" or device.type == 'mps':
return torch.randn(shape, device=cpu).to(device)
return torch.randn(shape, device=device)
def randn_without_seed(shape):
from modules.shared import opts
if opts.randn_source == "CPU" or device.type == 'mps':
return torch.randn(shape, device=cpu).to(device)
return torch.randn(shape, device=device)
def autocast(disable=False): def autocast(disable=False):
from modules import shared
if disable: if disable:
return contextlib.nullcontext() return contextlib.nullcontext()
@@ -131,8 +112,6 @@ class NansException(Exception):
def test_for_nans(x, where): def test_for_nans(x, where):
from modules import shared
if shared.cmd_opts.disable_nan_check: if shared.cmd_opts.disable_nan_check:
return return
@@ -172,3 +151,4 @@ def first_time_calculation():
x = torch.zeros((1, 1, 3, 3)).to(device, dtype) x = torch.zeros((1, 1, 3, 3)).to(device, dtype)
conv2d = torch.nn.Conv2d(1, 1, (3, 3)).to(device, dtype) conv2d = torch.nn.Conv2d(1, 1, (3, 3)).to(device, dtype)
conv2d(x) conv2d(x)
+66 -1
View File
@@ -6,6 +6,21 @@ import traceback
exception_records = [] exception_records = []
def format_traceback(tb):
return [[f"{x.filename}, line {x.lineno}, {x.name}", x.line] for x in traceback.extract_tb(tb)]
def format_exception(e, tb):
return {"exception": str(e), "traceback": format_traceback(tb)}
def get_exceptions():
try:
return list(reversed(exception_records))
except Exception as e:
return str(e)
def record_exception(): def record_exception():
_, e, tb = sys.exc_info() _, e, tb = sys.exc_info()
if e is None: if e is None:
@@ -14,7 +29,7 @@ def record_exception():
if exception_records and exception_records[-1] == e: if exception_records and exception_records[-1] == e:
return return
exception_records.append((e, tb)) exception_records.append(format_exception(e, tb))
if len(exception_records) > 5: if len(exception_records) > 5:
exception_records.pop(0) exception_records.pop(0)
@@ -83,3 +98,53 @@ def run(code, task):
code() code()
except Exception as e: except Exception as e:
display(task, e) display(task, e)
def check_versions():
from packaging import version
from modules import shared
import torch
import gradio
expected_torch_version = "2.0.0"
expected_xformers_version = "0.0.20"
expected_gradio_version = "3.41.2"
if version.parse(torch.__version__) < version.parse(expected_torch_version):
print_error_explanation(f"""
You are running torch {torch.__version__}.
The program is tested to work with torch {expected_torch_version}.
To reinstall the desired version, run with commandline flag --reinstall-torch.
Beware that this will cause a lot of large files to be downloaded, as well as
there are reports of issues with training tab on the latest version.
Use --skip-version-check commandline argument to disable this check.
""".strip())
if shared.xformers_available:
import xformers
if version.parse(xformers.__version__) < version.parse(expected_xformers_version):
print_error_explanation(f"""
You are running xformers {xformers.__version__}.
The program is tested to work with xformers {expected_xformers_version}.
To reinstall the desired version, run with commandline flag --reinstall-xformers.
Use --skip-version-check commandline argument to disable this check.
""".strip())
if gradio.__version__ != expected_gradio_version:
print_error_explanation(f"""
You are running gradio {gradio.__version__}.
The program is designed to work with gradio {expected_gradio_version}.
Using a different version of gradio is extremely likely to break the program.
Reasons why you have the mismatched gradio version can be:
- you use --skip-install flag.
- you use webui.py to start the program instead of launch.py.
- an extension installs the incompatible gradio version.
Use --skip-version-check commandline argument to disable this check.
""".strip())
+9 -12
View File
@@ -1,15 +1,13 @@
import os import sys
import numpy as np import numpy as np
import torch import torch
from PIL import Image from PIL import Image
from basicsr.utils.download_util import load_file_from_url
import modules.esrgan_model_arch as arch import modules.esrgan_model_arch as arch
from modules import modelloader, images, devices from modules import modelloader, images, devices
from modules.upscaler import Upscaler, UpscalerData
from modules.shared import opts from modules.shared import opts
from modules.upscaler import Upscaler, UpscalerData
def mod2normal(state_dict): def mod2normal(state_dict):
@@ -134,7 +132,7 @@ class UpscalerESRGAN(Upscaler):
scaler_data = UpscalerData(self.model_name, self.model_url, self, 4) scaler_data = UpscalerData(self.model_name, self.model_url, self, 4)
scalers.append(scaler_data) scalers.append(scaler_data)
for file in model_paths: for file in model_paths:
if "http" in file: if file.startswith("http"):
name = self.model_name name = self.model_name
else: else:
name = modelloader.friendly_name(file) name = modelloader.friendly_name(file)
@@ -143,26 +141,25 @@ class UpscalerESRGAN(Upscaler):
self.scalers.append(scaler_data) self.scalers.append(scaler_data)
def do_upscale(self, img, selected_model): def do_upscale(self, img, selected_model):
try:
model = self.load_model(selected_model) model = self.load_model(selected_model)
if model is None: except Exception as e:
print(f"Unable to load ESRGAN model {selected_model}: {e}", file=sys.stderr)
return img return img
model.to(devices.device_esrgan) model.to(devices.device_esrgan)
img = esrgan_upscale(model, img) img = esrgan_upscale(model, img)
return img return img
def load_model(self, path: str): def load_model(self, path: str):
if "http" in path: if path.startswith("http"):
filename = load_file_from_url( # TODO: this doesn't use `path` at all?
filename = modelloader.load_file_from_url(
url=self.model_url, url=self.model_url,
model_dir=self.model_download_path, model_dir=self.model_download_path,
file_name=f"{self.model_name}.pth", file_name=f"{self.model_name}.pth",
progress=True,
) )
else: else:
filename = path filename = path
if not os.path.exists(filename) or filename is None:
print(f"Unable to load {self.model_path} from {filename}")
return None
state_dict = torch.load(filename, map_location='cpu' if devices.device_esrgan.type == 'mps' else None) state_dict = torch.load(filename, map_location='cpu' if devices.device_esrgan.type == 'mps' else None)
+109 -20
View File
@@ -1,29 +1,76 @@
from __future__ import annotations
import configparser
import os import os
import threading import threading
import re
from modules import shared, errors from modules import shared, errors, cache, scripts
from modules.gitpython_hack import Repo from modules.gitpython_hack import Repo
from modules.paths_internal import extensions_dir, extensions_builtin_dir, script_path # noqa: F401 from modules.paths_internal import extensions_dir, extensions_builtin_dir, script_path # noqa: F401
extensions = []
if not os.path.exists(extensions_dir): os.makedirs(extensions_dir, exist_ok=True)
os.makedirs(extensions_dir)
def active(): def active():
if shared.opts.disable_all_extensions == "all": if shared.cmd_opts.disable_all_extensions or shared.opts.disable_all_extensions == "all":
return [] return []
elif shared.opts.disable_all_extensions == "extra": elif shared.cmd_opts.disable_extra_extensions or shared.opts.disable_all_extensions == "extra":
return [x for x in extensions if x.enabled and x.is_builtin] return [x for x in extensions if x.enabled and x.is_builtin]
else: else:
return [x for x in extensions if x.enabled] return [x for x in extensions if x.enabled]
class ExtensionMetadata:
filename = "metadata.ini"
config: configparser.ConfigParser
canonical_name: str
requires: list
def __init__(self, path, canonical_name):
self.config = configparser.ConfigParser()
filepath = os.path.join(path, self.filename)
if os.path.isfile(filepath):
try:
self.config.read(filepath)
except Exception:
errors.report(f"Error reading {self.filename} for extension {canonical_name}.", exc_info=True)
self.canonical_name = self.config.get("Extension", "Name", fallback=canonical_name)
self.canonical_name = canonical_name.lower().strip()
self.requires = self.get_script_requirements("Requires", "Extension")
def get_script_requirements(self, field, section, extra_section=None):
"""reads a list of requirements from the config; field is the name of the field in the ini file,
like Requires or Before, and section is the name of the [section] in the ini file; additionally,
reads more requirements from [extra_section] if specified."""
x = self.config.get(section, field, fallback='')
if extra_section:
x = x + ', ' + self.config.get(extra_section, field, fallback='')
return self.parse_list(x.lower())
def parse_list(self, text):
"""converts a line from config ("ext1 ext2, ext3 ") into a python list (["ext1", "ext2", "ext3"])"""
if not text:
return []
# both "," and " " are accepted as separator
return [x for x in re.split(r"[,\s]+", text.strip()) if x]
class Extension: class Extension:
lock = threading.Lock() lock = threading.Lock()
cached_fields = ['remote', 'commit_date', 'branch', 'commit_hash', 'version']
metadata: ExtensionMetadata
def __init__(self, name, path, enabled=True, is_builtin=False): def __init__(self, name, path, enabled=True, is_builtin=False, metadata=None):
self.name = name self.name = name
self.path = path self.path = path
self.enabled = enabled self.enabled = enabled
@@ -36,17 +83,36 @@ class Extension:
self.branch = None self.branch = None
self.remote = None self.remote = None
self.have_info_from_repo = False self.have_info_from_repo = False
self.metadata = metadata if metadata else ExtensionMetadata(self.path, name.lower())
self.canonical_name = metadata.canonical_name
def to_dict(self):
return {x: getattr(self, x) for x in self.cached_fields}
def from_dict(self, d):
for field in self.cached_fields:
setattr(self, field, d[field])
def read_info_from_repo(self): def read_info_from_repo(self):
if self.is_builtin or self.have_info_from_repo: if self.is_builtin or self.have_info_from_repo:
return return
def read_from_repo():
with self.lock: with self.lock:
if self.have_info_from_repo: if self.have_info_from_repo:
return return
self.do_read_info_from_repo() self.do_read_info_from_repo()
return self.to_dict()
try:
d = cache.cached_data_for_file('extensions-git', self.name, os.path.join(self.path, ".git"), read_from_repo)
self.from_dict(d)
except FileNotFoundError:
pass
self.status = 'unknown' if self.status == '' else self.status
def do_read_info_from_repo(self): def do_read_info_from_repo(self):
repo = None repo = None
try: try:
@@ -59,7 +125,6 @@ class Extension:
self.remote = None self.remote = None
else: else:
try: try:
self.status = 'unknown'
self.remote = next(repo.remote().urls, None) self.remote = next(repo.remote().urls, None)
commit = repo.head.commit commit = repo.head.commit
self.commit_date = commit.committed_date self.commit_date = commit.committed_date
@@ -75,8 +140,6 @@ class Extension:
self.have_info_from_repo = True self.have_info_from_repo = True
def list_files(self, subdir, extension): def list_files(self, subdir, extension):
from modules import scripts
dirpath = os.path.join(self.path, subdir) dirpath = os.path.join(self.path, subdir)
if not os.path.isdir(dirpath): if not os.path.isdir(dirpath):
return [] return []
@@ -123,26 +186,52 @@ class Extension:
def list_extensions(): def list_extensions():
extensions.clear() extensions.clear()
if not os.path.isdir(extensions_dir): if shared.cmd_opts.disable_all_extensions:
return print("*** \"--disable-all-extensions\" arg was used, will not load any extensions ***")
elif shared.opts.disable_all_extensions == "all":
if shared.opts.disable_all_extensions == "all":
print("*** \"Disable all extensions\" option was set, will not load any extensions ***") print("*** \"Disable all extensions\" option was set, will not load any extensions ***")
elif shared.cmd_opts.disable_extra_extensions:
print("*** \"--disable-extra-extensions\" arg was used, will only load built-in extensions ***")
elif shared.opts.disable_all_extensions == "extra": elif shared.opts.disable_all_extensions == "extra":
print("*** \"Disable all extensions\" option was set, will only load built-in extensions ***") print("*** \"Disable all extensions\" option was set, will only load built-in extensions ***")
extension_paths = [] loaded_extensions = {}
for dirname in [extensions_dir, extensions_builtin_dir]:
# scan through extensions directory and load metadata
for dirname in [extensions_builtin_dir, extensions_dir]:
if not os.path.isdir(dirname): if not os.path.isdir(dirname):
return continue
for extension_dirname in sorted(os.listdir(dirname)): for extension_dirname in sorted(os.listdir(dirname)):
path = os.path.join(dirname, extension_dirname) path = os.path.join(dirname, extension_dirname)
if not os.path.isdir(path): if not os.path.isdir(path):
continue continue
extension_paths.append((extension_dirname, path, dirname == extensions_builtin_dir)) canonical_name = extension_dirname
metadata = ExtensionMetadata(path, canonical_name)
for dirname, path, is_builtin in extension_paths: # check for duplicated canonical names
extension = Extension(name=dirname, path=path, enabled=dirname not in shared.opts.disabled_extensions, is_builtin=is_builtin) already_loaded_extension = loaded_extensions.get(metadata.canonical_name)
if already_loaded_extension is not None:
errors.report(f'Duplicate canonical name "{canonical_name}" found in extensions "{extension_dirname}" and "{already_loaded_extension.name}". Former will be discarded.', exc_info=False)
continue
is_builtin = dirname == extensions_builtin_dir
extension = Extension(name=extension_dirname, path=path, enabled=extension_dirname not in shared.opts.disabled_extensions, is_builtin=is_builtin, metadata=metadata)
extensions.append(extension) extensions.append(extension)
loaded_extensions[canonical_name] = extension
# check for requirements
for extension in extensions:
for req in extension.metadata.requires:
required_extension = loaded_extensions.get(req)
if required_extension is None:
errors.report(f'Extension "{extension.name}" requires "{req}" which is not installed.', exc_info=False)
continue
if not extension.enabled:
errors.report(f'Extension "{extension.name}" requires "{required_extension.name}" which is disabled.', exc_info=False)
continue
extensions: list[Extension] = []
+75 -15
View File
@@ -1,19 +1,28 @@
import json
import os
import re import re
import logging
from collections import defaultdict from collections import defaultdict
from modules import errors from modules import errors
extra_network_registry = {} extra_network_registry = {}
extra_network_aliases = {}
def initialize(): def initialize():
extra_network_registry.clear() extra_network_registry.clear()
extra_network_aliases.clear()
def register_extra_network(extra_network): def register_extra_network(extra_network):
extra_network_registry[extra_network.name] = extra_network extra_network_registry[extra_network.name] = extra_network
def register_extra_network_alias(extra_network, alias):
extra_network_aliases[alias] = extra_network
def register_default_extra_networks(): def register_default_extra_networks():
from modules.extra_networks_hypernet import ExtraNetworkHypernet from modules.extra_networks_hypernet import ExtraNetworkHypernet
register_extra_network(ExtraNetworkHypernet()) register_extra_network(ExtraNetworkHypernet())
@@ -78,24 +87,58 @@ class ExtraNetwork:
raise NotImplementedError raise NotImplementedError
def lookup_extra_networks(extra_network_data):
"""returns a dict mapping ExtraNetwork objects to lists of arguments for those extra networks.
Example input:
{
'lora': [<modules.extra_networks.ExtraNetworkParams object at 0x0000020690D58310>],
'lyco': [<modules.extra_networks.ExtraNetworkParams object at 0x0000020690D58F70>],
'hypernet': [<modules.extra_networks.ExtraNetworkParams object at 0x0000020690D5A800>]
}
Example output:
{
<extra_networks_lora.ExtraNetworkLora object at 0x0000020581BEECE0>: [<modules.extra_networks.ExtraNetworkParams object at 0x0000020690D58310>, <modules.extra_networks.ExtraNetworkParams object at 0x0000020690D58F70>],
<modules.extra_networks_hypernet.ExtraNetworkHypernet object at 0x0000020581BEEE60>: [<modules.extra_networks.ExtraNetworkParams object at 0x0000020690D5A800>]
}
"""
res = {}
for extra_network_name, extra_network_args in list(extra_network_data.items()):
extra_network = extra_network_registry.get(extra_network_name, None)
alias = extra_network_aliases.get(extra_network_name, None)
if alias is not None and extra_network is None:
extra_network = alias
if extra_network is None:
logging.info(f"Skipping unknown extra network: {extra_network_name}")
continue
res.setdefault(extra_network, []).extend(extra_network_args)
return res
def activate(p, extra_network_data): def activate(p, extra_network_data):
"""call activate for extra networks in extra_network_data in specified order, then call """call activate for extra networks in extra_network_data in specified order, then call
activate for all remaining registered networks with an empty argument list""" activate for all remaining registered networks with an empty argument list"""
for extra_network_name, extra_network_args in extra_network_data.items(): activated = []
extra_network = extra_network_registry.get(extra_network_name, None)
if extra_network is None: for extra_network, extra_network_args in lookup_extra_networks(extra_network_data).items():
print(f"Skipping unknown extra network: {extra_network_name}")
continue
try: try:
extra_network.activate(p, extra_network_args) extra_network.activate(p, extra_network_args)
activated.append(extra_network)
except Exception as e: except Exception as e:
errors.display(e, f"activating extra network {extra_network_name} with arguments {extra_network_args}") errors.display(e, f"activating extra network {extra_network.name} with arguments {extra_network_args}")
for extra_network_name, extra_network in extra_network_registry.items(): for extra_network_name, extra_network in extra_network_registry.items():
args = extra_network_data.get(extra_network_name, None) if extra_network in activated:
if args is not None:
continue continue
try: try:
@@ -103,24 +146,24 @@ def activate(p, extra_network_data):
except Exception as e: except Exception as e:
errors.display(e, f"activating extra network {extra_network_name}") errors.display(e, f"activating extra network {extra_network_name}")
if p.scripts is not None:
p.scripts.after_extra_networks_activate(p, batch_number=p.iteration, prompts=p.prompts, seeds=p.seeds, subseeds=p.subseeds, extra_network_data=extra_network_data)
def deactivate(p, extra_network_data): def deactivate(p, extra_network_data):
"""call deactivate for extra networks in extra_network_data in specified order, then call """call deactivate for extra networks in extra_network_data in specified order, then call
deactivate for all remaining registered networks""" deactivate for all remaining registered networks"""
for extra_network_name in extra_network_data: data = lookup_extra_networks(extra_network_data)
extra_network = extra_network_registry.get(extra_network_name, None)
if extra_network is None:
continue
for extra_network in data:
try: try:
extra_network.deactivate(p) extra_network.deactivate(p)
except Exception as e: except Exception as e:
errors.display(e, f"deactivating extra network {extra_network_name}") errors.display(e, f"deactivating extra network {extra_network.name}")
for extra_network_name, extra_network in extra_network_registry.items(): for extra_network_name, extra_network in extra_network_registry.items():
args = extra_network_data.get(extra_network_name, None) if extra_network in data:
if args is not None:
continue continue
try: try:
@@ -162,3 +205,20 @@ def parse_prompts(prompts):
return res, extra_data return res, extra_data
def get_user_metadata(filename):
if filename is None:
return {}
basename, ext = os.path.splitext(filename)
metadata_filename = basename + '.json'
metadata = {}
try:
if os.path.isfile(metadata_filename):
with open(metadata_filename, "r", encoding="utf8") as file:
metadata = json.load(file)
except Exception as e:
errors.display(e, f"reading extra network user metadata from {metadata_filename}")
return metadata
+34 -8
View File
@@ -7,7 +7,7 @@ import json
import torch import torch
import tqdm import tqdm
from modules import shared, images, sd_models, sd_vae, sd_models_config from modules import shared, images, sd_models, sd_vae, sd_models_config, errors
from modules.ui_common import plaintext_to_html from modules.ui_common import plaintext_to_html
import gradio as gr import gradio as gr
import safetensors.torch import safetensors.torch
@@ -72,9 +72,21 @@ def to_half(tensor, enable):
return tensor return tensor
def run_modelmerger(id_task, primary_model_name, secondary_model_name, tertiary_model_name, interp_method, multiplier, save_as_half, custom_name, checkpoint_format, config_source, bake_in_vae, discard_weights, save_metadata): def read_metadata(primary_model_name, secondary_model_name, tertiary_model_name):
shared.state.begin() metadata = {}
shared.state.job = 'model-merge'
for checkpoint_name in [primary_model_name, secondary_model_name, tertiary_model_name]:
checkpoint_info = sd_models.checkpoints_list.get(checkpoint_name, None)
if checkpoint_info is None:
continue
metadata.update(checkpoint_info.metadata)
return json.dumps(metadata, indent=4, ensure_ascii=False)
def run_modelmerger(id_task, primary_model_name, secondary_model_name, tertiary_model_name, interp_method, multiplier, save_as_half, custom_name, checkpoint_format, config_source, bake_in_vae, discard_weights, save_metadata, add_merge_recipe, copy_metadata_fields, metadata_json):
shared.state.begin(job="model-merge")
def fail(message): def fail(message):
shared.state.textinfo = message shared.state.textinfo = message
@@ -242,11 +254,25 @@ def run_modelmerger(id_task, primary_model_name, secondary_model_name, tertiary_
shared.state.textinfo = "Saving" shared.state.textinfo = "Saving"
print(f"Saving to {output_modelname}...") print(f"Saving to {output_modelname}...")
metadata = None metadata = {}
if save_metadata and copy_metadata_fields:
if primary_model_info:
metadata.update(primary_model_info.metadata)
if secondary_model_info:
metadata.update(secondary_model_info.metadata)
if tertiary_model_info:
metadata.update(tertiary_model_info.metadata)
if save_metadata: if save_metadata:
metadata = {"format": "pt"} try:
metadata.update(json.loads(metadata_json))
except Exception as e:
errors.display(e, "readin metadata from json")
metadata["format"] = "pt"
if save_metadata and add_merge_recipe:
merge_recipe = { merge_recipe = {
"type": "webui", # indicate this model was merged with webui's built-in merger "type": "webui", # indicate this model was merged with webui's built-in merger
"primary_model_hash": primary_model_info.sha256, "primary_model_hash": primary_model_info.sha256,
@@ -262,7 +288,6 @@ def run_modelmerger(id_task, primary_model_name, secondary_model_name, tertiary_
"is_inpainting": result_is_inpainting_model, "is_inpainting": result_is_inpainting_model,
"is_instruct_pix2pix": result_is_instruct_pix2pix_model "is_instruct_pix2pix": result_is_instruct_pix2pix_model
} }
metadata["sd_merge_recipe"] = json.dumps(merge_recipe)
sd_merge_models = {} sd_merge_models = {}
@@ -282,11 +307,12 @@ def run_modelmerger(id_task, primary_model_name, secondary_model_name, tertiary_
if tertiary_model_info: if tertiary_model_info:
add_model_metadata(tertiary_model_info) add_model_metadata(tertiary_model_info)
metadata["sd_merge_recipe"] = json.dumps(merge_recipe)
metadata["sd_merge_models"] = json.dumps(sd_merge_models) metadata["sd_merge_models"] = json.dumps(sd_merge_models)
_, extension = os.path.splitext(output_modelname) _, extension = os.path.splitext(output_modelname)
if extension.lower() == ".safetensors": if extension.lower() == ".safetensors":
safetensors.torch.save_file(theta_0, output_modelname, metadata=metadata) safetensors.torch.save_file(theta_0, output_modelname, metadata=metadata if len(metadata)>0 else None)
else: else:
torch.save(theta_0, output_modelname) torch.save(theta_0, output_modelname)
+37
View File
@@ -0,0 +1,37 @@
import threading
import collections
# reference: https://gist.github.com/vitaliyp/6d54dd76ca2c3cdfc1149d33007dc34a
class FIFOLock(object):
def __init__(self):
self._lock = threading.Lock()
self._inner_lock = threading.Lock()
self._pending_threads = collections.deque()
def acquire(self, blocking=True):
with self._inner_lock:
lock_acquired = self._lock.acquire(False)
if lock_acquired:
return True
elif not blocking:
return False
release_event = threading.Event()
self._pending_threads.append(release_event)
release_event.wait()
return self._lock.acquire()
def release(self):
with self._inner_lock:
if self._pending_threads:
release_event = self._pending_threads.popleft()
release_event.set()
self._lock.release()
__enter__ = acquire
def __exit__(self, t, v, tb):
self.release()
+26 -48
View File
@@ -6,10 +6,10 @@ import re
import gradio as gr import gradio as gr
from modules.paths import data_path from modules.paths import data_path
from modules import shared, ui_tempdir, script_callbacks from modules import shared, ui_tempdir, script_callbacks, processing
from PIL import Image from PIL import Image
re_param_code = r'\s*([\w ]+):\s*("(?:\\"[^,]|\\"|\\|[^\"])+"|[^,]*)(?:,|$)' re_param_code = r'\s*(\w[\w \-/]+):\s*("(?:\\.|[^\\"])+"|[^,]*)(?:,|$)'
re_param = re.compile(re_param_code) re_param = re.compile(re_param_code)
re_imagesize = re.compile(r"^(\d+)x(\d+)$") re_imagesize = re.compile(r"^(\d+)x(\d+)$")
re_hypernet_hash = re.compile("\(([0-9a-f]+)\)$") re_hypernet_hash = re.compile("\(([0-9a-f]+)\)$")
@@ -32,6 +32,7 @@ class ParamBinding:
def reset(): def reset():
paste_fields.clear() paste_fields.clear()
registered_param_bindings.clear()
def quote(text): def quote(text):
@@ -174,31 +175,6 @@ def send_image_and_dimensions(x):
return img, w, h return img, w, h
def find_hypernetwork_key(hypernet_name, hypernet_hash=None):
"""Determines the config parameter name to use for the hypernet based on the parameters in the infotext.
Example: an infotext provides "Hypernet: ke-ta" and "Hypernet hash: 1234abcd". For the "Hypernet" config
parameter this means there should be an entry that looks like "ke-ta-10000(1234abcd)" to set it to.
If the infotext has no hash, then a hypernet with the same name will be selected instead.
"""
hypernet_name = hypernet_name.lower()
if hypernet_hash is not None:
# Try to match the hash in the name
for hypernet_key in shared.hypernetworks.keys():
result = re_hypernet_hash.search(hypernet_key)
if result is not None and result[1] == hypernet_hash:
return hypernet_key
else:
# Fall back to a hypernet with the same name
for hypernet_key in shared.hypernetworks.keys():
if hypernet_key.lower().startswith(hypernet_name):
return hypernet_key
return None
def restore_old_hires_fix_params(res): def restore_old_hires_fix_params(res):
"""for infotexts that specify old First pass size parameter, convert it into """for infotexts that specify old First pass size parameter, convert it into
width, height, and hr scale""" width, height, and hr scale"""
@@ -223,7 +199,6 @@ def restore_old_hires_fix_params(res):
height = int(res.get("Size-2", 512)) height = int(res.get("Size-2", 512))
if firstpass_width == 0 or firstpass_height == 0: if firstpass_width == 0 or firstpass_height == 0:
from modules import processing
firstpass_width, firstpass_height = processing.old_hires_fix_first_pass_dimensions(width, height) firstpass_width, firstpass_height = processing.old_hires_fix_first_pass_dimensions(width, height)
res['Size-1'] = firstpass_width res['Size-1'] = firstpass_width
@@ -305,6 +280,9 @@ Steps: 20, Sampler: Euler a, CFG scale: 7, Seed: 965400086, Size: 512x512, Model
if "Hires sampler" not in res: if "Hires sampler" not in res:
res["Hires sampler"] = "Use same sampler" res["Hires sampler"] = "Use same sampler"
if "Hires checkpoint" not in res:
res["Hires checkpoint"] = "Use same checkpoint"
if "Hires prompt" not in res: if "Hires prompt" not in res:
res["Hires prompt"] = "" res["Hires prompt"] = ""
@@ -329,35 +307,28 @@ Steps: 20, Sampler: Euler a, CFG scale: 7, Seed: 965400086, Size: 512x512, Model
if "Schedule rho" not in res: if "Schedule rho" not in res:
res["Schedule rho"] = 0 res["Schedule rho"] = 0
if "VAE Encoder" not in res:
res["VAE Encoder"] = "Full"
if "VAE Decoder" not in res:
res["VAE Decoder"] = "Full"
return res return res
settings_map = {} infotext_to_setting_name_mapping = [
]
"""Mapping of infotext labels to setting names. Only left for backwards compatibility - use OptionInfo(..., infotext='...') instead.
Example content:
infotext_to_setting_name_mapping = [ infotext_to_setting_name_mapping = [
('Clip skip', 'CLIP_stop_at_last_layers', ),
('Conditional mask weight', 'inpainting_mask_weight'), ('Conditional mask weight', 'inpainting_mask_weight'),
('Model hash', 'sd_model_checkpoint'), ('Model hash', 'sd_model_checkpoint'),
('ENSD', 'eta_noise_seed_delta'), ('ENSD', 'eta_noise_seed_delta'),
('Schedule type', 'k_sched_type'), ('Schedule type', 'k_sched_type'),
('Schedule max sigma', 'sigma_max'),
('Schedule min sigma', 'sigma_min'),
('Schedule rho', 'rho'),
('Noise multiplier', 'initial_noise_multiplier'),
('Eta', 'eta_ancestral'),
('Eta DDIM', 'eta_ddim'),
('Discard penultimate sigma', 'always_discard_next_to_last_sigma'),
('UniPC variant', 'uni_pc_variant'),
('UniPC skip type', 'uni_pc_skip_type'),
('UniPC order', 'uni_pc_order'),
('UniPC lower order final', 'uni_pc_lower_order_final'),
('Token merging ratio', 'token_merging_ratio'),
('Token merging ratio hr', 'token_merging_ratio_hr'),
('RNG', 'randn_source'),
('NGMS', 's_min_uncond'),
] ]
"""
def create_override_settings_dict(text_pairs): def create_override_settings_dict(text_pairs):
@@ -378,7 +349,8 @@ def create_override_settings_dict(text_pairs):
params[k] = v.strip() params[k] = v.strip()
for param_name, setting_name in infotext_to_setting_name_mapping: mapping = [(info.infotext, k) for k, info in shared.opts.data_labels.items() if info.infotext]
for param_name, setting_name in mapping + infotext_to_setting_name_mapping:
value = params.get(param_name, None) value = params.get(param_name, None)
if value is None: if value is None:
@@ -427,10 +399,16 @@ def connect_paste(button, paste_fields, input_comp, override_settings_component,
return res return res
if override_settings_component is not None: if override_settings_component is not None:
already_handled_fields = {key: 1 for _, key in paste_fields}
def paste_settings(params): def paste_settings(params):
vals = {} vals = {}
for param_name, setting_name in infotext_to_setting_name_mapping: mapping = [(info.infotext, k) for k, info in shared.opts.data_labels.items() if info.infotext]
for param_name, setting_name in mapping + infotext_to_setting_name_mapping:
if param_name in already_handled_fields:
continue
v = params.get(param_name, None) v = params.get(param_name, None)
if v is None: if v is None:
continue continue
+22 -10
View File
@@ -9,6 +9,7 @@ from modules import paths, shared, devices, modelloader, errors
model_dir = "GFPGAN" model_dir = "GFPGAN"
user_path = None user_path = None
model_path = os.path.join(paths.models_path, model_dir) model_path = os.path.join(paths.models_path, model_dir)
model_file_path = None
model_url = "https://github.com/TencentARC/GFPGAN/releases/download/v1.3.0/GFPGANv1.4.pth" model_url = "https://github.com/TencentARC/GFPGAN/releases/download/v1.3.0/GFPGANv1.4.pth"
have_gfpgan = False have_gfpgan = False
loaded_gfpgan_model = None loaded_gfpgan_model = None
@@ -17,6 +18,7 @@ loaded_gfpgan_model = None
def gfpgann(): def gfpgann():
global loaded_gfpgan_model global loaded_gfpgan_model
global model_path global model_path
global model_file_path
if loaded_gfpgan_model is not None: if loaded_gfpgan_model is not None:
loaded_gfpgan_model.gfpgan.to(devices.device_gfpgan) loaded_gfpgan_model.gfpgan.to(devices.device_gfpgan)
return loaded_gfpgan_model return loaded_gfpgan_model
@@ -24,17 +26,24 @@ def gfpgann():
if gfpgan_constructor is None: if gfpgan_constructor is None:
return None return None
models = modelloader.load_models(model_path, model_url, user_path, ext_filter="GFPGAN") models = modelloader.load_models(model_path, model_url, user_path, ext_filter=['.pth'])
if len(models) == 1 and "http" in models[0]:
if len(models) == 1 and models[0].startswith("http"):
model_file = models[0] model_file = models[0]
elif len(models) != 0: elif len(models) != 0:
latest_file = max(models, key=os.path.getctime) gfp_models = []
for item in models:
if 'GFPGAN' in os.path.basename(item):
gfp_models.append(item)
latest_file = max(gfp_models, key=os.path.getctime)
model_file = latest_file model_file = latest_file
else: else:
print("Unable to load gfpgan model!") print("Unable to load gfpgan model!")
return None return None
if hasattr(facexlib.detection.retinaface, 'device'): if hasattr(facexlib.detection.retinaface, 'device'):
facexlib.detection.retinaface.device = devices.device_gfpgan facexlib.detection.retinaface.device = devices.device_gfpgan
model_file_path = model_file
model = gfpgan_constructor(model_path=model_file, upscale=1, arch='clean', channel_multiplier=2, bg_upsampler=None, device=devices.device_gfpgan) model = gfpgan_constructor(model_path=model_file, upscale=1, arch='clean', channel_multiplier=2, bg_upsampler=None, device=devices.device_gfpgan)
loaded_gfpgan_model = model loaded_gfpgan_model = model
@@ -70,29 +79,32 @@ gfpgan_constructor = None
def setup_model(dirname): def setup_model(dirname):
global model_path
if not os.path.exists(model_path):
os.makedirs(model_path)
try: try:
os.makedirs(model_path, exist_ok=True)
from gfpgan import GFPGANer from gfpgan import GFPGANer
from facexlib import detection, parsing # noqa: F401 from facexlib import detection, parsing # noqa: F401
global user_path global user_path
global have_gfpgan global have_gfpgan
global gfpgan_constructor global gfpgan_constructor
global model_file_path
facexlib_path = model_path
if dirname is not None:
facexlib_path = dirname
load_file_from_url_orig = gfpgan.utils.load_file_from_url load_file_from_url_orig = gfpgan.utils.load_file_from_url
facex_load_file_from_url_orig = facexlib.detection.load_file_from_url facex_load_file_from_url_orig = facexlib.detection.load_file_from_url
facex_load_file_from_url_orig2 = facexlib.parsing.load_file_from_url facex_load_file_from_url_orig2 = facexlib.parsing.load_file_from_url
def my_load_file_from_url(**kwargs): def my_load_file_from_url(**kwargs):
return load_file_from_url_orig(**dict(kwargs, model_dir=model_path)) return load_file_from_url_orig(**dict(kwargs, model_dir=model_file_path))
def facex_load_file_from_url(**kwargs): def facex_load_file_from_url(**kwargs):
return facex_load_file_from_url_orig(**dict(kwargs, save_dir=model_path, model_dir=None)) return facex_load_file_from_url_orig(**dict(kwargs, save_dir=facexlib_path, model_dir=None))
def facex_load_file_from_url2(**kwargs): def facex_load_file_from_url2(**kwargs):
return facex_load_file_from_url_orig2(**dict(kwargs, save_dir=model_path, model_dir=None)) return facex_load_file_from_url_orig2(**dict(kwargs, save_dir=facexlib_path, model_dir=None))
gfpgan.utils.load_file_from_url = my_load_file_from_url gfpgan.utils.load_file_from_url = my_load_file_from_url
facexlib.detection.load_file_from_url = facex_load_file_from_url facexlib.detection.load_file_from_url = facex_load_file_from_url
+1 -1
View File
@@ -23,7 +23,7 @@ class Git(git.Git):
) )
return self._parse_object_header(ret) return self._parse_object_header(ret)
def stream_object_data(self, ref: str) -> tuple[str, str, int, "Git.CatFileContentStream"]: def stream_object_data(self, ref: str) -> tuple[str, str, int, Git.CatFileContentStream]:
# Not really streaming, per se; this buffers the entire object in memory. # Not really streaming, per se; this buffers the entire object in memory.
# Shouldn't be a problem for our use case, since we're only using this for # Shouldn't be a problem for our use case, since we're only using this for
# object headers (commit objects). # object headers (commit objects).
+142
View File
@@ -0,0 +1,142 @@
from inspect import signature
from functools import wraps
import gradio as gr
from modules import scripts, ui_tempdir, patches
def add_classes_to_gradio_component(comp):
"""
this adds gradio-* to the component for css styling (ie gradio-button to gr.Button), as well as some others
"""
comp.elem_classes = [f"gradio-{comp.get_block_name()}", *(comp.elem_classes or [])]
if getattr(comp, 'multiselect', False):
comp.elem_classes.append('multiselect')
def IOComponent_init(self, *args, **kwargs):
self.webui_tooltip = kwargs.pop('tooltip', None)
if scripts.scripts_current is not None:
scripts.scripts_current.before_component(self, **kwargs)
scripts.script_callbacks.before_component_callback(self, **kwargs)
res = original_IOComponent_init(self, *args, **kwargs)
add_classes_to_gradio_component(self)
scripts.script_callbacks.after_component_callback(self, **kwargs)
if scripts.scripts_current is not None:
scripts.scripts_current.after_component(self, **kwargs)
return res
def Block_get_config(self):
config = original_Block_get_config(self)
webui_tooltip = getattr(self, 'webui_tooltip', None)
if webui_tooltip:
config["webui_tooltip"] = webui_tooltip
config.pop('example_inputs', None)
return config
def BlockContext_init(self, *args, **kwargs):
res = original_BlockContext_init(self, *args, **kwargs)
add_classes_to_gradio_component(self)
return res
def Blocks_get_config_file(self, *args, **kwargs):
config = original_Blocks_get_config_file(self, *args, **kwargs)
for comp_config in config["components"]:
if "example_inputs" in comp_config:
comp_config["example_inputs"] = {"serialized": []}
return config
def gradio_component_compatibility_layer(component_function):
@wraps(component_function)
def patched_function(*args, **kwargs):
original_signature = signature(component_function).parameters
valid_kwargs = {k: v for k, v in kwargs.items() if k in original_signature}
result = component_function(*args, **valid_kwargs)
return result
return patched_function
sub_events = ['then', 'success']
def gradio_component_events_compatibility_layer(component_function):
@wraps(component_function)
def patched_function(*args, **kwargs):
kwargs['js'] = kwargs.get('js', kwargs.pop('_js', None))
original_signature = signature(component_function).parameters
valid_kwargs = {k: v for k, v in kwargs.items() if k in original_signature}
result = component_function(*args, **valid_kwargs)
for sub_event in sub_events:
component_event_then_function = getattr(result, sub_event, None)
if component_event_then_function:
patched_component_event_then_function = gradio_component_sub_events_compatibility_layer(component_event_then_function)
setattr(result, sub_event, patched_component_event_then_function)
# original_component_event_then_function = patches.patch(f'{__name__}.', obj=result, field='then', replacement=patched_component_event_then_function)
return result
return patched_function
def gradio_component_sub_events_compatibility_layer(component_function):
@wraps(component_function)
def patched_function(*args, **kwargs):
kwargs['js'] = kwargs.get('js', kwargs.pop('_js', None))
original_signature = signature(component_function).parameters
valid_kwargs = {k: v for k, v in kwargs.items() if k in original_signature}
result = component_function(*args, **valid_kwargs)
return result
return patched_function
for component_name in set(gr.components.__all__ + gr.layouts.__all__):
try:
component = getattr(gr, component_name)
component_init = getattr(component, '__init__')
patched_component_init = gradio_component_compatibility_layer(component_init)
original_IOComponent_init = patches.patch(f'{__name__}.{component_name}', obj=component, field="__init__", replacement=patched_component_init)
component_events = set(getattr(component, 'EVENTS'))
for component_event in component_events:
component_event_function = getattr(component, component_event)
patched_component_event_function = gradio_component_events_compatibility_layer(component_event_function)
original_component_event_function = patches.patch(f'{__name__}.{component_name}.{component_event}', obj=component, field=component_event, replacement=patched_component_event_function)
except Exception as e:
print(e)
pass
gr.Box = gr.Group
original_IOComponent_init = patches.patch(__name__, obj=gr.components.base.Component, field="__init__", replacement=IOComponent_init)
original_Block_get_config = patches.patch(__name__, obj=gr.blocks.Block, field="get_config", replacement=Block_get_config)
original_BlockContext_init = patches.patch(__name__, obj=gr.blocks.BlockContext, field="__init__", replacement=BlockContext_init)
original_Blocks_get_config_file = patches.patch(__name__, obj=gr.blocks.Blocks, field="get_config_file", replacement=Blocks_get_config_file)
ui_tempdir.install_ui_tempdir_override()
+3 -30
View File
@@ -1,38 +1,11 @@
import hashlib import hashlib
import json
import os.path import os.path
import filelock
from modules import shared from modules import shared
from modules.paths import data_path import modules.cache
dump_cache = modules.cache.dump_cache
cache_filename = os.path.join(data_path, "cache.json") cache = modules.cache.cache
cache_data = None
def dump_cache():
with filelock.FileLock(f"{cache_filename}.lock"):
with open(cache_filename, "w", encoding="utf8") as file:
json.dump(cache_data, file, indent=4)
def cache(subsection):
global cache_data
if cache_data is None:
with filelock.FileLock(f"{cache_filename}.lock"):
if not os.path.isfile(cache_filename):
cache_data = {}
else:
with open(cache_filename, "r", encoding="utf8") as file:
cache_data = json.load(file)
s = cache_data.get(subsection, {})
cache_data[subsection] = s
return s
def calculate_sha256(filename): def calculate_sha256(filename):
+7 -30
View File
@@ -3,13 +3,14 @@ import glob
import html import html
import os import os
import inspect import inspect
from contextlib import closing
import modules.textual_inversion.dataset import modules.textual_inversion.dataset
import torch import torch
import tqdm import tqdm
from einops import rearrange, repeat from einops import rearrange, repeat
from ldm.util import default from ldm.util import default
from modules import devices, processing, sd_models, shared, sd_samplers, hashes, sd_hijack_checkpoint, errors from modules import devices, sd_models, shared, sd_samplers, hashes, sd_hijack_checkpoint, errors
from modules.textual_inversion import textual_inversion, logging from modules.textual_inversion import textual_inversion, logging
from modules.textual_inversion.learn_schedule import LearnRateScheduler from modules.textual_inversion.learn_schedule import LearnRateScheduler
from torch import einsum from torch import einsum
@@ -353,17 +354,6 @@ def load_hypernetworks(names, multipliers=None):
shared.loaded_hypernetworks.append(hypernetwork) shared.loaded_hypernetworks.append(hypernetwork)
def find_closest_hypernetwork_name(search: str):
if not search:
return None
search = search.lower()
applicable = [name for name in shared.hypernetworks if search in name.lower()]
if not applicable:
return None
applicable = sorted(applicable, key=lambda name: len(name))
return applicable[0]
def apply_single_hypernetwork(hypernetwork, context_k, context_v, layer=None): def apply_single_hypernetwork(hypernetwork, context_k, context_v, layer=None):
hypernetwork_layers = (hypernetwork.layers if hypernetwork is not None else {}).get(context_k.shape[2], None) hypernetwork_layers = (hypernetwork.layers if hypernetwork is not None else {}).get(context_k.shape[2], None)
@@ -388,7 +378,7 @@ def apply_hypernetworks(hypernetworks, context, layer=None):
return context_k, context_v return context_k, context_v
def attention_CrossAttention_forward(self, x, context=None, mask=None): def attention_CrossAttention_forward(self, x, context=None, mask=None, **kwargs):
h = self.heads h = self.heads
q = self.to_q(x) q = self.to_q(x)
@@ -446,18 +436,6 @@ def statistics(data):
return total_information, recent_information return total_information, recent_information
def report_statistics(loss_info:dict):
keys = sorted(loss_info.keys(), key=lambda x: sum(loss_info[x]) / len(loss_info[x]))
for key in keys:
try:
print("Loss statistics for file " + key)
info, recent = statistics(list(loss_info[key]))
print(info)
print(recent)
except Exception as e:
print(e)
def create_hypernetwork(name, enable_sizes, overwrite_old, layer_structure=None, activation_func=None, weight_init=None, add_layer_norm=False, use_dropout=False, dropout_structure=None): def create_hypernetwork(name, enable_sizes, overwrite_old, layer_structure=None, activation_func=None, weight_init=None, add_layer_norm=False, use_dropout=False, dropout_structure=None):
# Remove illegal characters from name. # Remove illegal characters from name.
name = "".join( x for x in name if (x.isalnum() or x in "._- ")) name = "".join( x for x in name if (x.isalnum() or x in "._- "))
@@ -490,9 +468,8 @@ def create_hypernetwork(name, enable_sizes, overwrite_old, layer_structure=None,
shared.reload_hypernetworks() shared.reload_hypernetworks()
def train_hypernetwork(id_task, hypernetwork_name, learn_rate, batch_size, gradient_step, data_root, log_directory, training_width, training_height, varsize, steps, clip_grad_mode, clip_grad_value, shuffle_tags, tag_drop_out, latent_sampling_method, use_weight, create_image_every, save_hypernetwork_every, template_filename, preview_from_txt2img, preview_prompt, preview_negative_prompt, preview_steps, preview_sampler_index, preview_cfg_scale, preview_seed, preview_width, preview_height): def train_hypernetwork(id_task, hypernetwork_name: str, learn_rate: float, batch_size: int, gradient_step: int, data_root: str, log_directory: str, training_width: int, training_height: int, varsize: bool, steps: int, clip_grad_mode: str, clip_grad_value: float, shuffle_tags: bool, tag_drop_out: bool, latent_sampling_method: str, use_weight: bool, create_image_every: int, save_hypernetwork_every: int, template_filename: str, preview_from_txt2img: bool, preview_prompt: str, preview_negative_prompt: str, preview_steps: int, preview_sampler_name: str, preview_cfg_scale: float, preview_seed: int, preview_width: int, preview_height: int):
# images allows training previews to have infotext. Importing it at the top causes a circular import problem. from modules import images, processing
from modules import images
save_hypernetwork_every = save_hypernetwork_every or 0 save_hypernetwork_every = save_hypernetwork_every or 0
create_image_every = create_image_every or 0 create_image_every = create_image_every or 0
@@ -721,7 +698,7 @@ def train_hypernetwork(id_task, hypernetwork_name, learn_rate, batch_size, gradi
p.prompt = preview_prompt p.prompt = preview_prompt
p.negative_prompt = preview_negative_prompt p.negative_prompt = preview_negative_prompt
p.steps = preview_steps p.steps = preview_steps
p.sampler_name = sd_samplers.samplers[preview_sampler_index].name p.sampler_name = sd_samplers.samplers_map[preview_sampler_name.lower()]
p.cfg_scale = preview_cfg_scale p.cfg_scale = preview_cfg_scale
p.seed = preview_seed p.seed = preview_seed
p.width = preview_width p.width = preview_width
@@ -734,6 +711,7 @@ def train_hypernetwork(id_task, hypernetwork_name, learn_rate, batch_size, gradi
preview_text = p.prompt preview_text = p.prompt
with closing(p):
processed = processing.process_images(p) processed = processing.process_images(p)
image = processed.images[0] if len(processed.images) > 0 else None image = processed.images[0] if len(processed.images) > 0 else None
@@ -770,7 +748,6 @@ Last saved image: {html.escape(last_saved_image)}<br/>
pbar.leave = False pbar.leave = False
pbar.close() pbar.close()
hypernetwork.eval() hypernetwork.eval()
#report_statistics(loss_dict)
sd_hijack_checkpoint.remove() sd_hijack_checkpoint.remove()
+99 -36
View File
@@ -1,3 +1,5 @@
from __future__ import annotations
import datetime import datetime
import pytz import pytz
@@ -10,7 +12,7 @@ import re
import numpy as np import numpy as np
import piexif import piexif
import piexif.helper import piexif.helper
from PIL import Image, ImageFont, ImageDraw, PngImagePlugin from PIL import Image, ImageFont, ImageDraw, ImageColor, PngImagePlugin
import string import string
import json import json
import hashlib import hashlib
@@ -19,8 +21,6 @@ from modules import sd_samplers, shared, script_callbacks, errors
from modules.paths_internal import roboto_ttf_file from modules.paths_internal import roboto_ttf_file
from modules.shared import opts from modules.shared import opts
import modules.sd_vae as sd_vae
LANCZOS = (Image.Resampling.LANCZOS if hasattr(Image, 'Resampling') else Image.LANCZOS) LANCZOS = (Image.Resampling.LANCZOS if hasattr(Image, 'Resampling') else Image.LANCZOS)
@@ -139,6 +139,11 @@ class GridAnnotation:
def draw_grid_annotations(im, width, height, hor_texts, ver_texts, margin=0): def draw_grid_annotations(im, width, height, hor_texts, ver_texts, margin=0):
color_active = ImageColor.getcolor(opts.grid_text_active_color, 'RGB')
color_inactive = ImageColor.getcolor(opts.grid_text_inactive_color, 'RGB')
color_background = ImageColor.getcolor(opts.grid_background_color, 'RGB')
def wrap(drawing, text, font, line_length): def wrap(drawing, text, font, line_length):
lines = [''] lines = ['']
for word in text.split(): for word in text.split():
@@ -168,9 +173,6 @@ def draw_grid_annotations(im, width, height, hor_texts, ver_texts, margin=0):
fnt = get_font(fontsize) fnt = get_font(fontsize)
color_active = (0, 0, 0)
color_inactive = (153, 153, 153)
pad_left = 0 if sum([sum([len(line.text) for line in lines]) for lines in ver_texts]) == 0 else width * 3 // 4 pad_left = 0 if sum([sum([len(line.text) for line in lines]) for lines in ver_texts]) == 0 else width * 3 // 4
cols = im.width // width cols = im.width // width
@@ -179,7 +181,7 @@ def draw_grid_annotations(im, width, height, hor_texts, ver_texts, margin=0):
assert cols == len(hor_texts), f'bad number of horizontal texts: {len(hor_texts)}; must be {cols}' assert cols == len(hor_texts), f'bad number of horizontal texts: {len(hor_texts)}; must be {cols}'
assert rows == len(ver_texts), f'bad number of vertical texts: {len(ver_texts)}; must be {rows}' assert rows == len(ver_texts), f'bad number of vertical texts: {len(ver_texts)}; must be {rows}'
calc_img = Image.new("RGB", (1, 1), "white") calc_img = Image.new("RGB", (1, 1), color_background)
calc_d = ImageDraw.Draw(calc_img) calc_d = ImageDraw.Draw(calc_img)
for texts, allowed_width in zip(hor_texts + ver_texts, [width] * len(hor_texts) + [pad_left] * len(ver_texts)): for texts, allowed_width in zip(hor_texts + ver_texts, [width] * len(hor_texts) + [pad_left] * len(ver_texts)):
@@ -200,7 +202,7 @@ def draw_grid_annotations(im, width, height, hor_texts, ver_texts, margin=0):
pad_top = 0 if sum(hor_text_heights) == 0 else max(hor_text_heights) + line_spacing * 2 pad_top = 0 if sum(hor_text_heights) == 0 else max(hor_text_heights) + line_spacing * 2
result = Image.new("RGB", (im.width + pad_left + margin * (cols-1), im.height + pad_top + margin * (rows-1)), "white") result = Image.new("RGB", (im.width + pad_left + margin * (cols-1), im.height + pad_top + margin * (rows-1)), color_background)
for row in range(rows): for row in range(rows):
for col in range(cols): for col in range(cols):
@@ -302,17 +304,19 @@ def resize_image(resize_mode, im, width, height, upscaler_name=None):
if ratio < src_ratio: if ratio < src_ratio:
fill_height = height // 2 - src_h // 2 fill_height = height // 2 - src_h // 2
if fill_height > 0:
res.paste(resized.resize((width, fill_height), box=(0, 0, width, 0)), box=(0, 0)) res.paste(resized.resize((width, fill_height), box=(0, 0, width, 0)), box=(0, 0))
res.paste(resized.resize((width, fill_height), box=(0, resized.height, width, resized.height)), box=(0, fill_height + src_h)) res.paste(resized.resize((width, fill_height), box=(0, resized.height, width, resized.height)), box=(0, fill_height + src_h))
elif ratio > src_ratio: elif ratio > src_ratio:
fill_width = width // 2 - src_w // 2 fill_width = width // 2 - src_w // 2
if fill_width > 0:
res.paste(resized.resize((fill_width, height), box=(0, 0, 0, height)), box=(0, 0)) res.paste(resized.resize((fill_width, height), box=(0, 0, 0, height)), box=(0, 0))
res.paste(resized.resize((fill_width, height), box=(resized.width, 0, resized.width, height)), box=(fill_width + src_w, 0)) res.paste(resized.resize((fill_width, height), box=(resized.width, 0, resized.width, height)), box=(fill_width + src_w, 0))
return res return res
invalid_filename_chars = '<>:"/\\|?*\n' invalid_filename_chars = '<>:"/\\|?*\n\r\t'
invalid_filename_prefix = ' ' invalid_filename_prefix = ' '
invalid_filename_postfix = ' .' invalid_filename_postfix = ' .'
re_nonletters = re.compile(r'[\s' + string.punctuation + ']+') re_nonletters = re.compile(r'[\s' + string.punctuation + ']+')
@@ -336,16 +340,6 @@ def sanitize_filename_part(text, replace_spaces=True):
class FilenameGenerator: class FilenameGenerator:
def get_vae_filename(self): #get the name of the VAE file.
if sd_vae.loaded_vae_file is None:
return "NoneType"
file_name = os.path.basename(sd_vae.loaded_vae_file)
split_file_name = file_name.split('.')
if len(split_file_name) > 1 and split_file_name[0] == '':
return split_file_name[1] # if the first character of the filename is "." then [1] is obtained.
else:
return split_file_name[0]
replacements = { replacements = {
'seed': lambda self: self.seed if self.seed is not None else '', 'seed': lambda self: self.seed if self.seed is not None else '',
'seed_first': lambda self: self.seed if self.p.batch_size == 1 else self.p.all_seeds[0], 'seed_first': lambda self: self.seed if self.p.batch_size == 1 else self.p.all_seeds[0],
@@ -357,11 +351,13 @@ class FilenameGenerator:
'styles': lambda self: self.p and sanitize_filename_part(", ".join([style for style in self.p.styles if not style == "None"]) or "None", replace_spaces=False), 'styles': lambda self: self.p and sanitize_filename_part(", ".join([style for style in self.p.styles if not style == "None"]) or "None", replace_spaces=False),
'sampler': lambda self: self.p and sanitize_filename_part(self.p.sampler_name, replace_spaces=False), 'sampler': lambda self: self.p and sanitize_filename_part(self.p.sampler_name, replace_spaces=False),
'model_hash': lambda self: getattr(self.p, "sd_model_hash", shared.sd_model.sd_model_hash), 'model_hash': lambda self: getattr(self.p, "sd_model_hash", shared.sd_model.sd_model_hash),
'model_name': lambda self: sanitize_filename_part(shared.sd_model.sd_checkpoint_info.model_name, replace_spaces=False), 'model_name': lambda self: sanitize_filename_part(shared.sd_model.sd_checkpoint_info.name_for_extra, replace_spaces=False),
'date': lambda self: datetime.datetime.now().strftime('%Y-%m-%d'), 'date': lambda self: datetime.datetime.now().strftime('%Y-%m-%d'),
'datetime': lambda self, *args: self.datetime(*args), # accepts formats: [datetime], [datetime<Format>], [datetime<Format><Time Zone>] 'datetime': lambda self, *args: self.datetime(*args), # accepts formats: [datetime], [datetime<Format>], [datetime<Format><Time Zone>]
'job_timestamp': lambda self: getattr(self.p, "job_timestamp", shared.state.job_timestamp), 'job_timestamp': lambda self: getattr(self.p, "job_timestamp", shared.state.job_timestamp),
'prompt_hash': lambda self: hashlib.sha256(self.prompt.encode()).hexdigest()[0:8], 'prompt_hash': lambda self, *args: self.string_hash(self.prompt, *args),
'negative_prompt_hash': lambda self, *args: self.string_hash(self.p.negative_prompt, *args),
'full_prompt_hash': lambda self, *args: self.string_hash(f"{self.p.prompt} {self.p.negative_prompt}", *args), # a space in between to create a unique string
'prompt': lambda self: sanitize_filename_part(self.prompt), 'prompt': lambda self: sanitize_filename_part(self.prompt),
'prompt_no_styles': lambda self: self.prompt_no_style(), 'prompt_no_styles': lambda self: self.prompt_no_style(),
'prompt_spaces': lambda self: sanitize_filename_part(self.prompt, replace_spaces=False), 'prompt_spaces': lambda self: sanitize_filename_part(self.prompt, replace_spaces=False),
@@ -372,8 +368,10 @@ class FilenameGenerator:
'hasprompt': lambda self, *args: self.hasprompt(*args), # accepts formats:[hasprompt<prompt1|default><prompt2>..] 'hasprompt': lambda self, *args: self.hasprompt(*args), # accepts formats:[hasprompt<prompt1|default><prompt2>..]
'clip_skip': lambda self: opts.data["CLIP_stop_at_last_layers"], 'clip_skip': lambda self: opts.data["CLIP_stop_at_last_layers"],
'denoising': lambda self: self.p.denoising_strength if self.p and self.p.denoising_strength else NOTHING_AND_SKIP_PREVIOUS_TEXT, 'denoising': lambda self: self.p.denoising_strength if self.p and self.p.denoising_strength else NOTHING_AND_SKIP_PREVIOUS_TEXT,
'user': lambda self: self.p.user,
'vae_filename': lambda self: self.get_vae_filename(), 'vae_filename': lambda self: self.get_vae_filename(),
'none': lambda self: '', # Overrides the default, so you can get just the sequence number
'image_hash': lambda self, *args: self.image_hash(*args) # accepts formats: [image_hash<length>] default full hash
} }
default_time_format = '%Y%m%d%H%M%S' default_time_format = '%Y%m%d%H%M%S'
@@ -384,6 +382,22 @@ class FilenameGenerator:
self.image = image self.image = image
self.zip = zip self.zip = zip
def get_vae_filename(self):
"""Get the name of the VAE file."""
import modules.sd_vae as sd_vae
if sd_vae.loaded_vae_file is None:
return "NoneType"
file_name = os.path.basename(sd_vae.loaded_vae_file)
split_file_name = file_name.split('.')
if len(split_file_name) > 1 and split_file_name[0] == '':
return split_file_name[1] # if the first character of the filename is "." then [1] is obtained.
else:
return split_file_name[0]
def hasprompt(self, *args): def hasprompt(self, *args):
lower = self.prompt.lower() lower = self.prompt.lower()
if self.p is None or self.prompt is None: if self.p is None or self.prompt is None:
@@ -437,6 +451,14 @@ class FilenameGenerator:
return sanitize_filename_part(formatted_time, replace_spaces=False) return sanitize_filename_part(formatted_time, replace_spaces=False)
def image_hash(self, *args):
length = int(args[0]) if (args and args[0] != "") else None
return hashlib.sha256(self.image.tobytes()).hexdigest()[0:length]
def string_hash(self, text, *args):
length = int(args[0]) if (args and args[0] != "") else 8
return hashlib.sha256(text.encode()).hexdigest()[0:length]
def apply(self, x): def apply(self, x):
res = '' res = ''
@@ -497,13 +519,23 @@ def get_next_sequence_number(path, basename):
return result + 1 return result + 1
def save_image_with_geninfo(image, geninfo, filename, extension=None, existing_pnginfo=None): def save_image_with_geninfo(image, geninfo, filename, extension=None, existing_pnginfo=None, pnginfo_section_name='parameters'):
"""
Saves image to filename, including geninfo as text information for generation info.
For PNG images, geninfo is added to existing pnginfo dictionary using the pnginfo_section_name argument as key.
For JPG images, there's no dictionary and geninfo just replaces the EXIF description.
"""
if extension is None: if extension is None:
extension = os.path.splitext(filename)[1] extension = os.path.splitext(filename)[1]
image_format = Image.registered_extensions()[extension] image_format = Image.registered_extensions()[extension]
if extension.lower() == '.png': if extension.lower() == '.png':
existing_pnginfo = existing_pnginfo or {}
if opts.enable_pnginfo:
existing_pnginfo[pnginfo_section_name] = geninfo
if opts.enable_pnginfo: if opts.enable_pnginfo:
pnginfo_data = PngImagePlugin.PngInfo() pnginfo_data = PngImagePlugin.PngInfo()
for k, v in (existing_pnginfo or {}).items(): for k, v in (existing_pnginfo or {}).items():
@@ -529,6 +561,8 @@ def save_image_with_geninfo(image, geninfo, filename, extension=None, existing_p
}) })
piexif.insert(exif_bytes, filename) piexif.insert(exif_bytes, filename)
elif extension.lower() == ".gif":
image.save(filename, format=image_format, comment=geninfo)
else: else:
image.save(filename, format=image_format, quality=opts.jpeg_quality) image.save(filename, format=image_format, quality=opts.jpeg_quality)
@@ -568,6 +602,11 @@ def save_image(image, path, basename, seed=None, prompt=None, extension='png', i
""" """
namegen = FilenameGenerator(p, seed, prompt, image) namegen = FilenameGenerator(p, seed, prompt, image)
# WebP and JPG formats have maximum dimension limits of 16383 and 65535 respectively. switch to PNG which has a much higher limit
if (image.height > 65535 or image.width > 65535) and extension.lower() in ("jpg", "jpeg") or (image.height > 16383 or image.width > 16383) and extension.lower() == "webp":
print('Image dimensions too large; saving as PNG')
extension = ".png"
if save_to_dirs is None: if save_to_dirs is None:
save_to_dirs = (grid and opts.grid_save_to_dirs) or (not grid and opts.save_to_dirs and not no_prompt) save_to_dirs = (grid and opts.grid_save_to_dirs) or (not grid and opts.save_to_dirs and not no_prompt)
@@ -585,13 +624,13 @@ def save_image(image, path, basename, seed=None, prompt=None, extension='png', i
else: else:
file_decoration = opts.samples_filename_pattern or "[seed]-[prompt_spaces]" file_decoration = opts.samples_filename_pattern or "[seed]-[prompt_spaces]"
file_decoration = namegen.apply(file_decoration) + suffix
add_number = opts.save_images_add_number or file_decoration == '' add_number = opts.save_images_add_number or file_decoration == ''
if file_decoration != "" and add_number: if file_decoration != "" and add_number:
file_decoration = f"-{file_decoration}" file_decoration = f"-{file_decoration}"
file_decoration = namegen.apply(file_decoration) + suffix
if add_number: if add_number:
basecount = get_next_sequence_number(path, basename) basecount = get_next_sequence_number(path, basename)
fullfn = None fullfn = None
@@ -622,9 +661,15 @@ def save_image(image, path, basename, seed=None, prompt=None, extension='png', i
""" """
temp_file_path = f"{filename_without_extension}.tmp" temp_file_path = f"{filename_without_extension}.tmp"
save_image_with_geninfo(image_to_save, info, temp_file_path, extension, params.pnginfo) save_image_with_geninfo(image_to_save, info, temp_file_path, extension, existing_pnginfo=params.pnginfo, pnginfo_section_name=pnginfo_section_name)
os.replace(temp_file_path, filename_without_extension + extension) filename = filename_without_extension + extension
if shared.opts.save_images_replace_action != "Replace":
n = 0
while os.path.exists(filename):
n += 1
filename = f"{filename_without_extension}-{n}{extension}"
os.replace(temp_file_path, filename)
fullfn_without_extension, extension = os.path.splitext(params.filename) fullfn_without_extension, extension = os.path.splitext(params.filename)
if hasattr(os, 'statvfs'): if hasattr(os, 'statvfs'):
@@ -639,12 +684,18 @@ def save_image(image, path, basename, seed=None, prompt=None, extension='png', i
oversize = image.width > opts.target_side_length or image.height > opts.target_side_length oversize = image.width > opts.target_side_length or image.height > opts.target_side_length
if opts.export_for_4chan and (oversize or os.stat(fullfn).st_size > opts.img_downscale_threshold * 1024 * 1024): if opts.export_for_4chan and (oversize or os.stat(fullfn).st_size > opts.img_downscale_threshold * 1024 * 1024):
ratio = image.width / image.height ratio = image.width / image.height
resize_to = None
if oversize and ratio > 1: if oversize and ratio > 1:
image = image.resize((round(opts.target_side_length), round(image.height * opts.target_side_length / image.width)), LANCZOS) resize_to = round(opts.target_side_length), round(image.height * opts.target_side_length / image.width)
elif oversize: elif oversize:
image = image.resize((round(image.width * opts.target_side_length / image.height), round(opts.target_side_length)), LANCZOS) resize_to = round(image.width * opts.target_side_length / image.height), round(opts.target_side_length)
if resize_to is not None:
try:
# Resizing image with LANCZOS could throw an exception if e.g. image mode is I;16
image = image.resize(resize_to, LANCZOS)
except Exception:
image = image.resize(resize_to)
try: try:
_atomically_save_image(image, fullfn_without_extension, ".jpg") _atomically_save_image(image, fullfn_without_extension, ".jpg")
except Exception as e: except Exception as e:
@@ -662,13 +713,25 @@ def save_image(image, path, basename, seed=None, prompt=None, extension='png', i
return fullfn, txt_fullfn return fullfn, txt_fullfn
def read_info_from_image(image): IGNORED_INFO_KEYS = {
items = image.info or {} 'jfif', 'jfif_version', 'jfif_unit', 'jfif_density', 'dpi', 'exif',
'loop', 'background', 'timestamp', 'duration', 'progressive', 'progression',
'icc_profile', 'chromaticity', 'photoshop',
}
def read_info_from_image(image: Image.Image) -> tuple[str | None, dict]:
items = (image.info or {}).copy()
geninfo = items.pop('parameters', None) geninfo = items.pop('parameters', None)
if "exif" in items: if "exif" in items:
exif = piexif.load(items["exif"]) exif_data = items["exif"]
try:
exif = piexif.load(exif_data)
except OSError:
# memory / exif was not valid so piexif tried to read from a file
exif = None
exif_comment = (exif or {}).get("Exif", {}).get(piexif.ExifIFD.UserComment, b'') exif_comment = (exif or {}).get("Exif", {}).get(piexif.ExifIFD.UserComment, b'')
try: try:
exif_comment = piexif.helper.UserComment.load(exif_comment) exif_comment = piexif.helper.UserComment.load(exif_comment)
@@ -678,10 +741,10 @@ def read_info_from_image(image):
if exif_comment: if exif_comment:
items['exif comment'] = exif_comment items['exif comment'] = exif_comment
geninfo = exif_comment geninfo = exif_comment
elif "comment" in items: # for gif
geninfo = items["comment"].decode('utf8', errors="ignore")
for field in ['jfif', 'jfif_version', 'jfif_unit', 'jfif_density', 'dpi', 'exif', for field in IGNORED_INFO_KEYS:
'loop', 'background', 'timestamp', 'duration', 'progressive', 'progression',
'icc_profile', 'chromaticity']:
items.pop(field, None) items.pop(field, None)
if items.get("Software", None) == "NovelAI": if items.get("Software", None) == "NovelAI":
+81 -43
View File
@@ -1,23 +1,27 @@
import os import os
from contextlib import closing
from pathlib import Path from pathlib import Path
import numpy as np import numpy as np
from PIL import Image, ImageOps, ImageFilter, ImageEnhance, ImageChops, UnidentifiedImageError from PIL import Image, ImageOps, ImageFilter, ImageEnhance, UnidentifiedImageError
import gradio as gr
from modules import sd_samplers from modules import images as imgutil
from modules.generation_parameters_copypaste import create_override_settings_dict from modules.generation_parameters_copypaste import create_override_settings_dict, parse_generation_parameters
from modules.processing import Processed, StableDiffusionProcessingImg2Img, process_images from modules.processing import Processed, StableDiffusionProcessingImg2Img, process_images
from modules.shared import opts, state from modules.shared import opts, state
from modules.sd_models import get_closet_checkpoint_match
import modules.shared as shared import modules.shared as shared
import modules.processing as processing import modules.processing as processing
from modules.ui import plaintext_to_html from modules.ui import plaintext_to_html
import modules.scripts import modules.scripts
def process_batch(p, input_dir, output_dir, inpaint_mask_dir, args, to_scale=False, scale_by=1.0): def process_batch(p, input_dir, output_dir, inpaint_mask_dir, args, to_scale=False, scale_by=1.0, use_png_info=False, png_info_props=None, png_info_dir=None):
output_dir = output_dir.strip()
processing.fix_seed(p) processing.fix_seed(p)
images = shared.listfiles(input_dir) images = list(shared.walk_files(input_dir, allowed_extensions=(".png", ".jpg", ".jpeg", ".webp", ".tif", ".tiff")))
is_inpaint_batch = False is_inpaint_batch = False
if inpaint_mask_dir: if inpaint_mask_dir:
@@ -29,13 +33,19 @@ def process_batch(p, input_dir, output_dir, inpaint_mask_dir, args, to_scale=Fal
print(f"Will process {len(images)} images, creating {p.n_iter * p.batch_size} new images for each.") print(f"Will process {len(images)} images, creating {p.n_iter * p.batch_size} new images for each.")
save_normally = output_dir == ''
p.do_not_save_grid = True
p.do_not_save_samples = not save_normally
state.job_count = len(images) * p.n_iter state.job_count = len(images) * p.n_iter
# extract "default" params to use in case getting png info fails
prompt = p.prompt
negative_prompt = p.negative_prompt
seed = p.seed
cfg_scale = p.cfg_scale
sampler_name = p.sampler_name
steps = p.steps
override_settings = p.override_settings
sd_model_checkpoint_override = get_closet_checkpoint_match(override_settings.get("sd_model_checkpoint", None))
batch_results = None
discard_further_results = False
for i, image in enumerate(images): for i, image in enumerate(images):
state.job = f"{i+1} out of {len(images)}" state.job = f"{i+1} out of {len(images)}"
if state.skipped: if state.skipped:
@@ -79,41 +89,77 @@ def process_batch(p, input_dir, output_dir, inpaint_mask_dir, args, to_scale=Fal
mask_image = Image.open(mask_image_path) mask_image = Image.open(mask_image_path)
p.image_mask = mask_image p.image_mask = mask_image
if use_png_info:
try:
info_img = img
if png_info_dir:
info_img_path = os.path.join(png_info_dir, os.path.basename(image))
info_img = Image.open(info_img_path)
geninfo, _ = imgutil.read_info_from_image(info_img)
parsed_parameters = parse_generation_parameters(geninfo)
parsed_parameters = {k: v for k, v in parsed_parameters.items() if k in (png_info_props or {})}
except Exception:
parsed_parameters = {}
p.prompt = prompt + (" " + parsed_parameters["Prompt"] if "Prompt" in parsed_parameters else "")
p.negative_prompt = negative_prompt + (" " + parsed_parameters["Negative prompt"] if "Negative prompt" in parsed_parameters else "")
p.seed = int(parsed_parameters.get("Seed", seed))
p.cfg_scale = float(parsed_parameters.get("CFG scale", cfg_scale))
p.sampler_name = parsed_parameters.get("Sampler", sampler_name)
p.steps = int(parsed_parameters.get("Steps", steps))
model_info = get_closet_checkpoint_match(parsed_parameters.get("Model hash", None))
if model_info is not None:
p.override_settings['sd_model_checkpoint'] = model_info.name
elif sd_model_checkpoint_override:
p.override_settings['sd_model_checkpoint'] = sd_model_checkpoint_override
else:
p.override_settings.pop("sd_model_checkpoint", None)
if output_dir:
p.outpath_samples = output_dir
p.override_settings['save_to_dirs'] = False
p.override_settings['save_images_replace_action'] = "Add number suffix"
if p.n_iter > 1 or p.batch_size > 1:
p.override_settings['samples_filename_pattern'] = f'{image_path.stem}-[generation_number]'
else:
p.override_settings['samples_filename_pattern'] = f'{image_path.stem}'
proc = modules.scripts.scripts_img2img.run(p, *args) proc = modules.scripts.scripts_img2img.run(p, *args)
if proc is None: if proc is None:
p.override_settings.pop('save_images_replace_action', None)
proc = process_images(p) proc = process_images(p)
for n, processed_image in enumerate(proc.images): if not discard_further_results and proc:
filename = image_path.name if batch_results:
batch_results.images.extend(proc.images)
batch_results.infotexts.extend(proc.infotexts)
else:
batch_results = proc
if n > 0: if 0 <= shared.opts.img2img_batch_show_results_limit < len(batch_results.images):
left, right = os.path.splitext(filename) discard_further_results = True
filename = f"{left}-{n}{right}" batch_results.images = batch_results.images[:int(shared.opts.img2img_batch_show_results_limit)]
batch_results.infotexts = batch_results.infotexts[:int(shared.opts.img2img_batch_show_results_limit)]
if not save_normally: return batch_results
os.makedirs(output_dir, exist_ok=True)
if processed_image.mode == 'RGBA':
processed_image = processed_image.convert("RGB")
processed_image.save(os.path.join(output_dir, filename))
def img2img(id_task: str, mode: int, prompt: str, negative_prompt: str, prompt_styles, init_img, sketch, init_img_with_mask, inpaint_color_sketch, inpaint_color_sketch_orig, init_img_inpaint, init_mask_inpaint, steps: int, sampler_index: int, mask_blur: int, mask_alpha: float, inpainting_fill: int, restore_faces: bool, tiling: bool, n_iter: int, batch_size: int, cfg_scale: float, image_cfg_scale: float, denoising_strength: float, seed: int, subseed: int, subseed_strength: float, seed_resize_from_h: int, seed_resize_from_w: int, seed_enable_extras: bool, selected_scale_tab: int, height: int, width: int, scale_by: float, resize_mode: int, inpaint_full_res: bool, inpaint_full_res_padding: int, inpainting_mask_invert: int, img2img_batch_input_dir: str, img2img_batch_output_dir: str, img2img_batch_inpaint_mask_dir: str, override_settings_texts, *args): def img2img(id_task: str, mode: int, prompt: str, negative_prompt: str, prompt_styles, init_img, sketch, init_img_with_mask, inpaint_color_sketch, inpaint_color_sketch_orig, init_img_inpaint, init_mask_inpaint, steps: int, sampler_name: str, mask_blur: int, mask_alpha: float, inpainting_fill: int, n_iter: int, batch_size: int, cfg_scale: float, image_cfg_scale: float, denoising_strength: float, selected_scale_tab: int, height: int, width: int, scale_by: float, resize_mode: int, inpaint_full_res: bool, inpaint_full_res_padding: int, inpainting_mask_invert: int, img2img_batch_input_dir: str, img2img_batch_output_dir: str, img2img_batch_inpaint_mask_dir: str, override_settings_texts, img2img_batch_use_png_info: bool, img2img_batch_png_info_props: list, img2img_batch_png_info_dir: str, request: gr.Request, *args):
override_settings = create_override_settings_dict(override_settings_texts) override_settings = create_override_settings_dict(override_settings_texts)
is_batch = mode == 5 is_batch = mode == 5
if mode == 0: # img2img if mode == 0: # img2img
image = init_img.convert("RGB") image = init_img
mask = None mask = None
elif mode == 1: # img2img sketch elif mode == 1: # img2img sketch
image = sketch.convert("RGB") image = sketch
mask = None mask = None
elif mode == 2: # inpaint elif mode == 2: # inpaint
image, mask = init_img_with_mask["image"], init_img_with_mask["mask"] image, mask = init_img_with_mask["image"], init_img_with_mask["mask"]
alpha_mask = ImageOps.invert(image.split()[-1]).convert('L').point(lambda x: 255 if x > 0 else 0, mode='1') mask = processing.create_binary_mask(mask)
mask = mask.convert('L').point(lambda x: 255 if x > 128 else 0, mode='1')
mask = ImageChops.lighter(alpha_mask, mask).convert('L')
image = image.convert("RGB")
elif mode == 3: # inpaint sketch elif mode == 3: # inpaint sketch
image = inpaint_color_sketch image = inpaint_color_sketch
orig = inpaint_color_sketch_orig or inpaint_color_sketch orig = inpaint_color_sketch_orig or inpaint_color_sketch
@@ -122,7 +168,6 @@ def img2img(id_task: str, mode: int, prompt: str, negative_prompt: str, prompt_s
mask = ImageEnhance.Brightness(mask).enhance(1 - mask_alpha / 100) mask = ImageEnhance.Brightness(mask).enhance(1 - mask_alpha / 100)
blur = ImageFilter.GaussianBlur(mask_blur) blur = ImageFilter.GaussianBlur(mask_blur)
image = Image.composite(image.filter(blur), orig, mask.filter(blur)) image = Image.composite(image.filter(blur), orig, mask.filter(blur))
image = image.convert("RGB")
elif mode == 4: # inpaint upload mask elif mode == 4: # inpaint upload mask
image = init_img_inpaint image = init_img_inpaint
mask = init_mask_inpaint mask = init_mask_inpaint
@@ -149,21 +194,13 @@ def img2img(id_task: str, mode: int, prompt: str, negative_prompt: str, prompt_s
prompt=prompt, prompt=prompt,
negative_prompt=negative_prompt, negative_prompt=negative_prompt,
styles=prompt_styles, styles=prompt_styles,
seed=seed, sampler_name=sampler_name,
subseed=subseed,
subseed_strength=subseed_strength,
seed_resize_from_h=seed_resize_from_h,
seed_resize_from_w=seed_resize_from_w,
seed_enable_extras=seed_enable_extras,
sampler_name=sd_samplers.samplers_for_img2img[sampler_index].name,
batch_size=batch_size, batch_size=batch_size,
n_iter=n_iter, n_iter=n_iter,
steps=steps, steps=steps,
cfg_scale=cfg_scale, cfg_scale=cfg_scale,
width=width, width=width,
height=height, height=height,
restore_faces=restore_faces,
tiling=tiling,
init_images=[image], init_images=[image],
mask=mask, mask=mask,
mask_blur=mask_blur, mask_blur=mask_blur,
@@ -180,25 +217,26 @@ def img2img(id_task: str, mode: int, prompt: str, negative_prompt: str, prompt_s
p.scripts = modules.scripts.scripts_img2img p.scripts = modules.scripts.scripts_img2img
p.script_args = args p.script_args = args
if shared.cmd_opts.enable_console_prompts: p.user = request.username
if shared.opts.enable_console_prompts:
print(f"\nimg2img: {prompt}", file=shared.progress_print_out) print(f"\nimg2img: {prompt}", file=shared.progress_print_out)
if mask: if mask:
p.extra_generation_params["Mask blur"] = mask_blur p.extra_generation_params["Mask blur"] = mask_blur
with closing(p):
if is_batch: if is_batch:
assert not shared.cmd_opts.hide_ui_dir_config, "Launched with --hide-ui-dir-config, batch img2img disabled" assert not shared.cmd_opts.hide_ui_dir_config, "Launched with --hide-ui-dir-config, batch img2img disabled"
processed = process_batch(p, img2img_batch_input_dir, img2img_batch_output_dir, img2img_batch_inpaint_mask_dir, args, to_scale=selected_scale_tab == 1, scale_by=scale_by, use_png_info=img2img_batch_use_png_info, png_info_props=img2img_batch_png_info_props, png_info_dir=img2img_batch_png_info_dir)
process_batch(p, img2img_batch_input_dir, img2img_batch_output_dir, img2img_batch_inpaint_mask_dir, args, to_scale=selected_scale_tab == 1, scale_by=scale_by) if processed is None:
processed = Processed(p, [], p.seed, "") processed = Processed(p, [], p.seed, "")
else: else:
processed = modules.scripts.scripts_img2img.run(p, *args) processed = modules.scripts.scripts_img2img.run(p, *args)
if processed is None: if processed is None:
processed = process_images(p) processed = process_images(p)
p.close()
shared.total_tqdm.clear() shared.total_tqdm.clear()
generation_info_js = processed.js() generation_info_js = processed.js()
@@ -208,4 +246,4 @@ def img2img(id_task: str, mode: int, prompt: str, negative_prompt: str, prompt_s
if opts.do_not_show_images: if opts.do_not_show_images:
processed.images = [] processed.images = []
return processed.images, generation_info_js, plaintext_to_html(processed.info), plaintext_to_html(processed.comments) return processed.images, generation_info_js, plaintext_to_html(processed.info), plaintext_to_html(processed.comments, classname="comments")
+168
View File
@@ -0,0 +1,168 @@
import importlib
import logging
import sys
import warnings
from threading import Thread
from modules.timer import startup_timer
def imports():
logging.getLogger("torch.distributed.nn").setLevel(logging.ERROR) # sshh...
logging.getLogger("xformers").addFilter(lambda record: 'A matching Triton is not available' not in record.getMessage())
import torch # noqa: F401
startup_timer.record("import torch")
import pytorch_lightning # noqa: F401
startup_timer.record("import torch")
warnings.filterwarnings(action="ignore", category=DeprecationWarning, module="pytorch_lightning")
warnings.filterwarnings(action="ignore", category=UserWarning, module="torchvision")
import gradio # noqa: F401
startup_timer.record("import gradio")
from modules import paths, timer, import_hook, errors # noqa: F401
startup_timer.record("setup paths")
import ldm.modules.encoders.modules # noqa: F401
startup_timer.record("import ldm")
import sgm.modules.encoders.modules # noqa: F401
startup_timer.record("import sgm")
from modules import shared_init
shared_init.initialize()
startup_timer.record("initialize shared")
from modules import processing, gradio_extensons, ui # noqa: F401
startup_timer.record("other imports")
def check_versions():
from modules.shared_cmd_options import cmd_opts
if not cmd_opts.skip_version_check:
from modules import errors
errors.check_versions()
def initialize():
from modules import initialize_util
initialize_util.fix_torch_version()
initialize_util.fix_asyncio_event_loop_policy()
initialize_util.validate_tls_options()
initialize_util.configure_sigint_handler()
initialize_util.configure_opts_onchange()
from modules import modelloader
modelloader.cleanup_models()
from modules import sd_models
sd_models.setup_model()
startup_timer.record("setup SD model")
from modules.shared_cmd_options import cmd_opts
from modules import codeformer_model
warnings.filterwarnings(action="ignore", category=UserWarning, module="torchvision.transforms.functional_tensor")
codeformer_model.setup_model(cmd_opts.codeformer_models_path)
startup_timer.record("setup codeformer")
from modules import gfpgan_model
gfpgan_model.setup_model(cmd_opts.gfpgan_models_path)
startup_timer.record("setup gfpgan")
initialize_rest(reload_script_modules=False)
def initialize_rest(*, reload_script_modules=False):
"""
Called both from initialize() and when reloading the webui.
"""
from modules.shared_cmd_options import cmd_opts
from modules import sd_samplers
sd_samplers.set_samplers()
startup_timer.record("set samplers")
from modules import extensions
extensions.list_extensions()
startup_timer.record("list extensions")
from modules import initialize_util
initialize_util.restore_config_state_file()
startup_timer.record("restore config state file")
from modules import shared, upscaler, scripts
if cmd_opts.ui_debug_mode:
shared.sd_upscalers = upscaler.UpscalerLanczos().scalers
scripts.load_scripts()
return
from modules import sd_models
sd_models.list_models()
startup_timer.record("list SD models")
from modules import localization
localization.list_localizations(cmd_opts.localizations_dir)
startup_timer.record("list localizations")
with startup_timer.subcategory("load scripts"):
scripts.load_scripts()
if reload_script_modules:
for module in [module for name, module in sys.modules.items() if name.startswith("modules.ui")]:
importlib.reload(module)
startup_timer.record("reload script modules")
from modules import modelloader
modelloader.load_upscalers()
startup_timer.record("load upscalers")
from modules import sd_vae
sd_vae.refresh_vae_list()
startup_timer.record("refresh VAE")
from modules import textual_inversion
textual_inversion.textual_inversion.list_textual_inversion_templates()
startup_timer.record("refresh textual inversion templates")
from modules import script_callbacks, sd_hijack_optimizations, sd_hijack
script_callbacks.on_list_optimizers(sd_hijack_optimizations.list_optimizers)
sd_hijack.list_optimizers()
startup_timer.record("scripts list_optimizers")
from modules import sd_unet
sd_unet.list_unets()
startup_timer.record("scripts list_unets")
def load_model():
"""
Accesses shared.sd_model property to load model.
After it's available, if it has been loaded before this access by some extension,
its optimization may be None because the list of optimizaers has neet been filled
by that time, so we apply optimization again.
"""
shared.sd_model # noqa: B018
if sd_hijack.current_optimizer is None:
sd_hijack.apply_optimizations()
from modules import devices
devices.first_time_calculation()
if not shared.cmd_opts.skip_load_model_at_start:
Thread(target=load_model).start()
from modules import shared_items
shared_items.reload_hypernetworks()
startup_timer.record("reload hypernetworks")
from modules import ui_extra_networks
ui_extra_networks.initialize()
ui_extra_networks.register_default_pages()
from modules import extra_networks
extra_networks.initialize()
extra_networks.register_default_extra_networks()
startup_timer.record("initialize extra networks")
+206
View File
@@ -0,0 +1,206 @@
import json
import os
import signal
import sys
import re
from modules.timer import startup_timer
def gradio_server_name():
from modules.shared_cmd_options import cmd_opts
if cmd_opts.server_name:
return cmd_opts.server_name
else:
return "0.0.0.0" if cmd_opts.listen else None
def fix_torch_version():
import torch
# Truncate version number of nightly/local build of PyTorch to not cause exceptions with CodeFormer or Safetensors
if ".dev" in torch.__version__ or "+git" in torch.__version__:
torch.__long_version__ = torch.__version__
torch.__version__ = re.search(r'[\d.]+[\d]', torch.__version__).group(0)
def fix_asyncio_event_loop_policy():
"""
The default `asyncio` event loop policy only automatically creates
event loops in the main threads. Other threads must create event
loops explicitly or `asyncio.get_event_loop` (and therefore
`.IOLoop.current`) will fail. Installing this policy allows event
loops to be created automatically on any thread, matching the
behavior of Tornado versions prior to 5.0 (or 5.0 on Python 2).
"""
import asyncio
if sys.platform == "win32" and hasattr(asyncio, "WindowsSelectorEventLoopPolicy"):
# "Any thread" and "selector" should be orthogonal, but there's not a clean
# interface for composing policies so pick the right base.
_BasePolicy = asyncio.WindowsSelectorEventLoopPolicy # type: ignore
else:
_BasePolicy = asyncio.DefaultEventLoopPolicy
class AnyThreadEventLoopPolicy(_BasePolicy): # type: ignore
"""Event loop policy that allows loop creation on any thread.
Usage::
asyncio.set_event_loop_policy(AnyThreadEventLoopPolicy())
"""
def get_event_loop(self) -> asyncio.AbstractEventLoop:
try:
return super().get_event_loop()
except (RuntimeError, AssertionError):
# This was an AssertionError in python 3.4.2 (which ships with debian jessie)
# and changed to a RuntimeError in 3.4.3.
# "There is no current event loop in thread %r"
loop = self.new_event_loop()
self.set_event_loop(loop)
return loop
asyncio.set_event_loop_policy(AnyThreadEventLoopPolicy())
def restore_config_state_file():
from modules import shared, config_states
config_state_file = shared.opts.restore_config_state_file
if config_state_file == "":
return
shared.opts.restore_config_state_file = ""
shared.opts.save(shared.config_filename)
if os.path.isfile(config_state_file):
print(f"*** About to restore extension state from file: {config_state_file}")
with open(config_state_file, "r", encoding="utf-8") as f:
config_state = json.load(f)
config_states.restore_extension_config(config_state)
startup_timer.record("restore extension config")
elif config_state_file:
print(f"!!! Config state backup not found: {config_state_file}")
def validate_tls_options():
from modules.shared_cmd_options import cmd_opts
if not (cmd_opts.tls_keyfile and cmd_opts.tls_certfile):
return
try:
if not os.path.exists(cmd_opts.tls_keyfile):
print("Invalid path to TLS keyfile given")
if not os.path.exists(cmd_opts.tls_certfile):
print(f"Invalid path to TLS certfile: '{cmd_opts.tls_certfile}'")
except TypeError:
cmd_opts.tls_keyfile = cmd_opts.tls_certfile = None
print("TLS setup invalid, running webui without TLS")
else:
print("Running with TLS")
startup_timer.record("TLS")
def get_gradio_auth_creds():
"""
Convert the gradio_auth and gradio_auth_path commandline arguments into
an iterable of (username, password) tuples.
"""
from modules.shared_cmd_options import cmd_opts
def process_credential_line(s):
s = s.strip()
if not s:
return None
return tuple(s.split(':', 1))
if cmd_opts.gradio_auth:
for cred in cmd_opts.gradio_auth.split(','):
cred = process_credential_line(cred)
if cred:
yield cred
if cmd_opts.gradio_auth_path:
with open(cmd_opts.gradio_auth_path, 'r', encoding="utf8") as file:
for line in file.readlines():
for cred in line.strip().split(','):
cred = process_credential_line(cred)
if cred:
yield cred
def dumpstacks():
import threading
import traceback
id2name = {th.ident: th.name for th in threading.enumerate()}
code = []
for threadId, stack in sys._current_frames().items():
code.append(f"\n# Thread: {id2name.get(threadId, '')}({threadId})")
for filename, lineno, name, line in traceback.extract_stack(stack):
code.append(f"""File: "{filename}", line {lineno}, in {name}""")
if line:
code.append(" " + line.strip())
print("\n".join(code))
def configure_sigint_handler():
# make the program just exit at ctrl+c without waiting for anything
from modules import shared
def sigint_handler(sig, frame):
print(f'Interrupted with signal {sig} in {frame}')
if shared.opts.dump_stacks_on_signal:
dumpstacks()
os._exit(0)
if not os.environ.get("COVERAGE_RUN"):
# Don't install the immediate-quit handler when running under coverage,
# as then the coverage report won't be generated.
signal.signal(signal.SIGINT, sigint_handler)
def configure_opts_onchange():
from modules import shared, sd_models, sd_vae, ui_tempdir, sd_hijack
from modules.call_queue import wrap_queued_call
shared.opts.onchange("sd_model_checkpoint", wrap_queued_call(lambda: sd_models.reload_model_weights()), call=False)
shared.opts.onchange("sd_vae", wrap_queued_call(lambda: sd_vae.reload_vae_weights()), call=False)
shared.opts.onchange("sd_vae_overrides_per_model_preferences", wrap_queued_call(lambda: sd_vae.reload_vae_weights()), call=False)
shared.opts.onchange("temp_dir", ui_tempdir.on_tmpdir_changed)
shared.opts.onchange("gradio_theme", shared.reload_gradio_theme)
shared.opts.onchange("cross_attention_optimization", wrap_queued_call(lambda: sd_hijack.model_hijack.redo_hijack(shared.sd_model)), call=False)
startup_timer.record("opts onchange")
def setup_middleware(app):
from starlette.middleware.gzip import GZipMiddleware
app.middleware_stack = None # reset current middleware to allow modifying user provided list
app.add_middleware(GZipMiddleware, minimum_size=1000)
configure_cors_middleware(app)
app.build_middleware_stack() # rebuild middleware stack on-the-fly
def configure_cors_middleware(app):
from starlette.middleware.cors import CORSMiddleware
from modules.shared_cmd_options import cmd_opts
cors_options = {
"allow_methods": ["*"],
"allow_headers": ["*"],
"allow_credentials": True,
}
if cmd_opts.cors_allow_origins:
cors_options["allow_origins"] = cmd_opts.cors_allow_origins.split(',')
if cmd_opts.cors_allow_origins_regex:
cors_options["allow_origin_regex"] = cmd_opts.cors_allow_origins_regex
app.add_middleware(CORSMiddleware, **cors_options)
+1 -3
View File
@@ -184,10 +184,8 @@ class InterrogateModels:
def interrogate(self, pil_image): def interrogate(self, pil_image):
res = "" res = ""
shared.state.begin() shared.state.begin(job="interrogate")
shared.state.job = 'interrogate'
try: try:
if shared.cmd_opts.lowvram or shared.cmd_opts.medvram:
lowvram.send_everything_to_cpu() lowvram.send_everything_to_cpu()
devices.torch_gc() devices.torch_gc()
+135 -30
View File
@@ -1,6 +1,9 @@
# this scripts installs necessary requirements and launches main program in webui.py # this scripts installs necessary requirements and launches main program in webui.py
import logging
import re
import subprocess import subprocess
import os import os
import shutil
import sys import sys
import importlib.util import importlib.util
import platform import platform
@@ -9,8 +12,11 @@ from functools import lru_cache
from modules import cmd_args, errors from modules import cmd_args, errors
from modules.paths_internal import script_path, extensions_dir from modules.paths_internal import script_path, extensions_dir
from modules.timer import startup_timer
from modules import logging_config
args, _ = cmd_args.parser.parse_known_args() args, _ = cmd_args.parser.parse_known_args()
logging_config.setup_logging(args.loglevel)
python = sys.executable python = sys.executable
git = os.environ.get('GIT', "git") git = os.environ.get('GIT', "git")
@@ -58,7 +64,7 @@ Use --skip-python-version-check to suppress this warning.
@lru_cache() @lru_cache()
def commit_hash(): def commit_hash():
try: try:
return subprocess.check_output([git, "rev-parse", "HEAD"], shell=False, encoding='utf8').strip() return subprocess.check_output([git, "-C", script_path, "rev-parse", "HEAD"], shell=False, encoding='utf8').strip()
except Exception: except Exception:
return "<none>" return "<none>"
@@ -66,13 +72,15 @@ def commit_hash():
@lru_cache() @lru_cache()
def git_tag(): def git_tag():
try: try:
return subprocess.check_output([git, "describe", "--tags"], shell=False, encoding='utf8').strip() return subprocess.check_output([git, "-C", script_path, "describe", "--tags"], shell=False, encoding='utf8').strip()
except Exception: except Exception:
try: try:
from pathlib import Path
changelog_md = Path(__file__).parent.parent / "CHANGELOG.md" changelog_md = os.path.join(os.path.dirname(os.path.dirname(__file__)), "CHANGELOG.md")
with changelog_md.open(encoding="utf-8") as file: with open(changelog_md, "r", encoding="utf-8") as file:
return next((line.strip() for line in file if line.strip()), "<none>") line = next((line.strip() for line in file if line.strip()), "<none>")
line = line.replace("## ", "")
return line
except Exception: except Exception:
return "<none>" return "<none>"
@@ -135,6 +143,25 @@ def check_run_python(code: str) -> bool:
return result.returncode == 0 return result.returncode == 0
def git_fix_workspace(dir, name):
run(f'"{git}" -C "{dir}" fetch --refetch --no-auto-gc', f"Fetching all contents for {name}", f"Couldn't fetch {name}", live=True)
run(f'"{git}" -C "{dir}" gc --aggressive --prune=now', f"Pruning {name}", f"Couldn't prune {name}", live=True)
return
def run_git(dir, name, command, desc=None, errdesc=None, custom_env=None, live: bool = default_command_live, autofix=True):
try:
return run(f'"{git}" -C "{dir}" {command}', desc=desc, errdesc=errdesc, custom_env=custom_env, live=live)
except RuntimeError:
if not autofix:
raise
print(f"{errdesc}, attempting autofix...")
git_fix_workspace(dir, name)
return run(f'"{git}" -C "{dir}" {command}', desc=desc, errdesc=errdesc, custom_env=custom_env, live=live)
def git_clone(url, dir, name, commithash=None): def git_clone(url, dir, name, commithash=None):
# TODO clone into temporary dir and move if successful # TODO clone into temporary dir and move if successful
@@ -142,15 +169,24 @@ def git_clone(url, dir, name, commithash=None):
if commithash is None: if commithash is None:
return return
current_hash = run(f'"{git}" -C "{dir}" rev-parse HEAD', None, f"Couldn't determine {name}'s hash: {commithash}").strip() current_hash = run_git(dir, name, 'rev-parse HEAD', None, f"Couldn't determine {name}'s hash: {commithash}", live=False).strip()
if current_hash == commithash: if current_hash == commithash:
return return
run(f'"{git}" -C "{dir}" fetch', f"Fetching updates for {name}...", f"Couldn't fetch {name}") if run_git(dir, name, 'config --get remote.origin.url', None, f"Couldn't determine {name}'s origin URL", live=False).strip() != url:
run(f'"{git}" -C "{dir}" checkout {commithash}', f"Checking out commit for {name} with hash: {commithash}...", f"Couldn't checkout commit {commithash} for {name}") run_git(dir, name, f'remote set-url origin "{url}"', None, f"Failed to set {name}'s origin URL", live=False)
run_git(dir, name, 'fetch', f"Fetching updates for {name}...", f"Couldn't fetch {name}", autofix=False)
run_git(dir, name, f'checkout {commithash}', f"Checking out commit for {name} with hash: {commithash}...", f"Couldn't checkout commit {commithash} for {name}", live=True)
return return
run(f'"{git}" clone "{url}" "{dir}"', f"Cloning {name} into {dir}...", f"Couldn't clone {name}") try:
run(f'"{git}" clone "{url}" "{dir}"', f"Cloning {name} into {dir}...", f"Couldn't clone {name}", live=True)
except RuntimeError:
shutil.rmtree(dir, ignore_errors=True)
raise
if commithash is not None: if commithash is not None:
run(f'"{git}" -C "{dir}" checkout {commithash}', None, "Couldn't checkout {name}'s hash: {commithash}") run(f'"{git}" -C "{dir}" checkout {commithash}', None, "Couldn't checkout {name}'s hash: {commithash}")
@@ -190,9 +226,11 @@ def run_extension_installer(extension_dir):
try: try:
env = os.environ.copy() env = os.environ.copy()
env['PYTHONPATH'] = os.path.abspath(".") env['PYTHONPATH'] = f"{os.path.abspath('.')}{os.pathsep}{env.get('PYTHONPATH', '')}"
print(run(f'"{python}" "{path_installer}"', errdesc=f"Error running install.py for extension {extension_dir}", custom_env=env)) stdout = run(f'"{python}" "{path_installer}"', errdesc=f"Error running install.py for extension {extension_dir}", custom_env=env).strip()
if stdout:
print(stdout)
except Exception as e: except Exception as e:
errors.report(str(e)) errors.report(str(e))
@@ -210,7 +248,7 @@ def list_extensions(settings_file):
disabled_extensions = set(settings.get('disabled_extensions', [])) disabled_extensions = set(settings.get('disabled_extensions', []))
disable_all_extensions = settings.get('disable_all_extensions', 'none') disable_all_extensions = settings.get('disable_all_extensions', 'none')
if disable_all_extensions != 'none': if disable_all_extensions != 'none' or args.disable_extra_extensions or args.disable_all_extensions or not os.path.isdir(extensions_dir):
return [] return []
return [x for x in os.listdir(extensions_dir) if x not in disabled_extensions] return [x for x in os.listdir(extensions_dir) if x not in disabled_extensions]
@@ -220,8 +258,53 @@ def run_extensions_installers(settings_file):
if not os.path.isdir(extensions_dir): if not os.path.isdir(extensions_dir):
return return
with startup_timer.subcategory("run extensions installers"):
for dirname_extension in list_extensions(settings_file): for dirname_extension in list_extensions(settings_file):
run_extension_installer(os.path.join(extensions_dir, dirname_extension)) logging.debug(f"Installing {dirname_extension}")
path = os.path.join(extensions_dir, dirname_extension)
if os.path.isdir(path):
run_extension_installer(path)
startup_timer.record(dirname_extension)
re_requirement = re.compile(r"\s*([-_a-zA-Z0-9]+)\s*(?:==\s*([-+_.a-zA-Z0-9]+))?\s*")
def requirements_met(requirements_file):
"""
Does a simple parse of a requirements.txt file to determine if all rerqirements in it
are already installed. Returns True if so, False if not installed or parsing fails.
"""
import importlib.metadata
import packaging.version
with open(requirements_file, "r", encoding="utf8") as file:
for line in file:
if line.strip() == "":
continue
m = re.match(re_requirement, line)
if m is None:
return False
package = m.group(1).strip()
version_required = (m.group(2) or "").strip()
if version_required == "":
continue
try:
version_installed = importlib.metadata.version(package)
except Exception:
return False
if packaging.version.parse(version_required) != packaging.version.parse(version_installed):
return False
return True
def prepare_environment(): def prepare_environment():
@@ -230,22 +313,23 @@ def prepare_environment():
requirements_file = os.environ.get('REQS_FILE', "requirements_versions.txt") requirements_file = os.environ.get('REQS_FILE', "requirements_versions.txt")
xformers_package = os.environ.get('XFORMERS_PACKAGE', 'xformers==0.0.20') xformers_package = os.environ.get('XFORMERS_PACKAGE', 'xformers==0.0.20')
gfpgan_package = os.environ.get('GFPGAN_PACKAGE', "https://github.com/TencentARC/GFPGAN/archive/8d2447a2d918f8eba5a4a01463fd48e45126a379.zip")
clip_package = os.environ.get('CLIP_PACKAGE', "https://github.com/openai/CLIP/archive/d50d76daa670286dd6cacf3bcd80b5e4823fc8e1.zip") clip_package = os.environ.get('CLIP_PACKAGE', "https://github.com/openai/CLIP/archive/d50d76daa670286dd6cacf3bcd80b5e4823fc8e1.zip")
openclip_package = os.environ.get('OPENCLIP_PACKAGE', "https://github.com/mlfoundations/open_clip/archive/bb6e834e9c70d9c27d0dc3ecedeebeaeb1ffad6b.zip") openclip_package = os.environ.get('OPENCLIP_PACKAGE', "https://github.com/mlfoundations/open_clip/archive/bb6e834e9c70d9c27d0dc3ecedeebeaeb1ffad6b.zip")
stable_diffusion_repo = os.environ.get('STABLE_DIFFUSION_REPO', "https://github.com/Stability-AI/stablediffusion.git") stable_diffusion_repo = os.environ.get('STABLE_DIFFUSION_REPO', "https://github.com/Stability-AI/stablediffusion.git")
stable_diffusion_xl_repo = os.environ.get('STABLE_DIFFUSION_XL_REPO', "https://github.com/Stability-AI/generative-models.git")
k_diffusion_repo = os.environ.get('K_DIFFUSION_REPO', 'https://github.com/crowsonkb/k-diffusion.git') k_diffusion_repo = os.environ.get('K_DIFFUSION_REPO', 'https://github.com/crowsonkb/k-diffusion.git')
codeformer_repo = os.environ.get('CODEFORMER_REPO', 'https://github.com/sczhou/CodeFormer.git') codeformer_repo = os.environ.get('CODEFORMER_REPO', 'https://github.com/sczhou/CodeFormer.git')
blip_repo = os.environ.get('BLIP_REPO', 'https://github.com/salesforce/BLIP.git') blip_repo = os.environ.get('BLIP_REPO', 'https://github.com/salesforce/BLIP.git')
stable_diffusion_commit_hash = os.environ.get('STABLE_DIFFUSION_COMMIT_HASH', "cf1d67a6fd5ea1aa600c4df58e5b47da45f6bdbf") stable_diffusion_commit_hash = os.environ.get('STABLE_DIFFUSION_COMMIT_HASH', "cf1d67a6fd5ea1aa600c4df58e5b47da45f6bdbf")
k_diffusion_commit_hash = os.environ.get('K_DIFFUSION_COMMIT_HASH', "c9fe758757e022f05ca5a53fa8fac28889e4f1cf") stable_diffusion_xl_commit_hash = os.environ.get('STABLE_DIFFUSION_XL_COMMIT_HASH', "45c443b316737a4ab6e40413d7794a7f5657c19f")
k_diffusion_commit_hash = os.environ.get('K_DIFFUSION_COMMIT_HASH', "ab527a9a6d347f364e3d185ba6d714e22d80cb3c")
codeformer_commit_hash = os.environ.get('CODEFORMER_COMMIT_HASH', "c5b4593074ba6214284d6acd5f1719b6c5d739af") codeformer_commit_hash = os.environ.get('CODEFORMER_COMMIT_HASH', "c5b4593074ba6214284d6acd5f1719b6c5d739af")
blip_commit_hash = os.environ.get('BLIP_COMMIT_HASH', "48211a1594f1321b00f14c9f7a5b4813144b2fb9") blip_commit_hash = os.environ.get('BLIP_COMMIT_HASH', "48211a1594f1321b00f14c9f7a5b4813144b2fb9")
try: try:
# the existance of this file is a signal to webui.sh/bat that webui needs to be restarted when it stops execution # the existence of this file is a signal to webui.sh/bat that webui needs to be restarted when it stops execution
os.remove(os.path.join(script_path, "tmp", "restart")) os.remove(os.path.join(script_path, "tmp", "restart"))
os.environ.setdefault('SD_WEBUI_RESTARTING', '1') os.environ.setdefault('SD_WEBUI_RESTARTING', '1')
except OSError: except OSError:
@@ -254,8 +338,11 @@ def prepare_environment():
if not args.skip_python_version_check: if not args.skip_python_version_check:
check_python_version() check_python_version()
startup_timer.record("checks")
commit = commit_hash() commit = commit_hash()
tag = git_tag() tag = git_tag()
startup_timer.record("git version info")
print(f"Python {sys.version}") print(f"Python {sys.version}")
print(f"Version: {tag}") print(f"Version: {tag}")
@@ -263,64 +350,69 @@ def prepare_environment():
if args.reinstall_torch or not is_installed("torch") or not is_installed("torchvision"): if args.reinstall_torch or not is_installed("torch") or not is_installed("torchvision"):
run(f'"{python}" -m {torch_command}', "Installing torch and torchvision", "Couldn't install torch", live=True) run(f'"{python}" -m {torch_command}', "Installing torch and torchvision", "Couldn't install torch", live=True)
startup_timer.record("install torch")
if not args.skip_torch_cuda_test and not check_run_python("import torch; assert torch.cuda.is_available()"): if not args.skip_torch_cuda_test and not check_run_python("import torch; assert torch.cuda.is_available()"):
raise RuntimeError( raise RuntimeError(
'Torch is not able to use GPU; ' 'Torch is not able to use GPU; '
'add --skip-torch-cuda-test to COMMANDLINE_ARGS variable to disable this check' 'add --skip-torch-cuda-test to COMMANDLINE_ARGS variable to disable this check'
) )
startup_timer.record("torch GPU test")
if not is_installed("gfpgan"):
run_pip(f"install {gfpgan_package}", "gfpgan")
if not is_installed("clip"): if not is_installed("clip"):
run_pip(f"install {clip_package}", "clip") run_pip(f"install {clip_package}", "clip")
startup_timer.record("install clip")
if not is_installed("open_clip"): if not is_installed("open_clip"):
run_pip(f"install {openclip_package}", "open_clip") run_pip(f"install {openclip_package}", "open_clip")
startup_timer.record("install open_clip")
if (not is_installed("xformers") or args.reinstall_xformers) and args.xformers: if (not is_installed("xformers") or args.reinstall_xformers) and args.xformers:
if platform.system() == "Windows":
if platform.python_version().startswith("3.10"):
run_pip(f"install -U -I --no-deps {xformers_package}", "xformers", live=True)
else:
print("Installation of xformers is not supported in this version of Python.")
print("You can also check this and build manually: https://github.com/AUTOMATIC1111/stable-diffusion-webui/wiki/Xformers#building-xformers-on-windows-by-duckness")
if not is_installed("xformers"):
exit(0)
elif platform.system() == "Linux":
run_pip(f"install -U -I --no-deps {xformers_package}", "xformers") run_pip(f"install -U -I --no-deps {xformers_package}", "xformers")
startup_timer.record("install xformers")
if not is_installed("ngrok") and args.ngrok: if not is_installed("ngrok") and args.ngrok:
run_pip("install ngrok", "ngrok") run_pip("install ngrok", "ngrok")
startup_timer.record("install ngrok")
os.makedirs(os.path.join(script_path, dir_repos), exist_ok=True) os.makedirs(os.path.join(script_path, dir_repos), exist_ok=True)
git_clone(stable_diffusion_repo, repo_dir('stable-diffusion-stability-ai'), "Stable Diffusion", stable_diffusion_commit_hash) git_clone(stable_diffusion_repo, repo_dir('stable-diffusion-stability-ai'), "Stable Diffusion", stable_diffusion_commit_hash)
git_clone(stable_diffusion_xl_repo, repo_dir('generative-models'), "Stable Diffusion XL", stable_diffusion_xl_commit_hash)
git_clone(k_diffusion_repo, repo_dir('k-diffusion'), "K-diffusion", k_diffusion_commit_hash) git_clone(k_diffusion_repo, repo_dir('k-diffusion'), "K-diffusion", k_diffusion_commit_hash)
git_clone(codeformer_repo, repo_dir('CodeFormer'), "CodeFormer", codeformer_commit_hash) git_clone(codeformer_repo, repo_dir('CodeFormer'), "CodeFormer", codeformer_commit_hash)
git_clone(blip_repo, repo_dir('BLIP'), "BLIP", blip_commit_hash) git_clone(blip_repo, repo_dir('BLIP'), "BLIP", blip_commit_hash)
startup_timer.record("clone repositores")
if not is_installed("lpips"): if not is_installed("lpips"):
run_pip(f"install -r \"{os.path.join(repo_dir('CodeFormer'), 'requirements.txt')}\"", "requirements for CodeFormer") run_pip(f"install -r \"{os.path.join(repo_dir('CodeFormer'), 'requirements.txt')}\"", "requirements for CodeFormer")
startup_timer.record("install CodeFormer requirements")
if not os.path.isfile(requirements_file): if not os.path.isfile(requirements_file):
requirements_file = os.path.join(script_path, requirements_file) requirements_file = os.path.join(script_path, requirements_file)
run_pip(f"install -r \"{requirements_file}\"", "requirements")
if not requirements_met(requirements_file):
run_pip(f"install -r \"{requirements_file}\"", "requirements")
startup_timer.record("install requirements")
if not args.skip_install:
run_extensions_installers(settings_file=args.ui_settings_file) run_extensions_installers(settings_file=args.ui_settings_file)
if args.update_check: if args.update_check:
version_check(commit) version_check(commit)
startup_timer.record("check version")
if args.update_all_extensions: if args.update_all_extensions:
git_pull_recursive(extensions_dir) git_pull_recursive(extensions_dir)
startup_timer.record("update extensions")
if "--exit" in sys.argv: if "--exit" in sys.argv:
print("Exiting because of --exit argument") print("Exiting because of --exit argument")
exit(0) exit(0)
def configure_for_tests(): def configure_for_tests():
if "--api" not in sys.argv: if "--api" not in sys.argv:
sys.argv.append("--api") sys.argv.append("--api")
@@ -342,3 +434,16 @@ def start():
webui.api_only() webui.api_only()
else: else:
webui.webui() webui.webui()
def dump_sysinfo():
from modules import sysinfo
import datetime
text = sysinfo.get()
filename = f"sysinfo-{datetime.datetime.utcnow().strftime('%Y-%m-%d-%H-%M')}.json"
with open(filename, "w", encoding="utf8") as file:
file.write(text)
return filename
+9 -7
View File
@@ -1,7 +1,7 @@
import json import json
import os import os
from modules import errors from modules import errors, scripts
localizations = {} localizations = {}
@@ -14,21 +14,23 @@ def list_localizations(dirname):
if ext.lower() != ".json": if ext.lower() != ".json":
continue continue
localizations[fn] = os.path.join(dirname, file) localizations[fn] = [os.path.join(dirname, file)]
from modules import scripts
for file in scripts.list_scripts("localizations", ".json"): for file in scripts.list_scripts("localizations", ".json"):
fn, ext = os.path.splitext(file.filename) fn, ext = os.path.splitext(file.filename)
localizations[fn] = file.path if fn not in localizations:
localizations[fn] = []
localizations[fn].append(file.path)
def localization_js(current_localization_name: str) -> str: def localization_js(current_localization_name: str) -> str:
fn = localizations.get(current_localization_name, None) fns = localizations.get(current_localization_name, None)
data = {} data = {}
if fn is not None: if fns is not None:
for fn in fns:
try: try:
with open(fn, "r", encoding="utf8") as file: with open(fn, "r", encoding="utf8") as file:
data = json.load(file) data.update(json.load(file))
except Exception: except Exception:
errors.report(f"Error loading localization from {fn}", exc_info=True) errors.report(f"Error loading localization from {fn}", exc_info=True)
+41
View File
@@ -0,0 +1,41 @@
import os
import logging
try:
from tqdm.auto import tqdm
class TqdmLoggingHandler(logging.Handler):
def __init__(self, level=logging.INFO):
super().__init__(level)
def emit(self, record):
try:
msg = self.format(record)
tqdm.write(msg)
self.flush()
except Exception:
self.handleError(record)
TQDM_IMPORTED = True
except ImportError:
# tqdm does not exist before first launch
# I will import once the UI finishes seting up the enviroment and reloads.
TQDM_IMPORTED = False
def setup_logging(loglevel):
if loglevel is None:
loglevel = os.environ.get("SD_WEBUI_LOG_LEVEL")
loghandlers = []
if TQDM_IMPORTED:
loghandlers.append(TqdmLoggingHandler())
if loglevel:
log_level = getattr(logging, loglevel.upper(), None) or logging.INFO
logging.basicConfig(
level=log_level,
format='%(asctime)s %(levelname)s [%(name)s] %(message)s',
datefmt='%Y-%m-%d %H:%M:%S',
handlers=loghandlers
)
+58 -15
View File
@@ -1,5 +1,5 @@
import torch import torch
from modules import devices from modules import devices, shared
module_in_gpu = None module_in_gpu = None
cpu = torch.device("cpu") cpu = torch.device("cpu")
@@ -14,7 +14,24 @@ def send_everything_to_cpu():
module_in_gpu = None module_in_gpu = None
def is_needed(sd_model):
return shared.cmd_opts.lowvram or shared.cmd_opts.medvram or shared.cmd_opts.medvram_sdxl and hasattr(sd_model, 'conditioner')
def apply(sd_model):
enable = is_needed(sd_model)
shared.parallel_processing_allowed = not enable
if enable:
setup_for_low_vram(sd_model, not shared.cmd_opts.lowvram)
else:
sd_model.lowvram = False
def setup_for_low_vram(sd_model, use_medvram): def setup_for_low_vram(sd_model, use_medvram):
if getattr(sd_model, 'lowvram', False):
return
sd_model.lowvram = True sd_model.lowvram = True
parents = {} parents = {}
@@ -53,19 +70,50 @@ def setup_for_low_vram(sd_model, use_medvram):
send_me_to_gpu(first_stage_model, None) send_me_to_gpu(first_stage_model, None)
return first_stage_model_decode(z) return first_stage_model_decode(z)
# for SD1, cond_stage_model is CLIP and its NN is in the tranformer frield, but for SD2, it's open clip, and it's in model field to_remain_in_cpu = [
if hasattr(sd_model.cond_stage_model, 'model'): (sd_model, 'first_stage_model'),
sd_model.cond_stage_model.transformer = sd_model.cond_stage_model.model (sd_model, 'depth_model'),
(sd_model, 'embedder'),
(sd_model, 'model'),
(sd_model, 'embedder'),
]
# remove several big modules: cond, first_stage, depth/embedder (if applicable), and unet from the model and then is_sdxl = hasattr(sd_model, 'conditioner')
# send the model to GPU. Then put modules back. the modules will be in CPU. is_sd2 = not is_sdxl and hasattr(sd_model.cond_stage_model, 'model')
stored = sd_model.cond_stage_model.transformer, sd_model.first_stage_model, getattr(sd_model, 'depth_model', None), getattr(sd_model, 'embedder', None), sd_model.model
sd_model.cond_stage_model.transformer, sd_model.first_stage_model, sd_model.depth_model, sd_model.embedder, sd_model.model = None, None, None, None, None if is_sdxl:
to_remain_in_cpu.append((sd_model, 'conditioner'))
elif is_sd2:
to_remain_in_cpu.append((sd_model.cond_stage_model, 'model'))
else:
to_remain_in_cpu.append((sd_model.cond_stage_model, 'transformer'))
# remove several big modules: cond, first_stage, depth/embedder (if applicable), and unet from the model
stored = []
for obj, field in to_remain_in_cpu:
module = getattr(obj, field, None)
stored.append(module)
setattr(obj, field, None)
# send the model to GPU.
sd_model.to(devices.device) sd_model.to(devices.device)
sd_model.cond_stage_model.transformer, sd_model.first_stage_model, sd_model.depth_model, sd_model.embedder, sd_model.model = stored
# put modules back. the modules will be in CPU.
for (obj, field), module in zip(to_remain_in_cpu, stored):
setattr(obj, field, module)
# register hooks for those the first three models # register hooks for those the first three models
if is_sdxl:
sd_model.conditioner.register_forward_pre_hook(send_me_to_gpu)
elif is_sd2:
sd_model.cond_stage_model.model.register_forward_pre_hook(send_me_to_gpu)
sd_model.cond_stage_model.model.token_embedding.register_forward_pre_hook(send_me_to_gpu)
parents[sd_model.cond_stage_model.model] = sd_model.cond_stage_model
parents[sd_model.cond_stage_model.model.token_embedding] = sd_model.cond_stage_model
else:
sd_model.cond_stage_model.transformer.register_forward_pre_hook(send_me_to_gpu) sd_model.cond_stage_model.transformer.register_forward_pre_hook(send_me_to_gpu)
parents[sd_model.cond_stage_model.transformer] = sd_model.cond_stage_model
sd_model.first_stage_model.register_forward_pre_hook(send_me_to_gpu) sd_model.first_stage_model.register_forward_pre_hook(send_me_to_gpu)
sd_model.first_stage_model.encode = first_stage_model_encode_wrap sd_model.first_stage_model.encode = first_stage_model_encode_wrap
sd_model.first_stage_model.decode = first_stage_model_decode_wrap sd_model.first_stage_model.decode = first_stage_model_decode_wrap
@@ -73,11 +121,6 @@ def setup_for_low_vram(sd_model, use_medvram):
sd_model.depth_model.register_forward_pre_hook(send_me_to_gpu) sd_model.depth_model.register_forward_pre_hook(send_me_to_gpu)
if sd_model.embedder: if sd_model.embedder:
sd_model.embedder.register_forward_pre_hook(send_me_to_gpu) sd_model.embedder.register_forward_pre_hook(send_me_to_gpu)
parents[sd_model.cond_stage_model.transformer] = sd_model.cond_stage_model
if hasattr(sd_model.cond_stage_model, 'model'):
sd_model.cond_stage_model.model = sd_model.cond_stage_model.transformer
del sd_model.cond_stage_model.transformer
if use_medvram: if use_medvram:
sd_model.model.register_forward_pre_hook(send_me_to_gpu) sd_model.model.register_forward_pre_hook(send_me_to_gpu)
@@ -101,4 +144,4 @@ def setup_for_low_vram(sd_model, use_medvram):
def is_enabled(sd_model): def is_enabled(sd_model):
return getattr(sd_model, 'lowvram', False) return sd_model.lowvram
+25 -5
View File
@@ -1,12 +1,20 @@
import logging
import torch import torch
import platform import platform
from modules.sd_hijack_utils import CondFunc from modules.sd_hijack_utils import CondFunc
from packaging import version from packaging import version
from modules import shared
log = logging.getLogger(__name__)
# has_mps is only available in nightly pytorch (for now) and macOS 12.3+. # before torch version 1.13, has_mps is only available in nightly pytorch and macOS 12.3+,
# check `getattr` and try it for compatibility # use check `getattr` and try it for compatibility.
# in torch version 1.13, backends.mps.is_available() and backends.mps.is_built() are introduced in to check mps availabilty,
# since torch 2.0.1+ nightly build, getattr(torch, 'has_mps', False) was deprecated, see https://github.com/pytorch/pytorch/pull/103279
def check_for_mps() -> bool: def check_for_mps() -> bool:
if version.parse(torch.__version__) <= version.parse("2.0.1"):
if not getattr(torch, 'has_mps', False): if not getattr(torch, 'has_mps', False):
return False return False
try: try:
@@ -14,9 +22,24 @@ def check_for_mps() -> bool:
return True return True
except Exception: except Exception:
return False return False
else:
return torch.backends.mps.is_available() and torch.backends.mps.is_built()
has_mps = check_for_mps() has_mps = check_for_mps()
def torch_mps_gc() -> None:
try:
if shared.state.current_latent is not None:
log.debug("`current_latent` is set, skipping MPS garbage collection")
return
from torch.mps import empty_cache
empty_cache()
except Exception:
log.warning("MPS garbage collection failed", exc_info=True)
# MPS workaround for https://github.com/pytorch/pytorch/issues/89784 # MPS workaround for https://github.com/pytorch/pytorch/issues/89784
def cumsum_fix(input, cumsum_func, *args, **kwargs): def cumsum_fix(input, cumsum_func, *args, **kwargs):
if input.device.type == 'mps': if input.device.type == 'mps':
@@ -29,9 +52,6 @@ def cumsum_fix(input, cumsum_func, *args, **kwargs):
if has_mps: if has_mps:
# MPS fix for randn in torchsde
CondFunc('torchsde._brownian.brownian_interval._randn', lambda _, size, dtype, device, seed: torch.randn(size, dtype=dtype, device=torch.device("cpu"), generator=torch.Generator(torch.device("cpu")).manual_seed(int(seed))).to(device), lambda _, size, dtype, device, seed: device.type == 'mps')
if platform.mac_ver()[0].startswith("13.2."): if platform.mac_ver()[0].startswith("13.2."):
# MPS workaround for https://github.com/pytorch/pytorch/issues/95188, thanks to danieldk (https://github.com/explosion/curated-transformers/pull/124) # MPS workaround for https://github.com/pytorch/pytorch/issues/95188, thanks to danieldk (https://github.com/explosion/curated-transformers/pull/124)
CondFunc('torch.nn.functional.linear', lambda _, input, weight, bias: (torch.matmul(input, weight.t()) + bias) if bias is not None else torch.matmul(input, weight.t()), lambda _, input, weight, bias: input.numel() > 10485760) CondFunc('torch.nn.functional.linear', lambda _, input, weight, bias: (torch.matmul(input, weight.t()) + bias) if bias is not None else torch.matmul(input, weight.t()), lambda _, input, weight, bias: input.numel() > 10485760)
+28 -6
View File
@@ -1,3 +1,5 @@
from __future__ import annotations
import os import os
import shutil import shutil
import importlib import importlib
@@ -8,6 +10,29 @@ from modules.upscaler import Upscaler, UpscalerLanczos, UpscalerNearest, Upscale
from modules.paths import script_path, models_path from modules.paths import script_path, models_path
def load_file_from_url(
url: str,
*,
model_dir: str,
progress: bool = True,
file_name: str | None = None,
) -> str:
"""Download a file from `url` into `model_dir`, using the file present if possible.
Returns the path to the downloaded file.
"""
os.makedirs(model_dir, exist_ok=True)
if not file_name:
parts = urlparse(url)
file_name = os.path.basename(parts.path)
cached_file = os.path.abspath(os.path.join(model_dir, file_name))
if not os.path.exists(cached_file):
print(f'Downloading: "{url}" to {cached_file}\n')
from torch.hub import download_url_to_file
download_url_to_file(url, cached_file, progress=progress)
return cached_file
def load_models(model_path: str, model_url: str = None, command_path: str = None, ext_filter=None, download_name=None, ext_blacklist=None) -> list: def load_models(model_path: str, model_url: str = None, command_path: str = None, ext_filter=None, download_name=None, ext_blacklist=None) -> list:
""" """
A one-and done loader to try finding the desired models in specified directories. A one-and done loader to try finding the desired models in specified directories.
@@ -46,9 +71,7 @@ def load_models(model_path: str, model_url: str = None, command_path: str = None
if model_url is not None and len(output) == 0: if model_url is not None and len(output) == 0:
if download_name is not None: if download_name is not None:
from basicsr.utils.download_util import load_file_from_url output.append(load_file_from_url(model_url, model_dir=places[0], file_name=download_name))
dl = load_file_from_url(model_url, places[0], True, download_name)
output.append(dl)
else: else:
output.append(model_url) output.append(model_url)
@@ -59,7 +82,7 @@ def load_models(model_path: str, model_url: str = None, command_path: str = None
def friendly_name(file: str): def friendly_name(file: str):
if "http" in file: if file.startswith("http"):
file = urlparse(file).path file = urlparse(file).path
file = os.path.basename(file) file = os.path.basename(file)
@@ -95,8 +118,7 @@ def cleanup_models():
def move_files(src_path: str, dest_path: str, ext_filter: str = None): def move_files(src_path: str, dest_path: str, ext_filter: str = None):
try: try:
if not os.path.exists(dest_path): os.makedirs(dest_path, exist_ok=True)
os.makedirs(dest_path)
if os.path.exists(src_path): if os.path.exists(src_path):
for file in os.listdir(src_path): for file in os.listdir(src_path):
fullpath = os.path.join(src_path, file) fullpath = os.path.join(src_path, file)
+308
View File
@@ -0,0 +1,308 @@
import json
import sys
from dataclasses import dataclass
import gradio as gr
from modules import errors
from modules.shared_cmd_options import cmd_opts
class OptionInfo:
def __init__(self, default=None, label="", component=None, component_args=None, onchange=None, section=None, refresh=None, comment_before='', comment_after='', infotext=None, restrict_api=False, category_id=None):
self.default = default
self.label = label
self.component = component
self.component_args = component_args
self.onchange = onchange
self.section = section
self.category_id = category_id
self.refresh = refresh
self.do_not_save = False
self.comment_before = comment_before
"""HTML text that will be added after label in UI"""
self.comment_after = comment_after
"""HTML text that will be added before label in UI"""
self.infotext = infotext
self.restrict_api = restrict_api
"""If True, the setting will not be accessible via API"""
def link(self, label, url):
self.comment_before += f"[<a href='{url}' target='_blank'>{label}</a>]"
return self
def js(self, label, js_func):
self.comment_before += f"[<a onclick='{js_func}(); return false'>{label}</a>]"
return self
def info(self, info):
self.comment_after += f"<span class='info'>({info})</span>"
return self
def html(self, html):
self.comment_after += html
return self
def needs_restart(self):
self.comment_after += " <span class='info'>(requires restart)</span>"
return self
def needs_reload_ui(self):
self.comment_after += " <span class='info'>(requires Reload UI)</span>"
return self
class OptionHTML(OptionInfo):
def __init__(self, text):
super().__init__(str(text).strip(), label='', component=lambda **kwargs: gr.HTML(elem_classes="settings-info", **kwargs))
self.do_not_save = True
def options_section(section_identifier, options_dict):
for v in options_dict.values():
if len(section_identifier) == 2:
v.section = section_identifier
elif len(section_identifier) == 3:
v.section = section_identifier[0:2]
v.category_id = section_identifier[2]
return options_dict
options_builtin_fields = {"data_labels", "data", "restricted_opts", "typemap"}
class Options:
typemap = {int: float}
def __init__(self, data_labels: dict[str, OptionInfo], restricted_opts):
self.data_labels = data_labels
self.data = {k: v.default for k, v in self.data_labels.items() if not v.do_not_save}
self.restricted_opts = restricted_opts
def __setattr__(self, key, value):
if key in options_builtin_fields:
return super(Options, self).__setattr__(key, value)
if self.data is not None:
if key in self.data or key in self.data_labels:
assert not cmd_opts.freeze_settings, "changing settings is disabled"
info = self.data_labels.get(key, None)
if info.do_not_save:
return
comp_args = info.component_args if info else None
if isinstance(comp_args, dict) and comp_args.get('visible', True) is False:
raise RuntimeError(f"not possible to set {key} because it is restricted")
if cmd_opts.hide_ui_dir_config and key in self.restricted_opts:
raise RuntimeError(f"not possible to set {key} because it is restricted")
self.data[key] = value
return
return super(Options, self).__setattr__(key, value)
def __getattr__(self, item):
if item in options_builtin_fields:
return super(Options, self).__getattribute__(item)
if self.data is not None:
if item in self.data:
return self.data[item]
if item in self.data_labels:
return self.data_labels[item].default
return super(Options, self).__getattribute__(item)
def set(self, key, value, is_api=False, run_callbacks=True):
"""sets an option and calls its onchange callback, returning True if the option changed and False otherwise"""
oldval = self.data.get(key, None)
if oldval == value:
return False
option = self.data_labels[key]
if option.do_not_save:
return False
if is_api and option.restrict_api:
return False
try:
setattr(self, key, value)
except RuntimeError:
return False
if run_callbacks and option.onchange is not None:
try:
option.onchange()
except Exception as e:
errors.display(e, f"changing setting {key} to {value}")
setattr(self, key, oldval)
return False
return True
def get_default(self, key):
"""returns the default value for the key"""
data_label = self.data_labels.get(key)
if data_label is None:
return None
return data_label.default
def save(self, filename):
assert not cmd_opts.freeze_settings, "saving settings is disabled"
with open(filename, "w", encoding="utf8") as file:
json.dump(self.data, file, indent=4, ensure_ascii=False)
def same_type(self, x, y):
if x is None or y is None:
return True
type_x = self.typemap.get(type(x), type(x))
type_y = self.typemap.get(type(y), type(y))
return type_x == type_y
def load(self, filename):
with open(filename, "r", encoding="utf8") as file:
self.data = json.load(file)
# 1.6.0 VAE defaults
if self.data.get('sd_vae_as_default') is not None and self.data.get('sd_vae_overrides_per_model_preferences') is None:
self.data['sd_vae_overrides_per_model_preferences'] = not self.data.get('sd_vae_as_default')
# 1.1.1 quicksettings list migration
if self.data.get('quicksettings') is not None and self.data.get('quicksettings_list') is None:
self.data['quicksettings_list'] = [i.strip() for i in self.data.get('quicksettings').split(',')]
# 1.4.0 ui_reorder
if isinstance(self.data.get('ui_reorder'), str) and self.data.get('ui_reorder') and "ui_reorder_list" not in self.data:
self.data['ui_reorder_list'] = [i.strip() for i in self.data.get('ui_reorder').split(',')]
bad_settings = 0
for k, v in self.data.items():
info = self.data_labels.get(k, None)
if info is not None and not self.same_type(info.default, v):
print(f"Warning: bad setting value: {k}: {v} ({type(v).__name__}; expected {type(info.default).__name__})", file=sys.stderr)
bad_settings += 1
if bad_settings > 0:
print(f"The program is likely to not work with bad settings.\nSettings file: {filename}\nEither fix the file, or delete it and restart.", file=sys.stderr)
def onchange(self, key, func, call=True):
item = self.data_labels.get(key)
item.onchange = func
if call:
func()
def dumpjson(self):
d = {k: self.data.get(k, v.default) for k, v in self.data_labels.items()}
d["_comments_before"] = {k: v.comment_before for k, v in self.data_labels.items() if v.comment_before is not None}
d["_comments_after"] = {k: v.comment_after for k, v in self.data_labels.items() if v.comment_after is not None}
item_categories = {}
for item in self.data_labels.values():
category = categories.mapping.get(item.category_id)
category = "Uncategorized" if category is None else category.label
if category not in item_categories:
item_categories[category] = item.section[1]
# _categories is a list of pairs: [section, category]. Each section (a setting page) will get a special heading above it with the category as text.
d["_categories"] = [[v, k] for k, v in item_categories.items()] + [["Defaults", "Other"]]
return json.dumps(d)
def add_option(self, key, info):
self.data_labels[key] = info
if key not in self.data and not info.do_not_save:
self.data[key] = info.default
def reorder(self):
"""Reorder settings so that:
- all items related to section always go together
- all sections belonging to a category go together
- sections inside a category are ordered alphabetically
- categories are ordered by creation order
Category is a superset of sections: for category "postprocessing" there could be multiple sections: "face restoration", "upscaling".
This function also changes items' category_id so that all items belonging to a section have the same category_id.
"""
category_ids = {}
section_categories = {}
settings_items = self.data_labels.items()
for _, item in settings_items:
if item.section not in section_categories:
section_categories[item.section] = item.category_id
for _, item in settings_items:
item.category_id = section_categories.get(item.section)
for category_id in categories.mapping:
if category_id not in category_ids:
category_ids[category_id] = len(category_ids)
def sort_key(x):
item: OptionInfo = x[1]
category_order = category_ids.get(item.category_id, len(category_ids))
section_order = item.section[1]
return category_order, section_order
self.data_labels = dict(sorted(settings_items, key=sort_key))
def cast_value(self, key, value):
"""casts an arbitrary to the same type as this setting's value with key
Example: cast_value("eta_noise_seed_delta", "12") -> returns 12 (an int rather than str)
"""
if value is None:
return None
default_value = self.data_labels[key].default
if default_value is None:
default_value = getattr(self, key, None)
if default_value is None:
return None
expected_type = type(default_value)
if expected_type == bool and value == "False":
value = False
else:
value = expected_type(value)
return value
@dataclass
class OptionsCategory:
id: str
label: str
class OptionsCategories:
def __init__(self):
self.mapping = {}
def register_category(self, category_id, label):
if category_id in self.mapping:
return category_id
self.mapping[category_id] = OptionsCategory(category_id, label)
categories = OptionsCategories()
+64
View File
@@ -0,0 +1,64 @@
from collections import defaultdict
def patch(key, obj, field, replacement):
"""Replaces a function in a module or a class.
Also stores the original function in this module, possible to be retrieved via original(key, obj, field).
If the function is already replaced by this caller (key), an exception is raised -- use undo() before that.
Arguments:
key: identifying information for who is doing the replacement. You can use __name__.
obj: the module or the class
field: name of the function as a string
replacement: the new function
Returns:
the original function
"""
patch_key = (obj, field)
if patch_key in originals[key]:
raise RuntimeError(f"patch for {field} is already applied")
original_func = getattr(obj, field)
originals[key][patch_key] = original_func
setattr(obj, field, replacement)
return original_func
def undo(key, obj, field):
"""Undoes the peplacement by the patch().
If the function is not replaced, raises an exception.
Arguments:
key: identifying information for who is doing the replacement. You can use __name__.
obj: the module or the class
field: name of the function as a string
Returns:
Always None
"""
patch_key = (obj, field)
if patch_key not in originals[key]:
raise RuntimeError(f"there is no patch for {field} to undo")
original_func = originals[key].pop(patch_key)
setattr(obj, field, original_func)
return None
def original(key, obj, field):
"""Returns the original function for the patch created by the patch() function"""
patch_key = (obj, field)
return originals[key].get(patch_key, None)
originals = defaultdict(dict)
+26 -15
View File
@@ -1,10 +1,25 @@
import os import os
import sys import sys
from modules.paths_internal import models_path, script_path, data_path, extensions_dir, extensions_builtin_dir # noqa: F401 from modules.paths_internal import models_path, script_path, data_path, extensions_dir, extensions_builtin_dir, cwd # noqa: F401
import modules.safe # noqa: F401 import modules.safe # noqa: F401
def mute_sdxl_imports():
"""create fake modules that SDXL wants to import but doesn't actually use for our purposes"""
class Dummy:
pass
module = Dummy()
module.LPIPS = None
sys.modules['taming.modules.losses.lpips'] = module
module = Dummy()
module.StableDataModuleFromConfig = None
sys.modules['sgm.data'] = module
# data_path = cmd_opts_pre.data # data_path = cmd_opts_pre.data
sys.path.insert(0, script_path) sys.path.insert(0, script_path)
@@ -18,8 +33,11 @@ for possible_sd_path in possible_sd_paths:
assert sd_path is not None, f"Couldn't find Stable Diffusion in any of: {possible_sd_paths}" assert sd_path is not None, f"Couldn't find Stable Diffusion in any of: {possible_sd_paths}"
mute_sdxl_imports()
path_dirs = [ path_dirs = [
(sd_path, 'ldm', 'Stable Diffusion', []), (sd_path, 'ldm', 'Stable Diffusion', []),
(os.path.join(sd_path, '../generative-models'), 'sgm', 'Stable Diffusion XL', ["sgm"]),
(os.path.join(sd_path, '../CodeFormer'), 'inference_codeformer.py', 'CodeFormer', []), (os.path.join(sd_path, '../CodeFormer'), 'inference_codeformer.py', 'CodeFormer', []),
(os.path.join(sd_path, '../BLIP'), 'models/blip.py', 'BLIP', []), (os.path.join(sd_path, '../BLIP'), 'models/blip.py', 'BLIP', []),
(os.path.join(sd_path, '../k-diffusion'), 'k_diffusion/sampling.py', 'k_diffusion', ["atstart"]), (os.path.join(sd_path, '../k-diffusion'), 'k_diffusion/sampling.py', 'k_diffusion', ["atstart"]),
@@ -35,20 +53,13 @@ for d, must_exist, what, options in path_dirs:
d = os.path.abspath(d) d = os.path.abspath(d)
if "atstart" in options: if "atstart" in options:
sys.path.insert(0, d) sys.path.insert(0, d)
elif "sgm" in options:
# Stable Diffusion XL repo has scripts dir with __init__.py in it which ruins every extension's scripts dir, so we
# import sgm and remove it from sys.path so that when a script imports scripts.something, it doesbn't use sgm's scripts dir.
sys.path.insert(0, d)
import sgm # noqa: F401
sys.path.pop(0)
else: else:
sys.path.append(d) sys.path.append(d)
paths[what] = d paths[what] = d
class Prioritize:
def __init__(self, name):
self.name = name
self.path = None
def __enter__(self):
self.path = sys.path.copy()
sys.path = [paths[self.name]] + sys.path
def __exit__(self, exc_type, exc_val, exc_tb):
sys.path = self.path
self.path = None
+1
View File
@@ -8,6 +8,7 @@ import shlex
commandline_args = os.environ.get('COMMANDLINE_ARGS', "") commandline_args = os.environ.get('COMMANDLINE_ARGS', "")
sys.argv += shlex.split(commandline_args) sys.argv += shlex.split(commandline_args)
cwd = os.getcwd()
modules_path = os.path.dirname(os.path.realpath(__file__)) modules_path = os.path.dirname(os.path.realpath(__file__))
script_path = os.path.dirname(modules_path) script_path = os.path.dirname(modules_path)
+15 -15
View File
@@ -9,13 +9,11 @@ from modules.shared import opts
def run_postprocessing(extras_mode, image, image_folder, input_dir, output_dir, show_extras_results, *args, save_output: bool = True): def run_postprocessing(extras_mode, image, image_folder, input_dir, output_dir, show_extras_results, *args, save_output: bool = True):
devices.torch_gc() devices.torch_gc()
shared.state.begin() shared.state.begin(job="extras")
shared.state.job = 'extras'
image_data = []
image_names = []
outputs = [] outputs = []
def get_images(extras_mode, image, image_folder, input_dir):
if extras_mode == 1: if extras_mode == 1:
for img in image_folder: for img in image_folder:
if isinstance(img, Image.Image): if isinstance(img, Image.Image):
@@ -24,8 +22,7 @@ def run_postprocessing(extras_mode, image, image_folder, input_dir, output_dir,
else: else:
image = Image.open(os.path.abspath(img.name)) image = Image.open(os.path.abspath(img.name))
fn = os.path.splitext(img.orig_name)[0] fn = os.path.splitext(img.orig_name)[0]
image_data.append(image) yield image, fn
image_names.append(fn)
elif extras_mode == 2: elif extras_mode == 2:
assert not shared.cmd_opts.hide_ui_dir_config, '--hide-ui-dir-config option must be disabled' assert not shared.cmd_opts.hide_ui_dir_config, '--hide-ui-dir-config option must be disabled'
assert input_dir, 'input directory not selected' assert input_dir, 'input directory not selected'
@@ -36,13 +33,10 @@ def run_postprocessing(extras_mode, image, image_folder, input_dir, output_dir,
image = Image.open(filename) image = Image.open(filename)
except Exception: except Exception:
continue continue
image_data.append(image) yield image, filename
image_names.append(filename)
else: else:
assert image, 'image not selected' assert image, 'image not selected'
yield image, None
image_data.append(image)
image_names.append(None)
if extras_mode == 2 and output_dir != '': if extras_mode == 2 and output_dir != '':
outpath = output_dir outpath = output_dir
@@ -51,12 +45,16 @@ def run_postprocessing(extras_mode, image, image_folder, input_dir, output_dir,
infotext = '' infotext = ''
for image, name in zip(image_data, image_names): for image_data, name in get_images(extras_mode, image, image_folder, input_dir):
image_data: Image.Image
shared.state.textinfo = name shared.state.textinfo = name
existing_pnginfo = image.info or {} parameters, existing_pnginfo = images.read_info_from_image(image_data)
if parameters:
existing_pnginfo["parameters"] = parameters
pp = scripts_postprocessing.PostprocessedImage(image.convert("RGB")) pp = scripts_postprocessing.PostprocessedImage(image_data.convert("RGB"))
scripts.scripts_postproc.run(pp, args) scripts.scripts_postproc.run(pp, args)
@@ -77,8 +75,10 @@ def run_postprocessing(extras_mode, image, image_folder, input_dir, output_dir,
if extras_mode != 2 or show_extras_results: if extras_mode != 2 or show_extras_results:
outputs.append(pp.image) outputs.append(pp.image)
devices.torch_gc() image_data.close()
devices.torch_gc()
shared.state.end()
return outputs, ui_common.plaintext_to_html(infotext), '' return outputs, ui_common.plaintext_to_html(infotext), ''
+541 -329
View File
File diff suppressed because it is too large Load Diff
+49
View File
@@ -0,0 +1,49 @@
import gradio as gr
from modules import scripts, sd_models
from modules.ui_common import create_refresh_button
from modules.ui_components import InputAccordion
class ScriptRefiner(scripts.ScriptBuiltinUI):
section = "accordions"
create_group = False
def __init__(self):
pass
def title(self):
return "Refiner"
def show(self, is_img2img):
return scripts.AlwaysVisible
def ui(self, is_img2img):
with InputAccordion(False, label="Refiner", elem_id=self.elem_id("enable")) as enable_refiner:
with gr.Row():
refiner_checkpoint = gr.Dropdown(label='Checkpoint', elem_id=self.elem_id("checkpoint"), choices=sd_models.checkpoint_tiles(), value='', tooltip="switch to another model in the middle of generation")
create_refresh_button(refiner_checkpoint, sd_models.list_models, lambda: {"choices": sd_models.checkpoint_tiles()}, self.elem_id("checkpoint_refresh"))
refiner_switch_at = gr.Slider(value=0.8, label="Switch at", minimum=0.01, maximum=1.0, step=0.01, elem_id=self.elem_id("switch_at"), tooltip="fraction of sampling steps when the switch to refiner model should happen; 1=never, 0.5=switch in the middle of generation")
def lookup_checkpoint(title):
info = sd_models.get_closet_checkpoint_match(title)
return None if info is None else info.title
self.infotext_fields = [
(enable_refiner, lambda d: 'Refiner' in d),
(refiner_checkpoint, lambda d: lookup_checkpoint(d.get('Refiner'))),
(refiner_switch_at, 'Refiner switch at'),
]
return enable_refiner, refiner_checkpoint, refiner_switch_at
def setup(self, p, enable_refiner, refiner_checkpoint, refiner_switch_at):
# the actual implementation is in sd_samplers_common.py, apply_refiner
if not enable_refiner or refiner_checkpoint in (None, "", "None"):
p.refiner_checkpoint = None
p.refiner_switch_at = None
else:
p.refiner_checkpoint = refiner_checkpoint
p.refiner_switch_at = refiner_switch_at
+111
View File
@@ -0,0 +1,111 @@
import json
import gradio as gr
from modules import scripts, ui, errors
from modules.shared import cmd_opts
from modules.ui_components import ToolButton
class ScriptSeed(scripts.ScriptBuiltinUI):
section = "seed"
create_group = False
def __init__(self):
self.seed = None
self.reuse_seed = None
self.reuse_subseed = None
def title(self):
return "Seed"
def show(self, is_img2img):
return scripts.AlwaysVisible
def ui(self, is_img2img):
with gr.Row(elem_id=self.elem_id("seed_row")):
if cmd_opts.use_textbox_seed:
self.seed = gr.Textbox(label='Seed', value="", elem_id=self.elem_id("seed"), min_width=100)
else:
self.seed = gr.Number(label='Seed', value=-1, elem_id=self.elem_id("seed"), min_width=100, precision=0)
random_seed = ToolButton(ui.random_symbol, elem_id=self.elem_id("random_seed"), tooltip="Set seed to -1, which will cause a new random number to be used every time")
reuse_seed = ToolButton(ui.reuse_symbol, elem_id=self.elem_id("reuse_seed"), tooltip="Reuse seed from last generation, mostly useful if it was randomized")
seed_checkbox = gr.Checkbox(label='Extra', elem_id=self.elem_id("subseed_show"), value=False)
with gr.Group(visible=False, elem_id=self.elem_id("seed_extras")) as seed_extras:
with gr.Row(elem_id=self.elem_id("subseed_row")):
subseed = gr.Number(label='Variation seed', value=-1, elem_id=self.elem_id("subseed"), precision=0)
random_subseed = ToolButton(ui.random_symbol, elem_id=self.elem_id("random_subseed"))
reuse_subseed = ToolButton(ui.reuse_symbol, elem_id=self.elem_id("reuse_subseed"))
subseed_strength = gr.Slider(label='Variation strength', value=0.0, minimum=0, maximum=1, step=0.01, elem_id=self.elem_id("subseed_strength"))
with gr.Row(elem_id=self.elem_id("seed_resize_from_row")):
seed_resize_from_w = gr.Slider(minimum=0, maximum=2048, step=8, label="Resize seed from width", value=0, elem_id=self.elem_id("seed_resize_from_w"))
seed_resize_from_h = gr.Slider(minimum=0, maximum=2048, step=8, label="Resize seed from height", value=0, elem_id=self.elem_id("seed_resize_from_h"))
random_seed.click(fn=None, _js="function(){setRandomSeed('" + self.elem_id("seed") + "')}", show_progress=False, inputs=[], outputs=[])
random_subseed.click(fn=None, _js="function(){setRandomSeed('" + self.elem_id("subseed") + "')}", show_progress=False, inputs=[], outputs=[])
seed_checkbox.change(lambda x: gr.update(visible=x), show_progress=False, inputs=[seed_checkbox], outputs=[seed_extras])
self.infotext_fields = [
(self.seed, "Seed"),
(seed_checkbox, lambda d: "Variation seed" in d or "Seed resize from-1" in d),
(subseed, "Variation seed"),
(subseed_strength, "Variation seed strength"),
(seed_resize_from_w, "Seed resize from-1"),
(seed_resize_from_h, "Seed resize from-2"),
]
self.on_after_component(lambda x: connect_reuse_seed(self.seed, reuse_seed, x.component, False), elem_id=f'generation_info_{self.tabname}')
self.on_after_component(lambda x: connect_reuse_seed(subseed, reuse_subseed, x.component, True), elem_id=f'generation_info_{self.tabname}')
return self.seed, seed_checkbox, subseed, subseed_strength, seed_resize_from_w, seed_resize_from_h
def setup(self, p, seed, seed_checkbox, subseed, subseed_strength, seed_resize_from_w, seed_resize_from_h):
p.seed = seed
if seed_checkbox and subseed_strength > 0:
p.subseed = subseed
p.subseed_strength = subseed_strength
if seed_checkbox and seed_resize_from_w > 0 and seed_resize_from_h > 0:
p.seed_resize_from_w = seed_resize_from_w
p.seed_resize_from_h = seed_resize_from_h
def connect_reuse_seed(seed: gr.Number, reuse_seed: gr.Button, generation_info: gr.Textbox, is_subseed):
""" Connects a 'reuse (sub)seed' button's click event so that it copies last used
(sub)seed value from generation info the to the seed field. If copying subseed and subseed strength
was 0, i.e. no variation seed was used, it copies the normal seed value instead."""
def copy_seed(gen_info_string: str, index):
res = -1
try:
gen_info = json.loads(gen_info_string)
index -= gen_info.get('index_of_first_image', 0)
if is_subseed and gen_info.get('subseed_strength', 0) > 0:
all_subseeds = gen_info.get('all_subseeds', [-1])
res = all_subseeds[index if 0 <= index < len(all_subseeds) else 0]
else:
all_seeds = gen_info.get('all_seeds', [-1])
res = all_seeds[index if 0 <= index < len(all_seeds) else 0]
except json.decoder.JSONDecodeError:
if gen_info_string:
errors.report(f"Error parsing JSON generation info: {gen_info_string}")
return [res, gr.update()]
reuse_seed.click(
fn=copy_seed,
_js="(x, y) => [x, selected_gallery_index()]",
show_progress=False,
inputs=[generation_info, seed],
outputs=[seed, seed]
)
+11 -6
View File
@@ -48,6 +48,7 @@ def add_task_to_queue(id_job):
class ProgressRequest(BaseModel): class ProgressRequest(BaseModel):
id_task: str = Field(default=None, title="Task ID", description="id of the task to get progress for") id_task: str = Field(default=None, title="Task ID", description="id of the task to get progress for")
id_live_preview: int = Field(default=-1, title="Live preview image ID", description="id of last received last preview image") id_live_preview: int = Field(default=-1, title="Live preview image ID", description="id of last received last preview image")
live_preview: bool = Field(default=True, title="Include live preview", description="boolean flag indicating whether to include the live preview image")
class ProgressResponse(BaseModel): class ProgressResponse(BaseModel):
@@ -71,7 +72,12 @@ def progressapi(req: ProgressRequest):
completed = req.id_task in finished_tasks completed = req.id_task in finished_tasks
if not active: if not active:
return ProgressResponse(active=active, queued=queued, completed=completed, id_live_preview=-1, textinfo="In queue..." if queued else "Waiting...") textinfo = "Waiting..."
if queued:
sorted_queued = sorted(pending_tasks.keys(), key=lambda x: pending_tasks[x])
queue_index = sorted_queued.index(req.id_task)
textinfo = "In queue: {}/{}".format(queue_index + 1, len(sorted_queued))
return ProgressResponse(active=active, queued=queued, completed=completed, id_live_preview=-1, textinfo=textinfo)
progress = 0 progress = 0
@@ -89,9 +95,12 @@ def progressapi(req: ProgressRequest):
predicted_duration = elapsed_since_start / progress if progress > 0 else None predicted_duration = elapsed_since_start / progress if progress > 0 else None
eta = predicted_duration - elapsed_since_start if predicted_duration is not None else None eta = predicted_duration - elapsed_since_start if predicted_duration is not None else None
live_preview = None
id_live_preview = req.id_live_preview id_live_preview = req.id_live_preview
if opts.live_previews_enable and req.live_preview:
shared.state.set_current_image() shared.state.set_current_image()
if opts.live_previews_enable and shared.state.id_live_preview != req.id_live_preview: if shared.state.id_live_preview != req.id_live_preview:
image = shared.state.current_image image = shared.state.current_image
if image is not None: if image is not None:
buffered = io.BytesIO() buffered = io.BytesIO()
@@ -110,10 +119,6 @@ def progressapi(req: ProgressRequest):
base64_image = base64.b64encode(buffered.getvalue()).decode('ascii') base64_image = base64.b64encode(buffered.getvalue()).decode('ascii')
live_preview = f"data:image/{opts.live_previews_image_format};base64,{base64_image}" live_preview = f"data:image/{opts.live_previews_image_format};base64,{base64_image}"
id_live_preview = shared.state.id_live_preview id_live_preview = shared.state.id_live_preview
else:
live_preview = None
else:
live_preview = None
return ProgressResponse(active=active, queued=queued, completed=completed, progress=progress, eta=eta, live_preview=live_preview, id_live_preview=id_live_preview, textinfo=shared.state.textinfo) return ProgressResponse(active=active, queued=queued, completed=completed, progress=progress, eta=eta, live_preview=live_preview, id_live_preview=id_live_preview, textinfo=shared.state.textinfo)
+123 -35
View File
@@ -1,9 +1,10 @@
from __future__ import annotations
import re import re
from collections import namedtuple from collections import namedtuple
from typing import List
import lark import lark
# a prompt like this: "fantasy landscape with a [mountain:lake:0.25] and [an oak:a christmas tree:0.75][ in foreground::0.6][ in background:0.25] [shoddy:masterful:0.5]" # a prompt like this: "fantasy landscape with a [mountain:lake:0.25] and [an oak:a christmas tree:0.75][ in foreground::0.6][: in background:0.25] [shoddy:masterful:0.5]"
# will be represented with prompt_schedule like this (assuming steps=100): # will be represented with prompt_schedule like this (assuming steps=100):
# [25, 'fantasy landscape with a mountain and an oak in foreground shoddy'] # [25, 'fantasy landscape with a mountain and an oak in foreground shoddy']
# [50, 'fantasy landscape with a lake and an oak in foreground in background shoddy'] # [50, 'fantasy landscape with a lake and an oak in foreground in background shoddy']
@@ -17,14 +18,14 @@ prompt: (emphasized | scheduled | alternate | plain | WHITESPACE)*
!emphasized: "(" prompt ")" !emphasized: "(" prompt ")"
| "(" prompt ":" prompt ")" | "(" prompt ":" prompt ")"
| "[" prompt "]" | "[" prompt "]"
scheduled: "[" [prompt ":"] prompt ":" [WHITESPACE] NUMBER "]" scheduled: "[" [prompt ":"] prompt ":" [WHITESPACE] NUMBER [WHITESPACE] "]"
alternate: "[" prompt ("|" prompt)+ "]" alternate: "[" prompt ("|" [prompt])+ "]"
WHITESPACE: /\s+/ WHITESPACE: /\s+/
plain: /([^\\\[\]():|]|\\.)+/ plain: /([^\\\[\]():|]|\\.)+/
%import common.SIGNED_NUMBER -> NUMBER %import common.SIGNED_NUMBER -> NUMBER
""") """)
def get_learned_conditioning_prompt_schedules(prompts, steps): def get_learned_conditioning_prompt_schedules(prompts, base_steps, hires_steps=None, use_old_scheduling=False):
""" """
>>> g = lambda p: get_learned_conditioning_prompt_schedules([p], 10)[0] >>> g = lambda p: get_learned_conditioning_prompt_schedules([p], 10)[0]
>>> g("test") >>> g("test")
@@ -51,18 +52,43 @@ def get_learned_conditioning_prompt_schedules(prompts, steps):
[[3, '((a][:b:c '], [10, '((a][:b:c d']] [[3, '((a][:b:c '], [10, '((a][:b:c d']]
>>> g("[a|(b:1.1)]") >>> g("[a|(b:1.1)]")
[[1, 'a'], [2, '(b:1.1)'], [3, 'a'], [4, '(b:1.1)'], [5, 'a'], [6, '(b:1.1)'], [7, 'a'], [8, '(b:1.1)'], [9, 'a'], [10, '(b:1.1)']] [[1, 'a'], [2, '(b:1.1)'], [3, 'a'], [4, '(b:1.1)'], [5, 'a'], [6, '(b:1.1)'], [7, 'a'], [8, '(b:1.1)'], [9, 'a'], [10, '(b:1.1)']]
>>> g("[fe|]male")
[[1, 'female'], [2, 'male'], [3, 'female'], [4, 'male'], [5, 'female'], [6, 'male'], [7, 'female'], [8, 'male'], [9, 'female'], [10, 'male']]
>>> g("[fe|||]male")
[[1, 'female'], [2, 'male'], [3, 'male'], [4, 'male'], [5, 'female'], [6, 'male'], [7, 'male'], [8, 'male'], [9, 'female'], [10, 'male']]
>>> g = lambda p: get_learned_conditioning_prompt_schedules([p], 10, 10)[0]
>>> g("a [b:.5] c")
[[10, 'a b c']]
>>> g("a [b:1.5] c")
[[5, 'a c'], [10, 'a b c']]
""" """
if hires_steps is None or use_old_scheduling:
int_offset = 0
flt_offset = 0
steps = base_steps
else:
int_offset = base_steps
flt_offset = 1.0
steps = hires_steps
def collect_steps(steps, tree): def collect_steps(steps, tree):
res = [steps] res = [steps]
class CollectSteps(lark.Visitor): class CollectSteps(lark.Visitor):
def scheduled(self, tree): def scheduled(self, tree):
tree.children[-1] = float(tree.children[-1]) s = tree.children[-2]
if tree.children[-1] < 1: v = float(s)
tree.children[-1] *= steps if use_old_scheduling:
tree.children[-1] = min(steps, int(tree.children[-1])) v = v*steps if v<1 else v
res.append(tree.children[-1]) else:
if "." in s:
v = (v - flt_offset) * steps
else:
v = (v - int_offset)
tree.children[-2] = min(steps, int(v))
if tree.children[-2] >= 1:
res.append(tree.children[-2])
def alternate(self, tree): def alternate(self, tree):
res.extend(range(1, steps+1)) res.extend(range(1, steps+1))
@@ -73,13 +99,14 @@ def get_learned_conditioning_prompt_schedules(prompts, steps):
def at_step(step, tree): def at_step(step, tree):
class AtStep(lark.Transformer): class AtStep(lark.Transformer):
def scheduled(self, args): def scheduled(self, args):
before, after, _, when = args before, after, _, when, _ = args
yield before or () if step <= when else after yield before or () if step <= when else after
def alternate(self, args): def alternate(self, args):
yield next(args[(step - 1)%len(args)]) args = ["" if not arg else arg for arg in args]
yield args[(step - 1) % len(args)]
def start(self, args): def start(self, args):
def flatten(x): def flatten(x):
if type(x) == str: if isinstance(x, str):
yield x yield x
else: else:
for gen in x: for gen in x:
@@ -109,7 +136,25 @@ def get_learned_conditioning_prompt_schedules(prompts, steps):
ScheduledPromptConditioning = namedtuple("ScheduledPromptConditioning", ["end_at_step", "cond"]) ScheduledPromptConditioning = namedtuple("ScheduledPromptConditioning", ["end_at_step", "cond"])
def get_learned_conditioning(model, prompts, steps): class SdConditioning(list):
"""
A list with prompts for stable diffusion's conditioner model.
Can also specify width and height of created image - SDXL needs it.
"""
def __init__(self, prompts, is_negative_prompt=False, width=None, height=None, copy_from=None):
super().__init__()
self.extend(prompts)
if copy_from is None:
copy_from = prompts
self.is_negative_prompt = is_negative_prompt or getattr(copy_from, 'is_negative_prompt', False)
self.width = width or getattr(copy_from, 'width', None)
self.height = height or getattr(copy_from, 'height', None)
def get_learned_conditioning(model, prompts: SdConditioning | list[str], steps, hires_steps=None, use_old_scheduling=False):
"""converts a list of prompts into a list of prompt schedules - each schedule is a list of ScheduledPromptConditioning, specifying the comdition (cond), """converts a list of prompts into a list of prompt schedules - each schedule is a list of ScheduledPromptConditioning, specifying the comdition (cond),
and the sampling step at which this condition is to be replaced by the next one. and the sampling step at which this condition is to be replaced by the next one.
@@ -129,7 +174,7 @@ def get_learned_conditioning(model, prompts, steps):
""" """
res = [] res = []
prompt_schedules = get_learned_conditioning_prompt_schedules(prompts, steps) prompt_schedules = get_learned_conditioning_prompt_schedules(prompts, steps, hires_steps, use_old_scheduling)
cache = {} cache = {}
for prompt, prompt_schedule in zip(prompts, prompt_schedules): for prompt, prompt_schedule in zip(prompts, prompt_schedules):
@@ -139,12 +184,17 @@ def get_learned_conditioning(model, prompts, steps):
res.append(cached) res.append(cached)
continue continue
texts = [x[1] for x in prompt_schedule] texts = SdConditioning([x[1] for x in prompt_schedule], copy_from=prompts)
conds = model.get_learned_conditioning(texts) conds = model.get_learned_conditioning(texts)
cond_schedule = [] cond_schedule = []
for i, (end_at_step, _) in enumerate(prompt_schedule): for i, (end_at_step, _) in enumerate(prompt_schedule):
cond_schedule.append(ScheduledPromptConditioning(end_at_step, conds[i])) if isinstance(conds, dict):
cond = {k: v[i] for k, v in conds.items()}
else:
cond = conds[i]
cond_schedule.append(ScheduledPromptConditioning(end_at_step, cond))
cache[prompt] = cond_schedule cache[prompt] = cond_schedule
res.append(cond_schedule) res.append(cond_schedule)
@@ -153,13 +203,15 @@ def get_learned_conditioning(model, prompts, steps):
re_AND = re.compile(r"\bAND\b") re_AND = re.compile(r"\bAND\b")
re_weight = re.compile(r"^(.*?)(?:\s*:\s*([-+]?(?:\d+\.?|\d*\.\d+)))?\s*$") re_weight = re.compile(r"^((?:\s|.)*?)(?:\s*:\s*([-+]?(?:\d+\.?|\d*\.\d+)))?\s*$")
def get_multicond_prompt_list(prompts):
def get_multicond_prompt_list(prompts: SdConditioning | list[str]):
res_indexes = [] res_indexes = []
prompt_flat_list = []
prompt_indexes = {} prompt_indexes = {}
prompt_flat_list = SdConditioning(prompts)
prompt_flat_list.clear()
for prompt in prompts: for prompt in prompts:
subprompts = re_AND.split(prompt) subprompts = re_AND.split(prompt)
@@ -187,16 +239,17 @@ def get_multicond_prompt_list(prompts):
class ComposableScheduledPromptConditioning: class ComposableScheduledPromptConditioning:
def __init__(self, schedules, weight=1.0): def __init__(self, schedules, weight=1.0):
self.schedules: List[ScheduledPromptConditioning] = schedules self.schedules: list[ScheduledPromptConditioning] = schedules
self.weight: float = weight self.weight: float = weight
class MulticondLearnedConditioning: class MulticondLearnedConditioning:
def __init__(self, shape, batch): def __init__(self, shape, batch):
self.shape: tuple = shape # the shape field is needed to send this object to DDIM/PLMS self.shape: tuple = shape # the shape field is needed to send this object to DDIM/PLMS
self.batch: List[List[ComposableScheduledPromptConditioning]] = batch self.batch: list[list[ComposableScheduledPromptConditioning]] = batch
def get_multicond_learned_conditioning(model, prompts, steps) -> MulticondLearnedConditioning:
def get_multicond_learned_conditioning(model, prompts, steps, hires_steps=None, use_old_scheduling=False) -> MulticondLearnedConditioning:
"""same as get_learned_conditioning, but returns a list of ScheduledPromptConditioning along with the weight objects for each prompt. """same as get_learned_conditioning, but returns a list of ScheduledPromptConditioning along with the weight objects for each prompt.
For each prompt, the list is obtained by splitting the prompt using the AND separator. For each prompt, the list is obtained by splitting the prompt using the AND separator.
@@ -205,7 +258,7 @@ def get_multicond_learned_conditioning(model, prompts, steps) -> MulticondLearne
res_indexes, prompt_flat_list, prompt_indexes = get_multicond_prompt_list(prompts) res_indexes, prompt_flat_list, prompt_indexes = get_multicond_prompt_list(prompts)
learned_conditioning = get_learned_conditioning(model, prompt_flat_list, steps) learned_conditioning = get_learned_conditioning(model, prompt_flat_list, steps, hires_steps, use_old_scheduling)
res = [] res = []
for indexes in res_indexes: for indexes in res_indexes:
@@ -214,20 +267,57 @@ def get_multicond_learned_conditioning(model, prompts, steps) -> MulticondLearne
return MulticondLearnedConditioning(shape=(len(prompts),), batch=res) return MulticondLearnedConditioning(shape=(len(prompts),), batch=res)
def reconstruct_cond_batch(c: List[List[ScheduledPromptConditioning]], current_step): class DictWithShape(dict):
def __init__(self, x, shape):
super().__init__()
self.update(x)
@property
def shape(self):
return self["crossattn"].shape
def reconstruct_cond_batch(c: list[list[ScheduledPromptConditioning]], current_step):
param = c[0][0].cond param = c[0][0].cond
is_dict = isinstance(param, dict)
if is_dict:
dict_cond = param
res = {k: torch.zeros((len(c),) + param.shape, device=param.device, dtype=param.dtype) for k, param in dict_cond.items()}
res = DictWithShape(res, (len(c),) + dict_cond['crossattn'].shape)
else:
res = torch.zeros((len(c),) + param.shape, device=param.device, dtype=param.dtype) res = torch.zeros((len(c),) + param.shape, device=param.device, dtype=param.dtype)
for i, cond_schedule in enumerate(c): for i, cond_schedule in enumerate(c):
target_index = 0 target_index = 0
for current, entry in enumerate(cond_schedule): for current, entry in enumerate(cond_schedule):
if current_step <= entry.end_at_step: if current_step <= entry.end_at_step:
target_index = current target_index = current
break break
if is_dict:
for k, param in cond_schedule[target_index].cond.items():
res[k][i] = param
else:
res[i] = cond_schedule[target_index].cond res[i] = cond_schedule[target_index].cond
return res return res
def stack_conds(tensors):
# if prompts have wildly different lengths above the limit we'll get tensors of different shapes
# and won't be able to torch.stack them. So this fixes that.
token_count = max([x.shape[0] for x in tensors])
for i in range(len(tensors)):
if tensors[i].shape[0] != token_count:
last_vector = tensors[i][-1:]
last_vector_repeated = last_vector.repeat([token_count - tensors[i].shape[0], 1])
tensors[i] = torch.vstack([tensors[i], last_vector_repeated])
return torch.stack(tensors)
def reconstruct_multicond_batch(c: MulticondLearnedConditioning, current_step): def reconstruct_multicond_batch(c: MulticondLearnedConditioning, current_step):
param = c.batch[0][0].schedules[0].cond param = c.batch[0][0].schedules[0].cond
@@ -249,16 +339,14 @@ def reconstruct_multicond_batch(c: MulticondLearnedConditioning, current_step):
conds_list.append(conds_for_batch) conds_list.append(conds_for_batch)
# if prompts have wildly different lengths above the limit we'll get tensors fo different shapes if isinstance(tensors[0], dict):
# and won't be able to torch.stack them. So this fixes that. keys = list(tensors[0].keys())
token_count = max([x.shape[0] for x in tensors]) stacked = {k: stack_conds([x[k] for x in tensors]) for k in keys}
for i in range(len(tensors)): stacked = DictWithShape(stacked, stacked['crossattn'].shape)
if tensors[i].shape[0] != token_count: else:
last_vector = tensors[i][-1:] stacked = stack_conds(tensors).to(device=param.device, dtype=param.dtype)
last_vector_repeated = last_vector.repeat([token_count - tensors[i].shape[0], 1])
tensors[i] = torch.vstack([tensors[i], last_vector_repeated])
return conds_list, torch.stack(tensors).to(device=param.device, dtype=param.dtype) return conds_list, stacked
re_attention = re.compile(r""" re_attention = re.compile(r"""
@@ -270,7 +358,7 @@ re_attention = re.compile(r"""
\\| \\|
\(| \(|
\[| \[|
:([+-]?[.\d]+)\)| :\s*([+-]?[.\d]+)\s*\)|
\)| \)|
]| ]|
[^\\()\[\]:]+| [^\\()\[\]:]+|
+15 -17
View File
@@ -2,7 +2,6 @@ import os
import numpy as np import numpy as np
from PIL import Image from PIL import Image
from basicsr.utils.download_util import load_file_from_url
from realesrgan import RealESRGANer from realesrgan import RealESRGANer
from modules.upscaler import Upscaler, UpscalerData from modules.upscaler import Upscaler, UpscalerData
@@ -43,9 +42,10 @@ class UpscalerRealESRGAN(Upscaler):
if not self.enable: if not self.enable:
return img return img
try:
info = self.load_model(path) info = self.load_model(path)
if not os.path.exists(info.local_data_path): except Exception:
print(f"Unable to load RealESRGAN model: {info.name}") errors.report(f"Unable to load RealESRGAN model {path}", exc_info=True)
return img return img
upsampler = RealESRGANer( upsampler = RealESRGANer(
@@ -55,6 +55,7 @@ class UpscalerRealESRGAN(Upscaler):
half=not cmd_opts.no_half and not cmd_opts.upcast_sampling, half=not cmd_opts.no_half and not cmd_opts.upcast_sampling,
tile=opts.ESRGAN_tile, tile=opts.ESRGAN_tile,
tile_pad=opts.ESRGAN_tile_overlap, tile_pad=opts.ESRGAN_tile_overlap,
device=self.device,
) )
upsampled = upsampler.enhance(np.array(img), outscale=info.scale)[0] upsampled = upsampler.enhance(np.array(img), outscale=info.scale)[0]
@@ -63,20 +64,17 @@ class UpscalerRealESRGAN(Upscaler):
return image return image
def load_model(self, path): def load_model(self, path):
try: for scaler in self.scalers:
info = next(iter([scaler for scaler in self.scalers if scaler.data_path == path]), None) if scaler.data_path == path:
if scaler.local_data_path.startswith("http"):
if info is None: scaler.local_data_path = modelloader.load_file_from_url(
print(f"Unable to find model info: {path}") scaler.data_path,
return None model_dir=self.model_download_path,
)
if info.local_data_path.startswith("http"): if not os.path.exists(scaler.local_data_path):
info.local_data_path = load_file_from_url(url=info.data_path, model_dir=self.model_download_path, progress=True) raise FileNotFoundError(f"RealESRGAN data missing: {scaler.local_data_path}")
return scaler
return info raise ValueError(f"Unable to find model info: {path}")
except Exception:
errors.report("Error making Real-ESRGAN models list", exc_info=True)
return None
def load_models(self, _): def load_models(self, _):
return get_realesrgan_models(self) return get_realesrgan_models(self)

Some files were not shown because too many files have changed in this diff Show More