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

7.6 KiB

Documentation

RegionalSampler节点旨在对图像的特定区域执行高级采样技术,允许对生成过程进行细粒度控制。它使用户能够对图像的不同区域应用不同的采样策略,提高生成输出的整体质量和一致性。

Input types

Required

  • seed
    • 种子参数对于随机数生成过程的初始化至关重要,确保了采样结果的可复现性。它在确定采样算法的起始点中起着重要作用,从而影响最终输出。
    • Comfy dtype: INT
    • Python dtype: int
  • steps
    • 步骤参数定义了采样过程将经历的迭代次数。它是控制生成图像的收敛和细节级别的关键因素,更多的步骤通常会导致更精细的结果。
    • Comfy dtype: INT
    • Python dtype: int
  • regional_prompts
    • 区域提示对于指导图像特定区域内的采样过程至关重要。它们作为掩码来隔离不同的区域,允许有针对性的采样,并增强最终输出的区域细节。
    • Comfy dtype: REGIONAL_PROMPTS
    • Python dtype: List[RegionalPrompt]

Output types

  • latent
    • 潜在参数代表采样过程后生成图像的编码形式。它很重要,因为它捕获了图像的底层结构和信息,可以进一步处理或用于其他任务。
    • Comfy dtype: LATENT
    • Python dtype: torch.Tensor

Usage tips

  • Infra type: GPU

Source code

