diff --git a/extensions-builtin/Lora/scripts/lora_script.py b/extensions-builtin/Lora/scripts/lora_script.py index bfe88b44b..b2803bf32 100644 --- a/extensions-builtin/Lora/scripts/lora_script.py +++ b/extensions-builtin/Lora/scripts/lora_script.py @@ -34,7 +34,7 @@ shared.options_templates.update(shared.options_section(('extra_networks', "Extra "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}), + "lora_in_memory_limit": shared.OptionInfo(0, "Lora in-memory cache", gr.Slider, {"minimum": 0, "maximum": 10, "step": 1}), })) diff --git a/modules/sd_models.py b/modules/sd_models.py index 7120271d1..8bf34d978 100644 --- a/modules/sd_models.py +++ b/modules/sd_models.py @@ -1240,6 +1240,9 @@ def apply_token_merging(sd_model, token_merging_ratio=0): except Exception: pass if token_merging_ratio > 0: + if shared.opts.hypertile_unet_enabled: + shared.log.warning('Token merging not supported with HyperTile for UNet') + return try: tomesd.apply_patch( sd_model, diff --git a/modules/sd_samplers_common.py b/modules/sd_samplers_common.py index 256bc119e..16fabefd4 100644 --- a/modules/sd_samplers_common.py +++ b/modules/sd_samplers_common.py @@ -2,10 +2,10 @@ from collections import namedtuple import numpy as np import torch from PIL import Image -from modules import devices, processing, images, sd_vae_approx, sd_samplers -import modules.shared as shared +from modules import devices, processing, images, sd_vae_approx, sd_samplers, shared import modules.taesd.sd_vae_taesd as sd_vae_taesd + SamplerData = namedtuple('SamplerData', ['name', 'constructor', 'aliases', 'options']) approximation_indexes = {"Full VAE": 0, "Approximate NN": 1, "Approximate simple": 2, "TAESD": 3} @@ -44,7 +44,8 @@ def single_sample_to_image(sample, approximation=None): return Image.new(mode="RGB", size=(512, 512)) x_sample = torch.clamp(255 * x_sample, min=0.0, max=255).cpu() x_sample = np.moveaxis(x_sample.numpy(), 0, 2).astype(np.uint8) - return Image.fromarray(x_sample) + image = Image.fromarray(x_sample) + return image def sample_to_image(samples, index=0, approximation=None): diff --git a/modules/sd_samplers_compvis.py b/modules/sd_samplers_compvis.py index cfad6dca8..548479ccc 100644 --- a/modules/sd_samplers_compvis.py +++ b/modules/sd_samplers_compvis.py @@ -1,11 +1,8 @@ import math import ldm.models.diffusion.ddim import ldm.models.diffusion.plms - import numpy as np import torch - -from modules.shared import state from modules import sd_samplers_common, prompt_parser, shared import modules.unipc @@ -43,8 +40,8 @@ class VanillaStableDiffusionSampler: return 0 def launch_sampling(self, steps, func): - state.sampling_steps = steps - state.sampling_step = 0 + shared.state.sampling_steps = steps + shared.state.sampling_step = 0 try: return func() except sd_samplers_common.InterruptedException: @@ -57,12 +54,12 @@ class VanillaStableDiffusionSampler: return res def before_sample(self, x, ts, cond, unconditional_conditioning): - if state.interrupted or state.skipped: + if shared.state.interrupted or shared.state.skipped: raise sd_samplers_common.InterruptedException - if state.paused: + if shared.state.paused: shared.log.debug('Sampling paused') - while state.paused: - if state.interrupted or state.skipped: + while shared.state.paused: + if shared.state.interrupted or shared.state.skipped: raise sd_samplers_common.InterruptedException import time time.sleep(0.1) @@ -120,7 +117,7 @@ class VanillaStableDiffusionSampler: self.last_latent = self.init_latent * self.mask + self.nmask * last_latent if self.mask is not None else last_latent sd_samplers_common.store_latent(self.last_latent) self.step += 1 - state.sampling_step = self.step + shared.state.sampling_step = self.step def after_sample(self, x, ts, cond, uncond, res): if not self.is_unipc: diff --git a/modules/sd_samplers_diffusers.py b/modules/sd_samplers_diffusers.py index fb9bf0f82..6dfd2c73c 100644 --- a/modules/sd_samplers_diffusers.py +++ b/modules/sd_samplers_diffusers.py @@ -1,4 +1,4 @@ -from modules.shared import opts, log +from modules import shared from modules import sd_samplers_common try: @@ -20,7 +20,7 @@ try: ) except Exception as e: import diffusers - log.error(f'Diffusers import error: version={diffusers.__version__} error: {e}') + shared.log.error(f'Diffusers import error: version={diffusers.__version__} error: {e}') config = { # beta_start, beta_end are typically per-scheduler, but we don't want them as they should be taken from the model itself as those are values model was trained on @@ -82,25 +82,25 @@ class DiffusionSampler: if key in self.config: self.config[key] = value # finally apply user preferences - if opts.schedulers_prediction_type != 'default': - self.config['prediction_type'] = opts.schedulers_prediction_type - if opts.schedulers_beta_schedule != 'default': - self.config['beta_schedule'] = opts.schedulers_beta_schedule + if shared.opts.schedulers_prediction_type != 'default': + self.config['prediction_type'] = shared.opts.schedulers_prediction_type + if shared.opts.schedulers_beta_schedule != 'default': + self.config['beta_schedule'] = shared.opts.schedulers_beta_schedule if 'use_karras_sigmas' in self.config: - self.config['use_karras_sigmas'] = opts.schedulers_use_karras + self.config['use_karras_sigmas'] = shared.opts.schedulers_use_karras if 'thresholding' in self.config: - self.config['thresholding'] = opts.schedulers_use_thresholding + self.config['thresholding'] = shared.opts.schedulers_use_thresholding if 'lower_order_final' in self.config: - self.config['lower_order_final'] = opts.schedulers_use_loworder + self.config['lower_order_final'] = shared.opts.schedulers_use_loworder if 'solver_order' in self.config: - self.config['solver_order'] = opts.schedulers_solver_order + self.config['solver_order'] = shared.opts.schedulers_solver_order if 'predict_x0' in self.config: - self.config['predict_x0'] = opts.uni_pc_variant + self.config['predict_x0'] = shared.opts.uni_pc_variant if name == 'DPM++ 2M': - self.config['algorithm_type'] = opts.schedulers_dpm_solver - if 'beta_start' in self.config and opts.schedulers_beta_start > 0: - self.config['beta_start'] = opts.schedulers_beta_start - if 'beta_end' in self.config and opts.schedulers_beta_end > 0: - self.config['beta_end'] = opts.schedulers_beta_end + self.config['algorithm_type'] = shared.opts.schedulers_dpm_solver + if 'beta_start' in self.config and shared.opts.schedulers_beta_start > 0: + self.config['beta_start'] = shared.opts.schedulers_beta_start + if 'beta_end' in self.config and shared.opts.schedulers_beta_end > 0: + self.config['beta_end'] = shared.opts.schedulers_beta_end self.sampler = constructor(**self.config) self.sampler.name = name diff --git a/modules/sd_samplers_kdiffusion.py b/modules/sd_samplers_kdiffusion.py index e6eb1daa3..6ea203e9c 100644 --- a/modules/sd_samplers_kdiffusion.py +++ b/modules/sd_samplers_kdiffusion.py @@ -7,7 +7,6 @@ from modules import prompt_parser from modules import devices from modules import sd_samplers_common -from modules.shared import opts, state import modules.shared as shared from modules.script_callbacks import CFGDenoiserParams, cfg_denoiser_callback from modules.script_callbacks import CFGDenoisedParams, cfg_denoised_callback @@ -78,12 +77,12 @@ class CFGDenoiser(torch.nn.Module): return denoised def forward(self, x, sigma, uncond, cond, cond_scale, s_min_uncond, image_cond): - if state.interrupted or state.skipped: + if shared.state.interrupted or shared.state.skipped: raise sd_samplers_common.InterruptedException - if state.paused: + if shared.state.paused: shared.log.debug('Sampling paused') - while state.paused: - if state.interrupted or state.skipped: + while shared.state.paused: + if shared.state.interrupted or shared.state.skipped: raise sd_samplers_common.InterruptedException import time time.sleep(0.1) @@ -116,7 +115,7 @@ class CFGDenoiser(torch.nn.Module): sigma_in = torch.cat([torch.stack([sigma[i] for _ in range(n)]) for i, n in enumerate(repeats)] + [sigma] + [sigma]) image_cond_in = torch.cat([torch.stack([image_cond[i] for _ in range(n)]) for i, n in enumerate(repeats)] + [image_uncond] + [torch.zeros_like(self.init_latent)]) - denoiser_params = CFGDenoiserParams(x_in, image_cond_in, sigma_in, state.sampling_step, state.sampling_steps, tensor, uncond) + denoiser_params = CFGDenoiserParams(x_in, image_cond_in, sigma_in, shared.state.sampling_step, shared.state.sampling_steps, tensor, uncond) cfg_denoiser_callback(denoiser_params) x_in = denoiser_params.x image_cond_in = denoiser_params.image_cond @@ -179,14 +178,14 @@ class CFGDenoiser(torch.nn.Module): fake_uncond = torch.cat([x_out[i:i+1] for i in denoised_image_indexes]) x_out = torch.cat([x_out, fake_uncond]) # we skipped uncond denoising, so we put cond-denoised image to where the uncond-denoised image should be - denoised_params = CFGDenoisedParams(x_out, state.sampling_step, state.sampling_steps, self.inner_model) + denoised_params = CFGDenoisedParams(x_out, shared.state.sampling_step, shared.state.sampling_steps, self.inner_model) cfg_denoised_callback(denoised_params) devices.test_for_nans(x_out, "unet") - if opts.live_preview_content == "Prompt": + if shared.opts.live_preview_content == "Prompt": sd_samplers_common.store_latent(torch.cat([x_out[i:i+1] for i in denoised_image_indexes])) - elif opts.live_preview_content == "Negative prompt": + elif shared.opts.live_preview_content == "Negative prompt": sd_samplers_common.store_latent(x_out[-uncond.shape[0]:]) if is_edit_model: @@ -199,7 +198,7 @@ class CFGDenoiser(torch.nn.Module): if self.mask is not None: denoised = self.init_latent * self.mask + self.nmask * denoised - after_cfg_callback_params = AfterCFGCallbackParams(denoised, state.sampling_step, state.sampling_steps) + after_cfg_callback_params = AfterCFGCallbackParams(denoised, shared.state.sampling_step, shared.state.sampling_steps) cfg_after_cfg_callback(after_cfg_callback_params) denoised = after_cfg_callback_params.x @@ -253,16 +252,16 @@ class KDiffusionSampler: def callback_state(self, d): step = d['i'] latent = d["denoised"] - if opts.live_preview_content == "Combined": + if shared.opts.live_preview_content == "Combined": sd_samplers_common.store_latent(latent) self.last_latent = latent if self.stop_at is not None and step > self.stop_at: raise sd_samplers_common.InterruptedException - state.sampling_step = step + shared.state.sampling_step = step def launch_sampling(self, steps, func): - state.sampling_steps = steps - state.sampling_step = 0 + shared.state.sampling_steps = steps + shared.state.sampling_step = 0 try: return func() except sd_samplers_common.InterruptedException: @@ -273,9 +272,9 @@ class KDiffusionSampler: def initialize(self, p): if self.config.options.get('brownian_noise', None) is not None: - self.config.options['brownian_noise'] = opts.data.get('schedulers_brownian_noise', False) + self.config.options['brownian_noise'] = shared.opts.data.get('schedulers_brownian_noise', False) if self.config.options.get('scheduler', None) is not None: - self.config.options['scheduler'] = opts.data.get('schedulers_sigma', None) + self.config.options['scheduler'] = shared.opts.data.get('schedulers_sigma', None) if p is None: return @@ -283,7 +282,7 @@ class KDiffusionSampler: self.model_wrap_cfg.nmask = p.nmask if hasattr(p, 'nmask') else None self.model_wrap_cfg.step = 0 self.model_wrap_cfg.image_cfg_scale = getattr(p, 'image_cfg_scale', None) - self.eta = p.eta if p.eta is not None else opts.scheduler_eta + self.eta = p.eta if p.eta is not None else shared.opts.scheduler_eta self.s_min_uncond = getattr(p, 's_min_uncond', 0.0) k_diffusion.sampling.torch = TorchHijack(self.sampler_noises if self.sampler_noises is not None else []) @@ -300,7 +299,7 @@ class KDiffusionSampler: return extra_params_kwargs def get_sigmas(self, p, steps): # pylint: disable=unused-argument - discard_next_to_last_sigma = opts.data.get('schedulers_discard_penultimate', True) if self.config.options.get('discard_next_to_last_sigma', None) is not None else False + discard_next_to_last_sigma = shared.opts.data.get('schedulers_discard_penultimate', True) if self.config.options.get('discard_next_to_last_sigma', None) is not None else False steps += 1 if discard_next_to_last_sigma else 0 if self.config.options.get('scheduler', None) == 'default' or self.config.options.get('scheduler', None) is None: diff --git a/modules/sd_vae_approx.py b/modules/sd_vae_approx.py index 78fd0f106..9b6ebe6bd 100644 --- a/modules/sd_vae_approx.py +++ b/modules/sd_vae_approx.py @@ -1,9 +1,9 @@ import os - import torch from torch import nn from modules import devices, paths + sd_vae_approx_model = None @@ -23,7 +23,7 @@ class VAEApprox(nn.Module): extra = 11 try: x = nn.functional.interpolate(x, (x.shape[2] * 2, x.shape[3] * 2)) - x = nn.functional.pad(x, (extra, extra, extra, extra)) + x = nn.functional.pad(x, (extra, extra, extra, extra)) # pylint: disable=not-callable for layer in [self.conv1, self.conv2, self.conv3, self.conv4, self.conv5, self.conv6, self.conv7, self.conv8, ]: x = layer(x) x = nn.functional.leaky_relu(x, 0.1) @@ -34,7 +34,6 @@ class VAEApprox(nn.Module): def model(): global sd_vae_approx_model # pylint: disable=global-statement - if sd_vae_approx_model is None: from modules.shared import log model_path = os.path.join(paths.models_path, "VAE-approx", "model.pt") @@ -45,7 +44,6 @@ def model(): sd_vae_approx_model.eval() sd_vae_approx_model.to(devices.device, devices.dtype) log.info(f"Loaded VAE-approx: model={model_path}") - return sd_vae_approx_model diff --git a/modules/shared.py b/modules/shared.py index c0ffd67ee..68e5a30b3 100644 --- a/modules/shared.py +++ b/modules/shared.py @@ -517,7 +517,7 @@ options_templates.update(options_section(('saving-images', "Image Options"), { "samples_save": OptionInfo(True, "Always save all generated images"), "samples_format": OptionInfo('jpg', 'File format for generated images', gr.Dropdown, lambda: {"choices": ["jpg", "png", "webp", "tiff", "jp2"]}), "jpeg_quality": OptionInfo(90, "Quality for saved jpeg images", gr.Slider, {"minimum": 1, "maximum": 100, "step": 1}), - "img_max_size_mp": OptionInfo(250, "Maximum allowed image size in megapixels", gr.Number), + "img_max_size_mp": OptionInfo(250, "Maximum image size (MP)", gr.Slider, {"minimum": 100, "maximum": 2000, "step": 1}), "webp_lossless": OptionInfo(False, "Use lossless compression for webp images"), "save_selected_only": OptionInfo(True, "When using 'Save' button, only save a single selected image"), "samples_save_zip": OptionInfo(True, "Create zip archive when downloading multiple images"), @@ -621,9 +621,9 @@ options_templates.update(options_section(('sampler-params', "Sampler Settings"), # managed from ui.py for backend diffusers "schedulers_sep_diffusers": OptionInfo("