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

6.6 KiB

Documentation

WAS_Mask_Paste_Region 节点旨在通过将裁剪区域粘贴到基础遮罩上来操作图像遮罩。它处理混合和锐化,以确保将粘贴区域无缝集成到原始遮罩中。该节点特别适用于需要精确控制遮罩修改的应用,例如在图像编辑或图形设计中。

Input types

Required

  • mask
    • 遮罩参数是节点的关键输入,代表将裁剪区域粘贴到的基础遮罩。它对于确定输出遮罩的最终外观至关重要。
    • Comfy dtype: MASK
    • Python dtype: torch.Tensor
  • crop_mask
    • crop_mask 参数定义了要粘贴到基础遮罩上的区域。它在整体操作中扮演着重要角色,因为它直接影响结果遮罩的内容和结构。
    • Comfy dtype: MASK
    • Python dtype: torch.Tensor

Optional

  • crop_data
    • crop_data 参数提供了要裁剪和粘贴的区域的尺寸和位置信息。它是可选的,但使用时会影响粘贴区域与基础遮罩的对齐方式。
    • Comfy dtype: CROP_DATA
    • Python dtype: Tuple[int, Tuple[int, int, int, int]]
  • crop_blending
    • crop_blending 参数控制将裁剪区域粘贴到基础遮罩时的混合强度。它允许微调粘贴区域与周围遮罩之间的过渡。
    • Comfy dtype: FLOAT
    • Python dtype: float
  • crop_sharpening
    • crop_sharpening 参数确定在粘贴之前要应用于裁剪区域的锐化级别。它可以增强粘贴区域的细节,以获得更好的视觉清晰度。
    • Comfy dtype: INT
    • Python dtype: int

Output types

  • result_mask
    • result_mask 输出参数代表粘贴操作后的最终遮罩。它是一个关键的输出,因为它反映了节点对原始遮罩与裁剪区域的操纵。
    • Comfy dtype: MASK
    • Python dtype: torch.Tensor
  • result_crop_mask
    • result_crop_mask 输出参数提供了所有处理步骤之后粘贴区域的遮罩。对于需要单独操作粘贴区域遮罩的应用来说,它是重要的。
    • Comfy dtype: MASK
    • Python dtype: torch.Tensor

Usage tips

  • Infra type: CPU

Source code

class WAS_Mask_Paste_Region:

    def __init__(self):
        pass

    @classmethod
    def INPUT_TYPES(cls):
        return {'required': {'mask': ('MASK',), 'crop_mask': ('MASK',), 'crop_data': ('CROP_DATA',), 'crop_blending': ('FLOAT', {'default': 0.25, 'min': 0.0, 'max': 1.0, 'step': 0.01}), 'crop_sharpening': ('INT', {'default': 0, 'min': 0, 'max': 3, 'step': 1})}}
    RETURN_TYPES = ('MASK', 'MASK')
    FUNCTION = 'mask_paste_region'
    CATEGORY = 'WAS Suite/Image/Masking'

    def mask_paste_region(self, mask, crop_mask, crop_data=None, crop_blending=0.25, crop_sharpening=0):
        if crop_data == False:
            cstr('No valid crop data found!').error.print()
            return (pil2mask(Image.new('L', (512, 512), 0)).unsqueeze(0).unsqueeze(1), pil2mask(Image.new('L', (512, 512), 0)).unsqueeze(0).unsqueeze(1))
        mask_pil = Image.fromarray(np.clip(255.0 * mask.cpu().numpy().squeeze(), 0, 255).astype(np.uint8))
        mask_crop_pil = Image.fromarray(np.clip(255.0 * crop_mask.cpu().numpy().squeeze(), 0, 255).astype(np.uint8))
        (result_mask, result_crop_mask) = self.paste_image(mask_pil, mask_crop_pil, crop_data, crop_blending, crop_sharpening)
        return (pil2mask(result_mask).unsqueeze(0).unsqueeze(1), pil2mask(result_crop_mask).unsqueeze(0).unsqueeze(1))

    def paste_image(self, image, crop_image, crop_data, blend_amount=0.25, sharpen_amount=1):

        def lingrad(size, direction, white_ratio):
            image = Image.new('RGB', size)
            draw = ImageDraw.Draw(image)
            if direction == 'vertical':
                black_end = int(size[1] * (1 - white_ratio))
                range_start = 0
                range_end = size[1]
                range_step = 1
                for y in range(range_start, range_end, range_step):
                    color_ratio = y / size[1]
                    if y <= black_end:
                        color = (0, 0, 0)
                    else:
                        color_value = int((y - black_end) / (size[1] - black_end) * 255)
                        color = (color_value, color_value, color_value)
                    draw.line([(0, y), (size[0], y)], fill=color)
            elif direction == 'horizontal':
                black_end = int(size[0] * (1 - white_ratio))
                range_start = 0
                range_end = size[0]
                range_step = 1
                for x in range(range_start, range_end, range_step):
                    color_ratio = x / size[0]
                    if x <= black_end:
                        color = (0, 0, 0)
                    else:
                        color_value = int((x - black_end) / (size[0] - black_end) * 255)
                        color = (color_value, color_value, color_value)
                    draw.line([(x, 0), (x, size[1])], fill=color)
            return image.convert('L')
        (crop_size, (left, top, right, bottom)) = crop_data
        crop_image = crop_image.resize(crop_size)
        if sharpen_amount > 0:
            for _ in range(int(sharpen_amount)):
                crop_image = crop_image.filter(ImageFilter.SHARPEN)
        blended_image = Image.new('RGBA', image.size, (0, 0, 0, 255))
        blended_mask = Image.new('L', image.size, 0)
        crop_padded = Image.new('RGBA', image.size, (0, 0, 0, 0))
        blended_image.paste(image, (0, 0))
        crop_padded.paste(crop_image, (left, top))
        crop_mask = Image.new('L', crop_image.size, 0)
        if top > 0:
            gradient_image = ImageOps.flip(lingrad(crop_image.size, 'vertical', blend_amount))
            crop_mask = ImageChops.screen(crop_mask, gradient_image)
        if left > 0:
            gradient_image = ImageOps.mirror(lingrad(crop_image.size, 'horizontal', blend_amount))
            crop_mask = ImageChops.screen(crop_mask, gradient_image)
        if right < image.width:
            gradient_image = lingrad(crop_image.size, 'horizontal', blend_amount)
            crop_mask = ImageChops.screen(crop_mask, gradient_image)
        if bottom < image.height:
            gradient_image = lingrad(crop_image.size, 'vertical', blend_amount)
            crop_mask = ImageChops.screen(crop_mask, gradient_image)
        crop_mask = ImageOps.invert(crop_mask)
        blended_mask.paste(crop_mask, (left, top))
        blended_mask = blended_mask.convert('L')
        blended_image.paste(crop_padded, (0, 0), blended_mask)
        return (ImageOps.invert(blended_image.convert('RGB')).convert('L'), ImageOps.invert(blended_mask.convert('RGB')).convert('L'))