Commit ColorofShadow&Highlight and Shadow&HighlightMask nodes, ImageScalyByAspectRatio node add 64 multiple to support LayerDiffusion

This commit is contained in:
chflame
2024-03-08 18:04:58 +08:00
parent fa6522da84
commit a828a35497
12 changed files with 530 additions and 70 deletions
+36
View File
@@ -36,6 +36,8 @@ git clone https://github.com/chflame163/ComfyUI_LayerStyle.git
## Update
<font size="4">**If the dependency package error after updating, please reinstall the relevant dependency packages. for details, please refer to [here](https://github.com/chflame163/ComfyUI_LayerStyle/issues/5). </font><br />
* Commit [Color of Shadow & Highlight](#Color of Shadow & Highlight) node, it can adjust the color of the dark and bright parts separately. Commit [Shadow & Highlight Mask](#Shadow & Highlight Mask) node, it can output mask for dark and bright areas.
* Commit [CropByMaskV2](#CropByMaskV2) node, On the basis of the original node, it supports ```crop_box``` input, making it convenient to cut layers of the same size.
* Commit [SimpleTextImage](#SimpleTextImage) node, it generate simple typesetting images and masks from text. This node references some of the functionalities and code of [ZHO-ZHO-ZHO/ComfyUI-Text_Image-Composite](https://github.com/ZHO-ZHO-ZHO/ComfyUI-Text_Image-Composite).
* Commit [PromptTagger](#PromptTagger) node,Inference the prompts based on the image. and it can replace key word for the prompt(need apply for Google Studio API key). Upgrade [ColorImageV2](#ColorImageV2) and [GradientImageV2](#GradientImageV2),support user customize preset sizes and size_as input.
@@ -276,6 +278,25 @@ Node options:
Change the exposure of the image.
![image](image/exposure_example.png)
### <a id="table1">Color of Shadow & Highlight</a>
Adjust the color of the dark and bright parts of the image.
![image](image/color_of_shadow_and_highlight_example.png)
Node options:
![image](image/color_of_shadow_and_highlight_node.png)
* image: The input image.
* mask: Optional input. if there is input, only the colors within the mask range will be adjusted.
* shadow_brightness: The brightness of the dark area.
* shadow_saturation: The color saturation in the dark area.
* shadow_hue: The color hue in the dark area.
* shadow_level_offset: The offset of values in the dark area, where larger values bring more areas closer to the bright into the dark area.
* shadow_range: The transitional range of the dark area.
* highlight_brightness: The brightness of the highlight area.
* highlight_saturation: The color saturation in the highlight area.
* highlight_hue: The color hue in the highlight area.
* highlight_level_offset: The offset of values in the highlight area, where larger values bring more areas closer to the dark into the highlight area.
* highlight_range: The transitional range of the highlight area.
Node option:
* exposure: Exposure value. Higher values indicate brighter image.
@@ -895,6 +916,21 @@ Node options:
* white_point: Edge white sampling threshold.
* process_detail: Set to false here will skip edge processing to save runtime.
### <a id="table1">Shadow & Highlight Mask</a>
Generate masks for the dark and bright parts of the image.
![image](image/shadow_and_highlight_mask_example.png)
Node options:
![image](image/shadow_and_highlight_mask_node.png)
* image: The input image.
* mask: Optional input. if there is input, only the colors within the mask range will be adjusted.
* shadow_level_offset: The offset of values in the dark area, where larger values bring more areas closer to the bright into the dark area.
* shadow_range: The transitional range of the dark area.
* highlight_level_offset: The offset of values in the highlight area, where larger values bring more areas closer to the dark into the highlight area.
* highlight_range: The transitional range of the highlight area.
### <a id="table1">PixelSpread</a>
Pixel expansion preprocessing on the masked edge of an image can effectively improve the edges of image composit.
![image](image/pixel_spread_example.png)
+35
View File
@@ -37,6 +37,8 @@ git clone https://github.com/chflame163/ComfyUI_LayerStyle.git
## 更新说明
<font size="4">**如果本插件更新后出现依赖包错误,请重新安装相关依赖包。详情见[这里](https://github.com/chflame163/ComfyUI_LayerStyle/issues/5)。 </font><br />
* 添加 [Color of Shadow & Highlight](#Color of Shadow & Highlight) 节点,可对暗部和亮部分别进行色彩调整。添加 [Shadow & Highlight Mask](#Shadow & Highlight Mask) 节点, 可输出暗部和亮部的遮罩。
* 添加 [CropByMaskV2](#CropByMaskV2) 节点,在原节点基础上支持```crop_box```输入,方便裁切相同尺寸的图层。
* 添加 [SimpleTextImage](#SimpleTextImage) 节点。从文字生成简单排版的图片以及遮罩。这个节点参考了[ZHO-ZHO-ZHO/ComfyUI-Text_Image-Composite](https://github.com/ZHO-ZHO-ZHO/ComfyUI-Text_Image-Composite)的部分功能和代码。
* 添加 [PromptTagger](#PromptTagger) 节点,根据图片反推提示词,可以替换关键词。需要申请Google Studio API使用。升级节点[ColorImageV2](#ColorImageV2)和[GradientImageV2](#GradientImageV2),支持用户自定义预设尺寸和size_as输入。
@@ -274,6 +276,25 @@ git clone https://github.com/chflame163/ComfyUI_LayerStyle.git
节点选项说明:
* exposure: 曝光值。更高的数值表示更亮的曝光。
### <a id="table1">Color of Shadow & Highlight</a>
调整图像暗部和亮部的颜色。
![image](image/color_of_shadow_and_highlight_example.png)
节点选项说明:
![image](image/color_of_shadow_and_highlight_node.png)
* image: 图像输入。
* mask: 可选输入。如果有输入,将只调整遮罩范围内的颜色。
* shadow_brightness: 暗部的亮度。
* shadow_saturation: 暗部的色彩饱和度。
* shadow_hue: 暗部的色相。
* shadow_level_offset: 暗部取值的偏移量,更大的数值使更多靠近明亮的区域纳入暗部。
* shadow_range: 暗部的过渡范围。
* highlight_brightness: 亮部的亮度。
* highlight_saturation: 亮部的色彩饱和度。
* highlight_hue: 亮部的色相。
* highlight_level_offset: 亮部取值的偏移量,更小的数值使更多靠近阴暗的区域纳入亮部。
* highlight_range: 亮部的过渡范围。
### <a id="table1">Gamma</a>
改变图像的Gamma值。
@@ -894,6 +915,20 @@ cropped_mask: 裁切后的遮罩。
* process_detail: 此处设为False将跳过边缘处理以节省运行时间。
### <a id="table1">Shadow & Highlight Mask</a>
生成图像暗部和亮部的遮罩。
![image](image/shadow_and_highlight_mask_example.png)
节点选项说明:
![image](image/shadow_and_highlight_mask_node.png)
* image: 图像输入。
* mask: 可选输入。如果有输入,将只调整遮罩范围内的颜色。
* shadow_level_offset: 暗部取值的偏移量,更大的数值使更多靠近明亮的区域纳入暗部。
* shadow_range: 暗部的过渡范围。
* highlight_level_offset: 亮部取值的偏移量,更小的数值使更多靠近阴暗的区域纳入亮部。
* highlight_range: 亮部的过渡范围。
### <a id="table1">PixelSpread</a>
对图像的遮罩边缘部分进行像素扩张预处理,可有效改善图像合成的边缘。
![image](image/pixel_spread_example.png)
Binary file not shown.

After

Width:  |  Height:  |  Size: 2.7 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 170 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 107 KiB

+128
View File
@@ -0,0 +1,128 @@
from .imagefunc import *
NODE_NAME = 'Color of Shadow & Highlight'
def norm_value(value):
if value < 0.01:
value = 0.01
if value > 0.99:
value = 0.99
return value
class ColorCorrectShadowAndHighlight:
def __init__(self):
pass
@classmethod
def INPUT_TYPES(self):
return {
"required": {
"image": ("IMAGE", ),
"shadow_brightness": ("FLOAT", {"default": 1, "min": 0.0, "max": 3, "step": 0.01}),
"shadow_saturation": ("FLOAT", {"default": 1, "min": 0.0, "max": 3, "step": 0.01}),
"shadow_hue": ("INT", {"default": 0, "min": -255, "max": 255, "step": 1}),
"shadow_level_offset": ("INT", {"default": 0, "min": -99, "max": 99, "step": 1}),
"shadow_range": ("FLOAT", {"default": 0.25, "min": 0.01, "max": 0.99, "step": 0.01}),
"highlight_brightness": ("FLOAT", {"default": 1, "min": 0.0, "max": 3, "step": 0.01}),
"highlight_saturation": ("FLOAT", {"default": 1, "min": 0.0, "max": 3, "step": 0.01}),
"highlight_hue": ("INT", {"default": 0, "min": -255, "max": 255, "step": 1}),
"highlight_level_offset": ("INT", {"default": 0, "min": -99, "max": 99, "step": 1}),
"highlight_range": ("FLOAT", {"default": 0.25, "min": 0.01, "max": 0.99, "step": 0.01}),
},
"optional": {
"mask": ("MASK",), #
}
}
RETURN_TYPES = ("IMAGE",)
RETURN_NAMES = ("image",)
FUNCTION = 'color_shadow_and_highlight'
CATEGORY = '😺dzNodes/LayerColor'
OUTPUT_NODE = True
def color_shadow_and_highlight(self, image,
shadow_brightness, shadow_saturation,
shadow_level_offset, shadow_range, shadow_hue,
highlight_brightness, highlight_saturation, highlight_hue,
highlight_level_offset, highlight_range,
mask=None
):
ret_images = []
input_images = []
input_masks = []
for i in image:
input_images.append(torch.unsqueeze(i, 0))
m = tensor2pil(i)
if m.mode == 'RGBA':
input_masks.append(m.split()[-1])
else:
input_masks.append(Image.new('L', size=m.size, color='white'))
if mask is not None:
if mask.dim() == 2:
mask = torch.unsqueeze(mask, 0)
input_masks = []
for m in mask:
input_masks.append(tensor2pil(torch.unsqueeze(m, 0)).convert('L'))
max_batch = max(len(input_images), len(input_masks))
for i in range(max_batch):
_image = input_images[i] if i < len(input_images) else input_images[-1]
_image = tensor2pil(_image).convert('RGB')
_mask = input_masks[i] if i < len(input_masks) else input_masks[-1]
avg_gray = get_gray_average(_image, _mask)
shadow_level, highlight_level = calculate_shadow_highlight_level(avg_gray)
_canvas = _image.copy()
if shadow_saturation !=1 or shadow_brightness !=1 or shadow_hue:
shadow_low_threshold = (shadow_level + shadow_level_offset) / 100 + shadow_range / 2
shadow_low_threshold = norm_value(shadow_low_threshold)
shadow_high_threshold = (shadow_level + shadow_level_offset) / 100 - shadow_range / 2
shadow_high_threshold = norm_value(shadow_high_threshold)
_shadow_mask = luminance_keyer(_image, shadow_low_threshold, shadow_high_threshold)
_shadow = _image.copy()
if shadow_brightness != 1:
brightness_image = ImageEnhance.Brightness(_shadow)
_shadow = brightness_image.enhance(factor=shadow_brightness)
if shadow_saturation != 1:
color_image = ImageEnhance.Color(_shadow)
_shadow = color_image.enhance(factor=shadow_saturation)
if shadow_hue:
_h, _s, _v = _shadow.convert('HSV').split()
_h = image_hue_offset(_h, shadow_hue)
_shadow = image_channel_merge((_h, _s, _v), 'HSV')
_canvas.paste(_shadow, mask=gaussian_blur(_shadow_mask,(_shadow_mask.width + _shadow_mask.height)//800))
_canvas.paste(_image, mask=ImageChops.invert(_mask))
if highlight_saturation != 1 or highlight_brightness != 1 or highlight_hue:
highlight_low_threshold = (highlight_level + highlight_level_offset) / 100 - highlight_range / 2
highlight_low_threshold = norm_value(highlight_low_threshold)
highlight_high_threshold = (highlight_level + highlight_level_offset) / 100 + highlight_range / 2
highlight_high_threshold = norm_value(highlight_high_threshold)
_highlight_mask = luminance_keyer(_image, highlight_low_threshold, highlight_high_threshold)
_highlight = _image.copy()
if highlight_brightness != 1:
brightness_image = ImageEnhance.Brightness(_highlight)
_highlight = brightness_image.enhance(factor=highlight_brightness)
if highlight_saturation != 1:
color_image = ImageEnhance.Color(_highlight)
_highlight = color_image.enhance(factor=highlight_saturation)
if highlight_hue:
_h, _s, _v = _highlight.convert('HSV').split()
_h = image_hue_offset(_h, highlight_hue)
_highlight = image_channel_merge((_h, _s, _v), 'HSV')
_canvas.paste(_highlight, mask=gaussian_blur(_highlight_mask, (_highlight_mask.width + _highlight_mask.height)//800))
_canvas.paste(_image, mask=ImageChops.invert(_mask))
ret_images.append(pil2tensor(_canvas))
log(f"{NODE_NAME} Processed {len(ret_images)} image(s).", message_type='finish')
return (torch.cat(ret_images, dim=0),)
NODE_CLASS_MAPPINGS = {
"LayerColor: Color of Shadow & Highlight": ColorCorrectShadowAndHighlight
}
NODE_DISPLAY_NAME_MAPPINGS = {
"LayerColor: Color of Shadow & Highlight": "LayerColor: Color of Shadow & Highlight"
}
+1 -1
View File
@@ -14,7 +14,7 @@ class ImageScaleByAspectRatio:
ratio_list = ['original', 'custom', '1:1', '3:2', '4:3', '16:9', '2:3', '3:4', '9:16']
fit_mode = ['letterbox', 'crop', 'fill']
method_mode = ['lanczos', 'bicubic', 'hamming', 'bilinear', 'box', 'nearest']
multiple_list = ['8', '16', 'None']
multiple_list = ['8', '16', '64', 'None']
return {
"required": {
+1 -1
View File
@@ -14,7 +14,7 @@ class ImageScaleByAspectRatioV2:
ratio_list = ['original', 'custom', '1:1', '3:2', '4:3', '16:9', '2:3', '3:4', '9:16']
fit_mode = ['letterbox', 'crop', 'fill']
method_mode = ['lanczos', 'bicubic', 'hamming', 'bilinear', 'box', 'nearest']
multiple_list = ['8', '16', 'None']
multiple_list = ['8', '16', '64', 'None']
scale_to_list = ['None', 'longest', 'shortest']
return {
"required": {
+83 -68
View File
@@ -277,80 +277,49 @@ def blend_hard_mix(background_image:Image, layer_image:Image) -> Image:
img = img * mask
return cv22pil(ski2cv2(img))
def tuple_averge(tuples:list) -> tuple:
values = []
ret = []
for i in tuples[0]:
values.append(0)
ret.append(0)
for t in tuples:
for j in range(len(t)):
values[j] += t[j]
for k in range(len(values)):
ret[k] = int(values[k] / len(tuples))
return tuple(ret)
# def tuple_averge(tuples:list) -> tuple:
# values = []
# ret = []
# for i in tuples[0]:
# values.append(0)
# ret.append(0)
# for t in tuples:
# for j in range(len(t)):
# values[j] += t[j]
# for k in range(len(values)):
# ret[k] = int(values[k] / len(tuples))
# return tuple(ret)
def get_pixel_from_round(image:Image, position:tuple) -> tuple:
(x, y) = position
width, height = image.size
pixels = []
if x > 0:
pixels.append(image.getpixel((x - 1, y)))
if y > 0:
pixels.append(image.getpixel((x - 1, y - 1)))
if y < height:
pixels.append(image.getpixel((x - 1, y + 1)))
if x < width:
pixels.append(image.getpixel((x + 1, y)))
if y > 0:
pixels.append(image.getpixel((x + 1, y - 1)))
if y < height:
pixels.append(image.getpixel((x + 1, y + 1)))
if y > 0:
pixels.append(image.getpixel((x, y-1)))
if y < height:
pixels.append(image.getpixel((x, y + 1)))
return tuple_averge(pixels)
# def get_pixel_from_round(image:Image, position:tuple) -> tuple:
# (x, y) = position
# width, height = image.size
# pixels = []
# if x > 0:
# pixels.append(image.getpixel((x - 1, y)))
# if y > 0:
# pixels.append(image.getpixel((x - 1, y - 1)))
# if y < height:
# pixels.append(image.getpixel((x - 1, y + 1)))
# if x < width:
# pixels.append(image.getpixel((x + 1, y)))
# if y > 0:
# pixels.append(image.getpixel((x + 1, y - 1)))
# if y < height:
# pixels.append(image.getpixel((x + 1, y + 1)))
# if y > 0:
# pixels.append(image.getpixel((x, y-1)))
# if y < height:
# pixels.append(image.getpixel((x, y + 1)))
# return tuple_averge(pixels)
def displace_pixel(image:Image, source_pixel:tuple, target_pixel:tuple) -> Image:
# ret_image = image.copy()
image.putpixel(target_pixel, image.getpixel(source_pixel))
return image
def displace_pixel_np(np_image:np.ndarray, source_pixel:tuple, target_pixel:tuple) -> np.ndarray:
np_image[target_pixel[1], target_pixel[0], :] = np_image[source_pixel[1], source_pixel[0], :]
return np_image
# def de_warp(image:Image) -> Image:
#
# img = pil2cv2(image)
# gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
# edges = cv2.Canny(gray, 50, 150, apertureSize=3)
#
# # 霍夫变换
# lines = cv2.HoughLines(edges, 1, np.pi / 180, 0)
# rotate_angle = 0
# for rho, theta in lines[0]:
# a = np.cos(theta)
# b = np.sin(theta)
# x0 = a * rho
# y0 = b * rho
# x1 = int(x0 + 1000 * (-b))
# y1 = int(y0 + 1000 * (a))
# x2 = int(x0 - 1000 * (-b))
# y2 = int(y0 - 1000 * (a))
# if x1 == x2 or y1 == y2:
# continue
# t = float(y2 - y1) / (x2 - x1)
# rotate_angle = math.degrees(math.atan(t)) + 45
# if rotate_angle > 45:
# rotate_angle = -90 + rotate_angle
# elif rotate_angle < -45:
# rotate_angle = 90 + rotate_angle
# rotate_img = scipy.ndimage.rotate(img, rotate_angle)
# return cv22pil(rotate_img)
# def displace_pixel_np(np_image:np.ndarray, source_pixel:tuple, target_pixel:tuple) -> np.ndarray:
# np_image[target_pixel[1], target_pixel[0], :] = np_image[source_pixel[1], source_pixel[0], :]
# return np_image
def shift_image(image:Image, distance_x:int, distance_y:int, background_color:str='#000000', cyclic:bool=False) -> Image:
width = image.width
@@ -717,7 +686,6 @@ def gradient(start_color_inhex:str, end_color_inhex:str, width:int, height:int,
def draw_rect(image:Image, x:int, y:int, width:int, height:int, line_color:str, line_width:int,
box_color:str=None) -> Image:
# image = image.convert('RGBA')
draw = ImageDraw.Draw(image)
draw.rectangle((x, y, x + width, y + height), fill=box_color, outline=line_color, width=line_width, )
return image
@@ -762,6 +730,53 @@ def get_image_color_average(image:Image) -> str:
ret_color = RGB_to_Hex(color)
return ret_color
def get_gray_average(image:Image, mask:Image=None) -> int:
# image.mode = 'HSV', mask.mode = 'L'
image = image.convert('HSV')
_, _, _v = image.convert('HSV').split()
if mask is not None:
if mask.mode != 'L':
mask = mask.convert('L')
width, height = image.size
total_gray = 0
valid_pixels = 0
for y in range(height):
for x in range(width):
if mask is not None:
if mask.getpixel((x, y)) > 16: #mask亮度低于16的忽略不计
gray = _v.getpixel((x, y))
total_gray += gray
valid_pixels += 1
else:
gray = _v.getpixel((x, y))
total_gray += gray
valid_pixels += 1
average_gray = total_gray // valid_pixels
return average_gray
def calculate_shadow_highlight_level(gray:int) -> float:
range = 255
shadow_exponent = 3
highlight_exponent = 2
shadow_ratio = gray ** shadow_exponent / range ** shadow_exponent
highlight_ratio = gray ** highlight_exponent / range ** highlight_exponent
shadow_level = shadow_ratio * 100 + (1 - shadow_ratio) * 32
highlight_level = highlight_ratio * 100 + (1 - highlight_ratio) * 32
return shadow_level, highlight_level
def luminance_keyer(image:Image, low:float=0, high:float=1, gamma:float=1) -> Image:
image = pil2tensor(image)
t = image[:, :, :, :3].detach().clone()
alpha = 0.2126 * t[:, :, :, 0] + 0.7152 * t[:, :, :, 1] + 0.0722 * t[:, :, :, 2]
if low == high:
alpha = (alpha > high).to(t.dtype)
else:
alpha = (alpha - low) / (high - low)
if gamma != 1.0:
alpha = torch.pow(alpha, 1 / gamma)
alpha = torch.clamp(alpha, min=0, max=1).unsqueeze(3).repeat(1, 1, 1, 3)
return tensor2pil(alpha).convert('L')
def get_image_bright_average(image:Image) -> int:
image = image.convert('L')
width, height = image.size
+101
View File
@@ -0,0 +1,101 @@
from .imagefunc import *
NODE_NAME = 'Shadow & Highlight Mask'
def norm_value(value):
if value < 0.01:
value = 0.01
if value > 0.99:
value = 0.99
return value
class ShadowAndHighlightMask:
def __init__(self):
pass
@classmethod
def INPUT_TYPES(self):
return {
"required": {
"image": ("IMAGE", ),
"shadow_level_offset": ("INT", {"default": 0, "min": -99, "max": 99, "step": 1}),
"shadow_range": ("FLOAT", {"default": 0.25, "min": 0.01, "max": 0.99, "step": 0.01}),
"highlight_level_offset": ("INT", {"default": 0, "min": -99, "max": 99, "step": 1}),
"highlight_range": ("FLOAT", {"default": 0.25, "min": 0.01, "max": 0.99, "step": 0.01}),
},
"optional": {
"mask": ("MASK",), #
}
}
RETURN_TYPES = ("MASK", "MASK")
RETURN_NAMES = ("shadow_mask", "highlight_mask")
FUNCTION = 'shadow_and_highlight_mask'
CATEGORY = '😺dzNodes/LayerMask'
OUTPUT_NODE = True
def shadow_and_highlight_mask(self, image,
shadow_level_offset, shadow_range,
highlight_level_offset, highlight_range,
mask=None
):
ret_shadow_masks = []
ret_highlight_masks = []
input_images = []
input_masks = []
for i in image:
input_images.append(torch.unsqueeze(i, 0))
m = tensor2pil(i)
if m.mode == 'RGBA':
input_masks.append(m.split()[-1])
else:
input_masks.append(Image.new('L', size=m.size, color='white'))
if mask is not None:
if mask.dim() == 2:
mask = torch.unsqueeze(mask, 0)
input_masks = []
for m in mask:
input_masks.append(tensor2pil(torch.unsqueeze(m, 0)).convert('L'))
max_batch = max(len(input_images), len(input_masks))
for i in range(max_batch):
_image = input_images[i] if i < len(input_images) else input_images[-1]
_image = tensor2pil(_image).convert('RGB')
_mask = input_masks[i] if i < len(input_masks) else input_masks[-1]
avg_gray = get_gray_average(_image, _mask)
shadow_level, highlight_level = calculate_shadow_highlight_level(avg_gray)
shadow_low_threshold = (shadow_level + shadow_level_offset) / 100 + shadow_range / 2
shadow_low_threshold = norm_value(shadow_low_threshold)
shadow_high_threshold = (shadow_level + shadow_level_offset) / 100 - shadow_range / 2
shadow_high_threshold = norm_value(shadow_high_threshold)
_shadow_mask = luminance_keyer(_image, shadow_low_threshold, shadow_high_threshold)
highlight_low_threshold = (highlight_level + highlight_level_offset) / 100 - highlight_range / 2
highlight_low_threshold = norm_value(highlight_low_threshold)
highlight_high_threshold = (highlight_level + highlight_level_offset) / 100 + highlight_range / 2
highlight_high_threshold = norm_value(highlight_high_threshold)
_highlight_mask = luminance_keyer(_image, highlight_low_threshold, highlight_high_threshold)
black = Image.new('L', size=_image.size, color='black')
_mask = ImageChops.invert(_mask)
_shadow_mask.paste(black, mask=_mask)
_highlight_mask.paste(black, mask=_mask)
ret_shadow_masks.append(image2mask(_shadow_mask))
ret_highlight_masks.append(image2mask(_highlight_mask))
log(f"{NODE_NAME} Processed {len(ret_shadow_masks)} image(s).", message_type='finish')
return (torch.cat(ret_shadow_masks, dim=0),torch.cat(ret_highlight_masks, dim=0),)
NODE_CLASS_MAPPINGS = {
"LayerMask: Shadow & Highlight Mask": ShadowAndHighlightMask
}
NODE_DISPLAY_NAME_MAPPINGS = {
"LayerMask: Shadow & Highlight Mask": "LayerMask: Shadow & Highlight Mask"
}
@@ -0,0 +1,145 @@
{
"last_node_id": 25,
"last_link_id": 45,
"nodes": [
{
"id": 15,
"type": "PreviewImage",
"pos": [
1190,
158
],
"size": {
"0": 649.0001220703125,
"1": 385.00006103515625
},
"flags": {},
"order": 2,
"mode": 0,
"inputs": [
{
"name": "images",
"type": "IMAGE",
"link": 42
}
],
"properties": {
"Node name for S&R": "PreviewImage"
}
},
{
"id": 12,
"type": "LoadImage",
"pos": [
194,
149
],
"size": {
"0": 504.2838134765625,
"1": 408.71527099609375
},
"flags": {},
"order": 0,
"mode": 0,
"outputs": [
{
"name": "IMAGE",
"type": "IMAGE",
"links": [
41
],
"shape": 3,
"slot_index": 0
},
{
"name": "MASK",
"type": "MASK",
"links": null,
"shape": 3
}
],
"properties": {
"Node name for S&R": "LoadImage"
},
"widgets_values": [
"image (6) (1).png",
"image"
]
},
{
"id": 22,
"type": "LayerColor: Color of Shadow & Highlight",
"pos": [
788,
233
],
"size": {
"0": 327.6000061035156,
"1": 294
},
"flags": {},
"order": 1,
"mode": 0,
"inputs": [
{
"name": "image",
"type": "IMAGE",
"link": 41
},
{
"name": "mask",
"type": "MASK",
"link": null
}
],
"outputs": [
{
"name": "image",
"type": "IMAGE",
"links": [
42
],
"shape": 3,
"slot_index": 0
}
],
"properties": {
"Node name for S&R": "LayerColor: Color of Shadow & Highlight"
},
"widgets_values": [
1,
1,
0,
0,
0.25,
0.88,
1.36,
-125,
12,
0.28
]
}
],
"links": [
[
41,
12,
0,
22,
0,
"IMAGE"
],
[
42,
22,
0,
15,
0,
"IMAGE"
]
],
"groups": [],
"config": {},
"extra": {},
"version": 0.4
}