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

7.9 KiB
Raw Permalink Blame History

Documentation

该节点旨在为采样过程配置和应用噪声设置,这可以增强生成样本的多样性和质量。它允许调整噪声因素和调度,有助于实现更精细和可控的生成工作流程。

Input types

Required

  • pipe
    • ‘pipe’参数是必要的,因为它携带了正在处理的模型和样本的必要信息。它是节点操作的支柱,促进了应用噪声和后续采样步骤所需的数据流。
    • Comfy dtype: PIPE_LINE
    • Python dtype: Dict[str, Any]
  • factor
    • ‘factor’参数在确定采样过程中引入的噪声程度方面至关重要。它直接影响原始噪声和变化噪声之间的平衡,从而影响最终输出的多样性和一致性。
    • Comfy dtype: FLOAT
    • Python dtype: float
  • steps
    • ‘steps’参数定义了采样过程将要经历的迭代次数。它很重要,因为它决定了采样的复杂性和深度,更多的步骤可能导致更精细的结果。
    • Comfy dtype: INT
    • Python dtype: int
  • cfg
    • ‘cfg’参数用于微调采样过程的配置。它在调整节点的整体行为方面发挥作用,确保采样按照期望的规格进行。
    • Comfy dtype: FLOAT
    • Python dtype: float
  • sampler_name
    • ‘sampler_name’参数在选择合适的采样方法方面至关重要。它影响生成样本时采用的策略,不同的采样器提供不同的噪声应用和样本多样性方法。
    • Comfy dtype: COMBO
    • Python dtype: str
  • scheduler
    • ‘scheduler’参数在管理采样步骤中噪声的应用方面很重要。它有助于控制引入噪声的速度,确保从采样过程的一个阶段平稳过渡到下一个阶段。
    • Comfy dtype: COMBO
    • Python dtype: str
  • denoise
    • ‘denoise’参数负责指定要应用的去噪水平。它影响生成样本的清晰度和锐度,较高的值导致更清晰的输出。
    • Comfy dtype: FLOAT
    • Python dtype: float
  • seed
    • ‘seed’参数对于确保采样过程中的可重复性至关重要。它初始化随机数生成器,允许在不同运行中使用相同设置时获得一致的结果。
    • Comfy dtype: INT
    • Python dtype: int

Output types

  • pipe
    • ‘pipe’输出是输入‘pipe’的修改版本,现在包含调整后的噪声设置和生成的样本。它是节点处理的成果,封装了增强的采样数据以供进一步使用。
    • Comfy dtype: PIPE_LINE
    • Python dtype: Dict[str, Any]

Usage tips

  • Infra type: CPU

Source code

