diff --git a/.ruff.toml b/.ruff.toml index fbd4e0b50..89c979e5d 100644 --- a/.ruff.toml +++ b/.ruff.toml @@ -67,6 +67,7 @@ ignore = [ "RUF010", # Use explicit conversion flag "RUF012", # Mutable class attributes "RUF013", # PEP 484 prohibits implicit `Optional` + "RUF015", # Prefer `next(...)` over single element slice ] fixable = ["ALL"] unfixable = [] diff --git a/CHANGELOG.md b/CHANGELOG.md index d297a4674..0769c70a0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -83,7 +83,7 @@ (in addition to previously added `/sdapi/v1/preprocessors` and `/sdapi/v1/masking`) example: > simple-control.py --prompt 'woman in the city' --sampler UniPC --steps 20 - > --input ~/generative/Samples/cutie-512.png --output /tmp/test.png --processed /tmp/proc.png + > --input \~/generative/Samples/cutie-512.png --output /tmp/test.png --processed /tmp/proc.png > --control 'Canny:Canny FP16:0.7, OpenPose:OpenPose FP16:0.8' --type controlnet > --ipadapter 'Plus:~/generative/Samples/cutie-512.png:0.5' - Add API endpoint `/sdapi/v1/vqa` and CLI util `cli/simple-vqa.py` diff --git a/installer.py b/installer.py index af1bb6c10..e46da0fd3 100644 --- a/installer.py +++ b/installer.py @@ -400,6 +400,16 @@ def check_python(): log.debug(f'Git {git_version.replace("git version", "").strip()}') +# check diffusers version +def check_diffusers(): + if args.experimental or args.skip_git: + return + if args.use_zluda: + install('diffusers', 'diffusers==0.27.2') + else: + install('diffusers', 'diffusers==0.28.0') + + # check onnx version def check_onnx(): if not installed('onnx', quiet=True): diff --git a/launch.py b/launch.py index 5533ce22f..d8da5f5bb 100755 --- a/launch.py +++ b/launch.py @@ -206,6 +206,7 @@ def main(): installer.set_environment() installer.check_torch() installer.check_onnx() + installer.check_diffusers() installer.check_modified_files() if args.reinstall: installer.log.info('Forcing reinstall of all packages') diff --git a/modules/hidiffusion/__init__.py b/modules/hidiffusion/__init__.py index 795ecb75f..50a52630a 100644 --- a/modules/hidiffusion/__init__.py +++ b/modules/hidiffusion/__init__.py @@ -6,6 +6,9 @@ from modules.hidiffusion import hidiffusion def apply_hidiffusion(p, model_type): + if model_type not in ['sd', 'sdxl'] and p.hidiffusion: + shared.log.warning(f'HiDiffusion: class={shared.sd_model.__class__.__name__} not supported') + return remove_hidiffusion(p) if p.hidiffusion: t0 = time.time() diff --git a/modules/processing_args.py b/modules/processing_args.py index 0e46b49f0..1f57817de 100644 --- a/modules/processing_args.py +++ b/modules/processing_args.py @@ -92,17 +92,20 @@ def set_pipeline_args(p, model, prompts: list, negative_prompts: list, prompts_2 steps = kwargs.get("num_inference_steps", None) or len(getattr(p, 'timesteps', ['1'])) if 'timesteps' in possible: - try: - timesteps = re.split(',| ', shared.opts.schedulers_timesteps) - timesteps = [int(x) for x in timesteps if x.isdigit()] - if len(timesteps) > 0: - args['timesteps'] = timesteps - p.steps = len(timesteps) - p.timesteps = timesteps - steps = p.steps - shared.log.debug(f'Sampler: steps={len(timesteps)} timesteps={timesteps}') - except Exception as e: - shared.log.error(f'Sampler timesteps: {e}') + if hasattr(model.scheduler, 'set_timesteps') and "timesteps" in set(inspect.signature(model.scheduler.set_timesteps).parameters.keys()): + try: + timesteps = re.split(',| ', shared.opts.schedulers_timesteps) + timesteps = [int(x) for x in timesteps if x.isdigit()] + if len(timesteps) > 0: + args['timesteps'] = timesteps + p.steps = len(timesteps) + p.timesteps = timesteps + steps = p.steps + shared.log.debug(f'Sampler: steps={len(timesteps)} timesteps={timesteps}') + except Exception as e: + shared.log.error(f'Sampler timesteps: {e}') + else: + shared.log.warning(f'Sampler: sampler={model.scheduler.__class__.__name__} timesteps not supported') if shared.opts.prompt_attention != 'Fixed attention' and ('StableDiffusion' in model.__class__.__name__ or 'StableCascade' in model.__class__.__name__) and 'Onnx' not in model.__class__.__name__: try: prompt_parser_diffusers.encode_prompts(model, p, prompts, negative_prompts, steps=steps, clip_skip=clip_skip) diff --git a/modules/processing_diffusers.py b/modules/processing_diffusers.py index 0e4b72608..9eddf3fba 100644 --- a/modules/processing_diffusers.py +++ b/modules/processing_diffusers.py @@ -85,6 +85,7 @@ def process_diffusers(p: processing.StableDiffusionProcessing): shared.sd_model = update_pipeline(shared.sd_model, p) shared.log.info(f'Base: class={shared.sd_model.__class__.__name__}') + update_sampler(p, shared.sd_model) base_args = set_pipeline_args( p=p, model=shared.sd_model, @@ -102,7 +103,6 @@ def process_diffusers(p: processing.StableDiffusionProcessing): clip_skip=p.clip_skip, desc='Base', ) - update_sampler(p, shared.sd_model) shared.state.sampling_steps = base_args.get('num_inference_steps', None) or p.steps p.extra_generation_params['Pipeline'] = shared.sd_model.__class__.__name__ if shared.opts.scheduler_eta is not None and shared.opts.scheduler_eta > 0 and shared.opts.scheduler_eta < 1: @@ -192,6 +192,7 @@ def process_diffusers(p: processing.StableDiffusionProcessing): sd_models.move_model(shared.sd_model, devices.device) orig_denoise = p.denoising_strength p.denoising_strength = getattr(p, 'hr_denoising_strength', p.denoising_strength) + update_sampler(p, shared.sd_model, second_pass=True) hires_args = set_pipeline_args( p=p, model=shared.sd_model, @@ -209,7 +210,6 @@ def process_diffusers(p: processing.StableDiffusionProcessing): strength=p.denoising_strength, desc='Hires', ) - update_sampler(p, shared.sd_model, second_pass=True) shared.state.job = 'HiRes' shared.state.sampling_steps = hires_args.get('num_inference_steps', None) or p.steps try: @@ -257,6 +257,7 @@ def process_diffusers(p: processing.StableDiffusionProcessing): if hasattr(p, 'task_args') and p.task_args.get('image', None) is not None and output is not None: # replace input with output so it can be used by hires/refine p.task_args['image'] = image shared.log.info(f'Refiner: class={shared.sd_refiner.__class__.__name__}') + update_sampler(p, shared.sd_refiner, second_pass=True) refiner_args = set_pipeline_args( p=p, model=shared.sd_refiner, @@ -275,7 +276,6 @@ def process_diffusers(p: processing.StableDiffusionProcessing): clip_skip=p.clip_skip, desc='Refiner', ) - update_sampler(p, shared.sd_refiner, second_pass=True) shared.state.sampling_steps = refiner_args.get('num_inference_steps', None) or p.steps try: if 'requires_aesthetics_score' in shared.sd_refiner.config: # sdxl-model needs false and sdxl-refiner needs true diff --git a/modules/sd_models.py b/modules/sd_models.py index 3a8bbcffd..2680390eb 100644 --- a/modules/sd_models.py +++ b/modules/sd_models.py @@ -610,6 +610,8 @@ def detect_pipeline(f: str, op: str = 'model', warning=True): if 'stable-cascade' in f.lower() or 'stablecascade' in f.lower() or 'wuerstchen3' in f.lower(): if shared.backend == shared.Backend.ORIGINAL: warn(f'Model detected as Stable Cascade model, but attempting to load using backend=original: {op}={f} size={size} MB') + if devices.dtype == torch.float16: + warn('Stable Cascade does not support Float16') guess = 'Stable Cascade' if 'pixart_sigma' in f.lower(): if shared.backend == shared.Backend.ORIGINAL: diff --git a/modules/sd_samplers_diffusers.py b/modules/sd_samplers_diffusers.py index b5cb4ef7b..81c05776d 100644 --- a/modules/sd_samplers_diffusers.py +++ b/modules/sd_samplers_diffusers.py @@ -1,4 +1,5 @@ import os +import re import inspect from modules import shared from modules import sd_samplers_common @@ -128,7 +129,9 @@ class DiffusionSampler: 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'] = shared.opts.schedulers_use_karras + timesteps = re.split(',| ', shared.opts.schedulers_timesteps) + timesteps = [int(x) for x in timesteps if x.isdigit()] + self.config['use_karras_sigmas'] = shared.opts.schedulers_use_karras if len(timesteps) == 0 else False if 'thresholding' in self.config: self.config['thresholding'] = shared.opts.schedulers_use_thresholding if 'lower_order_final' in self.config: diff --git a/requirements.txt b/requirements.txt index fede93e69..df1730d17 100644 --- a/requirements.txt +++ b/requirements.txt @@ -34,6 +34,7 @@ pymatting orjson invisible-watermark pi-heif +diffusers safetensors==0.4.3 tensordict==0.1.2 peft==0.11.1 @@ -46,7 +47,6 @@ requests==2.31.0 tqdm==4.66.4 accelerate==0.30.1 opencv-contrib-python-headless==4.9.0.80 -diffusers==0.28.0 einops==0.4.1 gradio==3.43.2 huggingface_hub==0.23.2