commit ImageAutoCropV2 node
This commit is contained in:
@@ -37,7 +37,7 @@ git clone https://github.com/chflame163/ComfyUI_LayerStyle.git
|
||||
## Common Issues
|
||||
If the node cannot load properly or there are errors during use, please check the error message in the ComfyUI terminal window. The following are common errors and their solutions.
|
||||
|
||||
### ImportError:cannot import name 'guidedFilter' from 'cv2.ximgproc'
|
||||
### Cannot import name 'guidedFilter' from 'cv2.ximgproc'
|
||||
This error is caused by incorrect version of the ```opencv-contrib-python``` package,or this package is overwriteen by other opencv packages.
|
||||
|
||||
Solution:
|
||||
@@ -59,7 +59,7 @@ Solution:
|
||||
### NameError: name 'guidedFilter' is not defined
|
||||
The solution is the same as above.
|
||||
|
||||
### cannot import name 'VitMatteImageProcessor' from 'transformers'
|
||||
### Cannot import name 'VitMatteImageProcessor' from 'transformers'
|
||||
This error is caused by the low version of ```transformers``` package. Please upgrade it to the latest version.
|
||||
|
||||
Solution:
|
||||
@@ -76,13 +76,14 @@ This error is caused by the mask area being too large or too small when using th
|
||||
Solution:
|
||||
* Please adjust the parameters to change the effective area of the mask. Or use other methods to handle the edges.
|
||||
|
||||
### requests.exceptions.ProxyError: HTTPSConnectionPool(xxxx...)
|
||||
### Requests.exceptions.ProxyError: HTTPSConnectionPool(xxxx...)
|
||||
When this error has occurred, please check the network environment.
|
||||
|
||||
|
||||
## Update
|
||||
<font size="4">**If the dependency package error after updating, please reinstall the relevant dependency packages. for details, please refer to [here](https://github.com/chflame163/ComfyUI_LayerStyle/issues/5). </font><br />
|
||||
|
||||
* Commit [ImageAutoCropV2](#ImageAutoCropV2) node, it can choose not to remove the background, support mask input, and scale by long or short side size.
|
||||
* Commit [ImageHub](#ImageHub) node, supports up to 9 sets of Image and Mask switching output, and supports random output.
|
||||
* Commit [TextJoin](#TextJoin) node.
|
||||
* Commit [PromptEmbellish](#PromptEmbellish) node. it output polished prompt words, and support inputting images as references.
|
||||
@@ -876,6 +877,17 @@ box_preview: Crop position preview.
|
||||
cropped_mask: Cropped mask.
|
||||
|
||||
|
||||
### <a id="table1">ImageAutoCropV2</a>
|
||||
|
||||
The V2 upgrad version of ```ImageAutoCrop```, it has made the following changes based on the previous version:
|
||||

|
||||
|
||||
* Add optional input for mask. when there is a mask input, use that input directly to skip the built-in mask generation.
|
||||
* Add ```fill_background```. When set to False, the background will not be processed and any parts beyond the frame will not be included in the output range.
|
||||
* ```aspect_ratio``` adds the ```original``` option.
|
||||
* ```scale_by_longest_side``` changed to support scaling by long or short edge, set in ```scale_to_side```. The side length is set in ```scale_to__length```.
|
||||
|
||||
|
||||
### <a id="table1">GetImageSize</a>
|
||||

|
||||
Obtain the width and height of the image.
|
||||
@@ -1280,17 +1292,17 @@ Node options:
|
||||
* blur: The size of blur.
|
||||
|
||||
## Annotation for <a id="table1">notes</a>
|
||||
<sup>1</sup> The layer_image, layer_mask and the background_image(if have input), These three items must be of the same size.
|
||||
<sup>1</sup> The layer_image, layer_mask and the background_image(if have input), These three items must be of the same size.
|
||||
|
||||
<sup>2</sup> The mask not a mandatory input item. the alpha channel of the image is used by default. If the image input does not include an alpha channel, the entire image's alpha channel will be automatically created. if have masks input simultaneously, the alpha channel will be overwrite by the mask.
|
||||
<sup>2</sup> The mask not a mandatory input item. the alpha channel of the image is used by default. If the image input does not include an alpha channel, the entire image's alpha channel will be automatically created. if have masks input simultaneously, the alpha channel will be overwrite by the mask.
|
||||
|
||||
<sup>3</sup> The <a id="table1">blend</a> mode include **normal, multply, screen, add, subtract, difference, darker, color_burn, color_dodge, linear_burn, linear_dodge, overlay, soft_light, hard_light, vivid_light, pin_light, linear_light, and hard_mix.** all of 19 blend modes in total.
|
||||
<sup>3</sup> The <a id="table1">blend</a> mode include **normal, multply, screen, add, subtract, difference, darker, color_burn, color_dodge, linear_burn, linear_dodge, overlay, soft_light, hard_light, vivid_light, pin_light, linear_light, and hard_mix.** all of 19 blend modes in total.
|
||||

|
||||
<font size="1">*Preview of the blend mode </font><br />
|
||||
<font size="1">*Preview of the blend mode </font><br />
|
||||
|
||||
<sup>4</sup> The RGB color described by hexadecimal RGB format, like '#FA3D86'.
|
||||
<sup>4</sup> The RGB color described by hexadecimal RGB format, like '#FA3D86'.
|
||||
|
||||
<sup>5</sup> The layer_image and layer_mask must be of the same size.
|
||||
<sup>5</sup> The layer_image and layer_mask must be of the same size.
|
||||
|
||||
# statement
|
||||
LayerStyle nodes follows the MIT license, Some of its functional code comes from other open-source projects. If used for commercial purposes, please refer to the original project license to authorization agreement.
|
||||
|
||||
+24
-9
@@ -29,7 +29,7 @@ git clone https://github.com/chflame163/ComfyUI_LayerStyle.git
|
||||
|
||||
## 常见问题
|
||||
如果节点不能正常加载,或者使用中出现错误,请在ComfyUI终端窗口查看报错信息。以下是常见的错误及解决方法。
|
||||
### ImportError:cannot import name 'guidedFilter' from 'cv2.ximgproc'
|
||||
### Cannot import name 'guidedFilter' from 'cv2.ximgproc'
|
||||
这个错误是```opencv-contrib-python```没有正确安装,或者安装后又安装了其他opencv包导致。
|
||||
|
||||
解决方法:
|
||||
@@ -51,7 +51,7 @@ git clone https://github.com/chflame163/ComfyUI_LayerStyle.git
|
||||
### NameError: name 'guidedFilter' is not defined
|
||||
解决方法同上。
|
||||
|
||||
### cannot import name 'VitMatteImageProcessor' from 'transformers'
|
||||
### Cannot import name 'VitMatteImageProcessor' from 'transformers'
|
||||
这个错误是由于```transformers``` 版本过低造成的。请升级这个依赖包到最新版本。
|
||||
|
||||
解决方法:
|
||||
@@ -68,7 +68,7 @@ git clone https://github.com/chflame163/ComfyUI_LayerStyle.git
|
||||
解决方法:
|
||||
* 请调整参数,改变遮罩有效面积。或者换用其他的方法处理边缘。
|
||||
|
||||
### requests.exceptions.ProxyError: HTTPSConnectionPool(xxxx...)
|
||||
### Requests.exceptions.ProxyError: HTTPSConnectionPool(xxxx...)
|
||||
出现这个错误,请检查网络环境。
|
||||
|
||||
|
||||
@@ -83,6 +83,7 @@ git clone https://github.com/chflame163/ComfyUI_LayerStyle.git
|
||||
## 更新说明
|
||||
<font size="4">**如果本插件更新后出现依赖包错误,请重新安装相关依赖包。详情见[这里](https://github.com/chflame163/ComfyUI_LayerStyle/issues/5)。 </font><br />
|
||||
|
||||
* 添加 [ImageAutoCropV2](#ImageAutoCropV2) 节点,可选择不去除背景,支持mask输入,支持按长边或短边尺寸缩放。
|
||||
* 添加 [ImageHub](#ImageHub) 节点,支持最多9组Image和Mask切换,支持随机输出。
|
||||
* 添加 [TextJoin](#TextJoin) 节点。
|
||||
* 添加 [PromptEmbellish](#PromptEmbellish) 节点, 对简单的提示词润色,支持图片输入参考,支持中文输入。
|
||||
@@ -870,6 +871,16 @@ cropped_image: 裁切并更换背景后的图像。
|
||||
box_preview: 裁切位置预览。
|
||||
cropped_mask: 裁切后的遮罩。
|
||||
|
||||
### <a id="table1">ImageAutoCropV2</a>
|
||||
|
||||
```ImageAutoCrop```的V2升级版,在之前基础上做了如下改变:
|
||||

|
||||
|
||||
* 增加```mask```可选输入。当有mask输入时,直接使用该输入跳过内置遮罩生成。
|
||||
* 增加```fill_background```, 当此项设置为False时将不处理背景,并且超出画幅的部分不纳入输出范围。
|
||||
* ```aspect_ratio```增加```original```(原始画面宽高比)选项。
|
||||
* 按长边缩放改为支持按长边或短边缩放,在```scale_to_side```中设置。边长在```scale_to_length```中设置。
|
||||
|
||||
|
||||
### <a id="table1">GetImageSize</a>
|
||||

|
||||
@@ -1282,15 +1293,19 @@ mask反转
|
||||
* blur: 模糊大小。
|
||||
|
||||
## <a id="table1">节点注解</a>
|
||||
<sup>1</sup> image、mask和background_image(如果有输入)这三项必须是相同的尺寸。
|
||||
<sup>2</sup> mask不是必须的输入项,默认使用image的alpha通道,如果image输入不包含alpha通道将自动创建整个图像的alpha通道。如果输入mask,原本的alpha通道将被mask覆盖。
|
||||
<sup>3</sup> <a id="table1">混合模式</a>包括normal、multply、screen、add、subtract、difference、darker、lighter、color_burn、color_dodge、linear_burn、linear_dodge、overlay、soft_light、hard_light、vivid_light、pin_light、linear_light、hard_mix, 共19种混合模式
|
||||
<sup>1</sup> image、mask和background_image(如果有输入)这三项必须是相同的尺寸。
|
||||
|
||||
<sup>2</sup> mask不是必须的输入项,默认使用image的alpha通道,如果image输入不包含alpha通道将自动创建整个图像的alpha通道。如果输入mask,原本的alpha通道将被mask覆盖。
|
||||
|
||||
<sup>3</sup> <a id="table1">混合模式</a>包括normal、multply、screen、add、subtract、difference、darker、lighter、color_burn、color_dodge、linear_burn、linear_dodge、overlay、soft_light、hard_light、vivid_light、pin_light、linear_light、hard_mix, 共19种混合模式
|
||||
|
||||
|
||||

|
||||
<font size="1">*混合模式预览</font><br />
|
||||
<font size="1">*混合模式预览</font><br />
|
||||
|
||||
<sup>4</sup> 颜色使用16进制RGB字符串格式描述,例如 '#FA3D86'。
|
||||
<sup>5</sup> image和mask这两项必须是相同的尺寸。
|
||||
<sup>4</sup> 颜色使用16进制RGB字符串格式描述,例如 '#FA3D86'。
|
||||
|
||||
<sup>5</sup> image和mask这两项必须是相同的尺寸。
|
||||
|
||||
|
||||
## 声明
|
||||
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 233 KiB |
@@ -0,0 +1,241 @@
|
||||
from .imagefunc import *
|
||||
from .segment_anything_func import *
|
||||
|
||||
NODE_NAME = 'ImageAutoCropV2'
|
||||
|
||||
SAM_MODEL = None
|
||||
DINO_MODEL = None
|
||||
previous_sam_model = ""
|
||||
previous_dino_model = ""
|
||||
|
||||
class ImageAutoCropV2:
|
||||
|
||||
def __init__(self):
|
||||
pass
|
||||
|
||||
@classmethod
|
||||
def INPUT_TYPES(self):
|
||||
matting_method_list = ['RMBG 1.4', 'SegmentAnything']
|
||||
detect_mode = ['min_bounding_rect', 'max_inscribed_rect', 'mask_area']
|
||||
ratio_list = ['1:1', '3:2', '4:3', '16:9', '2:3', '3:4', '9:16', 'custom', 'detect_mask', 'original']
|
||||
scale_to_side_list = ['None', 'longest', 'shortest']
|
||||
return {
|
||||
"required": {
|
||||
"image": ("IMAGE", ), #
|
||||
"fill_background": ("BOOLEAN", {"default": True}), # 是否填充背景
|
||||
"background_color": ("STRING", {"default": "#FFFFFF"}), # 背景颜色
|
||||
"aspect_ratio": (ratio_list,),
|
||||
"proportional_width": ("INT", {"default": 1, "min": 1, "max": 999, "step": 1}),
|
||||
"proportional_height": ("INT", {"default": 1, "min": 1, "max": 999, "step": 1}),
|
||||
"scale_to_side": (scale_to_side_list,), # 是否按长边缩放
|
||||
"scale_to_length": ("INT", {"default": 1024, "min": 4, "max": 999999, "step": 1}),
|
||||
"detect": (detect_mode,),
|
||||
"border_reserve": ("INT", {"default": 100, "min": -9999, "max": 9999, "step": 1}),
|
||||
"ultra_detail_range": ("INT", {"default": 0, "min": 0, "max": 256, "step": 1}),
|
||||
"matting_method": (matting_method_list,),
|
||||
"sam_model": (list_sam_model(),),
|
||||
"grounding_dino_model": (list_groundingdino_model(),),
|
||||
"sam_threshold": ("FLOAT", {"default": 0.3, "min": 0, "max": 1.0, "step": 0.01}),
|
||||
"sam_prompt": ("STRING", {"default": "subject"}),
|
||||
},
|
||||
"optional": {
|
||||
"mask": ("MASK",), #
|
||||
}
|
||||
}
|
||||
|
||||
RETURN_TYPES = ("IMAGE", "IMAGE", "MASK",)
|
||||
RETURN_NAMES = ("cropped_image", "box_preview", "cropped_mask",)
|
||||
FUNCTION = 'image_auto_crop_v2'
|
||||
CATEGORY = '😺dzNodes/LayerUtility'
|
||||
OUTPUT_NODE = True
|
||||
|
||||
def image_auto_crop_v2(self, image, fill_background, background_color, aspect_ratio,
|
||||
proportional_width, proportional_height,
|
||||
scale_to_side, scale_to_length, detect, border_reserve,
|
||||
ultra_detail_range, matting_method,
|
||||
sam_model, grounding_dino_model, sam_threshold, sam_prompt,
|
||||
mask=None,
|
||||
):
|
||||
|
||||
ret_images = []
|
||||
ret_box_previews = []
|
||||
ret_masks = []
|
||||
input_images = []
|
||||
input_masks = []
|
||||
crop_boxs = []
|
||||
|
||||
global SAM_MODEL
|
||||
global DINO_MODEL
|
||||
global previous_sam_model
|
||||
global previous_dino_model
|
||||
|
||||
for l in image:
|
||||
input_images.append(torch.unsqueeze(l, 0))
|
||||
m = tensor2pil(l)
|
||||
if m.mode == 'RGBA':
|
||||
input_masks.append(m.split()[-1])
|
||||
if mask is not None:
|
||||
if mask.dim() == 2:
|
||||
mask = torch.unsqueeze(mask, 0)
|
||||
input_masks = []
|
||||
for m in mask:
|
||||
input_masks.append(tensor2pil(torch.unsqueeze(m, 0)).convert('L'))
|
||||
|
||||
if len(input_masks) > 0 and len(input_masks) != len(input_images):
|
||||
input_masks = []
|
||||
log(f"Warning, {NODE_NAME} unable align alpha to image, drop it.", message_type='warning')
|
||||
|
||||
fit = 'letterbox'
|
||||
if aspect_ratio == 'custom':
|
||||
ratio = proportional_width / proportional_height
|
||||
elif aspect_ratio == 'original':
|
||||
_image = tensor2pil(input_images[0])
|
||||
ratio = _image.width / _image.height
|
||||
elif aspect_ratio == 'detect_mask':
|
||||
ratio = 0
|
||||
fit = 'fill'
|
||||
else:
|
||||
s = aspect_ratio.split(":")
|
||||
ratio = int(s[0]) / int(s[1])
|
||||
|
||||
for i in range(len(input_images)):
|
||||
_image = tensor2pil(input_images[i]).convert('RGB')
|
||||
|
||||
if len(input_masks) > 0:
|
||||
_mask = input_masks[i]
|
||||
else:
|
||||
if matting_method == 'SegmentAnything':
|
||||
if previous_sam_model != sam_model:
|
||||
SAM_MODEL = load_sam_model(sam_model)
|
||||
previous_sam_model = sam_model
|
||||
if previous_dino_model != grounding_dino_model:
|
||||
DINO_MODEL = load_groundingdino_model(grounding_dino_model)
|
||||
previous_dino_model = grounding_dino_model
|
||||
item = _image.convert('RGBA')
|
||||
boxes = groundingdino_predict(DINO_MODEL, item, sam_prompt, sam_threshold)
|
||||
(_, _mask) = sam_segment(SAM_MODEL, item, boxes)
|
||||
_mask = mask2image(_mask[0])
|
||||
else:
|
||||
_mask = RMBG(_image)
|
||||
if ultra_detail_range:
|
||||
_mask = tensor2pil(mask_edge_detail(input_images[i], pil2tensor(_mask), ultra_detail_range, 0.01, 0.99))
|
||||
bluredmask = gaussian_blur(_mask, 20).convert('L')
|
||||
x = 0
|
||||
y = 0
|
||||
width = 0
|
||||
height = 0
|
||||
x_offset = 0
|
||||
y_offset = 0
|
||||
if detect == "min_bounding_rect":
|
||||
(x, y, width, height) = min_bounding_rect(bluredmask)
|
||||
elif detect == "max_inscribed_rect":
|
||||
(x, y, width, height) = max_inscribed_rect(bluredmask)
|
||||
else:
|
||||
(x, y, width, height) = mask_area(bluredmask)
|
||||
|
||||
canvas_width, canvas_height = _image.size
|
||||
|
||||
x1 = x - border_reserve
|
||||
y1 = y - border_reserve
|
||||
x2 = x + width + border_reserve
|
||||
y2 = y + height + border_reserve
|
||||
|
||||
if x1 < 0:
|
||||
if fill_background:
|
||||
canvas_width -= x1
|
||||
x_offset = -x1
|
||||
else:
|
||||
x1 = 0
|
||||
if y1 < 0:
|
||||
if fill_background:
|
||||
canvas_height -= y1
|
||||
y_offset = -y1
|
||||
else:
|
||||
y1 = 0
|
||||
if x2 > _image.width:
|
||||
if fill_background:
|
||||
canvas_width += x2 - _image.width
|
||||
else:
|
||||
x2 = _image.width
|
||||
if y2 > _image.height:
|
||||
if fill_background:
|
||||
canvas_height += y2 - _image.height
|
||||
else:
|
||||
y2 = _image.height
|
||||
|
||||
if fill_background:
|
||||
crop_box = (x1 + x_offset, y1 + y_offset, width + border_reserve*2, height + border_reserve*2)
|
||||
else:
|
||||
crop_box = (x1, y1, x2 - x1, y2 - y1)
|
||||
crop_boxs.append(crop_box)
|
||||
if len(crop_boxs) > 0: # 批量图强制使用同一尺寸
|
||||
crop_box = crop_boxs[0]
|
||||
|
||||
orig_width = crop_box[2]
|
||||
orig_height = crop_box[3]
|
||||
if aspect_ratio == 'detect_mask':
|
||||
ratio = orig_width / orig_height
|
||||
|
||||
# calculate target width and height
|
||||
if orig_width > orig_height:
|
||||
if scale_to_side == 'longest':
|
||||
target_width = scale_to_length
|
||||
target_height = int(target_width / ratio)
|
||||
elif scale_to_side == 'shortest':
|
||||
target_height = scale_to_length
|
||||
target_width = int(target_height * ratio)
|
||||
else:
|
||||
target_width = orig_width
|
||||
target_height = int(target_width / ratio)
|
||||
else:
|
||||
if scale_to_side == 'longest':
|
||||
target_height = scale_to_length
|
||||
target_width = int(target_height * ratio)
|
||||
elif scale_to_side == 'shortest':
|
||||
target_width = scale_to_length
|
||||
target_height = int(target_width / ratio)
|
||||
else:
|
||||
target_height = orig_height
|
||||
target_width = int(target_height * ratio)
|
||||
|
||||
_canvas = Image.new('RGB', size=(canvas_width, canvas_height), color=background_color)
|
||||
_mask_canvas = Image.new('L', size=(canvas_width, canvas_height), color='black')
|
||||
if ultra_detail_range:
|
||||
_image = pixel_spread(_image, _mask)
|
||||
if fill_background:
|
||||
_canvas.paste(_image, box=(x_offset, y_offset), mask=_mask.convert('L'))
|
||||
else:
|
||||
_canvas.paste(_image, box=(x_offset, y_offset))
|
||||
_mask_canvas.paste(_mask, box=(x_offset, y_offset))
|
||||
preview_image = Image.new('RGB', size=(canvas_width, canvas_height), color='gray')
|
||||
preview_image.paste(_mask, box=(x_offset, y_offset))
|
||||
preview_image = draw_rect(preview_image,
|
||||
crop_box[0], crop_box[1], crop_box[2], crop_box[3],
|
||||
line_color="#F00000", line_width=(canvas_width + canvas_height)//200)
|
||||
|
||||
ret_image = _canvas.crop((crop_box[0], crop_box[1], crop_box[0]+crop_box[2], crop_box[1]+crop_box[3]))
|
||||
ret_image = fit_resize_image(ret_image, target_width, target_height,
|
||||
fit=fit, resize_sampler=Image.LANCZOS,
|
||||
background_color=background_color)
|
||||
ret_mask = _mask_canvas.crop((crop_box[0], crop_box[1], crop_box[0]+crop_box[2], crop_box[1]+crop_box[3]))
|
||||
ret_mask = fit_resize_image(ret_mask, target_width, target_height,
|
||||
fit=fit, resize_sampler=Image.LANCZOS,
|
||||
background_color="#000000")
|
||||
ret_images.append(pil2tensor(ret_image))
|
||||
ret_box_previews.append(pil2tensor(preview_image))
|
||||
ret_masks.append(image2mask(ret_mask))
|
||||
|
||||
log(f"{NODE_NAME} Processed {len(ret_images)} image(s).", message_type='finish')
|
||||
return (torch.cat(ret_images, dim=0),
|
||||
torch.cat(ret_box_previews, dim=0),
|
||||
torch.cat(ret_masks, dim=0),
|
||||
)
|
||||
|
||||
|
||||
NODE_CLASS_MAPPINGS = {
|
||||
"LayerUtility: ImageAutoCrop V2": ImageAutoCropV2
|
||||
}
|
||||
|
||||
NODE_DISPLAY_NAME_MAPPINGS = {
|
||||
"LayerUtility: ImageAutoCrop V2": "LayerUtility: ImageAutoCrop V2"
|
||||
}
|
||||
+4
-6
@@ -49,17 +49,15 @@ def log(message:str, message_type:str='info'):
|
||||
try:
|
||||
from cv2.ximgproc import guidedFilter
|
||||
except ImportError as e:
|
||||
print(e)
|
||||
log(f'Dependency package error, unable import "cv2.ximgproc".'
|
||||
f'\nPlease REINSTALL package "opencv-contrib-python".'
|
||||
f'\nFor detail refer to \033[4mhttps://github.com/chflame163/ComfyUI_LayerStyle/issues/5\033[0m',
|
||||
message_type='error')
|
||||
# print(e)
|
||||
log(f"Cannot import name 'guidedFilter' from 'cv2.ximgproc'"
|
||||
f"\nA few nodes cannot works properly, while most nodes are not affected. Please REINSTALL package 'opencv-contrib-python'."
|
||||
f"\nFor detail refer to \033[4mhttps://github.com/chflame163/ComfyUI_LayerStyle/issues/5\033[0m")
|
||||
|
||||
|
||||
'''pickle'''
|
||||
|
||||
|
||||
|
||||
def read_image(filename:str) -> Image:
|
||||
return Image.open(filename)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user