Add GradientOverlay, ColorOverlay nodes and add invalid mask input judgment.
@@ -7,6 +7,7 @@ The completed nodes are as follows, and further work is in progress.
|
||||
[中文说明点这里](./README_CN.MD)
|
||||
|
||||
## Update
|
||||
* Add GradientOverlay and ColorOverlay nodes. Add invalid mask input judgment and ignore it when invalid mask is input.
|
||||
* Commit InnerGlow, InnerShadow and MotionBlur nodes.
|
||||
* Renaming all completed nodes, the nodes are divided into 4 groups:LayerStyle, LayerMask, LayerUtility, LayerFilter. workflows containing old version nodes need to be manually replaced with new version nodes.
|
||||
* OuterGlow node has undergone significant modifications by adding options for **_brightness_**, **_light_color_**, and **_glow_color_**.
|
||||
@@ -21,8 +22,9 @@ The completed nodes are as follows, and further work is in progress.
|
||||
* Commit DropShadow node.
|
||||
|
||||
# LayerStyle
|
||||

|
||||
|
||||
### DropShadow:
|
||||
### DropShadow
|
||||
Generate shadow
|
||||

|
||||
|
||||
@@ -41,7 +43,7 @@ Node options:
|
||||
* blur: Shadow blur level.
|
||||
* shadow_color<sup>4</sup>: Shadow color.
|
||||
|
||||
### OuterGlow:
|
||||
### OuterGlow
|
||||
Generate outer glow
|
||||

|
||||
|
||||
@@ -59,7 +61,7 @@ Node options:
|
||||
* light_color<sup>4</sup>: Center part color of glow.
|
||||
* glow_color<sup>4</sup>: Edge part color of glow.
|
||||
|
||||
### InnerShadow:
|
||||
### InnerShadow
|
||||
Generate inner shadow
|
||||

|
||||
|
||||
@@ -77,7 +79,7 @@ Node options:
|
||||
* blur: Shadow blur level.
|
||||
* shadow_color<sup>4</sup>: Shadow color.
|
||||
|
||||
### InnerGlow:
|
||||
### InnerGlow
|
||||
Generate inner glow
|
||||

|
||||
|
||||
@@ -96,7 +98,7 @@ Node options:
|
||||
* glow_color<sup>4</sup>: Edge part color of glow.
|
||||
|
||||
|
||||
### Stroke:
|
||||
### Stroke
|
||||
Generate a stroke of layer。
|
||||

|
||||
|
||||
@@ -112,6 +114,37 @@ Node options:
|
||||
* stroke_width: Stroke width.
|
||||
* stroke_color<sup>4</sup>: Stroke color, described in hexadecimal RGB format.
|
||||
|
||||
### GradientOverlay
|
||||
Generate gradient overlay
|
||||

|
||||
|
||||
Node options:
|
||||
* background_image<sup>1</sup>: The background image.
|
||||
* layer_image<sup>1</sup>: Layer image for composite.
|
||||
* layer_mask<sup>1,2</sup>: Mask for layer_image.
|
||||
* invert_mask: Whether to reverse the mask.
|
||||
* blend_mode<sup>3</sup>: Blending mode of gradient.
|
||||
* opacity: Opacity of stroke.
|
||||
* start_color: Color at the beginning of the gradient.
|
||||
* start_alpha: Transparency at the beginning of the gradient.
|
||||
* end_color: Color at the end of the gradient.
|
||||
* end_alpha: Transparency at the end of the gradient.
|
||||
* angle: Gradient rotation angle.
|
||||
|
||||
### ColorOverlay
|
||||
Generate color overlay
|
||||

|
||||
|
||||
Node options:
|
||||

|
||||
* background_image<sup>1</sup>: The background image.
|
||||
* layer_image<sup>1</sup>: Layer image for composite.
|
||||
* layer_mask<sup>1,2</sup>: Mask for layer_image.
|
||||
* invert_mask: Whether to reverse the mask.
|
||||
* blend_mode<sup>3</sup>: Blending mode of color.
|
||||
* opacity: Opacity of stroke.
|
||||
* color: Color of overlay.
|
||||
|
||||
|
||||
#### Annotation for LayerStyle nodes:
|
||||
<sup>1</sup> The layer_image, layer_mask and the background_image(if have input), These three items must be of the same size.。
|
||||
@@ -123,6 +156,7 @@ Node options:
|
||||
<sup>4</sup> The RGB color described by hexadecimal RGB format, like '#FA3D86'.
|
||||
|
||||
# LayerMask
|
||||

|
||||
|
||||
### MaskPreview:
|
||||
Preview the input mask
|
||||
@@ -134,6 +168,7 @@ Invert the mask
|
||||
|
||||
|
||||
# LayerUtility
|
||||

|
||||
|
||||
### ImageBlend:
|
||||
Composit layer image and background image, multiple blend modes are available for option, and transparency can be set.
|
||||
@@ -169,6 +204,7 @@ Output Type:
|
||||
* string
|
||||
|
||||
# LayerFilter
|
||||

|
||||
|
||||
### MotionBlur:
|
||||
Make the image motion blur
|
||||
@@ -183,8 +219,26 @@ image Some JSON workflow files in the workflow directory, that is example for Co
|
||||
|
||||
## The following tasks are in the planned list
|
||||
|
||||
* Color Overlay
|
||||
* Gradient Overlay
|
||||
* LayerUtility:
|
||||
imageblend advance
|
||||
color image
|
||||
gradient image
|
||||
|
||||
* LayerMask:
|
||||
text mask
|
||||
gradient mask
|
||||
mask grow&blur
|
||||
mask subtract
|
||||
mask combine
|
||||
mask diffrent
|
||||
mask gradient
|
||||
mask softedge
|
||||
|
||||
* LayerFilter:
|
||||
bulr
|
||||
emboss
|
||||
contour
|
||||
findedge
|
||||
|
||||
|
||||
## How to install
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
# ComfyUI Layer Style:
|
||||
# ComfyUI Layer Style
|
||||
ComfyUI插件,通过一组节点实现仿照Adobe Photoshop的图层样式。
|
||||
|
||||
已完成的节点如下,后续工作进行中。
|
||||

