Files
2024-02-12 15:05:44 +09:00

129 lines
4.6 KiB
Python

from PIL import Image, ImageColor, ImageDraw
import numpy as np
import torch
from .base import BaseNode
class GetComplementaryColor(BaseNode):
RETURN_TYPES = ("INT", "INT", "INT", "STRING")
RETURN_NAMES = (
"r",
"g",
"b",
"hex_color_code",
)
FUNCTION = "get_complementary_color"
@classmethod
def INPUT_TYPES(cls):
return {
"required": {
"r": ("INT", {"default": 0, "min": 0, "max": 255, "step": 1}),
"g": ("INT", {"default": 0, "min": 0, "max": 255, "step": 1}),
"b": ("INT", {"default": 0, "min": 0, "max": 255, "step": 1}),
"threshold": ("INT", {"default": 30, "min": 0, "max": 765, "step": 1}),
"alt_color_code": ("STRING", {"default": "#000000"}),
}
}
def get_complementary_color(self, r, g, b, threshold, alt_color_code):
x = min([r, g, b]) + max([r, g, b])
r2 = x - r
g2 = x - g
b2 = x - b
color_code = f"#{r2:02x}{g2:02x}{b2:02x}"
if abs(r2 - r) + abs(g2 - g) + abs(b2 - b) < threshold:
color_code = alt_color_code
r2, g2, b2 = ImageColor.getcolor(alt_color_code, "RGB")
return (r2, g2, b2, color_code)
class FloodFill(BaseNode):
RETURN_TYPES = ("IMAGE", "INT", "INT", "INT")
RETURN_NAMES = (
"image",
"filled_color_r",
"filled_color_g",
"filled_color_b",
)
FUNCTION = "flood_fill"
@classmethod
def INPUT_TYPES(cls):
return {
"required": {
"image": ("IMAGE",),
"x": ("INT", {"default": 0, "min": 0, "max": 99999, "step": 1}),
"y": ("INT", {"default": 0, "min": 0, "max": 99999, "step": 1}),
"pick_color": ("BOOLEAN", {"default": True}),
"r": ("INT", {"default": 255, "min": 0, "max": 255, "step": 1}),
"g": ("INT", {"default": 255, "min": 0, "max": 255, "step": 1}),
"b": ("INT", {"default": 255, "min": 0, "max": 255, "step": 1}),
"threshold": ("INT", {"default": 50, "min": 0, "max": 1000, "step": 1}),
}
}
def flood_fill(self, image, x, y, pick_color, r, g, b, threshold):
p = (x, y)
i = 255.0 * image[0].cpu().numpy()
img = Image.fromarray(np.clip(i, 0, 255).astype(np.uint8))
img = img.convert("RGB")
if pick_color:
r, g, b = img.getpixel(p)
self._flood_fill(img, p, (r, g, b), thresh=threshold)
filled_image = np.array(img).astype(np.float32) / 255.0
filled_image = torch.from_numpy(filled_image)[None,]
return filled_image, r, g, b
# refer. https://github.com/python-pillow/Pillow/blob/5d6f22da122c6620331fd11bc8fd8a88cbc11132/src/PIL/ImageDraw.py#L897
def _flood_fill(self, image, xy, value, thresh):
pixel = image.load()
x, y = xy
try:
background = pixel[x, y]
# if _color_diff(value, background) <= thresh:
# return # seed point already has fill color
pixel[x, y] = value
except (ValueError, IndexError):
return # seed point outside image
edge = {(x, y)}
# use a set to keep record of current and previous edge pixels
# to reduce memory consumption
full_edge = set()
while edge:
new_edge = set()
for x, y in edge: # 4 adjacent method
for s, t in ((x + 1, y), (x - 1, y), (x, y + 1), (x, y - 1)):
# If already processed, or if a coordinate is negative, skip
if (s, t) in full_edge or s < 0 or t < 0:
continue
try:
p = pixel[s, t]
except (ValueError, IndexError):
pass
else:
full_edge.add((s, t))
fill = self._color_diff(p, background) <= thresh
if fill:
pixel[s, t] = value
new_edge.add((s, t))
full_edge = edge # discard pixels processed
edge = new_edge
# refer. https://github.com/python-pillow/Pillow/blob/5d6f22da122c6620331fd11bc8fd8a88cbc11132/src/PIL/ImageDraw.py#L1076
def _color_diff(self, color1, color2) -> float:
"""
Uses 1-norm distance to calculate difference between two values.
"""
if isinstance(color2, tuple):
return sum(abs(color1[i] - color2[i]) for i in range(0, len(color2)))
else:
return abs(color1 - color2)