13 KiB
13 KiB
Documentation
- Class name: KSamplerSeq
- Category: sampling
- Output node: False
- Repo Ref: https://github.com/WASasquatch/WAS_Extras
KSamplerSeq节点旨在使用各种种子模式和条件序列执行顺序采样操作。它管理生成潜在样本,具有在正负条件序列之间交替、插值潜在样本以及控制去噪过程的能力。该节点确保了一个连贯且可控的采样工作流程,可以根据特定的创造性或分析目的进行微调。
Input types
Required
- model
- 模型参数对于KSamplerSeq节点至关重要,因为它定义了将用于采样的生成模型。模型的选择显著影响生成样本的质量和风格。
- Comfy dtype: MODEL
- Python dtype: torch.nn.Module
- seed
- 种子参数对于初始化随机数生成过程至关重要,确保了采样操作中的可重复性。它为生成的种子序列设定了起始点。
- Comfy dtype: INT
- Python dtype: int
Optional
- seed_mode_seq
- seed_mode_seq参数决定了采样序列中种子值是如何更新的,允许采用不同的采样策略,如增量、递减、随机选择或固定种子。
- Comfy dtype: COMBO['increment', 'decrement', 'random', 'fixed']
- Python dtype: str
- alternate_values
- alternate_values参数在采样过程中启用不同种子状态之间的交替,为生成的样本增加了变化性。
- Comfy dtype: BOOLEAN
- Python dtype: bool
- steps
- steps参数指定了采样过程中的步骤数量,这直接影响生成样本的分辨率和细节。
- Comfy dtype: INT
- Python dtype: int
- cfg
- cfg参数,代表配置值,用于控制采样过程的保真度,在生成样本的细节和噪声之间进行平衡。
- Comfy dtype: FLOAT
- Python dtype: float
- sampler_name
- sampler_name参数从可用的采样器中选择要使用的特定采样方法,影响采样过程的整体行为和特性。
- Comfy dtype: comfy.samplers.KSampler.SAMPLERS
- Python dtype: str
- scheduler
- scheduler参数定义了采样过程中的噪声减少策略,这可能会影响生成样本的平滑度和连贯性。
- Comfy dtype: comfy.samplers.KSampler.SCHEDULERS
- Python dtype: str
- sequence_loop_count
- sequence_loop_count参数设置了采样序列的迭代次数,允许在单次运行中生成多个样本。
- Comfy dtype: INT
- Python dtype: int
- positive_seq
- positive_seq参数提供了一系列正向条件值,引导采样过程朝着期望的结果发展。
- Comfy dtype: CONDITIONING_SEQ
- Python dtype: List[Tuple[int, torch.Tensor, Dict[str, torch.Tensor]]]
- negative_seq
- negative_seq参数提供了一系列负向条件值,通过阻止不希望的结果来帮助完善采样过程。
- Comfy dtype: CONDITIONING_SEQ
- Python dtype: List[Tuple[int, torch.Tensor, Dict[str, torch.Tensor]]]
- use_conditioning_slerp
- use_conditioning_slerp参数启用了球面线性插值(SLERP)来混合条件值,这可以导致样本状态之间更平滑的过渡。
- Comfy dtype: BOOLEAN
- Python dtype: bool
- cond_slerp_strength
- cond_slerp_strength参数控制混合条件值时SLERP操作的强度,影响状态间插值的范围。
- Comfy dtype: FLOAT
- Python dtype: float
- latent_image
- latent_image参数表示用作采样过程起点的初始潜在状态,影响生成样本的方向和特性。
- Comfy dtype: LATENT
- Python dtype: Dict[str, torch.Tensor]
- use_latent_interpolation
- use_latent_interpolation参数切换应用潜在插值在生成样本之间,这可以产生更连贯的样本序列。
- Comfy dtype: BOOLEAN
- Python dtype: bool
- latent_interpolation_mode
- latent_interpolation_mode参数选择要使用的潜在插值方法,这定义了连续样本如何混合在一起。
- Comfy dtype: COMBO['Blend', 'Slerp', 'Cosine Interp']
- Python dtype: str
- latent_interp_strength
- latent_interp_strength参数调整潜在插值的影响,决定一个样本在序列中如何平滑地过渡到下一个样本。
- Comfy dtype: FLOAT
- Python dtype: float
- denoise_start
- denoise_start参数设置了初始去噪强度,这影响采样过程开始时的噪声减少。
- Comfy dtype: FLOAT
- Python dtype: float
- denoise_seq
- denoise_seq参数控制整个采样序列中的去噪强度,允许随时间微调噪声减少。
- Comfy dtype: FLOAT
- Python dtype: float
- unsample_latents
- 当启用unsample_latents参数时,触发未采样潜在状态的生成过程,这可以引入更多的多样性到采样输出中。
- Comfy dtype: BOOLEAN
- Python dtype: bool
Output types
- samples
- samples参数封装了采样过程中生成的潜在样本。它是代表生成图像潜在空间的张量集合。
- Comfy dtype: LATENT
- Python dtype: torch.Tensor
Usage tips
- Infra type: GPU
Source code
class KSamplerSeq:
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': 18446744073709551615}), '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,), 'sequence_loop_count': ('INT', {'default': 20, 'min': 1, 'max': 1024, 'step': 1}), 'positive_seq': ('CONDITIONING_SEQ',), 'negative_seq': ('CONDITIONING_SEQ',), '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})}}
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, 18446744073709551615)
elif seed_mode == 'fixed':
return seed
def hash_tensor(self, tensor):
tensor = tensor.cpu().contiguous()
return hashlib.sha256(tensor.numpy().tobytes()).hexdigest()
def update_conditioning(self, conditioning_seq, loop_count, last_conditioning):
matching_conditioning = None
for (idx, conditioning, *_) in conditioning_seq:
if int(idx) == loop_count:
matching_conditioning = conditioning
break
return matching_conditioning if matching_conditioning else last_conditioning if last_conditioning else None
def update_alternate_seed(self, loop_count):
if loop_count % 3 == 0:
if self.previous_seed is None:
self.previous_seed = self.current_seed
else:
(self.previous_seed, self.current_seed) = (self.current_seed, self.previous_seed + 1 if loop_count // 2 % 2 == 0 else self.previous_seed - 1)
return self.current_seed
def alternate_denoise(self, current_denoise):
return 0.95 if current_denoise == 0.75 else 0.75
def sample(self, model, seed, seed_mode_seq, alternate_values, steps, cfg, sampler_name, scheduler, sequence_loop_count, 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):
positive_seq = positive_seq
negative_seq = negative_seq
results = []
positive_conditioning = None
negative_conditioning = None
self.initialize_seeds(seed)
for loop_count in range(sequence_loop_count):
if alternate_values and loop_count % 2 == 0:
seq_seed = self.update_alternate_seed(seed) if seed_mode_seq != 'fixed' else seed
else:
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[0] if positive_conditioning else None
last_negative_conditioning = negative_conditioning[0] if negative_conditioning else None
positive_conditioning = self.update_conditioning(positive_seq, loop_count, last_positive_conditioning)
negative_conditioning = self.update_conditioning(negative_seq, loop_count, last_negative_conditioning)
if use_conditioning_slerp and (last_positive_conditioning and last_negative_conditioning):
a = last_positive_conditioning[0].clone()
b = positive_conditioning[0].clone()
na = last_negative_conditioning[0].clone()
nb = negative_conditioning[0].clone()
pa = last_positive_conditioning[1]['pooled_output'].clone()
pb = positive_conditioning[1]['pooled_output'].clone()
npa = last_negative_conditioning[1]['pooled_output'].clone()
npb = 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]
if positive_conditioning is not None or negative_conditioning is not None:
end_at_step = steps
if results is not None and len(results) > 0:
latent_input = {'samples': results[-1]}
denoise = 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 = False if loop_count > 0 or loop_count <= steps - 1 else True
disable_noise = False
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]
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:
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)
else:
sample = sample
results.append(sample)
results = torch.cat(results, dim=0)
results = {'samples': results}
return (results,)