update for support batch images
This commit is contained in:
@@ -13,8 +13,8 @@ Nodes are divided into 5 groups according to their functions: LayerStyle, LayerC
|
||||
[中文说明点这里](./README_CN.MD)
|
||||
|
||||
## Update
|
||||
* Comprehensive support for batch images, providing convenience for video creation. * to avoid unpredictable results, a few nodes such as CropByMask and RestoryCropBox still do not support batch images.
|
||||
If it is necessary to use these nodes in the batch process, please pre convert them using the [Impact-Pack's Image Batch To Image List](https://github.com/ltdrdata/ComfyUI-Impact-Pack) node.
|
||||
* All nodes have fully supported batch images, providing convenience for video creation.
|
||||
(The CropByMask node only supports cuts of the same size. if a batch mask_for_crop inputted, the data from the first sheet will be used.)
|
||||
* Commit [RemBgUltra](#RemBgUltra) and [PixelSpread](#PixelSpread) nodes significantly improved mask quality. *RemBgUltra requires manual model download.
|
||||
* Commit [TextImage](#TextImage) node, it generate text images and masks.
|
||||
* Add new types of [blend mode](#blend) between images. now supports up to 19 blend modes. add **color_burn, color_dodge, linear_burn, linear_dodge, overlay, soft_light, hard_light, vivid_light, pin_light, linear_light** and **hard_mix**.
|
||||
|
||||
+1
-1
@@ -10,7 +10,7 @@
|
||||
* [LayerFilter](#LayerFilter)节点组提供图像效果滤镜。
|
||||
|
||||
## 更新说明、
|
||||
* 全面支持批量图片,为创作视频提供方便。* 为避免不可预测的结果,CropByMask、RestoryCropBox等少数几个节点仍不支持批量图片。如果一定要在批量流程中使用这些节点,请使用[Impact-Pack 的 Image Batch To Image List](https://github.com/ltdrdata/ComfyUI-Impact-Pack)节点预先转换。
|
||||
* 所有节点已全面支持批量图片,为创作视频提供方便。( CropByMask 节点仅支持相同尺寸的切除, 如果输入批量mask_for_crop,将使用第一张的数据。)
|
||||
* 添加[RemBgUltra](#RemBgUltra) 和 [PixelSpread](#PixelSpread) 节点,显著提升了遮罩质量。*RemBgUltra需手动下载模型。
|
||||
* 添加[TextImage](#TextImage) 节点,生成文字图像和遮罩。
|
||||
* 图像之间的[混合模式](#混合模式)增加新类型,现在支持多达19种混合模式。新增color_burn颜色加深, color_dodge颜色减淡, linear_burn线性加深, linear_dodge线性减淡, overlay叠加, soft_light柔光, hard_light强光, vivid_light亮光, pin_light点光, linear_light线性光, hard_mix实色混合。新增的混合模式适用于所有支持混合模式的节点。
|
||||
|
||||
+6
-5
@@ -1,7 +1,8 @@
|
||||
import math
|
||||
|
||||
from .imagefunc import *
|
||||
|
||||
NODE_NAME = 'ChannelShake'
|
||||
|
||||
class ChannelShake:
|
||||
|
||||
def __init__(self):
|
||||
@@ -31,9 +32,9 @@ class ChannelShake:
|
||||
|
||||
ret_images = []
|
||||
|
||||
for image in image:
|
||||
|
||||
_canvas = tensor2pil(image).convert('RGB')
|
||||
for i in image:
|
||||
i = torch.unsqueeze(i, 0)
|
||||
_canvas = tensor2pil(i).convert('RGB')
|
||||
R, G, B = _canvas.split()
|
||||
x = int(math.cos(angle) * distance)
|
||||
y = int(math.sin(angle) * distance)
|
||||
@@ -53,7 +54,7 @@ class ChannelShake:
|
||||
ret_image = Image.merge('RGB', [R, G, B])
|
||||
ret_images.append(pil2tensor(ret_image))
|
||||
|
||||
log(f'ChannelShake Processed {len(ret_images)} image(s).')
|
||||
log(f"{NODE_NAME} Processed {len(ret_images)} image(s).")
|
||||
return (torch.cat(ret_images, dim=0),)
|
||||
|
||||
NODE_CLASS_MAPPINGS = {
|
||||
|
||||
+18
-4
@@ -1,5 +1,9 @@
|
||||
import torch
|
||||
|
||||
from .imagefunc import *
|
||||
|
||||
NODE_NAME = 'ColorAdapter'
|
||||
|
||||
class ColorAdapter:
|
||||
|
||||
def __init__(self):
|
||||
@@ -26,16 +30,26 @@ class ColorAdapter:
|
||||
|
||||
def color_adapter(self, image, color_ref_image, opacity):
|
||||
ret_images = []
|
||||
# if color_ref_image.shape[0] > 0:
|
||||
# color_ref_image = torch.unsqueeze(color_ref_image[0], 0)
|
||||
|
||||
for image in image:
|
||||
l_images = []
|
||||
r_images = []
|
||||
for l in image:
|
||||
l_images.append(torch.unsqueeze(l, 0))
|
||||
for r in color_ref_image:
|
||||
r_images.append(torch.unsqueeze(r, 0))
|
||||
for i in range(len(l_images)):
|
||||
_image = l_images[i]
|
||||
_ref = r_images[i] if len(ret_images) > i else r_images[-1]
|
||||
|
||||
_canvas = tensor2pil(image).convert('RGB')
|
||||
ret_image = color_adapter(_canvas, tensor2pil(color_ref_image).convert('RGB'))
|
||||
_canvas = tensor2pil(_image).convert('RGB')
|
||||
ret_image = color_adapter(_canvas, tensor2pil(_ref).convert('RGB'))
|
||||
ret_image = chop_image(_canvas, ret_image, blend_mode='normal', opacity=opacity)
|
||||
|
||||
ret_images.append(pil2tensor(ret_image))
|
||||
|
||||
log(f'ColorAdapter Processed {len(ret_images)} image(s).')
|
||||
log(f"{NODE_NAME} Processed {len(ret_images)} image(s).")
|
||||
return (torch.cat(ret_images, dim=0),)
|
||||
|
||||
NODE_CLASS_MAPPINGS = {
|
||||
|
||||
@@ -1,5 +1,9 @@
|
||||
import torch
|
||||
|
||||
from .imagefunc import *
|
||||
|
||||
NODE_NAME = 'HSV'
|
||||
|
||||
class ColorCorrectHSV:
|
||||
|
||||
def __init__(self):
|
||||
@@ -29,9 +33,9 @@ class ColorCorrectHSV:
|
||||
|
||||
ret_images = []
|
||||
|
||||
for image in image:
|
||||
|
||||
_h, _s, _v = tensor2pil(image).convert('HSV').split()
|
||||
for i in image:
|
||||
i = torch.unsqueeze(i,0)
|
||||
_h, _s, _v = tensor2pil(i).convert('HSV').split()
|
||||
if H != 0 :
|
||||
_h = image_hue_offset(_h, H)
|
||||
if S != 0 :
|
||||
@@ -42,7 +46,7 @@ class ColorCorrectHSV:
|
||||
|
||||
ret_images.append(pil2tensor(ret_image))
|
||||
|
||||
log(f'HSV Processed {len(ret_images)} image(s).')
|
||||
log(f"{NODE_NAME} Processed {len(ret_images)} image(s).")
|
||||
return (torch.cat(ret_images, dim=0),)
|
||||
|
||||
NODE_CLASS_MAPPINGS = {
|
||||
|
||||
@@ -1,5 +1,9 @@
|
||||
import torch
|
||||
|
||||
from .imagefunc import *
|
||||
|
||||
NODE_NAME = 'LAB'
|
||||
|
||||
class ColorCorrectLAB:
|
||||
|
||||
def __init__(self):
|
||||
@@ -29,9 +33,9 @@ class ColorCorrectLAB:
|
||||
|
||||
ret_images = []
|
||||
|
||||
for image in image:
|
||||
|
||||
_l, _a, _b = tensor2pil(image).convert('LAB').split()
|
||||
for i in image:
|
||||
i = torch.unsqueeze(i, 0)
|
||||
_l, _a, _b = tensor2pil(i).convert('LAB').split()
|
||||
if L != 0 :
|
||||
_l = image_gray_offset(_l, L)
|
||||
if A != 0 :
|
||||
@@ -42,7 +46,7 @@ class ColorCorrectLAB:
|
||||
|
||||
ret_images.append(pil2tensor(ret_image))
|
||||
|
||||
log(f'LAB Processed {len(ret_images)} image(s).')
|
||||
log(f"{NODE_NAME} Processed {len(ret_images)} image(s).")
|
||||
return (torch.cat(ret_images, dim=0),)
|
||||
|
||||
NODE_CLASS_MAPPINGS = {
|
||||
|
||||
@@ -1,7 +1,12 @@
|
||||
import os
|
||||
import glob
|
||||
|
||||
import torch
|
||||
|
||||
from .imagefunc import *
|
||||
|
||||
NODE_NAME = 'LUT Apply'
|
||||
|
||||
class ColorCorrectLUTapply:
|
||||
|
||||
def __init__(self):
|
||||
@@ -27,14 +32,14 @@ class ColorCorrectLUTapply:
|
||||
|
||||
def color_correct_LUTapply(self, image, LUT):
|
||||
ret_images = []
|
||||
for image in image:
|
||||
|
||||
_image = tensor2pil(image)
|
||||
for i in image:
|
||||
i = torch.unsqueeze(i, 0)
|
||||
_image = tensor2pil(i)
|
||||
lut_file = LUT_DICT[LUT]
|
||||
ret_image = lut_apply(_image, lut_file)
|
||||
ret_images.append(pil2tensor(ret_image))
|
||||
|
||||
log(f'LUT Apply Processed {len(ret_images)} image(s).')
|
||||
log(f"{NODE_NAME} Processed {len(ret_images)} image(s).")
|
||||
return (torch.cat(ret_images, dim=0),)
|
||||
|
||||
NODE_CLASS_MAPPINGS = {
|
||||
|
||||
@@ -1,5 +1,9 @@
|
||||
import torch
|
||||
|
||||
from .imagefunc import *
|
||||
|
||||
NODE_NAME = 'RGB'
|
||||
|
||||
class ColorCorrectRGB:
|
||||
|
||||
def __init__(self):
|
||||
@@ -29,9 +33,9 @@ class ColorCorrectRGB:
|
||||
|
||||
ret_images = []
|
||||
|
||||
for image in image:
|
||||
|
||||
_r, _g, _b = tensor2pil(image).convert('RGB').split()
|
||||
for i in image:
|
||||
i = torch.unsqueeze(i,0)
|
||||
_r, _g, _b = tensor2pil(i).convert('RGB').split()
|
||||
if R != 0 :
|
||||
_r = image_gray_offset(_r, R)
|
||||
if G != 0 :
|
||||
@@ -42,7 +46,7 @@ class ColorCorrectRGB:
|
||||
|
||||
ret_images.append(pil2tensor(ret_image))
|
||||
|
||||
log(f'RGB Processed {len(ret_images)} image(s).')
|
||||
log(f"{NODE_NAME} Processed {len(ret_images)} image(s).")
|
||||
return (torch.cat(ret_images, dim=0),)
|
||||
|
||||
NODE_CLASS_MAPPINGS = {
|
||||
|
||||
@@ -1,5 +1,9 @@
|
||||
import torch
|
||||
|
||||
from .imagefunc import *
|
||||
|
||||
NODE_NAME = 'YUV'
|
||||
|
||||
class ColorCorrectYUV:
|
||||
|
||||
def __init__(self):
|
||||
@@ -29,9 +33,9 @@ class ColorCorrectYUV:
|
||||
|
||||
ret_images = []
|
||||
|
||||
for image in image:
|
||||
|
||||
_y, _u, _v = tensor2pil(image).convert('YCbCr').split()
|
||||
for i in image:
|
||||
i = torch.unsqueeze(i, 0)
|
||||
_y, _u, _v = tensor2pil(i).convert('YCbCr').split()
|
||||
if Y != 0 :
|
||||
_y = image_gray_offset(_y, Y)
|
||||
if U != 0 :
|
||||
@@ -42,7 +46,7 @@ class ColorCorrectYUV:
|
||||
|
||||
ret_images.append(pil2tensor(ret_image))
|
||||
|
||||
log(f'YUV Processed {len(ret_images)} image(s).')
|
||||
log(f"{NODE_NAME} Processed {len(ret_images)} image(s).")
|
||||
return (torch.cat(ret_images, dim=0),)
|
||||
|
||||
NODE_CLASS_MAPPINGS = {
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
from PIL import ImageEnhance
|
||||
from .imagefunc import *
|
||||
|
||||
NODE_NAME = 'Brightness & Contrast'
|
||||
|
||||
class ColorCorrectBrightnessAndContrast:
|
||||
|
||||
def __init__(self):
|
||||
@@ -30,9 +32,10 @@ class ColorCorrectBrightnessAndContrast:
|
||||
|
||||
ret_images = []
|
||||
|
||||
for image in image:
|
||||
for i in image:
|
||||
i = torch.unsqueeze(i,0)
|
||||
|
||||
_image = tensor2pil(image).convert('RGB')
|
||||
_image = tensor2pil(i).convert('RGB')
|
||||
if brightness != 1:
|
||||
brightness_image = ImageEnhance.Brightness(_image)
|
||||
_image = brightness_image.enhance(factor=brightness)
|
||||
@@ -42,10 +45,9 @@ class ColorCorrectBrightnessAndContrast:
|
||||
if saturation != 1:
|
||||
color_image = ImageEnhance.Color(_image)
|
||||
_image = color_image.enhance(factor=saturation)
|
||||
|
||||
ret_images.append(pil2tensor(_image))
|
||||
|
||||
log(f'Brightness & Contrast Processed {len(ret_images)} image(s).')
|
||||
log(f"{NODE_NAME} Processed {len(ret_images)} image(s).")
|
||||
return (torch.cat(ret_images, dim=0),)
|
||||
|
||||
NODE_CLASS_MAPPINGS = {
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
from .imagefunc import *
|
||||
|
||||
NODE_NAME = 'Gamma'
|
||||
|
||||
class ColorCorrectGamma:
|
||||
|
||||
def __init__(self):
|
||||
@@ -27,13 +29,13 @@ class ColorCorrectGamma:
|
||||
|
||||
ret_images = []
|
||||
|
||||
for image in image:
|
||||
|
||||
ret_image = gamma_trans(tensor2pil(image), gamma)
|
||||
for i in image:
|
||||
i = torch.unsqueeze(i, 0)
|
||||
ret_image = gamma_trans(tensor2pil(i), gamma)
|
||||
|
||||
ret_images.append(pil2tensor(ret_image))
|
||||
|
||||
log(f'Gamma Processed {len(ret_images)} image(s).')
|
||||
log(f"{NODE_NAME} Processed {len(ret_images)} image(s).")
|
||||
return (torch.cat(ret_images, dim=0),)
|
||||
|
||||
NODE_CLASS_MAPPINGS = {
|
||||
|
||||
+7
-3
@@ -1,5 +1,7 @@
|
||||
from .imagefunc import *
|
||||
|
||||
NODE_NAME = 'ColorMap'
|
||||
|
||||
colormap_list = ['autumn', 'bone', 'jet', 'winter', 'rainbow', 'ocean',
|
||||
'summer', 'sprint', 'cool', 'HSV', 'pink', 'hot',
|
||||
'parula', 'magma', 'inferno', 'plasma', 'viridis', 'cividis',
|
||||
@@ -34,13 +36,15 @@ class ColorMap:
|
||||
|
||||
ret_images = []
|
||||
|
||||
for image in image:
|
||||
_canvas = tensor2pil(image)
|
||||
for i in image:
|
||||
i = torch.unsqueeze(i, 0)
|
||||
_canvas = tensor2pil(i)
|
||||
_image = image_to_colormap(_canvas, colormap_list.index(color_map))
|
||||
ret_image = chop_image(_canvas, _image, 'normal', opacity)
|
||||
|
||||
ret_images.append(pil2tensor(ret_image))
|
||||
log(f'ColorMap Processed {len(ret_images)} image(s).')
|
||||
|
||||
log(f"{NODE_NAME} Processed {len(ret_images)} image(s).")
|
||||
return (torch.cat(ret_images, dim=0),)
|
||||
|
||||
NODE_CLASS_MAPPINGS = {
|
||||
|
||||
+13
-13
@@ -1,5 +1,7 @@
|
||||
from .imagefunc import *
|
||||
|
||||
NODE_NAME = 'ColorOverlay'
|
||||
|
||||
class ColorOverlay:
|
||||
|
||||
def __init__(self):
|
||||
@@ -37,37 +39,35 @@ class ColorOverlay:
|
||||
l_images = []
|
||||
l_masks = []
|
||||
ret_images = []
|
||||
|
||||
for b in background_image:
|
||||
b_images.append(b)
|
||||
b_images.append(torch.unsqueeze(b, 0))
|
||||
for l in layer_image:
|
||||
l_images.append(l)
|
||||
l_images.append(torch.unsqueeze(l, 0))
|
||||
m = tensor2pil(l)
|
||||
if tensor2pil(l).mode == 'RGBA':
|
||||
l_masks.append(m.convert('RGBA').split()[-1])
|
||||
else:
|
||||
l_masks.append(Image.new('L', m.size, 'white'))
|
||||
if m.mode == 'RGBA':
|
||||
l_masks.append(m.split()[-1])
|
||||
if layer_mask is not None:
|
||||
l_masks = []
|
||||
for m in layer_mask:
|
||||
if invert_mask:
|
||||
m = 1 - m
|
||||
l_masks.append(tensor2pil(m).convert('L'))
|
||||
max_batch = max(len(b_images), len(l_images), len(l_masks))
|
||||
l_masks.append(tensor2pil(torch.unsqueeze(m, 0)).convert('L'))
|
||||
if len(l_masks) == 0:
|
||||
log(f"Error: {NODE_NAME} skipped, because the available mask is not found.")
|
||||
return (background_image,)
|
||||
|
||||
max_batch = max(len(b_images), len(l_images), len(l_masks))
|
||||
_color = Image.new("RGB", tensor2pil(l_images[0]).size, color=color)
|
||||
for i in range(max_batch):
|
||||
background_image = b_images[i] if i < len(b_images) else b_images[-1]
|
||||
layer_image = l_images[i] if i < len(l_images) else l_images[-1]
|
||||
_mask = l_masks[i] if i < len(l_masks) else l_masks[-1]
|
||||
|
||||
# preprocess
|
||||
_canvas = tensor2pil(background_image).convert('RGB')
|
||||
_layer = tensor2pil(layer_image).convert('RGB')
|
||||
|
||||
if _mask.size != _layer.size:
|
||||
_mask = Image.new('L', _layer.size, 'white')
|
||||
log('Warning: mask mismatch, droped!')
|
||||
log(f"Warning: {NODE_NAME} mask mismatch, dropped!")
|
||||
|
||||
# 合成layer
|
||||
_comp = chop_image(_layer, _color, blend_mode, opacity)
|
||||
@@ -75,7 +75,7 @@ class ColorOverlay:
|
||||
|
||||
ret_images.append(pil2tensor(_canvas))
|
||||
|
||||
log(f'ColorOverlay Processed {len(ret_images)} image(s).')
|
||||
log(f"{NODE_NAME} Processed {len(ret_images)} image(s).")
|
||||
return (torch.cat(ret_images, dim=0),)
|
||||
|
||||
NODE_CLASS_MAPPINGS = {
|
||||
|
||||
+31
-7
@@ -1,5 +1,8 @@
|
||||
import torch
|
||||
from .imagefunc import *
|
||||
|
||||
NODE_NAME = 'CropByMask'
|
||||
|
||||
class CropByMask:
|
||||
|
||||
def __init__(self):
|
||||
@@ -33,7 +36,23 @@ class CropByMask:
|
||||
top_reserve, bottom_reserve, left_reserve, right_reserve
|
||||
):
|
||||
|
||||
_canvas = tensor2pil(image).convert('RGB')
|
||||
ret_images = []
|
||||
ret_masks = []
|
||||
l_images = []
|
||||
l_masks = []
|
||||
for l in image:
|
||||
l_images.append(torch.unsqueeze(l, 0))
|
||||
m = tensor2pil(l)
|
||||
if m.mode == 'RGBA':
|
||||
l_masks.append(m.split()[-1])
|
||||
for m in mask_for_crop:
|
||||
if invert_mask:
|
||||
m = 1 - m
|
||||
l_masks.append(tensor2pil(torch.unsqueeze(m, 0)).convert('L'))
|
||||
max_batch = max(len(l_images), len(l_masks))
|
||||
# 如果有多张mask输入,使用第一张
|
||||
if mask_for_crop.shape[0] > 0:
|
||||
mask_for_crop = torch.unsqueeze(mask_for_crop[0], 0)
|
||||
if invert_mask:
|
||||
mask_for_crop = 1 - mask_for_crop
|
||||
_mask = mask2image(mask_for_crop)
|
||||
@@ -46,20 +65,25 @@ class CropByMask:
|
||||
(x, y, width, height) = min_bounding_rect(bluredmask)
|
||||
if detect == "max_inscribed_rect":
|
||||
(x, y, width, height) = max_inscribed_rect(bluredmask)
|
||||
|
||||
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 + 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
|
||||
preview_image = tensor2pil(mask_for_crop).convert('RGB')
|
||||
preview_image = draw_rect(preview_image, x, y, width, height, line_color="#F00000", line_width=(width+height)//100)
|
||||
preview_image = draw_rect(preview_image, x1, y1, x2 - x1, y2 - y1,
|
||||
line_color="#00F000", line_width=(width+height)//200)
|
||||
crop_box = (x1, y1, x2, y2)
|
||||
ret_image = _canvas.crop(crop_box)
|
||||
ret_mask = _mask.crop(crop_box)
|
||||
for i in range(max_batch):
|
||||
_canvas = tensor2pil(l_images[i]).convert('RGB')
|
||||
_mask = l_masks[i] if len(l_masks) > i else l_masks[-1]
|
||||
ret_images.append(pil2tensor(_canvas.crop(crop_box)))
|
||||
ret_masks.append(image2mask(_mask.crop(crop_box)))
|
||||
|
||||
log(f"{NODE_NAME} Processed {len(ret_images)} image(s).")
|
||||
return (torch.cat(ret_images, dim=0), torch.cat(ret_masks, dim=0), list(crop_box), pil2tensor(preview_image),)
|
||||
|
||||
return (pil2tensor(ret_image), image2mask(ret_mask), list(crop_box), pil2tensor(preview_image),)
|
||||
|
||||
NODE_CLASS_MAPPINGS = {
|
||||
"LayerUtility: CropByMask": CropByMask
|
||||
|
||||
+13
-13
@@ -1,5 +1,7 @@
|
||||
from .imagefunc import *
|
||||
|
||||
NODE_NAME = 'DropShadow'
|
||||
|
||||
class DropShadow:
|
||||
|
||||
def __init__(self):
|
||||
@@ -42,25 +44,24 @@ class DropShadow:
|
||||
l_images = []
|
||||
l_masks = []
|
||||
ret_images = []
|
||||
|
||||
for b in background_image:
|
||||
b_images.append(b)
|
||||
b_images.append(torch.unsqueeze(b, 0))
|
||||
for l in layer_image:
|
||||
l_images.append(l)
|
||||
l_images.append(torch.unsqueeze(l, 0))
|
||||
m = tensor2pil(l)
|
||||
if tensor2pil(l).mode == 'RGBA':
|
||||
l_masks.append(m.convert('RGBA').split()[-1])
|
||||
else:
|
||||
l_masks.append(Image.new('L', m.size, 'white'))
|
||||
if m.mode == 'RGBA':
|
||||
l_masks.append(m.split()[-1])
|
||||
if layer_mask is not None:
|
||||
l_masks = []
|
||||
for m in layer_mask:
|
||||
if invert_mask:
|
||||
m = 1 - m
|
||||
l_masks.append(tensor2pil(m).convert('L'))
|
||||
l_masks.append(tensor2pil(torch.unsqueeze(m, 0)).convert('L'))
|
||||
if len(l_masks) == 0:
|
||||
log(f"Error: {NODE_NAME} skipped, because the available mask is not found.")
|
||||
return (background_image,)
|
||||
|
||||
max_batch = max(len(b_images), len(l_images), len(l_masks))
|
||||
|
||||
|
||||
distance_x = -distance_x
|
||||
distance_y = -distance_y
|
||||
shadow_color = Image.new("RGB", tensor2pil(l_images[0]).size, color=shadow_color)
|
||||
@@ -70,14 +71,13 @@ class DropShadow:
|
||||
layer_image = l_images[i] if i < len(l_images) else l_images[-1]
|
||||
_mask = l_masks[i] if i < len(l_masks) else l_masks[-1]
|
||||
|
||||
|
||||
# preprocess
|
||||
_canvas = tensor2pil(background_image).convert('RGB')
|
||||
_layer = tensor2pil(layer_image)
|
||||
|
||||
if _mask.size != _layer.size:
|
||||
_mask = Image.new('L', _layer.size, 'white')
|
||||
log('Warning: mask mismatch, droped!')
|
||||
log(f"Warning: {NODE_NAME} mask mismatch, dropped!")
|
||||
|
||||
if distance_x != 0 or distance_y != 0:
|
||||
__mask = shift_image(_mask, distance_x, distance_y) # 位移
|
||||
@@ -91,7 +91,7 @@ class DropShadow:
|
||||
|
||||
ret_images.append(pil2tensor(_canvas))
|
||||
|
||||
log(f'DropShadow Processed {len(ret_images)} image(s).')
|
||||
log(f"{NODE_NAME} Processed {len(ret_images)} image(s).")
|
||||
return (torch.cat(ret_images, dim=0),)
|
||||
|
||||
|
||||
|
||||
+40
-15
@@ -1,5 +1,7 @@
|
||||
from .imagefunc import *
|
||||
|
||||
NODE_NAME = 'ExtendCanvas'
|
||||
|
||||
class ExtendCanvas:
|
||||
|
||||
def __init__(self):
|
||||
@@ -34,24 +36,47 @@ class ExtendCanvas:
|
||||
mask=None,
|
||||
):
|
||||
|
||||
_image = tensor2pil(image).convert('RGB')
|
||||
_mask = tensor2pil(image).convert('RGBA').split()[-1]
|
||||
l_images = []
|
||||
l_masks = []
|
||||
ret_images = []
|
||||
ret_masks = []
|
||||
|
||||
for l in image:
|
||||
l_images.append(torch.unsqueeze(l, 0))
|
||||
m = tensor2pil(l)
|
||||
if m.mode == 'RGBA':
|
||||
l_masks.append(m.split()[-1])
|
||||
|
||||
if mask is not None:
|
||||
if invert_mask:
|
||||
mask = 1 - mask
|
||||
_mask = mask2image(mask).convert('L')
|
||||
l_masks = []
|
||||
for m in mask:
|
||||
if invert_mask:
|
||||
m = 1 - m
|
||||
l_masks.append(tensor2pil(torch.unsqueeze(m, 0)).convert('L'))
|
||||
else:
|
||||
l_masks.append(Image.new('L', size=tensor2pil(l_images[0]).size, color='white'))
|
||||
|
||||
width = _image.width + left + right
|
||||
height = _image.height + top + bottom
|
||||
_canvas = Image.new('RGB', (width, height), color)
|
||||
_mask_canvas = Image.new('L', (width, height), "black")
|
||||
max_batch = max(len(l_images), len(l_masks))
|
||||
for i in range(max_batch):
|
||||
|
||||
_image = l_images[i] if i < len(l_images) else l_images[-1]
|
||||
_image = tensor2pil(_image).convert('RGB')
|
||||
_mask = l_masks[i] if i < len(l_masks) else l_masks[-1]
|
||||
|
||||
width = _image.width + left + right
|
||||
height = _image.height + top + bottom
|
||||
_canvas = Image.new('RGB', (width, height), color)
|
||||
_mask_canvas = Image.new('L', (width, height), "black")
|
||||
|
||||
_canvas.paste(_image, box=(left,top))
|
||||
_mask_canvas.paste(_mask.convert('L'), box=(left, top))
|
||||
|
||||
ret_images.append(pil2tensor(_canvas))
|
||||
ret_masks.append(image2mask(_mask_canvas))
|
||||
|
||||
log(f"{NODE_NAME} Processed {len(ret_images)} image(s).")
|
||||
return (torch.cat(ret_images, dim=0), torch.cat(ret_masks, dim=0),)
|
||||
|
||||
_canvas.paste(_image, box=(left,top))
|
||||
_mask_canvas.paste(_mask.convert('L'), box=(left, top))
|
||||
ret_image = _canvas
|
||||
ret_mask = image2mask(_mask_canvas)
|
||||
log('ExtendCanvas Processed.')
|
||||
return (pil2tensor(ret_image), ret_mask,)
|
||||
|
||||
NODE_CLASS_MAPPINGS = {
|
||||
"LayerUtility: ExtendCanvas": ExtendCanvas
|
||||
|
||||
+5
-5
@@ -1,5 +1,7 @@
|
||||
from .imagefunc import *
|
||||
|
||||
NODE_NAME = 'GaussianBlur'
|
||||
|
||||
class GaussianBlur:
|
||||
|
||||
def __init__(self):
|
||||
@@ -27,12 +29,10 @@ class GaussianBlur:
|
||||
|
||||
ret_images = []
|
||||
|
||||
for image in image:
|
||||
for i in image:
|
||||
_canvas = tensor2pil(torch.unsqueeze(i, 0)).convert('RGB')
|
||||
|
||||
_canvas = tensor2pil(image).convert('RGB')
|
||||
ret_image = gaussian_blur(_canvas, blur)
|
||||
|
||||
ret_images.append(pil2tensor(ret_image))
|
||||
ret_images.append(pil2tensor(gaussian_blur(_canvas, blur)))
|
||||
|
||||
log(f'GaussianBlur Processed {len(ret_images)} image(s).')
|
||||
return (torch.cat(ret_images, dim=0),)
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
import torch
|
||||
|
||||
from .imagefunc import *
|
||||
|
||||
class GetColorTone:
|
||||
@@ -24,7 +26,8 @@ class GetColorTone:
|
||||
OUTPUT_NODE = True
|
||||
|
||||
def get_color_tone(self, image, mode,):
|
||||
|
||||
if image.shape[0] > 0:
|
||||
image = torch.unsqueeze(image[0], 0)
|
||||
_canvas = tensor2pil(image).convert('RGB')
|
||||
_canvas = gaussian_blur(_canvas, int((_canvas.width + _canvas.height) / 200))
|
||||
if mode == 'main_color':
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
import torch
|
||||
|
||||
from .imagefunc import *
|
||||
|
||||
class GetImageSize:
|
||||
@@ -24,6 +26,8 @@ class GetImageSize:
|
||||
|
||||
def get_image_size(self, image,):
|
||||
|
||||
if image.shape[0] > 0:
|
||||
image = torch.unsqueeze(image[0], 0)
|
||||
_image = tensor2pil(image)
|
||||
|
||||
return (_image.width, _image.height, [_image.width, _image.height],)
|
||||
|
||||
+13
-14
@@ -1,5 +1,7 @@
|
||||
from .imagefunc import *
|
||||
|
||||
NODE_NAME = 'GradientOverlay'
|
||||
|
||||
class GradientOverlay:
|
||||
|
||||
def __init__(self):
|
||||
@@ -42,43 +44,40 @@ class GradientOverlay:
|
||||
l_images = []
|
||||
l_masks = []
|
||||
ret_images = []
|
||||
|
||||
for b in background_image:
|
||||
b_images.append(b)
|
||||
b_images.append(torch.unsqueeze(b, 0))
|
||||
for l in layer_image:
|
||||
l_images.append(l)
|
||||
l_images.append(torch.unsqueeze(l, 0))
|
||||
m = tensor2pil(l)
|
||||
if tensor2pil(l).mode == 'RGBA':
|
||||
l_masks.append(m.convert('RGBA').split()[-1])
|
||||
else:
|
||||
l_masks.append(Image.new('L', m.size, 'white'))
|
||||
if m.mode == 'RGBA':
|
||||
l_masks.append(m.split()[-1])
|
||||
if layer_mask is not None:
|
||||
l_masks = []
|
||||
for m in layer_mask:
|
||||
if invert_mask:
|
||||
m = 1 - m
|
||||
l_masks.append(tensor2pil(m).convert('L'))
|
||||
l_masks.append(tensor2pil(torch.unsqueeze(m, 0)).convert('L'))
|
||||
if len(l_masks) == 0:
|
||||
log(f"Error: {NODE_NAME} skipped, because the available mask is not found.")
|
||||
return (background_image,)
|
||||
max_batch = max(len(b_images), len(l_images), len(l_masks))
|
||||
width, height = tensor2pil(l_images[0]).size
|
||||
|
||||
_gradient = gradient(start_color, end_color, width, height, float(angle))
|
||||
#
|
||||
start_color = RGB_to_Hex((start_alpha, start_alpha, start_alpha))
|
||||
end_color = RGB_to_Hex((end_alpha, end_alpha, end_alpha))
|
||||
comp_alpha = gradient(start_color, end_color, width, height, float(angle))
|
||||
comp_alpha = ImageChops.invert(comp_alpha).convert('L')
|
||||
|
||||
for i in range(max_batch):
|
||||
background_image = b_images[i] if i < len(b_images) else b_images[-1]
|
||||
layer_image = l_images[i] if i < len(l_images) else l_images[-1]
|
||||
_mask = l_masks[i] if i < len(l_masks) else l_masks[-1]
|
||||
|
||||
# preprocess
|
||||
_canvas = tensor2pil(background_image).convert('RGB')
|
||||
_layer = tensor2pil(layer_image).convert('RGB')
|
||||
|
||||
if _mask.size != _layer.size:
|
||||
_mask = Image.new('L', _layer.size, 'white')
|
||||
log('Warning: mask mismatch, droped!')
|
||||
log(f"Warning: {NODE_NAME} mask mismatch, dropped!")
|
||||
|
||||
# 合成layer
|
||||
_comp = chop_image(_layer, _gradient, blend_mode, opacity)
|
||||
@@ -88,7 +87,7 @@ class GradientOverlay:
|
||||
|
||||
ret_images.append(pil2tensor(_canvas))
|
||||
|
||||
log(f'GradientOverlay Processed {len(ret_images)} image(s).')
|
||||
log(f"{NODE_NAME} Processed {len(ret_images)} image(s).")
|
||||
return (torch.cat(ret_images, dim=0),)
|
||||
|
||||
NODE_CLASS_MAPPINGS = {
|
||||
|
||||
+13
-7
@@ -1,5 +1,8 @@
|
||||
import torch
|
||||
from .imagefunc import *
|
||||
|
||||
NODE_NAME = 'ImageBlend'
|
||||
|
||||
class ImageBlend:
|
||||
|
||||
def __init__(self):
|
||||
@@ -37,12 +40,12 @@ class ImageBlend:
|
||||
l_masks = []
|
||||
ret_images = []
|
||||
for b in background_image:
|
||||
b_images.append(b)
|
||||
b_images.append(torch.unsqueeze(b, 0))
|
||||
for l in layer_image:
|
||||
l_images.append(l)
|
||||
l_images.append(torch.unsqueeze(l, 0))
|
||||
m = tensor2pil(l)
|
||||
if tensor2pil(l).mode == 'RGBA':
|
||||
l_masks.append(m.convert('RGBA').split()[-1])
|
||||
if m.mode == 'RGBA':
|
||||
l_masks.append(m.split()[-1])
|
||||
else:
|
||||
l_masks.append(Image.new('L', m.size, 'white'))
|
||||
if layer_mask is not None:
|
||||
@@ -50,7 +53,7 @@ class ImageBlend:
|
||||
for m in layer_mask:
|
||||
if invert_mask:
|
||||
m = 1 - m
|
||||
l_masks.append(tensor2pil(m).convert('L'))
|
||||
l_masks.append(tensor2pil(torch.unsqueeze(m, 0)).convert('L'))
|
||||
max_batch = max(len(b_images), len(l_images), len(l_masks))
|
||||
for i in range(max_batch):
|
||||
background_image = b_images[i] if i < len(b_images) else b_images[-1]
|
||||
@@ -59,14 +62,17 @@ class ImageBlend:
|
||||
|
||||
_canvas = tensor2pil(background_image).convert('RGB')
|
||||
_layer = tensor2pil(layer_image).convert('RGB')
|
||||
_mask = tensor2pil(layer_image).convert('RGBA').split()[-1]
|
||||
|
||||
if _mask.size != _layer.size:
|
||||
_mask = Image.new('L', _layer.size, 'white')
|
||||
log(f"Warning: {NODE_NAME} mask mismatch, dropped!")
|
||||
|
||||
# 合成layer
|
||||
_comp = chop_image(_canvas, _layer, blend_mode, opacity)
|
||||
_canvas.paste(_comp, mask=_mask)
|
||||
|
||||
ret_images.append(pil2tensor(_canvas))
|
||||
log(f'ImageBlend Processed {len(ret_images)} image(s).')
|
||||
log(f"{NODE_NAME} Processed {len(ret_images)} image(s).")
|
||||
return (torch.cat(ret_images, dim=0),)
|
||||
|
||||
NODE_CLASS_MAPPINGS = {
|
||||
|
||||
@@ -1,6 +1,11 @@
|
||||
import copy
|
||||
|
||||
import torch
|
||||
|
||||
from .imagefunc import *
|
||||
|
||||
NODE_NAME = 'ImageBlendAdvance'
|
||||
|
||||
class ImageBlendAdvance:
|
||||
|
||||
def __init__(self):
|
||||
@@ -51,12 +56,12 @@ class ImageBlendAdvance:
|
||||
ret_images = []
|
||||
ret_masks = []
|
||||
for b in background_image:
|
||||
b_images.append(b)
|
||||
b_images.append(torch.unsqueeze(b, 0))
|
||||
for l in layer_image:
|
||||
l_images.append(l)
|
||||
l_images.append(torch.unsqueeze(l, 0))
|
||||
m = tensor2pil(l)
|
||||
if tensor2pil(l).mode == 'RGBA':
|
||||
l_masks.append(m.convert('RGBA').split()[-1])
|
||||
if m.mode == 'RGBA':
|
||||
l_masks.append(m.split()[-1])
|
||||
else:
|
||||
l_masks.append(Image.new('L', m.size, 'white'))
|
||||
if layer_mask is not None:
|
||||
@@ -64,7 +69,8 @@ class ImageBlendAdvance:
|
||||
for m in layer_mask:
|
||||
if invert_mask:
|
||||
m = 1 - m
|
||||
l_masks.append(tensor2pil(m).convert('L'))
|
||||
l_masks.append(tensor2pil(torch.unsqueeze(m, 0)).convert('L'))
|
||||
|
||||
max_batch = max(len(b_images), len(l_images), len(l_masks))
|
||||
for i in range(max_batch):
|
||||
background_image = b_images[i] if i < len(b_images) else b_images[-1]
|
||||
@@ -76,7 +82,7 @@ class ImageBlendAdvance:
|
||||
|
||||
if _mask.size != _layer.size:
|
||||
_mask = Image.new('L', _layer.size, 'white')
|
||||
log('Warning: mask mismatch, droped!')
|
||||
log(f"Warning: {NODE_NAME} mask mismatch, dropped!")
|
||||
|
||||
orig_layer_width = _layer.width
|
||||
orig_layer_height = _layer.height
|
||||
@@ -117,7 +123,7 @@ class ImageBlendAdvance:
|
||||
ret_images.append(pil2tensor(_canvas))
|
||||
ret_masks.append(image2mask(_compmask))
|
||||
|
||||
log(f'ImageBlendAdvance Processed {len(ret_images)} image(s).')
|
||||
log(f"{NODE_NAME} Processed {len(ret_images)} image(s).")
|
||||
return (torch.cat(ret_images, dim=0), torch.cat(ret_masks, dim=0),)
|
||||
|
||||
NODE_CLASS_MAPPINGS = {
|
||||
|
||||
@@ -1,5 +1,9 @@
|
||||
import torch
|
||||
|
||||
from .imagefunc import *
|
||||
|
||||
NODE_NAME = 'ImageChannelMerge'
|
||||
|
||||
class ImageChannelMerge:
|
||||
|
||||
def __init__(self):
|
||||
@@ -28,14 +32,37 @@ class ImageChannelMerge:
|
||||
|
||||
def image_channel_merge(self, channel_1, channel_2, channel_3, mode, channel_4=None):
|
||||
|
||||
_channel1 = tensor2pil(channel_1)
|
||||
_channel2 = tensor2pil(channel_2)
|
||||
_channel3 = tensor2pil(channel_3)
|
||||
_channel4 = Image.new('L', size=_channel1.size, color='white')
|
||||
c1_images = []
|
||||
c2_images = []
|
||||
c3_images = []
|
||||
c4_images = []
|
||||
ret_images = []
|
||||
|
||||
width, height = tensor2pil(torch.unsqueeze(channel_1[0], 0)).size
|
||||
for c in channel_1:
|
||||
c1_images.append(torch.unsqueeze(c, 0))
|
||||
for c in channel_2:
|
||||
c2_images.append(torch.unsqueeze(c, 0))
|
||||
for c in channel_3:
|
||||
c3_images.append(torch.unsqueeze(c, 0))
|
||||
if channel_4 is not None:
|
||||
_channel4 = tensor2pil(channel_4)
|
||||
ret_image = image_channel_merge((_channel1, _channel2, _channel3, _channel4), mode)
|
||||
return (pil2tensor(ret_image),)
|
||||
for c in channel_4:
|
||||
c4_images.append(torch.unsqueeze(c, 0))
|
||||
else:
|
||||
c4_images.append(Image.new('L', size=(width, height), color='white'))
|
||||
|
||||
max_batch = max(len(c1_images), len(c2_images), len(c3_images), len(c4_images))
|
||||
for i in range(max_batch):
|
||||
c_1 = c1_images[i] if i < len(c1_images) else c1_images[-1]
|
||||
c_2 = c2_images[i] if i < len(c2_images) else c2_images[-1]
|
||||
c_3 = c3_images[i] if i < len(c3_images) else c3_images[-1]
|
||||
c_4 = c4_images[i] if i < len(c4_images) else c4_images[-1]
|
||||
ret_image = image_channel_merge((tensor2pil(c_1), tensor2pil(c_2), tensor2pil(c_3), tensor2pil(c_4)), mode)
|
||||
|
||||
ret_images.append(pil2tensor(ret_image))
|
||||
|
||||
log(f"{NODE_NAME} Processed {len(ret_images)} image(s).")
|
||||
return (torch.cat(ret_images, dim=0),)
|
||||
|
||||
NODE_CLASS_MAPPINGS = {
|
||||
"LayerUtility: ImageChannelMerge": ImageChannelMerge
|
||||
|
||||
@@ -1,5 +1,9 @@
|
||||
import torch
|
||||
|
||||
from .imagefunc import *
|
||||
|
||||
NODE_NAME = 'ImageChannelSplit'
|
||||
|
||||
class ImageChannelSplit:
|
||||
|
||||
def __init__(self):
|
||||
@@ -25,10 +29,22 @@ class ImageChannelSplit:
|
||||
|
||||
def image_channel_split(self, image, mode):
|
||||
|
||||
_image = tensor2pil(image).convert('RGBA')
|
||||
channel1, channel2, channel3, channel4 = image_channel_split(_image, mode)
|
||||
c1_images = []
|
||||
c2_images = []
|
||||
c3_images = []
|
||||
c4_images = []
|
||||
|
||||
return (pil2tensor(channel1), pil2tensor(channel2), pil2tensor(channel3), pil2tensor(channel4),)
|
||||
for i in image:
|
||||
i = torch.unsqueeze(i, 0)
|
||||
_image = tensor2pil(i).convert('RGBA')
|
||||
channel1, channel2, channel3, channel4 = image_channel_split(_image, mode)
|
||||
c1_images.append(pil2tensor(channel1))
|
||||
c2_images.append(pil2tensor(channel2))
|
||||
c3_images.append(pil2tensor(channel3))
|
||||
c4_images.append(pil2tensor(channel4))
|
||||
|
||||
log(f"{NODE_NAME} Processed {len(c1_images)} image(s).")
|
||||
return (torch.cat(c1_images, dim=0), torch.cat(c2_images, dim=0), torch.cat(c3_images, dim=0), torch.cat(c4_images, dim=0),)
|
||||
|
||||
NODE_CLASS_MAPPINGS = {
|
||||
"LayerUtility: ImageChannelSplit": ImageChannelSplit
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
import torch
|
||||
from .imagefunc import *
|
||||
|
||||
NODE_NAME = 'ImageMaskScaleAs'
|
||||
|
||||
any = AnyType("*")
|
||||
|
||||
class ImageMaskScaleAs:
|
||||
@@ -59,24 +62,30 @@ class ImageMaskScaleAs:
|
||||
ret_masks = []
|
||||
|
||||
if image is not None:
|
||||
for image in image:
|
||||
_image = tensor2pil(image).convert('RGB')
|
||||
for i in image:
|
||||
i = torch.unsqueeze(i, 0)
|
||||
_image = tensor2pil(i).convert('RGB')
|
||||
orig_width, orig_height = _image.size
|
||||
_image = fit_resize_image(_image, target_width, target_height, fit, resize_sampler)
|
||||
ret_images.append(pil2tensor(_image))
|
||||
if mask is not None:
|
||||
for mask in mask:
|
||||
_mask = tensor2pil(mask).convert('L')
|
||||
for m in mask:
|
||||
m = torch.unsqueeze(m, 0)
|
||||
_mask = tensor2pil(m).convert('L')
|
||||
orig_width, orig_height = _mask.size
|
||||
_mask = fit_resize_image(_mask, target_width, target_height, fit, resize_sampler).convert('L')
|
||||
ret_masks.append(image2mask(_mask))
|
||||
if len(ret_images) > 0 and len(ret_masks) >0:
|
||||
log(f"{NODE_NAME} Processed {len(ret_images)} image(s).")
|
||||
return (torch.cat(ret_images, dim=0), torch.cat(ret_masks, dim=0), [orig_width, orig_height],)
|
||||
elif len(ret_images) > 0 and len(ret_masks) == 0:
|
||||
log(f"{NODE_NAME} Processed {len(ret_images)} image(s).")
|
||||
return (torch.cat(ret_images, dim=0), None,)
|
||||
elif len(ret_images) == 0 and len(ret_masks) > 0:
|
||||
log(f"{NODE_NAME} Processed {len(ret_mask)} image(s).")
|
||||
return (None, torch.cat(ret_masks, dim=0), [orig_width, orig_height],)
|
||||
else:
|
||||
log(f"Error: {NODE_NAME} skipped, because the available image or mask is not found.")
|
||||
return (None, None,)
|
||||
|
||||
NODE_CLASS_MAPPINGS = {
|
||||
|
||||
+41
-18
@@ -1,5 +1,8 @@
|
||||
import torch
|
||||
from .imagefunc import *
|
||||
|
||||
NODE_NAME = 'ImageOpacity'
|
||||
|
||||
class ImageOpacity:
|
||||
|
||||
def __init__(self):
|
||||
@@ -29,29 +32,49 @@ class ImageOpacity:
|
||||
mask=None,
|
||||
):
|
||||
|
||||
_image = tensor2pil(image).convert('RGB')
|
||||
_mask = tensor2pil(image).convert('RGBA').split()[-1]
|
||||
ret_images = []
|
||||
ret_masks = []
|
||||
l_images = []
|
||||
l_masks = []
|
||||
for l in image:
|
||||
l_images.append(torch.unsqueeze(l, 0))
|
||||
m = tensor2pil(l)
|
||||
if m.mode == 'RGBA':
|
||||
l_masks.append(m.split()[-1])
|
||||
else:
|
||||
l_masks.append(Image.new('L', size=m.size, color='white'))
|
||||
|
||||
if mask is not None:
|
||||
_mask = mask2image(mask).convert('L')
|
||||
if invert_mask:
|
||||
_color = Image.new("L", _image.size, color=(255))
|
||||
else:
|
||||
_color = Image.new("L", _image.size, color=(0))
|
||||
l_masks = []
|
||||
for m in mask:
|
||||
if invert_mask:
|
||||
m = 1 - m
|
||||
l_masks.append(tensor2pil(torch.unsqueeze(m, 0)).convert('L'))
|
||||
|
||||
ret_mask = _mask
|
||||
if opacity == 0:
|
||||
ret_mask = _color
|
||||
elif opacity < 100:
|
||||
alpha = 1.0 - float(opacity) / 100
|
||||
max_batch = max(len(l_images), len(l_masks))
|
||||
|
||||
for i in range(max_batch):
|
||||
_image = l_images[i] if i < len(l_images) else l_images[-1]
|
||||
_image = tensor2pil(_image)
|
||||
_mask = l_masks[i] if i < len(l_masks) else l_masks[-1]
|
||||
if invert_mask:
|
||||
_color = Image.new("L", _image.size, color=('white'))
|
||||
_mask = ImageChops.invert(_mask)
|
||||
else:
|
||||
_color = Image.new("L", _image.size, color=('black'))
|
||||
|
||||
alpha = 1 - opacity / 100.0
|
||||
ret_mask = Image.blend(_mask, _color, alpha)
|
||||
R, G, B, = _image.split()
|
||||
if invert_mask:
|
||||
ret_image = Image.merge('RGBA', (R, G, B, ImageChops.invert(ret_mask)))
|
||||
else:
|
||||
R, G, B, = _image.convert('RGB').split()
|
||||
if invert_mask:
|
||||
ret_mask = ImageChops.invert(ret_mask)
|
||||
ret_image = Image.merge('RGBA', (R, G, B, ret_mask))
|
||||
log('ImageOpacity Processed.')
|
||||
return (pil2tensor(ret_image), image2mask(ret_mask),)
|
||||
|
||||
ret_images.append(pil2tensor(ret_image))
|
||||
ret_masks.append(image2mask(ret_mask))
|
||||
|
||||
log(f"{NODE_NAME} Processed {len(ret_images)} image(s).")
|
||||
return (torch.cat(ret_images, dim=0), torch.cat(ret_masks, dim=0),)
|
||||
|
||||
NODE_CLASS_MAPPINGS = {
|
||||
"LayerUtility: ImageOpacity": ImageOpacity
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
from .imagefunc import *
|
||||
|
||||
NODE_NAME = 'ImageScaleRestore'
|
||||
|
||||
class ImageScaleRestore:
|
||||
|
||||
def __init__(self):
|
||||
@@ -33,9 +35,24 @@ class ImageScaleRestore:
|
||||
mask = None, original_size = None
|
||||
):
|
||||
|
||||
_canvas = tensor2pil(image).convert('RGB')
|
||||
orig_width = _canvas.width
|
||||
orig_height = _canvas.height
|
||||
l_images = []
|
||||
l_masks = []
|
||||
ret_images = []
|
||||
ret_masks = []
|
||||
for l in image:
|
||||
l_images.append(torch.unsqueeze(l, 0))
|
||||
m = tensor2pil(l)
|
||||
if m.mode == 'RGBA':
|
||||
l_masks.append(m.split()[-1])
|
||||
|
||||
if mask is not None:
|
||||
l_masks = []
|
||||
for m in mask:
|
||||
l_masks.append(tensor2pil(torch.unsqueeze(m, 0)).convert('L'))
|
||||
|
||||
max_batch = max(len(l_images), len(l_masks))
|
||||
|
||||
orig_width, orig_height = tensor2pil(l_images[0]).size
|
||||
if original_size is not None:
|
||||
target_width = original_size[0]
|
||||
target_height = original_size[1]
|
||||
@@ -64,13 +81,24 @@ class ImageScaleRestore:
|
||||
resize_sampler = Image.BOX
|
||||
elif method == "nearest":
|
||||
resize_sampler = Image.NEAREST
|
||||
ret_image = _canvas.resize((target_width, target_height), resize_sampler)
|
||||
ret_mask = Image.new('L', size=ret_image.size, color='white')
|
||||
if mask is not None:
|
||||
_mask = mask2image(mask).convert('L')
|
||||
ret_mask = _mask.resize((target_width, target_height), resize_sampler)
|
||||
|
||||
return (pil2tensor(ret_image), image2mask(ret_mask), [orig_width, orig_height],)
|
||||
for i in range(max_batch):
|
||||
|
||||
_image = l_images[i] if i < len(l_images) else l_images[-1]
|
||||
|
||||
_canvas = tensor2pil(_image).convert('RGB')
|
||||
ret_image = _canvas.resize((target_width, target_height), resize_sampler)
|
||||
ret_mask = Image.new('L', size=ret_image.size, color='white')
|
||||
if mask is not None:
|
||||
_mask = l_masks[i] if i < len(l_masks) else l_masks[-1]
|
||||
ret_mask = _mask.resize((target_width, target_height), resize_sampler)
|
||||
|
||||
ret_images.append(pil2tensor(ret_image))
|
||||
ret_masks.append(image2mask(ret_mask))
|
||||
|
||||
log(f"{NODE_NAME} Processed {len(ret_images)} image(s).")
|
||||
return (torch.cat(ret_images, dim=0), torch.cat(ret_masks, dim=0), [orig_width, orig_height],)
|
||||
|
||||
|
||||
NODE_CLASS_MAPPINGS = {
|
||||
"LayerUtility: ImageScaleRestore": ImageScaleRestore
|
||||
|
||||
+26
-8
@@ -1,7 +1,8 @@
|
||||
import math
|
||||
|
||||
from .imagefunc import *
|
||||
|
||||
NODE_NAME = 'ImageShift'
|
||||
|
||||
class ImageShift:
|
||||
|
||||
def __init__(self):
|
||||
@@ -40,13 +41,30 @@ class ImageShift:
|
||||
ret_images = []
|
||||
ret_masks = []
|
||||
ret_border_masks = []
|
||||
for image in image:
|
||||
|
||||
shift_x, shift_y = -shift_x, -shift_y
|
||||
_canvas = tensor2pil(image).convert('RGB')
|
||||
_mask = tensor2pil(image).convert('RGBA').split()[-1]
|
||||
if mask is not None:
|
||||
_mask = mask2image(mask).convert('L')
|
||||
l_images = []
|
||||
l_masks = []
|
||||
|
||||
|
||||
for l in image:
|
||||
l_images.append(torch.unsqueeze(l, 0))
|
||||
m = tensor2pil(l)
|
||||
if m.mode == 'RGBA':
|
||||
l_masks.append(m.split()[-1])
|
||||
else:
|
||||
l_masks.append(Image.new('L', size=m.size, color='white'))
|
||||
if mask is not None:
|
||||
l_masks = []
|
||||
for m in mask:
|
||||
if invert_mask:
|
||||
m = 1 - m
|
||||
l_masks.append(tensor2pil(torch.unsqueeze(m, 0)).convert('L'))
|
||||
|
||||
shift_x, shift_y = -shift_x, -shift_y
|
||||
for i in range(len(l_images)):
|
||||
_image = l_images[i]
|
||||
_canvas = tensor2pil(_image).convert('RGB')
|
||||
_mask = l_masks[i] if len(l_masks) < i else l_masks[-1]
|
||||
_border = Image.new('L', size=_canvas.size, color='black')
|
||||
_border = draw_border(_border, border_width=border_mask_width, color='#FFFFFF')
|
||||
_border = _border.resize(_canvas.size)
|
||||
@@ -59,7 +77,7 @@ class ImageShift:
|
||||
ret_masks.append(image2mask(_mask))
|
||||
ret_border_masks.append(image2mask(_border))
|
||||
|
||||
log(f'ImageShift Processed {len(ret_images)} image(s).')
|
||||
log(f"{NODE_NAME} Processed {len(ret_images)} image(s).")
|
||||
return (torch.cat(ret_images, dim=0), torch.cat(ret_masks, dim=0), torch.cat(ret_border_masks, dim=0),)
|
||||
|
||||
NODE_CLASS_MAPPINGS = {
|
||||
|
||||
+12
-10
@@ -1,5 +1,7 @@
|
||||
from .imagefunc import *
|
||||
|
||||
NODE_NAME = 'InnerGlow'
|
||||
|
||||
class InnerGlow:
|
||||
|
||||
def __init__(self):
|
||||
@@ -44,22 +46,22 @@ class InnerGlow:
|
||||
l_images = []
|
||||
l_masks = []
|
||||
ret_images = []
|
||||
|
||||
for b in background_image:
|
||||
b_images.append(b)
|
||||
b_images.append(torch.unsqueeze(b, 0))
|
||||
for l in layer_image:
|
||||
l_images.append(l)
|
||||
l_images.append(torch.unsqueeze(l, 0))
|
||||
m = tensor2pil(l)
|
||||
if tensor2pil(l).mode == 'RGBA':
|
||||
l_masks.append(m.convert('RGBA').split()[-1])
|
||||
else:
|
||||
l_masks.append(Image.new('L', m.size, 'white'))
|
||||
if m.mode == 'RGBA':
|
||||
l_masks.append(m.split()[-1])
|
||||
if layer_mask is not None:
|
||||
l_masks = []
|
||||
for m in layer_mask:
|
||||
if invert_mask:
|
||||
m = 1 - m
|
||||
l_masks.append(tensor2pil(m).convert('L'))
|
||||
l_masks.append(tensor2pil(torch.unsqueeze(m, 0)).convert('L'))
|
||||
if len(l_masks) == 0:
|
||||
log(f"Error: {NODE_NAME} skipped, because the available mask is not found.")
|
||||
return (background_image,)
|
||||
max_batch = max(len(b_images), len(l_images), len(l_masks))
|
||||
for i in range(max_batch):
|
||||
background_image = b_images[i] if i < len(b_images) else b_images[-1]
|
||||
@@ -71,7 +73,7 @@ class InnerGlow:
|
||||
|
||||
if _mask.size != _layer.size:
|
||||
_mask = Image.new('L', _layer.size, 'white')
|
||||
log('Warning: mask mismatch, droped!')
|
||||
log(f"Warning: {NODE_NAME} mask mismatch, dropped!")
|
||||
|
||||
blur_factor = blur / 20.0
|
||||
grow = glow_range
|
||||
@@ -90,7 +92,7 @@ class InnerGlow:
|
||||
_layer.paste(_canvas, mask=ImageChops.invert(_mask))
|
||||
ret_images.append(pil2tensor(_layer))
|
||||
|
||||
log(f'InnerGlow Processed {len(ret_images)} image(s).')
|
||||
log(f"{NODE_NAME} Processed {len(ret_images)} image(s).")
|
||||
return (torch.cat(ret_images, dim=0),)
|
||||
|
||||
|
||||
|
||||
+12
-12
@@ -1,5 +1,7 @@
|
||||
from .imagefunc import *
|
||||
|
||||
NODE_NAME = 'InnerShadow'
|
||||
|
||||
class InnerShadow:
|
||||
|
||||
def __init__(self):
|
||||
@@ -43,22 +45,22 @@ class InnerShadow:
|
||||
l_images = []
|
||||
l_masks = []
|
||||
ret_images = []
|
||||
|
||||
for b in background_image:
|
||||
b_images.append(b)
|
||||
b_images.append(torch.unsqueeze(b, 0))
|
||||
for l in layer_image:
|
||||
l_images.append(l)
|
||||
l_images.append(torch.unsqueeze(l, 0))
|
||||
m = tensor2pil(l)
|
||||
if tensor2pil(l).mode == 'RGBA':
|
||||
l_masks.append(m.convert('RGBA').split()[-1])
|
||||
else:
|
||||
l_masks.append(Image.new('L', m.size, 'white'))
|
||||
if m.mode == 'RGBA':
|
||||
l_masks.append(m.split()[-1])
|
||||
if layer_mask is not None:
|
||||
l_masks = []
|
||||
for m in layer_mask:
|
||||
if invert_mask:
|
||||
m = 1 - m
|
||||
l_masks.append(tensor2pil(m).convert('L'))
|
||||
l_masks.append(tensor2pil(torch.unsqueeze(m, 0)).convert('L'))
|
||||
if len(l_masks) == 0:
|
||||
log(f"Error: {NODE_NAME} skipped, because the available mask is not found.")
|
||||
return (background_image,)
|
||||
max_batch = max(len(b_images), len(l_images), len(l_masks))
|
||||
distance_x = -distance_x
|
||||
distance_y = -distance_y
|
||||
@@ -67,14 +69,12 @@ class InnerShadow:
|
||||
background_image = b_images[i] if i < len(b_images) else b_images[-1]
|
||||
layer_image = l_images[i] if i < len(l_images) else l_images[-1]
|
||||
_mask = l_masks[i] if i < len(l_masks) else l_masks[-1]
|
||||
|
||||
# preprocess
|
||||
_canvas = tensor2pil(background_image).convert('RGB')
|
||||
_layer = tensor2pil(layer_image).convert('RGB')
|
||||
|
||||
if _mask.size != _layer.size:
|
||||
_mask = Image.new('L', _layer.size, 'white')
|
||||
log('Warning: mask mismatch, droped!')
|
||||
log(f"Warning: {NODE_NAME} mask mismatch, dropped!")
|
||||
|
||||
if distance_x != 0 or distance_y != 0:
|
||||
__mask = shift_image(_mask, distance_x, distance_y) # 位移
|
||||
@@ -88,7 +88,7 @@ class InnerShadow:
|
||||
|
||||
ret_images.append(pil2tensor(_canvas))
|
||||
|
||||
log(f'InnerShadow Processed {len(ret_images)} image(s).')
|
||||
log(f"{NODE_NAME} Processed {len(ret_images)} image(s).")
|
||||
return (torch.cat(ret_images, dim=0),)
|
||||
|
||||
NODE_CLASS_MAPPINGS = {
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
from .imagefunc import *
|
||||
|
||||
NODE_NAME = 'MaskBoxDetect'
|
||||
|
||||
class MaskBoxDetect:
|
||||
|
||||
def __init__(self):
|
||||
@@ -28,6 +30,9 @@ class MaskBoxDetect:
|
||||
|
||||
def mask_box_detect(self,mask, detect, x_adjust, y_adjust, scale_adjust):
|
||||
|
||||
if mask.shape[0] > 0:
|
||||
mask = torch.unsqueeze(mask[0], 0)
|
||||
|
||||
_mask = mask2image(mask).convert('RGB')
|
||||
|
||||
_mask = gaussian_blur(_mask, 20).convert('L')
|
||||
@@ -54,6 +59,7 @@ class MaskBoxDetect:
|
||||
preview_image = tensor2pil(mask).convert('RGB')
|
||||
preview_image = draw_rect(preview_image, x - x_adjust, y - y_adjust, width, height, line_color="#F00000", line_width=int(preview_image.height / 60))
|
||||
preview_image = draw_rect(preview_image, x, y, width, height, line_color="#00F000", line_width=int(preview_image.height / 40))
|
||||
log(f"{NODE_NAME} Processed.")
|
||||
return ( pil2tensor(preview_image), round(x_percent, 2), round(y_percent, 2), _width, _height, x, y,)
|
||||
|
||||
NODE_CLASS_MAPPINGS = {
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
from .imagefunc import *
|
||||
|
||||
NODE_NAME = 'MaskEdgeShrink'
|
||||
|
||||
class MaskEdgeShrink:
|
||||
|
||||
def __init__(self):
|
||||
@@ -36,7 +38,7 @@ class MaskEdgeShrink:
|
||||
for m in mask:
|
||||
if invert_mask:
|
||||
m = 1 - m
|
||||
l_masks.append(tensor2pil(m).convert('L'))
|
||||
l_masks.append(tensor2pil(torch.unsqueeze(m, 0)).convert('L'))
|
||||
|
||||
glow_range = shrink_level * soft
|
||||
blur = 12
|
||||
@@ -63,7 +65,7 @@ class MaskEdgeShrink:
|
||||
|
||||
ret_masks.append(image2mask(_layer))
|
||||
|
||||
log(f'MaskEdgeShrink Processed {len(ret_masks)} image(s).')
|
||||
log(f"{NODE_NAME} Processed {len(ret_masks)} image(s).")
|
||||
return (torch.cat(ret_masks, dim=0),)
|
||||
|
||||
|
||||
|
||||
+5
-7
@@ -1,7 +1,8 @@
|
||||
import copy
|
||||
|
||||
from .imagefunc import *
|
||||
|
||||
NODE_NAME = 'MaskGradient'
|
||||
|
||||
class MaskGradient:
|
||||
|
||||
def __init__(self):
|
||||
@@ -34,11 +35,10 @@ class MaskGradient:
|
||||
l_masks = []
|
||||
ret_masks = []
|
||||
|
||||
|
||||
for m in mask:
|
||||
if invert_mask:
|
||||
m = 1 - m
|
||||
l_masks.append(tensor2pil(m).convert('L'))
|
||||
l_masks.append(tensor2pil(torch.unsqueeze(m, 0)).convert('L'))
|
||||
|
||||
for i in range(len(l_masks)):
|
||||
_mask = l_masks[i]
|
||||
@@ -48,9 +48,7 @@ class MaskGradient:
|
||||
_gradient = gradient('#000000', '#FFFFFF',
|
||||
_mask.width, _mask.height, 0)
|
||||
(box_x, box_y, box_width, box_height) = min_bounding_rect(_mask)
|
||||
# preview_image = mask2image(mask).convert('RGB')
|
||||
# preview_image = draw_rect(preview_image, box_x, box_y, box_width, box_height,
|
||||
# line_color = "#F00000", line_width = int(preview_image.height / 60))
|
||||
|
||||
if gradient_side == 'top':
|
||||
boxsize = (width, box_height)
|
||||
_gradient = _gradient.transpose(Image.FLIP_TOP_BOTTOM)
|
||||
@@ -131,7 +129,7 @@ class MaskGradient:
|
||||
_canvas = chop_image(_mask, _canvas, 'normal', opacity)
|
||||
ret_masks.append(image2mask(_canvas))
|
||||
|
||||
log(f'MaskGradient Processed {len(ret_masks)} image(s).')
|
||||
log(f"{NODE_NAME} Processed {len(ret_masks)} image(s).")
|
||||
return (torch.cat(ret_masks, dim=0),)
|
||||
|
||||
NODE_CLASS_MAPPINGS = {
|
||||
|
||||
+4
-2
@@ -1,5 +1,7 @@
|
||||
from .imagefunc import *
|
||||
|
||||
NODE_NAME = ''
|
||||
|
||||
class MaskGrow:
|
||||
|
||||
def __init__(self):
|
||||
@@ -33,14 +35,14 @@ class MaskGrow:
|
||||
for m in mask:
|
||||
if invert_mask:
|
||||
m = 1 - m
|
||||
l_masks.append(tensor2pil(m).convert('L'))
|
||||
l_masks.append(tensor2pil(torch.unsqueeze(m, 0)).convert('L'))
|
||||
|
||||
for i in range(len(l_masks)):
|
||||
|
||||
_mask = l_masks[i]
|
||||
ret_masks.append(expand_mask(image2mask(_mask), grow, blur) )
|
||||
|
||||
log(f'MaskGrow Processed {len(ret_masks)} image(s).')
|
||||
log(f"{NODE_NAME} Processed {len(ret_masks)} image(s).")
|
||||
return (torch.cat(ret_masks, dim=0),)
|
||||
|
||||
|
||||
|
||||
+4
-2
@@ -1,5 +1,7 @@
|
||||
from .imagefunc import *
|
||||
|
||||
NODE_NAME = 'MaskInvert'
|
||||
|
||||
class MaskInvert:
|
||||
|
||||
def __init__(self):
|
||||
@@ -26,13 +28,13 @@ class MaskInvert:
|
||||
ret_masks = []
|
||||
|
||||
for m in mask:
|
||||
l_masks.append(tensor2pil(m).convert('L'))
|
||||
l_masks.append(tensor2pil(torch.unsqueeze(m, 0)).convert('L'))
|
||||
|
||||
for i in range(len(l_masks)):
|
||||
_mask = l_masks[i]
|
||||
ret_masks.append(mask_invert(image2mask(_mask)))
|
||||
|
||||
log(f'MaskInvert Processed {len(ret_masks)} image(s).')
|
||||
log(f"{NODE_NAME} Processed {len(ret_masks)} image(s).")
|
||||
return (torch.cat(ret_masks, dim=0),)
|
||||
|
||||
NODE_CLASS_MAPPINGS = {
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
import copy
|
||||
|
||||
from .imagefunc import *
|
||||
|
||||
NODE_NAME = 'MaskMotionBlur'
|
||||
|
||||
class MaskMotionBlur:
|
||||
|
||||
def __init__(self):
|
||||
@@ -35,14 +36,14 @@ class MaskMotionBlur:
|
||||
for m in mask:
|
||||
if invert_mask:
|
||||
m = 1 - m
|
||||
l_masks.append(tensor2pil(m).convert('L'))
|
||||
l_masks.append(tensor2pil(torch.unsqueeze(m, 0)).convert('L'))
|
||||
|
||||
for i in range(len(l_masks)):
|
||||
_mask = l_masks[i]
|
||||
_blurimage = motion_blur(_mask, angle, blur)
|
||||
ret_masks.append(image2mask(_blurimage))
|
||||
|
||||
log(f'MaskMotionBlur Processed {len(ret_masks)} image(s).')
|
||||
log(f"{NODE_NAME} Processed {len(ret_masks)} image(s).")
|
||||
return (torch.cat(ret_masks, dim=0),)
|
||||
|
||||
NODE_CLASS_MAPPINGS = {
|
||||
|
||||
+7
-5
@@ -1,6 +1,8 @@
|
||||
from .imagefunc import *
|
||||
|
||||
class MaskStrkoe:
|
||||
NODE_NAME = 'MaskStroke'
|
||||
|
||||
class MaskStroke:
|
||||
|
||||
def __init__(self):
|
||||
pass
|
||||
@@ -34,7 +36,7 @@ class MaskStrkoe:
|
||||
for m in mask:
|
||||
if invert_mask:
|
||||
m = 1 - m
|
||||
l_masks.append(tensor2pil(m).convert('L'))
|
||||
l_masks.append(tensor2pil(torch.unsqueeze(m, 0)).convert('L'))
|
||||
|
||||
for i in range(len(l_masks)):
|
||||
_mask = l_masks[i]
|
||||
@@ -46,13 +48,13 @@ class MaskStrkoe:
|
||||
stroke_mask = subtract_mask(outer_mask, inner_mask)
|
||||
ret_masks.append(stroke_mask)
|
||||
|
||||
log(f'MaskStrkoe Processed {len(ret_masks)} image(s).')
|
||||
log(f"{NODE_NAME} Processed {len(ret_masks)} image(s).")
|
||||
return (torch.cat(ret_masks, dim=0),)
|
||||
|
||||
NODE_CLASS_MAPPINGS = {
|
||||
"LayerMask: MaskStrkoe": MaskStrkoe
|
||||
"LayerMask: MaskStroke": MaskStroke
|
||||
}
|
||||
|
||||
NODE_DISPLAY_NAME_MAPPINGS = {
|
||||
"LayerMask: MaskStrkoe": "LayerMask: MaskStrkoe"
|
||||
"LayerMask: MaskStroke": "LayerMask: MaskStroke"
|
||||
}
|
||||
+6
-5
@@ -1,5 +1,7 @@
|
||||
from .imagefunc import *
|
||||
|
||||
NODE_NAME = 'MotionBlur'
|
||||
|
||||
class MotionBlur:
|
||||
|
||||
def __init__(self):
|
||||
@@ -28,14 +30,13 @@ class MotionBlur:
|
||||
|
||||
ret_images = []
|
||||
|
||||
for image in image:
|
||||
for i in image:
|
||||
|
||||
_canvas = tensor2pil(image).convert('RGB')
|
||||
ret_image = motion_blur(_canvas, angle, blur)
|
||||
_canvas = tensor2pil(torch.unsqueeze(i, 0)).convert('RGB')
|
||||
|
||||
ret_images.append(pil2tensor(ret_image))
|
||||
ret_images.append(pil2tensor(motion_blur(_canvas, angle, blur)))
|
||||
|
||||
log(f'MotionBlur Processed {len(ret_images)} image(s).')
|
||||
log(f"{NODE_NAME} Processed {len(ret_images)} image(s).")
|
||||
return (torch.cat(ret_images, dim=0),)
|
||||
|
||||
NODE_CLASS_MAPPINGS = {
|
||||
|
||||
+11
-12
@@ -1,5 +1,6 @@
|
||||
from .imagefunc import *
|
||||
|
||||
NODE_NAME = 'OuterGlow'
|
||||
class OuterGlow:
|
||||
|
||||
def __init__(self):
|
||||
@@ -46,35 +47,33 @@ class OuterGlow:
|
||||
ret_images = []
|
||||
|
||||
for b in background_image:
|
||||
b_images.append(b)
|
||||
b_images.append(torch.unsqueeze(b, 0))
|
||||
for l in layer_image:
|
||||
l_images.append(l)
|
||||
l_images.append(torch.unsqueeze(l, 0))
|
||||
m = tensor2pil(l)
|
||||
if tensor2pil(l).mode == 'RGBA':
|
||||
l_masks.append(m.convert('RGBA').split()[-1])
|
||||
else:
|
||||
l_masks.append(Image.new('L', m.size, 'white'))
|
||||
if m.mode == 'RGBA':
|
||||
l_masks.append(m.split()[-1])
|
||||
if layer_mask is not None:
|
||||
l_masks = []
|
||||
for m in layer_mask:
|
||||
if invert_mask:
|
||||
m = 1 - m
|
||||
l_masks.append(tensor2pil(m).convert('L'))
|
||||
l_masks.append(tensor2pil(torch.unsqueeze(m, 0)).convert('L'))
|
||||
if len(l_masks) == 0:
|
||||
log(f"Error: {NODE_NAME} skipped, because the available mask is not found.")
|
||||
return (background_image,)
|
||||
max_batch = max(len(b_images), len(l_images), len(l_masks))
|
||||
blur_factor = blur / 20.0
|
||||
for i in range(max_batch):
|
||||
background_image = b_images[i] if i < len(b_images) else b_images[-1]
|
||||
layer_image = l_images[i] if i < len(l_images) else l_images[-1]
|
||||
_mask = l_masks[i] if i < len(l_masks) else l_masks[-1]
|
||||
|
||||
# preprocess
|
||||
_canvas = tensor2pil(background_image).convert('RGB')
|
||||
_layer = tensor2pil(layer_image).convert('RGB')
|
||||
|
||||
if _mask.size != _layer.size:
|
||||
_mask = Image.new('L', _layer.size, 'white')
|
||||
log('Warning: mask mismatch, droped!')
|
||||
|
||||
log(f"Warning: {NODE_NAME} mask mismatch, dropped!")
|
||||
grow = glow_range
|
||||
for x in range(brightness):
|
||||
blur = int(grow * blur_factor)
|
||||
@@ -91,7 +90,7 @@ class OuterGlow:
|
||||
|
||||
ret_images.append(pil2tensor(_canvas))
|
||||
|
||||
log(f'OuterGlow Processed {len(ret_images)} image(s).')
|
||||
log(f"{NODE_NAME} Processed {len(ret_images)} image(s).")
|
||||
return (torch.cat(ret_images, dim=0),)
|
||||
|
||||
|
||||
|
||||
+9
-8
@@ -2,6 +2,8 @@ import copy
|
||||
from pymatting import *
|
||||
from .imagefunc import *
|
||||
|
||||
NODE_NAME = 'PixelSpread'
|
||||
|
||||
class PixelSpread:
|
||||
|
||||
def __init__(self):
|
||||
@@ -34,7 +36,7 @@ class PixelSpread:
|
||||
ret_images = []
|
||||
|
||||
for l in image:
|
||||
i = tensor2pil(l)
|
||||
i = tensor2pil(torch.unsqueeze(l, 0))
|
||||
l_images.append(i)
|
||||
if i.mode == 'RGBA':
|
||||
l_masks.append(i.split()[-1])
|
||||
@@ -45,11 +47,10 @@ class PixelSpread:
|
||||
for m in mask:
|
||||
if invert_mask:
|
||||
m = 1 - m
|
||||
l_masks.append(tensor2pil(m).convert('L'))
|
||||
l_masks.append(tensor2pil(torch.unsqueeze(m, 0)).convert('L'))
|
||||
max_batch = max(len(l_images), len(l_masks))
|
||||
|
||||
for i in range(max_batch):
|
||||
|
||||
_image = l_images[i] if i < len(l_images) else l_images[-1]
|
||||
_mask = l_masks[i] if i < len(l_masks) else l_masks[-1]
|
||||
if mask_grow != 0:
|
||||
@@ -57,20 +58,20 @@ class PixelSpread:
|
||||
_mask = mask2image(_mask)
|
||||
i1 = pil2tensor(_image.convert('RGB'))
|
||||
_mask = _mask.convert('RGB')
|
||||
if _image.size != _mask.size:
|
||||
log(f"Error: {NODE_NAME} skipped, because the mask is not match image.")
|
||||
return (image,)
|
||||
i_dup = copy.deepcopy(i1.cpu().numpy().astype(np.float64))
|
||||
a_dup = copy.deepcopy(pil2tensor(_mask).cpu().numpy().astype(np.float64))
|
||||
fg = copy.deepcopy(i1.cpu().numpy().astype(np.float64))
|
||||
|
||||
for index, img in enumerate(i_dup):
|
||||
alpha = a_dup[index][:, :, 0] # convert to single channel
|
||||
# trimap = fix_trimap(trimap, 0.01, 0.99)
|
||||
# alpha = estimate_alpha_cf(image, trimap, laplacian_kwargs={"epsilon": 1e-6},
|
||||
# cg_kwargs={"maxiter": 100})
|
||||
alpha = a_dup[index][:, :, 0]
|
||||
fg[index], _ = estimate_foreground_ml(img, np.array(alpha), return_background=True)
|
||||
|
||||
ret_images.append(torch.from_numpy(fg.astype(np.float32)))
|
||||
|
||||
log(f'PixelSpread Processed {len(ret_images)} image(s).')
|
||||
log(f"{NODE_NAME} Processed {len(ret_images)} image(s).")
|
||||
return (torch.cat(ret_images, dim=0),)
|
||||
|
||||
NODE_CLASS_MAPPINGS = {
|
||||
|
||||
+2
-3
@@ -50,6 +50,7 @@ class RemBgUltra:
|
||||
ret_masks = []
|
||||
|
||||
for i in image:
|
||||
i = torch.unsqueeze(i, 0)
|
||||
rmbgmodel = load_model()
|
||||
orig_image = tensor2pil(i).convert('RGB')
|
||||
w,h = orig_image.size
|
||||
@@ -57,7 +58,6 @@ class RemBgUltra:
|
||||
im_tensor = torch.tensor(im_np, dtype=torch.float32).permute(2,0,1)
|
||||
im_tensor = torch.unsqueeze(im_tensor,0)
|
||||
im_tensor = torch.divide(im_tensor,255.0)
|
||||
|
||||
im_tensor = TF.normalize(im_tensor, [0.5, 0.5, 0.5], [1.0, 1.0, 1.0])
|
||||
if torch.cuda.is_available():
|
||||
im_tensor=im_tensor.cuda()
|
||||
@@ -70,9 +70,8 @@ class RemBgUltra:
|
||||
_mask = Image.fromarray(np.squeeze(im_array)).convert('L')
|
||||
if process_detail:
|
||||
# ultra edge process
|
||||
i1 = torch.unsqueeze(i, dim=0)
|
||||
d = detail_range * 2 + 1
|
||||
i_dup = copy.deepcopy(i1.cpu().numpy().astype(np.float64))
|
||||
i_dup = copy.deepcopy(i.cpu().numpy().astype(np.float64))
|
||||
a_dup = copy.deepcopy(pil2tensor(_mask.convert('RGB')).cpu().numpy().astype(np.float64))
|
||||
for index, img in enumerate(i_dup):
|
||||
trimap = a_dup[index][:,:,0] # convert to single channel
|
||||
|
||||
+39
-10
@@ -1,5 +1,7 @@
|
||||
from .imagefunc import *
|
||||
|
||||
NODE_NAME = 'RestoreCropBox'
|
||||
|
||||
class RestoreCropBox:
|
||||
|
||||
def __init__(self):
|
||||
@@ -30,18 +32,45 @@ class RestoreCropBox:
|
||||
croped_mask=None
|
||||
):
|
||||
|
||||
_canvas = tensor2pil(background_image).convert('RGB')
|
||||
_layer = tensor2pil(croped_image).convert('RGB')
|
||||
_mask = Image.new('L', size=_layer.size, color='white')
|
||||
b_images = []
|
||||
l_images = []
|
||||
l_masks = []
|
||||
ret_images = []
|
||||
ret_masks = []
|
||||
for b in background_image:
|
||||
b_images.append(torch.unsqueeze(b, 0))
|
||||
for l in croped_image:
|
||||
l_images.append(torch.unsqueeze(l, 0))
|
||||
m = tensor2pil(l)
|
||||
if m.mode == 'RGBA':
|
||||
l_masks.append(m.split()[-1])
|
||||
else:
|
||||
l_masks.append(Image.new('L', size=m.size, color='white'))
|
||||
if croped_mask is not None:
|
||||
if invert_mask:
|
||||
croped_mask = 1 - croped_mask
|
||||
_mask = mask2image(croped_mask).convert('L')
|
||||
ret_mask = Image.new('L', size=_canvas.size, color='black')
|
||||
_canvas.paste(_layer, box=tuple(crop_box), mask=_mask)
|
||||
ret_mask.paste(_mask, box=tuple(crop_box))
|
||||
l_masks = []
|
||||
for m in croped_mask:
|
||||
if invert_mask:
|
||||
m = 1 - m
|
||||
l_masks.append(tensor2pil(torch.unsqueeze(m, 0)).convert('L'))
|
||||
|
||||
max_batch = max(len(b_images), len(l_images), len(l_masks))
|
||||
for i in range(max_batch):
|
||||
background_image = b_images[i] if i < len(b_images) else b_images[-1]
|
||||
croped_image = l_images[i] if i < len(l_images) else l_images[-1]
|
||||
_mask = l_masks[i] if i < len(l_masks) else l_masks[-1]
|
||||
|
||||
_canvas = tensor2pil(background_image).convert('RGB')
|
||||
_layer = tensor2pil(croped_image).convert('RGB')
|
||||
|
||||
ret_mask = Image.new('L', size=_canvas.size, color='black')
|
||||
_canvas.paste(_layer, box=tuple(crop_box), mask=_mask)
|
||||
ret_mask.paste(_mask, box=tuple(crop_box))
|
||||
ret_images.append(pil2tensor(_canvas))
|
||||
ret_masks.append(image2mask(ret_mask))
|
||||
|
||||
log(f"{NODE_NAME} Processed {len(ret_images)} image(s).")
|
||||
return (torch.cat(ret_images, dim=0), torch.cat(ret_masks, dim=0),)
|
||||
|
||||
return (pil2tensor(_canvas), image2mask(ret_mask),)
|
||||
|
||||
NODE_CLASS_MAPPINGS = {
|
||||
"LayerUtility: RestoreCropBox": RestoreCropBox
|
||||
|
||||
+6
-4
@@ -1,5 +1,7 @@
|
||||
from .imagefunc import *
|
||||
|
||||
NODE_NAME = 'SkinBeauty'
|
||||
|
||||
class SkinBeauty:
|
||||
|
||||
def __init__(self):
|
||||
@@ -30,9 +32,9 @@ class SkinBeauty:
|
||||
|
||||
ret_images = []
|
||||
ret_masks = []
|
||||
for image in image:
|
||||
|
||||
_canvas = tensor2pil(image).convert('RGB')
|
||||
for i in image:
|
||||
i = torch.unsqueeze(i, 0)
|
||||
_canvas = tensor2pil(i).convert('RGB')
|
||||
_R, _, _, _ = image_channel_split(_canvas, mode='RGB')
|
||||
_otsumask = gray_threshold(_R, otsu=True)
|
||||
_removebkgd = remove_background(_R, _otsumask, '#000000')
|
||||
@@ -48,7 +50,7 @@ class SkinBeauty:
|
||||
ret_images.append(pil2tensor(_canvas))
|
||||
ret_masks.append(image2mask(light_mask))
|
||||
|
||||
log(f'SkinBeauty Processed {len(ret_images)} image(s).')
|
||||
log(f"{NODE_NAME} Processed {len(ret_images)} image(s).")
|
||||
return (torch.cat(ret_images, dim=0), torch.cat(ret_masks, dim=0),)
|
||||
|
||||
|
||||
|
||||
+6
-4
@@ -1,5 +1,7 @@
|
||||
from .imagefunc import *
|
||||
|
||||
NODE_NAME = 'SoftLight'
|
||||
|
||||
class SoftLight:
|
||||
|
||||
def __init__(self):
|
||||
@@ -29,10 +31,10 @@ class SoftLight:
|
||||
|
||||
ret_images = []
|
||||
|
||||
for image in image:
|
||||
|
||||
for i in image:
|
||||
i = torch.unsqueeze(i, 0)
|
||||
blend_mode = 'screen'
|
||||
_canvas = tensor2pil(image).convert('RGB')
|
||||
_canvas = tensor2pil(i).convert('RGB')
|
||||
blur = int((_canvas.width + _canvas.height) / 200 * soft)
|
||||
_otsumask = gray_threshold(_canvas, otsu=True)
|
||||
_removebkgd = remove_background(_canvas, _otsumask, '#000000').convert('L')
|
||||
@@ -47,7 +49,7 @@ class SoftLight:
|
||||
|
||||
ret_images.append(pil2tensor(_canvas))
|
||||
|
||||
log(f'SoftLight Processed {len(ret_images)} image(s).')
|
||||
log(f"{NODE_NAME} Processed {len(ret_images)} image(s).")
|
||||
return (torch.cat(ret_images, dim=0),)
|
||||
|
||||
NODE_CLASS_MAPPINGS = {
|
||||
|
||||
+13
-9
@@ -1,5 +1,7 @@
|
||||
from .imagefunc import *
|
||||
|
||||
NODE_NAME = 'Storke'
|
||||
|
||||
class Stroke:
|
||||
|
||||
def __init__(self):
|
||||
@@ -42,20 +44,22 @@ class Stroke:
|
||||
l_masks = []
|
||||
ret_images = []
|
||||
for b in background_image:
|
||||
b_images.append(b)
|
||||
b_images.append(torch.unsqueeze(b, 0))
|
||||
for l in layer_image:
|
||||
l_images.append(l)
|
||||
l_images.append(torch.unsqueeze(l, 0))
|
||||
m = tensor2pil(l)
|
||||
if tensor2pil(l).mode == 'RGBA':
|
||||
l_masks.append(m.convert('RGBA').split()[-1])
|
||||
else:
|
||||
l_masks.append(Image.new('L', m.size, 'white'))
|
||||
if m.mode == 'RGBA':
|
||||
l_masks.append(m.split()[-1])
|
||||
if layer_mask is not None:
|
||||
l_masks = []
|
||||
for m in layer_mask:
|
||||
if invert_mask:
|
||||
m = 1 - m
|
||||
l_masks.append(tensor2pil(m).convert('L'))
|
||||
l_masks.append(tensor2pil(torch.unsqueeze(m, 0)).convert('L'))
|
||||
if len(l_masks) == 0:
|
||||
log(f"Error: {NODE_NAME} skipped, because the available mask is not found.")
|
||||
return (background_image,)
|
||||
|
||||
max_batch = max(len(b_images), len(l_images), len(l_masks))
|
||||
|
||||
grow_offset = int(stroke_width / 2)
|
||||
@@ -72,7 +76,7 @@ class Stroke:
|
||||
|
||||
if _mask.size != _layer.size:
|
||||
_mask = Image.new('L', _layer.size, 'white')
|
||||
log('Warning: mask mismatch, droped!')
|
||||
log(f"Warning: {NODE_NAME} mask mismatch, dropped!")
|
||||
|
||||
inner_mask = expand_mask(image2mask(_mask), inner_stroke, blur)
|
||||
outer_mask = expand_mask(image2mask(_mask), outer_stroke, blur)
|
||||
@@ -84,7 +88,7 @@ class Stroke:
|
||||
|
||||
ret_images.append(pil2tensor(_canvas))
|
||||
|
||||
log(f'Stroke Processed {len(ret_images)} image(s).')
|
||||
log(f"{NODE_NAME} Processed {len(ret_images)} image(s).")
|
||||
return (torch.cat(ret_images, dim=0),)
|
||||
|
||||
NODE_CLASS_MAPPINGS = {
|
||||
|
||||
@@ -3,6 +3,7 @@ import random
|
||||
from PIL import ImageFont
|
||||
from .imagefunc import *
|
||||
|
||||
NODE_NAME = 'TextImage'
|
||||
any = AnyType("*")
|
||||
|
||||
class TextImage:
|
||||
@@ -127,6 +128,7 @@ class TextImage:
|
||||
_color = Image.new('RGB', size=(width, height), color=text_color)
|
||||
_canvas.paste(_color, mask=_mask.convert('L'))
|
||||
_canvas = RGB2RGBA(_canvas, _mask)
|
||||
log(f"{NODE_NAME} Processed.")
|
||||
return (pil2tensor(_canvas), image2mask(_mask),)
|
||||
|
||||
NODE_CLASS_MAPPINGS = {
|
||||
|
||||
+6
-4
@@ -1,5 +1,7 @@
|
||||
from .imagefunc import *
|
||||
|
||||
NODE_NAME = 'WaterColor'
|
||||
|
||||
class WaterColor:
|
||||
|
||||
def __init__(self):
|
||||
@@ -29,15 +31,15 @@ class WaterColor:
|
||||
|
||||
ret_images = []
|
||||
|
||||
for image in image:
|
||||
|
||||
_canvas = tensor2pil(image).convert('RGB')
|
||||
for i in image:
|
||||
i = torch.unsqueeze(i, 0)
|
||||
_canvas = tensor2pil(i).convert('RGB')
|
||||
_image = image_watercolor(_canvas, level=101-line_density)
|
||||
ret_image = chop_image(_canvas, _image, 'normal', opacity)
|
||||
|
||||
ret_images.append(pil2tensor(ret_image))
|
||||
|
||||
log(f'WaterColor Processed {len(ret_images)} image(s).')
|
||||
log(f"{NODE_NAME} Processed {len(ret_images)} image(s).")
|
||||
return (torch.cat(ret_images, dim=0),)
|
||||
|
||||
NODE_CLASS_MAPPINGS = {
|
||||
|
||||
Reference in New Issue
Block a user