nodes_custom_sampler.py 9.38 KB
Newer Older
comfyanonymous's avatar
comfyanonymous committed
1
2
3
4
import comfy.samplers
import comfy.sample
from comfy.k_diffusion import sampling as k_diffusion_sampling
import latent_preview
5
import torch
6
import comfy.utils
comfyanonymous's avatar
comfyanonymous committed
7

8
9
10
11
12
13
14
15
16
17
18

class BasicScheduler:
    @classmethod
    def INPUT_TYPES(s):
        return {"required":
                    {"model": ("MODEL",),
                     "scheduler": (comfy.samplers.SCHEDULER_NAMES, ),
                     "steps": ("INT", {"default": 20, "min": 1, "max": 10000}),
                      }
               }
    RETURN_TYPES = ("SIGMAS",)
19
    CATEGORY = "sampling/custom_sampling"
20
21
22
23
24
25
26
27

    FUNCTION = "get_sigmas"

    def get_sigmas(self, model, scheduler, steps):
        sigmas = comfy.samplers.calculate_sigmas_scheduler(model.model, scheduler, steps).cpu()
        return (sigmas, )


comfyanonymous's avatar
comfyanonymous committed
28
29
30
31
32
33
34
35
36
37
38
class KarrasScheduler:
    @classmethod
    def INPUT_TYPES(s):
        return {"required":
                    {"steps": ("INT", {"default": 20, "min": 1, "max": 10000}),
                     "sigma_max": ("FLOAT", {"default": 14.614642, "min": 0.0, "max": 1000.0, "step":0.01, "round": False}),
                     "sigma_min": ("FLOAT", {"default": 0.0291675, "min": 0.0, "max": 1000.0, "step":0.01, "round": False}),
                     "rho": ("FLOAT", {"default": 7.0, "min": 0.0, "max": 100.0, "step":0.01, "round": False}),
                    }
               }
    RETURN_TYPES = ("SIGMAS",)
39
    CATEGORY = "sampling/custom_sampling"
comfyanonymous's avatar
comfyanonymous committed
40
41
42
43
44
45
46

    FUNCTION = "get_sigmas"

    def get_sigmas(self, steps, sigma_max, sigma_min, rho):
        sigmas = k_diffusion_sampling.get_sigmas_karras(n=steps, sigma_min=sigma_min, sigma_max=sigma_max, rho=rho)
        return (sigmas, )

47
48
49
50
51
52
53
54
55
56
class ExponentialScheduler:
    @classmethod
    def INPUT_TYPES(s):
        return {"required":
                    {"steps": ("INT", {"default": 20, "min": 1, "max": 10000}),
                     "sigma_max": ("FLOAT", {"default": 14.614642, "min": 0.0, "max": 1000.0, "step":0.01, "round": False}),
                     "sigma_min": ("FLOAT", {"default": 0.0291675, "min": 0.0, "max": 1000.0, "step":0.01, "round": False}),
                    }
               }
    RETURN_TYPES = ("SIGMAS",)
57
    CATEGORY = "sampling/custom_sampling"
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75

    FUNCTION = "get_sigmas"

    def get_sigmas(self, steps, sigma_max, sigma_min):
        sigmas = k_diffusion_sampling.get_sigmas_exponential(n=steps, sigma_min=sigma_min, sigma_max=sigma_max)
        return (sigmas, )

class PolyexponentialScheduler:
    @classmethod
    def INPUT_TYPES(s):
        return {"required":
                    {"steps": ("INT", {"default": 20, "min": 1, "max": 10000}),
                     "sigma_max": ("FLOAT", {"default": 14.614642, "min": 0.0, "max": 1000.0, "step":0.01, "round": False}),
                     "sigma_min": ("FLOAT", {"default": 0.0291675, "min": 0.0, "max": 1000.0, "step":0.01, "round": False}),
                     "rho": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 100.0, "step":0.01, "round": False}),
                    }
               }
    RETURN_TYPES = ("SIGMAS",)
76
    CATEGORY = "sampling/custom_sampling"
77
78
79
80
81
82
83

    FUNCTION = "get_sigmas"

    def get_sigmas(self, steps, sigma_max, sigma_min, rho):
        sigmas = k_diffusion_sampling.get_sigmas_polyexponential(n=steps, sigma_min=sigma_min, sigma_max=sigma_max, rho=rho)
        return (sigmas, )

