From c3bfe3e8cd03b4b3da15ebf9ed427e07ab37f740 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20du=20Hamel?= Date: Sat, 4 Jul 2026 02:01:44 +0200 Subject: [PATCH 1/2] feat: denosie strength as starting noise level --- src/runtime/denoiser.hpp | 14 ++++++++++++ src/stable-diffusion.cpp | 49 +++++++++++++++++++++++++++++++++++++--- 2 files changed, 60 insertions(+), 3 deletions(-) diff --git a/src/runtime/denoiser.hpp b/src/runtime/denoiser.hpp index 812eebe61..f6645fd46 100644 --- a/src/runtime/denoiser.hpp +++ b/src/runtime/denoiser.hpp @@ -1013,6 +1013,7 @@ struct Denoiser { const sd::Tensor& latent) = 0; virtual sd::Tensor inverse_noise_scaling(float sigma, const sd::Tensor& latent) = 0; + virtual float noise_level_to_sigma(float noise_level) = 0; virtual std::vector get_sigmas(uint32_t n, int image_seq_len, scheduler_t scheduler_type, SDVersion version, const char* extra_sample_args = nullptr) { auto bound_t_to_sigma = std::bind(&Denoiser::t_to_sigma, this, std::placeholders::_1); @@ -1160,6 +1161,10 @@ struct CompVisDenoiser : public Denoiser { SD_UNUSED(sigma); return latent; } + + float noise_level_to_sigma(float noise_level){ + return noise_level / (1.0f - noise_level); + } }; struct CompVisVDenoiser : public CompVisDenoiser { @@ -1247,6 +1252,10 @@ struct DiscreteFlowDenoiser : public Denoiser { sd::Tensor inverse_noise_scaling(float sigma, const sd::Tensor& latent) override { return latent * (1.0f / (1.0f - sigma)); } + + float noise_level_to_sigma(float noise_level){ + return noise_level; + } }; struct FluxFlowDenoiser : public DiscreteFlowDenoiser { @@ -1384,6 +1393,11 @@ struct MiniT2IFlowDenoiser : public Denoiser { return latent; } + float noise_level_to_sigma(float noise_level){ + SD_UNUSED(noise_level); + return 1.0f; + } + std::vector get_sigmas(uint32_t n, int image_seq_len, scheduler_t scheduler_type, SDVersion version, const char* extra_sample_args = nullptr) override { SD_UNUSED(image_seq_len); SD_UNUSED(scheduler_type); diff --git a/src/stable-diffusion.cpp b/src/stable-diffusion.cpp index eb592f414..77cd856d5 100644 --- a/src/stable-diffusion.cpp +++ b/src/stable-diffusion.cpp @@ -4050,13 +4050,56 @@ static std::optional prepare_image_generation_latents(sd LOG_INFO("IMG2IMG"); if (request->strength < 1.f) { - size_t t_enc = static_cast(plan->sample_steps * request->strength); - if (t_enc == static_cast(plan->sample_steps)) { - t_enc--; + bool strength_as_noise_level = false; + bool force_first_sigma = false; + for (const auto& [key, value] : parse_key_value_args(sd_img_gen_params->sample_params.extra_sample_args, "img2img arg")) { + if (key == "strength_as_noise_level") { + if (!parse_strict_bool(value, strength_as_noise_level)) { + LOG_WARN("ignoring invalid img2img sample arg '%s=%s'", key.c_str(), value.c_str()); + } + } else if (key == "force_first_sigma") { + if (!parse_strict_bool(value, force_first_sigma)) { + LOG_WARN("ignoring invalid img2img sample arg '%s=%s'", key.c_str(), value.c_str()); + } + } + } + + size_t t_enc; + float target_sigma = -1; + if (!strength_as_noise_level) { + t_enc = static_cast(plan->sample_steps * request->strength); + if (t_enc == static_cast(plan->sample_steps)) { + t_enc--; + } + } else { + LOG_DEBUG("Interpreting denoise strength as relative noise level"); + // assume x_noised = K * (x * (1-noise_level) + noise * noise_level) = K * lerp(x, noise, noise_level) + // K = 1, noise_level = sigma for flow models + // K = 1+sigma, noise_level=sigma/(1+sigma) for diffusion models + float target_noise_level = request->strength; + target_sigma = sd_ctx->sd->denoiser->noise_level_to_sigma(target_noise_level); + size_t start_index = 0; + for (size_t i = 0; i < plan->sigmas.size(); ++i) { + if (plan->sigmas[i] <= target_sigma) { + start_index = i; + break; + } + } + + if (start_index >= plan->sigmas.size() - 1) { + start_index = plan->sigmas.size() - 2; // Leave at least 1 step + } + t_enc = plan->sample_steps - start_index - 1; } LOG_INFO("target t_enc is %zu steps", t_enc); std::vector sigma_sched; sigma_sched.assign(plan->sigmas.begin() + plan->sample_steps - t_enc - 1, plan->sigmas.end()); + + if (target_sigma > 0 && force_first_sigma && strength_as_noise_level) { + LOG_DEBUG("force_first_sigma to %.4f (from %.4f)", target_sigma, sigma_sched[0]); + sigma_sched[0] = target_sigma; + } + plan->sigmas = std::move(sigma_sched); plan->sample_steps = static_cast(plan->sigmas.size() - 1); } From bcba2b019aa4d308ee4e1f9974d632ebf53bde8b Mon Sep 17 00:00:00 2001 From: leejet Date: Sun, 5 Jul 2026 16:23:19 +0800 Subject: [PATCH 2/2] format code --- src/runtime/denoiser.hpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/runtime/denoiser.hpp b/src/runtime/denoiser.hpp index f6645fd46..a45685f98 100644 --- a/src/runtime/denoiser.hpp +++ b/src/runtime/denoiser.hpp @@ -1162,7 +1162,7 @@ struct CompVisDenoiser : public Denoiser { return latent; } - float noise_level_to_sigma(float noise_level){ + float noise_level_to_sigma(float noise_level) { return noise_level / (1.0f - noise_level); } }; @@ -1252,8 +1252,8 @@ struct DiscreteFlowDenoiser : public Denoiser { sd::Tensor inverse_noise_scaling(float sigma, const sd::Tensor& latent) override { return latent * (1.0f / (1.0f - sigma)); } - - float noise_level_to_sigma(float noise_level){ + + float noise_level_to_sigma(float noise_level) { return noise_level; } }; @@ -1393,7 +1393,7 @@ struct MiniT2IFlowDenoiser : public Denoiser { return latent; } - float noise_level_to_sigma(float noise_level){ + float noise_level_to_sigma(float noise_level) { SD_UNUSED(noise_level); return 1.0f; }