Skip to content

Commit 49e25ab

Browse files
authored
Merge PR #533 from Kosinkadink/develop - Ancestral Options
Add Ancestral Options to control ancestral sampler noise
2 parents 7992292 + 6c01970 commit 49e25ab

File tree

5 files changed

+140
-15
lines changed

5 files changed

+140
-15
lines changed

animatediff/nodes.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@
2626
from .nodes_sample import (FreeInitOptionsNode, NoiseLayerAddWeightedNode, NoiseLayerNormalizedSumNode, SampleSettingsNode, NoiseLayerAddNode, NoiseLayerReplaceNode, IterationOptionsNode,
2727
CustomCFGNode, CustomCFGSimpleNode, CustomCFGKeyframeNode, CustomCFGKeyframeSimpleNode, CustomCFGKeyframeInterpolationNode, CustomCFGKeyframeFromListNode,
2828
CFGExtrasPAGNode, CFGExtrasPAGSimpleNode, CFGExtrasRescaleCFGNode, CFGExtrasRescaleCFGSimpleNode,
29-
NoisedImageInjectionNode, NoisedImageInjectOptionsNode, NoiseCalibrationNode)
29+
NoisedImageInjectionNode, NoisedImageInjectOptionsNode, NoiseCalibrationNode, AncestralOptionsNode)
3030
from .nodes_sigma_schedule import (SigmaScheduleNode, RawSigmaScheduleNode, WeightedAverageSigmaScheduleNode, InterpolatedWeightedAverageSigmaScheduleNode, SplitAndCombineSigmaScheduleNode, SigmaScheduleToSigmasNode)
3131
from .nodes_context import (LegacyLoopedUniformContextOptionsNode, LoopedUniformContextOptionsNode, LoopedUniformViewOptionsNode, StandardUniformContextOptionsNode, StandardStaticContextOptionsNode, BatchedContextOptionsNode,
3232
StandardStaticViewOptionsNode, StandardUniformViewOptionsNode, ViewAsContextOptionsNode,
@@ -158,6 +158,7 @@
158158
"ADE_SigmaScheduleToSigmas": SigmaScheduleToSigmasNode,
159159
"ADE_NoisedImageInjection": NoisedImageInjectionNode,
160160
"ADE_NoisedImageInjectOptions": NoisedImageInjectOptionsNode,
161+
"ADE_AncestralOptions": AncestralOptionsNode,
161162
#"ADE_NoiseCalibration": NoiseCalibrationNode,
162163
# Scheduling
163164
PromptSchedulingNode.NodeID: PromptSchedulingNode,
@@ -338,6 +339,7 @@
338339
"ADE_NoisedImageInjection": "Image Injection 🎭🅐🅓",
339340
"ADE_NoisedImageInjectOptions": "Image Injection Options 🎭🅐🅓",
340341
"ADE_NoiseCalibration": "Noise Calibration 🎭🅐🅓",
342+
"ADE_AncestralOptions": "Ancestral Options 🎭🅐🅓",
341343
# Scheduling
342344
PromptSchedulingNode.NodeID: PromptSchedulingNode.NodeName,
343345
PromptSchedulingLatentsNode.NodeID: PromptSchedulingLatentsNode.NodeName,

animatediff/nodes_sample.py

Lines changed: 40 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,9 @@
55
from comfy.sd import VAE
66

77
from .freeinit import FreeInitFilter
8-
from .sample_settings import (FreeInitOptions, IterationOptions,
8+
from .sample_settings import (FreeInitOptions, IterationOptions, AncestralOptions,
99
NoiseLayerAdd, NoiseLayerAddWeighted, NoiseLayerNormalizedSum, NoiseLayerGroup, NoiseLayerReplace, NoiseLayerType,
10-
SeedNoiseGeneration, SampleSettings, NoiseCalibration,
10+
SeedNoiseGeneration, SampleSettings, NoiseCalibration, NoiseDeterminism,
1111
CustomCFGKeyframeGroup, CustomCFGKeyframe, CFGExtrasGroup, CFGExtras,
1212
NoisedImageToInjectGroup, NoisedImageToInject, NoisedImageInjectOptions)
1313
from .utils_model import BIGMIN, BIGMAX, MAX_RESOLUTION, SigmaSchedule, InterpolationMethod
@@ -28,11 +28,12 @@ def INPUT_TYPES(s):
2828
"optional": {
2929
"noise_layers": ("NOISE_LAYERS",),
3030
"iteration_opts": ("ITERATION_OPTS",),
31-
"seed_override": ("INT", {"default": 0, "min": 0, "max": 0xffffffffffffffff, "forceInput": True}),
31+
"seed_override": ("INT", {"default": 0, "min": 0, "max": 0xffffffffffffffff, "defaultInput": True}),
3232
"adapt_denoise_steps": ("BOOLEAN", {"default": False},),
3333
"custom_cfg": ("CUSTOM_CFG",),
3434
"sigma_schedule": ("SIGMA_SCHEDULE",),
3535
"image_inject": ("IMAGE_INJECT",),
36+
"ancestral_opts": ("ANCESTRAL_OPTS",),
3637
#"noise_calib": ("NOISE_CALIBRATION",), # TODO: bring back once NoiseCalibration is working
3738
},
3839
"hidden": {
@@ -48,13 +49,43 @@ def INPUT_TYPES(s):
4849
def create_settings(self, batch_offset: int, noise_type: str, seed_gen: str, seed_offset: int, noise_layers: NoiseLayerGroup=None,
4950
iteration_opts: IterationOptions=None, seed_override: int=None, adapt_denoise_steps=False,
5051
custom_cfg: CustomCFGKeyframeGroup=None, sigma_schedule: SigmaSchedule=None, image_inject: NoisedImageToInjectGroup=None,
51-
noise_calib: NoiseCalibration=None):
52+
noise_calib: NoiseCalibration=None, ancestral_opts=None):
5253
sampling_settings = SampleSettings(batch_offset=batch_offset, noise_type=noise_type, seed_gen=seed_gen, seed_offset=seed_offset, noise_layers=noise_layers,
5354
iteration_opts=iteration_opts, seed_override=seed_override, adapt_denoise_steps=adapt_denoise_steps,
54-
custom_cfg=custom_cfg, sigma_schedule=sigma_schedule, image_injection=image_inject, noise_calibration=noise_calib)
55+
custom_cfg=custom_cfg, sigma_schedule=sigma_schedule, image_injection=image_inject, noise_calibration=noise_calib,
56+
ancestral_opts=ancestral_opts)
5557
return (sampling_settings,)
5658

5759

60+
class AncestralOptionsNode:
61+
@classmethod
62+
def INPUT_TYPES(s):
63+
return {
64+
"required": {
65+
#"batch_offset": ("INT", {"default": 0, "min": 0, "max": BIGMAX}),
66+
"noise_type": (NoiseLayerType.LIST_ANCESTRAL,),
67+
#"determinism": (NoiseDeterminism._LIST,),
68+
"seed_offset": ("INT", {"default": 0, "min": BIGMIN, "max": BIGMAX}),
69+
#"seed_gen_override": (SeedNoiseGeneration.LIST_WITH_OVERRIDE,),
70+
},
71+
"optional": {
72+
"seed_override": ("INT", {"default": 0, "min": 0, "max": 0xffffffffffffffff, "defaultInput": True}),
73+
},
74+
"hidden": {
75+
"autosize": ("ADEAUTOSIZE", {"padding": 0}),
76+
}
77+
}
78+
79+
RETURN_TYPES = ("ANCESTRAL_OPTS",)
80+
CATEGORY = "Animate Diff 🎭🅐🅓/sample settings"
81+
FUNCTION = "create_ancestral_opts"
82+
83+
def create_ancestral_opts(self, noise_type: str, seed_offset: int, determinism: str=NoiseDeterminism.DEFAULT, seed_override: int=None):
84+
if isinstance(seed_override, Iterable):
85+
raise Exception("Passing in a list of seeds for Ancestral Options is not supported at this time.")
86+
return (AncestralOptions(noise_type=noise_type, determinism=determinism, seed_offset=seed_offset, seed_override=seed_override),)
87+
88+
5889
class NoiseLayerReplaceNode:
5990
@classmethod
6091
def INPUT_TYPES(s):
@@ -68,7 +99,7 @@ def INPUT_TYPES(s):
6899
"optional": {
69100
"prev_noise_layers": ("NOISE_LAYERS",),
70101
"mask_optional": ("MASK",),
71-
"seed_override": ("INT", {"default": 0, "min": 0, "max": 0xffffffffffffffff, "forceInput": True}),
102+
"seed_override": ("INT", {"default": 0, "min": 0, "max": 0xffffffffffffffff, "defaultInput": True}),
72103
},
73104
"hidden": {
74105
"autosize": ("ADEAUTOSIZE", {"padding": 0}),
@@ -106,7 +137,7 @@ def INPUT_TYPES(s):
106137
"optional": {
107138
"prev_noise_layers": ("NOISE_LAYERS",),
108139
"mask_optional": ("MASK",),
109-
"seed_override": ("INT", {"default": 0, "min": 0, "max": 0xffffffffffffffff, "forceInput": True}),
140+
"seed_override": ("INT", {"default": 0, "min": 0, "max": 0xffffffffffffffff, "defaultInput": True}),
110141
},
111142
"hidden": {
112143
"autosize": ("ADEAUTOSIZE", {"padding": 0}),
@@ -147,7 +178,7 @@ def INPUT_TYPES(s):
147178
"optional": {
148179
"prev_noise_layers": ("NOISE_LAYERS",),
149180
"mask_optional": ("MASK",),
150-
"seed_override": ("INT", {"default": 0, "min": 0, "max": 0xffffffffffffffff, "forceInput": True}),
181+
"seed_override": ("INT", {"default": 0, "min": 0, "max": 0xffffffffffffffff, "defaultInput": True}),
151182
},
152183
"hidden": {
153184
"autosize": ("ADEAUTOSIZE", {"padding": 0}),
@@ -187,7 +218,7 @@ def INPUT_TYPES(s):
187218
"optional": {
188219
"prev_noise_layers": ("NOISE_LAYERS",),
189220
"mask_optional": ("MASK",),
190-
"seed_override": ("INT", {"default": 0, "min": 0, "max": 0xffffffffffffffff, "forceInput": True}),
221+
"seed_override": ("INT", {"default": 0, "min": 0, "max": 0xffffffffffffffff, "defaultInput": True}),
191222
},
192223
"hidden": {
193224
"autosize": ("ADEAUTOSIZE", {"padding": 0}),

animatediff/sample_settings.py

Lines changed: 93 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,16 @@
1+
from __future__ import annotations
12
from collections.abc import Iterable
23
from typing import Union, Callable
34
import torch
45
from torch import Tensor
56
import torch.fft as fft
67
from einops import rearrange
78

9+
import comfy.k_diffusion.sampling
810
import comfy.sample
911
import comfy.samplers
1012
import comfy.model_management
13+
from comfy.patcher_extension import WrappersMP, add_wrapper_with_key
1114
from comfy.model_patcher import ModelPatcher
1215
from comfy.model_base import BaseModel
1316
from comfy.sd import VAE
@@ -29,6 +32,13 @@ def prepare_mask_ad(noise_mask, shape, device):
2932
return noise_mask
3033

3134

35+
class NoiseDeterminism:
36+
DEFAULT = "default"
37+
DETERMINISTIC = "deterministic"
38+
39+
_LIST = [DEFAULT, DETERMINISTIC]
40+
41+
3242
class NoiseLayerType:
3343
DEFAULT = "default"
3444
CONSTANT = "constant"
@@ -37,6 +47,7 @@ class NoiseLayerType:
3747
FREENOISE = "FreeNoise"
3848

3949
LIST = [DEFAULT, CONSTANT, EMPTY, REPEATED_CONTEXT, FREENOISE]
50+
LIST_ANCESTRAL = [DEFAULT, CONSTANT]
4051

4152

4253
class NoiseApplication:
@@ -56,10 +67,10 @@ class NoiseNormalize:
5667

5768

5869
class SampleSettings:
59-
def __init__(self, batch_offset: int=0, noise_type: str=None, seed_gen: str=None, seed_offset: int=0, noise_layers: 'NoiseLayerGroup'=None,
70+
def __init__(self, batch_offset: int=0, noise_type: str=None, seed_gen: str=None, seed_offset: int=0, noise_layers: NoiseLayerGroup=None,
6071
iteration_opts=None, seed_override:int=None, negative_cond_flipflop=False, adapt_denoise_steps: bool=False,
61-
custom_cfg: 'CustomCFGKeyframeGroup'=None, sigma_schedule: SigmaSchedule=None, image_injection: 'NoisedImageToInjectGroup'=None,
62-
noise_calibration: 'NoiseCalibration'=None):
72+
custom_cfg: CustomCFGKeyframeGroup=None, sigma_schedule: SigmaSchedule=None, image_injection: NoisedImageToInjectGroup=None,
73+
noise_calibration: NoiseCalibration=None, ancestral_opts: AncestralOptions=None):
6374
self.batch_offset = batch_offset
6475
self.noise_type = noise_type if noise_type is not None else NoiseLayerType.DEFAULT
6576
self.seed_gen = seed_gen if seed_gen is not None else SeedNoiseGeneration.COMFY
@@ -73,6 +84,7 @@ def __init__(self, batch_offset: int=0, noise_type: str=None, seed_gen: str=None
7384
self.sigma_schedule = sigma_schedule
7485
self.image_injection = image_injection.clone() if image_injection else NoisedImageToInjectGroup()
7586
self.noise_calibration = noise_calibration
87+
self.ancestral_opts = ancestral_opts
7688

7789
def prepare_noise(self, seed: int, latents: Tensor, noise: Tensor, extra_seed_offset=0, extra_args:dict={}, force_create_noise=True):
7890
if self.seed_override is not None:
@@ -113,7 +125,84 @@ def clone(self):
113125
return SampleSettings(batch_offset=self.batch_offset, noise_type=self.noise_type, seed_gen=self.seed_gen, seed_offset=self.seed_offset,
114126
noise_layers=self.noise_layers.clone(), iteration_opts=self.iteration_opts, seed_override=self.seed_override,
115127
negative_cond_flipflop=self.negative_cond_flipflop, adapt_denoise_steps=self.adapt_denoise_steps, custom_cfg=self.custom_cfg,
116-
sigma_schedule=self.sigma_schedule, image_injection=self.image_injection, noise_calibration=self.noise_calibration)
128+
sigma_schedule=self.sigma_schedule, image_injection=self.image_injection, noise_calibration=self.noise_calibration,
129+
ancestral_opts=self.ancestral_opts)
130+
131+
132+
class AncestralOptions:
133+
def __init__(self, noise_type: str, determinism: str, seed_offset: int, seed_override: int=None):
134+
self.noise_type = noise_type
135+
self.determinism = determinism
136+
self.seed_offset = seed_offset
137+
self.seed_override = seed_override
138+
139+
def init_custom_noise_sampler(self, seed: int):
140+
if self.seed_override is not None:
141+
seed = self.seed_override
142+
if isinstance(seed, Iterable):
143+
raise Exception("Passing in a list of seeds for Ancestral Options is not supported at this time.")
144+
seed += self.seed_offset
145+
return _custom_noise_sampler_factory(real_seed=seed, noise_type=self.noise_type, determinism=self.determinism)
146+
147+
def add_wrapper_sampler_sample(self, model_options, seed):
148+
add_wrapper_with_key(WrappersMP.SAMPLER_SAMPLE, "ADE",
149+
_sampler_sample_ancestral_options_factory(self.init_custom_noise_sampler(seed)),
150+
model_options, is_model_options=True)
151+
152+
153+
def _sampler_sample_ancestral_options_factory(custom_noise_sampler: Callable):
154+
def sampler_sample_ancestral_options_wrapper(executor, *args, **kwargs):
155+
try:
156+
# TODO: implement this as a model_options thing instead in core ComfyUI
157+
orig_default_noise_sampler = comfy.k_diffusion.sampling.default_noise_sampler
158+
comfy.k_diffusion.sampling.default_noise_sampler = custom_noise_sampler
159+
return executor(*args, **kwargs)
160+
finally:
161+
comfy.k_diffusion.sampling.default_noise_sampler = orig_default_noise_sampler
162+
return sampler_sample_ancestral_options_wrapper
163+
164+
165+
def _custom_noise_sampler_factory(real_seed: int, noise_type: str, determinism: str):
166+
def custom_noise_sampler(x: Tensor, seed: int=None):
167+
single_generator = None
168+
multiple_generators = []
169+
if determinism == NoiseDeterminism.DEFAULT:
170+
# prepare generators
171+
single_generator = torch.Generator(device=x.device)
172+
single_generator.manual_seed(real_seed)
173+
# create function to handle determinism type
174+
def sample_default(sigma, sigma_next):
175+
if noise_type == NoiseLayerType.CONSTANT:
176+
goal_shape = list(x.shape)
177+
goal_shape[0] = 1
178+
one_noise = torch.randn(goal_shape, dtype=x.dtype, layout=x.layout, device=x.device, generator=single_generator)
179+
return torch.cat([one_noise]*x.shape[0], dim=0)
180+
return torch.randn(x.size(), dtype=x.dtype, layout=x.layout, device=x.device, generator=single_generator)
181+
# return function
182+
return sample_default
183+
elif determinism == NoiseDeterminism.DETERMINISTIC:
184+
# prepare generators
185+
for i in range(x.size(0)):
186+
generator = torch.Generator(device=x.device)
187+
multiple_generators.append(generator.manual_seed(real_seed+i))
188+
# create function to handle determinism type
189+
def sample_deterministic(sigma, sigma_next):
190+
goal_shape = list(x.shape)
191+
goal_shape[0] = 1
192+
if noise_type == NoiseLayerType.CONSTANT:
193+
one_noise = torch.randn(goal_shape, dtype=x.dtype, layout=x.layout, device=x.device, generator=multiple_generators[0])
194+
return torch.cat([one_noise]*x.shape[0], dim=0)
195+
noises = []
196+
for generator in multiple_generators:
197+
one_noise = torch.randn(goal_shape, dtype=x.dtype, layout=x.layout, device=x.device, generator=generator)
198+
noises.append(one_noise)
199+
return torch.cat(noises, dim=0)
200+
# return function
201+
return sample_deterministic
202+
else:
203+
raise Exception(f"Determinism type '{determinism}' is not recognized.")
204+
# return function
205+
return custom_noise_sampler
117206

118207

119208
class NoiseLayer:

animatediff/sampling.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -408,6 +408,9 @@ def outer_sample_wrapper(executor: WrapperExecutor, *args, **kwargs):
408408
# if noise is not disabled, do noise stuff
409409
if not disable_noise:
410410
noise = helper.get_sample_settings().prepare_noise(seed, latents, noise, extra_args=noise_extra_args, force_create_noise=False)
411+
# handle AncestralOptions, if present
412+
if helper.get_sample_settings().ancestral_opts is not None:
413+
helper.get_sample_settings().ancestral_opts.add_wrapper_sampler_sample(guider.model_options, seed)
411414

412415
# callback setup
413416
original_callback = args[-3]

pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
[project]
22
name = "comfyui-animatediff-evolved"
33
description = "Improved AnimateDiff integration for ComfyUI."
4-
version = "1.4.2"
4+
version = "1.4.3"
55
license = { file = "LICENSE" }
66
dependencies = []
77

0 commit comments

Comments
 (0)