comfyanonymous's avatar
comfyanonymous committed
84
85
86
87
88
89
90
91
92
93
94
class VPScheduler:
    @classmethod
    def INPUT_TYPES(s):
        return {"required":
                    {"steps": ("INT", {"default": 20, "min": 1, "max": 10000}),
                     "beta_d": ("FLOAT", {"default": 19.9, "min": 0.0, "max": 1000.0, "step":0.01, "round": False}), #TODO: fix default values
                     "beta_min": ("FLOAT", {"default": 0.1, "min": 0.0, "max": 1000.0, "step":0.01, "round": False}),
                     "eps_s": ("FLOAT", {"default": 0.001, "min": 0.0, "max": 1.0, "step":0.0001, "round": False}),
                    }
               }
    RETURN_TYPES = ("SIGMAS",)
95
    CATEGORY = "sampling/custom_sampling"
comfyanonymous's avatar
comfyanonymous committed
96
97
98
99
100
101
102

    FUNCTION = "get_sigmas"

    def get_sigmas(self, steps, beta_d, beta_min, eps_s):
        sigmas = k_diffusion_sampling.get_sigmas_vp(n=steps, beta_d=beta_d, beta_min=beta_min, eps_s=eps_s)
        return (sigmas, )

comfyanonymous's avatar
comfyanonymous committed
103
104
105
106
107
108
109
110
111
class SplitSigmas:
    @classmethod
    def INPUT_TYPES(s):
        return {"required":
                    {"sigmas": ("SIGMAS", ),
                    "step": ("INT", {"default": 0, "min": 0, "max": 10000}),
                     }
                }
    RETURN_TYPES = ("SIGMAS","SIGMAS")
112
    CATEGORY = "sampling/custom_sampling"
comfyanonymous's avatar
comfyanonymous committed
113
114
115
116
117

    FUNCTION = "get_sigmas"

    def get_sigmas(self, sigmas, step):
        sigmas1 = sigmas[:step + 1]
comfyanonymous's avatar
comfyanonymous committed
118
        sigmas2 = sigmas[step:]
comfyanonymous's avatar
comfyanonymous committed
119
        return (sigmas1, sigmas2)
comfyanonymous's avatar
comfyanonymous committed
120
121
122
123
124

class KSamplerSelect:
    @classmethod
    def INPUT_TYPES(s):
        return {"required":
125
                    {"sampler_name": (comfy.samplers.SAMPLER_NAMES, ),
comfyanonymous's avatar
comfyanonymous committed
126
127
128
                      }
               }
    RETURN_TYPES = ("SAMPLER",)
129
    CATEGORY = "sampling/custom_sampling"
comfyanonymous's avatar
comfyanonymous committed
130
131
132
133

    FUNCTION = "get_sampler"

    def get_sampler(self, sampler_name):
134
        sampler = comfy.samplers.sampler_class(sampler_name)()
comfyanonymous's avatar
comfyanonymous committed
135
136
        return (sampler, )

comfyanonymous's avatar
comfyanonymous committed
137
138
139
140
141
142
143
144
145
146
147
class SamplerDPMPP_2M_SDE:
    @classmethod
    def INPUT_TYPES(s):
        return {"required":
                    {"solver_type": (['midpoint', 'heun'], ),
                     "eta": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 100.0, "step":0.01, "round": False}),
                     "s_noise": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 100.0, "step":0.01, "round": False}),
                     "noise_device": (['gpu', 'cpu'], ),
                      }
               }
    RETURN_TYPES = ("SAMPLER",)
148
    CATEGORY = "sampling/custom_sampling"
comfyanonymous's avatar
comfyanonymous committed
149
150
151
152
153
154
155
156
157
158
159
160

    FUNCTION = "get_sampler"

    def get_sampler(self, solver_type, eta, s_noise, noise_device):
        if noise_device == 'cpu':
            sampler_name = "dpmpp_2m_sde"
        else:
            sampler_name = "dpmpp_2m_sde_gpu"
        sampler = comfy.samplers.ksampler(sampler_name, {"eta": eta, "s_noise": s_noise, "solver_type": solver_type})()
        return (sampler, )


comfyanonymous's avatar
comfyanonymous committed
161
162
163
164
165
166
167
168
169
170
171
class SamplerDPMPP_SDE:
    @classmethod
    def INPUT_TYPES(s):
        return {"required":
                    {"eta": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 100.0, "step":0.01, "round": False}),
                     "s_noise": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 100.0, "step":0.01, "round": False}),
                     "r": ("FLOAT", {"default": 0.5, "min": 0.0, "max": 100.0, "step":0.01, "round": False}),
                     "noise_device": (['gpu', 'cpu'], ),
                      }
               }
    RETURN_TYPES = ("SAMPLER",)