class samplerSettingsNoiseIn:

    def __init__(self):
        pass

    @classmethod
    def INPUT_TYPES(cls):
        return {'required': {'pipe': ('PIPE_LINE',), 'factor': ('FLOAT', {'default': 0.1, 'min': 0.0, 'max': 1.0, 'step': 0.01, 'round': 0.01}), 'steps': ('INT', {'default': 20, 'min': 1, 'max': 10000}), 'cfg': ('FLOAT', {'default': 8.0, 'min': 0.0, 'max': 100.0}), 'sampler_name': (comfy.samplers.KSampler.SAMPLERS,), 'scheduler': (comfy.samplers.KSampler.SCHEDULERS,), 'denoise': ('FLOAT', {'default': 1.0, 'min': 0.0, 'max': 1.0, 'step': 0.01}), 'seed': ('INT', {'default': 0, 'min': 0, 'max': MAX_SEED_NUM})}, 'optional': {'optional_noise_seed': ('INT', {'forceInput': True}), 'optional_latent': ('LATENT',)}, 'hidden': {'prompt': 'PROMPT', 'extra_pnginfo': 'EXTRA_PNGINFO', 'my_unique_id': 'UNIQUE_ID'}}
    RETURN_TYPES = ('PIPE_LINE',)
    RETURN_NAMES = ('pipe',)
    OUTPUT_NODE = True
    FUNCTION = 'settings'
    CATEGORY = 'EasyUse/PreSampling'

    def slerp(self, val, low, high):
        dims = low.shape
        low = low.reshape(dims[0], -1)
        high = high.reshape(dims[0], -1)
        low_norm = low / torch.norm(low, dim=1, keepdim=True)
        high_norm = high / torch.norm(high, dim=1, keepdim=True)
        low_norm[low_norm != low_norm] = 0.0
        high_norm[high_norm != high_norm] = 0.0
        omega = torch.acos((low_norm * high_norm).sum(1))
        so = torch.sin(omega)
        res = (torch.sin((1.0 - val) * omega) / so).unsqueeze(1) * low + (torch.sin(val * omega) / so).unsqueeze(1) * high
        return res.reshape(dims)

    def prepare_mask(self, mask, shape):
        mask = torch.nn.functional.interpolate(mask.reshape((-1, 1, mask.shape[-2], mask.shape[-1])), size=(shape[2], shape[3]), mode='bilinear')
        mask = mask.expand((-1, shape[1], -1, -1))
        if mask.shape[0] < shape[0]:
            mask = mask.repeat((shape[0] - 1) // mask.shape[0] + 1, 1, 1, 1)[:shape[0]]
        return mask

    def expand_mask(self, mask, expand, tapered_corners):
        try:
            import numpy as np
            import scipy
            c = 0 if tapered_corners else 1
            kernel = np.array([[c, 1, c], [1, 1, 1], [c, 1, c]])
            mask = mask.reshape((-1, mask.shape[-2], mask.shape[-1]))
            out = []
            for m in mask:
                output = m.numpy()
                for _ in range(abs(expand)):
                    if expand < 0:
                        output = scipy.ndimage.grey_erosion(output, footprint=kernel)
                    else:
                        output = scipy.ndimage.grey_dilation(output, footprint=kernel)
                output = torch.from_numpy(output)
                out.append(output)
            return torch.stack(out, dim=0)
        except:
            return None

    def settings(self, pipe, factor, steps, cfg, sampler_name, scheduler, denoise, seed, optional_noise_seed=None, optional_latent=None, prompt=None, extra_pnginfo=None, my_unique_id=None):
        latent = optional_latent if optional_latent is not None else pipe['samples']
        model = pipe['model']
        (batch_size, _, height, width) = latent['samples'].shape
        generator = torch.manual_seed(seed)
        base_noise = torch.randn((1, 4, height, width), dtype=torch.float32, device='cpu', generator=generator).repeat(batch_size, 1, 1, 1).cpu()
        if optional_noise_seed is None or optional_noise_seed == seed:
            optional_noise_seed = seed + 1
        generator = torch.manual_seed(optional_noise_seed)
        variation_noise = torch.randn((batch_size, 4, height, width), dtype=torch.float32, device='cpu', generator=generator).cpu()
        slerp_noise = self.slerp(factor, base_noise, variation_noise)
        end_at_step = steps
        start_at_step = round(end_at_step - end_at_step * denoise)
        device = comfy.model_management.get_torch_device()
        comfy.model_management.load_model_gpu(model)
        model_patcher = comfy.model_patcher.ModelPatcher(model.model, load_device=device, offload_device=comfy.model_management.unet_offload_device())
        sampler = comfy.samplers.KSampler(model_patcher, steps=steps, device=device, sampler=sampler_name, scheduler=scheduler, denoise=1.0, model_options=model.model_options)
        sigmas = sampler.sigmas
        sigma = sigmas[start_at_step] - sigmas[end_at_step]
        sigma /= model.model.latent_format.scale_factor
        sigma = sigma.cpu().numpy()
        work_latent = latent.copy()
        work_latent['samples'] = latent['samples'].clone() + slerp_noise * sigma
        if 'noise_mask' in latent:
            noise_mask = self.prepare_mask(latent['noise_mask'], latent['samples'].shape)
            work_latent['samples'] = noise_mask * work_latent['samples'] + (1 - noise_mask) * latent['samples']
            work_latent['noise_mask'] = self.expand_mask(latent['noise_mask'].clone(), 5, True)
        if pipe is None:
            pipe = {}
        new_pipe = {'model': pipe['model'], 'positive': pipe['positive'], 'negative': pipe['negative'], 'vae': pipe['vae'], 'clip': pipe['clip'], 'samples': work_latent, 'images': pipe['images'], 'seed': seed, 'loader_settings': {**pipe['loader_settings'], 'steps': steps, 'cfg': cfg, 'sampler_name': sampler_name, 'scheduler': scheduler, 'denoise': denoise, 'add_noise': 'disable'}}
        return (new_pipe,)