From eecfcc461e75b45d3cb6f6998f42c622d2cd04ec Mon Sep 17 00:00:00 2001 From: Haoming Date: Mon, 8 Dec 2025 22:48:03 +0800 Subject: [PATCH] Seed Variance Enhancer https://github.com/ChangeTheConstants/SeedVarianceEnhancer --- .../seed-variance-enhancer/scripts/sve.py | 77 +++++++++++++++++++ modules/sd_models.py | 10 --- modules/sd_samplers_cfg_denoiser.py | 8 +- modules/shared_options.py | 19 ----- 4 files changed, 78 insertions(+), 36 deletions(-) create mode 100644 extensions-builtin/seed-variance-enhancer/scripts/sve.py diff --git a/extensions-builtin/seed-variance-enhancer/scripts/sve.py b/extensions-builtin/seed-variance-enhancer/scripts/sve.py new file mode 100644 index 00000000..36e28245 --- /dev/null +++ b/extensions-builtin/seed-variance-enhancer/scripts/sve.py @@ -0,0 +1,77 @@ +# https://github.com/ChangeTheConstants/SeedVarianceEnhancer + +import gradio as gr +import torch + +from modules import scripts +from modules.infotext_utils import PasteField +from modules.processing import StableDiffusionProcessingTxt2Img +from modules.script_callbacks import CFGDenoiserParams, on_cfg_denoiser +from modules.ui_components import InputAccordion + + +class SeedVarianceEnhancer(scripts.Script): + enable: bool + steps: int + percentage: float + strength: float + seed: int + + def title(self): + return "SeedVarianceEnhancer Integrated" + + def show(self, is_img2img): + return None if is_img2img else scripts.AlwaysVisible + + def ui(self, is_img2img): + with InputAccordion(value=False, label=self.title()) as enable: + with gr.Row(): + steps = gr.Slider(value=3, minimum=0, maximum=8, step=1, label="Steps", info="the number of steps to inject random noise") + percentage = gr.Slider(value=0.6, minimum=0.0, maximum=1.0, step=0.05, label="Percentage", info="the percentage of conditioning to inject random noise") + strength = gr.Slider(value=32, minimum=0, maximum=64, step=1, label="Strength", info="the strength of the random noise") + + self.infotext_fields = [ + PasteField(steps, "SVE Steps"), + PasteField(percentage, "SVE Percentage"), + PasteField(strength, "SVE Strength"), + ] + + return [enable, steps, percentage, strength] + + def before_process_batch(self, p: StableDiffusionProcessingTxt2Img, enable: bool, steps: int, percentage: float, strength: int, **kwargs): + SeedVarianceEnhancer.enable = enable and isinstance(p, StableDiffusionProcessingTxt2Img) + if not SeedVarianceEnhancer.enable: + return + + SeedVarianceEnhancer.steps = steps + SeedVarianceEnhancer.percentage = percentage + SeedVarianceEnhancer.strength = strength + SeedVarianceEnhancer.seed = kwargs["seeds"][0] + + p.extra_generation_params.update( + { + "SVE Steps": steps, + "SVE Percentage": percentage, + "SVE Strength": strength, + } + ) + + @classmethod + @torch.inference_mode() + def on_cfg(cls, params: CFGDenoiserParams): + if not cls.enable: + return + if cls.steps < params.sampling_step: + return + + cond: torch.Tensor = params.text_cond + torch.manual_seed(cls.seed) + + noise = torch.rand_like(cond) * 2.0 * cls.strength - cls.strength + noise_mask = torch.bernoulli(torch.ones_like(cond) * cls.percentage).bool() + + modified_noise = noise * noise_mask + params.text_cond = cond + modified_noise + + +on_cfg_denoiser(SeedVarianceEnhancer.on_cfg) diff --git a/modules/sd_models.py b/modules/sd_models.py index 3665bf47..ef6bdca5 100644 --- a/modules/sd_models.py +++ b/modules/sd_models.py @@ -379,16 +379,6 @@ def forge_model_reload(): script_callbacks.model_loaded_callback(sd_model) timer.record("scripts callbacks") - if opts.early_empty_prompt > 0: - _cond = sd_model.get_learned_conditioning(SdConditioning([opts.empty_prompt_template])) - if isinstance(_cond, dict): - _cond = DictWithShape(_cond) - elif isinstance(_cond, list): - _cond = torch.stack(_cond) - - sd_model.empty_cond = _cond.to(devices.cpu) - timer.record("calculate empty prompt") - print(f"Model loaded in {timer.summary()}.") model_data.forge_hash = current_hash diff --git a/modules/sd_samplers_cfg_denoiser.py b/modules/sd_samplers_cfg_denoiser.py index 7ee8f427..cde31d4b 100644 --- a/modules/sd_samplers_cfg_denoiser.py +++ b/modules/sd_samplers_cfg_denoiser.py @@ -1,8 +1,7 @@ import torch -import modules.shared as shared from backend.sampling.sampling_function import sampling_function -from modules import processing, prompt_parser, sd_samplers_common +from modules import prompt_parser, sd_samplers_common from modules.script_callbacks import AfterCFGCallbackParams, CFGDenoiserParams, cfg_after_cfg_callback, cfg_denoiser_callback from modules.shared import opts, state @@ -127,11 +126,6 @@ class CFGDenoiser(torch.nn.Module): noisy_initial_latent = predictor.noise_scaling(sigma[:, None, None, None], torch.randn_like(self.init_latent).to(self.init_latent), self.init_latent, max_denoise=False) x = x * self.nmask + noisy_initial_latent * self.mask - if 0 < self.step <= opts.early_empty_prompt: - if isinstance(self.p, processing.StableDiffusionProcessingTxt2Img): - cond = shared.sd_model.empty_cond.to(original_x_device) - self.p.extra_generation_params["Empty Early CFG"] = opts.early_empty_prompt - denoiser_params = CFGDenoiserParams(x, image_cond, sigma, state.sampling_step, state.sampling_steps, cond, uncond, self) cfg_denoiser_callback(denoiser_params) diff --git a/modules/shared_options.py b/modules/shared_options.py index 4c52ad5e..830a8502 100644 --- a/modules/shared_options.py +++ b/modules/shared_options.py @@ -242,25 +242,6 @@ options_templates.update( gr.Textbox, {"lines": 3, "max_lines": 6, "placeholder": ""}, ), - "divdistill": OptionDiv(), - "early_empty_prompt": OptionInfo( - 0, - "Steps to use Empty Prompt at the beginning", - gr.Slider, - {"minimum": 0, "maximum": 8, "step": 1}, - infotext="Empty Early CFG", - ) - .info("improve variance for distilled models") - .info("does not affect img2img") - .needs_restart(), - "empty_prompt_template": OptionInfo( - "", - "Prompt to Encode as the Empty Prompt", - gr.Textbox, - {"lines": 1, "max_lines": 3, "placeholder": "high quality"}, - ) - .info("default is empty") - .needs_restart(), }, ) )