From ea0389162f6fa8de7cb0859fb2c1661c41b37fba Mon Sep 17 00:00:00 2001 From: Vladimir Mandic Date: Thu, 3 Apr 2025 19:23:33 -0400 Subject: [PATCH 1/7] check for init and call Signed-off-by: Vladimir Mandic --- modules/onnx_impl/pipelines/__init__.py | 2 +- modules/sd_models_utils.py | 4 +++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/modules/onnx_impl/pipelines/__init__.py b/modules/onnx_impl/pipelines/__init__.py index 62db80cd5..6d035771d 100644 --- a/modules/onnx_impl/pipelines/__init__.py +++ b/modules/onnx_impl/pipelines/__init__.py @@ -47,7 +47,7 @@ class PipelineBase(TorchCompatibleModule, diffusers.DiffusionPipeline, metaclass if "optimum.onnxruntime" in sys.modules: import optimum.onnxruntime - if isinstance(module, optimum.onnxruntime.modeling_diffusion._ORTDiffusionModelPart): # pylint: disable=protected-access + if isinstance(module, optimum.onnxruntime.modeling_diffusion._ORTDiffusionModelPart): # pylint: disable=protected-access, no-member device = extract_device(args, kwargs) if device is None: return self diff --git a/modules/sd_models_utils.py b/modules/sd_models_utils.py index 0b2794bac..ae50e5748 100644 --- a/modules/sd_models_utils.py +++ b/modules/sd_models_utils.py @@ -19,12 +19,14 @@ class NoWatermark: def get_signature(cls): + if cls is None or not hasattr(cls, '__init__'): + return {} signature = inspect.signature(cls.__init__, follow_wrapped=True) return signature.parameters def get_call(cls): - if cls is None: + if cls is None or not hasattr(cls, '__call__'): # noqa: B004 return [] signature = inspect.signature(cls.__call__, follow_wrapped=True) return signature.parameters From cd8357f1f47603aad3f99b643fa47b1fc630dba6 Mon Sep 17 00:00:00 2001 From: Vladimir Mandic Date: Thu, 3 Apr 2025 21:12:58 -0400 Subject: [PATCH 2/7] add detailer renoise feature Signed-off-by: Vladimir Mandic --- CHANGELOG.md | 22 +++++++++++---------- cli/image-exif.py | 13 ++++++++++++- modules/postprocess/yolo.py | 34 +++++++++++++++++++++++---------- modules/processing_callbacks.py | 1 + modules/shared.py | 2 ++ 5 files changed, 51 insertions(+), 21 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0e73825b3..c4d8c91ba 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -134,21 +134,23 @@ Models...And support for new models: **CogView-4**, **SANA 1.5**, - `torch.compile` is now available - Flash Attention 2 is now available - **Other** - - new command line option `--monitor PERIOD` to monitor CPU and GPU memory ever n seconds - - **upscale**: new [asymmetric vae v2](https://huggingface.co/Heasterian/AsymmetricAutoencoderKLUpscaler_v2) upscaling method - - **upscale**: new experimental support for `libvips` upscaling - - **quantization**: add support for `optimum-quanto` on-the-fly quantization during load for all models + - **Command line** new option `--monitor PERIOD` to monitor CPU and GPU memory ever n seconds + - **Upscale** new [asymmetric vae v2](https://huggingface.co/Heasterian/AsymmetricAutoencoderKLUpscaler_v2) upscaling method + - **Upscale** new experimental support for `libvips` upscaling + - **Quantization** add support for `optimum-quanto` on-the-fly quantization during load for all models note: previous method for quanto is still valid and is noted in settings as post-load quantization - - add quantization support to **CogView-3Plus** - - update `diffusers` and other requirements - - rename vae, unet and text-encoder settings *None* to *Default* to avoid confusion + - **Quantization** add support to **CogView-3Plus** + - **Default values** rename vae, unet and text-encoder settings *None* to *Default* to avoid confusion + - **Detailer**: add *renoise* option to increase/decrease noise during detailer pass + which can help with improving level of details - **CLI**: add `cli/api-grid.py` which can generate grids using params-from-file for x/y axis - **Samplers** add ability to set sigma adjustment for each sampler - **ModernUI** updates - **CSS** updates - - settings vertiocal/dirty indicator restores to default setting instead to previous value - - video interpolate do not skip duplicate frames - - **settings UI** full refactor + - **Video** interpolate do not skip duplicate frames + - **Settings UI** full refactor + - **Settings UI** vertical/dirty indicator restores to default setting instead to previous value + - update `diffusers` and other requirements - **Wiki/Docs** - updated [Models](https://github.com/vladmandic/sdnext/wiki/Models) info - new [Video](https://github.com/vladmandic/sdnext/wiki/Video) guide diff --git a/cli/image-exif.py b/cli/image-exif.py index 2e3754241..9a48d2dd7 100755 --- a/cli/image-exif.py +++ b/cli/image-exif.py @@ -4,6 +4,7 @@ import os import io import re import sys +import json import importlib.util from PIL import Image, ExifTags, TiffImagePlugin, PngImagePlugin from rich import print # pylint: disable=redefined-builtin @@ -95,6 +96,14 @@ class Exif: # pylint: disable=single-string-used-for-slots return raw +def print_json(data): + try: + for k, v in data.items(): + print(f'json: k={k}', json.loads(v)) + except Exception: + pass + + def read_exif(filename: str): if filename.lower().endswith('.heic'): from pi_heif import register_heif_opener @@ -103,8 +112,10 @@ def read_exif(filename: str): image = Image.open(filename) exif = Exif(image) print('image:', filename, 'format:', image) - print('exif:', vars(exif.exif)['_data']) + data = vars(exif.exif)['_data'] + print('exif:', data) print('info:', exif.parse()) + print_json(data) except Exception as e: print('metadata error reading:', filename, e) diff --git a/modules/postprocess/yolo.py b/modules/postprocess/yolo.py index 8054e03ea..d57d59e04 100644 --- a/modules/postprocess/yolo.py +++ b/modules/postprocess/yolo.py @@ -293,6 +293,12 @@ class YoloRestorer(Detailer): p.state = '' prev_state = shared.state.job pc = copy(p) + + orig_sigma_adjust: float = shared.opts.schedulers_sigma_adjust + orig_sigma_end: float = shared.opts.schedulers_sigma_adjust_max + shared.opts.schedulers_sigma_adjust = shared.opts.detailer_sigma_adjust + shared.opts.schedulers_sigma_adjust_max = shared.opts.detailer_sigma_adjust_max + for item in items: if item.mask is None: continue @@ -308,6 +314,9 @@ class YoloRestorer(Detailer): if len(pp.images) > 1: mask_all.append(pp.images[1]) + shared.opts.schedulers_sigma_adjust = orig_sigma_adjust + shared.opts.schedulers_sigma_adjust_max = orig_sigma_end + # restore pipeline if control_pipeline is not None: shared.sd_model = control_pipeline @@ -330,7 +339,7 @@ class YoloRestorer(Detailer): return np_image def ui(self, tab: str): - def ui_settings_change(detailers, classes, strength, padding, blur, min_confidence, max_detected, min_size, max_size, iou, steps): + def ui_settings_change(detailers, classes, strength, padding, blur, min_confidence, max_detected, min_size, max_size, iou, steps, renoise_value, renoise_end): shared.opts.detailer_models = detailers shared.opts.detailer_classes = classes shared.opts.detailer_padding = padding @@ -340,6 +349,8 @@ class YoloRestorer(Detailer): shared.opts.detailer_min_size = min_size shared.opts.detailer_max_size = max_size shared.opts.detailer_iou = iou + shared.opts.detailer_sigma_adjust = renoise_value + shared.opts.detailer_sigma_adjust_max = renoise_end shared.opts.save(shared.config_filename, silent=True) shared.log.debug(f'Detailer settings: models={detailers} classes={classes} strength={strength} conf={min_confidence} max={max_detected} iou={iou} size={min_size}-{max_size} padding={padding} steps={steps}') @@ -371,15 +382,18 @@ class YoloRestorer(Detailer): min_size = gr.Slider(label="Min size", elem_id=f"{tab}_detailer_min_size", value=min_size, minimum=0.0, maximum=1.0, step=0.05) max_size = shared.opts.detailer_max_size if shared.opts.detailer_max_size < 1 and shared.opts.detailer_max_size > 0 else 1.0 max_size = gr.Slider(label="Max size", elem_id=f"{tab}_detailer_max_size", value=max_size, minimum=0.0, maximum=1.0, step=0.05) - detailers.change(fn=ui_settings_change, inputs=[detailers, classes, strength, padding, blur, min_confidence, max_detected, min_size, max_size, iou, steps], outputs=[]) - classes.change(fn=ui_settings_change, inputs=[detailers, classes, strength, padding, blur, min_confidence, max_detected, min_size, max_size, iou, steps], outputs=[]) - padding.change(fn=ui_settings_change, inputs=[detailers, classes, strength, padding, blur, min_confidence, max_detected, min_size, max_size, iou, steps], outputs=[]) - blur.change(fn=ui_settings_change, inputs=[detailers, classes, strength, padding, blur, min_confidence, max_detected, min_size, max_size, iou, steps], outputs=[]) - min_confidence.change(fn=ui_settings_change, inputs=[detailers, classes, strength, padding, blur, min_confidence, max_detected, min_size, max_size, iou, steps], outputs=[]) - max_detected.change(fn=ui_settings_change, inputs=[detailers, classes, strength, padding, blur, min_confidence, max_detected, min_size, max_size, iou, steps], outputs=[]) - min_size.change(fn=ui_settings_change, inputs=[detailers, classes, strength, padding, blur, min_confidence, max_detected, min_size, max_size, iou, steps], outputs=[]) - max_size.change(fn=ui_settings_change, inputs=[detailers, classes, strength, padding, blur, min_confidence, max_detected, min_size, max_size, iou, steps], outputs=[]) - iou.change(fn=ui_settings_change, inputs=[detailers, classes, strength, padding, blur, min_confidence, max_detected, min_size, max_size, iou, steps], outputs=[]) + with gr.Row(elem_classes=['flex-break']): + renoise_value = gr.Slider(minimum=0.5, maximum=1.5, step=0.01, label='Renoiose', value=shared.opts.detailer_sigma_adjust, elem_id=f"{tab}_detailer_renoise") + renoise_end = gr.Slider(minimum=0.0, maximum=1.0, step=0.01, label='Renoise end', value=shared.opts.detailer_sigma_adjust_max, elem_id=f"{tab}_detailer_renoise_end") + detailers.change(fn=ui_settings_change, inputs=[detailers, classes, strength, padding, blur, min_confidence, max_detected, min_size, max_size, iou, steps, renoise_value, renoise_end], outputs=[]) + classes.change(fn=ui_settings_change, inputs=[detailers, classes, strength, padding, blur, min_confidence, max_detected, min_size, max_size, iou, steps, renoise_value, renoise_end], outputs=[]) + padding.change(fn=ui_settings_change, inputs=[detailers, classes, strength, padding, blur, min_confidence, max_detected, min_size, max_size, iou, steps, renoise_value, renoise_end], outputs=[]) + blur.change(fn=ui_settings_change, inputs=[detailers, classes, strength, padding, blur, min_confidence, max_detected, min_size, max_size, iou, steps, renoise_value, renoise_end], outputs=[]) + min_confidence.change(fn=ui_settings_change, inputs=[detailers, classes, strength, padding, blur, min_confidence, max_detected, min_size, max_size, iou, steps, renoise_value, renoise_end], outputs=[]) + max_detected.change(fn=ui_settings_change, inputs=[detailers, classes, strength, padding, blur, min_confidence, max_detected, min_size, max_size, iou, steps, renoise_value, renoise_end], outputs=[]) + min_size.change(fn=ui_settings_change, inputs=[detailers, classes, strength, padding, blur, min_confidence, max_detected, min_size, max_size, iou, steps, renoise_value, renoise_end], outputs=[]) + max_size.change(fn=ui_settings_change, inputs=[detailers, classes, strength, padding, blur, min_confidence, max_detected, min_size, max_size, iou, steps, renoise_value, renoise_end], outputs=[]) + iou.change(fn=ui_settings_change, inputs=[detailers, classes, strength, padding, blur, min_confidence, max_detected, min_size, max_size, iou, steps, renoise_value, renoise_end], outputs=[]) return enabled, prompt, negative, steps, strength diff --git a/modules/processing_callbacks.py b/modules/processing_callbacks.py index 0ab91baa6..599a77534 100644 --- a/modules/processing_callbacks.py +++ b/modules/processing_callbacks.py @@ -125,6 +125,7 @@ def diffusers_callback(pipe, step: int = 0, timestep: int = 0, kwargs: dict = {} shared.state.current_sigma_next = pipe.scheduler.sigmas[pipe.scheduler.step_index] if (shared.opts.schedulers_sigma_adjust != 1.0) and (timestep > 1000 * shared.opts.schedulers_sigma_adjust_min) and (timestep < 1000 * shared.opts.schedulers_sigma_adjust_max): pipe.scheduler.sigmas[pipe.scheduler.step_index+1] = pipe.scheduler.sigmas[pipe.scheduler.step_index+1] * shared.opts.schedulers_sigma_adjust + p.extra_generation_params["Sigma adjust"] = shared.opts.schedulers_sigma_adjust except Exception: pass except Exception as e: diff --git a/modules/shared.py b/modules/shared.py index 63d01446c..1db4c2935 100644 --- a/modules/shared.py +++ b/modules/shared.py @@ -829,6 +829,8 @@ options_templates.update(options_section(('postprocessing', "Postprocessing"), { "detailer_conf": OptionInfo(0.6, "Min confidence", gr.Slider, {"minimum": 0.0, "maximum": 1.0, "step": 0.05, "visible": False}), "detailer_max": OptionInfo(2, "Max detected", gr.Slider, {"minimum": 1, "maximum": 10, "step": 1, "visible": False}), "detailer_iou": OptionInfo(0.5, "Max overlap", gr.Slider, {"minimum": 0, "maximum": 1.0, "step": 0.05, "visible": False}), + "detailer_sigma_adjust": OptionInfo(1.0, "Detailer sigma adjust", gr.Slider, {"minimum": 0, "maximum": 1.0, "step": 0.05, "visible": False}), + "detailer_sigma_adjust_max": OptionInfo(1.0, "Detailer sigma end", gr.Slider, {"minimum": 0, "maximum": 1.0, "step": 0.05, "visible": False}), "detailer_min_size": OptionInfo(0.0, "Min object size", gr.Slider, {"minimum": 0.1, "maximum": 1, "step": 0.05, "visible": False}), "detailer_max_size": OptionInfo(1.0, "Max object size", gr.Slider, {"minimum": 0.1, "maximum": 1, "step": 0.05, "visible": False}), "detailer_padding": OptionInfo(20, "Item padding", gr.Slider, {"minimum": 0, "maximum": 100, "step": 1, "visible": False}), From 64bc5cea6a8bf7830dfb9ca6ce3c6304019ed363 Mon Sep 17 00:00:00 2001 From: Seunghoon Lee Date: Fri, 4 Apr 2025 21:20:02 +0900 Subject: [PATCH 3/7] zluda triton add more gpus --- modules/zluda_hijacks.py | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/modules/zluda_hijacks.py b/modules/zluda_hijacks.py index bbbec7a81..132db85ac 100644 --- a/modules/zluda_hijacks.py +++ b/modules/zluda_hijacks.py @@ -13,8 +13,23 @@ MEM_BUS_WIDTH = { "AMD Radeon RX 7900 GRE": 256, "AMD Radeon RX 7800 XT": 256, "AMD Radeon RX 7700 XT": 192, + "AMD Radeon RX 7700": 192, + "AMD Radeon RX 7650 GRE": 128, "AMD Radeon RX 7600 XT": 128, "AMD Radeon RX 7600": 128, + "AMD Radeon RX 7500 XT": 96, + "AMD Radeon RX 6950 XT": 256, + "AMD Radeon RX 6900 XT": 256, + "AMD Radeon RX 6800 XT": 256, + "AMD Radeon RX 6800": 256, + "AMD Radeon RX 6750 XT": 192, + "AMD Radeon RX 6700 XT": 192, + "AMD Radeon RX 6700": 160, + "AMD Radeon RX 6650 XT": 128, + "AMD Radeon RX 6600 XT": 128, + "AMD Radeon RX 6600": 128, + "AMD Radeon RX 6500 XT": 64, + "AMD Radeon RX 6400": 64, } From 1f446bc8ed7262bcd943e64df6535669059162e4 Mon Sep 17 00:00:00 2001 From: Vladimir Mandic Date: Fri, 4 Apr 2025 08:21:29 -0400 Subject: [PATCH 4/7] progress use batch info Signed-off-by: Vladimir Mandic --- CHANGELOG.md | 7 ++++++ installer.py | 2 +- modules/processing.py | 4 ++-- modules/progress.py | 52 ++++++++++++++++++++++++++++++++++------- modules/shared_state.py | 4 ++++ wiki | 2 +- 6 files changed, 59 insertions(+), 12 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c4d8c91ba..e800e47ff 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,12 @@ # Change Log for SD.Next +## Update for 2025-04-04 + +- LoRA: obey configured device when performing calculations +- Video: add FasterCache and PAB support to WanDB and LTX models +- Progress: add additional fields to progress API +- Progress: use batch-count for progress + ## Update for 2025-04-03 ### Highlights for 2025-04-03 diff --git a/installer.py b/installer.py index 36c84e549..2e7ef987d 100644 --- a/installer.py +++ b/installer.py @@ -538,7 +538,7 @@ def check_diffusers(): t_start = time.time() if args.skip_all or args.skip_git or args.experimental: return - sha = 'e5c6027ef89ec1a2800c0421599da89d4820f2e4' # diffusers commit hash + sha = 'f10775b1b55cbebc58655b966b4ba3a6fc259ca3' # diffusers commit hash pkg = pkg_resources.working_set.by_key.get('diffusers', None) minor = int(pkg.version.split('.')[1] if pkg is not None else 0) cur = opts.get('diffusers_version', '') if minor > 0 else '' diff --git a/modules/processing.py b/modules/processing.py index b1a8bd548..2728903a2 100644 --- a/modules/processing.py +++ b/modules/processing.py @@ -294,14 +294,14 @@ def process_images_inner(p: StableDiffusionProcessing) -> Processed: ema_scope_context = p.sd_model.ema_scope if not shared.native else nullcontext if not shared.native: shared.state.job_count = p.n_iter + shared.state.batch_count = p.n_iter with devices.inference_context(), ema_scope_context(): t0 = time.time() if not hasattr(p, 'skip_init'): p.init(p.all_prompts, p.all_seeds, p.all_subseeds) debug(f'Processing inner: args={vars(p)}') for n in range(p.n_iter): - # if hasattr(p, 'skip_processing'): - # continue + shared.state.batch_no = n + 1 pag.apply(p) debug(f'Processing inner: iteration={n+1}/{p.n_iter}') p.iteration = n diff --git a/modules/progress.py b/modules/progress.py index 354c90032..6413e7188 100644 --- a/modules/progress.py +++ b/modules/progress.py @@ -47,44 +47,80 @@ class ProgressRequest(BaseModel): class InternalProgressResponse(BaseModel): job: str = Field(default=None, title="Job name", description="Internal job name") + textinfo: str = Field(default=None, title="Info text", description="Info text used by WebUI.") + # status fields active: bool = Field(title="Whether the task is being worked on right now") queued: bool = Field(title="Whether the task is in queue") paused: bool = Field(title="Whether the task is paused") completed: bool = Field(title="Whether the task has already finished") debug: bool = Field(title="Debug logging level") + # raw fields + step: int = Field(default=None, title="Current step", description="Current step of the task") + steps: int = Field(default=None, title="Total steps", description="Total number of steps") + batch_no: int = Field(default=None, title="Current batch", description="Current batch") + batch_count: int = Field(default=None, title="Total batches", description="Total number of batches") + # calculated fields progress: float = Field(default=None, title="Progress", description="The progress with a range of 0 to 1") eta: float = Field(default=None, title="ETA in secs") + # image fields live_preview: str = Field(default=None, title="Live preview image", description="Current live preview; a data: uri") id_live_preview: int = Field(default=None, title="Live preview image ID", description="Send this together with next request to prevent receiving same image") - textinfo: str = Field(default=None, title="Info text", description="Info text used by WebUI.") -def progressapi(req: ProgressRequest): +def api_progress(req: ProgressRequest): active = req.id_task == current_task queued = req.id_task in pending_tasks completed = req.id_task in finished_tasks paused = shared.state.paused step = max(shared.state.sampling_step, 0) steps = max(shared.state.sampling_steps, 1) - progress = round(min(1, abs(step / steps) if steps > 0 else 0), 2) + batch_no = max(shared.state.batch_no, 0) + batch_count = max(shared.state.batch_count, 0) + + current = step / steps if step > 0 and steps > 0 else 0 + batch = batch_no / batch_count if batch_no > 0 and batch_count > 0 else 1 + progress = round(min(1, current * batch), 2) + elapsed = time.time() - shared.state.time_start if shared.state.time_start is not None else 0 predicted = elapsed / progress if progress > 0 else None eta = predicted - elapsed if predicted is not None else None id_live_preview = req.id_live_preview live_preview = None + textinfo = shared.state.textinfo updated = shared.state.set_current_image() - debug_log(f'Preview: job={shared.state.job} active={active} progress={step}/{steps}/{progress} image={shared.state.current_image_sampling_step} request={id_live_preview} last={shared.state.id_live_preview} enabled={shared.opts.live_previews_enable} job={shared.state.preview_job} updated={updated} image={shared.state.current_image} elapsed={elapsed:.3f}') if not active: - return InternalProgressResponse(job=shared.state.job, active=active, queued=queued, paused=paused, completed=completed, id_live_preview=-1, debug=debug, textinfo="Queued..." if queued else "Waiting...") - if shared.opts.live_previews_enable and (shared.state.id_live_preview != id_live_preview) and (shared.state.current_image is not None): + id_live_preview = -1 + textinfo = "Queued..." if queued else "Waiting..." + + debug_log(f'Preview: job={shared.state.job} active={active} progress={step}/{steps}/{progress} image={shared.state.current_image_sampling_step} request={id_live_preview} last={shared.state.id_live_preview} enabled={shared.opts.live_previews_enable} job={shared.state.preview_job} updated={updated} image={shared.state.current_image} elapsed={elapsed:.3f}') + + if shared.opts.live_previews_enable and active and (shared.state.id_live_preview != req.id_live_preview) and (shared.state.current_image is not None): buffered = io.BytesIO() shared.state.current_image.save(buffered, format='jpeg') live_preview = f'data:image/jpeg;base64,{base64.b64encode(buffered.getvalue()).decode("ascii")}' + id_live_preview = shared.state.id_live_preview - res = InternalProgressResponse(job=shared.state.job, active=active, queued=queued, paused=paused, completed=completed, progress=progress, eta=eta, live_preview=live_preview, id_live_preview=id_live_preview, debug=debug, textinfo=shared.state.textinfo) + res = InternalProgressResponse( + job=shared.state.job, + textinfo=textinfo, + active=active, + queued=queued, + paused=paused, + completed=completed, + debug=debug, + progress=progress, + step=step, + steps=steps, + batch_no=batch_no, + batch_count=batch_count, + job_timestamp=shared.state.time_start, + eta=eta, + live_preview=live_preview, + id_live_preview=id_live_preview, + ) return res def setup_progress_api(): - shared.api.add_api_route("/internal/progress", progressapi, methods=["POST"], response_model=InternalProgressResponse) + shared.api.add_api_route("/internal/progress", api_progress, methods=["POST"], response_model=InternalProgressResponse) diff --git a/modules/shared_state.py b/modules/shared_state.py index b4c92bb65..ea4fb6433 100644 --- a/modules/shared_state.py +++ b/modules/shared_state.py @@ -19,6 +19,8 @@ class State: job = "" job_no = 0 job_count = 0 + batch_no = 0 + batch_count = 0 frame_count = 0 total_jobs = 0 job_timestamp = '0' @@ -145,6 +147,8 @@ class State: self.job = title self.job_count = 0 self.frame_count = 0 + self.batch_no = 0 + self.batch_count = 0 self.job_no = 0 self.job_timestamp = datetime.datetime.now().strftime("%Y%m%d%H%M%S") self.paused = False diff --git a/wiki b/wiki index 9408b299f..7d2b46a48 160000 --- a/wiki +++ b/wiki @@ -1 +1 @@ -Subproject commit 9408b299fffbb8efaccae968b2ac64d9216326fb +Subproject commit 7d2b46a482d20febe8179b955ca160cc6936515f From 7520be4874286ae491f2b9d674d2b88840502bd0 Mon Sep 17 00:00:00 2001 From: Vladimir Mandic Date: Fri, 4 Apr 2025 09:05:32 -0400 Subject: [PATCH 5/7] styles resize and bring quick-ui forward on hover Signed-off-by: Vladimir Mandic --- CHANGELOG.md | 3 +++ extensions-builtin/sdnext-modernui | 2 +- javascript/sdnext.css | 1 + modules/images_grid.py | 30 +++++++++++++++++++++++------- modules/processing.py | 2 +- modules/shared.py | 4 +++- modules/ui_common.py | 4 ++-- 7 files changed, 34 insertions(+), 12 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e800e47ff..c059dc4a0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,9 @@ - Video: add FasterCache and PAB support to WanDB and LTX models - Progress: add additional fields to progress API - Progress: use batch-count for progress +- Grid: add of max-rows and max-columns in settings to control grid format +- Gallery: add max-columns in settings for gradio gallery components +- Styles: resize and bring quick-ui to forward on hover ## Update for 2025-04-03 diff --git a/extensions-builtin/sdnext-modernui b/extensions-builtin/sdnext-modernui index 9bed415dc..777f6bdca 160000 --- a/extensions-builtin/sdnext-modernui +++ b/extensions-builtin/sdnext-modernui @@ -1 +1 @@ -Subproject commit 9bed415dccad1041e0573134d2f214e40bd310f1 +Subproject commit 777f6bdca27abe39cbfe050f83cbd5bd71f8f84a diff --git a/javascript/sdnext.css b/javascript/sdnext.css index 5ffc03d36..8b52b1a47 100644 --- a/javascript/sdnext.css +++ b/javascript/sdnext.css @@ -148,6 +148,7 @@ div#extras_scale_to_tab div.form { flex-direction: row; } #txt2img_prompt, #txt2img_neg_prompt, #img2img_prompt, #img2img_neg_prompt, #control_prompt, #control_neg_prompt, #video_prompt, #video_neg_prompt { background-color: var(--background-color); box-shadow: none !important; } #txt2img_prompt > label > textarea, #txt2img_neg_prompt > label > textarea, #img2img_prompt > label > textarea, #img2img_neg_prompt > label > textarea, #control_prompt > label > textarea, #control_neg_prompt > label > textarea, #video_prompt > label > textarea, #video_neg_prompt > label > textarea { font-size: 1.0em; line-height: 1.4em; } #txt2img_styles, #img2img_styles, #control_styles, #video_styles { padding: 0; margin-top: 2px; } +#txt2img_styles:hover, #img2img_styles:hover, #control_styles:hover, #video_styles:hover { z-index: 1000; } #txt2img_styles_refresh, #img2img_styles_refresh, #control_styles_refresh, #video_styles_refresh { padding: 0; margin-top: 1em; } /* settings */ diff --git a/modules/images_grid.py b/modules/images_grid.py index a7f10396e..262440f75 100644 --- a/modules/images_grid.py +++ b/modules/images_grid.py @@ -19,24 +19,40 @@ def check_grid_size(imgs): return ok -def get_grid_size(imgs, batch_size=1, rows=None): - if rows is None: +def get_grid_size(imgs, batch_size=1, rows=None, cols=None): + if rows and rows > len(imgs): + rows = len(imgs) + if cols and cols > len(imgs): + cols = len(imgs) + if rows is None and cols is None: if shared.opts.n_rows > 0: rows = shared.opts.n_rows + cols = math.ceil(len(imgs) / rows) elif shared.opts.n_rows == 0: rows = batch_size + cols = math.ceil(len(imgs) / rows) + elif shared.opts.n_cols > 0: + cols = shared.opts.n_cols + rows = math.ceil(len(imgs) / cols) + elif shared.opts.n_cols == 0: + cols = batch_size + rows = math.ceil(len(imgs) / cols) else: rows = math.floor(math.sqrt(len(imgs))) while len(imgs) % rows != 0: rows -= 1 - if rows > len(imgs): - rows = len(imgs) - cols = math.ceil(len(imgs) / rows) + cols = math.ceil(len(imgs) / rows) + elif cols is None: + cols = math.ceil(len(imgs) / rows) + elif rows is None: + rows = math.ceil(len(imgs) / cols) + else: + pass return rows, cols -def image_grid(imgs, batch_size=1, rows=None): - rows, cols = get_grid_size(imgs, batch_size, rows=rows) +def image_grid(imgs, batch_size:int=1, rows:int=None, cols:int=None): + rows, cols = get_grid_size(imgs, batch_size, rows=rows, cols=cols) params = script_callbacks.ImageGridLoopParams(imgs, cols, rows) script_callbacks.image_grid_callback(params) imgs = [i for i in imgs if i is not None] if imgs is not None else [] diff --git a/modules/processing.py b/modules/processing.py index 2728903a2..619b544dc 100644 --- a/modules/processing.py +++ b/modules/processing.py @@ -455,7 +455,7 @@ def process_images_inner(p: StableDiffusionProcessing) -> Processed: p.color_corrections = None index_of_first_image = 0 - if (shared.opts.return_grid or shared.opts.grid_save) and not p.do_not_save_grid and len(output_images) > 1: + if (shared.opts.return_grid or shared.opts.grid_save) and (not p.do_not_save_grid) and (len(output_images) > 1): if images.check_grid_size(output_images): r, c = images.get_grid_size(output_images, p.batch_size) grid = images.image_grid(output_images, p.batch_size) diff --git a/modules/shared.py b/modules/shared.py index 1db4c2935..8e84c71eb 100644 --- a/modules/shared.py +++ b/modules/shared.py @@ -677,7 +677,8 @@ options_templates.update(options_section(('saving-images', "Image Options"), { "image_sep_grid": OptionInfo("

Grid Options

", "", gr.HTML), "grid_save": OptionInfo(True, "Save all generated image grids"), "grid_format": OptionInfo('jpg', 'File format', gr.Dropdown, {"choices": ["jpg", "png", "webp", "tiff", "jp2", "jxl"]}), - "n_rows": OptionInfo(-1, "Row count", gr.Slider, {"minimum": -1, "maximum": 16, "step": 1}), + "n_rows": OptionInfo(-1, "Grid max rows count", gr.Slider, {"minimum": -1, "maximum": 16, "step": 1}), + "n_cols": OptionInfo(-1, "Grid max columns count", gr.Slider, {"minimum": -1, "maximum": 16, "step": 1}), "grid_background": OptionInfo("#000000", "Grid background color", gr.ColorPicker, {}), "font": OptionInfo("", "Font file"), "font_color": OptionInfo("#FFFFFF", "Font color", gr.ColorPicker, {}), @@ -745,6 +746,7 @@ options_templates.update(options_section(('ui', "User Interface"), { "ui_request_timeout": OptionInfo(30000, "UI request timeout", gr.Slider, {"minimum": 1000, "maximum": 120000, "step": 10}), "motd": OptionInfo(False, "Show MOTD"), "compact_view": OptionInfo(False, "Compact view"), + "ui_columns": OptionInfo(4, "Gallery view columns", gr.Slider, {"minimum": 1, "maximum": 8, "step": 1}), "return_grid": OptionInfo(True, "Show grid in results"), "return_mask": OptionInfo(False, "Inpainting include greyscale mask in results"), "return_mask_composite": OptionInfo(False, "Inpainting include masked composite in results"), diff --git a/modules/ui_common.py b/modules/ui_common.py index f5260d10c..84e0c888d 100644 --- a/modules/ui_common.py +++ b/modules/ui_common.py @@ -242,7 +242,6 @@ def create_output_panel(tabname, preview=True, prompt=None, height=None, transfe with gr.Group(elem_id=f"{tabname}_gallery_container"): if tabname == "txt2img": gr.HTML(value="", elem_id="main_info", visible=False, elem_classes=["main-info"]) - # columns are for <576px, <768px, <992px, <1200px, <1400px, >1400px result_gallery = gr.Gallery(value=[], label='Output', show_label=False, @@ -250,12 +249,13 @@ def create_output_panel(tabname, preview=True, prompt=None, height=None, transfe allow_preview=True, container=False, preview=preview, - columns=4, + columns=shared.opts.ui_columns, object_fit='scale-down', height=height, elem_id=f"{tabname}_gallery", elem_classes=["gallery_main"], ) + print('HERE', shared.opts.ui_columns) if prompt is not None: ui_sections.create_interrogate_button(tab=tabname, inputs=result_gallery, outputs=prompt) From ff4d57814e2291c80fa694dcd0508a17ea4da12e Mon Sep 17 00:00:00 2001 From: Vladimir Mandic Date: Fri, 4 Apr 2025 09:14:48 -0400 Subject: [PATCH 6/7] fix debug logging Signed-off-by: Vladimir Mandic --- modules/sd_models.py | 5 +++-- modules/sd_offload.py | 3 ++- modules/ui_common.py | 1 - 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/modules/sd_models.py b/modules/sd_models.py index 4e85ec74c..d4fd53a0b 100644 --- a/modules/sd_models.py +++ b/modules/sd_models.py @@ -9,6 +9,7 @@ import diffusers import diffusers.loaders.single_file_utils import torch +from installer import log from modules import paths, shared, shared_state, modelloader, devices, script_callbacks, sd_vae, sd_unet, errors, sd_models_config, sd_models_compile, sd_hijack_accelerate, sd_detect, model_quant from modules.timer import Timer, process as process_timer from modules.memstats import memory_stats @@ -25,9 +26,9 @@ sd_metadata_file = os.path.join(paths.data_path, "metadata.json") sd_metadata = None sd_metadata_pending = 0 sd_metadata_timer = 0 -debug_move = shared.log.trace if os.environ.get('SD_MOVE_DEBUG', None) is not None else lambda *args, **kwargs: None +debug_move = log.trace if os.environ.get('SD_MOVE_DEBUG', None) is not None else lambda *args, **kwargs: None debug_load = os.environ.get('SD_LOAD_DEBUG', None) -debug_process = shared.log.trace if os.environ.get('SD_PROCESS_DEBUG', None) is not None else lambda *args, **kwargs: None +debug_process = log.trace if os.environ.get('SD_PROCESS_DEBUG', None) is not None else lambda *args, **kwargs: None diffusers_version = int(diffusers.__version__.split('.')[1]) checkpoint_tiles = checkpoint_titles # legacy compatibility diff --git a/modules/sd_offload.py b/modules/sd_offload.py index e4b24c17b..e27b70ac2 100644 --- a/modules/sd_offload.py +++ b/modules/sd_offload.py @@ -4,11 +4,12 @@ import time import inspect import torch import accelerate.hooks +from installer import log from modules import shared, devices, errors, model_quant from modules.timer import process as process_timer -debug_move = shared.log.trace if os.environ.get('SD_MOVE_DEBUG', None) is not None else lambda *args, **kwargs: None +debug_move = log.trace if os.environ.get('SD_MOVE_DEBUG', None) is not None else lambda *args, **kwargs: None should_offload = ['sc', 'sd3', 'f1', 'hunyuandit', 'auraflow', 'omnigen', 'cogview4'] offload_hook_instance = None balanced_offload_exclude = ['OmniGenPipeline', 'CogView4Pipeline'] diff --git a/modules/ui_common.py b/modules/ui_common.py index 84e0c888d..d0696461a 100644 --- a/modules/ui_common.py +++ b/modules/ui_common.py @@ -255,7 +255,6 @@ def create_output_panel(tabname, preview=True, prompt=None, height=None, transfe elem_id=f"{tabname}_gallery", elem_classes=["gallery_main"], ) - print('HERE', shared.opts.ui_columns) if prompt is not None: ui_sections.create_interrogate_button(tab=tabname, inputs=result_gallery, outputs=prompt) From 2ff4b5c07795198aa661f659b45376b4d404bdd3 Mon Sep 17 00:00:00 2001 From: Vladimir Mandic Date: Fri, 4 Apr 2025 09:16:44 -0400 Subject: [PATCH 7/7] update changelog Signed-off-by: Vladimir Mandic --- CHANGELOG.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c059dc4a0..ab70b2350 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,13 +2,15 @@ ## Update for 2025-04-04 -- LoRA: obey configured device when performing calculations - Video: add FasterCache and PAB support to WanDB and LTX models +- ZLUDA: add more GPUs to recognized list +- LoRA: obey configured device when performing calculations - Progress: add additional fields to progress API - Progress: use batch-count for progress - Grid: add of max-rows and max-columns in settings to control grid format - Gallery: add max-columns in settings for gradio gallery components - Styles: resize and bring quick-ui to forward on hover +- Logging: fix debug logging ## Update for 2025-04-03