Commit WaterColor and SkinBeauty nodes
This commit is contained in:
@@ -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.
|
||||

|
||||
|
||||
|
||||
|
||||
### <a id="table1">SkinBeauty</a>
|
||||
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.
|
||||
|
||||
|
||||
### <a id="table1">WaterColor</a>
|
||||
Watercolor painting effect
|
||||

|
||||
|
||||
Node option:
|
||||

|
||||
* 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.
|
||||

|
||||
|
||||
+21
-1
@@ -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>
|
||||

|
||||
|
||||
### <a id="table1">SkinBeauty</a>
|
||||
磨皮效果。
|
||||

|
||||
|
||||
节点选项说明:
|
||||

|
||||
* smooth: 皮肤平滑度。
|
||||
* threshold: 磨皮范围。数值越小范围越大。
|
||||
* opacity: 磨皮的不透明度。
|
||||
|
||||
|
||||
### <a id="table1">WaterColor</a>
|
||||
水彩画效果。
|
||||

|
||||
|
||||
节点选项说明:
|
||||

|
||||
* 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
@@ -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)
|
||||
|
||||
@@ -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"
|
||||
}
|
||||
@@ -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}), # 透明度
|
||||
|
||||
@@ -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"
|
||||
}
|
||||
Reference in New Issue
Block a user