Files
CavinHuang-comfyui-nodes-docs/docs/KSamplerSeq.md
2024-05-26 20:35:16 +08:00

13 KiB

Documentation

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,)