KSampler Sequence (v2)¶
Documentation¶
- Class name:
KSamplerSeq2
- Category:
sampling
- Output node:
False
KSamplerSeq2 is designed for advanced sequence sampling in generative models, allowing for intricate control over the sampling process through various parameters. It supports features like latent interpolation, conditioning sequence manipulation, and unsampling latents, enabling the creation of complex and nuanced sequences.
Input types¶
Required¶
model
- Specifies the generative model to be used for sampling.
- Comfy dtype:
MODEL
- Python dtype:
object
seed
- Determines the initial seed for random number generation, influencing the sampling process.
- Comfy dtype:
INT
- Python dtype:
int
seed_mode_seq
- Defines the mode of seed progression throughout the sequence, allowing for incremental, decremental, random, or fixed seed values.
- Comfy dtype:
COMBO[STRING]
- Python dtype:
List[str]
alternate_values
- Enables or disables the alternation of certain parameter values across the sequence.
- Comfy dtype:
BOOLEAN
- Python dtype:
bool
steps
- Specifies the number of steps to be taken in the sampling process.
- Comfy dtype:
INT
- Python dtype:
int
cfg
- Sets the configuration guidance scale, influencing the sampling output.
- Comfy dtype:
FLOAT
- Python dtype:
float
sampler_name
- Selects the specific sampler algorithm to be used.
- Comfy dtype:
COMBO[STRING]
- Python dtype:
str
scheduler
- Chooses the scheduler for controlling the sampling process.
- Comfy dtype:
COMBO[STRING]
- Python dtype:
str
frame_count
- Determines the total number of frames to be generated in the sequence.
- Comfy dtype:
INT
- Python dtype:
int
cond_keyframes
- Specifies keyframes for conditioning, allowing for dynamic changes in the sequence.
- Comfy dtype:
INT
- Python dtype:
int
positive_seq
- Defines the positive conditioning sequence for guiding the sampling towards desired attributes.
- Comfy dtype:
CONDITIONING
- Python dtype:
object
negative_seq
- Specifies the negative conditioning sequence for steering the sampling away from undesired attributes.
- Comfy dtype:
CONDITIONING
- Python dtype:
object
use_conditioning_slerp
- Enables or disables the use of spherical linear interpolation for conditioning sequences.
- Comfy dtype:
BOOLEAN
- Python dtype:
bool
cond_slerp_strength
- Sets the strength of the conditioning spherical linear interpolation.
- Comfy dtype:
FLOAT
- Python dtype:
float
latent_image
- Provides the initial latent image to start the sampling from.
- Comfy dtype:
LATENT
- Python dtype:
object
use_latent_interpolation
- Enables or disables latent interpolation between steps.
- Comfy dtype:
BOOLEAN
- Python dtype:
bool
latent_interpolation_mode
- Determines the mode of latent interpolation, such as blending, spherical linear interpolation, or cosine interpolation.
- Comfy dtype:
COMBO[STRING]
- Python dtype:
List[str]
latent_interp_strength
- Sets the strength of the latent interpolation.
- Comfy dtype:
FLOAT
- Python dtype:
float
denoise_start
- Specifies the initial denoising factor for the sampling process.
- Comfy dtype:
FLOAT
- Python dtype:
float
denoise_seq
- Specifies the denoising factor for the sequence.
- Comfy dtype:
FLOAT
- Python dtype:
float
unsample_latents
- Enables or disables the unsampling of latents, providing an option to refine the generated sequence.
- Comfy dtype:
BOOLEAN
- Python dtype:
bool
inject_noise
- Determines whether noise should be injected into the latent images.
- Comfy dtype:
BOOLEAN
- Python dtype:
bool
noise_strength
- Specifies the strength of the noise to be injected.
- Comfy dtype:
FLOAT
- Python dtype:
float
denoise_sine
- Enables or disables the use of a sine function to modulate the denoise parameter over the sequence.
- Comfy dtype:
BOOLEAN
- Python dtype:
bool
denoise_max
- Specifies the maximum denoise value when using a sine function for modulation.
- Comfy dtype:
FLOAT
- Python dtype:
float
seed_keying
- Enables or disables seed keying, a method to vary the seed based on certain conditions.
- Comfy dtype:
BOOLEAN
- Python dtype:
bool
seed_keying_mode
- Specifies the mode of seed keying, such as 'sine' or 'modulo'.
- Comfy dtype:
COMBO[STRING]
- Python dtype:
str
seed_divisor
- Specifies the divisor used in the 'modulo' seed keying mode.
- Comfy dtype:
INT
- Python dtype:
int
Output types¶
latent
- Comfy dtype:
LATENT
- The output is a latent representation of the generated sequence, which can be further processed or visualized.
- Python dtype:
object
- Comfy dtype:
Usage tips¶
- Infra type:
GPU
- Common nodes: unknown
Source code¶
class KSamplerSeq2:
def __init__(self):
self.previous_seed = None
self.current_seed = None
def initialize_seeds(self, initial_seed):
self.previous_seed = initial_seed
self.current_seed = initial_seed
@classmethod
def INPUT_TYPES(s):
return {"required":
{"model": ("MODEL",),
"seed": ("INT", {"default": 0, "min": 0, "max": 0xffffffffffffffff}),
"seed_mode_seq": (["increment", "decrement", "random", "fixed"],),
"alternate_values": ("BOOLEAN", {"default": True}),
"steps": ("INT", {"default": 20, "min": 1, "max": 10000}),
"cfg": ("FLOAT", {"default": 8.0, "min": 0.0, "max": 100.0, "step":0.5, "round": 0.01}),
"sampler_name": (comfy.samplers.KSampler.SAMPLERS, ),
"scheduler": (comfy.samplers.KSampler.SCHEDULERS, ),
"frame_count": ("INT", {"default": 0, "min": 0, "max": 1024, "step": 1}),
"cond_keyframes": ("INT", {"default": 0, "min": 0, "max": 1024, "step": 1}),
"positive_seq": ("CONDITIONING", ),
"negative_seq": ("CONDITIONING", ),
"use_conditioning_slerp": ("BOOLEAN", {"default": False}),
"cond_slerp_strength": ("FLOAT", {"default": 0.5, "min": 0.0, "max": 1.0, "step": 0.001}),
"latent_image": ("LATENT", ),
"use_latent_interpolation": ("BOOLEAN", {"default": False}),
"latent_interpolation_mode": (["Blend", "Slerp", "Cosine Interp"],),
"latent_interp_strength": ("FLOAT", {"default": 0.5, "min": 0.0, "max": 1.0, "step": 0.001}),
"denoise_start": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 1.0, "step": 0.01}),
"denoise_seq": ("FLOAT", {"default": 0.5, "min": 0.0, "max": 1.0, "step": 0.01}),
"unsample_latents": ("BOOLEAN", {"default": False}),
"inject_noise": ("BOOLEAN", {"default": True}),
"noise_strength": ("FLOAT", {"default": 0.1, "max": 1.0, "min": 0.001, "step": 0.001}),
"denoise_sine": ("BOOLEAN", {"default": True}),
"denoise_max": ("FLOAT", {"default": 0.9, "max": 1.0, "min": 0.0, "step": 0.001}),
"seed_keying": ("BOOLEAN", {"default": True}),
"seed_keying_mode": (["sine", "modulo"],),
"seed_divisor": ("INT", {"default": 4, "max": 1024, "min": 2, "step": 1}),
}
}
RETURN_TYPES = ("LATENT",)
FUNCTION = "sample"
CATEGORY = "sampling"
def update_seed(self, seed, seed_mode):
if seed_mode == "increment":
return seed + 1
elif seed_mode == "decrement":
return seed - 1
elif seed_mode == "random":
return random.randint(0, 0xffffffffffffffff)
elif seed_mode == "fixed":
return seed
def alternate_seed_modulo(self, current, seed, divisor):
if current % divisor == 0:
new_seed = (seed + current) % 0xffffffffffffffff
else:
new_seed = seed
return new_seed
def alternate_seed_sine(self, current, start_seed, divisor):
seed = 1000 * np.sin(2 * math.pi * current / divisor) + start_seed
return seed
def alternate_denoise(self, curent, total, start_denoise=0.5, max_denoise=0.95):
amplitude = (max_denoise - start_denoise) / 2
mid_point = (max_denoise + start_denoise) / 2
cycle_position = (math.pi * 2 * curent) / total
current_denoise = amplitude * math.sin(cycle_position) + mid_point
return current_denoise
def inject_noise(self, latent_image, noise_strength):
noise = torch.randn_like(latent_image) * noise_strength
return latent_image + noise
def sample(self, model, seed, seed_mode_seq, alternate_values, steps, cfg, sampler_name, scheduler,
frame_count, cond_keyframes, positive_seq, negative_seq, cond_slerp_strength, latent_image,
use_latent_interpolation, latent_interpolation_mode, latent_interp_strength, denoise_start=1.0,
denoise_seq=0.5, use_conditioning_slerp=False, unsample_latents=False, alternate_mode=False,
inject_noise=True, noise_strength=0.1, denoise_sine=True, denoise_max=0.9, seed_keying=True,
seed_keying_mode="sine", seed_divisor=4):
if not isinstance(positive_seq, list):
positive_seq = [positive_seq]
if not isinstance(negative_seq, list):
negative_seq = [negative_seq]
if not isinstance(cond_keyframes, list):
cond_keyframes = [cond_keyframes]
cond_keyframes.sort()
positive_cond_idx = 0
negative_cond_idx = 0
results = []
self.initialize_seeds(seed)
sequence_loop_count = max(frame_count, len(positive_seq)) if cond_keyframes else len(positive_seq)
print(f"Starting loop sequence with {sequence_loop_count} frames.")
print(f"Using {len(positive_seq)} positive conditionings and {len(negative_seq)} negative conditionings")
print(f"Conditioning keyframe schedule is: {', '.join(map(str, cond_keyframes))}")
for loop_count in range(sequence_loop_count):
if loop_count in cond_keyframes:
positive_cond_idx = min(positive_cond_idx + 1, len(positive_seq) - 1)
negative_cond_idx = min(negative_cond_idx + 1, len(negative_seq) - 1)
positive_conditioning = positive_seq[positive_cond_idx]
negative_conditioning = negative_seq[negative_cond_idx]
if seed_keying:
if seed_keying_mode == "sine":
seq_seed = seed if loop_count <= 0 else self.alternate_seed_sine(loop_count, seed, seed_divisor)
else:
seq_seed = seed if loop_count <= 0 else self.alternate_seed_modulo(loop_count, seed, seed_divisor)
else:
seq_seed = seed if loop_count <= 0 else self.update_seed(seq_seed, seed_mode_seq)
seq_seed = seed if loop_count <= 0 else self.update_seed(seq_seed, seed_mode_seq)
print(f"Loop count: {loop_count}, Seed: {seq_seed}")
last_positive_conditioning = positive_conditioning if positive_conditioning else None
last_negative_conditioning = negative_conditioning if negative_conditioning else None
if use_conditioning_slerp and (last_positive_conditioning and last_negative_conditioning):
a, b = last_positive_conditioning[0].clone(), positive_conditioning[0].clone()
na, nb = last_negative_conditioning[0].clone(), negative_conditioning[0].clone()
pa, pb = last_positive_conditioning[1]["pooled_output"].clone(), positive_conditioning[1]["pooled_output"].clone()
npa, npb = last_negative_conditioning[1]["pooled_output"].clone(), negative_conditioning[1]["pooled_output"].clone()
pos_cond = slerp(cond_slerp_strength, a, b)
pos_pooled = slerp(cond_slerp_strength, pa, pb)
neg_cond = slerp(cond_slerp_strength, na, nb)
neg_pooled = slerp(cond_slerp_strength, npa, npb)
positive_conditioning = [pos_cond, {"pooled_output": pos_pooled}]
negative_conditioning = [neg_cond, {"pooled_output": neg_pooled}]
positive_conditioning = [positive_conditioning]
negative_conditioning = [negative_conditioning]
end_at_step = steps
if results and len(results) > 0:
latent_input = {'samples': results[-1]}
denoise = self.alternate_denoise(loop_count, sequence_loop_count, denoise_seq, denoise_max) if denoise_sine else denoise_seq
start_at_step = round((1 - denoise) * steps)
end_at_step = steps
else:
latent_input = latent_image
denoise = denoise_start
if unsample_latents and loop_count > 0:
force_full_denoise = not (loop_count > 0 or loop_count <= steps - 1)
disable_noise = False
if seed_keying:
if seed_keying_mode == "modulo" and loop_count % seed_divisor == 0:
unsampled_latent = latent_input
else:
unsampled_latent = unsample(model=model, seed=seq_seed, cfg=cfg, sampler_name=sampler_name, steps=steps, end_at_step=end_at_step, scheduler=scheduler, normalize=False, positive=positive_conditioning, negative=negative_conditioning, latent_image=latent_input)[0]
else:
unsampled_latent = unsample(model=model, seed=seq_seed, cfg=cfg, sampler_name=sampler_name, steps=steps, end_at_step=end_at_step, scheduler=scheduler, normalize=False, positive=positive_conditioning, negative=negative_conditioning, latent_image=latent_input)[0]
if inject_noise and loop_count > 0:
print(f"Injecting noise at {noise_strength} strength.")
unsampled_latent['samples'] = self.inject_noise(unsampled_latent['samples'], noise_strength)
sample = nodes.common_ksampler(model, seq_seed, steps, cfg, sampler_name, scheduler, positive_conditioning, negative_conditioning, unsampled_latent, denoise=denoise, disable_noise=disable_noise, start_step=start_at_step, last_step=end_at_step, force_full_denoise=force_full_denoise)[0]['samples']
else:
if inject_noise and loop_count > 0:
print(f"Injecting noise at {noise_strength} strength.")
latent_input['samples'] = self.inject_noise(latent_input['samples'], noise_strength)
sample = nodes.common_ksampler(model, seq_seed, steps, cfg, sampler_name, scheduler, positive_conditioning, negative_conditioning, latent_input, denoise=denoise)[0]['samples']
if use_latent_interpolation and results and loop_count > 0:
if latent_interpolation_mode == "Blend":
sample = blend_latents(latent_interp_strength, results[-1], sample)
elif latent_interpolation_mode == "Slerp":
sample = slerp_latents(latent_interp_strength, results[-1], sample)
elif latent_interpolation_mode == "Cosine Interp":
sample = cosine_interp_latents(latent_interp_strength, results[-1], sample)
results.append(sample)
results = torch.cat(results, dim=0)
results = {'samples': results}
return (results,)