class RegionalSampler:

    @classmethod
    def INPUT_TYPES(s):
        return {'required': {'seed': ('INT', {'default': 0, 'min': 0, 'max': 18446744073709551615}), 'seed_2nd': ('INT', {'default': 0, 'min': 0, 'max': 18446744073709551615}), 'seed_2nd_mode': (['ignore', 'fixed', 'seed+seed_2nd', 'seed-seed_2nd', 'increment', 'decrement', 'randomize'],), 'steps': ('INT', {'default': 20, 'min': 1, 'max': 10000}), 'base_only_steps': ('INT', {'default': 2, 'min': 0, 'max': 10000}), 'denoise': ('FLOAT', {'default': 1.0, 'min': 0.0, 'max': 1.0, 'step': 0.01}), 'samples': ('LATENT',), 'base_sampler': ('KSAMPLER_ADVANCED',), 'regional_prompts': ('REGIONAL_PROMPTS',), 'overlap_factor': ('INT', {'default': 10, 'min': 0, 'max': 10000}), 'restore_latent': ('BOOLEAN', {'default': True, 'label_on': 'enabled', 'label_off': 'disabled'}), 'additional_mode': (['DISABLE', 'ratio additional', 'ratio between'], {'default': 'ratio between'}), 'additional_sampler': (['AUTO', 'euler', 'heun', 'heunpp2', 'dpm_2', 'dpm_fast', 'dpmpp_2m', 'ddpm'],), 'additional_sigma_ratio': ('FLOAT', {'default': 0.3, 'min': 0.0, 'max': 1.0, 'step': 0.01})}, 'hidden': {'unique_id': 'UNIQUE_ID'}}
    RETURN_TYPES = ('LATENT',)
    FUNCTION = 'doit'
    CATEGORY = 'ImpactPack/Regional'

    @staticmethod
    def mask_erosion(samples, mask, grow_mask_by):
        mask = mask.clone()
        w = samples['samples'].shape[3]
        h = samples['samples'].shape[2]
        mask2 = torch.nn.functional.interpolate(mask.reshape((-1, 1, mask.shape[-2], mask.shape[-1])), size=(w, h), mode='bilinear')
        if grow_mask_by == 0:
            mask_erosion = mask2
        else:
            kernel_tensor = torch.ones((1, 1, grow_mask_by, grow_mask_by))
            padding = math.ceil((grow_mask_by - 1) / 2)
            mask_erosion = torch.clamp(torch.nn.functional.conv2d(mask2.round(), kernel_tensor, padding=padding), 0, 1)
        return mask_erosion[:, :, :w, :h].round()

    def doit(self, seed, seed_2nd, seed_2nd_mode, steps, base_only_steps, denoise, samples, base_sampler, regional_prompts, overlap_factor, restore_latent, additional_mode, additional_sampler, additional_sigma_ratio, unique_id=None):
        if restore_latent:
            latent_compositor = nodes.NODE_CLASS_MAPPINGS['LatentCompositeMasked']()
        else:
            latent_compositor = None
        masks = [regional_prompt.mask.numpy() for regional_prompt in regional_prompts]
        masks = [np.ceil(mask).astype(np.int32) for mask in masks]
        combined_mask = torch.from_numpy(np.bitwise_or.reduce(masks))
        inv_mask = torch.where(combined_mask == 0, torch.tensor(1.0), torch.tensor(0.0))
        adv_steps = int(steps / denoise)
        start_at_step = adv_steps - steps
        region_len = len(regional_prompts)
        total = steps * region_len
        leftover_noise = False
        if base_only_steps > 0:
            if seed_2nd_mode == 'ignore':
                leftover_noise = True
            samples = base_sampler.sample_advanced(True, seed, adv_steps, samples, start_at_step, start_at_step + base_only_steps, leftover_noise, recovery_mode='DISABLE')
        if seed_2nd_mode == 'seed+seed_2nd':
            seed += seed_2nd
            if seed > 1125899906842624:
                seed = seed - 1125899906842624
        elif seed_2nd_mode == 'seed-seed_2nd':
            seed -= seed_2nd
            if seed < 0:
                seed += 1125899906842624
        elif seed_2nd_mode != 'ignore':
            seed = seed_2nd
        new_latent_image = samples.copy()
        base_latent_image = None
        if not leftover_noise:
            add_noise = True
        else:
            add_noise = False
        for i in range(start_at_step + base_only_steps, adv_steps):
            core.update_node_status(unique_id, f'{i}/{steps} steps  |         ', (i - start_at_step) * region_len / total)
            new_latent_image['noise_mask'] = inv_mask
            new_latent_image = base_sampler.sample_advanced(add_noise, seed, adv_steps, new_latent_image, i, i + 1, True, recovery_mode=additional_mode, recovery_sampler=additional_sampler, recovery_sigma_ratio=additional_sigma_ratio)
            if restore_latent:
                if 'noise_mask' in new_latent_image:
                    del new_latent_image['noise_mask']
                base_latent_image = new_latent_image.copy()
            j = 1
            for regional_prompt in regional_prompts:
                if restore_latent:
                    new_latent_image = base_latent_image.copy()
                core.update_node_status(unique_id, f'{i}/{steps} steps  |  {j}/{region_len}', ((i - start_at_step) * region_len + j) / total)
                region_mask = regional_prompt.get_mask_erosion(overlap_factor).squeeze(0).squeeze(0)
                new_latent_image['noise_mask'] = region_mask
                new_latent_image = regional_prompt.sampler.sample_advanced(False, seed, adv_steps, new_latent_image, i, i + 1, True, recovery_mode=additional_mode, recovery_sampler=additional_sampler, recovery_sigma_ratio=additional_sigma_ratio)
                if restore_latent:
                    del new_latent_image['noise_mask']
                    base_latent_image = latent_compositor.composite(base_latent_image, new_latent_image, 0, 0, False, region_mask)[0]
                    new_latent_image = base_latent_image
                j += 1
            add_noise = False
        core.update_node_status(unique_id, f'finalize')
        if base_latent_image is not None:
            new_latent_image = base_latent_image
        else:
            base_latent_image = new_latent_image
        new_latent_image['noise_mask'] = inv_mask
        new_latent_image = base_sampler.sample_advanced(False, seed, adv_steps, new_latent_image, adv_steps, adv_steps + 1, False, recovery_mode=additional_mode, recovery_sampler=additional_sampler, recovery_sigma_ratio=additional_sigma_ratio)
        core.update_node_status(unique_id, f'{steps}/{steps} steps', total)
        core.update_node_status(unique_id, '', None)
        if restore_latent:
            new_latent_image = base_latent_image
        if 'noise_mask' in new_latent_image:
            del new_latent_image['noise_mask']
        return (new_latent_image,)