diff --git a/README.md b/README.md
index 84cf855..01fd2f3 100644
--- a/README.md
+++ b/README.md
@@ -145,6 +145,7 @@ Please try downgrading the ```protobuf``` dependency package to 3.20.3, or set e
**If the dependency package error after updating, please double clicking ```repair_dependency.bat``` (for Official ComfyUI Protable) or ```repair_dependency_aki.bat``` (for ComfyUI-aki-v1.x) in the plugin folder to reinstall the dependency packages.
+* Commit [DrawBBOXMaskV2](#DrawBBOXMaskV2) node, can draw rounded rectangle masks.
* Commit [SmolLM2](#SmolLM2), [SmolVLM](#SmolVLM), [LoadSmolLM2Model](#LoadSmolLM2Model) and [LoadSmolVLMModel](#LoadSmolVLMModel) nodes, use SMOL model for text inference and image recognition.
download the model file from [BaiduNetdisk](https://pan.baidu.com/s/1_jeNosYdDqqHkzpnSNGfDQ?pwd=to5b) or [huggingface](https://huggingface.co/chflame163/ComfyUI_LayerStyle/tree/main/ComfyUI/models/smol) and copy to ```ComfyUI/models/smol``` folder.
* Florence2 add support [gokaygokay/Florence-2-Flux-Large](https://huggingface.co/gokaygokay/Florence-2-Flux-Large) and [gokaygokay/Florence-2-Flux](https://huggingface.co/gokaygokay/Florence-2-Flux) models,
@@ -153,7 +154,6 @@ download Florence-2-Flux-Large and Florence-2-Flux folder from [BaiduNetdisk](ht
* Strip some nodes from [ComfyUI Layer Style](https://github.com/chflame163/ComfyUI_LayerStyle) to this repository.
-
## Description
### QWenImage2Prompt
@@ -798,7 +798,8 @@ Node Options:
* bbox_select: Select the input box data. There are three options: "all" to select all, "first" to select the box with the highest confidence, and "by_index" to specify the index of the box.
* select_index: This option is valid when bbox_delect is 'by_index'. 0 is the first one. Multiple values can be entered, separated by any non numeric character, including but not limited to commas, periods, semicolons, spaces or letters, and even Chinese.
-### ObjectDetectorYOLOWorld(Obsoleted. If you want to continue using it, you need to manually install the dependency package)
+### ObjectDetectorYOLOWorld
+#### (Obsoleted. If you want to continue using it, you need to manually install the dependency package)
Due to potential installation issues with dependency packages, this node has been obsoleted. To use, please manually install the following dependency packages:
```
pip install inference-cli>=0.13.0
@@ -872,6 +873,15 @@ Node Options:
* grow_left: Each BBox expands to the left as a percentage of its width, positive values expand to the left and negative values expand to the right.
* grow_right: Each BBox expands to the right as a percentage of its width, positive values indicate expansion to the right and negative values indicate expansion to the left.
+### DrawBBoxMaskV2
+Add rounded rectangle drawing to the [DrawBBoxMask](#DrawBBoxMask) node.
+
+
+Add Options:
+
+* rounded_rect_radius: Rounded rectangle radius. The range is 0-100, and the larger the value, the more pronounced the rounded corners.
+* anti_aliasing: Anti aliasing, ranging from 0-16, with larger values indicating less pronounced aliasing. Excessive values will significantly reduce the processing speed of nodes.
+
### EVF-SAMUltra
This node is implementation of [EVF-SAM](https://github.com/hustvl/EVF-SAM) in ComfyUI.
diff --git a/README_CN.MD b/README_CN.MD
index 46362f8..c8fe801 100644
--- a/README_CN.MD
+++ b/README_CN.MD
@@ -121,6 +121,7 @@ If this call came from a _pb2.py file, your generated code is out of date and mu
## 更新说明
**如果本插件更新后出现依赖包错误,请双击运行插件目录下的```install_requirements.bat```(官方便携包),或 ```install_requirements_aki.bat```(秋叶整合包) 重新安装依赖包。
+* 添加 [DrawBBOXMaskV2](#DrawBBOXMaskV2) 节点,可绘制圆角矩形遮罩。
* 添加 [SmolLM2](#SmolLM2), [SmolVLM](#SmolVLM), [LoadSmolLM2Model](#LoadSmolLM2Model) 和 [LoadSmolVLMModel](#LoadSmolVLMModel) 节点,使用smol轻量级本地模型进行文本推理和图片识别。
从[百度网盘](https://pan.baidu.com/s/1_jeNosYdDqqHkzpnSNGfDQ?pwd=to5b) 或 [huggingface](https://huggingface.co/chflame163/ComfyUI_LayerStyle/tree/main/ComfyUI/models/smol) 下载模型文件至```ComfyUI/models/smol```文件夹。
* Florence2 节点支持[gokaygokay/Florence-2-Flux-Large](https://huggingface.co/gokaygokay/Florence-2-Flux-Large) 和 [gokaygokay/Florence-2-Flux](https://huggingface.co/gokaygokay/Florence-2-Flux)模型。
@@ -794,6 +795,15 @@ pip install inference-gpu[yolo-world]>=0.13.0
* grow_left: 每个识别框向左扩展范围,为识别框宽度的百分比。正值为向左扩展,负值为向右扩展。
* grow_right: 每个识别框向右扩展范围,为识别框宽度的百分比。正值为向右扩展,负值为向左扩展。
+### DrawBBoxMaskV2
+在[DrawBBoxMask](#DrawBBoxMask) 节点基础上增加圆角矩形绘制。
+
+
+新增选项:
+
+* rounded_rect_radius: 圆角矩形半径,范围0-100,数值越大圆角半径越大。
+* anti_alias: 抗锯齿,范围从0-16,数值越大,锯齿越不明显。过高的数值将显著降低节点的处理速度。
+
### EVF-SAMUltra
本节点是[EVF-SAM](https://github.com/hustvl/EVF-SAM)在ComfyUI中的实现。
diff --git a/image/draw_bbox_mask_v2_example.jpg b/image/draw_bbox_mask_v2_example.jpg
new file mode 100644
index 0000000..a6ba323
Binary files /dev/null and b/image/draw_bbox_mask_v2_example.jpg differ
diff --git a/image/draw_bbox_mask_v2_node.jpg b/image/draw_bbox_mask_v2_node.jpg
new file mode 100644
index 0000000..db0fa0f
Binary files /dev/null and b/image/draw_bbox_mask_v2_node.jpg differ
diff --git a/py/imagefunc.py b/py/imagefunc.py
index bff6d02..07af3b5 100644
--- a/py/imagefunc.py
+++ b/py/imagefunc.py
@@ -748,6 +748,31 @@ def gradient(start_color_inhex:str, end_color_inhex:str, width:int, height:int,
ret_image = ret_image.resize((width, height))
return ret_image
+def draw_rounded_rectangle(image:Image, radius:int, bboxes:list, scale_factor:int=2, color:str="white") -> Image:
+ """
+ 绘制圆角矩形图像。
+ image:输入图片
+ radius: 半径,100为纯椭圆
+ bboxes: (x1,y1,x2,y2)列表
+ scale_factor: 放大倍数
+ :return: 绘制好的pillow图像
+ """
+ if scale_factor < 1 : scale_factor = 1
+
+ img = image.resize((image.width * scale_factor, image.height * scale_factor), Image.LANCZOS)
+ draw = ImageDraw.Draw(img)
+
+ for (x1, y1, x2, y2) in bboxes:
+ r = radius * min(x2-x1, y2-y1) * 0.005
+ x1, y1, x2, y2 = x1 * scale_factor, y1 * scale_factor, x2 * scale_factor, y2 * scale_factor
+ # 计算圆角矩形的四个角的圆弧
+ draw.rounded_rectangle([x1, y1, x2, y2], radius=r * scale_factor, fill=color)
+
+ img = img.filter(ImageFilter.SMOOTH_MORE)
+ img = img.resize((image.width, image.height), Image.LANCZOS)
+
+ return img
+
def draw_rect(image:Image, x:int, y:int, width:int, height:int, line_color:str, line_width:int,
box_color:str=None) -> Image:
draw = ImageDraw.Draw(image)
diff --git a/py/object_detector.py b/py/object_detector.py
index 40d98d1..e81e022 100644
--- a/py/object_detector.py
+++ b/py/object_detector.py
@@ -1,5 +1,5 @@
# layerstyle advance
-
+import folder_paths
from .imagefunc import *
select_list = ["all", "first", "by_index"]
@@ -179,8 +179,8 @@ class LS_OBJECT_DETECTOR_FL2:
y1_c = height
x2_c = y2_c = 0
for polygon in F_BBOXES["polygons"][0]:
- if len(_polygon) < 3:
- print('Invalid polygon:', _polygon)
+ if len(polygon) < 3:
+ print('Invalid polygon:', polygon)
continue
x1_c = min(x1_c, int(min(polygon[0::2])))
x2_c = max(x2_c, int(max(polygon[0::2])))
@@ -347,8 +347,11 @@ class LS_OBJECT_DETECTOR_YOLOWORLD:
ret_previews = []
ret_bboxes = []
- import supervision as sv
-
+ try:
+ import supervision as sv
+ except ImportError as e:
+ log(f"{self.NODE_NAME}: {e}", message_type='warning')
+ return None
model=self.load_yolo_world_model(yolo_world_model, prompt)
for i in image:
@@ -393,14 +396,17 @@ class LS_OBJECT_DETECTOR_YOLOWORLD:
return [category.strip().lower() for category in categories.split(',')]
def load_yolo_world_model(self,model_id: str, categories: str) -> List[torch.nn.Module]:
- from inference.models import YOLOWorld as YOLOWorldImpl
+ try:
+ from inference.models import YOLOWorld as YOLOWorldImpl
+ except ImportError as e:
+ log(f"{self.NODE_NAME}: {e}", message_type='warning')
+ return None
model = YOLOWorldImpl(model_id=model_id)
categories = self.process_categories(categories)
model.set_classes(categories)
return model
-
class LS_DrawBBoxMask:
def __init__(self):
@@ -467,8 +473,85 @@ class LS_DrawBBoxMask:
return (torch.cat(ret_masks, dim=0),)
+class LS_DrawBBoxMaskV2:
+
+ def __init__(self):
+ self.NODE_NAME = 'Draw BBOX Mask V2'
+ pass
+
+ @classmethod
+ def INPUT_TYPES(cls):
+ return {
+ "required": {
+ "image": ("IMAGE",),
+ "bboxes": ("BBOXES",),
+ "grow_top": ("FLOAT", {"default": 0, "min": -10, "max": 10, "step": 0.01}), # bbox向上扩展,按高度比例
+ "grow_bottom": ("FLOAT", {"default": 0, "min": -10, "max": 10, "step": 0.01}),
+ "grow_left": ("FLOAT", {"default": 0, "min": -10, "max": 10, "step": 0.01}),
+ "grow_right": ("FLOAT", {"default": 0, "min": -10, "max": 10, "step": 0.01}),
+ "rounded_rect_radius": ("INT", {"default": 50, "min": 0, "max": 100, "step": 1}),
+ "anti_aliasing": ("INT", {"default": 2, "min": 0, "max": 16, "step": 1}),
+ },
+ "optional": {
+ }
+ }
+
+ RETURN_TYPES = ("MASK",)
+ RETURN_NAMES = ("mask",)
+ FUNCTION = 'draw_bbox_mask_v2'
+ CATEGORY = '😺dzNodes/LayerMask'
+
+ def draw_bbox_mask_v2(self, image, bboxes, grow_top, grow_bottom, grow_left, grow_right,
+ rounded_rect_radius, anti_aliasing):
+
+ ret_masks = []
+ for index in range(len(image)):
+ img = tensor2pil(image[index].unsqueeze(0))
+ mask = Image.new("L", img.size, color='black')
+ bboxes_i = bboxes[index]
+ if grow_top or grow_bottom or grow_left or grow_right:
+ new_bboxes_i = []
+ for bbox in bboxes_i:
+ try:
+ if len(bbox) == 0:
+ continue
+ else:
+ x1, y1, x2, y2 = bbox
+ except ValueError:
+ if len(bbox) == 0:
+ continue
+ else:
+ x1, y1, x2, y2 = bbox[index]
+ w = x2 - x1
+ h = y2 - y1
+ if grow_top:
+ y1 = int(y1 - h * grow_top)
+ if grow_bottom:
+ y2 = int(y2 + h * grow_bottom)
+ if grow_left:
+ x1 = int(x1 - w * grow_left)
+ if grow_right:
+ x2 = int(x2 + w * grow_right)
+ if y1 > y2:
+ y1, y2 = y2, y1
+ if x1 > x2:
+ x1, x2 = x2, x1
+ if y2 - y1 < 1:
+ y2 += 1
+ if x2 - x1 < 1:
+ x2 += 1
+ new_bboxes_i.append((x1, y1, x2, y2))
+ bboxes_i = new_bboxes_i
+ mask = draw_rounded_rectangle(mask, rounded_rect_radius, bboxes_i, anti_aliasing)
+ ret_masks.append(pil2tensor(mask))
+
+ log(f"{self.NODE_NAME} Processed {len(ret_masks)} mask(s).", message_type='finish')
+ return (torch.cat(ret_masks, dim=0),)
+
+
NODE_CLASS_MAPPINGS = {
"LayerMask: BBoxJoin": LS_BBOXES_JOIN,
+ "LayerMask: DrawBBoxMaskV2": LS_DrawBBoxMaskV2,
"LayerMask: DrawBBoxMask": LS_DrawBBoxMask,
"LayerMask: ObjectDetectorFL2": LS_OBJECT_DETECTOR_FL2,
"LayerMask: ObjectDetectorMask": LS_OBJECT_DETECTOR_MASK,
@@ -478,6 +561,7 @@ NODE_CLASS_MAPPINGS = {
NODE_DISPLAY_NAME_MAPPINGS = {
"LayerMask: BBoxJoin": "LayerMask: BBox Join(Advance)",
+ "LayerMask: DrawBBoxMaskV2": "LayerMask: Draw BBox Mask V2(Advance)",
"LayerMask: DrawBBoxMask": "LayerMask: Draw BBox Mask(Advance)",
"LayerMask: ObjectDetectorFL2": "LayerMask: Object Detector Florence2(Advance)",
"LayerMask: ObjectDetectorMask": "LayerMask: Object Detector Mask(Advance)",
diff --git a/pyproject.toml b/pyproject.toml
index 499bb22..773560d 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -1,7 +1,7 @@
[project]
name = "comfyui_layerstyle_advance"
description = "The nodes detached from ComfyUI Layer Style are mainly those with complex requirements for dependency packages."
-version = "2.0.4"
+version = "2.0.5"
license = "MIT"
dependencies = ["numpy", "matplotlib", "scikit_image", "scikit_learn", "opencv-contrib-python", "pymatting", "timm", "blend_modes", "transformers", "diffusers", "loguru", "colour-science", "huggingface_hub", "segment_anything", "addict", "omegaconf", "yapf", "wget", "iopath", "mediapipe", "typer_config", "fastapi", "rich", "google-generativeai", "ultralytics", "transparent-background", "accelerate", "onnxruntime", "bitsandbytes", "peft", "protobuf", "hydra-core", "blind-watermark", "qrcode", "pyzbar", "psd-tools", "wandb"]
diff --git a/workflow/draw_bbox_mask_v2_example.json b/workflow/draw_bbox_mask_v2_example.json
new file mode 100644
index 0000000..f98023c
--- /dev/null
+++ b/workflow/draw_bbox_mask_v2_example.json
@@ -0,0 +1,393 @@
+{
+ "last_node_id": 10,
+ "last_link_id": 10,
+ "nodes": [
+ {
+ "id": 5,
+ "type": "LayerMask: LoadFlorence2Model",
+ "pos": [
+ -3210.825927734375,
+ 1715.41015625
+ ],
+ "size": [
+ 368.73028564453125,
+ 58
+ ],
+ "flags": {},
+ "order": 0,
+ "mode": 0,
+ "inputs": [],
+ "outputs": [
+ {
+ "name": "florence2_model",
+ "type": "FLORENCE2",
+ "links": [
+ 3
+ ]
+ }
+ ],
+ "properties": {
+ "Node name for S&R": "LayerMask: LoadFlorence2Model"
+ },
+ "widgets_values": [
+ "base"
+ ],
+ "color": "rgba(27, 80, 119, 0.7)"
+ },
+ {
+ "id": 4,
+ "type": "LayerMask: ObjectDetectorFL2",
+ "pos": [
+ -3201.633056640625,
+ 1844.1142578125
+ ],
+ "size": [
+ 378,
+ 150
+ ],
+ "flags": {},
+ "order": 2,
+ "mode": 0,
+ "inputs": [
+ {
+ "name": "image",
+ "type": "IMAGE",
+ "link": 4
+ },
+ {
+ "name": "florence2_model",
+ "type": "FLORENCE2",
+ "link": 3
+ }
+ ],
+ "outputs": [
+ {
+ "name": "bboxes",
+ "type": "BBOXES",
+ "links": [
+ 6
+ ],
+ "slot_index": 0
+ },
+ {
+ "name": "preview",
+ "type": "IMAGE",
+ "links": [
+ 5
+ ],
+ "slot_index": 1
+ }
+ ],
+ "properties": {
+ "Node name for S&R": "LayerMask: ObjectDetectorFL2"
+ },
+ "widgets_values": [
+ "car",
+ "left_to_right",
+ "all",
+ "0,"
+ ],
+ "color": "rgba(27, 80, 119, 0.7)"
+ },
+ {
+ "id": 6,
+ "type": "PreviewImage",
+ "pos": [
+ -2680.909912109375,
+ 1622.822998046875
+ ],
+ "size": [
+ 373.50555419921875,
+ 261.75958251953125
+ ],
+ "flags": {},
+ "order": 4,
+ "mode": 0,
+ "inputs": [
+ {
+ "name": "images",
+ "type": "IMAGE",
+ "link": 5
+ }
+ ],
+ "outputs": [],
+ "properties": {
+ "Node name for S&R": "PreviewImage"
+ },
+ "widgets_values": []
+ },
+ {
+ "id": 7,
+ "type": "LayerMask: MaskPreview",
+ "pos": [
+ -2269.84716796875,
+ 1622.8236083984375
+ ],
+ "size": [
+ 354.0278015136719,
+ 257.8196105957031
+ ],
+ "flags": {},
+ "order": 5,
+ "mode": 0,
+ "inputs": [
+ {
+ "name": "mask",
+ "type": "MASK",
+ "link": 7
+ }
+ ],
+ "outputs": [],
+ "properties": {
+ "Node name for S&R": "LayerMask: MaskPreview"
+ },
+ "widgets_values": [],
+ "color": "rgba(27, 80, 119, 0.7)"
+ },
+ {
+ "id": 2,
+ "type": "LoadImage",
+ "pos": [
+ -3583.1455078125,
+ 2019.438232421875
+ ],
+ "size": [
+ 315,
+ 314
+ ],
+ "flags": {},
+ "order": 1,
+ "mode": 0,
+ "inputs": [],
+ "outputs": [
+ {
+ "name": "IMAGE",
+ "type": "IMAGE",
+ "links": [
+ 1,
+ 4,
+ 8
+ ],
+ "slot_index": 0
+ },
+ {
+ "name": "MASK",
+ "type": "MASK",
+ "links": null
+ }
+ ],
+ "properties": {
+ "Node name for S&R": "LoadImage"
+ },
+ "widgets_values": [
+ "1280x720car.jpg",
+ "image"
+ ]
+ },
+ {
+ "id": 9,
+ "type": "PreviewImage",
+ "pos": [
+ -1886.3648681640625,
+ 1981.2880859375
+ ],
+ "size": [
+ 363.8528137207031,
+ 311.7305603027344
+ ],
+ "flags": {},
+ "order": 7,
+ "mode": 0,
+ "inputs": [
+ {
+ "name": "images",
+ "type": "IMAGE",
+ "link": 10
+ }
+ ],
+ "outputs": [],
+ "properties": {
+ "Node name for S&R": "PreviewImage"
+ },
+ "widgets_values": []
+ },
+ {
+ "id": 8,
+ "type": "LayerUtility: ImageCombineAlpha",
+ "pos": [
+ -2273.5244140625,
+ 2261.54541015625
+ ],
+ "size": [
+ 295.9768371582031,
+ 46
+ ],
+ "flags": {},
+ "order": 6,
+ "mode": 0,
+ "inputs": [
+ {
+ "name": "RGB_image",
+ "type": "IMAGE",
+ "link": 8
+ },
+ {
+ "name": "mask",
+ "type": "MASK",
+ "link": 9
+ }
+ ],
+ "outputs": [
+ {
+ "name": "RGBA_image",
+ "type": "IMAGE",
+ "links": [
+ 10
+ ],
+ "slot_index": 0
+ }
+ ],
+ "properties": {
+ "Node name for S&R": "LayerUtility: ImageCombineAlpha"
+ },
+ "widgets_values": [],
+ "color": "rgba(38, 73, 116, 0.7)"
+ },
+ {
+ "id": 1,
+ "type": "LayerMask: DrawBBoxMaskV2",
+ "pos": [
+ -2681.6484375,
+ 2029.784423828125
+ ],
+ "size": [
+ 349.97314453125,
+ 198
+ ],
+ "flags": {},
+ "order": 3,
+ "mode": 0,
+ "inputs": [
+ {
+ "name": "image",
+ "type": "IMAGE",
+ "link": 1
+ },
+ {
+ "name": "bboxes",
+ "type": "BBOXES",
+ "link": 6
+ }
+ ],
+ "outputs": [
+ {
+ "name": "mask",
+ "type": "MASK",
+ "links": [
+ 7,
+ 9
+ ],
+ "slot_index": 0
+ }
+ ],
+ "properties": {
+ "Node name for S&R": "LayerMask: DrawBBoxMaskV2"
+ },
+ "widgets_values": [
+ 0.11,
+ 0.11,
+ 0.06,
+ 0.06,
+ 70,
+ 2
+ ],
+ "color": "rgba(27, 80, 119, 0.7)"
+ }
+ ],
+ "links": [
+ [
+ 1,
+ 2,
+ 0,
+ 1,
+ 0,
+ "IMAGE"
+ ],
+ [
+ 3,
+ 5,
+ 0,
+ 4,
+ 1,
+ "FLORENCE2"
+ ],
+ [
+ 4,
+ 2,
+ 0,
+ 4,
+ 0,
+ "IMAGE"
+ ],
+ [
+ 5,
+ 4,
+ 1,
+ 6,
+ 0,
+ "IMAGE"
+ ],
+ [
+ 6,
+ 4,
+ 0,
+ 1,
+ 1,
+ "BBOXES"
+ ],
+ [
+ 7,
+ 1,
+ 0,
+ 7,
+ 0,
+ "MASK"
+ ],
+ [
+ 8,
+ 2,
+ 0,
+ 8,
+ 0,
+ "IMAGE"
+ ],
+ [
+ 9,
+ 1,
+ 0,
+ 8,
+ 1,
+ "MASK"
+ ],
+ [
+ 10,
+ 8,
+ 0,
+ 9,
+ 0,
+ "IMAGE"
+ ]
+ ],
+ "groups": [],
+ "config": {},
+ "extra": {
+ "ds": {
+ "scale": 0.7627768444385546,
+ "offset": [
+ 4132.421086528808,
+ -1131.7549496286297
+ ]
+ }
+ },
+ "version": 0.4
+}
\ No newline at end of file