|
||||
|
||||
## 更新说明:
|
||||
## 更新说明:
|
||||
* 添加GradientOverlay,ColorOverlay节点。增加无效mask输入判断,当输入无效mask时将其忽略。
|
||||
* 添加InnerGlow, InnerShadow, MotionBlur节点。
|
||||
* 所有已完成的节点重新命名,节点分为4组:LayerStyle, LayerMask, LayerUtility, LayerFilter。因为重新命名,包含旧版节点的工作流需手动替换新版节点。
|
||||
* OuterGlow节点修改,增加亮度、灯光颜色、辉光颜色选项。
|
||||
@@ -18,12 +19,13 @@ ComfyUI
|
||||
* 首个节点DropShadow提交。
|
||||
|
||||
# LayerStyle
|
||||

|
||||
|
||||
### DropShadow:
|
||||
### DropShadow
|
||||
生成阴影。
|
||||

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

|
||||
* background_image<sup>1</sup>: 背景图像。
|
||||
* layer_image<sup>1</sup>: 用于合成的层图像。
|
||||
@@ -34,14 +36,14 @@ ComfyUI
|
||||
* distance_x: 阴影的水平方向偏移量。
|
||||
* distance_y: 阴影的垂直方向偏移量。
|
||||
* grow: 阴影扩张幅度。
|
||||
* blur:阴影模糊程度。
|
||||
* blur:阴影模糊程度。
|
||||
* shadow_color<sup>4</sup>: 阴影颜色。
|
||||
|
||||
### OuterGlow:
|
||||
### OuterGlow
|
||||
生成外发光。
|
||||

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

|
||||
* background_image<sup>1</sup>: 背景图像。
|
||||
* layer_image<sup>1</sup>: 用于合成的层图像。
|
||||
@@ -51,15 +53,15 @@ ComfyUI
|
||||
* opacity: 发光的不透明度。
|
||||
* brightness: 发光亮度。
|
||||
* glow_range: 发光范围。
|
||||
* blur:发光模糊程度。
|
||||
* blur:发光模糊程度。
|
||||
* light_color<sup>4</sup>: 发光中心颜色。
|
||||
* glow_colo<sup>4</sup>: 辉光外围颜色。
|
||||
|
||||
### InnerShadow:
|
||||
### InnerShadow
|
||||
生成内阴影。
|
||||

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

|
||||
* background_image<sup>1</sup>: 背景图像。
|
||||
* layer_image<sup>1</sup>: 用于合成的层图像。
|
||||
@@ -70,14 +72,14 @@ ComfyUI
|
||||
* distance_x: 阴影的水平方向偏移量。
|
||||
* distance_y: 阴影的垂直方向偏移量。
|
||||
* grow: 阴影扩张幅度。
|
||||
* blur:阴影模糊程度。
|
||||
* blur:阴影模糊程度。
|
||||
* shadow_color<sup>4</sup>: 阴影颜色。
|
||||
|
||||
### InnerGlow:
|
||||
### InnerGlow
|
||||
生成内发光。
|
||||

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

|
||||
* background_image<sup>1</sup>: 背景图像。
|
||||
* layer_image<sup>1</sup>: 用于合成的层图像。
|
||||
@@ -87,15 +89,15 @@ ComfyUI
|
||||
* opacity: 发光的不透明度。
|
||||
* brightness: 发光亮度。
|
||||
* glow_range: 发光范围。
|
||||
* blur:发光模糊程度。
|
||||
* blur:发光模糊程度。
|
||||
* light_color<sup>4</sup>: 发光中心颜色。
|
||||
* glow_colo<sup>4</sup>: 辉光外围颜色。
|
||||
|
||||
### Stroke:
|
||||
### Stroke
|
||||
生成描边。
|
||||

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

|
||||
* background_image<sup>1</sup>: 背景图像。
|
||||
* layer_image<sup>1</sup>: 用于合成的层图像。
|
||||
@@ -103,18 +105,51 @@ ComfyUI
|
||||
* invert_mask: 是否反转遮罩。
|
||||
* blend_mode<sup>3</sup>: 描边的混合模式。
|
||||
* opacity: 不透明度。
|
||||
* stroke_grow<sup></sup>: 描边扩张/收缩幅度,正值是扩张,负值是收缩。
|
||||
* stroke_width<sup></sup>:描边宽度。
|
||||
* stroke_grow: 描边扩张/收缩幅度,正值是扩张,负值是收缩。
|
||||
* stroke_width: 描边宽度。
|
||||
* stroke_color<sup>4</sup>: 描边颜色。
|
||||
|
||||
### GradientOverlay
|
||||
渐变覆盖
|
||||

|
||||
|
||||
#### 节点选项注解:
|
||||
节点选项说明:
|
||||

|
||||
* background_image<sup>1</sup>: 背景图像。
|
||||
* layer_image<sup>1</sup>: 用于合成的层图像。
|
||||
* layer_mask<sup>1,2</sup>: 层图像的遮罩。
|
||||
* invert_mask: 是否反转遮罩。
|
||||
* blend_mode<sup>3</sup>: 描边的混合模式。
|
||||
* opacity: 不透明度。
|
||||
* start_color: 渐变开始端的颜色。
|
||||
* start_alpha: 渐变开始端的透明度。
|
||||
* end_color: 渐变结束端的颜色。
|
||||
* end_alpha: 渐变结束端的透明度。
|
||||
* angle: 渐变旋转角度。
|
||||
|
||||
### ColorOverlay
|
||||
颜色覆盖
|
||||

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

