commit HSVValue, CheckMask nodes, and CropByMaskV2 node add round_to_multiple option

This commit is contained in:
chflame163
2024-06-24 19:54:14 +08:00
parent 6ceb41f92e
commit 318c2e26d4
10 changed files with 171 additions and 14 deletions
+18
View File
@@ -80,6 +80,8 @@ When this error has occurred, please check the network environment.
## Update
<font size="4">**If the dependency package error after updating, please reinstall the relevant dependency packages. </font><br />
* [CropByMaskV2](#CropByMaskV2) add option to round the cutting size by multiples.
* Commit [CheckMask](#CheckMask) node, it detect whether the mask contains sufficient effective areas. Commit [HSVValue](#HSVValue) node, it convert color values to HSV values.
* [BooleanOperatorV2](#BooleanOperatorV2), [NumberCalculatorV2](#NumberCalculatorV2), [Integer](#Integer), [Float](#Float), [Boolean](#Boolean) nodes add string output to output the value as a string for use with [SwitchCase](#SwitchCase).
* Commit [SwitchCase](#SwitchCase) node, Switches the output based on the matching string. Can be used for any type of data switching.
* Commit [String](#String) node, Used to output a string. It is the TextBox simplified node.
@@ -564,6 +566,7 @@ The following changes have been made based on CropByMask:
![image](image/corp_by_mask_v2_node.jpg)
* The input ```mask_for_crop``` reanme to ```mask```。
* Add optional inputs to the ```crop_box```. If there are inputs here, mask detection will be ignored and this data will be directly used for cropping.
* Add the option ```round_to_multiple``` to round the trimming edge length multiple. For example, setting it to 8 will force the width and height to be multiples of 8.
### <a id="table1">RestoreCropBox</a>
Restore the cropped image to the original image by [CropByMask](#CropByMask).
@@ -768,6 +771,13 @@ Output the color value as a single R, G, B three decimal values. Supports HEX an
Node Options:
* color_value: Supports hexadecimal (HEX) or decimal (DEC) color values and should be of string or tuple type. Forcing in other types will result in an error.
### <a id="table1">HSVValue</a>
Output color values as individual decimal values of H, S, and V (maximum value of 255). Supports HEX and DEC formats for ColorPicker node output.
![image](image/hsv_value_node.jpg)
Node Options:
* color_value: Supports hexadecimal (HEX) or decimal (DEC) color values and should be of string or tuple type. Forcing in other types will result in an error.
### <a id="table1">GetColorTone</a>
Obtain the main color or average color from the image and output RGB values.
![image](image/get_color_tone_example.jpg)
@@ -1169,6 +1179,14 @@ Node Options:
* condition: Judgment conditions. ```include``` determines whether it contains a substring, and ```exclude``` determines whether it does not.
* sub_string: Substring.
### <a id="table1">CheckMask</a>
Check if the mask contains enough valid areas and output a Boolean value.
Node Options:
![image](image/check_mask_node.jpg)
* white_point: The white point threshold used to determine whether the mask is valid is considered valid if it exceeds this value.
* area_percent: The percentage of effective areas. If the proportion of effective areas exceeds this value, output True.
### <a id="table1">If</a>
![image](image/if_example.jpg)
+18
View File
@@ -80,6 +80,9 @@ git clone https://github.com/chflame163/ComfyUI_LayerStyle.git
## 更新说明
<font size="4">**如果本插件更新后出现依赖包错误,请重新安装相关依赖包。
* [CropByMaskV2](#CropByMaskV2) 增加裁切尺寸按倍数取整选项。
* 添加 [CheckMask](#CheckMask) 节点, 用于检测遮罩是否包含足够的有效区域。
* 添加 [HSVValue](#HSVValue) 节点, 用于转换色值为HSV值。
* [BooleanOperatorV2](#BooleanOperatorV2), [NumberCalculatorV2](#NumberCalculatorV2), [Integer](#Integer), [Float](#Float), [Boolean](#Boolean)节点增加string输出,将值输出为字符串,以配合[SwitchCase](#SwitchCase)使用。
* 添加 [SwitchCase](#SwitchCase) 节点, 根据匹配字符串切换输出。可用于任意类型的数据切换。
* 添加 [String](#String) 节点, 用于输出字符串。这是TextBox简化版节点。
@@ -556,6 +559,7 @@ CropByMask的V2升级版。支持crop_box输入,方便裁切相同尺寸的图
![image](image/corp_by_mask_v2_node.jpg)
* ```mask_for_crop```更名为```mask```。
* 增加```crop_box```可选输入,如果这里有输入将忽略遮罩探测,直接使用此数据裁切。
* 增加```round_to_multiple```选项,使裁切边长倍数取整。例如设置为8,宽和高将强制设置为8的倍数。
### <a id="table1">RestoreCropBox</a>
@@ -758,6 +762,12 @@ ImageScaleByAspectRatio的V2升级版
节点选项说明:
* color_value: 支持十六进制(HEX)或十进制(DEC)色值,应是string或tuple类型,强行接入其他类型将导致错误。
### <a id="table1">HSVValue</a>
将色值输出为单独的H, S, V三个10进制数值(最大值255)。支持ColorPicker节点输出的HEX和DEC格式。
![image](image/hsv_value_node.jpg)
节点选项说明:
* color_value: 支持十六进制(HEX)或十进制(DEC)色值,应是string或tuple类型,强行接入其他类型将导致错误。
### <a id="table1">GetColorTone</a>
从图片中获取主颜色或平均色。
@@ -1160,6 +1170,14 @@ BooleanOperator的升级版,增加了节点内数值输入,增加了大于
* sub_string: 子字符串文本。
### <a id="table1">CheckMask</a>
检测遮罩是否包含足够的有效区域, 输出布尔值。
节点选项说明:
![image](image/check_mask_node.jpg)
* white_point: 判断遮罩是否有效的白点值,高于此值被计入有效。
* area_percent: 有效区域所占百分比。检测有效区域占比超过此值则输出True。
### <a id="table1">If</a>
![image](image/if_example.jpg)
根据布尔值条件输入切换输出。可用于任意类型的数据切换,包括且不限于数值、字符串、图片、遮罩、模型、latent、pipe管线等。
Binary file not shown.

After

Width:  |  Height:  |  Size: 34 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 79 KiB

After

Width:  |  Height:  |  Size: 80 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 26 KiB

+46
View File
@@ -0,0 +1,46 @@
from .imagefunc import *
NODE_NAME = 'CheckMask'
# 检查mask是否有效,如果mask面积少于指定比例则判为无效mask
class CheckMask:
def __init__(self):
pass
@classmethod
def INPUT_TYPES(self):
blank_mask_list = ['white', 'black']
return {
"required": {
"mask": ("MASK",), #
"white_point": ("INT", {"default": 1, "min": 1, "max": 254, "step": 1}), # 用于判断mask是否有效的白点值,高于此值被计入有效
"area_percent": ("INT", {"default": 1, "min": 1, "max": 99, "step": 1}), # 区域百分比,低于此则mask判定无效
},
"optional": { #
}
}
RETURN_TYPES = ("BOOLEAN",)
RETURN_NAMES = ('bool',)
FUNCTION = 'check_mask'
CATEGORY = '😺dzNodes/LayerUtility'
def check_mask(self, mask, white_point, area_percent,):
if mask.dim() == 2:
mask = torch.unsqueeze(mask, 0)
mask = tensor2pil(mask[0])
if mask.width * mask.height > 262144:
target_width = 512
target_height = int(target_width * mask.height / mask.width)
mask = mask.resize((target_width, target_height), Image.LANCZOS)
return (mask_white_area(mask, white_point) * 100 > area_percent,)
NODE_CLASS_MAPPINGS = {
"LayerUtility: CheckMask": CheckMask
}
NODE_DISPLAY_NAME_MAPPINGS = {
"LayerUtility: CheckMask": "LayerUtility: Check Mask"
}
+44
View File
@@ -0,0 +1,44 @@
from .imagefunc import *
NODE_NAME = 'HSV Value'
any = AnyType("*")
class ColorValuetoHSVValue:
def __init__(self):
pass
@classmethod
def INPUT_TYPES(self):
return {
"required": {
"color_value": (any, {}),
},
"optional": {
}
}
RETURN_TYPES = ("INT", "INT", "INT")
RETURN_NAMES = ("H", "S", "V")
FUNCTION = 'color_value_to_hsv_value'
CATEGORY = '😺dzNodes/LayerUtility/Data'
def color_value_to_hsv_value(self, color_value,):
H, S, V = 0, 0, 0
if isinstance(color_value, str):
H, S, V = Hex_to_HSV_255level(color_value)
elif isinstance(color_value, tuple):
H, S, V = Hex_to_HSV_255level(RGB_to_Hex(color_value))
else:
log(f"{NODE_NAME}: color_value input type must be tuple or string.", message_type="error")
return (H, S, V,)
NODE_CLASS_MAPPINGS = {
"LayerUtility: HSV Value": ColorValuetoHSVValue
}
NODE_DISPLAY_NAME_MAPPINGS = {
"LayerUtility: HSV Value": "LayerUtility: HSV Value"
}
+22 -13
View File
@@ -9,7 +9,8 @@ class CropByMaskV2:
@classmethod
def INPUT_TYPES(self):
detect_mode = ['min_bounding_rect', 'max_inscribed_rect', 'mask_area']
detect_mode = ['mask_area', 'min_bounding_rect', 'max_inscribed_rect']
multiple_list = ['8', '16', '32', '64', 'None']
return {
"required": {
"image": ("IMAGE", ), #
@@ -20,6 +21,7 @@ class CropByMaskV2:
"bottom_reserve": ("INT", {"default": 20, "min": -9999, "max": 9999, "step": 1}),
"left_reserve": ("INT", {"default": 20, "min": -9999, "max": 9999, "step": 1}),
"right_reserve": ("INT", {"default": 20, "min": -9999, "max": 9999, "step": 1}),
"round_to_multiple": (multiple_list,),
},
"optional": {
"crop_box": ("BOX",),
@@ -33,7 +35,7 @@ class CropByMaskV2:
def crop_by_mask_v2(self, image, mask, invert_mask, detect,
top_reserve, bottom_reserve,
left_reserve, right_reserve,
left_reserve, right_reserve, round_to_multiple,
crop_box=None
):
@@ -63,24 +65,31 @@ class CropByMaskV2:
width = 0
height = 0
if detect == "min_bounding_rect":
(x, y, width, height) = min_bounding_rect(bluredmask)
(x, y, w, h) = min_bounding_rect(bluredmask)
elif detect == "max_inscribed_rect":
(x, y, width, height) = max_inscribed_rect(bluredmask)
(x, y, w, h) = max_inscribed_rect(bluredmask)
else:
(x, y, width, height) = mask_area(_mask)
(x, y, w, h) = mask_area(_mask)
width = num_round_to_multiple(width, 8)
height = num_round_to_multiple(height, 8)
log(f"{NODE_NAME}: Box detected. x={x},y={y},width={width},height={height}")
canvas_width, canvas_height = tensor2pil(torch.unsqueeze(image[0], 0)).convert('RGB').size
x1 = x - left_reserve if x - left_reserve > 0 else 0
y1 = y - top_reserve if y - top_reserve > 0 else 0
x2 = x + width + right_reserve if x + width + right_reserve < canvas_width else canvas_width
y2 = y + height + bottom_reserve if y + height + bottom_reserve < canvas_height else canvas_height
x2 = x + w + right_reserve if x + w + right_reserve < canvas_width else canvas_width
y2 = y + h + bottom_reserve if y + h + bottom_reserve < canvas_height else canvas_height
if round_to_multiple != 'None':
multiple = int(round_to_multiple)
width = num_round_up_to_multiple(x2 - x1, multiple)
height = num_round_up_to_multiple(y2 - y1, multiple)
x1 = x1 - (width - (x2 - x1)) // 2
y1 = y1 - (height - (y2 - y1)) // 2
x2 = x1 + width
y2 = y1 + height
log(f"{NODE_NAME}: Box detected. x={x1},y={y1},width={width},height={height}")
crop_box = (x1, y1, x2, y2)
preview_image = draw_rect(preview_image, x, y, width, height, line_color="#F00000",
line_width=(width + height) // 100)
preview_image = draw_rect(preview_image, x, y, w, h, line_color="#F00000",
line_width=(w + h) // 100)
preview_image = draw_rect(preview_image, crop_box[0], crop_box[1],
crop_box[2] - crop_box[0], crop_box[3] - crop_box[1],
line_color="#00F000",
+22
View File
@@ -1631,6 +1631,18 @@ def gray_threshold(image:Image, thresh:int=127, otsu:bool=False) -> Image:
def image_to_colormap(image:Image, index:int) -> Image:
return cv22pil(cv2.applyColorMap(pil2cv2(image), index))
# 检查mask有效区域面积比例
def mask_white_area(mask:Image, white_point:int) -> float:
if mask.mode != 'L':
mask.convert('L')
white_pixels = 0
for y in range(mask.height):
for x in range(mask.width):
mask.getpixel((x, y)) > 16
if mask.getpixel((x, y)) > white_point:
white_pixels += 1
return white_pixels / (mask.width * mask.height)
'''Color Functions'''
@@ -1766,6 +1778,7 @@ def random_numbers(total:int, random_range:int, seed:int=0, sum_of_numbers:int=0
ret_list.append((sum_of_numbers - sum(ret_list)) // 2)
return ret_list
# 四舍五入取整数倍
def num_round_to_multiple(number:int, multiple:int) -> int:
remainder = number % multiple
if remainder == 0 :
@@ -1776,6 +1789,15 @@ def num_round_to_multiple(number:int, multiple:int) -> int:
factor += 1
return factor * multiple
# 向上取整数倍
def num_round_up_to_multiple(number: int, multiple: int) -> int:
remainder = number % multiple
if remainder == 0:
return number
else:
factor = (number + multiple - 1) // multiple # 向上取整的计算方式
return factor * multiple
def calculate_side_by_ratio(orig_width:int, orig_height:int, ratio:float, longest_side:int=0) -> int:
if orig_width > orig_height:
+1 -1
View File
@@ -1,7 +1,7 @@
[project]
name = "comfyui_layerstyle"
description = "A set of nodes for ComfyUI it generate image like Adobe Photoshop's Layer Style. the Drop Shadow is first completed node, and follow-up work is in progress."
version = "1.0.8"
version = "1.0.9"
license = "MIT"
dependencies = ["numpy", "pillow", "torch", "matplotlib", "Scipy", "scikit_image", "opencv-contrib-python", "pymatting", "segment_anything", "timm", "addict", "yapf", "colour-science", "wget", "mediapipe", "loguru", "typer_config", "fastapi", "rich", "google-generativeai", "diffusers", "omegaconf", "tqdm", "transformers", "kornia", "image-reward", "ultralytics", "blend_modes", "blind-watermark", "qrcode", "pyzbar", "psd-tools"]