diff --git a/CHANGELOG.md b/CHANGELOG.md index 54077ea5f..0e612cd33 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,8 +1,8 @@ # Change Log for SD.Next -## Update for 2025-09-11 +## Update for 2025-09-12 -### Highlights for 2025-09-11 +### Highlights for 2025-09-12 *What's new*? Big one is that we're (finally) switching the default UI to **ModernUI**! StandardUI is still available and can be selected in settings, but ModernUI is now the default for new installs @@ -12,7 +12,7 @@ Also, there are quite a few offloading improvements and many quality-of-life cha [ReadMe](https://github.com/vladmandic/automatic/blob/master/README.md) | [ChangeLog](https://github.com/vladmandic/automatic/blob/master/CHANGELOG.md) | [Docs](https://vladmandic.github.io/sdnext-docs/) | [WiKi](https://github.com/vladmandic/automatic/wiki) | [Discord](https://discord.com/invite/sd-next-federal-batch-inspectors-1101998836328697867) | [Sponsor](https://github.com/sponsors/vladmandic) -### Details for 2025-09-11 +### Details for 2025-09-12 - **Models** - **Chroma** final versions: [Chroma1-HD](https://huggingface.co/lodestones/Chroma1-HD), [Chroma1-Base](https://huggingface.co/lodestones/Chroma1-Base) and [Chroma1-Flash](https://huggingface.co/lodestones/Chroma1-Flash) @@ -88,6 +88,8 @@ Also, there are quite a few offloading improvements and many quality-of-life cha - disallow `zluda` and `directml` on non-windows platforms - update openvino to `openvino==2025.3.0` - add deprecation warning for `python==3.9` + - allow setting denoise strength to 0 in control/img2img + this allows to run workflows which only refine or detail existing image without changing it - **Detailer** allow manually setting processing resolution *note*: this does not impact the actual image resolution, only the resolution at which detailer internally operates - **Fixes** diff --git a/html/previews.json b/html/previews.json index d40398bd8..6f77db164 100644 --- a/html/previews.json +++ b/html/previews.json @@ -23,5 +23,7 @@ "chroma-unlocked-v50": "models/Reference/lodestones Chroma Unlocked HD", "chroma-unlocked-v50-annealed": "models/Reference/lodestones Chroma Unlocked HD", "vladmandic--Qwen-Lightning": "models/Reference/Qwen-Lightning.jpg", - "vladmandic--Qwen-Lightning-Edit": "models/Reference/Qwen-Lightning.jpg" + "vladmandic--Qwen-Lightning-Edit": "models/Reference/Qwen-Lightning.jpg", + "Wan-AI--Wan2.2-T2V-A14B-Diffusers": "models/Reference/Wan2.2-T2V-A14B.jpg", + "Wan-AI--Wan2.1-T2V-14B-Diffusers": "models/Reference/Wan-AI--Wan2.1.jpg" } diff --git a/modules/processing.py b/modules/processing.py index 6bdf619cc..28185d6cc 100644 --- a/modules/processing.py +++ b/modules/processing.py @@ -383,6 +383,7 @@ def process_images_inner(p: StableDiffusionProcessing) -> Processed: if p.scripts is not None and isinstance(p.scripts, scripts_manager.ScriptRunner): p.scripts.process(p) + shared.state.begin('Process') shared.state.batch_count = p.n_iter with devices.inference_context(): t0 = time.time() @@ -494,4 +495,5 @@ def process_images_inner(p: StableDiffusionProcessing) -> Processed: if shared.cmd_opts.lowvram or shared.cmd_opts.medvram: devices.torch_gc(force=True, reason='final') + shared.state.end() return results diff --git a/modules/processing_args.py b/modules/processing_args.py index 76e7a21dd..91e41003b 100644 --- a/modules/processing_args.py +++ b/modules/processing_args.py @@ -23,19 +23,20 @@ def task_specific_kwargs(p, model): vae_scale_factor = sd_vae.get_vae_scale_factor(model) task_args = {} is_img2img_model = bool('Zero123' in model_cls) + task_type = sd_models.get_diffusers_task(model) if len(getattr(p, 'init_images', [])) > 0: if isinstance(p.init_images[0], str): p.init_images = [helpers.decode_base64_to_image(i, quiet=True) for i in p.init_images] if isinstance(p.init_images[0], Image.Image): p.init_images = [i.convert('RGB') if i.mode != 'RGB' else i for i in p.init_images if i is not None] - if (sd_models.get_diffusers_task(model) == sd_models.DiffusersTaskType.TEXT_2_IMAGE or len(getattr(p, 'init_images', [])) == 0) and not is_img2img_model and 'video' not in p.ops: + if (task_type == sd_models.DiffusersTaskType.TEXT_2_IMAGE or len(getattr(p, 'init_images', [])) == 0) and not is_img2img_model and 'video' not in p.ops: p.ops.append('txt2img') if hasattr(p, 'width') and hasattr(p, 'height'): task_args = { 'width': vae_scale_factor * math.ceil(p.width / vae_scale_factor), 'height': vae_scale_factor * math.ceil(p.height / vae_scale_factor), } - elif (sd_models.get_diffusers_task(model) == sd_models.DiffusersTaskType.IMAGE_2_IMAGE or is_img2img_model) and len(getattr(p, 'init_images', [])) > 0: + elif (task_type == sd_models.DiffusersTaskType.IMAGE_2_IMAGE or is_img2img_model) and len(getattr(p, 'init_images', [])) > 0: if shared.sd_model_type == 'sdxl' and hasattr(model, 'register_to_config'): if model_cls in sd_models.i2i_pipes: pass @@ -69,7 +70,7 @@ def task_specific_kwargs(p, model): 'height': p.height, 'input_images': [p.init_images], # omnigen expects list-of-lists } - elif sd_models.get_diffusers_task(model) == sd_models.DiffusersTaskType.INSTRUCT and len(getattr(p, 'init_images', [])) > 0: + elif task_type == sd_models.DiffusersTaskType.INSTRUCT and len(getattr(p, 'init_images', [])) > 0: p.ops.append('instruct') task_args = { 'width': vae_scale_factor * math.ceil(p.width / vae_scale_factor) if hasattr(p, 'width') else None, @@ -77,7 +78,7 @@ def task_specific_kwargs(p, model): 'image': p.init_images, 'strength': p.denoising_strength, } - elif (sd_models.get_diffusers_task(model) == sd_models.DiffusersTaskType.INPAINTING or is_img2img_model) and len(getattr(p, 'init_images', [])) > 0: + elif (task_type == sd_models.DiffusersTaskType.INPAINTING or is_img2img_model) and len(getattr(p, 'init_images', [])) > 0: if shared.sd_model_type == 'sdxl' and hasattr(model, 'register_to_config'): if model_cls in [sd_models.i2i_pipes]: pass diff --git a/modules/processing_diffusers.py b/modules/processing_diffusers.py index 6bd9041dd..642755e1a 100644 --- a/modules/processing_diffusers.py +++ b/modules/processing_diffusers.py @@ -118,6 +118,7 @@ def process_post(p: processing.StableDiffusionProcessing): def process_base(p: processing.StableDiffusionProcessing): + shared.state.begin('Base') txt2img = is_txt2img() use_refiner_start = is_refiner_enabled(p) and (not p.is_hr_pass) use_denoise_start = not txt2img and p.refiner_start > 0 and p.refiner_start < 1 @@ -207,6 +208,7 @@ def process_base(p: processing.StableDiffusionProcessing): process_post(p) shared.state.nextjob() + shared.state.end() return output @@ -518,8 +520,8 @@ def process_diffusers(p: processing.StableDiffusionProcessing): p.init_images.append(p.init_images[-1]) # pipeline type is set earlier in processing, but check for sanity is_control = getattr(p, 'is_control', False) is True - has_images = len(getattr(p, 'init_images' ,[])) > 0 - if sd_models.get_diffusers_task(shared.sd_model) != sd_models.DiffusersTaskType.TEXT_2_IMAGE and not has_images and not is_control: + has_images = len(getattr(p, 'init_images', [])) > 0 + if (sd_models.get_diffusers_task(shared.sd_model) != sd_models.DiffusersTaskType.TEXT_2_IMAGE) and (not has_images) and (not is_control): shared.sd_model = sd_models.set_diffuser_pipe(shared.sd_model, sd_models.DiffusersTaskType.TEXT_2_IMAGE) # reset pipeline if hasattr(shared.sd_model, 'unet') and hasattr(shared.sd_model.unet, 'config') and hasattr(shared.sd_model.unet.config, 'in_channels') and shared.sd_model.unet.config.in_channels == 9 and not is_control: shared.sd_model = sd_models.set_diffuser_pipe(shared.sd_model, sd_models.DiffusersTaskType.INPAINTING) # force pipeline @@ -541,6 +543,10 @@ def process_diffusers(p: processing.StableDiffusionProcessing): images, _index=shared.history.selected output = SimpleNamespace(images=images) + if len(output.images) == 0 and has_images: + shared.log.debug('Processing: using input as base output') + output.images = p.init_images + if shared.state.interrupted or shared.state.skipped: shared.sd_model = orig_pipeline return results diff --git a/modules/ui_control.py b/modules/ui_control.py index 47bc42ae5..04c5470a6 100644 --- a/modules/ui_control.py +++ b/modules/ui_control.py @@ -97,7 +97,6 @@ def generate_click(job_id: str, state: str, active_tab: str, *args): time.sleep(0.01) from modules.control.run import control_run debug(f'Control: tab="{active_tab}" job={job_id} args={args}') - shared.state.begin('Generate') progress.add_task_to_queue(job_id) with call_queue.queue_lock: yield [None, None, None, None, 'Control: starting', ''] @@ -140,7 +139,7 @@ def create_ui(_blocks: gr.Blocks=None): with gr.Row(): input_type = gr.Radio(label="Control input type", choices=['Control only', 'Init image same as control', 'Separate init image'], value='Control only', type='index', elem_id='control_input_type') with gr.Row(): - denoising_strength = gr.Slider(minimum=0.01, maximum=1.0, step=0.01, label='Denoising strength', value=0.30, elem_id="control_input_denoising_strength") + denoising_strength = gr.Slider(minimum=0.00, maximum=0.99, step=0.01, label='Denoising strength', value=0.30, elem_id="control_input_denoising_strength") with gr.Accordion(open=False, label="Size", elem_id="control_size", elem_classes=["small-accordion"]): with gr.Tabs(): diff --git a/modules/ui_img2img.py b/modules/ui_img2img.py index c326d24fb..c1597321d 100644 --- a/modules/ui_img2img.py +++ b/modules/ui_img2img.py @@ -129,7 +129,7 @@ def create_ui(): with gr.Accordion(open=False, label="Denoise", elem_classes=["small-accordion"], elem_id="img2img_denoise_group"): with gr.Row(): - denoising_strength = gr.Slider(minimum=0.0, maximum=0.99, step=0.01, label='Denoising strength', value=0.30, elem_id="img2img_denoising_strength") + denoising_strength = gr.Slider(minimum=0.00, maximum=0.99, step=0.01, label='Denoising strength', value=0.30, 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") vae_type, tiling, hidiffusion, cfg_scale, clip_skip, image_cfg_scale, diffusers_guidance_rescale, pag_scale, pag_adaptive, cfg_end = ui_sections.create_advanced_inputs('img2img') diff --git a/pipelines/model_wanai.py b/pipelines/model_wanai.py index fcc7f3de5..f58c69a8e 100644 --- a/pipelines/model_wanai.py +++ b/pipelines/model_wanai.py @@ -81,7 +81,7 @@ def load_wan(checkpoint_info, diffusers_load_config={}): load_args, _quant_args = model_quant.get_dit_args(diffusers_load_config, module='Model') boundary_ratio = shared.opts.model_wan_boundary if transformer_2 is not None else None - shared.log.debug(f'Load model: type=WanAI model="{checkpoint_info.name}" repo="{repo_id}" offload={shared.opts.diffusers_offload_mode} dtype={devices.dtype} args={load_args} stage={shared.opts.model_wan_stage} boundary={boundary_ratio}') + shared.log.debug(f'Load model: type=WanAI model="{checkpoint_info.name}" repo="{repo_id}" offload={shared.opts.diffusers_offload_mode} dtype={devices.dtype} args={load_args} stage="{shared.opts.model_wan_stage}" boundary={boundary_ratio}') cls = diffusers.WanPipeline pipe = cls.from_pretrained( @@ -103,7 +103,7 @@ def load_wan(checkpoint_info, diffusers_load_config={}): del transformer_2 diffusers.pipelines.auto_pipeline.AUTO_TEXT2IMAGE_PIPELINES_MAPPING["wanai"] = diffusers.WanPipeline - diffusers.pipelines.auto_pipeline.AUTO_IMAGE2IMAGE_PIPELINES_MAPPING["wanai"] = diffusers.WanImageToVideoPipeline + # diffusers.pipelines.auto_pipeline.AUTO_IMAGE2IMAGE_PIPELINES_MAPPING["wanai"] = diffusers.WanImageToVideoPipeline sd_hijack_te.init_hijack(pipe) sd_hijack_vae.init_hijack(pipe)