Commit WaterColor and SkinBeauty nodes

This commit is contained in:
chflame163
2024-02-03 23:32:37 +08:00
parent 61ed17a16e
commit 023d10d524
10 changed files with 160 additions and 4 deletions
+23
View File
@@ -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)
### <a id="table1">SkinBeauty</a>
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.
### <a id="table1">WaterColor</a>
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.
### <a id="table1">SoftLight</a>
Soft light effect, the bright highlights on the screen appear blurry.
![image](image/soft_light_example.png)
+21 -1
View File
@@ -10,6 +10,7 @@ ComfyUI
* [LayerFilter](#LayerFilter)节点组提供图像效果滤镜。
## 更新说明
* 添加[WaterColor](#WaterColor) 和 [SkinBeauty](#SkinBeauty) 节点。这是两个图像滤镜,生成水彩画和磨皮效果。
* 添加[ImageShift](#ImageShift) 节点,使图片产生位移,可输出位移接缝遮罩,方便制作连续贴图。
* 添加[ImageMaskScaleAs](#ImageMaskScaleAs) 节点,可根据参考图片调整图像或遮罩大小。
* 添加[ImageScaleRestore](#ImageScaleRestore) 节点,用于配合CropByMask进行局部放大修复工作。
@@ -586,6 +587,26 @@ mask
# <a id="table1">LayerFilter</a>
![image](image/layerfilter_nodes.png)
### <a id="table1">SkinBeauty</a>
磨皮效果。
![image](image/skin_beauty_example.png)
节点选项说明:
![image](image/skin_beauty_node.png)
* smooth: 皮肤平滑度。
* threshold: 磨皮范围。数值越小范围越大。
* opacity: 磨皮的不透明度。
### <a id="table1">WaterColor</a>
水彩画效果。
![image](image/water_color_example.png)
节点选项说明:
![image](image/water_color_node.png)
* line_density: 线条密度。
* opacity: 水彩效果的不透明度。
### <a id="table1">SoftLight</a>
柔光效果。
@@ -642,7 +663,6 @@ LayerFilter:
* Emboss
* Contour
* Findedge
* PhotoStyle
* ColorMap
Binary file not shown.

After

Width:  |  Height:  |  Size: 2.7 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 126 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 97 KiB

+23 -2
View File
@@ -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)
+51
View File
@@ -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"
}
-1
View File
@@ -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}), # 透明度
+42
View File
@@ -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"
}