From 8797a34e19068f658759d0854b5d304ad0fa5501 Mon Sep 17 00:00:00 2001 From: Vladimir Mandic Date: Mon, 18 Sep 2023 15:49:04 -0400 Subject: [PATCH 01/11] add tensor to samples method --- modules/esrgan_model.py | 2 +- modules/realesrgan_model_arch.py | 5 ++--- modules/sd_samplers_common.py | 23 +++++++++++++++++++++++ 3 files changed, 26 insertions(+), 4 deletions(-) diff --git a/modules/esrgan_model.py b/modules/esrgan_model.py index cbede232f..a9682bceb 100644 --- a/modules/esrgan_model.py +++ b/modules/esrgan_model.py @@ -220,7 +220,7 @@ def esrgan_upscale(model, img): with Progress(TextColumn('[cyan]{task.description}'), BarColumn(), TaskProgressColumn(), TimeRemainingColumn(), TimeElapsedColumn(), console=console) as progress: total = 0 - for y, h, row in grid.tiles: + for _y, _h, row in grid.tiles: total += len(row) task = progress.add_task(description="Upscaling", total=total) for y, h, row in grid.tiles: diff --git a/modules/realesrgan_model_arch.py b/modules/realesrgan_model_arch.py index 8b991de6d..50f085255 100644 --- a/modules/realesrgan_model_arch.py +++ b/modules/realesrgan_model_arch.py @@ -155,7 +155,7 @@ class RealESRGANer(): # input tile dimensions input_tile_width = input_end_x - input_start_x input_tile_height = input_end_y - input_start_y - tile_idx = y * tiles_x + x + 1 + tile_idx = y * tiles_x + x + 1 # noqa input_tile = self.img[:, :, input_start_y_pad:input_end_y_pad, input_start_x_pad:input_end_x_pad] # upscale tile @@ -315,7 +315,7 @@ class IOConsumer(threading.Thread): from basicsr.utils.registry import ARCH_REGISTRY from torch import nn as nn -from torch.nn import functional as F +from torch.nn import functional as F # noqa class SRVGGNetCompact(nn.Module): @@ -381,4 +381,3 @@ class SRVGGNetCompact(nn.Module): base = F.interpolate(x, scale_factor=self.upscale, mode='nearest') out += base return out - \ No newline at end of file diff --git a/modules/sd_samplers_common.py b/modules/sd_samplers_common.py index 4c2b3655a..b2e2daf36 100644 --- a/modules/sd_samplers_common.py +++ b/modules/sd_samplers_common.py @@ -55,6 +55,29 @@ def samples_to_image_grid(samples, approximation=None): return images.image_grid([single_sample_to_image(sample, approximation) for sample in samples]) +def images_tensor_to_samples(image, approximation=None, model=None): + '''image[0, 1] -> latent''' + if approximation is None: + approximation = approximation_indexes.get(shared.opts.show_progress_type, 0) + if approximation == 3: + image = image.to(devices.device, devices.dtype) + x_latent = sd_vae_taesd.encode(image) + else: + if model is None: + model = shared.sd_model + model.first_stage_model.to(devices.dtype_vae) + image = image.to(shared.device, dtype=devices.dtype_vae) + image = image * 2 - 1 + if len(image) > 1: + x_latent = torch.stack([ + model.get_first_stage_encoding(model.encode_first_stage(torch.unsqueeze(img, 0)))[0] + for img in image + ]) + else: + x_latent = model.get_first_stage_encoding(model.encode_first_stage(image)) + return x_latent + + def store_latent(decoded): shared.state.current_latent = decoded if shared.opts.live_previews_enable and shared.opts.show_progress_every_n_steps > 0 and shared.state.sampling_step % shared.opts.show_progress_every_n_steps == 0: From 7ae304f76e72b781bf1fde3eb0280dcd1675fd1a Mon Sep 17 00:00:00 2001 From: Vladimir Mandic Date: Mon, 18 Sep 2023 21:24:40 -0400 Subject: [PATCH 02/11] cleanup settings --- html/locale_en.json | 12 ++++++------ html/logo-bg-dark.jpg | Bin 18778 -> 18702 bytes javascript/loader.js | 3 ++- javascript/script.js | 2 +- javascript/setHints.js | 2 +- javascript/style.css | 11 ++++++++--- modules/shared.py | 38 ++++++++++++++++++++------------------ 7 files changed, 38 insertions(+), 30 deletions(-) diff --git a/html/locale_en.json b/html/locale_en.json index 692a82929..1436a6786 100644 --- a/html/locale_en.json +++ b/html/locale_en.json @@ -365,9 +365,9 @@ {"id":"","label":"Split attention","localized":"","hint":""}, {"id":"","label":"xFormers enable flash Attention","localized":"","hint":""}, {"id":"","label":"SDP disable memory attention","localized":"","hint":""}, - {"id":"","label":"Sub-quadratic cross-attention query chunk size","localized":"","hint":""}, - {"id":"","label":"Sub-quadratic cross-attention kv chunk size","localized":"","hint":""}, - {"id":"","label":"Sub-quadratic cross-attention chunking threshold","localized":"","hint":""}, + {"id":"","label":"cross-attention query chunk size","localized":"","hint":""}, + {"id":"","label":"cross-attention kv chunk size","localized":"","hint":""}, + {"id":"","label":"cross-attention chunking threshold","localized":"","hint":""}, {"id":"","label":"Full parser","localized":"","hint":""}, {"id":"","label":"Compel parser","localized":"","hint":""}, {"id":"","label":"A1111 parser","localized":"","hint":""}, @@ -466,9 +466,9 @@ {"id":"","label":"Auto","localized":"","hint":""}, {"id":"","label":"Dark","localized":"","hint":""}, {"id":"","label":"Light","localized":"","hint":""}, - {"id":"","label":"Show grid in results for web","localized":"","hint":""}, - {"id":"","label":"For inpainting, include the greyscale mask in results for web","localized":"","hint":""}, - {"id":"","label":"For inpainting, include masked composite in results for web","localized":"","hint":""}, + {"id":"","label":"Show grid in results","localized":"","hint":""}, + {"id":"","label":"For inpainting, include the greyscale mask in results","localized":"","hint":""}, + {"id":"","label":"For inpainting, include masked composite in results","localized":"","hint":""}, {"id":"","label":"Do not change selected model when reading generation parameters","localized":"","hint":""}, {"id":"","label":"Send seed when sending prompt or image to other interface","localized":"","hint":""}, {"id":"","label":"Send size when sending prompt or image to another interface","localized":"","hint":""}, diff --git a/html/logo-bg-dark.jpg b/html/logo-bg-dark.jpg index 20392e1ae68a6249591c8d990c681d88a976ce49..40af4bbbc41fe91365ec82c8cc7f5ac8ba7ff2a9 100644 GIT binary patch delta 12973 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 delta 13027 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 B_Xhv~ diff --git a/javascript/loader.js b/javascript/loader.js index a280f3cf5..f32c7d294 100644 --- a/javascript/loader.js +++ b/javascript/loader.js @@ -34,9 +34,10 @@ async function createSplash() { `; document.body.insertAdjacentHTML('beforeend', splash); await preloadImages(); - const imgElement = `
`; + const imgElement = `
`; document.getElementById('splash').insertAdjacentHTML('afterbegin', imgElement); } + async function removeSplash() { const splash = document.getElementById('splash'); if (splash) splash.remove(); diff --git a/javascript/script.js b/javascript/script.js index 6ad77aa26..d9e35376f 100644 --- a/javascript/script.js +++ b/javascript/script.js @@ -75,7 +75,7 @@ let executedOnLoaded = false; document.addEventListener('DOMContentLoaded', () => { const mutationObserver = new MutationObserver((m) => { - if (!executedOnLoaded && gradioApp().querySelector('#txt2img_prompt')) { + if (!executedOnLoaded && gradioApp().getElementById('txt2img_prompt')) { executedOnLoaded = true; executeCallbacks(uiLoadedCallbacks); } diff --git a/javascript/setHints.js b/javascript/setHints.js index 7398c7a5e..1ce221723 100644 --- a/javascript/setHints.js +++ b/javascript/setHints.js @@ -51,7 +51,7 @@ async function setHints() { if (localeData.data.length === 0) { const res = await fetch('/file=html/locale_en.json'); const json = await res.json(); - localeData.data = Object.values(json).flat(); + localeData.data = Object.values(json).flat().filter((e) => e.hint.length > 0); for (const e of localeData.data) e.label = e.label.toLowerCase().trim(); } const elements = [ diff --git a/javascript/style.css b/javascript/style.css index 29c7b7cb2..78f63f8fa 100644 --- a/javascript/style.css +++ b/javascript/style.css @@ -230,7 +230,7 @@ table.settings-value-table td { padding: 0.4em; border: 1px solid #ccc; max-widt .extra-networks .description { flex: 3; } .extra-networks .tab-nav > button { margin-right: 0; height: 24px; padding: 2px 4px 2px 4px; } .extra-networks-tab { padding: 0 !important; } -.extra-network-subdirs { background: var(--input-background-fill); overflow-x: hidden; overflow-y: auto; min-width: max(20%, 120px); padding-top: 0.5em; } +.extra-network-subdirs { background: var(--input-background-fill); overflow-x: hidden; overflow-y: auto; min-width: max(15%, 120px); padding-top: 0.5em; } .extra-networks-page { display: flex } .extra-networks .custom-button { width: 120px; width: 100%; background: none; justify-content: left; text-align: left; padding: 2px 8px 2px 16px; text-indent: -8px; box-shadow: none; line-break: auto; } .extra-networks .custom-button:hover { background: var(--button-primary-background-fill) } @@ -294,8 +294,8 @@ div.controlnet_main_options { display: grid; grid-template-columns: 1fr 1fr; gri /* Workaround for Gradio dropdowns capturing clicks during and after fadeout */ .gradio-dropdown > label > div > div:first-child:not(.showOptions) ~ ul.options { pointer-events: none; } -.splash { position: fixed; top: 0; left: 0; width: 100vw; height: 100vh; z-index: 100; display: block; text-align: center; } -.splash-img { margin: 10% auto 0 auto; width: 512px; background-repeat: no-repeat; height: 512px; animation: move 5s infinite alternate; } +.splash { position: fixed; top: 0; left: 0; width: 100vw; height: 100vh; z-index: 1000; display: block; text-align: center; } +.splash-img { margin: 10% auto 0 auto; width: 512px; background-repeat: no-repeat; height: 512px; animation: color 10s infinite alternate; } .loading { color: white; position: absolute; top: 20%; left: 50%; transform: translateX(-50%); } .loader { width: 300px; height: 300px; border: var(--spacing-md) solid transparent; border-radius: 50%; border-top: var(--spacing-md) solid var(--primary-600); animation: spin 4s linear infinite; position: relative; } .loader::before, .loader::after { content: ""; position: absolute; top: 6px; bottom: 6px; left: 6px; right: 6px; border-radius: 50%; border: var(--spacing-md) solid transparent; } @@ -311,3 +311,8 @@ div.controlnet_main_options { display: grid; grid-template-columns: 1fr 1fr; gri from { transform: rotate(0deg); } to { transform: rotate(360deg); } } + +@keyframes color { + from { filter: hue-rotate(0deg) } + to { filter: hue-rotate(360deg) } +} diff --git a/modules/shared.py b/modules/shared.py index 8dd49648d..9897ed3fc 100644 --- a/modules/shared.py +++ b/modules/shared.py @@ -376,24 +376,26 @@ options_templates.update(options_section(('sd', "Execution & Models"), { "sd_checkpoint_autoload": OptionInfo(True, "Model autoload on server start"), "sd_model_checkpoint": OptionInfo(default_checkpoint, "Base model", gr.Dropdown, lambda: {"choices": list_checkpoint_tiles()}, refresh=refresh_checkpoints), "sd_model_refiner": OptionInfo('None', "Refiner model", gr.Dropdown, lambda: {"choices": ['None'] + list_checkpoint_tiles()}, refresh=refresh_checkpoints), - "sd_checkpoint_cache": OptionInfo(0, "Number of cached models", gr.Slider, {"minimum": 0, "maximum": 10, "step": 1}), - "sd_vae_checkpoint_cache": OptionInfo(0, "Number of cached VAEs", gr.Slider, {"minimum": 0, "maximum": 10, "step": 1}), "sd_vae": OptionInfo("Automatic", "VAE model", gr.Dropdown, lambda: {"choices": shared_items.sd_vae_items()}, refresh=shared_items.refresh_vae_list), "sd_model_dict": OptionInfo('None', "Use baseline data from a different model", gr.Dropdown, lambda: {"choices": ['None'] + list_checkpoint_tiles()}, refresh=refresh_checkpoints), "stream_load": OptionInfo(False, "Load models using stream loading method"), - "model_reuse_dict": OptionInfo(False, "When loading models attempt to reuse previous model dictionary"), + "model_reuse_dict": OptionInfo(False, "When loading models attempt to reuse previous model dictionary", gr.Checkbox, {"visible": False}), "prompt_attention": OptionInfo("Full parser", "Prompt attention parser", gr.Radio, lambda: {"choices": ["Full parser", "Compel parser", "A1111 parser", "Fixed attention"] }), "prompt_mean_norm": OptionInfo(True, "Prompt attention mean normalization"), "comma_padding_backtrack": OptionInfo(20, "Prompt padding for long prompts", gr.Slider, {"minimum": 0, "maximum": 74, "step": 1 }), + "sd_checkpoint_cache": OptionInfo(0, "Number of cached models", gr.Slider, {"minimum": 0, "maximum": 10, "step": 1}), + "sd_vae_checkpoint_cache": OptionInfo(0, "Number of cached VAEs", gr.Slider, {"minimum": 0, "maximum": 10, "step": 1}), "sd_disable_ckpt": OptionInfo(False, "Disallow usage of models in ckpt format"), })) options_templates.update(options_section(('optimizations', "Optimizations"), { "cross_attention_optimization": OptionInfo(cross_attention_optimization_default, "Cross-attention optimization method", gr.Radio, lambda: {"choices": shared_items.list_crossattention() }), "cross_attention_options": OptionInfo([], "Cross-attention advanced options", gr.CheckboxGroup, lambda: {"choices": ['xFormers enable flash Attention', 'SDP disable memory attention']}), - "sub_quad_q_chunk_size": OptionInfo(512, "Sub-quadratic cross-attention query chunk size", gr.Slider, {"minimum": 16, "maximum": 8192, "step": 8}), - "sub_quad_kv_chunk_size": OptionInfo(512, "Sub-quadratic cross-attention kv chunk size", gr.Slider, {"minimum": 0, "maximum": 8192, "step": 8}), - "sub_quad_chunk_threshold": OptionInfo(80, "Sub-quadratic cross-attention chunking threshold", gr.Slider, {"minimum": 0, "maximum": 100, "step": 1}), + "sub_quad_sep": OptionInfo("

Sub-quadratic options

", "", gr.HTML), + "sub_quad_q_chunk_size": OptionInfo(512, "cross-attention query chunk size", gr.Slider, {"minimum": 16, "maximum": 8192, "step": 8}), + "sub_quad_kv_chunk_size": OptionInfo(512, "cross-attention kv chunk size", gr.Slider, {"minimum": 0, "maximum": 8192, "step": 8}), + "sub_quad_chunk_threshold": OptionInfo(80, "cross-attention chunking threshold", gr.Slider, {"minimum": 0, "maximum": 100, "step": 1}), + "token_merging_sep": OptionInfo("

Token Merging

", "", gr.HTML), "token_merging_ratio": OptionInfo(0.0, "Token merging ratio", gr.Slider, {"minimum": 0.0, "maximum": 0.9, "step": 0.1}), "token_merging_ratio_img2img": OptionInfo(0.0, "Token merging ratio for img2img", gr.Slider, {"minimum": 0.0, "maximum": 0.9, "step": 0.1}), "token_merging_ratio_hr": OptionInfo(0.0, "Token merging ratio for hires pass", gr.Slider, {"minimum": 0.0, "maximum": 0.9, "step": 0.1}), @@ -529,19 +531,19 @@ options_templates.update(options_section(('saving-paths', "Image Naming & Paths" options_templates.update(options_section(('ui', "User Interface"), { "gradio_theme": OptionInfo("black-teal", "UI theme", gr.Dropdown, lambda: {"choices": list_themes()}, refresh=refresh_themes), "theme_style": OptionInfo("Auto", "Theme mode", gr.Radio, {"choices": ["Auto", "Dark", "Light"]}), - "tooltips": OptionInfo("UI Tooltips", "UI tooltips", gr.Radio, {"choices": ["None", "Browser default", "UI tooltips"]}), - "return_grid": OptionInfo(True, "Show grid in results for web"), - "return_mask": OptionInfo(False, "For inpainting, include the greyscale mask in results for web"), - "return_mask_composite": OptionInfo(False, "For inpainting, include masked composite in results for web"), + "tooltips": OptionInfo("UI Tooltips", "UI tooltips", gr.Radio, {"choices": ["None", "Browser default", "UI tooltips"], "visible": False}), + "return_grid": OptionInfo(True, "Show grid in results"), + "return_mask": OptionInfo(False, "For inpainting, include the greyscale mask in results"), + "return_mask_composite": OptionInfo(False, "For inpainting, include masked composite in results"), "disable_weights_auto_swap": OptionInfo(True, "Do not change selected model when reading generation parameters"), "send_seed": OptionInfo(True, "Send seed when sending prompt or image to other interface"), "send_size": OptionInfo(True, "Send size when sending prompt or image to another interface"), "font": OptionInfo("", "Font for image grids that have text"), - "keyedit_precision_attention": OptionInfo(0.1, "Ctrl+up/down precision when editing (attention:1.1)", gr.Slider, {"minimum": 0.01, "maximum": 0.2, "step": 0.001}), - "keyedit_precision_extra": OptionInfo(0.05, "Ctrl+up/down precision when editing ", gr.Slider, {"minimum": 0.01, "maximum": 0.2, "step": 0.001}), - "keyedit_delimiters": OptionInfo(".,\/!?%^*;:{}=`~()", "Ctrl+up/down word delimiters"), # pylint: disable=anomalous-backslash-in-string + "keyedit_precision_attention": OptionInfo(0.1, "Ctrl+up/down precision when editing (attention:1.1)", gr.Slider, {"minimum": 0.01, "maximum": 0.2, "step": 0.001, "visible": False}), + "keyedit_precision_extra": OptionInfo(0.05, "Ctrl+up/down precision when editing ", gr.Slider, {"minimum": 0.01, "maximum": 0.2, "step": 0.001, "visible": False}), + "keyedit_delimiters": OptionInfo(".,\/!?%^*;:{}=`~()", "Ctrl+up/down word delimiters", gr.Textbox, { "visible": False }), # pylint: disable=anomalous-backslash-in-string "quicksettings_list": OptionInfo(["sd_model_checkpoint"] if backend == Backend.ORIGINAL else ["sd_model_checkpoint", "sd_model_refiner"], "Quicksettings list", ui_components.DropdownMulti, lambda: {"choices": list(opts.data_labels.keys())}), - "ui_scripts_reorder": OptionInfo("", "UI scripts order"), + "ui_scripts_reorder": OptionInfo("", "UI scripts order", gr.Textbox, { "visible": False }), })) options_templates.update(options_section(('live-preview', "Live Previews"), { @@ -552,7 +554,7 @@ options_templates.update(options_section(('live-preview', "Live Previews"), { "notification_audio_path": OptionInfo("html/notification.mp3","Path to notification sound", component_args=hide_dirs, folder=True), "show_progress_every_n_steps": OptionInfo(1, "Live preview display period", gr.Slider, {"minimum": -1, "maximum": 32, "step": 1}), "show_progress_type": OptionInfo("Approximate NN", "Live preview method", gr.Radio, {"choices": ["Full VAE", "Approximate NN", "Approximate simple", "TAESD"]}), - "live_preview_content": OptionInfo("Combined", "Live preview subject", gr.Radio, {"choices": ["Combined", "Prompt", "Negative prompt"]}), + "live_preview_content": OptionInfo("Combined", "Live preview subject", gr.Radio, {"choices": ["Combined", "Prompt", "Negative prompt"], "visible": False}), "live_preview_refresh_period": OptionInfo(500, "Progress update period", gr.Slider, {"minimum": 0, "maximum": 5000, "step": 25}), "logmonitor_show": OptionInfo(True, "Show log view"), "logmonitor_refresh_period": OptionInfo(5000, "Log view update period", gr.Slider, {"minimum": 0, "maximum": 30000, "step": 25}), @@ -638,7 +640,7 @@ options_templates.update(options_section(('interrogate', "Interrogate"), { "interrogate_clip_num_beams": OptionInfo(1, "Interrogate: num_beams for BLIP", gr.Slider, {"minimum": 1, "maximum": 16, "step": 1}), "interrogate_clip_min_length": OptionInfo(32, "Interrogate: minimum description length", gr.Slider, {"minimum": 1, "maximum": 128, "step": 1}), "interrogate_clip_max_length": OptionInfo(192, "Interrogate: maximum description length", gr.Slider, {"minimum": 1, "maximum": 256, "step": 1}), - "interrogate_clip_dict_limit": OptionInfo(2048, "CLIP: maximum number of lines in text file"), + "interrogate_clip_dict_limit": OptionInfo(2048, "CLIP: maximum number of lines in text file", gr.Slider, { "visible": False }), "interrogate_clip_skip_categories": OptionInfo(["artists", "movements", "flavors"], "Interrogate: skip categories", gr.CheckboxGroup, lambda: {"choices": modules.interrogate.category_types()}, refresh=modules.interrogate.category_types), "interrogate_deepbooru_score_threshold": OptionInfo(0.65, "Interrogate: deepbooru score threshold", gr.Slider, {"minimum": 0, "maximum": 1, "step": 0.01}), "deepbooru_sort_alpha": OptionInfo(False, "Interrogate: deepbooru sort alphabetically"), @@ -652,10 +654,10 @@ options_templates.update(options_section(('extra_networks', "Extra Networks"), { "extra_networks_card_cover": OptionInfo("sidebar", "UI position", gr.Radio, lambda: {"choices": ["cover", "inline", "sidebar"]}), "extra_networks_height": OptionInfo(53, "UI height (%)", gr.Slider, {"minimum": 10, "maximum": 100, "step": 1}), "extra_networks_sidebar_width": OptionInfo(35, "UI sidebar width (%)", gr.Slider, {"minimum": 10, "maximum": 80, "step": 1}), - "extra_networks_card_lazy": OptionInfo(True, "UI card preview lazy loading"), + "extra_networks_card_lazy": OptionInfo(True, "UI card preview lazy loading", gr.Checkbox, { "visible": False }), "extra_networks_card_size": OptionInfo(160, "UI card size (px)", gr.Slider, {"minimum": 20, "maximum": 2000, "step": 1}), "extra_networks_card_square": OptionInfo(True, "UI disable variable aspect ratio"), - "extra_networks_card_fit": OptionInfo("cover", "UI image contain method", gr.Radio, lambda: {"choices": ["contain", "cover", "fill"]}), + "extra_networks_card_fit": OptionInfo("cover", "UI image contain method", gr.Radio, lambda: {"choices": ["contain", "cover", "fill"], "visible": False}), "extra_network_skip_indexing": OptionInfo(False, "Do not automatically build extra network pages", gr.Checkbox), "lyco_patch_lora": OptionInfo(False, "Use LyCoris handler for all LoRA types", gr.Checkbox), "lora_functional": OptionInfo(False, "Use Kohya method for handling multiple LoRA", gr.Checkbox, { "visible": False }), From cefb5959e33fc04cb679c7f12d78dcb36a93586d Mon Sep 17 00:00:00 2001 From: Aptronymist <108482020+Aptronymist@users.noreply.github.com> Date: Mon, 18 Sep 2023 21:27:20 -0400 Subject: [PATCH 03/11] Correcting legacy image optimization code Legacy A1111 code improperly implemented Pillow image optimization options, "quality=" never applied to PNG files (or anything else), only JPEG files, per https://pillow.readthedocs.io/en/stable/handbook/image-file-formats.html#png-saving and https://pillow.readthedocs.io/en/stable/handbook/image-file-formats.html#jpeg-saving This is now corrected, other than turning on the optimize=True, for backup and giggles, compress_level=9 is also set. For JPEG files, I also enabled optimize=True as it supposedly makes more optimized files. --- modules/images.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/modules/images.py b/modules/images.py index 39d9e7246..d8bda5e03 100644 --- a/modules/images.py +++ b/modules/images.py @@ -456,7 +456,7 @@ def atomically_save_image(): pnginfo_data = PngImagePlugin.PngInfo() for k, v in params.pnginfo.items(): pnginfo_data.add_text(k, str(v)) - image.save(fn, format=image_format, quality=shared.opts.jpeg_quality, pnginfo=pnginfo_data if shared.opts.image_metadata else None) + image.save(fn, format=image_format, optimize=True, compress_level=9, pnginfo=pnginfo_data if shared.opts.image_metadata else None) elif image_format == 'JPEG': if image.mode == 'RGBA': shared.log.warning('Saving RGBA image as JPEG: Alpha channel will be lost') @@ -464,7 +464,7 @@ def atomically_save_image(): elif image.mode == 'I;16': image = image.point(lambda p: p * 0.0038910505836576).convert("L") exif_bytes = piexif.dump({ "Exif": { piexif.ExifIFD.UserComment: piexif.helper.UserComment.dump(exifinfo, encoding="unicode") } }) - image.save(fn, format=image_format, quality=shared.opts.jpeg_quality, exif=exif_bytes) + image.save(fn, format=image_format, optimize=True, quality=shared.opts.jpeg_quality, exif=exif_bytes) elif image_format == 'WEBP': if image.mode == 'I;16': image = image.point(lambda p: p * 0.0038910505836576).convert("RGB") From a5973b6d96cdf42daf8ce955895cfe24193536a8 Mon Sep 17 00:00:00 2001 From: Vladimir Mandic Date: Mon, 18 Sep 2023 22:18:16 -0400 Subject: [PATCH 04/11] update --- extensions-builtin/sd-extension-system-info | 2 +- javascript/style.css | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/extensions-builtin/sd-extension-system-info b/extensions-builtin/sd-extension-system-info index 83dd4d8f6..b73ffdcc8 160000 --- a/extensions-builtin/sd-extension-system-info +++ b/extensions-builtin/sd-extension-system-info @@ -1 +1 @@ -Subproject commit 83dd4d8f65511af720ddb034125fe4ac0a46fb18 +Subproject commit b73ffdcc8d6a9622d3d8074862b109b20726ac73 diff --git a/javascript/style.css b/javascript/style.css index 78f63f8fa..4c4950fe7 100644 --- a/javascript/style.css +++ b/javascript/style.css @@ -1,6 +1,7 @@ :root, .dark{ --checkbox-label-gap: 0.25em 0.1em; --section-header-text-size: 12pt; --block-background-fill: transparent;} a { font-weight: bold; cursor: pointer; } h2 { margin-top: 1em !important; font-size: 1.4em !important; } +table { overflow-x: auto !important; overflow-y: auto !important; } div.gradio-container{ max-width: unset !important; padding: 8px !important; } div.tabitem { padding: 0 !important; } div.form { border-width: 0; box-shadow: none; background: transparent; overflow: visible; gap: 0.5em 1em; flex-grow: 1 !important; } From e6d6ee621a2acc3bd2cf35bf03edce4b2b90e856 Mon Sep 17 00:00:00 2001 From: Vladimir Mandic Date: Mon, 18 Sep 2023 22:30:36 -0400 Subject: [PATCH 05/11] diffusers lora unload logic --- modules/processing_diffusers.py | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/modules/processing_diffusers.py b/modules/processing_diffusers.py index 9cd74f162..d12e3e670 100644 --- a/modules/processing_diffusers.py +++ b/modules/processing_diffusers.py @@ -315,7 +315,8 @@ def process_diffusers(p: StableDiffusionProcessing, seeds, prompts, negative_pro task_specific_kwargs = {"image": p.init_images, "mask_image": p.mask, "strength": p.denoising_strength, "height": 8 * math.ceil(p.height / 8), "width": 8 * math.ceil(p.width / 8)} if shared.state.interrupted or shared.state.skipped: - unload_diffusers_lora() + if lora_state['active']: + unload_diffusers_lora() return results if shared.opts.diffusers_move_base and not shared.sd_model.has_accelerate: @@ -361,11 +362,9 @@ def process_diffusers(p: StableDiffusionProcessing, seeds, prompts, negative_pro if hasattr(shared.sd_model, 'embedding_db') and len(shared.sd_model.embedding_db.embeddings_used) > 0: p.extra_generation_params['Embeddings'] = ', '.join(shared.sd_model.embedding_db.embeddings_used) - if lora_state['active']: - p.extra_generation_params['LoRA method'] = shared.opts.diffusers_lora_loader - unload_diffusers_lora() - if shared.state.interrupted or shared.state.skipped: + if lora_state['active']: + unload_diffusers_lora() return results # optional hires pass @@ -407,6 +406,10 @@ def process_diffusers(p: StableDiffusionProcessing, seeds, prompts, negative_pro except AssertionError as e: shared.log.info(e) + if lora_state['active']: + p.extra_generation_params['LoRA method'] = shared.opts.diffusers_lora_loader + unload_diffusers_lora() + # optional refiner pass or decode if is_refiner_enabled: if shared.opts.save and not p.do_not_save_samples and shared.opts.save_images_before_refiner and hasattr(shared.sd_model, 'vae'): @@ -423,6 +426,8 @@ def process_diffusers(p: StableDiffusionProcessing, seeds, prompts, negative_pro sd_samplers.create_sampler(sampler.name, shared.sd_refiner) # TODO(Patrick): For wrapped pipelines this is currently a no-op if shared.state.interrupted or shared.state.skipped: + if lora_state['active']: + unload_diffusers_lora() return results if shared.opts.diffusers_move_refiner and not shared.sd_refiner.has_accelerate: From 947be2905532399838d836b2ba5ae8d74c0ca2e0 Mon Sep 17 00:00:00 2001 From: Vladimir Mandic Date: Mon, 18 Sep 2023 23:04:09 -0400 Subject: [PATCH 06/11] fix save --- modules/ui_common.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/modules/ui_common.py b/modules/ui_common.py index cac1363a9..fa5569162 100644 --- a/modules/ui_common.py +++ b/modules/ui_common.py @@ -84,6 +84,8 @@ def save_files(js_data, images, html_info, index): self.infotexts = getattr(self, 'infotexts', [html_info]) self.infotext = self.infotexts[0] if len(self.infotexts) > 0 else html_info self.index_of_first_image = getattr(self, 'index_of_first_image', 0) + self.batch_size = 1 + try: data = json.loads(js_data) except Exception: @@ -93,6 +95,8 @@ def save_files(js_data, images, html_info, index): if index > -1 and shared.opts.save_selected_only and (index >= p.index_of_first_image): # ensures we are looking at a specific non-grid picture, and we have save_selected_only # pylint: disable=no-member images = [images[index]] start_index = index + else: + p.batch_size = len(images) filenames = [] fullfns = [] for image_index, filedata in enumerate(images, start_index): From c33bbc83e71a012ac29b8ae2f5ee82c87c4c1c60 Mon Sep 17 00:00:00 2001 From: Disty0 Date: Tue, 19 Sep 2023 11:19:32 +0300 Subject: [PATCH 07/11] Update OpenVINO --- installer.py | 2 +- modules/shared.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/installer.py b/installer.py index ae227723d..9a322b677 100644 --- a/installer.py +++ b/installer.py @@ -496,7 +496,7 @@ def check_torch(): if opts.get('cuda_compile_backend', '') == 'hidet': install('hidet', 'hidet') if args.use_openvino or opts.get('cuda_compile_backend', '') == 'openvino_fx': - install('openvino==2023.1.0.dev20230811', 'openvino') + install('openvino==2023.1.0', 'openvino') os.environ.setdefault('PYTORCH_TRACING_MODE', 'TORCHFX') if args.profile: print_profile(pr, 'Torch') diff --git a/modules/shared.py b/modules/shared.py index 9897ed3fc..f1da73266 100644 --- a/modules/shared.py +++ b/modules/shared.py @@ -443,7 +443,7 @@ options_templates.update(options_section(('diffusers', "Diffusers Settings"), { "diffusers_attention_slicing": OptionInfo(True if devices.backend == "ipex" else False, "Enable attention slicing"), "diffusers_model_load_variant": OptionInfo("default", "Diffusers model loading variant", gr.Radio, lambda: {"choices": ['default', 'fp32', 'fp16']}), "diffusers_vae_load_variant": OptionInfo("default", "Diffusers VAE loading variant", gr.Radio, lambda: {"choices": ['default', 'fp32', 'fp16']}), - "diffusers_lora_loader": OptionInfo("sequential apply", "Diffusers LoRA loading variant", gr.Radio, lambda: {"choices": ['diffusers', 'sequential apply', 'merge and apply']}), + "diffusers_lora_loader": OptionInfo("diffusers" if cmd_opts.use_openvino else "sequential apply", "Diffusers LoRA loading variant", gr.Radio, lambda: {"choices": ['diffusers', 'sequential apply', 'merge and apply']}), "diffusers_force_zeros": OptionInfo(True, "Force zeros for prompts when empty"), "diffusers_aesthetics_score": OptionInfo(False, "Require aesthetics score"), })) From 4d9ccbc035912d5747feaef8eeedd7f0b73ee566 Mon Sep 17 00:00:00 2001 From: Vladimir Mandic Date: Tue, 19 Sep 2023 11:13:38 -0400 Subject: [PATCH 08/11] add batch info to metadata --- html/locale_en.json | 2 +- modules/images.py | 28 ++++++++++++++++++++-------- modules/processing.py | 19 ++++++++++--------- modules/sd_hijack.py | 15 ++++----------- modules/sd_models.py | 4 ++-- modules/shared.py | 5 +++-- modules/shared_items.py | 2 +- modules/ui_common.py | 24 +++++++++++++----------- modules/ui_tempdir.py | 2 +- 9 files changed, 55 insertions(+), 46 deletions(-) diff --git a/html/locale_en.json b/html/locale_en.json index 1436a6786..f17742039 100644 --- a/html/locale_en.json +++ b/html/locale_en.json @@ -356,7 +356,7 @@ {"id":"","label":"Enable splitting of hires batch processing","localized":"","hint":"Reduces VRAM usage when using hires fix on batches of images"}, {"id":"","label":"Load models using stream loading method","localized":"","hint":"When loading models attempt stream loading optimized for slow or network storage"}, {"id":"","label":"When loading models attempt to reuse previous model dictionary","localized":"","hint":""}, - {"id":"","label":"Disable cross-attention layer optimization","localized":"","hint":"Disable the all cross-attention optimization. May result in higher VRAM usage and longer generation times"}, + {"id":"","label":"Disabled","localized":"","hint":""}, {"id":"","label":"xFormers","localized":"","hint":"Memory optimization. Non-Deterministic (different results each time)"}, {"id":"","label":"Scaled-Dot-Product","localized":"","hint":"Memory optimization. Non-Deterministic unless SDP memory attention is disabled."}, {"id":"","label":"Doggettx's","localized":"","hint":""}, diff --git a/modules/images.py b/modules/images.py index 7f29983bb..98b44c4d0 100644 --- a/modules/images.py +++ b/modules/images.py @@ -291,7 +291,8 @@ def sanitize_filename_part(text, replace_spaces=True): class FilenameGenerator: replacements = { - 'batch_number': lambda self: NOTHING if self.index <= 1 else self.index, + 'batch_number': lambda self: self.batch_number, + 'iter_number': lambda self: self.iter_number, 'cfg': lambda self: self.p and self.p.cfg_scale, 'clip_skip': lambda self: self.p and self.p.clip_skip, 'date': lambda self: datetime.datetime.now().strftime('%Y-%m-%d'), @@ -320,12 +321,17 @@ class FilenameGenerator: } default_time_format = '%Y%m%d%H%M%S' - def __init__(self, p, seed, prompt, image, index = 0): + def __init__(self, p, seed, prompt, image, grid=False): self.p = p self.seed = seed self.prompt = prompt self.image = image - self.index = index if self.p is None or self.p.batch_size == 1 else self.p.batch_index + 1 + if not grid: + self.batch_number = NOTHING if self.p is None or getattr(self.p, 'batch_size', 1) == 1 else (self.p.batch_index + 1 if hasattr(self.p, 'batch_index') else NOTHING) + self.iter_number = NOTHING if self.p is None or getattr(self.p, 'n_iter', 1) == 1 else (self.p.iteration + 1 if hasattr(self.p, 'iteration') else NOTHING) + else: + self.batch_number = NOTHING + self.iter_number = NOTHING def hasprompt(self, *args): lower = self.prompt.lower() @@ -449,7 +455,7 @@ def atomically_save_image(): image_format = 'JPEG' if shared.opts.image_watermark_enabled: image = set_watermark(image, shared.opts.image_watermark) - shared.log.debug(f'Saving: image={fn} type={image_format} size={image.width}x{image.height}') + shared.log.debug(f'Saving: image="{fn}" type={image_format} size={image.width}x{image.height}') # actual save exifinfo = (exifinfo or "") if shared.opts.image_metadata else "" if image_format == 'PNG': @@ -489,8 +495,14 @@ def atomically_save_image(): with open(os.path.join(paths.data_path, "params.txt"), "w", encoding="utf8") as file: file.write(exifinfo) if shared.opts.save_log_fn != '' and len(exifinfo) > 0: - entry = { 'filename': filename, 'time': datetime.datetime.now().isoformat(), 'info': exifinfo } - shared.writefile(entry, os.path.join(paths.data_path, shared.opts.save_log_fn), mode='a+') + fn = os.path.join(paths.data_path, shared.opts.save_log_fn) + entries = shared.readfile(fn) + idx = len(list(entries)) + if idx == 0: + entries = [] + entry = { 'id': idx, 'filename': filename, 'time': datetime.datetime.now().isoformat(), 'info': exifinfo } + entries.append(entry) + shared.writefile(entries, fn, mode='w') save_queue.task_done() @@ -499,7 +511,7 @@ save_thread = threading.Thread(target=atomically_save_image, daemon=True) save_thread.start() -def save_image(image, path, basename, seed=None, prompt=None, extension='jpg', info=None, short_filename=False, no_prompt=False, grid=False, pnginfo_section_name='parameters', p=None, existing_info=None, forced_filename=None, suffix="", save_to_dirs=None, index=0): +def save_image(image, path, basename, seed=None, prompt=None, extension='jpg', info=None, short_filename=False, no_prompt=False, grid=False, pnginfo_section_name='parameters', p=None, existing_info=None, forced_filename=None, suffix="", save_to_dirs=None): """Save an image. Args: image (`PIL.Image`): @@ -537,7 +549,7 @@ def save_image(image, path, basename, seed=None, prompt=None, extension='jpg', i return None, None if path is None or len(path) == 0: # set default path to avoid errors when functions are triggered manually or via api and param is not set path = shared.opts.outdir_save - namegen = FilenameGenerator(p, seed, prompt, image, index) + namegen = FilenameGenerator(p, seed, prompt, image, grid=grid) if save_to_dirs is None: save_to_dirs = (grid and shared.opts.grid_save_to_dirs) or (not grid and shared.opts.save_to_dirs and not no_prompt) if save_to_dirs: diff --git a/modules/processing.py b/modules/processing.py index 772bbafba..90c4038af 100644 --- a/modules/processing.py +++ b/modules/processing.py @@ -472,6 +472,7 @@ def create_infotext(p: StableDiffusionProcessing, all_prompts, all_seeds, all_su "CFG scale": p.cfg_scale, "Size": f"{p.width}x{p.height}", "Batch": f'{p.n_iter}x{p.batch_size}' if p.n_iter > 1 or p.batch_size > 1 else None, + "Index": f'{p.iteration + 1}x{index + 1}' if (p.n_iter > 1 or p.batch_size > 1) and index >= 0 else None, "Parser": shared.opts.prompt_attention, "Model": None if (not shared.opts.add_model_name_to_info) or (not shared.sd_model.sd_checkpoint_info.model_name) else shared.sd_model.sd_checkpoint_info.model_name.replace(',', '').replace(':', ''), "Model hash": getattr(p, 'sd_model_hash', None if (not shared.opts.add_model_hash_to_info) or (not shared.sd_model.sd_model_hash) else shared.sd_model.sd_model_hash), @@ -798,7 +799,7 @@ def process_images_inner(p: StableDiffusionProcessing) -> Processed: p.scripts.postprocess_batch_list(p, batch_params, batch_number=n) x_samples_ddim = batch_params.images - def infotext(index=0): # pylint: disable=function-redefined # noqa: F811 + def infotext(index): # pylint: disable=function-redefined # noqa: F811 return create_infotext(p, p.prompts, p.seeds, p.subseeds, index=index, all_negative_prompts=p.negative_prompts) for i, x_sample in enumerate(x_samples_ddim): @@ -816,7 +817,7 @@ def process_images_inner(p: StableDiffusionProcessing) -> Processed: p.restore_faces = False info = infotext(i) p.restore_faces = orig - images.save_image(Image.fromarray(x_sample), path=p.outpath_samples, basename="", seed=p.seeds[i], prompt=p.prompts[i], extension=shared.opts.samples_format, info=info, p=p, suffix="-before-face-restoration") + images.save_image(Image.fromarray(x_sample), path=p.outpath_samples, basename="", seed=p.seeds[i], prompt=p.prompts[i], extension=shared.opts.samples_format, info=info, p=p, suffix="-before-face-restore") p.ops.append('face') x_sample = modules.face_restoration.restore_faces(x_sample) image = Image.fromarray(x_sample) @@ -831,7 +832,7 @@ def process_images_inner(p: StableDiffusionProcessing) -> Processed: info = infotext(i) p.color_corrections = orig image_without_cc = apply_overlay(image, p.paste_to, i, p.overlay_images) - images.save_image(image_without_cc, path=p.outpath_samples, basename="", seed=p.seeds[i], prompt=p.prompts[i], extension=shared.opts.samples_format, info=info, p=p, suffix="-before-color-correction") + images.save_image(image_without_cc, path=p.outpath_samples, basename="", seed=p.seeds[i], prompt=p.prompts[i], extension=shared.opts.samples_format, info=info, p=p, suffix="-before-color-correct") p.ops.append('color') image = apply_color_correction(p.color_corrections[i], image) image = apply_overlay(image, p.paste_to, i, p.overlay_images) @@ -840,7 +841,7 @@ def process_images_inner(p: StableDiffusionProcessing) -> Processed: image.info["parameters"] = text output_images.append(image) if shared.opts.samples_save and not p.do_not_save_samples: - images.save_image(image, p.outpath_samples, "", p.seeds[i], p.prompts[i], shared.opts.samples_format, info=text, p=p) + images.save_image(image, p.outpath_samples, "", p.seeds[i], p.prompts[i], shared.opts.samples_format, info=text, p=p) # main save image if hasattr(p, 'mask_for_overlay') and p.mask_for_overlay and any([shared.opts.save_mask, shared.opts.save_mask_composite, shared.opts.return_mask, shared.opts.return_mask_composite]): image_mask = p.mask_for_overlay.convert('RGB') image_mask_composite = Image.composite(image.convert('RGBA').convert('RGBa'), Image.new('RGBa', image.size), images.resize_image(3, p.mask_for_overlay, image.width, image.height).convert('L')).convert('RGBA') @@ -865,13 +866,13 @@ def process_images_inner(p: StableDiffusionProcessing) -> Processed: if images.check_grid_size(output_images): grid = images.image_grid(output_images, p.batch_size) if shared.opts.return_grid: - text = infotext() + text = infotext(-1) infotexts.insert(0, text) grid.info["parameters"] = text output_images.insert(0, grid) index_of_first_image = 1 if shared.opts.grid_save: - images.save_image(grid, p.outpath_grids, "grid", p.all_seeds[0], p.all_prompts[0], shared.opts.grid_format, info=infotext(), short_filename=not shared.opts.grid_extended_filename, p=p, grid=True) + images.save_image(grid, p.outpath_grids, "", p.all_seeds[0], p.all_prompts[0], shared.opts.grid_format, info=infotext(-1), short_filename=not shared.opts.grid_extended_filename, p=p, grid=True, suffix="-grid") # main save grid if not p.disable_extra_networks and extra_network_data: modules.extra_networks.deactivate(p, extra_network_data) @@ -880,7 +881,7 @@ def process_images_inner(p: StableDiffusionProcessing) -> Processed: p, images_list=output_images, seed=p.all_seeds[0], - info=infotext(), + info=infotext(0), comments="\n".join(comments), subseed=p.all_subseeds[0], index_of_first_image=index_of_first_image, @@ -987,7 +988,7 @@ class StableDiffusionProcessingTxt2Img(StableDiffusionProcessing): info = create_infotext(self, self.all_prompts, self.all_seeds, self.all_subseeds, [], iteration=self.iteration, position_in_batch=index) self.extra_generation_params = orig1 self.restore_faces = orig2 - images.save_image(image, self.outpath_samples, "", seeds[index], prompts[index], shared.opts.samples_format, info=info, suffix="-before-hires", index=index+1) + images.save_image(image, self.outpath_samples, "", seeds[index], prompts[index], shared.opts.samples_format, info=info, suffix="-before-hires") if shared.backend == shared.Backend.DIFFUSERS: modules.sd_models.set_diffuser_pipe(self.sd_model, modules.sd_models.DiffusersTaskType.TEXT_2_IMAGE) @@ -1140,7 +1141,7 @@ class StableDiffusionProcessingImg2Img(StableDiffusionProcessing): self.init_img_width = img.width # pylint: disable=attribute-defined-outside-init self.init_img_height = img.height # pylint: disable=attribute-defined-outside-init if shared.opts.save_init_img: - images.save_image(img, path=shared.opts.outdir_init_images, basename=None, forced_filename=self.init_img_hash, save_to_dirs=False) + images.save_image(img, path=shared.opts.outdir_init_images, basename=None, forced_filename=self.init_img_hash, save_to_dirs=False, suffix="-init-image") image = images.flatten(img, shared.opts.img2img_background_color) if crop_region is None and self.resize_mode != 4: image = images.resize_image(self.resize_mode, image, self.width, self.height) diff --git a/modules/sd_hijack.py b/modules/sd_hijack.py index 5cd407e19..3f465a600 100644 --- a/modules/sd_hijack.py +++ b/modules/sd_hijack.py @@ -37,45 +37,38 @@ def apply_optimizations(): can_use_sdp = hasattr(torch.nn.functional, "scaled_dot_product_attention") and callable(torch.nn.functional.scaled_dot_product_attention) if devices.device == torch.device("cpu"): if opts.cross_attention_optimization == "Scaled-Dot-Product": - shared.log.warning("Scaled dot product cross attention is not available on CPU") + shared.log.warning("Cross-attention: Scaled dot product is not available on CPU") can_use_sdp = False if opts.cross_attention_optimization == "xFormers": - shared.log.warning("xFormers cross attention is not available on CPU") + shared.log.warning("Cross-attention: xFormers is not available on CPU") shared.xformers_available = False - if opts.cross_attention_optimization == "Disable cross-attention layer optimization": - shared.log.warning("Cross-attention optimization disabled") + shared.log.info(f"Cross-attention: optimization={opts.cross_attention_optimization} options={opts.cross_attention_options}") + if opts.cross_attention_optimization == "Disabled": optimization_method = 'none' if can_use_sdp and opts.cross_attention_optimization == "Scaled-Dot-Product" and 'SDP disable memory attention' in opts.cross_attention_options: - shared.log.info("Applying scaled dot product cross attention optimization (without memory efficient attention)") ldm.modules.attention.CrossAttention.forward = sd_hijack_optimizations.scaled_dot_product_no_mem_attention_forward ldm.modules.diffusionmodules.model.AttnBlock.forward = sd_hijack_optimizations.sdp_no_mem_attnblock_forward optimization_method = 'sdp-no-mem' elif can_use_sdp and opts.cross_attention_optimization == "Scaled-Dot-Product": - shared.log.info("Applying scaled dot product cross attention optimization") ldm.modules.attention.CrossAttention.forward = sd_hijack_optimizations.scaled_dot_product_attention_forward ldm.modules.diffusionmodules.model.AttnBlock.forward = sd_hijack_optimizations.sdp_attnblock_forward optimization_method = 'sdp' if shared.xformers_available and opts.cross_attention_optimization == "xFormers": - shared.log.info("Applying xformers cross attention optimization") ldm.modules.attention.CrossAttention.forward = sd_hijack_optimizations.xformers_attention_forward ldm.modules.diffusionmodules.model.AttnBlock.forward = sd_hijack_optimizations.xformers_attnblock_forward optimization_method = 'xformers' if opts.cross_attention_optimization == "Sub-quadratic": - shared.log.info("Applying sub-quadratic cross attention optimization") ldm.modules.attention.CrossAttention.forward = sd_hijack_optimizations.sub_quad_attention_forward ldm.modules.diffusionmodules.model.AttnBlock.forward = sd_hijack_optimizations.sub_quad_attnblock_forward optimization_method = 'sub-quadratic' if opts.cross_attention_optimization == "Split attention": - shared.log.info("Applying split attention optimization") ldm.modules.attention.CrossAttention.forward = sd_hijack_optimizations.split_cross_attention_forward_v1 optimization_method = 'v1' if opts.cross_attention_optimization == "InvokeAI's": - shared.log.info("Applying InvokeAI cross attention optimization") ldm.modules.attention.CrossAttention.forward = sd_hijack_optimizations.split_cross_attention_forward_invokeAI optimization_method = 'invokeai' if opts.cross_attention_optimization == "Doggettx's": - shared.log.info("Applying Doggettx cross attention optimization") ldm.modules.attention.CrossAttention.forward = sd_hijack_optimizations.split_cross_attention_forward ldm.modules.diffusionmodules.model.AttnBlock.forward = sd_hijack_optimizations.cross_attention_attnblock_forward optimization_method = 'doggettx' diff --git a/modules/sd_models.py b/modules/sd_models.py index 5278015e6..d77134238 100644 --- a/modules/sd_models.py +++ b/modules/sd_models.py @@ -261,7 +261,7 @@ def select_checkpoint(op='model'): return None checkpoint_info = get_closet_checkpoint_match(model_checkpoint) if checkpoint_info is not None: - shared.log.debug(f'Select checkpoint: {op} {checkpoint_info.title if checkpoint_info is not None else None}') + shared.log.debug(f'Select checkpoint: {op}="{checkpoint_info.title if checkpoint_info is not None else None}"') return checkpoint_info if len(checkpoints_list) == 0 and not shared.cmd_opts.no_download: shared.log.error("Cannot run without a checkpoint") @@ -275,7 +275,7 @@ def select_checkpoint(op='model'): shared.log.info("Selecting first available checkpoint") # shared.log.warning(f"Loading fallback checkpoint: {checkpoint_info.title}") shared.opts.data['sd_model_checkpoint'] = checkpoint_info.title - shared.log.debug(f'Select checkpoint: {checkpoint_info.title if checkpoint_info is not None else None}') + shared.log.debug(f'Select checkpoint: {op}="{checkpoint_info.title if checkpoint_info is not None else None}"') return checkpoint_info diff --git a/modules/shared.py b/modules/shared.py index f1da73266..d69f0ff3b 100644 --- a/modules/shared.py +++ b/modules/shared.py @@ -340,7 +340,7 @@ def readfile(filename, silent=False): return data -def writefile(data, filename, mode='w'): +def writefile(data, filename, mode='w', silent=False): def default(obj): log.error(f"Saving: {filename} not a valid object: {obj}") @@ -350,7 +350,8 @@ def writefile(data, filename, mode='w'): with fasteners.InterProcessLock(f"{filename}.lock"): # skipkeys=True, ensure_ascii=True, check_circular=True, allow_nan=True output = json.dumps(data, indent=2, default=default) - log.debug(f'Saving: {filename} len={len(output)}') + if not silent: + log.debug(f'Saving: {filename} len={len(output)}') with open(filename, mode, encoding="utf8") as file: file.write(output) except Exception as e: diff --git a/modules/shared_items.py b/modules/shared_items.py index 8bb05b064..061fe4929 100644 --- a/modules/shared_items.py +++ b/modules/shared_items.py @@ -20,7 +20,7 @@ def refresh_vae_list(): def list_crossattention(): return [ - "Disable cross-attention layer optimization", + "Disabled", "xFormers", "Scaled-Dot-Product", "Doggettx's", diff --git a/modules/ui_common.py b/modules/ui_common.py index fa5569162..8d1bb48fd 100644 --- a/modules/ui_common.py +++ b/modules/ui_common.py @@ -75,17 +75,21 @@ def save_files(js_data, images, html_info, index): class PObject: # pylint: disable=too-few-public-methods def __init__(self, d=None): if d is not None: - for key, value in d.items(): - setattr(self, key, value) - self.seed = getattr(self, 'seed', None) or getattr(self, 'Seed', None) + for k, v in d.items(): + setattr(self, k, v) self.prompt = getattr(self, 'prompt', None) or getattr(self, 'Prompt', None) - self.all_seeds = getattr(self, 'all_seeds', [self.seed]) self.all_prompts = getattr(self, 'all_prompts', [self.prompt]) + self.negative_prompt = getattr(self, 'negative_prompt', None) + self.all_negative_prompt = getattr(self, 'all_negative_prompts', [self.negative_prompt]) + self.seed = getattr(self, 'seed', None) or getattr(self, 'Seed', None) + self.all_seeds = getattr(self, 'all_seeds', [self.seed]) + self.subseed = getattr(self, 'subseed', None) + self.all_subseeds = getattr(self, 'all_subseeds', [self.subseed]) + self.width = getattr(self, 'width', None) + self.height = getattr(self, 'height', None) + self.index_of_first_image = getattr(self, 'index_of_first_image', 0) self.infotexts = getattr(self, 'infotexts', [html_info]) self.infotext = self.infotexts[0] if len(self.infotexts) > 0 else html_info - self.index_of_first_image = getattr(self, 'index_of_first_image', 0) - self.batch_size = 1 - try: data = json.loads(js_data) except Exception: @@ -95,8 +99,6 @@ def save_files(js_data, images, html_info, index): if index > -1 and shared.opts.save_selected_only and (index >= p.index_of_first_image): # ensures we are looking at a specific non-grid picture, and we have save_selected_only # pylint: disable=no-member images = [images[index]] start_index = index - else: - p.batch_size = len(images) filenames = [] fullfns = [] for image_index, filedata in enumerate(images, start_index): @@ -114,12 +116,12 @@ def save_files(js_data, images, html_info, index): fullfns.append(fullfn) destination = shared.opts.outdir_save if shared.opts.use_save_to_dirs_for_ui: - namegen = modules.images.FilenameGenerator(p, seed=p.all_seeds[i], prompt=p.all_prompts[i], image=None, index=image_index) # pylint: disable=no-member + namegen = modules.images.FilenameGenerator(p, seed=p.all_seeds[i], prompt=p.all_prompts[i], image=None) # pylint: disable=no-member dirname = namegen.apply(shared.opts.directories_filename_pattern or "[prompt_words]").lstrip(' ').rstrip('\\ /') destination = os.path.join(destination, dirname) os.makedirs(destination, exist_ok = True) shutil.copy(fullfn, destination) - shared.log.info(f"Copying image: {fullfn} -> {destination}") + shared.log.info(f'Copying image: file="{fullfn}" folder="{destination}"') tgt_filename = os.path.join(destination, os.path.basename(fullfn)) modules.script_callbacks.image_save_btn_callback(tgt_filename) else: diff --git a/modules/ui_tempdir.py b/modules/ui_tempdir.py index 945a004b6..38b4b5923 100644 --- a/modules/ui_tempdir.py +++ b/modules/ui_tempdir.py @@ -61,7 +61,7 @@ def pil_to_temp_file(self, img, dir: str, format="png") -> str: # pylint: disabl file_obj = tempfile.NamedTemporaryFile(delete=False, suffix=".png", dir=dir) img.save(file_obj, pnginfo=(metadata if use_metadata else None)) name = file_obj.name - shared.log.debug(f'Saving temp image: {name}') + shared.log.debug(f'Saving temp: image="{name}"') return name From df377d6d279ac5845caeeffa96ab81258b61e88a Mon Sep 17 00:00:00 2001 From: Vladimir Mandic Date: Tue, 19 Sep 2023 12:55:32 -0400 Subject: [PATCH 09/11] safe handling of accelerate --- modules/processing_diffusers.py | 11 +++++++---- modules/sd_models.py | 18 +++++++++--------- modules/sd_vae.py | 4 ++-- 3 files changed, 18 insertions(+), 15 deletions(-) diff --git a/modules/processing_diffusers.py b/modules/processing_diffusers.py index d12e3e670..5362fa31a 100644 --- a/modules/processing_diffusers.py +++ b/modules/processing_diffusers.py @@ -65,7 +65,7 @@ def process_diffusers(p: StableDiffusionProcessing, seeds, prompts, negative_pro def full_vae_decode(latents, model): t0 = time.time() - if shared.opts.diffusers_move_unet and not model.has_accelerate: + if shared.opts.diffusers_move_unet and not getattr(model, 'has_accelerate', False): shared.log.debug('Moving to CPU: model=UNet') unet_device = model.unet.device model.unet.to(devices.cpu) @@ -80,7 +80,7 @@ def process_diffusers(p: StableDiffusionProcessing, seeds, prompts, negative_pro latents = latents.to(next(iter(model.vae.post_quant_conv.parameters())).dtype) decoded = model.vae.decode(latents / model.vae.config.scaling_factor, return_dict=False)[0] - if shared.opts.diffusers_move_unet and not model.has_accelerate: + if shared.opts.diffusers_move_unet and not getattr(model, 'has_accelerate', False): model.unet.to(unet_device) t1 = time.time() shared.log.debug(f'VAE decode: name={sd_vae.loaded_vae_file if sd_vae.loaded_vae_file is not None else "baked"} dtype={model.vae.dtype} upcast={upcast} images={latents.shape[0]} latents={latents.shape} time={round(t1-t0, 3)}s') @@ -88,7 +88,7 @@ def process_diffusers(p: StableDiffusionProcessing, seeds, prompts, negative_pro def full_vae_encode(image, model): shared.log.debug(f'VAE encode: name={sd_vae.loaded_vae_file if sd_vae.loaded_vae_file is not None else "baked"} dtype={model.vae.dtype} upcast={model.vae.config.get("force_upcast", None)}') - if shared.opts.diffusers_move_unet and not model.has_accelerate: + if shared.opts.diffusers_move_unet and not getattr(model, 'has_accelerate', False): shared.log.debug('Moving to CPU: model=UNet') unet_device = model.unet.device model.unet.to(devices.cpu) @@ -96,7 +96,7 @@ def process_diffusers(p: StableDiffusionProcessing, seeds, prompts, negative_pro if not shared.cmd_opts.lowvram and not shared.opts.diffusers_seq_cpu_offload: model.vae.to(devices.device) encoded = model.vae.encode(image.to(model.vae.device, model.vae.dtype)) - if shared.opts.diffusers_move_unet and not model.has_accelerate: + if shared.opts.diffusers_move_unet and not getattr(model, 'has_accelerate', False): model.unet.to(unet_device) return encoded @@ -358,6 +358,9 @@ def process_diffusers(p: StableDiffusionProcessing, seeds, prompts, negative_pro output = shared.sd_model(**base_args) # pylint: disable=not-callable except AssertionError as e: shared.log.info(e) + except ValueError as e: + shared.state.interrupted = True + shared.log.error(e) if hasattr(shared.sd_model, 'embedding_db') and len(shared.sd_model.embedding_db.embeddings_used) > 0: p.extra_generation_params['Embeddings'] = ', '.join(shared.sd_model.embedding_db.embeddings_used) diff --git a/modules/sd_models.py b/modules/sd_models.py index d77134238..c58a22932 100644 --- a/modules/sd_models.py +++ b/modules/sd_models.py @@ -896,7 +896,7 @@ def load_diffuser(checkpoint_info=None, already_loaded_state_dict=None, timer=No base_sent_to_cpu=False if (shared.opts.cuda_compile and shared.opts.cuda_compile_backend != 'none') or shared.opts.ipex_optimize: - if op == 'refiner' and not sd_model.has_accelerate: + if op == 'refiner' and not getattr(sd_model, 'has_accelerate', False): gpu_vram = memory_stats().get('gpu', {}) free_vram = gpu_vram.get('total', 0) - gpu_vram.get('used', 0) refiner_enough_vram = free_vram >= 7 if "StableDiffusionXL" in sd_model.__class__.__name__ else 3 @@ -917,7 +917,7 @@ def load_diffuser(checkpoint_info=None, already_loaded_state_dict=None, timer=No devices.torch_gc(force=True) sd_model.to(devices.device) base_sent_to_cpu=True - elif not sd_model.has_accelerate: + elif not getattr(sd_model, 'has_accelerate', False): sd_model.to(devices.device) compile_diffusers(sd_model) @@ -931,10 +931,10 @@ def load_diffuser(checkpoint_info=None, already_loaded_state_dict=None, timer=No shared.opts.data["sd_checkpoint_hash"] = checkpoint_info.sha256 if hasattr(sd_model, "set_progress_bar_config"): sd_model.set_progress_bar_config(bar_format='Progress {rate_fmt}{postfix} {bar} {percentage:3.0f}% {n_fmt}/{total_fmt} {elapsed} {remaining}', ncols=80, colour='#327fba') - if op == 'refiner' and shared.opts.diffusers_move_refiner and not sd_model.has_accelerate: + if op == 'refiner' and shared.opts.diffusers_move_refiner and not getattr(sd_model, 'has_accelerate', False): shared.log.debug('Moving refiner model to CPU') sd_model.to(devices.cpu) - elif not sd_model.has_accelerate: # In offload modes, accelerate will move models around + elif not getattr(sd_model, 'has_accelerate', False): # In offload modes, accelerate will move models around sd_model.to(devices.device) if op == 'refiner' and base_sent_to_cpu: shared.log.debug('Moving base model back to GPU') @@ -1067,6 +1067,7 @@ def load_model(checkpoint_info=None, already_loaded_state_dict=None, timer=None, shared.log.info(f'LDM: {line.strip()}') shared.log.debug(f"Model created from config: {checkpoint_config}") sd_model.used_config = checkpoint_config + sd_model.has_accelerate = False timer.record("create") ok = load_model_weights(sd_model, checkpoint_info, state_dict, timer) if not ok: @@ -1090,7 +1091,6 @@ def load_model(checkpoint_info=None, already_loaded_state_dict=None, timer=None, sd_hijack.model_hijack.hijack(sd_model) timer.record("hijack") sd_model.eval() - sd_model.has_accelerate = False if op == 'refiner': model_data.sd_refiner = sd_model else: @@ -1126,12 +1126,12 @@ def reload_model_weights(sd_model=None, info=None, reuse_dict=False, op='model') current_checkpoint_info = getattr(sd_model, 'sd_checkpoint_info', None) if current_checkpoint_info is not None and checkpoint_info is not None and current_checkpoint_info.filename == checkpoint_info.filename: return - if not sd_model.has_accelerate: + if not getattr(sd_model, 'has_accelerate', False): if shared.cmd_opts.lowvram or shared.cmd_opts.medvram: lowvram.send_everything_to_cpu() else: sd_model.to(devices.cpu) - if (reuse_dict or shared.opts.model_reuse_dict) and not sd_model.has_accelerate: + if (reuse_dict or shared.opts.model_reuse_dict) and not getattr(sd_model, 'has_accelerate', False): shared.log.info('Reusing previous model dictionary') sd_hijack.model_hijack.undo_hijack(sd_model) else: @@ -1164,7 +1164,7 @@ def reload_model_weights(sd_model=None, info=None, reuse_dict=False, op='model') timer.record("hijack") script_callbacks.model_loaded_callback(sd_model) timer.record("callbacks") - if sd_model is not None and not shared.cmd_opts.lowvram and not shared.cmd_opts.medvram and not sd_model.has_accelerate: + if sd_model is not None and not shared.cmd_opts.lowvram and not shared.cmd_opts.medvram and not getattr(sd_model, 'has_accelerate', False): sd_model.to(devices.device) timer.record("device") shared.log.info(f"Weights loaded in {timer.summary()}") @@ -1172,7 +1172,7 @@ def reload_model_weights(sd_model=None, info=None, reuse_dict=False, op='model') def disable_offload(sd_model): from accelerate.hooks import remove_hook_from_module - if not sd_model.has_accelerate: + if not getattr(sd_model, 'has_accelerate', False): return for _name, model in sd_model.components.items(): if not isinstance(model, torch.nn.Module): diff --git a/modules/sd_vae.py b/modules/sd_vae.py index 0360d83b3..173f54337 100644 --- a/modules/sd_vae.py +++ b/modules/sd_vae.py @@ -243,7 +243,7 @@ def reload_vae_weights(sd_model=None, vae_file=unspecified): vae_source = "function-argument" if loaded_vae_file == vae_file: return - if not sd_model.has_accelerate: + if not getattr(sd_model, 'has_accelerate', False): if shared.cmd_opts.lowvram or shared.cmd_opts.medvram: lowvram.send_everything_to_cpu() else: @@ -265,6 +265,6 @@ def reload_vae_weights(sd_model=None, vae_file=unspecified): if vae is not None: sd_model.vae = vae - if not shared.cmd_opts.lowvram and not shared.cmd_opts.medvram and not sd_model.has_accelerate: + if not shared.cmd_opts.lowvram and not shared.cmd_opts.medvram and not getattr(sd_model, 'has_accelerate', False): sd_model.to(devices.device) return sd_model From f82e05c5f1fbf483146049f0e4e79327b2cf304b Mon Sep 17 00:00:00 2001 From: Vladimir Mandic Date: Tue, 19 Sep 2023 13:05:42 -0400 Subject: [PATCH 10/11] second part of has_accelerate cleanup --- modules/processing_diffusers.py | 8 ++++---- modules/sd_models.py | 8 ++++---- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/modules/processing_diffusers.py b/modules/processing_diffusers.py index 5362fa31a..9d8605d75 100644 --- a/modules/processing_diffusers.py +++ b/modules/processing_diffusers.py @@ -319,7 +319,7 @@ def process_diffusers(p: StableDiffusionProcessing, seeds, prompts, negative_pro unload_diffusers_lora() return results - if shared.opts.diffusers_move_base and not shared.sd_model.has_accelerate: + if shared.opts.diffusers_move_base and not hasattr(shared.sd_model, 'has_accelerate', False): shared.sd_model.to(devices.device) is_img2img = bool(sd_models.get_diffusers_task(shared.sd_model) == sd_models.DiffusersTaskType.IMAGE_2_IMAGE or sd_models.get_diffusers_task(shared.sd_model) == sd_models.DiffusersTaskType.INPAINTING) @@ -417,7 +417,7 @@ def process_diffusers(p: StableDiffusionProcessing, seeds, prompts, negative_pro if is_refiner_enabled: if shared.opts.save and not p.do_not_save_samples and shared.opts.save_images_before_refiner and hasattr(shared.sd_model, 'vae'): save_intermediate(latents=output.images, suffix="-before-refiner") - if shared.opts.diffusers_move_base and not shared.sd_model.has_accelerate: + if shared.opts.diffusers_move_base and not hasattr(shared.sd_model, 'has_accelerate', False): shared.log.debug('Moving to CPU: model=base') shared.sd_model.to(devices.cpu) devices.torch_gc() @@ -433,7 +433,7 @@ def process_diffusers(p: StableDiffusionProcessing, seeds, prompts, negative_pro unload_diffusers_lora() return results - if shared.opts.diffusers_move_refiner and not shared.sd_refiner.has_accelerate: + if shared.opts.diffusers_move_refiner and not hasattr(shared.sd_refiner, 'has_accelerate', False): shared.sd_refiner.to(devices.device) refiner_is_sdxl = bool("StableDiffusionXL" in shared.sd_refiner.__class__.__name__) p.ops.append('refine') @@ -469,7 +469,7 @@ def process_diffusers(p: StableDiffusionProcessing, seeds, prompts, negative_pro for refiner_image in refiner_images: results.append(refiner_image) - if shared.opts.diffusers_move_refiner and not shared.sd_refiner.has_accelerate: + if shared.opts.diffusers_move_refiner and not hasattr(shared.sd_refiner, 'has_accelerate', False): shared.log.debug('Moving to CPU: model=refiner') shared.sd_refiner.to(devices.cpu) devices.torch_gc() diff --git a/modules/sd_models.py b/modules/sd_models.py index c58a22932..4ace52368 100644 --- a/modules/sd_models.py +++ b/modules/sd_models.py @@ -965,10 +965,10 @@ class DiffusersTaskType(Enum): INPAINTING = 3 def set_diffuser_pipe(pipe, new_pipe_type): - sd_checkpoint_info = pipe.sd_checkpoint_info if hasattr(pipe, "sd_checkpoint_info") else None - sd_model_checkpoint = pipe.sd_model_checkpoint if hasattr(pipe, "sd_model_checkpoint") else None - sd_model_hash = pipe.sd_model_hash if hasattr(pipe, "sd_model_hash") else None - has_accelerate = pipe.has_accelerate if hasattr(pipe, "has_accelerate") else None + sd_checkpoint_info = getattr(pipe, "sd_checkpoint_info", None) + sd_model_checkpoint = getattr(pipe, "sd_model_checkpoint", None) + sd_model_hash = getattr(pipe, "sd_model_hash", None) + has_accelerate = getattr(pipe, "has_accelerate", None) if new_pipe_type == DiffusersTaskType.TEXT_2_IMAGE: new_pipe = diffusers.AutoPipelineForText2Image.from_pipe(pipe) From d73f5f6ff6315c37d585ca9379ad86f3d55360b5 Mon Sep 17 00:00:00 2001 From: Vladimir Mandic Date: Tue, 19 Sep 2023 15:14:58 -0400 Subject: [PATCH 11/11] allow zero denoising --- modules/processing.py | 3 +++ modules/ui.py | 4 ++-- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/modules/processing.py b/modules/processing.py index 90c4038af..f325e6000 100644 --- a/modules/processing.py +++ b/modules/processing.py @@ -965,6 +965,9 @@ class StableDiffusionProcessingTxt2Img(StableDiffusionProcessing): if (self.hr_upscale_to_x == self.width and self.hr_upscale_to_y == self.height) or self.hr_upscaler is None or self.hr_upscaler == 'None': self.is_hr_pass = False return + if self.denoising_strength == 0: + self.is_hr_pass = False + return self.is_hr_pass = True if not shared.state.processing_has_refined_job_count: if shared.state.job_count == -1: diff --git a/modules/ui.py b/modules/ui.py index 8da53b22a..898da57d1 100644 --- a/modules/ui.py +++ b/modules/ui.py @@ -392,7 +392,7 @@ def create_ui(startup_timer = None): with FormGroup(): with FormRow(elem_id="sampler_selection_txt2img_alt_row1"): latent_index = gr.Dropdown(label='Secondary sampler', elem_id="txt2img_sampling_alt", choices=[x.name for x in modules.sd_samplers.samplers], value='Default', type="index") - denoising_strength = gr.Slider(minimum=0.05, maximum=1.0, step=0.01, label='Denoising strength', value=0.5, elem_id="txt2img_denoising_strength") + denoising_strength = gr.Slider(minimum=0.0, maximum=1.0, step=0.05, label='Denoising strength', value=0.5, elem_id="txt2img_denoising_strength") with FormRow(elem_id="txt2img_hires_finalres", variant="compact"): hr_final_resolution = FormHTML(value="", elem_id="txtimg_hr_finalres", label="Upscaled resolution", interactive=False) with FormRow(elem_id="txt2img_hires_fix_row1", variant="compact"): @@ -678,7 +678,7 @@ def create_ui(startup_timer = None): with FormGroup(visible=show_denoise.value, elem_id=f"{tab}_denoise_group") as denoise_group: with FormRow(): - denoising_strength = gr.Slider(minimum=0.05, maximum=1.0, step=0.01, label='Denoising strength', value=0.75, elem_id="img2img_denoising_strength") + denoising_strength = gr.Slider(minimum=0.0, maximum=1.0, step=0.05, label='Denoising strength', value=0.75, elem_id="img2img_denoising_strength") refiner_start = gr.Slider(minimum=0.0, maximum=1.0, step=0.05, label='Denoise start', value=0.0, elem_id="img2img_refiner_start") with FormGroup(visible=show_advanced.value, elem_id=f"{tab}_advanced_group") as advanced_group: