7.9 KiB
7.9 KiB
Documentation
- Class name: samplerSettingsNoiseIn
- Category: EasyUse/PreSampling
- Output node: True
- Repo Ref: https://github.com/yolain/ComfyUI-Easy-Use.git
该节点旨在为采样过程配置和应用噪声设置,这可以增强生成样本的多样性和质量。它允许调整噪声因素和调度,有助于实现更精细和可控的生成工作流程。
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,)