|
||||
* background_image<sup>1</sup>: 背景图像。
|
||||
* layer_image<sup>1</sup>: 用于合成的层图像。
|
||||
* layer_mask<sup>1,2</sup>: 层图像的遮罩。
|
||||
* invert_mask: 是否反转遮罩。
|
||||
* blend_mode<sup>3</sup>: 描边的混合模式。
|
||||
* opacity: 不透明度。
|
||||
* color: 覆盖的颜色。
|
||||
|
||||
|
||||
#### 节点选项注解:
|
||||
<sup>1</sup> image、mask和background_image(如果有输入)这三项必须是相同的尺寸。
|
||||
<sup>2</sup> mask不是必须的输入项,默认使用image的alpha通道,如果image输入不包含alpha通道将自动创建整个图像的alpha通道。如果输入mask,原本的alpha通道将被mask覆盖。
|
||||
<sup>3</sup>: 混合模式包括normal、multply、screen、add、subtract、difference、darker和lighter。
|
||||
<sup>4</sup>: 颜色使用16进制RGB字符串格式描述,例如 '#FA3D86'。
|
||||
|
||||
# LayerMask
|
||||

|
||||
|
||||
### MaskPreview:
|
||||
预览mask
|
||||
@@ -125,12 +160,13 @@ mask
|
||||

|
||||
|
||||
# LayerUtility
|
||||

|
||||
|
||||
### ImageBlend:
|
||||
用于合成图层,提供多种混合模式供选择,可设置透明度。
|
||||

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

|
||||
* background_image<sup>1</sup>: 背景图像。
|
||||
* layer_image<sup>1</sup>: 用于合成的层图像。
|
||||
@@ -143,7 +179,7 @@ mask
|
||||
调整图像不透明度。
|
||||

