From 8dfbf6bf4f40f22db4773ce72ee856d03865ed4e Mon Sep 17 00:00:00 2001 From: Vladimir Mandic Date: Wed, 1 Jan 2025 16:03:42 -0500 Subject: [PATCH 1/3] update readme Signed-off-by: Vladimir Mandic --- README.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index a612f6127..e9312568e 100644 --- a/README.md +++ b/README.md @@ -31,8 +31,9 @@ All individual features are not listed here, instead check [ChangeLog](CHANGELOG - Multiplatform! ▹ **Windows | Linux | MacOS | nVidia | AMD | IntelArc/IPEX | DirectML | OpenVINO | ONNX+Olive | ZLUDA** - Platform specific autodetection and tuning performed on install -- Optimized processing with latest `torch` developments with built-in support for `torch.compile` - and multiple compile backends: *Triton, StableFast, DeepCache, NNCF, OneDiff* +- Optimized processing with latest `torch` developments with built-in support for model compile, quantize and compress + Compile backends: *Triton | StableFast | DeepCache | OneDiff* + Quantization and compression methods: *BitsAndBytes | TorchAO | Optimum-Quanto | NNCF* - Built-in queue management - Built in installer with automatic updates and dependency management - Mobile compatible From 94a659ee1f9061d97a431f0de2afee63c93e2cdd Mon Sep 17 00:00:00 2001 From: Vladimir Mandic Date: Thu, 2 Jan 2025 11:00:10 -0500 Subject: [PATCH 2/3] fix xyz with detailer, sd35 img2img Signed-off-by: Vladimir Mandic --- CHANGELOG.md | 7 +++++-- modules/postprocess/yolo.py | 24 +++++++++++------------- modules/processing.py | 2 +- modules/processing_args.py | 3 +++ modules/processing_class.py | 2 +- modules/sd_models.py | 2 +- modules/sd_samplers_diffusers.py | 5 +++++ scripts/xyz_grid_on.py | 2 ++ 8 files changed, 29 insertions(+), 18 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7ecde280c..8a24d8ff5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,6 @@ # Change Log for SD.Next -## Update for 2025-01-01 +## Update for 2025-01-02 - [Allegro Video](https://huggingface.co/rhymes-ai/Allegro) - optimizations: full offload and quantization support @@ -18,8 +18,11 @@ - **Fixes**: - explict clear caches on model load - lock adetailer commit: `#a89c01d` - - xyzgrid fix progress calculation + - xyzgrid progress calculation + - xyzgrid detailer - vae tiling use default value if not set + - sd35 img2img + - samplers test for scale noise before using ## Update for 2024-12-31 diff --git a/modules/postprocess/yolo.py b/modules/postprocess/yolo.py index 3ee2406d1..4011147ec 100644 --- a/modules/postprocess/yolo.py +++ b/modules/postprocess/yolo.py @@ -1,5 +1,6 @@ from typing import TYPE_CHECKING import os +from copy import copy import numpy as np import gradio as gr from PIL import Image, ImageDraw @@ -259,23 +260,23 @@ def restore(self, np_image, p: processing.StableDiffusionProcessing = None): report = [{'label': i.label, 'score': i.score, 'size': f'{i.width}x{i.height}' } for i in items] shared.log.info(f'Detailer: model="{name}" items={report} args={items[0].args} denoise={p.denoising_strength} blur={p.mask_blur} width={p.width} height={p.height} padding={p.inpaint_full_res_padding}') - shared.log.debug(f'Detailer: prompt="{prompt}" negative="{negative}"') + # shared.log.debug(f'Detailer: prompt="{prompt}" negative="{negative}"') models_used.append(name) mask_all = [] p.state = '' prev_state = shared.state.job + pc = copy(p) for item in items: if item.mask is None: continue - p.init_images = [image] - p.image_mask = [item.mask] - # mask_all.append(item.mask) - p.recursion = True + pc.init_images = [image] + pc.image_mask = [item.mask] + pc.overlay_images = [] + pc.recursion = True shared.state.job = 'Detailer' - pp = processing.process_images_inner(p) - del p.recursion - p.overlay_images = None # skip applying overlay twice + pp = processing.process_images_inner(pc) + del pc.recursion if pp is not None and pp.images is not None and len(pp.images) > 0: image = pp.images[0] # update image to be reused for next item if len(pp.images) > 1: @@ -298,13 +299,10 @@ def restore(self, np_image, p: processing.StableDiffusionProcessing = None): if len(mask_all) > 0 and shared.opts.include_mask: from modules.control.util import blend p.image_mask = blend([np.array(m) for m in mask_all]) - # combined = blend([np_image, p.image_mask]) - # combined = Image.fromarray(combined) - # combined.save('/tmp/item.png') p.image_mask = Image.fromarray(p.image_mask) - if len(models_used) > 0: - shared.log.debug(f'Detailer processed: models={models_used}') + # if len(models_used) > 0: + # shared.log.debug(f'Detailer processed: models={models_used}') return np_image def ui(self, tab: str): diff --git a/modules/processing.py b/modules/processing.py index c85938268..23fde5dee 100644 --- a/modules/processing.py +++ b/modules/processing.py @@ -463,7 +463,7 @@ def process_images_inner(p: StableDiffusionProcessing) -> Processed: ipadapter.unapply(shared.sd_model, unload=getattr(p, 'ip_adapter_unload', False)) if shared.opts.include_mask: - if shared.opts.mask_apply_overlay and p.overlay_images is not None and len(p.overlay_images): + if shared.opts.mask_apply_overlay and p.overlay_images is not None and len(p.overlay_images) > 0: p.image_mask = create_binary_mask(p.overlay_images[0]) p.image_mask = ImageOps.invert(p.image_mask) output_images.append(p.image_mask) diff --git a/modules/processing_args.py b/modules/processing_args.py index f39537538..360928de7 100644 --- a/modules/processing_args.py +++ b/modules/processing_args.py @@ -307,6 +307,9 @@ def set_pipeline_args(p, model, prompts:list, negative_prompts:list, prompts_2:t if isinstance(args['image'], torch.Tensor) or isinstance(args['image'], np.ndarray): args['width'] = 8 * args['image'].shape[-1] args['height'] = 8 * args['image'].shape[-2] + elif isinstance(args['image'][0], torch.Tensor) or isinstance(args['image'][0], np.ndarray): + args['width'] = 8 * args['image'][0].shape[-1] + args['height'] = 8 * args['image'][0].shape[-2] else: args['width'] = 8 * math.ceil(args['image'][0].width / 8) args['height'] = 8 * math.ceil(args['image'][0].height / 8) diff --git a/modules/processing_class.py b/modules/processing_class.py index 96761d262..5960fea76 100644 --- a/modules/processing_class.py +++ b/modules/processing_class.py @@ -502,7 +502,7 @@ def init(self, all_prompts=None, all_seeds=None, all_subseeds=None): image = images.resize_image(self.resize_mode, image, self.width, self.height, upscaler_name=self.resize_name, context=self.resize_context) self.width = image.width self.height = image.height - if self.image_mask is not None and shared.opts.mask_apply_overlay and not hasattr(self, 'xyz'): + if self.image_mask is not None and shared.opts.mask_apply_overlay: image_masked = Image.new('RGBa', (image.width, image.height)) image_to_paste = image.convert("RGBA").convert("RGBa") image_to_mask = ImageOps.invert(self.mask_for_overlay.convert('L')) if self.mask_for_overlay is not None else None diff --git a/modules/sd_models.py b/modules/sd_models.py index c401ae6b7..51d25edfa 100644 --- a/modules/sd_models.py +++ b/modules/sd_models.py @@ -1288,7 +1288,7 @@ def set_diffuser_pipe(pipe, new_pipe_type): new_pipe.image_encoder = image_encoder if feature_extractor is not None: new_pipe.feature_extractor = feature_extractor - if new_pipe.__class__.__name__ == 'FluxPipeline': + if new_pipe.__class__.__name__ in ['FluxPipeline', 'StableDiffusion3Pipeline']: new_pipe.register_modules(image_encoder = image_encoder) new_pipe.register_modules(feature_extractor = feature_extractor) new_pipe.is_sdxl = getattr(pipe, 'is_sdxl', False) # a1111 compatibility item diff --git a/modules/sd_samplers_diffusers.py b/modules/sd_samplers_diffusers.py index 06459d8a8..6e5f37cd7 100644 --- a/modules/sd_samplers_diffusers.py +++ b/modules/sd_samplers_diffusers.py @@ -271,11 +271,16 @@ def __init__(self, name, constructor, model, **kwargs): sampler = constructor(**self.config) accept_sigmas = "sigmas" in set(inspect.signature(sampler.set_timesteps).parameters.keys()) accepts_timesteps = "timesteps" in set(inspect.signature(sampler.set_timesteps).parameters.keys()) + accept_scale_noise = hasattr(sampler, "scale_noise") debug(f'Sampler: sampler="{name}" sigmas={accept_sigmas} timesteps={accepts_timesteps}') if ('Flux' in model.__class__.__name__) and (not accept_sigmas): shared.log.warning(f'Sampler: sampler="{name}" does not accept sigmas') self.sampler = None return + if ('StableDiffusion3' in model.__class__.__name__) and (not accept_scale_noise): + shared.log.warning(f'Sampler: sampler="{name}" does not implement scale noise') + self.sampler = None + return self.sampler = sampler if name == 'DC Solver': if not hasattr(self.sampler, 'dc_ratios'): diff --git a/scripts/xyz_grid_on.py b/scripts/xyz_grid_on.py index ba23356da..0abd0fa1c 100644 --- a/scripts/xyz_grid_on.py +++ b/scripts/xyz_grid_on.py @@ -325,12 +325,14 @@ def cell(x, y, z, ix, iy, iz): x_opt.apply(pc, x, xs) y_opt.apply(pc, y, ys) z_opt.apply(pc, z, zs) + try: processed = processing.process_images(pc) except Exception as e: shared.log.error(f"XYZ grid: Failed to process image: {e}") errors.display(e, 'XYZ grid') processed = None + if ix == 0 and iy == 0: # create subgrid info text pc.extra_generation_params = copy(pc.extra_generation_params) pc.extra_generation_params['Script'] = self.title() From 9f30abbad5a3bd89d24bbe74c9c438fb418d0c6d Mon Sep 17 00:00:00 2001 From: Vladimir Mandic Date: Thu, 2 Jan 2025 16:45:08 -0500 Subject: [PATCH 3/3] fix scheduler api Signed-off-by: Vladimir Mandic --- CHANGELOG.md | 3 +++ extensions-builtin/sd-extension-system-info | 2 +- modules/api/api.py | 4 ++++ modules/api/models.py | 10 +++------- modules/shared.py | 8 ++++++-- 5 files changed, 17 insertions(+), 10 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8a24d8ff5..55ecccf30 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -15,6 +15,8 @@ - update installer messages - **Detailer**: - add explicit detailer steps setting +- **SysInfo**: + - update to collected data and benchmarks - **Fixes**: - explict clear caches on model load - lock adetailer commit: `#a89c01d` @@ -23,6 +25,7 @@ - vae tiling use default value if not set - sd35 img2img - samplers test for scale noise before using + - scheduler api ## Update for 2024-12-31 diff --git a/extensions-builtin/sd-extension-system-info b/extensions-builtin/sd-extension-system-info index dfa01ce99..aafc660ba 160000 --- a/extensions-builtin/sd-extension-system-info +++ b/extensions-builtin/sd-extension-system-info @@ -1 +1 @@ -Subproject commit dfa01ce99a17d76b45284ef28cef018ff52ac353 +Subproject commit aafc660ba3cf78724059917f7c3a05f3ac1ed46a diff --git a/modules/api/api.py b/modules/api/api.py index b958085ea..b38456d8f 100644 --- a/modules/api/api.py +++ b/modules/api/api.py @@ -99,6 +99,10 @@ def __init__(self, app: FastAPI, queue_lock: Lock): # gallery api gallery.register_api(app) + # compatibility api + self.text2imgapi = self.generate.post_text2img + self.img2imgapi = self.generate.post_img2img + def add_api_route(self, path: str, endpoint, **kwargs): if (shared.cmd_opts.auth or shared.cmd_opts.auth_file) and shared.cmd_opts.api_only: return self.app.add_api_route(path, endpoint, dependencies=[Depends(self.auth)], **kwargs) diff --git a/modules/api/models.py b/modules/api/models.py index 39bcbe383..ce423b639 100644 --- a/modules/api/models.py +++ b/modules/api/models.py @@ -1,5 +1,5 @@ import inspect -from typing import Any, Optional, Dict, List, Type, Callable +from typing import Any, Optional, Dict, List, Type, Callable, Union from pydantic import BaseModel, Field, create_model # pylint: disable=no-name-in-module from inflection import underscore from modules.processing import StableDiffusionProcessingTxt2Img, StableDiffusionProcessingImg2Img @@ -194,7 +194,7 @@ class ItemExtension(BaseModel): "StableDiffusionProcessingTxt2Img", StableDiffusionProcessingTxt2Img, [ - {"key": "sampler_index", "type": int, "default": 0}, + {"key": "sampler_index", "type": Union[int, str], "default": 0}, {"key": "sampler_name", "type": str, "default": "UniPC"}, {"key": "hr_sampler_name", "type": str, "default": "Same as primary"}, {"key": "script_name", "type": str, "default": "none"}, @@ -218,7 +218,7 @@ class ResTxt2Img(BaseModel): "StableDiffusionProcessingImg2Img", StableDiffusionProcessingImg2Img, [ - {"key": "sampler_index", "type": int, "default": 0}, + {"key": "sampler_index", "type": Union[int, str], "default": 0}, {"key": "sampler_name", "type": str, "default": "UniPC"}, {"key": "hr_sampler_name", "type": str, "default": "Same as primary"}, {"key": "script_name", "type": str, "default": "none"}, @@ -405,10 +405,6 @@ class ResNVML(BaseModel): # definition of http response state: str = Field(title="State") -# compatibility items -StableDiffusionTxt2ImgProcessingAPI = ResTxt2Img -StableDiffusionImg2ImgProcessingAPI = ResImg2Img - # helper function def create_model_from_signature(func: Callable, model_name: str, base_model: Type[BaseModel] = BaseModel, additional_fields: List = [], exclude_fields: List[str] = []): diff --git a/modules/shared.py b/modules/shared.py index 2ccb1e0a9..6150017d0 100644 --- a/modules/shared.py +++ b/modules/shared.py @@ -303,8 +303,12 @@ def validate(self, opt, value): # return False minimum = args.get("minimum", None) maximum = args.get("maximum", None) - if (minimum is not None and value < minimum) or (maximum is not None and value > maximum): - log.error(f'Setting validation: "{opt}"={value} default={self.default} minimum={minimum} maximum={maximum}') + try: + if (minimum is not None and value < minimum) or (maximum is not None and value > maximum): + log.error(f'Setting validation: "{opt}"={value} default={self.default} minimum={minimum} maximum={maximum}') + return False + except Exception as err: + log.error(f'Setting validation: "{opt}"={value} default={self.default} minimum={minimum} maximum={maximum} error={err}') return False return True