Commit GetColorTone, MaskGrow and MaskEdgeShink nodes.
Commit GetColorTone node, can obtain the main color or average color of the image. Commit MaskGrow and MaskEdgeShink node.
This commit is contained in:
@@ -0,0 +1,38 @@
|
||||
from .imagefunc import *
|
||||
|
||||
class ColorImage:
|
||||
|
||||
def __init__(self):
|
||||
pass
|
||||
|
||||
@classmethod
|
||||
def INPUT_TYPES(self):
|
||||
|
||||
return {
|
||||
"required": {
|
||||
"width": ("INT", {"default": 512, "min": 4, "max": 99999, "step": 1}),
|
||||
"height": ("INT", {"default": 512, "min": 4, "max": 99999, "step": 1}),
|
||||
"color": ("STRING", {"default": "#000000"},),
|
||||
},
|
||||
"optional": {
|
||||
}
|
||||
}
|
||||
|
||||
RETURN_TYPES = ("IMAGE", )
|
||||
RETURN_NAMES = ("image", )
|
||||
FUNCTION = 'color_image'
|
||||
CATEGORY = '😺dzNodes/LayerUtility'
|
||||
OUTPUT_NODE = True
|
||||
|
||||
def color_image(self, width, height, color, ):
|
||||
|
||||
ret_image = Image.new('RGB', (width, height), color=color)
|
||||
return (pil2tensor(ret_image), )
|
||||
|
||||
NODE_CLASS_MAPPINGS = {
|
||||
"LayerUtility: ColorImage": ColorImage
|
||||
}
|
||||
|
||||
NODE_DISPLAY_NAME_MAPPINGS = {
|
||||
"LayerUtility: ColorImage": "LayerUtility: ColorImage"
|
||||
}
|
||||
+1
-3
@@ -1,8 +1,6 @@
|
||||
|
||||
from .imagefunc import Hex_to_RGB
|
||||
|
||||
mode_list = ['HEX', 'DEC']
|
||||
|
||||
class ColorPicker:
|
||||
|
||||
def __init__(self):
|
||||
@@ -10,7 +8,7 @@ class ColorPicker:
|
||||
|
||||
@classmethod
|
||||
def INPUT_TYPES(self):
|
||||
|
||||
mode_list = ['HEX', 'DEC']
|
||||
return {
|
||||
"required": {
|
||||
"color": ("COLOR", {"default": "#FFFFFF"},),
|
||||
|
||||
@@ -0,0 +1,43 @@
|
||||
from .imagefunc import *
|
||||
|
||||
class GetColorTone:
|
||||
|
||||
def __init__(self):
|
||||
pass
|
||||
|
||||
@classmethod
|
||||
def INPUT_TYPES(self):
|
||||
mode_list = ['main_color', 'average']
|
||||
return {
|
||||
"required": {
|
||||
"image": ("IMAGE", ), #
|
||||
"mode": (mode_list,), # 主色/平均色
|
||||
},
|
||||
"optional": {
|
||||
}
|
||||
}
|
||||
|
||||
RETURN_TYPES = ("STRING",)
|
||||
RETURN_NAMES = ("RGB color in HEX",)
|
||||
FUNCTION = 'get_color_tone'
|
||||
CATEGORY = '😺dzNodes/LayerUtility'
|
||||
OUTPUT_NODE = True
|
||||
|
||||
def get_color_tone(self, image, mode,):
|
||||
|
||||
_canvas = tensor2pil(image).convert('RGB')
|
||||
_canvas = gaussian_blur(_canvas, int((_canvas.width + _canvas.height) / 200))
|
||||
if mode == 'main_color':
|
||||
ret_color = get_image_color_tone(_canvas)
|
||||
else:
|
||||
ret_color = get_image_color_average(_canvas)
|
||||
|
||||
return (ret_color,)
|
||||
|
||||
NODE_CLASS_MAPPINGS = {
|
||||
"LayerUtility: GetColorTone": GetColorTone
|
||||
}
|
||||
|
||||
NODE_DISPLAY_NAME_MAPPINGS = {
|
||||
"LayerUtility: GetColorTone": "LayerUtility: GetColorTone"
|
||||
}
|
||||
@@ -1,9 +1,5 @@
|
||||
from .imagefunc import *
|
||||
|
||||
def process(image:Image) -> Image:
|
||||
|
||||
return image
|
||||
|
||||
class GetImageSize:
|
||||
|
||||
def __init__(self):
|
||||
|
||||
@@ -0,0 +1,41 @@
|
||||
from .imagefunc import *
|
||||
|
||||
class GradientImage:
|
||||
|
||||
def __init__(self):
|
||||
pass
|
||||
|
||||
@classmethod
|
||||
def INPUT_TYPES(self):
|
||||
|
||||
return {
|
||||
"required": {
|
||||
"width": ("INT", {"default": 512, "min": 4, "max": 99999, "step": 1}),
|
||||
"height": ("INT", {"default": 512, "min": 4, "max": 99999, "step": 1}),
|
||||
"angle": ("INT", {"default": 0, "min": -360, "max": 360, "step": 1}),
|
||||
"start_color": ("STRING", {"default": "#FFFFFF"},),
|
||||
"end_color": ("STRING", {"default": "#000000"},),
|
||||
},
|
||||
"optional": {
|
||||
}
|
||||
}
|
||||
|
||||
RETURN_TYPES = ("IMAGE", )
|
||||
RETURN_NAMES = ("image", )
|
||||
FUNCTION = 'gradient_image'
|
||||
CATEGORY = '😺dzNodes/LayerUtility'
|
||||
OUTPUT_NODE = True
|
||||
|
||||
def gradient_image(self, width, height, angle, start_color, end_color, ):
|
||||
|
||||
ret_image = gradint(start_color, end_color, width, height, angle)
|
||||
|
||||
return (pil2tensor(ret_image), )
|
||||
|
||||
NODE_CLASS_MAPPINGS = {
|
||||
"LayerUtility: GradientImage": GradientImage
|
||||
}
|
||||
|
||||
NODE_DISPLAY_NAME_MAPPINGS = {
|
||||
"LayerUtility: GradientImage": "LayerUtility: GradientImage"
|
||||
}
|
||||
@@ -7,6 +7,7 @@ import scipy.ndimage
|
||||
import cv2
|
||||
from typing import Union, List
|
||||
from PIL import Image, ImageFilter, ImageChops, ImageDraw
|
||||
import colorsys
|
||||
|
||||
def log(message):
|
||||
name = 'LayerStyle'
|
||||
@@ -213,6 +214,42 @@ def draw_rect(image:Image, x:int, y:int, width:int, height:int, line_color:str,
|
||||
draw.rectangle((x, y, x + width, y + height), fill=box_color, outline=line_color, width=line_width, )
|
||||
return image
|
||||
|
||||
def get_image_color_tone(image:Image) -> str:
|
||||
image = image.convert('RGB')
|
||||
max_score = 0.0001
|
||||
dominant_color = None
|
||||
for count, (r, g, b) in image.getcolors(image.size[0] * image.size[1]):
|
||||
saturation = colorsys.rgb_to_hsv(r / 255.0, g / 255.0, b / 255.0)[1]
|
||||
y = min(abs(r * 2104 + g * 4130 + b * 802 + 4096 + 131072) >> 13,235)
|
||||
y = (y - 16.0) / (235 - 16)
|
||||
if y>0.9:
|
||||
continue
|
||||
score = (saturation+0.1)*count
|
||||
if score > max_score:
|
||||
max_score = score
|
||||
dominant_color = (r, g, b)
|
||||
ret_color = RGB_to_Hex(dominant_color)
|
||||
return ret_color
|
||||
|
||||
def get_image_color_average(image:Image) -> str:
|
||||
image = image.convert('RGB')
|
||||
width, height = image.size
|
||||
total_red = 0
|
||||
total_green = 0
|
||||
total_blue = 0
|
||||
for y in range(height):
|
||||
for x in range(width):
|
||||
rgb = image.getpixel((x, y))
|
||||
total_red += rgb[0]
|
||||
total_green += rgb[1]
|
||||
total_blue += rgb[2]
|
||||
|
||||
average_red = total_red // (width * height)
|
||||
average_green = total_green // (width * height)
|
||||
average_blue = total_blue // (width * height)
|
||||
color = (average_red, average_green, average_blue)
|
||||
ret_color = RGB_to_Hex(color)
|
||||
return ret_color
|
||||
|
||||
'''Mask Functions'''
|
||||
|
||||
|
||||
@@ -0,0 +1,68 @@
|
||||
from .imagefunc import *
|
||||
|
||||
class MaskEdgeShrink:
|
||||
|
||||
def __init__(self):
|
||||
pass
|
||||
|
||||
@classmethod
|
||||
def INPUT_TYPES(self):
|
||||
|
||||
return {
|
||||
"required": {
|
||||
"mask": ("MASK", ), #
|
||||
"invert_mask": ("BOOLEAN", {"default": True}), # 反转mask
|
||||
"shrink_level": ("INT", {"default": 4, "min": 0, "max": 16, "step": 1}),
|
||||
"soft": ("INT", {"default": 6, "min": 0, "max": 64, "step": 1}),
|
||||
"edge_shrink": ("INT", {"default": 1, "min": 0, "max": 999, "step": 1}),
|
||||
"edge_reserve": ("INT", {"default": 25, "min": 0, "max": 100, "step": 1}), # 透明度
|
||||
},
|
||||
"optional": {
|
||||
}
|
||||
}
|
||||
|
||||
RETURN_TYPES = ("MASK",)
|
||||
RETURN_NAMES = ("mask",)
|
||||
FUNCTION = 'mask_edge_shrink'
|
||||
CATEGORY = '😺dzNodes/LayerMask'
|
||||
OUTPUT_NODE = True
|
||||
|
||||
def mask_edge_shrink(self, mask, invert_mask, shrink_level, soft, edge_shrink, edge_reserve):
|
||||
|
||||
glow_range = shrink_level * soft
|
||||
blur = 12
|
||||
_inputmask = mask2image(mask)
|
||||
_canvas = Image.new('RGB', size=_inputmask.size, color='black')
|
||||
_layer = Image.new('RGB', size=_inputmask.size, color='white')
|
||||
|
||||
if invert_mask:
|
||||
mask = 1 - mask
|
||||
_mask = mask2image(mask).convert('L')
|
||||
|
||||
loop_grow = glow_range
|
||||
inner_mask = _mask
|
||||
for x in range(shrink_level):
|
||||
|
||||
_color = step_color('#FFFFFF', '#000000', shrink_level, x)
|
||||
glow_mask = expand_mask(image2mask(inner_mask), -loop_grow, blur / (x+0.1)) #扩张,模糊
|
||||
# 合成
|
||||
color_image = Image.new("RGB", _layer.size, color=_color)
|
||||
alpha = tensor2pil(mask_invert(glow_mask)).convert('L')
|
||||
_glow = chop_image(_layer, color_image, 'subtract', int(step_value(1, 100, shrink_level, x)))
|
||||
_layer.paste(_glow, mask=alpha)
|
||||
loop_grow = loop_grow - int(glow_range / shrink_level)
|
||||
# 合成layer
|
||||
_edge = tensor2pil(expand_mask(mask, -edge_shrink, 0)).convert('RGB')
|
||||
_layer = chop_image(_layer, _edge, 'normal', edge_reserve)
|
||||
_layer.paste(_canvas, mask=ImageChops.invert(_mask))
|
||||
ret_mask = image2mask(_layer)
|
||||
|
||||
return (ret_mask,)
|
||||
|
||||
NODE_CLASS_MAPPINGS = {
|
||||
"LayerMask: MaskEdgeShrink": MaskEdgeShrink
|
||||
}
|
||||
|
||||
NODE_DISPLAY_NAME_MAPPINGS = {
|
||||
"LayerMask: MaskEdgeShrink": "LayerMask: MaskEdgeShrink"
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
from .imagefunc import *
|
||||
|
||||
class MaskGrow:
|
||||
|
||||
def __init__(self):
|
||||
pass
|
||||
|
||||
@classmethod
|
||||
def INPUT_TYPES(self):
|
||||
|
||||
return {
|
||||
"required": {
|
||||
"mask": ("MASK", ), #
|
||||
"invert_mask": ("BOOLEAN", {"default": True}), # 反转mask
|
||||
"grow": ("INT", {"default": 4, "min": -999, "max": 999, "step": 1}),
|
||||
"blur": ("INT", {"default": 4, "min": 0, "max": 999, "step": 1}),
|
||||
},
|
||||
"optional": {
|
||||
}
|
||||
}
|
||||
|
||||
RETURN_TYPES = ("MASK",)
|
||||
RETURN_NAMES = ("mask",)
|
||||
FUNCTION = 'mask_grow'
|
||||
CATEGORY = '😺dzNodes/LayerMask'
|
||||
OUTPUT_NODE = True
|
||||
|
||||
def mask_grow(self, mask, invert_mask, grow, blur,):
|
||||
|
||||
if invert_mask:
|
||||
mask = 1 - mask
|
||||
# _mask = mask2image(mask).convert('L')
|
||||
|
||||
ret_mask = expand_mask(mask, grow, blur) # 扩张,模糊
|
||||
|
||||
return (ret_mask,)
|
||||
|
||||
NODE_CLASS_MAPPINGS = {
|
||||
"LayerMask: MaskGrow": MaskGrow
|
||||
}
|
||||
|
||||
NODE_DISPLAY_NAME_MAPPINGS = {
|
||||
"LayerMask: MaskGrow": "LayerMask: MaskGrow"
|
||||
}
|
||||
@@ -1,9 +1,5 @@
|
||||
from .imagefunc import *
|
||||
|
||||
def process(image:Image) -> Image:
|
||||
|
||||
return image
|
||||
|
||||
class XYtoPercent:
|
||||
|
||||
def __init__(self):
|
||||
|
||||
Reference in New Issue
Block a user