|
||||
|
||||
节点选项说明:
|
||||
节点选项说明:
|
||||
* image: 图像输入,支持RGB和RGBA输入。
|
||||
* mask: 遮罩输入。
|
||||
* invert_mask: 是否反转遮罩。
|
||||
@@ -154,31 +190,50 @@ mask
|
||||
改自[mtb nodes](https://github.com/melMass/comfy_mtb)的web extensions。
|
||||

|
||||
|
||||
节点选项说明:
|
||||
节点选项说明:
|
||||
* mode: 输出格式,可选十六进制(HEX)或十进制(DEC)。
|
||||
|
||||
输出:
|
||||
输出:
|
||||
* string
|
||||
|
||||
# LayerFilter
|
||||

|
||||
|
||||
### MotionBlur:
|
||||
运动模糊。
|
||||

|
||||
|
||||
节点选项说明:
|
||||
节点选项说明:
|
||||
* angle: 模糊角度。
|
||||
* blur: 模糊大小。
|
||||
|
||||
|
||||
## 使用示例:
|
||||
## 使用示例:
|
||||
|
||||
在workflow目录下有json格式的工作流示例文件。
|
||||
|
||||
## 以下工作在计划列表中
|
||||
|
||||
* Color Overlay
|
||||
* Gradient Overlay
|
||||
* LayerUtility:
|
||||
imageblend advance
|
||||
color image
|
||||
gradient image
|
||||
|
||||
* LayerMask:
|
||||
text mask
|
||||
gradient mask
|
||||
mask grow&blur
|
||||
mask subtract
|
||||
mask combine
|
||||
mask diffrent
|
||||
mask gradient
|
||||
mask softedge
|
||||
|
||||
* LayerFilter:
|
||||
bulr
|
||||
emboss
|
||||
contour
|
||||
findedge
|
||||
|
||||
## 安装方法:
|
||||
|
||||
|
||||
|
After Width: | Height: | Size: 541 KiB |
|
After Width: | Height: | Size: 88 KiB |
|
After Width: | Height: | Size: 508 KiB |
|
After Width: | Height: | Size: 128 KiB |
|
After Width: | Height: | Size: 158 KiB |
|
After Width: | Height: | Size: 159 KiB |
|
After Width: | Height: | Size: 1.4 MiB |
|
After Width: | Height: | Size: 165 KiB |
|
Before Width: | Height: | Size: 372 KiB After Width: | Height: | Size: 376 KiB |
@@ -0,0 +1,66 @@
|
||||
from .imagefunc import *
|
||||
|
||||
class ColorOverlay:
|
||||
|
||||
def __init__(self):
|
||||
pass
|
||||
|
||||
@classmethod
|
||||
def INPUT_TYPES(self):
|
||||
chop_mode = ['normal','multply','screen','add','subtract','difference','darker','lighter']
|
||||
return {
|
||||
"required": {
|
||||
"background_image": ("IMAGE", ), #
|
||||
"layer_image": ("IMAGE",), #
|
||||
"invert_mask": ("BOOLEAN", {"default": True}), # 反转mask
|
||||
"blend_mode": (chop_mode,), # 混合模式
|
||||
"opacity": ("INT", {"default": 100, "min": 0, "max": 100, "step": 1}), # 透明度
|
||||
"color": ("STRING", {"default": "#FFBF30"}), # 渐变开始颜色
|
||||
},
|
||||
"optional": {
|
||||
"layer_mask": ("MASK",), #
|
||||
}
|
||||
}
|
||||
|
||||
RETURN_TYPES = ("IMAGE",)
|
||||
RETURN_NAMES = ("image",)
|
||||
FUNCTION = 'color_overlay'
|
||||
CATEGORY = '😺dzNodes/LayerStyle'
|
||||
OUTPUT_NODE = True
|
||||
|
||||
def color_overlay(self, background_image, layer_image,
|
||||
invert_mask, blend_mode, opacity, color,
|
||||
layer_mask=None
|
||||
):
|
||||
|
||||
# preprocess
|
||||
_canvas = tensor2pil(background_image).convert('RGB')
|
||||
_layer = tensor2pil(layer_image)
|
||||
if _layer.mode == 'RGBA':
|
||||
_mask = tensor2pil(layer_image).convert('RGBA').split()[-1]
|
||||
else:
|
||||
_mask = Image.new('L', _layer.size, 'white')
|
||||
_layer = _layer.convert('RGB')
|
||||
if layer_mask is not None:
|
||||
if invert_mask:
|
||||
layer_mask = 1 - layer_mask
|
||||
_mask = mask2image(layer_mask).convert('L')
|
||||
if _mask.size != _layer.size:
|
||||
_mask = Image.new('L', _layer.size, 'white')
|
||||
log('Warning: mask mismatch, droped!')
|
||||
|
||||
_color = Image.new('RGB', size=_layer.size, color=color)
|
||||
# 合成layer
|
||||
_comp = chop_image(_layer, _color, blend_mode, opacity)
|
||||
_canvas.paste(_comp, mask=_mask)
|
||||
ret_image = _canvas
|
||||
log('ColorOverlay Processed.')
|
||||
return (pil2tensor(ret_image),)
|
||||
|
||||
NODE_CLASS_MAPPINGS = {
|
||||
"LayerStyle: ColorOverlay": ColorOverlay
|
||||
}
|
||||
|
||||
NODE_DISPLAY_NAME_MAPPINGS = {
|
||||
"LayerStyle: ColorOverlay": "LayerStyle: ColorOverlay"
|
||||
}
|
||||
@@ -38,16 +38,24 @@ class DropShadow:
|
||||
layer_mask=None
|
||||
):
|
||||
|
||||
distance_x = -distance_x
|
||||
distance_y = -distance_y
|
||||
# preprocess
|
||||
_canvas = tensor2pil(background_image).convert('RGB')
|
||||
_layer = tensor2pil(layer_image).convert('RGB')
|
||||
_mask = tensor2pil(layer_image).convert('RGBA').split()[-1]
|
||||
# 处理mask
|
||||
_layer = tensor2pil(layer_image)
|
||||
if _layer.mode == 'RGBA':
|
||||
_mask = tensor2pil(layer_image).convert('RGBA').split()[-1]
|
||||
else:
|
||||
_mask = Image.new('L', _layer.size, 'white')
|
||||
_layer = _layer.convert('RGB')
|
||||
if layer_mask is not None:
|
||||
if invert_mask:
|
||||
layer_mask = 1 - layer_mask
|
||||
_mask = mask2image(layer_mask).convert('L')
|
||||
if _mask.size != _layer.size:
|
||||
_mask = Image.new('L', _layer.size, 'white')
|
||||
log('Warning: mask mismatch, droped!')
|
||||
|
||||
distance_x = -distance_x
|
||||
distance_y = -distance_y
|
||||
if distance_x != 0 or distance_y != 0:
|
||||
__mask = shift_image(_mask, distance_x, distance_y) # 位移
|
||||
shadow_mask = expand_mask(image2mask(__mask), grow, blur) #扩张,模糊
|
||||
|
||||
@@ -0,0 +1,79 @@
|
||||
from .imagefunc import *
|
||||
|
||||
class GradientOverlay:
|
||||
|
||||
def __init__(self):
|
||||
pass
|
||||
|
||||
@classmethod
|
||||
def INPUT_TYPES(self):
|
||||
chop_mode = ['normal','multply','screen','add','subtract','difference','darker','lighter']
|
||||
return {
|
||||
"required": {
|
||||
"background_image": ("IMAGE", ), #
|
||||
"layer_image": ("IMAGE",), #
|
||||
"invert_mask": ("BOOLEAN", {"default": True}), # 反转mask
|
||||
"blend_mode": (chop_mode,), # 混合模式
|
||||
"opacity": ("INT", {"default": 100, "min": 0, "max": 100, "step": 1}), # 透明度
|
||||
"start_color": ("STRING", {"default": "#FFBF30"}), # 渐变开始颜色
|
||||
"start_alpha": ("INT", {"default": 255, "min": 0, "max": 255, "step": 1}),
|
||||
"end_color": ("STRING", {"default": "#FE0000"}), # 渐变结束颜色
|
||||
"end_alpha": ("INT", {"default": 255, "min": 0, "max": 255, "step": 1}),
|
||||
"angle": ("INT", {"default": 0, "min": -180, "max": 180, "step": 1}), # 渐变角度
|
||||
},
|
||||
"optional": {
|
||||
"layer_mask": ("MASK",), #
|
||||
}
|
||||
}
|
||||
|
||||
RETURN_TYPES = ("IMAGE",)
|
||||
RETURN_NAMES = ("image",)
|
||||
FUNCTION = 'gradient_overlay'
|
||||
CATEGORY = '😺dzNodes/LayerStyle'
|
||||
OUTPUT_NODE = True
|
||||
|
||||
def gradient_overlay(self, background_image, layer_image,
|
||||
invert_mask, blend_mode, opacity,
|
||||
start_color, start_alpha, end_color, end_alpha, angle,
|
||||
layer_mask=None
|
||||
):
|
||||
|
||||
# preprocess
|
||||
_canvas = tensor2pil(background_image).convert('RGB')
|
||||
_layer = tensor2pil(layer_image)
|
||||
if _layer.mode == 'RGBA':
|
||||
_mask = tensor2pil(layer_image).convert('RGBA').split()[-1]
|
||||
else:
|
||||
_mask = Image.new('L', _layer.size, 'white')
|
||||
_layer = _layer.convert('RGB')
|
||||
if layer_mask is not None:
|
||||
if invert_mask:
|
||||
layer_mask = 1 - layer_mask
|
||||
_mask = mask2image(layer_mask).convert('L')
|
||||
if _mask.size != _layer.size:
|
||||
_mask = Image.new('L', _layer.size, 'white')
|
||||
log('Warning: mask mismatch, droped!')
|
||||
|
||||
_gradient = gradint(start_color, end_color, _layer.width, _layer.height, float(angle))
|
||||
|
||||
# 合成layer
|
||||
_comp = chop_image(_layer, _gradient, blend_mode, opacity)
|
||||
if start_alpha < 255 or end_alpha < 255:
|
||||
#
|
||||
start_color = RGB_to_Hex((start_alpha, start_alpha, start_alpha))
|
||||
end_color = RGB_to_Hex((end_alpha, end_alpha, end_alpha))
|
||||
comp_alpha = gradint(start_color, end_color, _layer.width, _layer.height, float(angle))
|
||||
comp_alpha = ImageChops.invert(comp_alpha).convert('L')
|
||||
_comp.paste(_layer, comp_alpha)
|
||||
_canvas.paste(_comp, mask=_mask)
|
||||
ret_image = _canvas
|
||||
log('GradientOverlay Processed.')
|
||||
return (pil2tensor(ret_image),)
|
||||
|
||||
NODE_CLASS_MAPPINGS = {
|
||||
"LayerStyle: GradientOverlay": GradientOverlay
|
||||
}
|
||||
|
||||
NODE_DISPLAY_NAME_MAPPINGS = {
|
||||
"LayerStyle: GradientOverlay": "LayerStyle: GradientOverlay"
|
||||
}
|
||||
@@ -6,7 +6,7 @@ import torch
|
||||
import scipy.ndimage
|
||||
import cv2
|
||||
from typing import Union, List
|
||||
from PIL import Image, ImageFilter, ImageChops
|
||||
from PIL import Image, ImageFilter, ImageChops, ImageDraw
|
||||
|
||||
def log(message):
|
||||
name = 'LayerStyle'
|
||||
@@ -119,9 +119,70 @@ def motion_blur(image:Image, angle:int, blur:int) -> Image:
|
||||
ret_image = cv22pil(blurred)
|
||||
return ret_image
|
||||
|
||||
def direction_blur(image:Image, angle:int, blur:int, color:str) -> Image:
|
||||
def __rotate_expand(image:Image, angle:float, SSAA:int=0) -> Image:
|
||||
images = pil2tensor(image)
|
||||
expand = "true"
|
||||
height, width = images[0, :, :, 0].shape
|
||||
|
||||
ret_image = image
|
||||
def rotate_tensor(tensor):
|
||||
resize_sampler = Image.LANCZOS
|
||||
rotate_sampler = Image.BICUBIC
|
||||
if SSAA > 1:
|
||||
img = tensor.tensor_to_image()
|
||||
img_us_scaled = img.resize((width * SSAA, height * SSAA), resize_sampler)
|
||||
img_rotated = img_us_scaled.rotate(angle, rotate_sampler, expand == "true", fillcolor=(0, 0, 0, 0))
|
||||
img_down_scaled = img_rotated.resize((img_rotated.width // SSAA, img_rotated.height // SSAA), resize_sampler)
|
||||
result = img_down_scaled.image_to_tensor()
|
||||
else:
|
||||
img = tensor.tensor_to_image()
|
||||
img_rotated = img.rotate(angle, rotate_sampler, expand == "true", fillcolor=(0, 0, 0, 0))
|
||||
result = img_rotated.image_to_tensor()
|
||||
return result
|
||||
|
||||
if angle == 0.0 or angle == 360.0:
|
||||
return tensor2pil(images)
|
||||
else:
|
||||
rotated_tensor = torch.stack([rotate_tensor(images[i]) for i in range(len(images))])
|
||||
return tensor2pil(rotated_tensor).convert('RGB')
|
||||
|
||||
def image_rotate_extend_with_alpha(image:Image, alpha:Image, angle:float, SSAA:int=0) -> tuple:
|
||||
_image = __rotate_expand(image.convert('RGB'), angle, SSAA)
|
||||
_alpha = __rotate_expand(alpha.convert('RGB'), angle, SSAA)
|
||||
R, G, B = _image.split()
|
||||
A = _alpha.convert('L')
|
||||
ret_image = Image.merge('RGBA', (R, G, B, A))
|
||||
return (_image, _alpha, ret_image)
|
||||
|
||||
|
||||
def create_gradient(start_color_inhex:str, end_color_inhex:str, width:int, height:int) -> Image:
|
||||
start_color = Hex_to_RGB(start_color_inhex)
|
||||
end_color = Hex_to_RGB(end_color_inhex)
|
||||
ret_image = Image.new("RGB", (width, height), start_color)
|
||||
draw = ImageDraw.Draw(ret_image)
|
||||
for i in range(height):
|
||||
R = int(start_color[0] * (height - i) / height + end_color[0] * i / height)
|
||||
G = int(start_color[1] * (height - i) / height + end_color[1] * i / height)
|
||||
B = int(start_color[2] * (height - i) / height + end_color[2] * i / height)
|
||||
color = (R, G, B)
|
||||
draw.line((0, i, width, i), fill=color)
|
||||
return ret_image
|
||||
|
||||
def gradint(start_color_inhex:str, end_color_inhex:str, width:int, height:int, angle:float, ) -> Image:
|
||||
radius = int((width + height) / 4)
|
||||
g = create_gradient(start_color_inhex, end_color_inhex, radius, radius)
|
||||
_canvas = Image.new('RGB', size=(radius, radius*3), color=start_color_inhex)
|
||||
top = Image.new('RGB', size=(radius, radius), color=start_color_inhex)
|
||||
bottom = Image.new('RGB', size=(radius, radius),color=end_color_inhex)
|
||||
_canvas.paste(top, box=(0, 0, radius, radius))
|
||||
_canvas.paste(g, box=(0, radius, radius, radius * 2))
|
||||
_canvas.paste(bottom,box=(0, radius * 2, radius, radius * 3))
|
||||
_canvas = _canvas.resize((radius * 3, radius * 3))
|
||||
_canvas = __rotate_expand(_canvas,angle)
|
||||
center = int(_canvas.width / 2)
|
||||
_x = int(width / 3)
|
||||
_y = int(height / 3)
|
||||
ret_image = _canvas.crop((center - _x, center - _y, center + _x, center + _y))
|
||||
ret_image = ret_image.resize((width, height))
|
||||
return ret_image
|
||||
|
||||
'''Mask Functions'''
|
||||
@@ -180,18 +241,15 @@ def step_value(start_value, end_value, total_step, step) -> float: # 按当前
|
||||
factor = step / total_step
|
||||
return (end_value - start_value) * factor + start_value
|
||||
|
||||
def step_color(start_color, end_color, total_step, step): # 按当前步数在总步数中的位置返回比例颜色
|
||||
if isinstance(start_color, str):
|
||||
start_color = tuple(Hex_to_RGB(start_color))
|
||||
if isinstance(end_color, str):
|
||||
end_color = tuple(Hex_to_RGB(end_color))
|
||||
def step_color(start_color_inhex:str, end_color_inhex:str, total_step:int, step:int) -> str: # 按当前步数在总步数中的位置返回比例颜色
|
||||
start_color = tuple(Hex_to_RGB(start_color_inhex))
|
||||
end_color = tuple(Hex_to_RGB(end_color_inhex))
|
||||
start_R, start_G, start_B = start_color[0], start_color[1], start_color[2]
|
||||
end_R, end_G, end_B = end_color[0], end_color[1], end_color[2]
|
||||
ret_color = (int(step_value(start_R, end_R, total_step, step)),
|
||||
int(step_value(start_G, end_G, total_step, step)),
|
||||
int(step_value(start_B, end_B, total_step, step)),
|
||||
)
|
||||
if isinstance(start_color, str):
|
||||
return RGB_to_Hex(ret_color)
|
||||
else:
|
||||
return ret_color
|
||||
return RGB_to_Hex(ret_color)
|
||||
|
||||
|
||||
|
||||
@@ -38,14 +38,21 @@ class InnerGlow:
|
||||
layer_mask=None
|
||||
):
|
||||
|
||||
# preprocess
|
||||
_canvas = tensor2pil(background_image).convert('RGB')
|
||||
_layer = tensor2pil(layer_image).convert('RGB')
|
||||
_mask = tensor2pil(layer_image).convert('RGBA').split()[-1]
|
||||
# 处理mask
|
||||
_layer = tensor2pil(layer_image)
|
||||
if _layer.mode == 'RGBA':
|
||||
_mask = tensor2pil(layer_image).convert('RGBA').split()[-1]
|
||||
else:
|
||||
_mask = Image.new('L', _layer.size, 'white')
|
||||
_layer = _layer.convert('RGB')
|
||||
if layer_mask is not None:
|
||||
if invert_mask:
|
||||
layer_mask = 1 - layer_mask
|
||||
_mask = mask2image(layer_mask).convert('L')
|
||||
if _mask.size != _layer.size:
|
||||
_mask = Image.new('L', _layer.size, 'white')
|
||||
log('Warning: mask mismatch, droped!')
|
||||
|
||||
blur_factor = blur / 20.0
|
||||
grow = glow_range
|
||||
|
||||
@@ -39,16 +39,24 @@ class InnerShadow:
|
||||
layer_mask=None
|
||||
):
|
||||
|
||||
distance_x = -distance_x
|
||||
distance_y = -distance_y
|
||||
# preprocess
|
||||
_canvas = tensor2pil(background_image).convert('RGB')
|
||||
_layer = tensor2pil(layer_image).convert('RGB')
|
||||
_mask = tensor2pil(layer_image).convert('RGBA').split()[-1]
|
||||
# 处理mask
|
||||
_layer = tensor2pil(layer_image)
|
||||
if _layer.mode == 'RGBA':
|
||||
_mask = tensor2pil(layer_image).convert('RGBA').split()[-1]
|
||||
else:
|
||||
_mask = Image.new('L', _layer.size, 'white')
|
||||
_layer = _layer.convert('RGB')
|
||||
if layer_mask is not None:
|
||||
if invert_mask:
|
||||
layer_mask = 1 - layer_mask
|
||||
_mask = mask2image(layer_mask).convert('L')
|
||||
if _mask.size != _layer.size:
|
||||
_mask = Image.new('L', _layer.size, 'white')
|
||||
log('Warning: mask mismatch, droped!')
|
||||
|
||||
distance_x = -distance_x
|
||||
distance_y = -distance_y
|
||||
if distance_x != 0 or distance_y != 0:
|
||||
__mask = shift_image(_mask, distance_x, distance_y) # 位移
|
||||
shadow_mask = expand_mask(image2mask(__mask), grow, blur) #扩张,模糊
|
||||
|
||||
@@ -38,14 +38,22 @@ class OuterGlow:
|
||||
layer_mask=None
|
||||
):
|
||||
|
||||
# preprocess
|
||||
_canvas = tensor2pil(background_image).convert('RGB')
|
||||
_layer = tensor2pil(layer_image).convert('RGB')
|
||||
_mask = tensor2pil(layer_image).convert('RGBA').split()[-1]
|
||||
# 处理mask
|
||||
_layer = tensor2pil(layer_image)
|
||||
if _layer.mode == 'RGBA':
|
||||
_mask = tensor2pil(layer_image).convert('RGBA').split()[-1]
|
||||
else:
|
||||
_mask = Image.new('L', _layer.size, 'white')
|
||||
_layer = _layer.convert('RGB')
|
||||
if layer_mask is not None:
|
||||
if invert_mask:
|
||||
layer_mask = 1 - layer_mask
|
||||
_mask = mask2image(layer_mask).convert('L')
|
||||
if _mask.size != _layer.size:
|
||||
_mask = Image.new('L', _layer.size, 'white')
|
||||
log('Warning: mask mismatch, droped!')
|
||||
|
||||
blur_factor = blur / 20.0
|
||||
grow = glow_range
|
||||
for x in range(brightness):
|
||||
|
||||
@@ -37,13 +37,21 @@ class Stroke:
|
||||
layer_mask=None
|
||||
):
|
||||
|
||||
# preprocess
|
||||
_canvas = tensor2pil(background_image).convert('RGB')
|
||||
_layer = tensor2pil(layer_image).convert('RGB')
|
||||
_mask = tensor2pil(layer_image).convert('RGBA').split()[-1]
|
||||
_layer = tensor2pil(layer_image)
|
||||
if _layer.mode == 'RGBA':
|
||||
_mask = tensor2pil(layer_image).convert('RGBA').split()[-1]
|
||||
else:
|
||||
_mask = Image.new('L', _layer.size, 'white')
|
||||
_layer = _layer.convert('RGB')
|
||||
if layer_mask is not None:
|
||||
if invert_mask:
|
||||
layer_mask = 1 - layer_mask
|
||||
_mask = mask2image(layer_mask).convert('L')
|
||||
if _mask.size != _layer.size:
|
||||
_mask = Image.new('L', _layer.size, 'white')
|
||||
log('Warning: mask mismatch, droped!')
|
||||
|
||||
grow_offset = int(stroke_width / 2)
|
||||
inner_stroke = stroke_grow - grow_offset
|
||||
|
||||
@@ -0,0 +1,203 @@
|
||||
{
|
||||
"last_node_id": 25,
|
||||
"last_link_id": 61,
|
||||
"nodes": [
|
||||
{
|
||||
"id": 15,
|
||||
"type": "LoadImage",
|
||||
"pos": [
|
||||
659,
|
||||
386
|
||||
],
|
||||
"size": {
|
||||
"0": 315,
|
||||
"1": 314
|
||||
},
|
||||
"flags": {},
|
||||
"order": 0,
|
||||
"mode": 0,
|
||||
"outputs": [
|
||||
{
|
||||
"name": "IMAGE",
|
||||
"type": "IMAGE",
|
||||
"links": [
|
||||
59
|
||||
],
|
||||
"shape": 3,
|
||||
"slot_index": 0
|
||||
},
|
||||
{
|
||||
"name": "MASK",
|
||||
"type": "MASK",
|
||||
"links": [
|
||||
60
|
||||
],
|
||||
"shape": 3,
|
||||
"slot_index": 1
|
||||
}
|
||||
],
|
||||
"properties": {
|
||||
"Node name for S&R": "LoadImage"
|
||||
},
|
||||
"widgets_values": [
|
||||
"512x512 (14).png",
|
||||
"image"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": 2,
|
||||
"type": "LoadImage",
|
||||
"pos": [
|
||||
307,
|
||||
382
|
||||
],
|
||||
"size": {
|
||||
"0": 315,
|
||||
"1": 314
|
||||
},
|
||||
"flags": {},
|
||||
"order": 1,
|
||||
"mode": 0,
|
||||
"outputs": [
|
||||
{
|
||||
"name": "IMAGE",
|
||||
"type": "IMAGE",
|
||||
"links": [
|
||||
58
|
||||
],
|
||||
"shape": 3,
|
||||
"slot_index": 0
|
||||
},
|
||||
{
|
||||
"name": "MASK",
|
||||
"type": "MASK",
|
||||
"links": [],
|
||||
"shape": 3,
|
||||
"slot_index": 1
|
||||
}
|
||||
],
|
||||
"properties": {
|
||||
"Node name for S&R": "LoadImage"
|
||||
},
|
||||
"widgets_values": [
|
||||
"512x512bk.jpg",
|
||||
"image"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": 24,
|
||||
"type": "LayerStyle: ColorOverlay",
|
||||
"pos": [
|
||||
1008,
|
||||
440
|
||||
],
|
||||
"size": {
|
||||
"0": 315,
|
||||
"1": 170
|
||||
},
|
||||
"flags": {},
|
||||
"order": 2,
|
||||
"mode": 0,
|
||||
"inputs": [
|
||||
{
|
||||
"name": "background_image",
|
||||
"type": "IMAGE",
|
||||
"link": 58
|
||||
},
|
||||
{
|
||||
"name": "layer_image",
|
||||
"type": "IMAGE",
|
||||
"link": 59
|
||||
},
|
||||
{
|
||||
"name": "layer_mask",
|
||||
"type": "MASK",
|
||||
"link": 60
|
||||
}
|
||||
],
|
||||
"outputs": [
|
||||
{
|
||||
"name": "image",
|
||||
"type": "IMAGE",
|
||||
"links": [
|
||||
61
|
||||
],
|
||||
"shape": 3,
|
||||
"slot_index": 0
|
||||
}
|
||||
],
|
||||
"properties": {
|
||||
"Node name for S&R": "LayerStyle: ColorOverlay"
|
||||
},
|
||||
"widgets_values": [
|
||||
true,
|
||||
"multply",
|
||||
66,
|
||||
"#FFBF30"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": 4,
|
||||
"type": "PreviewImage",
|
||||
"pos": [
|
||||
1358,
|
||||
386
|
||||
],
|
||||
"size": {
|
||||
"0": 310.00006103515625,
|
||||
"1": 308.33331298828125
|
||||
},
|
||||
"flags": {},
|
||||
"order": 3,
|
||||
"mode": 0,
|
||||
"inputs": [
|
||||
{
|
||||
"name": "images",
|
||||
"type": "IMAGE",
|
||||
"link": 61
|
||||
}
|
||||
],
|
||||
"properties": {
|
||||
"Node name for S&R": "PreviewImage"
|
||||
}
|
||||
}
|
||||
],
|
||||
"links": [
|
||||
[
|
||||
58,
|
||||
2,
|
||||
0,
|
||||
24,
|
||||
0,
|
||||
"IMAGE"
|
||||
],
|
||||
[
|
||||
59,
|
||||
15,
|
||||
0,
|
||||
24,
|
||||
1,
|
||||
"IMAGE"
|
||||
],
|
||||
[
|
||||
60,
|
||||
15,
|
||||
1,
|
||||
24,
|
||||
2,
|
||||
"MASK"
|
||||
],
|
||||
[
|
||||
61,
|
||||
24,
|
||||
0,
|
||||
4,
|
||||
0,
|
||||
"IMAGE"
|
||||
]
|
||||
],
|
||||
"groups": [],
|
||||
"config": {},
|
||||
"extra": {},
|
||||
"version": 0.4
|
||||
}
|
||||
@@ -0,0 +1,207 @@
|
||||
{
|
||||
"last_node_id": 29,
|
||||
"last_link_id": 66,
|
||||
"nodes": [
|
||||
{
|
||||
"id": 4,
|
||||
"type": "PreviewImage",
|
||||
"pos": [
|
||||
1358,
|
||||
386
|
||||
],
|
||||
"size": {
|
||||
"0": 310.00006103515625,
|
||||
"1": 308.33331298828125
|
||||
},
|
||||
"flags": {},
|
||||
"order": 3,
|
||||
"mode": 0,
|
||||
"inputs": [
|
||||
{
|
||||
"name": "images",
|
||||
"type": "IMAGE",
|
||||
"link": 66
|
||||
}
|
||||
],
|
||||
"properties": {
|
||||
"Node name for S&R": "PreviewImage"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": 2,
|
||||
"type": "LoadImage",
|
||||
"pos": [
|
||||
307,
|
||||
382
|
||||
],
|
||||
"size": {
|
||||
"0": 315,
|
||||
"1": 314
|
||||
},
|
||||
"flags": {},
|
||||
"order": 0,
|
||||
"mode": 0,
|
||||
"outputs": [
|
||||
{
|
||||
"name": "IMAGE",
|
||||
"type": "IMAGE",
|
||||
"links": [
|
||||
62
|
||||
],
|
||||
"shape": 3,
|
||||
"slot_index": 0
|
||||
},
|
||||
{
|
||||
"name": "MASK",
|
||||
"type": "MASK",
|
||||
"links": [],
|
||||
"shape": 3,
|
||||
"slot_index": 1
|
||||
}
|
||||
],
|
||||
"properties": {
|
||||
"Node name for S&R": "LoadImage"
|
||||
},
|
||||
"widgets_values": [
|
||||
"512x512bk.jpg",
|
||||
"image"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": 15,
|
||||
"type": "LoadImage",
|
||||
"pos": [
|
||||
659,
|
||||
386
|
||||
],
|
||||
"size": {
|
||||
"0": 315,
|
||||
"1": 314
|
||||
},
|
||||
"flags": {},
|
||||
"order": 1,
|
||||
"mode": 0,
|
||||
"outputs": [
|
||||
{
|
||||
"name": "IMAGE",
|
||||
"type": "IMAGE",
|
||||
"links": [
|
||||
63
|
||||
],
|
||||
"shape": 3,
|
||||
"slot_index": 0
|
||||
},
|
||||
{
|
||||
"name": "MASK",
|
||||
"type": "MASK",
|
||||
"links": [
|
||||
64
|
||||
],
|
||||
"shape": 3,
|
||||
"slot_index": 1
|
||||
}
|
||||
],
|
||||
"properties": {
|
||||
"Node name for S&R": "LoadImage"
|
||||
},
|
||||
"widgets_values": [
|
||||
"512x512 (14).png",
|
||||
"image"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": 27,
|
||||
"type": "LayerStyle: GradientOverlay",
|
||||
"pos": [
|
||||
1015,
|
||||
408
|
||||
],
|
||||
"size": {
|
||||
"0": 315,
|
||||
"1": 266
|
||||
},
|
||||
"flags": {},
|
||||
"order": 2,
|
||||
"mode": 0,
|
||||
"inputs": [
|
||||
{
|
||||
"name": "background_image",
|
||||
"type": "IMAGE",
|
||||
"link": 62
|
||||
},
|
||||
{
|
||||
"name": "layer_image",
|
||||
"type": "IMAGE",
|
||||
"link": 63
|
||||
},
|
||||
{
|
||||
"name": "layer_mask",
|
||||
"type": "MASK",
|
||||
"link": 64
|
||||
}
|
||||
],
|
||||
"outputs": [
|
||||
{
|
||||
"name": "image",
|
||||
"type": "IMAGE",
|
||||
"links": [
|
||||
66
|
||||
],
|
||||
"shape": 3,
|
||||
"slot_index": 0
|
||||
}
|
||||
],
|
||||
"properties": {
|
||||
"Node name for S&R": "LayerStyle: GradientOverlay"
|
||||
},
|
||||
"widgets_values": [
|
||||
true,
|
||||
"normal",
|
||||
100,
|
||||
"#FFBF30",
|
||||
60,
|
||||
"#FE0000",
|
||||
255,
|
||||
0
|
||||
]
|
||||
}
|
||||
],
|
||||
"links": [
|
||||
[
|
||||
62,
|
||||
2,
|
||||
0,
|
||||
27,
|
||||
0,
|
||||
"IMAGE"
|
||||
],
|
||||
[
|
||||
63,
|
||||
15,
|
||||
0,
|
||||
27,
|
||||
1,
|
||||
"IMAGE"
|
||||
],
|
||||
[
|
||||
64,
|
||||
15,
|
||||
1,
|
||||
27,
|
||||
2,
|
||||
"MASK"
|
||||
],
|
||||
[
|
||||
66,
|
||||
27,
|
||||
0,
|
||||
4,
|
||||
0,
|
||||
"IMAGE"
|
||||
]
|
||||
],
|
||||
"groups": [],
|
||||
"config": {},
|
||||
"extra": {},
|
||||
"version": 0.4
|
||||
}
|
||||