diff --git a/README.MD b/README.MD
index 714f05a..8d59408 100644
--- a/README.MD
+++ b/README.MD
@@ -13,6 +13,7 @@ Nodes are divided into 5 groups according to their functions: LayerStyle, LayerC
[䏿–‡è¯´æ˜Žç‚¹è¿™é‡Œ](./README_CN.MD)
## Update
+* 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**.
The newly added blend mode is applicable to all nodes that support blend mode.
@@ -561,6 +562,29 @@ Output:
* x: The x-coordinate of the top left corner position.
* y: The y-coordinate of the top left corner position.
+### RemBgUltra
+Remove background. compared to the previous RemBg, the cutout of this node has ultra-high edge details.
+This node combines the Alpha Matte node of Spacepxl's [ComfyUI-Image-Filters](https://github.com/spacepxl/ComfyUI-Image-Filters) and the functionality of ZO-ZHO-ZHO's [ComfyUI-BRIA_AI-RMBG](https://github.com/ZHO-ZHO-ZHO/ComfyUI-BRIA_AI-RMBG).
+
+*Download the [BRIA Background Removal v1.4](https://huggingface.co/briaai/RMBG-1.4) model file (model. pth) to the customer_modes/ComfyUI-LayerStyle/RMBG-1.4 floder.
+This model was developed by BRIA AI and can be used as an open-source model for non-commercial purposes.
+
+
+Node options:
+
+* detail_range: Edge detail range.
+* black_point: Edge black sampling threshold.
+* white_point: Edge white sampling threshold.
+
+### PixelSpread
+Pixel expansion preprocessing on the masked edge of an image can effectively improve the edges of image synthesis.
+
+
+Node options:
+
+* invert_mask: Whether to reverse the mask.
+
+
### MaskGrow
Grow and shrink edges and blur the mask
@@ -724,7 +748,9 @@ image Some JSON workflow files in the workflow directory, that is example for Co
## How to install
-* Open the cmd window in the plugin directory of ComfyUI, like "ComfyUI\custom_nodes\",type```git clone https://github.com/chflame163/ComfyUI_LayerStyle.git```
+* Recommended use ComfyUI Manager for installation.
+
+* Or open the cmd window in the plugin directory of ComfyUI, like "ComfyUI\custom_nodes\",type```git clone https://github.com/chflame163/ComfyUI_LayerStyle.git```
or download the zip file and extracted, copy the resulting folder to ComfyUI\custom_ Nodes\
* Install dependency packages, open the cmd window in the WordCloud plugin directory like "ComfyUI\custom_ Nodes\ComfyUI_WordCloud" and enter the following command:
diff --git a/README_CN.MD b/README_CN.MD
index 266736b..9b343ed 100644
--- a/README_CN.MD
+++ b/README_CN.MD
@@ -9,7 +9,8 @@
* [LayerUtility](#LayerUtility)½Úµã×éÌṩͼ²ãºÏ³É¹¤¾ßºÍ¹¤×÷Á÷Ïà¹ØµÄ¸¨Öú½Úµã¡£
* [LayerFilter](#LayerFilter)½Úµã×éÌṩͼÏñЧ¹ûÂ˾µ¡£
-## ¸üÐÂ˵Ã÷
+## ¸üÐÂ˵Ã÷¡¢
+* Ìí¼Ó[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ʵɫ»ìºÏ¡£ÐÂÔöµÄ»ìºÏģʽÊÊÓÃÓÚËùÓÐÖ§³Ö»ìºÏģʽµÄ½Úµã¡£
* Ìí¼Ó[ColorMap](#ColorMap) Â˾µ½Úµã£¬ÓÃÓÚÖÆ×÷α²ÊÉ«ÈÈÁ¦Í¼Ð§¹û¡£
@@ -547,6 +548,29 @@
* x: ×óÉϽÇλÖÃx×ø±êÊä³ö¡£
* y: ×óÉϽÇλÖÃy×ø±êÊä³ö¡£
+### RemBgUltra
+È¥³ý±³¾°¡£Óë֮ǰµÄRemBgÏà±È£¬Õâ¸ö½ÚµãµÄ¿ÙÏñ¾ßÓг¬¸ßµÄ±ßԵϸ½Ú¡£
+±¾½Úµã½áºÏÁËspacepxlµÄ[ComfyUI-Image-Filters](https://github.com/spacepxl/ComfyUI-Image-Filters)µÄAlpha Matte½Úµã£¬ÒÔ¼°ZHO-ZHO-ZHOµÄ[ComfyUI-BRIA_AI-RMBG](https://github.com/ZHO-ZHO-ZHO/ComfyUI-BRIA_AI-RMBG)µÄ¹¦ÄÜ¡£
+*½«[BRIA Background Removal v1.4](https://huggingface.co/briaai/RMBG-1.4)Ä£ÐÍÎļþ(model.pth)ÏÂÔØÖÁ/custom_nodes/ComfyUI_LayerStyle/RMBG-1.4¡£
+¸ÃÄ£ÐÍÓÉ BRIA AI ¿ª·¢£¬¿É×÷Ϊ·ÇÉÌÒµÓÃ;µÄ¿ªÔ´Ä£ÐÍ¡£
+
+
+
+½ÚµãÑ¡Ïî˵Ã÷:
+
+* detail_range: ±ßԵϸ½Ú·¶Î§¡£
+* black_point: ±ßÔµºÚÉ«²ÉÑùãÐÖµ¡£
+* white_point: ±ßÔµºÚÉ«²ÉÑùãÐÖµ¡£
+
+### PixelSpread
+¶ÔͼÏñµÄÕÚÕÖ±ßÔµ²¿·Ö½øÐÐÏñËØÀ©ÕÅÔ¤´¦Àí£¬¿ÉÓÐЧ¸ÄÉÆÍ¼ÏñºÏ³ÉµÄ±ßÔµ¡£
+
+
+½ÚµãÑ¡Ïî˵Ã÷:
+
+* invert_mask: ÊÇ·ñ·´×ªÕÚÕÖ¡£
+
+
### MaskGrow
¶Ômask½øÐÐÀ©ÕÅÊÕËõ±ßÔµºÍÄ£ºý´¦Àí

@@ -700,7 +724,8 @@ mask
## °²×°·½·¨
-* ÔÚCompyUI²å¼þĿ¼(ÀýÈç¡°CompyUI\custom_nodes\¡±)Öдò¿ªcmd´°¿Ú£¬¼üÈë```git clone https://github.com/chflame163/ComfyUI_LayerStyle.git```°²×°¡£»òÕßÏÂÔØ½âѹzipÎļþ£¬½«µÃµ½µÄÎļþ¼Ð¸´ÖƵ½ ComfyUI\custom_nodes\
+* ÍÆ¼öʹÓà ComfyUI Manager °²×°¡£
+* »òÕßÔÚCompyUI²å¼þĿ¼(ÀýÈç¡°CompyUI\custom_nodes\¡±)Öдò¿ªcmd´°¿Ú£¬¼üÈë```git clone https://github.com/chflame163/ComfyUI_LayerStyle.git```°²×°¡£»òÕßÏÂÔØ½âѹzipÎļþ£¬½«µÃµ½µÄÎļþ¼Ð¸´ÖƵ½ ComfyUI\custom_nodes\
* °²×°ÒÀÀµ°ü£¬ÔÚ×ÊÔ´¹ÜÀíÆ÷ComfyUI\custom_nodes\ComfyUI_WordCloud ²å¼þĿ¼λÖôò¿ªcmd´°¿Ú£¬ÊäÈëÒÔÏÂÃüÁ
```..\..\..\python_embeded\python.exe -m pip install -r requirements.txt```
* ÖØÐ´ò¿ªComfyUI¡£
diff --git a/RMBG-1.4/put model here.txt b/RMBG-1.4/put model here.txt
new file mode 100644
index 0000000..8b13789
--- /dev/null
+++ b/RMBG-1.4/put model here.txt
@@ -0,0 +1 @@
+
diff --git a/image/layermask_nodes.png b/image/layermask_nodes.png
index e0e65db..713fe18 100644
Binary files a/image/layermask_nodes.png and b/image/layermask_nodes.png differ
diff --git a/image/pixel_spread_example.png b/image/pixel_spread_example.png
new file mode 100644
index 0000000..e026134
Binary files /dev/null and b/image/pixel_spread_example.png differ
diff --git a/image/pixel_spread_node.png b/image/pixel_spread_node.png
new file mode 100644
index 0000000..176c060
Binary files /dev/null and b/image/pixel_spread_node.png differ
diff --git a/image/rembg_ultra_example.png b/image/rembg_ultra_example.png
new file mode 100644
index 0000000..0189831
Binary files /dev/null and b/image/rembg_ultra_example.png differ
diff --git a/image/rembg_ultra_node.png b/image/rembg_ultra_node.png
new file mode 100644
index 0000000..df83016
Binary files /dev/null and b/image/rembg_ultra_node.png differ
diff --git a/py/briarmbg.py b/py/briarmbg.py
new file mode 100644
index 0000000..647bdc0
--- /dev/null
+++ b/py/briarmbg.py
@@ -0,0 +1,455 @@
+import torch
+import torch.nn as nn
+import torch.nn.functional as F
+
+class REBNCONV(nn.Module):
+ def __init__(self,in_ch=3,out_ch=3,dirate=1,stride=1):
+ super(REBNCONV,self).__init__()
+
+ self.conv_s1 = nn.Conv2d(in_ch,out_ch,3,padding=1*dirate,dilation=1*dirate,stride=stride)
+ self.bn_s1 = nn.BatchNorm2d(out_ch)
+ self.relu_s1 = nn.ReLU(inplace=True)
+
+ def forward(self,x):
+
+ hx = x
+ xout = self.relu_s1(self.bn_s1(self.conv_s1(hx)))
+
+ return xout
+
+## upsample tensor 'src' to have the same spatial size with tensor 'tar'
+def _upsample_like(src,tar):
+
+ src = F.interpolate(src,size=tar.shape[2:],mode='bilinear')
+
+ return src
+
+
+### RSU-7 ###
+class RSU7(nn.Module):
+
+ def __init__(self, in_ch=3, mid_ch=12, out_ch=3, img_size=512):
+ super(RSU7,self).__init__()
+
+ self.in_ch = in_ch
+ self.mid_ch = mid_ch
+ self.out_ch = out_ch
+
+ self.rebnconvin = REBNCONV(in_ch,out_ch,dirate=1) ## 1 -> 1/2
+
+ self.rebnconv1 = REBNCONV(out_ch,mid_ch,dirate=1)
+ self.pool1 = nn.MaxPool2d(2,stride=2,ceil_mode=True)
+
+ self.rebnconv2 = REBNCONV(mid_ch,mid_ch,dirate=1)
+ self.pool2 = nn.MaxPool2d(2,stride=2,ceil_mode=True)
+
+ self.rebnconv3 = REBNCONV(mid_ch,mid_ch,dirate=1)
+ self.pool3 = nn.MaxPool2d(2,stride=2,ceil_mode=True)
+
+ self.rebnconv4 = REBNCONV(mid_ch,mid_ch,dirate=1)
+ self.pool4 = nn.MaxPool2d(2,stride=2,ceil_mode=True)
+
+ self.rebnconv5 = REBNCONV(mid_ch,mid_ch,dirate=1)
+ self.pool5 = nn.MaxPool2d(2,stride=2,ceil_mode=True)
+
+ self.rebnconv6 = REBNCONV(mid_ch,mid_ch,dirate=1)
+
+ self.rebnconv7 = REBNCONV(mid_ch,mid_ch,dirate=2)
+
+ self.rebnconv6d = REBNCONV(mid_ch*2,mid_ch,dirate=1)
+ self.rebnconv5d = REBNCONV(mid_ch*2,mid_ch,dirate=1)
+ self.rebnconv4d = REBNCONV(mid_ch*2,mid_ch,dirate=1)
+ self.rebnconv3d = REBNCONV(mid_ch*2,mid_ch,dirate=1)
+ self.rebnconv2d = REBNCONV(mid_ch*2,mid_ch,dirate=1)
+ self.rebnconv1d = REBNCONV(mid_ch*2,out_ch,dirate=1)
+
+ def forward(self,x):
+ b, c, h, w = x.shape
+
+ hx = x
+ hxin = self.rebnconvin(hx)
+
+ hx1 = self.rebnconv1(hxin)
+ hx = self.pool1(hx1)
+
+ hx2 = self.rebnconv2(hx)
+ hx = self.pool2(hx2)
+
+ hx3 = self.rebnconv3(hx)
+ hx = self.pool3(hx3)
+
+ hx4 = self.rebnconv4(hx)
+ hx = self.pool4(hx4)
+
+ hx5 = self.rebnconv5(hx)
+ hx = self.pool5(hx5)
+
+ hx6 = self.rebnconv6(hx)
+
+ hx7 = self.rebnconv7(hx6)
+
+ hx6d = self.rebnconv6d(torch.cat((hx7,hx6),1))
+ hx6dup = _upsample_like(hx6d,hx5)
+
+ hx5d = self.rebnconv5d(torch.cat((hx6dup,hx5),1))
+ hx5dup = _upsample_like(hx5d,hx4)
+
+ hx4d = self.rebnconv4d(torch.cat((hx5dup,hx4),1))
+ hx4dup = _upsample_like(hx4d,hx3)
+
+ hx3d = self.rebnconv3d(torch.cat((hx4dup,hx3),1))
+ hx3dup = _upsample_like(hx3d,hx2)
+
+ hx2d = self.rebnconv2d(torch.cat((hx3dup,hx2),1))
+ hx2dup = _upsample_like(hx2d,hx1)
+
+ hx1d = self.rebnconv1d(torch.cat((hx2dup,hx1),1))
+
+ return hx1d + hxin
+
+
+### RSU-6 ###
+class RSU6(nn.Module):
+
+ def __init__(self, in_ch=3, mid_ch=12, out_ch=3):
+ super(RSU6,self).__init__()
+
+ self.rebnconvin = REBNCONV(in_ch,out_ch,dirate=1)
+
+ self.rebnconv1 = REBNCONV(out_ch,mid_ch,dirate=1)
+ self.pool1 = nn.MaxPool2d(2,stride=2,ceil_mode=True)
+
+ self.rebnconv2 = REBNCONV(mid_ch,mid_ch,dirate=1)
+ self.pool2 = nn.MaxPool2d(2,stride=2,ceil_mode=True)
+
+ self.rebnconv3 = REBNCONV(mid_ch,mid_ch,dirate=1)
+ self.pool3 = nn.MaxPool2d(2,stride=2,ceil_mode=True)
+
+ self.rebnconv4 = REBNCONV(mid_ch,mid_ch,dirate=1)
+ self.pool4 = nn.MaxPool2d(2,stride=2,ceil_mode=True)
+
+ self.rebnconv5 = REBNCONV(mid_ch,mid_ch,dirate=1)
+
+ self.rebnconv6 = REBNCONV(mid_ch,mid_ch,dirate=2)
+
+ self.rebnconv5d = REBNCONV(mid_ch*2,mid_ch,dirate=1)
+ self.rebnconv4d = REBNCONV(mid_ch*2,mid_ch,dirate=1)
+ self.rebnconv3d = REBNCONV(mid_ch*2,mid_ch,dirate=1)
+ self.rebnconv2d = REBNCONV(mid_ch*2,mid_ch,dirate=1)
+ self.rebnconv1d = REBNCONV(mid_ch*2,out_ch,dirate=1)
+
+ def forward(self,x):
+
+ hx = x
+
+ hxin = self.rebnconvin(hx)
+
+ hx1 = self.rebnconv1(hxin)
+ hx = self.pool1(hx1)
+
+ hx2 = self.rebnconv2(hx)
+ hx = self.pool2(hx2)
+
+ hx3 = self.rebnconv3(hx)
+ hx = self.pool3(hx3)
+
+ hx4 = self.rebnconv4(hx)
+ hx = self.pool4(hx4)
+
+ hx5 = self.rebnconv5(hx)
+
+ hx6 = self.rebnconv6(hx5)
+
+
+ hx5d = self.rebnconv5d(torch.cat((hx6,hx5),1))
+ hx5dup = _upsample_like(hx5d,hx4)
+
+ hx4d = self.rebnconv4d(torch.cat((hx5dup,hx4),1))
+ hx4dup = _upsample_like(hx4d,hx3)
+
+ hx3d = self.rebnconv3d(torch.cat((hx4dup,hx3),1))
+ hx3dup = _upsample_like(hx3d,hx2)
+
+ hx2d = self.rebnconv2d(torch.cat((hx3dup,hx2),1))
+ hx2dup = _upsample_like(hx2d,hx1)
+
+ hx1d = self.rebnconv1d(torch.cat((hx2dup,hx1),1))
+
+ return hx1d + hxin
+
+### RSU-5 ###
+class RSU5(nn.Module):
+
+ def __init__(self, in_ch=3, mid_ch=12, out_ch=3):
+ super(RSU5,self).__init__()
+
+ self.rebnconvin = REBNCONV(in_ch,out_ch,dirate=1)
+
+ self.rebnconv1 = REBNCONV(out_ch,mid_ch,dirate=1)
+ self.pool1 = nn.MaxPool2d(2,stride=2,ceil_mode=True)
+
+ self.rebnconv2 = REBNCONV(mid_ch,mid_ch,dirate=1)
+ self.pool2 = nn.MaxPool2d(2,stride=2,ceil_mode=True)
+
+ self.rebnconv3 = REBNCONV(mid_ch,mid_ch,dirate=1)
+ self.pool3 = nn.MaxPool2d(2,stride=2,ceil_mode=True)
+
+ self.rebnconv4 = REBNCONV(mid_ch,mid_ch,dirate=1)
+
+ self.rebnconv5 = REBNCONV(mid_ch,mid_ch,dirate=2)
+
+ self.rebnconv4d = REBNCONV(mid_ch*2,mid_ch,dirate=1)
+ self.rebnconv3d = REBNCONV(mid_ch*2,mid_ch,dirate=1)
+ self.rebnconv2d = REBNCONV(mid_ch*2,mid_ch,dirate=1)
+ self.rebnconv1d = REBNCONV(mid_ch*2,out_ch,dirate=1)
+
+ def forward(self,x):
+
+ hx = x
+
+ hxin = self.rebnconvin(hx)
+
+ hx1 = self.rebnconv1(hxin)
+ hx = self.pool1(hx1)
+
+ hx2 = self.rebnconv2(hx)
+ hx = self.pool2(hx2)
+
+ hx3 = self.rebnconv3(hx)
+ hx = self.pool3(hx3)
+
+ hx4 = self.rebnconv4(hx)
+
+ hx5 = self.rebnconv5(hx4)
+
+ hx4d = self.rebnconv4d(torch.cat((hx5,hx4),1))
+ hx4dup = _upsample_like(hx4d,hx3)
+
+ hx3d = self.rebnconv3d(torch.cat((hx4dup,hx3),1))
+ hx3dup = _upsample_like(hx3d,hx2)
+
+ hx2d = self.rebnconv2d(torch.cat((hx3dup,hx2),1))
+ hx2dup = _upsample_like(hx2d,hx1)
+
+ hx1d = self.rebnconv1d(torch.cat((hx2dup,hx1),1))
+
+ return hx1d + hxin
+
+### RSU-4 ###
+class RSU4(nn.Module):
+
+ def __init__(self, in_ch=3, mid_ch=12, out_ch=3):
+ super(RSU4,self).__init__()
+
+ self.rebnconvin = REBNCONV(in_ch,out_ch,dirate=1)
+
+ self.rebnconv1 = REBNCONV(out_ch,mid_ch,dirate=1)
+ self.pool1 = nn.MaxPool2d(2,stride=2,ceil_mode=True)
+
+ self.rebnconv2 = REBNCONV(mid_ch,mid_ch,dirate=1)
+ self.pool2 = nn.MaxPool2d(2,stride=2,ceil_mode=True)
+
+ self.rebnconv3 = REBNCONV(mid_ch,mid_ch,dirate=1)
+
+ self.rebnconv4 = REBNCONV(mid_ch,mid_ch,dirate=2)
+
+ self.rebnconv3d = REBNCONV(mid_ch*2,mid_ch,dirate=1)
+ self.rebnconv2d = REBNCONV(mid_ch*2,mid_ch,dirate=1)
+ self.rebnconv1d = REBNCONV(mid_ch*2,out_ch,dirate=1)
+
+ def forward(self,x):
+
+ hx = x
+
+ hxin = self.rebnconvin(hx)
+
+ hx1 = self.rebnconv1(hxin)
+ hx = self.pool1(hx1)
+
+ hx2 = self.rebnconv2(hx)
+ hx = self.pool2(hx2)
+
+ hx3 = self.rebnconv3(hx)
+
+ hx4 = self.rebnconv4(hx3)
+
+ hx3d = self.rebnconv3d(torch.cat((hx4,hx3),1))
+ hx3dup = _upsample_like(hx3d,hx2)
+
+ hx2d = self.rebnconv2d(torch.cat((hx3dup,hx2),1))
+ hx2dup = _upsample_like(hx2d,hx1)
+
+ hx1d = self.rebnconv1d(torch.cat((hx2dup,hx1),1))
+
+ return hx1d + hxin
+
+### RSU-4F ###
+class RSU4F(nn.Module):
+
+ def __init__(self, in_ch=3, mid_ch=12, out_ch=3):
+ super(RSU4F,self).__init__()
+
+ self.rebnconvin = REBNCONV(in_ch,out_ch,dirate=1)
+
+ self.rebnconv1 = REBNCONV(out_ch,mid_ch,dirate=1)
+ self.rebnconv2 = REBNCONV(mid_ch,mid_ch,dirate=2)
+ self.rebnconv3 = REBNCONV(mid_ch,mid_ch,dirate=4)
+
+ self.rebnconv4 = REBNCONV(mid_ch,mid_ch,dirate=8)
+
+ self.rebnconv3d = REBNCONV(mid_ch*2,mid_ch,dirate=4)
+ self.rebnconv2d = REBNCONV(mid_ch*2,mid_ch,dirate=2)
+ self.rebnconv1d = REBNCONV(mid_ch*2,out_ch,dirate=1)
+
+ def forward(self,x):
+
+ hx = x
+
+ hxin = self.rebnconvin(hx)
+
+ hx1 = self.rebnconv1(hxin)
+ hx2 = self.rebnconv2(hx1)
+ hx3 = self.rebnconv3(hx2)
+
+ hx4 = self.rebnconv4(hx3)
+
+ hx3d = self.rebnconv3d(torch.cat((hx4,hx3),1))
+ hx2d = self.rebnconv2d(torch.cat((hx3d,hx2),1))
+ hx1d = self.rebnconv1d(torch.cat((hx2d,hx1),1))
+
+ return hx1d + hxin
+
+
+class myrebnconv(nn.Module):
+ def __init__(self, in_ch=3,
+ out_ch=1,
+ kernel_size=3,
+ stride=1,
+ padding=1,
+ dilation=1,
+ groups=1):
+ super(myrebnconv,self).__init__()
+
+ self.conv = nn.Conv2d(in_ch,
+ out_ch,
+ kernel_size=kernel_size,
+ stride=stride,
+ padding=padding,
+ dilation=dilation,
+ groups=groups)
+ self.bn = nn.BatchNorm2d(out_ch)
+ self.rl = nn.ReLU(inplace=True)
+
+ def forward(self,x):
+ return self.rl(self.bn(self.conv(x)))
+
+
+class BriaRMBG(nn.Module):
+
+ def __init__(self,in_ch=3,out_ch=1):
+ super(BriaRMBG,self).__init__()
+
+ self.conv_in = nn.Conv2d(in_ch,64,3,stride=2,padding=1)
+ self.pool_in = nn.MaxPool2d(2,stride=2,ceil_mode=True)
+
+ self.stage1 = RSU7(64,32,64)
+ self.pool12 = nn.MaxPool2d(2,stride=2,ceil_mode=True)
+
+ self.stage2 = RSU6(64,32,128)
+ self.pool23 = nn.MaxPool2d(2,stride=2,ceil_mode=True)
+
+ self.stage3 = RSU5(128,64,256)
+ self.pool34 = nn.MaxPool2d(2,stride=2,ceil_mode=True)
+
+ self.stage4 = RSU4(256,128,512)
+ self.pool45 = nn.MaxPool2d(2,stride=2,ceil_mode=True)
+
+ self.stage5 = RSU4F(512,256,512)
+ self.pool56 = nn.MaxPool2d(2,stride=2,ceil_mode=True)
+
+ self.stage6 = RSU4F(512,256,512)
+
+ # decoder
+ self.stage5d = RSU4F(1024,256,512)
+ self.stage4d = RSU4(1024,128,256)
+ self.stage3d = RSU5(512,64,128)
+ self.stage2d = RSU6(256,32,64)
+ self.stage1d = RSU7(128,16,64)
+
+ self.side1 = nn.Conv2d(64,out_ch,3,padding=1)
+ self.side2 = nn.Conv2d(64,out_ch,3,padding=1)
+ self.side3 = nn.Conv2d(128,out_ch,3,padding=1)
+ self.side4 = nn.Conv2d(256,out_ch,3,padding=1)
+ self.side5 = nn.Conv2d(512,out_ch,3,padding=1)
+ self.side6 = nn.Conv2d(512,out_ch,3,padding=1)
+
+ # self.outconv = nn.Conv2d(6*out_ch,out_ch,1)
+
+ def forward(self,x):
+
+ hx = x
+
+ hxin = self.conv_in(hx)
+ #hx = self.pool_in(hxin)
+
+ #stage 1
+ hx1 = self.stage1(hxin)
+ hx = self.pool12(hx1)
+
+ #stage 2
+ hx2 = self.stage2(hx)
+ hx = self.pool23(hx2)
+
+ #stage 3
+ hx3 = self.stage3(hx)
+ hx = self.pool34(hx3)
+
+ #stage 4
+ hx4 = self.stage4(hx)
+ hx = self.pool45(hx4)
+
+ #stage 5
+ hx5 = self.stage5(hx)
+ hx = self.pool56(hx5)
+
+ #stage 6
+ hx6 = self.stage6(hx)
+ hx6up = _upsample_like(hx6,hx5)
+
+ #-------------------- decoder --------------------
+ hx5d = self.stage5d(torch.cat((hx6up,hx5),1))
+ hx5dup = _upsample_like(hx5d,hx4)
+
+ hx4d = self.stage4d(torch.cat((hx5dup,hx4),1))
+ hx4dup = _upsample_like(hx4d,hx3)
+
+ hx3d = self.stage3d(torch.cat((hx4dup,hx3),1))
+ hx3dup = _upsample_like(hx3d,hx2)
+
+ hx2d = self.stage2d(torch.cat((hx3dup,hx2),1))
+ hx2dup = _upsample_like(hx2d,hx1)
+
+ hx1d = self.stage1d(torch.cat((hx2dup,hx1),1))
+
+
+ #side output
+ d1 = self.side1(hx1d)
+ d1 = _upsample_like(d1,x)
+
+ d2 = self.side2(hx2d)
+ d2 = _upsample_like(d2,x)
+
+ d3 = self.side3(hx3d)
+ d3 = _upsample_like(d3,x)
+
+ d4 = self.side4(hx4d)
+ d4 = _upsample_like(d4,x)
+
+ d5 = self.side5(hx5d)
+ d5 = _upsample_like(d5,x)
+
+ d6 = self.side6(hx6)
+ d6 = _upsample_like(d6,x)
+
+ return [F.sigmoid(d1), F.sigmoid(d2), F.sigmoid(d3), F.sigmoid(d4), F.sigmoid(d5), F.sigmoid(d6)],[hx1d,hx2d,hx3d,hx4d,hx5d,hx6]
+
diff --git a/py/imagefunc.py b/py/imagefunc.py
index 3b11af9..e42e9ac 100644
--- a/py/imagefunc.py
+++ b/py/imagefunc.py
@@ -199,7 +199,6 @@ def blend_linear_light(background_image:Image, layer_image:Image) -> Image:
img = img * (1 - mask_2) + mask_2
return cv22pil(ski2cv2(img))
-
def blend_hard_mix(background_image:Image, layer_image:Image) -> Image:
img_1 = cv22ski(pil2cv2(background_image))
img_2 = cv22ski(pil2cv2(layer_image))
@@ -209,7 +208,6 @@ def blend_hard_mix(background_image:Image, layer_image:Image) -> Image:
img = img * mask
return cv22pil(ski2cv2(img))
-
def shift_image(image:Image, distance_x:int, distance_y:int, background_color:str='#000000', cyclic:bool=False) -> Image:
width = image.width
height = image.height
@@ -273,8 +271,6 @@ def chop_image(background_image:Image, layer_image:Image, blend_mode:str, opacit
ret_image = blend_linear_light(background_image, layer_image)
if blend_mode == 'hard_mix':
ret_image = blend_hard_mix(background_image, layer_image)
-
-
# opacity
if opacity == 0:
ret_image = background_image
@@ -624,6 +620,22 @@ def image_beauty(image:Image, level:int=50) -> Image:
ret_image = cv2.cvtColor(img_bit, cv2.COLOR_BGR2RGB)
return cv22pil(ret_image)
+def imagebatch2imagelist(image:torch.Tensor) -> torch.Tensor:
+ images = [image[i:i + 1, ...] for i in range(image.shape[0])]
+ return images
+
+def imagelist2imagebatch(images:torch.Tensor) -> torch.Tensor:
+ if len(images) <= 1:
+ return (images,)
+ else:
+ image1 = images[0]
+ for image2 in images[1:]:
+ if image1.shape[1:] != image2.shape[1:]:
+ image2 = comfy.utils.common_upscale(image2.movedim(-1, 1), image1.shape[2], image1.shape[1], "lanczos", "center").movedim(1, -1)
+ image1 = torch.cat((image1, image2), dim=0)
+ return image1
+
+
'''Mask Functions'''
def expand_mask(mask:torch.Tensor, grow:int, blur:int) -> torch.Tensor:
diff --git a/py/pixel_spread.py b/py/pixel_spread.py
new file mode 100644
index 0000000..8784566
--- /dev/null
+++ b/py/pixel_spread.py
@@ -0,0 +1,61 @@
+import copy
+from pymatting import *
+from .imagefunc import *
+
+class PixelSpread:
+
+ def __init__(self):
+ pass
+
+ @classmethod
+ def INPUT_TYPES(self):
+
+ return {
+ "required": {
+ "image": ("IMAGE", ), #
+ "invert_mask": ("BOOLEAN", {"default": False}), # �转mask
+ },
+ "optional": {
+ "mask": ("MASK",), #
+ }
+ }
+
+ RETURN_TYPES = ("IMAGE", )
+ RETURN_NAMES = ("image", )
+ FUNCTION = 'pixel_spread'
+ CATEGORY = '😺dzNodes/LayerMask'
+ OUTPUT_NODE = True
+
+ def pixel_spread(self, image, invert_mask, mask=None):
+ _image = tensor2pil(image)
+ if _image.mode == 'RGBA':
+ _mask = _image.split()[-1]
+ else:
+ _mask = Image.new('L', _image.size, 'white')
+ if mask is not None:
+ if invert_mask:
+ mask = 1 - mask
+ _mask = mask2image(mask).convert('L')
+ image = pil2tensor(_image.convert('RGB'))
+ _mask = _mask.convert('RGB')
+ i_dup = copy.deepcopy(image.cpu().numpy().astype(np.float64))
+ a_dup = copy.deepcopy(pil2tensor(_mask).cpu().numpy().astype(np.float64))
+ fg = copy.deepcopy(image.cpu().numpy().astype(np.float64))
+
+ for index, image in enumerate(i_dup):
+ trimap = 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})
+ fg[index], _ = estimate_foreground_ml(image, np.array(alpha), return_background=True)
+
+ return (torch.from_numpy(fg.astype(np.float32)), # fg
+ )
+
+NODE_CLASS_MAPPINGS = {
+ "LayerMask: PixelSpread": PixelSpread
+}
+
+NODE_DISPLAY_NAME_MAPPINGS = {
+ "LayerMask: PixelSpread": "LayerMask: PixelSpread"
+}
\ No newline at end of file
diff --git a/py/rembg_ultra.py b/py/rembg_ultra.py
new file mode 100644
index 0000000..ac5bde7
--- /dev/null
+++ b/py/rembg_ultra.py
@@ -0,0 +1,89 @@
+
+import copy
+import torch, os
+import torch.nn.functional as F
+import numpy as np
+from PIL import Image
+from pymatting import *
+# from torchvision.transforms.functional import normalize
+import torchvision.transforms.functional as TF
+from .briarmbg import BriaRMBG
+from .imagefunc import *
+
+current_directory = os.path.dirname(os.path.abspath(__file__))
+device = "cuda" if torch.cuda.is_available() else "cpu"
+
+def load_model():
+ net = BriaRMBG()
+ model_path = os.path.join(os.path.dirname(current_directory), "RMBG-1.4/model.pth")
+ net.load_state_dict(torch.load(model_path, map_location=device))
+ net.to(device)
+ net.eval()
+ return net
+
+class RemBgUltra:
+ def __init__(self):
+ pass
+
+ @classmethod
+ def INPUT_TYPES(cls):
+
+ return {
+ "required": {
+ "image": ("IMAGE",),
+ "detail_range": ("INT", {"default": 8, "min": 0, "max": 256, "step": 1}),
+ "black_point": ("FLOAT", {"default": 0.01, "min": 0.01, "max": 0.98, "step": 0.01}),
+ "white_point": ("FLOAT", {"default": 0.99, "min": 0.02, "max": 0.99, "step": 0.01}),
+ },
+ "optional": {
+ }
+ }
+
+ RETURN_TYPES = ("IMAGE", "MASK", )
+ RETURN_NAMES = ("image", "mask", )
+ FUNCTION = "rembg_ultra"
+ CATEGORY = '😺dzNodes/LayerMask'
+
+ def rembg_ultra(self, image, detail_range, black_point, white_point):
+
+ rmbgmodel = load_model()
+ orig_image = tensor2pil(image).convert('RGB')
+ w,h = orig_image.size
+ im_np = np.array(orig_image.resize((1024, 1024), Image.BILINEAR))
+ 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 = normalize(im_tensor,[0.5,0.5,0.5],[1.0,1.0,1.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()
+ result=rmbgmodel(im_tensor)
+ result = torch.squeeze(F.interpolate(result[0][0], size=(h,w), mode='bilinear') ,0)
+ ma = torch.max(result)
+ mi = torch.min(result)
+ result = (result-mi)/(ma-mi)
+ im_array = (result*255).cpu().data.numpy().astype(np.uint8)
+ _mask = Image.fromarray(np.squeeze(im_array)).convert('L')
+ # ultra edge process
+ d = detail_range * 2 + 1
+ i_dup = copy.deepcopy(image.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
+ if detail_range > 0:
+ trimap = cv2.GaussianBlur(trimap, (d, d), 0)
+ trimap = fix_trimap(trimap, black_point, white_point)
+ alpha = estimate_alpha_cf(img, trimap, laplacian_kwargs={"epsilon": 1e-6},
+ cg_kwargs={"maxiter": 500})
+ a_dup[index] = np.stack([alpha, alpha, alpha], axis=-1) # convert back to rgb
+ _mask = tensor2pil(torch.from_numpy(a_dup.astype(np.float32))) # alpha
+ ret_image = RGB2RGBA(orig_image, _mask.convert('L'))
+ return (pil2tensor(ret_image), image2mask(_mask),)
+
+NODE_CLASS_MAPPINGS = {
+ "LayerMask: RemBgUltra": RemBgUltra,
+}
+
+NODE_DISPLAY_NAME_MAPPINGS = {
+ "LayerMask: RemBgUltra": "LayerMask: RemBgUltra",
+}
diff --git a/requirements.txt b/requirements.txt
index 2bd74b9..91bd24a 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -4,4 +4,6 @@ torch
matplotlib
Scipy
opencv-python
-scikit_image
\ No newline at end of file
+scikit_image
+opencv-contrib-python
+pymatting
\ No newline at end of file
diff --git a/workflow/1344x768_beach.png b/workflow/1344x768_beach.png
new file mode 100644
index 0000000..cdc1b69
Binary files /dev/null and b/workflow/1344x768_beach.png differ
diff --git a/workflow/1344x768_hair.png b/workflow/1344x768_hair.png
new file mode 100644
index 0000000..48a9d93
Binary files /dev/null and b/workflow/1344x768_hair.png differ
diff --git a/workflow/pixel_spread_example.json b/workflow/pixel_spread_example.json
new file mode 100644
index 0000000..8adef58
--- /dev/null
+++ b/workflow/pixel_spread_example.json
@@ -0,0 +1,234 @@
+{
+ "last_node_id": 46,
+ "last_link_id": 82,
+ "nodes": [
+ {
+ "id": 39,
+ "type": "LoadImage",
+ "pos": [
+ 490,
+ 660
+ ],
+ "size": {
+ "0": 330.9508972167969,
+ "1": 298.0813903808594
+ },
+ "flags": {},
+ "order": 0,
+ "mode": 0,
+ "outputs": [
+ {
+ "name": "IMAGE",
+ "type": "IMAGE",
+ "links": [
+ 74,
+ 79
+ ],
+ "shape": 3,
+ "slot_index": 0
+ },
+ {
+ "name": "MASK",
+ "type": "MASK",
+ "links": null,
+ "shape": 3
+ }
+ ],
+ "properties": {
+ "Node name for S&R": "LoadImage"
+ },
+ "widgets_values": [
+ "1344x768_hair (2).png",
+ "image"
+ ]
+ },
+ {
+ "id": 44,
+ "type": "LayerMask: RemBgUltra",
+ "pos": [
+ 869,
+ 784
+ ],
+ "size": {
+ "0": 315,
+ "1": 126
+ },
+ "flags": {},
+ "order": 1,
+ "mode": 0,
+ "inputs": [
+ {
+ "name": "image",
+ "type": "IMAGE",
+ "link": 74
+ }
+ ],
+ "outputs": [
+ {
+ "name": "image",
+ "type": "IMAGE",
+ "links": null,
+ "shape": 3
+ },
+ {
+ "name": "mask",
+ "type": "MASK",
+ "links": [
+ 75,
+ 80
+ ],
+ "shape": 3,
+ "slot_index": 1
+ }
+ ],
+ "properties": {
+ "Node name for S&R": "LayerMask: RemBgUltra"
+ },
+ "widgets_values": [
+ 10,
+ 0.01,
+ 0.99
+ ]
+ },
+ {
+ "id": 41,
+ "type": "LayerMask: MaskPreview",
+ "pos": [
+ 1247,
+ 802
+ ],
+ "size": {
+ "0": 309.01806640625,
+ "1": 246
+ },
+ "flags": {},
+ "order": 2,
+ "mode": 0,
+ "inputs": [
+ {
+ "name": "mask",
+ "type": "MASK",
+ "link": 75
+ }
+ ],
+ "properties": {
+ "Node name for S&R": "LayerMask: MaskPreview"
+ }
+ },
+ {
+ "id": 42,
+ "type": "PreviewImage",
+ "pos": [
+ 1624,
+ 721
+ ],
+ "size": {
+ "0": 314.5066223144531,
+ "1": 246
+ },
+ "flags": {},
+ "order": 4,
+ "mode": 0,
+ "inputs": [
+ {
+ "name": "images",
+ "type": "IMAGE",
+ "link": 81
+ }
+ ],
+ "properties": {
+ "Node name for S&R": "PreviewImage"
+ }
+ },
+ {
+ "id": 46,
+ "type": "LayerMask: PixelSpread",
+ "pos": [
+ 1238,
+ 665
+ ],
+ "size": {
+ "0": 315,
+ "1": 78
+ },
+ "flags": {},
+ "order": 3,
+ "mode": 0,
+ "inputs": [
+ {
+ "name": "image",
+ "type": "IMAGE",
+ "link": 79
+ },
+ {
+ "name": "mask",
+ "type": "MASK",
+ "link": 80
+ }
+ ],
+ "outputs": [
+ {
+ "name": "image",
+ "type": "IMAGE",
+ "links": [
+ 81
+ ],
+ "shape": 3,
+ "slot_index": 0
+ }
+ ],
+ "properties": {
+ "Node name for S&R": "LayerMask: PixelSpread"
+ },
+ "widgets_values": [
+ false
+ ]
+ }
+ ],
+ "links": [
+ [
+ 74,
+ 39,
+ 0,
+ 44,
+ 0,
+ "IMAGE"
+ ],
+ [
+ 75,
+ 44,
+ 1,
+ 41,
+ 0,
+ "MASK"
+ ],
+ [
+ 79,
+ 39,
+ 0,
+ 46,
+ 0,
+ "IMAGE"
+ ],
+ [
+ 80,
+ 44,
+ 1,
+ 46,
+ 1,
+ "MASK"
+ ],
+ [
+ 81,
+ 46,
+ 0,
+ 42,
+ 0,
+ "IMAGE"
+ ]
+ ],
+ "groups": [],
+ "config": {},
+ "extra": {},
+ "version": 0.4
+}
\ No newline at end of file
diff --git a/workflow/rembg_ultra_example.json b/workflow/rembg_ultra_example.json
new file mode 100644
index 0000000..182de1b
--- /dev/null
+++ b/workflow/rembg_ultra_example.json
@@ -0,0 +1,697 @@
+{
+ "last_node_id": 37,
+ "last_link_id": 70,
+ "nodes": [
+ {
+ "id": 28,
+ "type": "PreviewImage",
+ "pos": [
+ 1863,
+ 741
+ ],
+ "size": {
+ "0": 629.1258544921875,
+ "1": 378.28240966796875
+ },
+ "flags": {},
+ "order": 11,
+ "mode": 0,
+ "inputs": [
+ {
+ "name": "images",
+ "type": "IMAGE",
+ "link": 47
+ }
+ ],
+ "properties": {
+ "Node name for S&R": "PreviewImage"
+ }
+ },
+ {
+ "id": 30,
+ "type": "LayerMask: MaskPreview",
+ "pos": [
+ 2512,
+ 737
+ ],
+ "size": {
+ "0": 629.4749145507812,
+ "1": 381.56378173828125
+ },
+ "flags": {},
+ "order": 9,
+ "mode": 0,
+ "inputs": [
+ {
+ "name": "mask",
+ "type": "MASK",
+ "link": 52
+ }
+ ],
+ "properties": {
+ "Node name for S&R": "LayerMask: MaskPreview"
+ }
+ },
+ {
+ "id": 33,
+ "type": "PreviewImage",
+ "pos": [
+ 1865.9044634155273,
+ 1310.3026451171872
+ ],
+ "size": {
+ "0": 629.1258544921875,
+ "1": 378.28240966796875
+ },
+ "flags": {},
+ "order": 12,
+ "mode": 0,
+ "inputs": [
+ {
+ "name": "images",
+ "type": "IMAGE",
+ "link": 60
+ }
+ ],
+ "properties": {
+ "Node name for S&R": "PreviewImage"
+ }
+ },
+ {
+ "id": 34,
+ "type": "LayerMask: MaskPreview",
+ "pos": [
+ 2520.904463415527,
+ 1310.3026451171872
+ ],
+ "size": {
+ "0": 629.4749145507812,
+ "1": 381.56378173828125
+ },
+ "flags": {},
+ "order": 6,
+ "mode": 0,
+ "inputs": [
+ {
+ "name": "mask",
+ "type": "MASK",
+ "link": 61
+ }
+ ],
+ "properties": {
+ "Node name for S&R": "LayerMask: MaskPreview"
+ }
+ },
+ {
+ "id": 27,
+ "type": "LayerUtility: ImageBlendAdvance",
+ "pos": [
+ 1490,
+ 790
+ ],
+ "size": {
+ "0": 315,
+ "1": 338
+ },
+ "flags": {},
+ "order": 8,
+ "mode": 0,
+ "inputs": [
+ {
+ "name": "background_image",
+ "type": "IMAGE",
+ "link": 57
+ },
+ {
+ "name": "layer_image",
+ "type": "IMAGE",
+ "link": 67
+ },
+ {
+ "name": "layer_mask",
+ "type": "MASK",
+ "link": 45
+ }
+ ],
+ "outputs": [
+ {
+ "name": "image",
+ "type": "IMAGE",
+ "links": [
+ 47
+ ],
+ "shape": 3,
+ "slot_index": 0
+ },
+ {
+ "name": "mask",
+ "type": "MASK",
+ "links": null,
+ "shape": 3
+ }
+ ],
+ "properties": {
+ "Node name for S&R": "LayerUtility: ImageBlendAdvance"
+ },
+ "widgets_values": [
+ false,
+ "normal",
+ 100,
+ 50,
+ 50,
+ "None",
+ 1,
+ 1,
+ 0,
+ "lanczos",
+ 0
+ ]
+ },
+ {
+ "id": 15,
+ "type": "LayerUtility: ImageBlendAdvance",
+ "pos": [
+ 1490,
+ 1280
+ ],
+ "size": {
+ "0": 315,
+ "1": 338
+ },
+ "flags": {},
+ "order": 10,
+ "mode": 0,
+ "inputs": [
+ {
+ "name": "background_image",
+ "type": "IMAGE",
+ "link": 58
+ },
+ {
+ "name": "layer_image",
+ "type": "IMAGE",
+ "link": 70
+ },
+ {
+ "name": "layer_mask",
+ "type": "MASK",
+ "link": 50
+ }
+ ],
+ "outputs": [
+ {
+ "name": "image",
+ "type": "IMAGE",
+ "links": [
+ 60
+ ],
+ "shape": 3,
+ "slot_index": 0
+ },
+ {
+ "name": "mask",
+ "type": "MASK",
+ "links": null,
+ "shape": 3
+ }
+ ],
+ "properties": {
+ "Node name for S&R": "LayerUtility: ImageBlendAdvance"
+ },
+ "widgets_values": [
+ false,
+ "normal",
+ 100,
+ 50,
+ 50,
+ "None",
+ 1,
+ 1,
+ 0,
+ "lanczos",
+ 0
+ ]
+ },
+ {
+ "id": 36,
+ "type": "Reroute",
+ "pos": [
+ 1039,
+ 1096
+ ],
+ "size": [
+ 75,
+ 26
+ ],
+ "flags": {},
+ "order": 3,
+ "mode": 0,
+ "inputs": [
+ {
+ "name": "",
+ "type": "*",
+ "link": 65
+ }
+ ],
+ "outputs": [
+ {
+ "name": "",
+ "type": "IMAGE",
+ "links": [
+ 66,
+ 67
+ ],
+ "slot_index": 0
+ }
+ ],
+ "properties": {
+ "showOutputText": false,
+ "horizontal": false
+ }
+ },
+ {
+ "id": 26,
+ "type": "Image Remove Background Rembg (mtb)",
+ "pos": [
+ 1139,
+ 1067
+ ],
+ "size": {
+ "0": 294,
+ "1": 230
+ },
+ "flags": {},
+ "order": 5,
+ "mode": 0,
+ "inputs": [
+ {
+ "name": "image",
+ "type": "IMAGE",
+ "link": 66
+ }
+ ],
+ "outputs": [
+ {
+ "name": "Image (rgba)",
+ "type": "IMAGE",
+ "links": null,
+ "shape": 3
+ },
+ {
+ "name": "Mask",
+ "type": "MASK",
+ "links": [
+ 45,
+ 52
+ ],
+ "shape": 3,
+ "slot_index": 1
+ },
+ {
+ "name": "Image",
+ "type": "IMAGE",
+ "links": null,
+ "shape": 3
+ }
+ ],
+ "properties": {
+ "Node name for S&R": "Image Remove Background Rembg (mtb)"
+ },
+ "widgets_values": [
+ false,
+ 240,
+ 10,
+ 10,
+ false,
+ "#000000"
+ ]
+ },
+ {
+ "id": 2,
+ "type": "LoadImage",
+ "pos": [
+ 957,
+ 677
+ ],
+ "size": {
+ "0": 224.95281982421875,
+ "1": 314
+ },
+ "flags": {},
+ "order": 0,
+ "mode": 0,
+ "outputs": [
+ {
+ "name": "IMAGE",
+ "type": "IMAGE",
+ "links": [
+ 62,
+ 65
+ ],
+ "shape": 3,
+ "slot_index": 0
+ },
+ {
+ "name": "MASK",
+ "type": "MASK",
+ "links": null,
+ "shape": 3
+ }
+ ],
+ "properties": {
+ "Node name for S&R": "LoadImage"
+ },
+ "widgets_values": [
+ "1344x768_hair (4).png",
+ "image"
+ ]
+ },
+ {
+ "id": 16,
+ "type": "LoadImage",
+ "pos": [
+ 1220,
+ 676
+ ],
+ "size": {
+ "0": 235.49658203125,
+ "1": 314
+ },
+ "flags": {},
+ "order": 1,
+ "mode": 0,
+ "outputs": [
+ {
+ "name": "IMAGE",
+ "type": "IMAGE",
+ "links": [
+ 57,
+ 58
+ ],
+ "shape": 3,
+ "slot_index": 0
+ },
+ {
+ "name": "MASK",
+ "type": "MASK",
+ "links": null,
+ "shape": 3
+ }
+ ],
+ "properties": {
+ "Node name for S&R": "LoadImage"
+ },
+ "widgets_values": [
+ "1344x768_beach (2).png",
+ "image"
+ ]
+ },
+ {
+ "id": 35,
+ "type": "Reroute",
+ "pos": [
+ 1015,
+ 1343
+ ],
+ "size": [
+ 75,
+ 26
+ ],
+ "flags": {},
+ "order": 2,
+ "mode": 0,
+ "inputs": [
+ {
+ "name": "",
+ "type": "*",
+ "link": 62
+ }
+ ],
+ "outputs": [
+ {
+ "name": "",
+ "type": "IMAGE",
+ "links": [
+ 64,
+ 68
+ ],
+ "slot_index": 0
+ }
+ ],
+ "properties": {
+ "showOutputText": false,
+ "horizontal": false
+ }
+ },
+ {
+ "id": 37,
+ "type": "LayerMask: PixelSpread",
+ "pos": [
+ 1126,
+ 1352
+ ],
+ "size": {
+ "0": 315,
+ "1": 78
+ },
+ "flags": {},
+ "order": 7,
+ "mode": 0,
+ "inputs": [
+ {
+ "name": "image",
+ "type": "IMAGE",
+ "link": 68
+ },
+ {
+ "name": "mask",
+ "type": "MASK",
+ "link": 69
+ }
+ ],
+ "outputs": [
+ {
+ "name": "image",
+ "type": "IMAGE",
+ "links": [
+ 70
+ ],
+ "shape": 3,
+ "slot_index": 0
+ }
+ ],
+ "properties": {
+ "Node name for S&R": "LayerMask: PixelSpread"
+ },
+ "widgets_values": [
+ false
+ ]
+ },
+ {
+ "id": 29,
+ "type": "LayerMask: RemBgUltra",
+ "pos": [
+ 1124,
+ 1484
+ ],
+ "size": {
+ "0": 315,
+ "1": 126
+ },
+ "flags": {},
+ "order": 4,
+ "mode": 0,
+ "inputs": [
+ {
+ "name": "image",
+ "type": "IMAGE",
+ "link": 64
+ }
+ ],
+ "outputs": [
+ {
+ "name": "image",
+ "type": "IMAGE",
+ "links": null,
+ "shape": 3
+ },
+ {
+ "name": "mask",
+ "type": "MASK",
+ "links": [
+ 50,
+ 61,
+ 69
+ ],
+ "shape": 3,
+ "slot_index": 1
+ }
+ ],
+ "properties": {
+ "Node name for S&R": "LayerMask: RemBgUltra"
+ },
+ "widgets_values": [
+ 10,
+ 0.01,
+ 0.99
+ ]
+ }
+ ],
+ "links": [
+ [
+ 45,
+ 26,
+ 1,
+ 27,
+ 2,
+ "MASK"
+ ],
+ [
+ 47,
+ 27,
+ 0,
+ 28,
+ 0,
+ "IMAGE"
+ ],
+ [
+ 50,
+ 29,
+ 1,
+ 15,
+ 2,
+ "MASK"
+ ],
+ [
+ 52,
+ 26,
+ 1,
+ 30,
+ 0,
+ "MASK"
+ ],
+ [
+ 57,
+ 16,
+ 0,
+ 27,
+ 0,
+ "IMAGE"
+ ],
+ [
+ 58,
+ 16,
+ 0,
+ 15,
+ 0,
+ "IMAGE"
+ ],
+ [
+ 60,
+ 15,
+ 0,
+ 33,
+ 0,
+ "IMAGE"
+ ],
+ [
+ 61,
+ 29,
+ 1,
+ 34,
+ 0,
+ "MASK"
+ ],
+ [
+ 62,
+ 2,
+ 0,
+ 35,
+ 0,
+ "*"
+ ],
+ [
+ 64,
+ 35,
+ 0,
+ 29,
+ 0,
+ "IMAGE"
+ ],
+ [
+ 65,
+ 2,
+ 0,
+ 36,
+ 0,
+ "*"
+ ],
+ [
+ 66,
+ 36,
+ 0,
+ 26,
+ 0,
+ "IMAGE"
+ ],
+ [
+ 67,
+ 36,
+ 0,
+ 27,
+ 1,
+ "IMAGE"
+ ],
+ [
+ 68,
+ 35,
+ 0,
+ 37,
+ 0,
+ "IMAGE"
+ ],
+ [
+ 69,
+ 29,
+ 1,
+ 37,
+ 1,
+ "MASK"
+ ],
+ [
+ 70,
+ 37,
+ 0,
+ 15,
+ 1,
+ "IMAGE"
+ ]
+ ],
+ "groups": [
+ {
+ "title": "Previous RemBg",
+ "bounding": [
+ 1848,
+ 642,
+ 1306,
+ 498
+ ],
+ "color": "#3f789e",
+ "font_size": 30
+ },
+ {
+ "title": "RemBgUltra + PixelSpread",
+ "bounding": [
+ 1852,
+ 1215,
+ 1312,
+ 497
+ ],
+ "color": "#b06634",
+ "font_size": 30
+ }
+ ],
+ "config": {},
+ "extra": {},
+ "version": 0.4
+}
\ No newline at end of file