Sign In

How to get "DPM++ 2M Karras Sharp v1"

Jul 9, 2023
tool guide
How to get "DPM++ 2M Karras Sharp v1"

I'm frequently asked about my default sampler, "DPM++ 2M Karras Sharp v1", so I figured I'd just write an article about so I could link to it.

Some background:

A while back, it was discovered that there was a bug in the DPM++ 2M Karras algorithm that was causing it not to resolve pictures as clearly as they could be, so someone created a fix and posted it on the automatic1111 github, here:

https://github.com/AUTOMATIC1111/stable-diffusion-webui/discussions/8457

People complained that the fix washed out the image colors slightly, so the sampler was revised to address that issue, and in the revision, it lost some of that sharpness. The original, sharp revision is now hidden away in the March 9th edit of the top comment (which you can find by clicking on the tiny "edited" dropdown in the top right corner of the comment). Because I prefer the extra sharpness and detail even with the slight loss of color (which can easily be restored in any modern photo processing program, whereas additional detail cannot be), I dug out the original version and implemented it myself.

Here are some images to compare. I recommend opening them in new tabs and then switching between them so you can see the changes directly. (Unfortunately, the low resolution that articles save images with doesn't really do it justice. It's pretty striking at high resolution, so I recommend comparing it yourself.)

Anyway, I'm running vladmandic's fork, so YMMV on this, but it will probably work on automatic1111's original fork as well. First, to the end of repositories\k-diffusion\k_diffusion\sampling.py, add this function:

@torch.no_grad()
def sample_dpmpp_2m_v1(model, x, sigmas, extra_args=None, callback=None, disable=None):
    """DPM-Solver++(2M)."""
    extra_args = {} if extra_args is None else extra_args
    s_in = x.new_ones([x.shape[0]])
    sigma_fn = lambda t: t.neg().exp()
    t_fn = lambda sigma: sigma.log().neg()
    old_denoised = None

    for i in trange(len(sigmas) - 1, disable=disable):
        denoised = model(x, sigmas[i] * s_in, **extra_args)
        if callback is not None:
            callback({'x': x, 'i': i, 'sigma': sigmas[i], 'sigma_hat': sigmas[i], 'denoised': denoised})
        t, t_next = t_fn(sigmas[i]), t_fn(sigmas[i + 1])
        h = t_next - t
        if old_denoised is None or sigmas[i + 1] == 0:
            x = (sigma_fn(t_next) / sigma_fn(t)) * x - (-h).expm1() * denoised
        else:
            h_last = t - t_fn(sigmas[i - 1])
            r = h_last / h
            denoised_d = (1 + 1 / (2 * r)) * denoised - (1 / (2 * r)) * old_denoised
            x = (sigma_fn(t_next) / sigma_fn(t)) * x - (-h).expm1() * denoised_d
        sigma_progress = i / len(sigmas)
        adjustment_factor = 1 + (0.15 * (sigma_progress * sigma_progress))
        old_denoised = denoised * adjustment_factor
    return x

Then, in modules/sd_samplers_kdiffusion.py, add the following line to the end of the samplers_k_diffusion variable definition near the top of the file:

    ('DPM++ 2M Karras Sharp v1', 'sample_dpmpp_2m_v1', ['k_dpmpp_2m_ka_v1'], {'scheduler': 'karras'}),

After that, restart your web interface and reload the page, and go to the settings page and type "Karras" into the search bar. You should see a list of checkboxes that enable samplers in the select list. Click the DPM++ 2M Karras Sharp v1 checkbox, then save your settings. Restart the program again and reload the page, and the sampler should be selectable on in the samplers pull-down along with the others.

Note: I'm writing these steps basically from memory, so let me know if something doesn't work and I'll update this article.

53

Comments