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.

+
+### SkinBeauty
+Make the skin look smoother.
+
+
+Node options:
+
+* smooth: Skin smoothness.
+* threshold: Smooth range. the larger the range with the smaller value.
+* opacity: The opacity of the smoothness.
+
+
+### WaterColor
+Watercolor painting effect
+
+
+Node option:
+
+* line_density: The black line density.
+* opacity: The opacity of watercolor effects.
+
+
### SoftLight
Soft light effect, the bright highlights on the screen appear blurry.

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

+### SkinBeauty
+ĥƤЧ¹û¡£
+
+
+½ÚµãÑ¡Ïî˵Ã÷:
+
+* smooth: Ƥ·ôƽ»¬¶È¡£
+* threshold: ĥƤ·¶Î§¡£ÊýֵԽС·¶Î§Ô½´ó¡£
+* opacity: ĥƤµÄ²»Í¸Ã÷¶È¡£
+
+
+### WaterColor
+Ë®²Ê»Ð§¹û¡£
+
+
+½ÚµãÑ¡Ïî˵Ã÷:
+
+* 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