Commit CropByMask and RestoreCropBox nodes. Commit ColorAdapter node,Commit MaskStroke node.
Commit CropByMask and RestoreCropBox nodes. The combination of these two can partially crop and redraw the image before restoring it. Commit ColorAdapter node, that can automatically adjust the color tone of the image. Commit MaskStroke node, it can generate mask contour strokes.
This commit is contained in:
@@ -13,6 +13,9 @@ Nodes are divided into four groups according to their functions: LayerStyle, Lay
|
||||
[中文说明点这里](./README_CN.MD)
|
||||
|
||||
## Update
|
||||
* Commit [CropByMask](#CropByMask) and [RestoreCropBox](#RestoreCropBox) nodes. The combination of these two can partially crop and redraw the image before restoring it.
|
||||
* Commit [ColorAdapter](#ColorAdapter) node, that can automatically adjust the color tone of the image.
|
||||
* Commit [MaskStroke](#MaskStroke) node, it can generate mask contour strokes.
|
||||
* Add [LayerColor](#LayerColor) node group, used to adjust image color. it include [LUT Apply](#LUT), [Gamma](#Gamma), [Brightness & Contrast](#Brightness), [RGB](#RGB), [YUV](#YUV), [LAB](#LAB) adn [HSV](#HSV).
|
||||
* Commit [ImageChannelSplit](#ImageChannelSplit) and [ImageChannelMerge](#ImageChannelMerge) nodes.
|
||||
* Commit [MaskMotionBlur](#MaskMotionBlur) node.
|
||||
@@ -144,6 +147,7 @@ Node options:
|
||||
* opacity: Opacity of stroke.
|
||||
* stroke_grow: Stroke expansion/contraction amplitude, positive values indicate expansion and negative values indicate contraction.
|
||||
* stroke_width: Stroke width.
|
||||
* blur: Blur of stroke.
|
||||
* stroke_color<sup>4</sup>: Stroke color, described in hexadecimal RGB format.
|
||||
* [note](#notes)
|
||||
|
||||
@@ -196,6 +200,14 @@ Node options:
|
||||
Open the text editing software and find the line starting with "LUT dir=", after "=", enter the custom folder path name. all .cube files in this folder will be collected and displayed in the node list during ComfyUI initialization.
|
||||
If the folder set in ini is invalid, the LUT folder that comes with the plugin will be enabled.
|
||||
|
||||
### <a id="table1">ColorAdapter</a>
|
||||
Auto adjust the color tone of the image to resemble the reference image.
|
||||

|
||||
|
||||
Node options:
|
||||

|
||||
* opacity: The opacity of an image after adjusting its color tone.
|
||||
|
||||
### <a id="table1">Gamma</a>
|
||||
Change the gamma value of the image.
|
||||
|
||||
@@ -320,6 +332,16 @@ Node options:
|
||||
* gradient_offset: Gradient position offset.
|
||||
* opacity: The opacity of the gradient.
|
||||
|
||||
### <a id="table1">MaskStroke</a>
|
||||
Generate mask contour strokes.
|
||||

|
||||
|
||||
Node options:
|
||||

|
||||
* invert_mask: Whether to reverse the mask.
|
||||
* stroke_grow: Stroke expansion/contraction amplitude, positive values indicate expansion and negative values indicate contraction.
|
||||
* stroke_width: Stroke width.
|
||||
* blur: Blur of stroke.
|
||||
|
||||
### <a id="table1">MaskPreview</a>
|
||||
Preview the input mask
|
||||
@@ -361,6 +383,41 @@ Node options:
|
||||
* anti_aliasing: Anti aliasing, ranging from 0 to 16, the larger the value, the less obvious the aliasing. An excessively high value will significantly reduce the processing speed of the node.
|
||||
* [note](#notes)
|
||||
|
||||
### <a id="table1">CropByMask</a>
|
||||
Crop the image according to the mask range, and set the size of the surrounding borders to be retained.
|
||||
This node can be used in conjunction with the [RestoreCropBox](#RestoreCropBox) node to crop and modify parts of an image, and then paste them back in place.
|
||||

|
||||
|
||||
Node options:
|
||||

|
||||
* image<sup>5</sup>: The input image.
|
||||
* mask_for_crop<sup>5</sup>: Mask of the image, it will automatically be cut according to the mask range.
|
||||
* invert_mask: Whether to reverse the mask.
|
||||
* detect: Detection method, min_bounding_rect is the minimum bounding rectangle, max_inscribed_rect is the maximum inscribed rectangle.
|
||||
* top_reserve: Cut the top to preserve size.
|
||||
* bottom_reserve: Cut the bottom to preserve size.
|
||||
* left_reserve: Cut the left to preserve size.
|
||||
* right_reserve: Cut the right to preserve size.
|
||||
* [note](#notes)
|
||||
|
||||
Output:
|
||||
* croped_image: The image after crop.
|
||||
* croped_mask: The mask after crop.
|
||||
* crop_box: The trimmed box data is used when restoring the RestoreCropBox node.
|
||||
* box_preview: Preview image of cutting position, red represents the detected range, and green represents the cutting range after adding the reserved border.
|
||||
|
||||
### <a id="table1">RestoreCropBox</a>
|
||||
Restore the cropped image to the original image by [CropByMask](#CropByMask).
|
||||
|
||||
Node options:
|
||||

|
||||
* background_image: The original image before cutting.
|
||||
* croped_image<sup>5</sup>: The cropped image. If the middle is enlarged, the size needs to be restored before restoration.
|
||||
* croped_mask<sup>5</sup>: The cut mask.
|
||||
* crop_box: Box data during cutting.
|
||||
* invert_mask: Whether to reverse the mask.
|
||||
* [note](#notes)
|
||||
|
||||
### <a id="table1">ImageBlend</a>
|
||||
A simple node for composit layer image and background image, multiple blend modes are available for option, and transparency can be set.
|
||||

|
||||
@@ -535,25 +592,16 @@ image Some JSON workflow files in the workflow directory, that is example for Co
|
||||
|
||||
## The following tasks are in the planned list
|
||||
|
||||
LayerUtility:
|
||||
* image channel split
|
||||
* image channel merge
|
||||
|
||||
LayerMask:
|
||||
* text mask
|
||||
* mask subtract
|
||||
* mask combine
|
||||
* mask diffrent
|
||||
* TextMask
|
||||
|
||||
LayerFilter:
|
||||
* emboss
|
||||
* contour
|
||||
* findedge
|
||||
* Emboss
|
||||
* Contour
|
||||
* Findedge
|
||||
* PhotoStyle
|
||||
* ColorMap
|
||||
|
||||
LayerColor:
|
||||
* RGB adjust
|
||||
* HSV adjust
|
||||
* brightness & contrast adjust
|
||||
|
||||
## How to install
|
||||
|
||||
|
||||
+63
-13
@@ -10,6 +10,9 @@ ComfyUI
|
||||
* [LayerFilter](#LayerFilter)节点组提供图像效果滤镜。
|
||||
|
||||
## 更新说明
|
||||
* 添加[CropByMask](#CropByMask) 和 [RestoreCropBox](#RestoreCropBox)节点。此二者配合可将图片局部裁切重绘然后还原。
|
||||
* 添加[ColorAdapter](#ColorAdapter) 节点,可自动调整图片色调。
|
||||
* 添加[MaskStroke](#MaskStroke) 节点,可产生mask描边。
|
||||
* 添加[LayerColor](#LayerColor)节点组,用于调整图像颜色。包括[LUT Apply](#LUT),[Gamma](#Gamma), [Brightness & Contrast](#Brightness), [RGB](#RGB), [YUV](#YUV), [LAB](#LAB)和[HSV](#HSV)。
|
||||
* 添加[ImageChannelSplit](#ImageChannelSplit)和[ImageChannelMerge](#ImageChannelMerge)节点。
|
||||
* 添加[MaskMotionBlur](#MaskMotionBlur)节点。
|
||||
@@ -137,6 +140,7 @@ ComfyUI
|
||||
* opacity: 不透明度。
|
||||
* stroke_grow: 描边扩张/收缩幅度,正值是扩张,负值是收缩。
|
||||
* stroke_width: 描边宽度。
|
||||
* blur: 描边模糊。
|
||||
* stroke_color<sup>4</sup>: 描边颜色。
|
||||
* [节点注解](#节点注解)
|
||||
|
||||
@@ -188,6 +192,14 @@ ComfyUI
|
||||
|
||||
<sup>*</sup>LUT文件夹在resource_dir.ini中定义,这个文件位于插件根目录下。用文本编辑软件打开,找到“LUT_dir=”开头的这一行,编辑“=”之后为自定义文件夹路径名。这个文件夹里面所有的.cube文件将在ComfyUI初始化时被收集并显示在节点的列表中。如果ini中设定的文件夹无效,将启用插件自带的LUT文件夹。
|
||||
|
||||
### <a id="table1">ColorAdapter</a>
|
||||
自动调整图片色调,使之与参考图片相似。
|
||||

|
||||
|
||||
节点选项说明:
|
||||

|
||||
* opacity: 图像调整色调之后的不透明度。
|
||||
|
||||
### <a id="table1">Gamma</a>
|
||||
改变图像的Gamma值。
|
||||
|
||||
@@ -310,6 +322,17 @@ MaskGrow
|
||||
* gradient_offset: 渐变位置偏移。
|
||||
* opacity: 渐变的不透明度。
|
||||
|
||||
### <a id="table1">MaskStroke</a>
|
||||
产生mask轮廓描边。
|
||||

|
||||
|
||||
节点选项说明:
|
||||

|
||||
* invert_mask: 是否反转遮罩。
|
||||
* stroke_grow: 描边扩张/收缩幅度,正值是扩张,负值是收缩。
|
||||
* stroke_width: 描边宽度。
|
||||
* blur: 描边模糊。
|
||||
|
||||
### <a id="table1">MaskPreview</a>
|
||||
预览mask
|
||||

|
||||
@@ -350,6 +373,40 @@ mask
|
||||
* anti_aliasing: 抗锯齿,范围从0-16,数值越大,锯齿越不明显。过高的数值将显著降低节点的处理速度。
|
||||
* [节点注解](#节点注解)
|
||||
|
||||
### <a id="table1">CropByMask</a>
|
||||
将图片按照mask范围裁切,可设置四周边框保留大小。这个节点与[RestoreCropBox](#RestoreCropBox)节点配合使用,可以对图片的局部进行裁切修改,然后贴回原处。
|
||||

|
||||
|
||||
节点选项说明:
|
||||

|
||||
* image<sup>5</sup>: 输入的图像。
|
||||
* mask_for_crop<sup>5</sup>: image的遮罩,将自动按照遮罩范围进行裁切。
|
||||
* invert_mask: 是否反转遮罩。
|
||||
* detect: 探测方法,min_bounding_rect是最小外接矩形, max_inscribed_rect是最大内接矩形。
|
||||
* top_reserve: 裁切顶端保留大小。
|
||||
* bottom_reserve: 裁切底部保留大小。
|
||||
* left_reserve: 裁切左侧保留大小。
|
||||
* right_reserve: 裁切右侧保留大小。
|
||||
* [节点注解](#节点注解)
|
||||
|
||||
输出:
|
||||
* croped_image: 裁切后的图片。
|
||||
* croped_mask: 裁切后的遮罩。
|
||||
* crop_box: 裁切box数据,在RestoreCropBox节点恢复时使用。
|
||||
* box_preview: 裁切位置预览图,红色是探测到的范围,绿色是加上保留边框后裁切的范围。
|
||||
|
||||
### <a id="table1">RestoreCropBox</a>
|
||||
将被[CropByMask](#CropByMask)裁切后的图片恢复到原图。
|
||||
|
||||
节点选项说明:
|
||||

|
||||
* background_image: 裁切前的原图。
|
||||
* croped_image<sup>5</sup>: 裁切后的图片。如果中间经过放大处理,恢复前需将尺寸还原。
|
||||
* croped_mask<sup>5</sup>: 裁切后的遮罩。
|
||||
* crop_box: 裁切时的box数据。
|
||||
* invert_mask: 是否反转遮罩。
|
||||
* [节点注解](#节点注解)
|
||||
|
||||
### <a id="table1">ImageBlend</a>
|
||||
一个用于合成图层的简单节点,提供多种混合模式供选择,可设置透明度。
|
||||

|
||||
@@ -520,22 +577,15 @@ mask
|
||||
|
||||
## 以下工作计划中
|
||||
|
||||
LayerUtility:
|
||||
* image channel split
|
||||
* image channel merge
|
||||
|
||||
LayerMask:
|
||||
* text mask
|
||||
* TextMask
|
||||
|
||||
LayerFilter:
|
||||
* emboss
|
||||
* contour
|
||||
* findedge
|
||||
|
||||
LayerColor:
|
||||
* RGB adjust
|
||||
* HSV adjust
|
||||
* brightness & contrast adjust
|
||||
* Emboss
|
||||
* Contour
|
||||
* Findedge
|
||||
* PhotoStyle
|
||||
* ColorMap
|
||||
|
||||
|
||||
## 安装方法
|
||||
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 1.9 MiB |
Binary file not shown.
|
After Width: | Height: | Size: 93 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 1.4 MiB |
Binary file not shown.
|
After Width: | Height: | Size: 180 KiB |
@@ -0,0 +1,39 @@
|
||||
from .imagefunc import *
|
||||
|
||||
class ColorAdapter:
|
||||
|
||||
def __init__(self):
|
||||
pass
|
||||
|
||||
@classmethod
|
||||
def INPUT_TYPES(self):
|
||||
|
||||
return {
|
||||
"required": {
|
||||
"image": ("IMAGE", ), #
|
||||
"color_ref_image": ("IMAGE", ), #
|
||||
"opacity": ("INT", {"default": 75, "min": 0, "max": 100, "step": 1}), # 透明度
|
||||
},
|
||||
"optional": {
|
||||
}
|
||||
}
|
||||
|
||||
RETURN_TYPES = ("IMAGE",)
|
||||
RETURN_NAMES = ("image",)
|
||||
FUNCTION = 'color_adapter'
|
||||
CATEGORY = '😺dzNodes/LayerColor'
|
||||
OUTPUT_NODE = True
|
||||
|
||||
def color_adapter(self, image, color_ref_image, opacity):
|
||||
_canvas = tensor2pil(image).convert('RGB')
|
||||
ret_image = color_adapter(_canvas, tensor2pil(color_ref_image).convert('RGB'))
|
||||
ret_image = chop_image(_canvas, ret_image, blend_mode='normal', opacity=opacity)
|
||||
return (pil2tensor(ret_image),)
|
||||
|
||||
NODE_CLASS_MAPPINGS = {
|
||||
"LayerColor: ColorAdapter": ColorAdapter
|
||||
}
|
||||
|
||||
NODE_DISPLAY_NAME_MAPPINGS = {
|
||||
"LayerColor: ColorAdapter": "LayerColor: ColorAdapter"
|
||||
}
|
||||
@@ -0,0 +1,70 @@
|
||||
from .imagefunc import *
|
||||
|
||||
class CropByMask:
|
||||
|
||||
def __init__(self):
|
||||
pass
|
||||
|
||||
@classmethod
|
||||
def INPUT_TYPES(self):
|
||||
detect_mode = ['min_bounding_rect', 'max_inscribed_rect']
|
||||
return {
|
||||
"required": {
|
||||
"image": ("IMAGE", ), #
|
||||
"mask_for_crop": ("MASK",),
|
||||
"invert_mask": ("BOOLEAN", {"default": False}), # 反转mask#
|
||||
"detect": (detect_mode,),
|
||||
"top_reserve": ("INT", {"default": 20, "min": -9999, "max": 9999, "step": 1}),
|
||||
"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}),
|
||||
},
|
||||
"optional": {
|
||||
}
|
||||
}
|
||||
|
||||
RETURN_TYPES = ("IMAGE", "MASK", "BOX", "IMAGE",)
|
||||
RETURN_NAMES = ("croped_image", "croped_mask", "crop_box", "box_preview")
|
||||
FUNCTION = 'crop_by_mask'
|
||||
CATEGORY = '😺dzNodes/LayerUtility'
|
||||
OUTPUT_NODE = True
|
||||
|
||||
def crop_by_mask(self, image, mask_for_crop, invert_mask, detect,
|
||||
top_reserve, bottom_reserve, left_reserve, right_reserve
|
||||
):
|
||||
|
||||
_canvas = tensor2pil(image).convert('RGB')
|
||||
if invert_mask:
|
||||
mask_for_crop = 1 - mask_for_crop
|
||||
_mask = mask2image(mask_for_crop)
|
||||
bluredmask = gaussian_blur(_mask, 20).convert('L')
|
||||
x = 0
|
||||
y = 0
|
||||
width = 0
|
||||
height = 0
|
||||
if detect == "min_bounding_rect":
|
||||
(x, y, width, height) = min_bounding_rect(bluredmask)
|
||||
if detect == "max_inscribed_rect":
|
||||
(x, y, width, height) = max_inscribed_rect(bluredmask)
|
||||
|
||||
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
|
||||
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)
|
||||
|
||||
return (pil2tensor(ret_image), image2mask(ret_mask), list(crop_box), pil2tensor(preview_image),)
|
||||
|
||||
NODE_CLASS_MAPPINGS = {
|
||||
"LayerUtility: CropByMask": CropByMask
|
||||
}
|
||||
|
||||
NODE_DISPLAY_NAME_MAPPINGS = {
|
||||
"LayerUtility: CropByMask": "LayerUtility: CropByMask"
|
||||
}
|
||||
+20
-1
@@ -334,7 +334,6 @@ def image_hue_offset(image:Image, offset:int) -> Image:
|
||||
ret_image.putpixel((x, y), _pixel)
|
||||
return ret_image
|
||||
|
||||
|
||||
def gamma_trans(image:Image, gamma:float) -> Image:
|
||||
cv2_image = pil2cv2(image)
|
||||
gamma_table = [np.power(x/255.0,gamma)*255.0 for x in range(256)]
|
||||
@@ -370,6 +369,26 @@ def lut_apply(image:Image, lut_file:str) -> Image:
|
||||
_image.putpixel((x, y), new_pixel)
|
||||
return _image
|
||||
|
||||
|
||||
def color_adapter(image:Image, ref_image:Image) -> Image:
|
||||
image = pil2cv2(image)
|
||||
ref_image = pil2cv2(ref_image)
|
||||
image = cv2.cvtColor(image, cv2.COLOR_BGR2LAB)
|
||||
image_mean, image_std = calculate_mean_std(image)
|
||||
ref_image = cv2.cvtColor(ref_image, cv2.COLOR_BGR2LAB)
|
||||
ref_image_mean, ref_image_std = calculate_mean_std(ref_image)
|
||||
_image = ((image - image_mean) * (ref_image_std / image_std)) + ref_image_mean
|
||||
np.putmask(_image, _image > 255, values=255)
|
||||
np.putmask(_image, _image < 0, values=0)
|
||||
ret_image = cv2.cvtColor(cv2.convertScaleAbs(_image), cv2.COLOR_LAB2BGR)
|
||||
return cv22pil(ret_image)
|
||||
|
||||
def calculate_mean_std(image:Image):
|
||||
mean, std = cv2.meanStdDev(image)
|
||||
mean = np.hstack(np.around(mean, decimals=2))
|
||||
std = np.hstack(np.around(std, decimals=2))
|
||||
return mean, std
|
||||
|
||||
'''Mask Functions'''
|
||||
|
||||
def expand_mask(mask:torch.Tensor, grow:int, blur:int) -> torch.Tensor:
|
||||
|
||||
@@ -0,0 +1,49 @@
|
||||
from .imagefunc import *
|
||||
|
||||
class MaskStrkoe:
|
||||
|
||||
def __init__(self):
|
||||
pass
|
||||
|
||||
@classmethod
|
||||
def INPUT_TYPES(self):
|
||||
|
||||
return {
|
||||
"required": {
|
||||
"mask": ("MASK", ), #
|
||||
"invert_mask": ("BOOLEAN", {"default": True}), # 反转mask
|
||||
"stroke_grow": ("INT", {"default": 0, "min": -999, "max": 999, "step": 1}), # 收缩值
|
||||
"stroke_width": ("INT", {"default": 20, "min": 0, "max": 999, "step": 1}), # 扩张值
|
||||
"blur": ("INT", {"default": 6, "min": 0, "max": 100, "step": 1}), # 模糊
|
||||
},
|
||||
"optional": {
|
||||
}
|
||||
}
|
||||
|
||||
RETURN_TYPES = ("MASK",)
|
||||
RETURN_NAMES = ("mask",)
|
||||
FUNCTION = 'mask_stroke'
|
||||
CATEGORY = '😺dzNodes/LayerMask'
|
||||
OUTPUT_NODE = True
|
||||
|
||||
def mask_stroke(self, mask, invert_mask, stroke_grow, stroke_width, blur,):
|
||||
|
||||
if invert_mask:
|
||||
mask = 1 - mask
|
||||
_mask = mask2image(mask).convert('L')
|
||||
grow_offset = int(stroke_width / 2)
|
||||
inner_stroke = stroke_grow - grow_offset
|
||||
outer_stroke = inner_stroke + stroke_width
|
||||
inner_mask = expand_mask(image2mask(_mask), inner_stroke, blur)
|
||||
outer_mask = expand_mask(image2mask(_mask), outer_stroke, blur)
|
||||
stroke_mask = subtract_mask(outer_mask, inner_mask)
|
||||
|
||||
return (stroke_mask,)
|
||||
|
||||
NODE_CLASS_MAPPINGS = {
|
||||
"LayerMask: MaskStrkoe": MaskStrkoe
|
||||
}
|
||||
|
||||
NODE_DISPLAY_NAME_MAPPINGS = {
|
||||
"LayerMask: MaskStrkoe": "LayerMask: MaskStrkoe"
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
from .imagefunc import *
|
||||
|
||||
class RestoreCropBox:
|
||||
|
||||
def __init__(self):
|
||||
pass
|
||||
|
||||
@classmethod
|
||||
def INPUT_TYPES(self):
|
||||
|
||||
return {
|
||||
"required": {
|
||||
"background_image": ("IMAGE", ),
|
||||
"croped_image": ("IMAGE",),
|
||||
"invert_mask": ("BOOLEAN", {"default": False}), # 反转mask#
|
||||
"crop_box": ("BOX",),
|
||||
},
|
||||
"optional": {
|
||||
"croped_mask": ("MASK",),
|
||||
}
|
||||
}
|
||||
|
||||
RETURN_TYPES = ("IMAGE", "MASK", )
|
||||
RETURN_NAMES = ("image", "mask", )
|
||||
FUNCTION = 'restore_crop_box'
|
||||
CATEGORY = '😺dzNodes/LayerUtility'
|
||||
OUTPUT_NODE = True
|
||||
|
||||
def restore_crop_box(self, background_image, croped_image, invert_mask, crop_box,
|
||||
croped_mask=None
|
||||
):
|
||||
|
||||
_canvas = tensor2pil(background_image).convert('RGB')
|
||||
_layer = tensor2pil(croped_image).convert('RGB')
|
||||
_mask = Image.new('L', size=_layer.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))
|
||||
|
||||
return (pil2tensor(_canvas), image2mask(ret_mask),)
|
||||
|
||||
NODE_CLASS_MAPPINGS = {
|
||||
"LayerUtility: RestoreCropBox": RestoreCropBox
|
||||
}
|
||||
|
||||
NODE_DISPLAY_NAME_MAPPINGS = {
|
||||
"LayerUtility: RestoreCropBox": "LayerUtility: RestoreCropBox"
|
||||
}
|
||||
+2
-2
@@ -58,11 +58,11 @@ class Stroke:
|
||||
outer_stroke = inner_stroke + stroke_width
|
||||
inner_mask = expand_mask(image2mask(_mask), inner_stroke, blur)
|
||||
outer_mask = expand_mask(image2mask(_mask), outer_stroke, blur)
|
||||
strock_mask = subtract_mask(outer_mask, inner_mask)
|
||||
stroke_mask = subtract_mask(outer_mask, inner_mask)
|
||||
color_image = Image.new('RGB', size=_layer.size, color=stroke_color)
|
||||
blend_image = chop_image(_layer, color_image, blend_mode, opacity)
|
||||
_canvas.paste(_layer, mask=_mask)
|
||||
_canvas.paste(blend_image, mask=tensor2pil(strock_mask))
|
||||
_canvas.paste(blend_image, mask=tensor2pil(stroke_mask))
|
||||
ret_image = _canvas
|
||||
log('Stroke Processed.')
|
||||
return (pil2tensor(ret_image),)
|
||||
|
||||
@@ -0,0 +1,472 @@
|
||||
{
|
||||
"last_node_id": 34,
|
||||
"last_link_id": 75,
|
||||
"nodes": [
|
||||
{
|
||||
"id": 27,
|
||||
"type": "PreviewImage",
|
||||
"pos": [
|
||||
2360,
|
||||
430
|
||||
],
|
||||
"size": [
|
||||
516.973361206055,
|
||||
302.7666168212892
|
||||
],
|
||||
"flags": {},
|
||||
"order": 7,
|
||||
"mode": 0,
|
||||
"inputs": [
|
||||
{
|
||||
"name": "images",
|
||||
"type": "IMAGE",
|
||||
"link": 56
|
||||
}
|
||||
],
|
||||
"properties": {
|
||||
"Node name for S&R": "PreviewImage"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": 28,
|
||||
"type": "LayerMask: MaskPreview",
|
||||
"pos": [
|
||||
2360,
|
||||
780
|
||||
],
|
||||
"size": [
|
||||
511.933361206055,
|
||||
312.2199255371095
|
||||
],
|
||||
"flags": {},
|
||||
"order": 8,
|
||||
"mode": 0,
|
||||
"inputs": [
|
||||
{
|
||||
"name": "mask",
|
||||
"type": "MASK",
|
||||
"link": 57
|
||||
}
|
||||
],
|
||||
"properties": {
|
||||
"Node name for S&R": "LayerMask: MaskPreview"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": 4,
|
||||
"type": "PreviewImage",
|
||||
"pos": [
|
||||
1500,
|
||||
250
|
||||
],
|
||||
"size": [
|
||||
385.7333923339845,
|
||||
246
|
||||
],
|
||||
"flags": {},
|
||||
"order": 3,
|
||||
"mode": 0,
|
||||
"inputs": [
|
||||
{
|
||||
"name": "images",
|
||||
"type": "IMAGE",
|
||||
"link": 64
|
||||
}
|
||||
],
|
||||
"properties": {
|
||||
"Node name for S&R": "PreviewImage"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": 22,
|
||||
"type": "LayerMask: MaskPreview",
|
||||
"pos": [
|
||||
1920,
|
||||
250
|
||||
],
|
||||
"size": [
|
||||
380.6532714843752,
|
||||
246
|
||||
],
|
||||
"flags": {},
|
||||
"order": 4,
|
||||
"mode": 0,
|
||||
"inputs": [
|
||||
{
|
||||
"name": "mask",
|
||||
"type": "MASK",
|
||||
"link": 65
|
||||
}
|
||||
],
|
||||
"properties": {
|
||||
"Node name for S&R": "LayerMask: MaskPreview"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": 16,
|
||||
"type": "Image Remove Background Rembg (mtb)",
|
||||
"pos": [
|
||||
780,
|
||||
670
|
||||
],
|
||||
"size": {
|
||||
"0": 294,
|
||||
"1": 230
|
||||
},
|
||||
"flags": {},
|
||||
"order": 1,
|
||||
"mode": 0,
|
||||
"inputs": [
|
||||
{
|
||||
"name": "image",
|
||||
"type": "IMAGE",
|
||||
"link": 22
|
||||
}
|
||||
],
|
||||
"outputs": [
|
||||
{
|
||||
"name": "Image (rgba)",
|
||||
"type": "IMAGE",
|
||||
"links": null,
|
||||
"shape": 3
|
||||
},
|
||||
{
|
||||
"name": "Mask",
|
||||
"type": "MASK",
|
||||
"links": [
|
||||
59
|
||||
],
|
||||
"shape": 3,
|
||||
"slot_index": 1
|
||||
},
|
||||
{
|
||||
"name": "Image",
|
||||
"type": "IMAGE",
|
||||
"links": [],
|
||||
"shape": 3,
|
||||
"slot_index": 2
|
||||
}
|
||||
],
|
||||
"properties": {
|
||||
"Node name for S&R": "Image Remove Background Rembg (mtb)"
|
||||
},
|
||||
"widgets_values": [
|
||||
false,
|
||||
240,
|
||||
10,
|
||||
10,
|
||||
false,
|
||||
"#000000"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": 18,
|
||||
"type": "PreviewImage",
|
||||
"pos": [
|
||||
1490,
|
||||
810
|
||||
],
|
||||
"size": [
|
||||
369.95327148437514,
|
||||
246
|
||||
],
|
||||
"flags": {},
|
||||
"order": 6,
|
||||
"mode": 0,
|
||||
"inputs": [
|
||||
{
|
||||
"name": "images",
|
||||
"type": "IMAGE",
|
||||
"link": 63
|
||||
}
|
||||
],
|
||||
"properties": {
|
||||
"Node name for S&R": "PreviewImage"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": 29,
|
||||
"type": "LayerUtility: CropByMask",
|
||||
"pos": [
|
||||
1130,
|
||||
570
|
||||
],
|
||||
"size": {
|
||||
"0": 330,
|
||||
"1": 238
|
||||
},
|
||||
"flags": {},
|
||||
"order": 2,
|
||||
"mode": 0,
|
||||
"inputs": [
|
||||
{
|
||||
"name": "image",
|
||||
"type": "IMAGE",
|
||||
"link": 58
|
||||
},
|
||||
{
|
||||
"name": "mask_for_crop",
|
||||
"type": "MASK",
|
||||
"link": 59
|
||||
}
|
||||
],
|
||||
"outputs": [
|
||||
{
|
||||
"name": "croped_image",
|
||||
"type": "IMAGE",
|
||||
"links": [
|
||||
64,
|
||||
75
|
||||
],
|
||||
"shape": 3,
|
||||
"slot_index": 0
|
||||
},
|
||||
{
|
||||
"name": "croped_mask",
|
||||
"type": "MASK",
|
||||
"links": [
|
||||
61,
|
||||
65
|
||||
],
|
||||
"shape": 3,
|
||||
"slot_index": 1
|
||||
},
|
||||
{
|
||||
"name": "crop_box",
|
||||
"type": "BOX",
|
||||
"links": [
|
||||
62
|
||||
],
|
||||
"shape": 3,
|
||||
"slot_index": 2
|
||||
},
|
||||
{
|
||||
"name": "box_preview",
|
||||
"type": "IMAGE",
|
||||
"links": [
|
||||
63
|
||||
],
|
||||
"shape": 3,
|
||||
"slot_index": 3
|
||||
}
|
||||
],
|
||||
"properties": {
|
||||
"Node name for S&R": "LayerUtility: CropByMask"
|
||||
},
|
||||
"widgets_values": [
|
||||
false,
|
||||
"min_bounding_rect",
|
||||
20,
|
||||
20,
|
||||
20,
|
||||
20
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": 26,
|
||||
"type": "LayerUtility: RestoreCropBox",
|
||||
"pos": [
|
||||
1950,
|
||||
560
|
||||
],
|
||||
"size": {
|
||||
"0": 315,
|
||||
"1": 118
|
||||
},
|
||||
"flags": {},
|
||||
"order": 5,
|
||||
"mode": 0,
|
||||
"inputs": [
|
||||
{
|
||||
"name": "background_image",
|
||||
"type": "IMAGE",
|
||||
"link": 67
|
||||
},
|
||||
{
|
||||
"name": "croped_image",
|
||||
"type": "IMAGE",
|
||||
"link": 75
|
||||
},
|
||||
{
|
||||
"name": "croped_mask",
|
||||
"type": "MASK",
|
||||
"link": 61
|
||||
},
|
||||
{
|
||||
"name": "crop_box",
|
||||
"type": "BOX",
|
||||
"link": 62
|
||||
}
|
||||
],
|
||||
"outputs": [
|
||||
{
|
||||
"name": "image",
|
||||
"type": "IMAGE",
|
||||
"links": [
|
||||
56
|
||||
],
|
||||
"shape": 3,
|
||||
"slot_index": 0
|
||||
},
|
||||
{
|
||||
"name": "mask",
|
||||
"type": "MASK",
|
||||
"links": [
|
||||
57
|
||||
],
|
||||
"shape": 3,
|
||||
"slot_index": 1
|
||||
}
|
||||
],
|
||||
"properties": {
|
||||
"Node name for S&R": "LayerUtility: RestoreCropBox"
|
||||
},
|
||||
"widgets_values": [
|
||||
false
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": 2,
|
||||
"type": "LoadImage",
|
||||
"pos": [
|
||||
410,
|
||||
400
|
||||
],
|
||||
"size": {
|
||||
"0": 315,
|
||||
"1": 314
|
||||
},
|
||||
"flags": {},
|
||||
"order": 0,
|
||||
"mode": 0,
|
||||
"outputs": [
|
||||
{
|
||||
"name": "IMAGE",
|
||||
"type": "IMAGE",
|
||||
"links": [
|
||||
22,
|
||||
58,
|
||||
67
|
||||
],
|
||||
"shape": 3,
|
||||
"slot_index": 0
|
||||
},
|
||||
{
|
||||
"name": "MASK",
|
||||
"type": "MASK",
|
||||
"links": null,
|
||||
"shape": 3
|
||||
}
|
||||
],
|
||||
"properties": {
|
||||
"Node name for S&R": "LoadImage"
|
||||
},
|
||||
"widgets_values": [
|
||||
"3840x2160car (13).jpg",
|
||||
"image"
|
||||
]
|
||||
}
|
||||
],
|
||||
"links": [
|
||||
[
|
||||
22,
|
||||
2,
|
||||
0,
|
||||
16,
|
||||
0,
|
||||
"IMAGE"
|
||||
],
|
||||
[
|
||||
56,
|
||||
26,
|
||||
0,
|
||||
27,
|
||||
0,
|
||||
"IMAGE"
|
||||
],
|
||||
[
|
||||
57,
|
||||
26,
|
||||
1,
|
||||
28,
|
||||
0,
|
||||
"MASK"
|
||||
],
|
||||
[
|
||||
58,
|
||||
2,
|
||||
0,
|
||||
29,
|
||||
0,
|
||||
"IMAGE"
|
||||
],
|
||||
[
|
||||
59,
|
||||
16,
|
||||
1,
|
||||
29,
|
||||
1,
|
||||
"MASK"
|
||||
],
|
||||
[
|
||||
61,
|
||||
29,
|
||||
1,
|
||||
26,
|
||||
2,
|
||||
"MASK"
|
||||
],
|
||||
[
|
||||
62,
|
||||
29,
|
||||
2,
|
||||
26,
|
||||
3,
|
||||
"BOX"
|
||||
],
|
||||
[
|
||||
63,
|
||||
29,
|
||||
3,
|
||||
18,
|
||||
0,
|
||||
"IMAGE"
|
||||
],
|
||||
[
|
||||
64,
|
||||
29,
|
||||
0,
|
||||
4,
|
||||
0,
|
||||
"IMAGE"
|
||||
],
|
||||
[
|
||||
65,
|
||||
29,
|
||||
1,
|
||||
22,
|
||||
0,
|
||||
"MASK"
|
||||
],
|
||||
[
|
||||
67,
|
||||
2,
|
||||
0,
|
||||
26,
|
||||
0,
|
||||
"IMAGE"
|
||||
],
|
||||
[
|
||||
75,
|
||||
29,
|
||||
0,
|
||||
26,
|
||||
1,
|
||||
"IMAGE"
|
||||
]
|
||||
],
|
||||
"groups": [],
|
||||
"config": {},
|
||||
"extra": {},
|
||||
"version": 0.4
|
||||
}
|
||||
Reference in New Issue
Block a user