172
    CATEGORY = "sampling/custom_sampling"
comfyanonymous's avatar
comfyanonymous committed
173
174
175
176
177
178
179
180
181
182
183

    FUNCTION = "get_sampler"

    def get_sampler(self, eta, s_noise, r, noise_device):
        if noise_device == 'cpu':
            sampler_name = "dpmpp_sde"
        else:
            sampler_name = "dpmpp_sde_gpu"
        sampler = comfy.samplers.ksampler(sampler_name, {"eta": eta, "s_noise": s_noise, "r": r})()
        return (sampler, )

comfyanonymous's avatar
comfyanonymous committed
184
185
186
187
188
class SamplerCustom:
    @classmethod
    def INPUT_TYPES(s):
        return {"required":
                    {"model": ("MODEL",),
189
                    "add_noise": ("BOOLEAN", {"default": True}),
comfyanonymous's avatar
comfyanonymous committed
190
                    "noise_seed": ("INT", {"default": 0, "min": 0, "max": 0xffffffffffffffff}),
191
                    "cfg": ("FLOAT", {"default": 8.0, "min": 0.0, "max": 100.0, "step":0.1, "round": 0.01}),
comfyanonymous's avatar
comfyanonymous committed
192
193
194
195
196
197
198
199
200
201
202
203
204
                    "positive": ("CONDITIONING", ),
                    "negative": ("CONDITIONING", ),
                    "sampler": ("SAMPLER", ),
                    "sigmas": ("SIGMAS", ),
                    "latent_image": ("LATENT", ),
                     }
                }

    RETURN_TYPES = ("LATENT","LATENT")
    RETURN_NAMES = ("output", "denoised_output")

    FUNCTION = "sample"

205
    CATEGORY = "sampling/custom_sampling"
comfyanonymous's avatar
comfyanonymous committed
206
207
208
209

    def sample(self, model, add_noise, noise_seed, cfg, positive, negative, sampler, sigmas, latent_image):
        latent = latent_image
        latent_image = latent["samples"]
210
        if not add_noise:
comfyanonymous's avatar
comfyanonymous committed
211
212
213
214
215
216
217
218
219
220
221
222
            noise = torch.zeros(latent_image.size(), dtype=latent_image.dtype, layout=latent_image.layout, device="cpu")
        else:
            batch_inds = latent["batch_index"] if "batch_index" in latent else None
            noise = comfy.sample.prepare_noise(latent_image, noise_seed, batch_inds)

        noise_mask = None
        if "noise_mask" in latent:
            noise_mask = latent["noise_mask"]

        x0_output = {}
        callback = latent_preview.prepare_callback(model, sigmas.shape[-1] - 1, x0_output)

223
        disable_pbar = not comfy.utils.PROGRESS_BAR_ENABLED
comfyanonymous's avatar
comfyanonymous committed
224
225
226
227
228
229
230
231
232
233
234
235
236
237
        samples = comfy.sample.sample_custom(model, noise, cfg, sampler, sigmas, positive, negative, latent_image, noise_mask=noise_mask, callback=callback, disable_pbar=disable_pbar, seed=noise_seed)

        out = latent.copy()
        out["samples"] = samples
        if "x0" in x0_output:
            out_denoised = latent.copy()
            out_denoised["samples"] = model.model.process_latent_out(x0_output["x0"].cpu())
        else:
            out_denoised = out
        return (out, out_denoised)

NODE_CLASS_MAPPINGS = {
    "SamplerCustom": SamplerCustom,
    "KarrasScheduler": KarrasScheduler,
238
239
    "ExponentialScheduler": ExponentialScheduler,
    "PolyexponentialScheduler": PolyexponentialScheduler,
comfyanonymous's avatar
comfyanonymous committed
240
    "VPScheduler": VPScheduler,
comfyanonymous's avatar
comfyanonymous committed
241
    "KSamplerSelect": KSamplerSelect,
comfyanonymous's avatar
comfyanonymous committed
242
    "SamplerDPMPP_2M_SDE": SamplerDPMPP_2M_SDE,
comfyanonymous's avatar
comfyanonymous committed
243
    "SamplerDPMPP_SDE": SamplerDPMPP_SDE,
244
    "BasicScheduler": BasicScheduler,
comfyanonymous's avatar
comfyanonymous committed
245
    "SplitSigmas": SplitSigmas,
comfyanonymous's avatar
comfyanonymous committed
246
}