diff --git a/README.MD b/README.MD index d4b95c4..30c1468 100644 --- a/README.MD +++ b/README.MD @@ -13,6 +13,7 @@ Nodes are divided into four groups according to their functions: LayerStyle, Lay [中文说明点这里](./README_CN.MD) ## Update +* Commit [WaterColor](#WaterColor) and [SkinBeauty](#SkinBeauty) nodes。These are image filters that generate watercolor and skin smoothness effects. * Commit [ImageShift](#ImageShift) node to shift the image and output a displacement seam mask, making it convenient to create continuous textures. * Commit [ImageMaskScaleAs](#ImageMaskScaleAs) node to adjust the image or mask size based on the reference image. * Commit [ImageScaleRestore](#ImageScaleRestore) node to work with CropByMask for local upscale and repair works. @@ -600,6 +601,28 @@ This node allows any type of input. ![image](image/layerfilter_nodes.png) + +### SkinBeauty +Make the skin look smoother. +![image](image/skin_beauty_example.png) + +Node options: +![image](image/skin_beauty_node.png) +* smooth: Skin smoothness. +* threshold: Smooth range. the larger the range with the smaller value. +* opacity: The opacity of the smoothness. + + +### WaterColor +Watercolor painting effect +![image](image/water_color_example.png) + +Node option: +![image](image/water_color_node.png) +* line_density: The black line density. +* opacity: The opacity of watercolor effects. + + ### SoftLight Soft light effect, the bright highlights on the screen appear blurry. ![image](image/soft_light_example.png) diff --git a/README_CN.MD b/README_CN.MD index 3328de4..aa7d132 100644 --- a/README_CN.MD +++ b/README_CN.MD @@ -10,6 +10,7 @@ ComfyUI * [LayerFilter](#LayerFilter)½Úµã×éÌṩͼÏñЧ¹ûÂ˾µ¡£ ## ¸üÐÂ˵Ã÷ +* Ìí¼Ó[WaterColor](#WaterColor) ºÍ [SkinBeauty](#SkinBeauty) ½Úµã¡£ÕâÊÇÁ½¸öͼÏñÂ˾µ£¬Éú³ÉË®²Ê»­ºÍĥƤЧ¹û¡£ * Ìí¼Ó[ImageShift](#ImageShift) ½Úµã£¬Ê¹Í¼Æ¬²úÉúÎ»ÒÆ£¬¿ÉÊä³öÎ»ÒÆ½Ó·ìÕÚÕÖ£¬·½±ãÖÆ×÷Á¬ÐøÌùͼ¡£ * Ìí¼Ó[ImageMaskScaleAs](#ImageMaskScaleAs) ½Úµã£¬¿É¸ù¾Ý²Î¿¼Í¼Æ¬µ÷ÕûͼÏñ»òÕÚÕÖ´óС¡£ * Ìí¼Ó[ImageScaleRestore](#ImageScaleRestore) ½Úµã£¬ÓÃÓÚÅäºÏCropByMask½øÐоֲ¿·Å´óÐÞ¸´¹¤×÷¡£ @@ -586,6 +587,26 @@ mask # LayerFilter ![image](image/layerfilter_nodes.png) +### SkinBeauty +ĥƤЧ¹û¡£ +![image](image/skin_beauty_example.png) + +½ÚµãÑ¡Ïî˵Ã÷: +![image](image/skin_beauty_node.png) +* smooth: Ƥ·ôƽ»¬¶È¡£ +* threshold: ĥƤ·¶Î§¡£ÊýֵԽС·¶Î§Ô½´ó¡£ +* opacity: ĥƤµÄ²»Í¸Ã÷¶È¡£ + + +### WaterColor +Ë®²Ê»­Ð§¹û¡£ +![image](image/water_color_example.png) + +½ÚµãÑ¡Ïî˵Ã÷: +![image](image/water_color_node.png) +* line_density: ÏßÌõÃܶȡ£ +* opacity: Ë®²ÊЧ¹ûµÄ²»Í¸Ã÷¶È¡£ + ### SoftLight Èá¹âЧ¹û¡£ @@ -642,7 +663,6 @@ LayerFilter: * Emboss * Contour * Findedge -* PhotoStyle * ColorMap diff --git a/image/skin_beauty_example.png b/image/skin_beauty_example.png new file mode 100644 index 0000000..d786833 Binary files /dev/null and b/image/skin_beauty_example.png differ diff --git a/image/skin_beauty_node.png b/image/skin_beauty_node.png new file mode 100644 index 0000000..d5d174b Binary files /dev/null and b/image/skin_beauty_node.png differ diff --git a/image/water_color_example.png b/image/water_color_example.png new file mode 100644 index 0000000..8b26709 Binary files /dev/null and b/image/water_color_example.png differ diff --git a/image/water_color_node.png b/image/water_color_node.png new file mode 100644 index 0000000..563130a Binary files /dev/null and b/image/water_color_node.png differ diff --git a/py/imagefunc.py b/py/imagefunc.py index 4b5a862..9b1a28e 100644 --- a/py/imagefunc.py +++ b/py/imagefunc.py @@ -430,6 +430,28 @@ def calculate_mean_std(image:Image): std = np.hstack(np.around(std, decimals=2)) return mean, std +def image_watercolor(image:Image, level:int=50) -> Image: + img = pil2cv2(image) + img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB) + factor = (level / 128.0) ** 2 + sigmaS= int((image.width + image.height) / 5.0 * factor) + 1 + sigmaR = sigmaS / 32.0 * factor + 0.002 + img_color = cv2.stylization(img, sigma_s=sigmaS, sigma_r=sigmaR) + ret_image = cv2.cvtColor(img_color, cv2.COLOR_BGR2RGB) + return cv22pil(ret_image) + + +def image_beauty(image:Image, level:int=50) -> Image: + img = pil2cv2(image) + img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB) + factor = (level / 50.0)**2 + d = int((image.width + image.height) / 256 * factor) + sigmaColor = int((image.width + image.height) / 256 * factor) + sigmaSpace = int((image.width + image.height) / 160 * factor) + img_bit = cv2.bilateralFilter(src=img, d=d, sigmaColor=sigmaColor, sigmaSpace=sigmaSpace) + ret_image = cv2.cvtColor(img_bit, cv2.COLOR_BGR2RGB) + return cv22pil(ret_image) + '''Mask Functions''' def expand_mask(mask:torch.Tensor, grow:int, blur:int) -> torch.Tensor: @@ -458,8 +480,7 @@ def expand_mask(mask:torch.Tensor, grow:int, blur:int) -> torch.Tensor: return ret_mask def mask_invert(mask:torch.Tensor) -> torch.Tensor: - _image = mask2image(mask) - return image2mask(ImageChops.invert(_image)) + return 1 - mask def subtract_mask(masks_a:torch.Tensor, masks_b:torch.Tensor) -> torch.Tensor: return torch.clamp(masks_a - masks_b, 0, 255) diff --git a/py/skin_beauty.py b/py/skin_beauty.py new file mode 100644 index 0000000..46ad12b --- /dev/null +++ b/py/skin_beauty.py @@ -0,0 +1,51 @@ +from .imagefunc import * + +class SkinBeauty: + + def __init__(self): + pass + + @classmethod + def INPUT_TYPES(self): + + return { + "required": { + "image": ("IMAGE", ), + "smooth": ("INT", {"default": 20, "min": 1, "max": 64, "step": 1}), # 磨皮程度 + "threshold": ("INT", {"default": -10, "min": -255, "max": 255, "step": 1}), # 高光阈值 + "opacity": ("INT", {"default": 100, "min": 0, "max": 100, "step": 1}), # é€�明度 + }, + "optional": { + } + } + + RETURN_TYPES = ("IMAGE", "MASK") + RETURN_NAMES = ("image", "beauty_mask") + FUNCTION = 'skin_beauty' + CATEGORY = '😺dzNodes/LayerFilter' + OUTPUT_NODE = True + + def skin_beauty(self, image, smooth, threshold, opacity + ): + + _canvas = tensor2pil(image).convert('RGB') + _R, _, _, _ = image_channel_split(_canvas, mode='RGB') + _otsumask = gray_threshold(_R, otsu=True) + _removebkgd = remove_background(_R, _otsumask, '#000000') + auto_threshold = get_image_bright_average(_removebkgd) - 16 + light_mask = gray_threshold(_canvas, auto_threshold + threshold) + blur = int((_canvas.width + _canvas.height) / 2000 * smooth) + _image = image_beauty(_canvas, level=smooth) + _image = gaussian_blur(_image, blur) + _image = chop_image(_canvas, _image, 'normal', opacity) + _canvas.paste(_image, mask=gaussian_blur(light_mask, blur).convert('L')) + + return (pil2tensor(_canvas), image2mask(light_mask),) + +NODE_CLASS_MAPPINGS = { + "LayerFilter: SkinBeauty": SkinBeauty +} + +NODE_DISPLAY_NAME_MAPPINGS = { + "LayerFilter: SkinBeauty": "LayerFilter: SkinBeauty" +} \ No newline at end of file diff --git a/py/soft_light.py b/py/soft_light.py index fb06890..833b1ca 100644 --- a/py/soft_light.py +++ b/py/soft_light.py @@ -11,7 +11,6 @@ class SoftLight: return { "required": { "image": ("IMAGE", ), # - "soft": ("FLOAT", {"default": 1, "min": 0.2, "max": 10, "step": 0.01}), # 模糊 "threshold": ("INT", {"default": -10, "min": -255, "max": 255, "step": 1}), # 高光阈值 "opacity": ("INT", {"default": 100, "min": 0, "max": 100, "step": 1}), # é€�明度 diff --git a/py/water_color.py b/py/water_color.py new file mode 100644 index 0000000..27a8123 --- /dev/null +++ b/py/water_color.py @@ -0,0 +1,42 @@ +from .imagefunc import * + +class WaterColor: + + def __init__(self): + pass + + @classmethod + def INPUT_TYPES(self): + + return { + "required": { + "image": ("IMAGE", ), + "line_density": ("INT", {"default": 50, "min": 1, "max": 100, "step": 1}), # é€�明度 + "opacity": ("INT", {"default": 100, "min": 0, "max": 100, "step": 1}), # é€�明度 + }, + "optional": { + } + } + + RETURN_TYPES = ("IMAGE",) + RETURN_NAMES = ("image",) + FUNCTION = 'water_color' + CATEGORY = '😺dzNodes/LayerFilter' + OUTPUT_NODE = True + + def water_color(self, image, line_density, opacity + ): + + _canvas = tensor2pil(image).convert('RGB') + _image = image_watercolor(_canvas, level=101-line_density) + ret_image = chop_image(_canvas, _image, 'normal', opacity) + + return (pil2tensor(ret_image),) + +NODE_CLASS_MAPPINGS = { + "LayerFilter: WaterColor": WaterColor +} + +NODE_DISPLAY_NAME_MAPPINGS = { + "LayerFilter: WaterColor": "LayerFilter: WaterColor" +} \ No newline at end of file