add some rle<->mask nodes
This commit is contained in:
@@ -3,7 +3,7 @@
|
||||
|
||||
转成base64的节点都是输出节点,websocket消息中会包含base64Images和base64Type属性(具体格式请查看ImageNode.py中的ImageToBase64Advanced类源代码,或者自己搭建简单流程运行在浏览器开发者工具-->网络中查看)
|
||||
|
||||
Tips: base64格式字符串比较长,会导致界面卡顿,接口请求带宽可能也会有瓶颈,条件允许可以把图片上传到OSS服务器得到URL,然后用LoadImageFromUrl加载,由于无相关OSS账号,上传OSS节点需自行编写,暂不支持。
|
||||
Tips: base64格式字符串比较长,会导致界面卡顿,接口请求带宽可能也会有瓶颈,条件允许可以把图片上传到OSS服务器得到URL,然后用LoadImageFromURL加载,由于无相关OSS账号,上传OSS节点需自行编写,暂不支持。
|
||||
|
||||
## 安装
|
||||
- 方式1:通过ComfyUI-Manager安装
|
||||
@@ -22,62 +22,67 @@ Tips: base64格式字符串比较长,会导致界面卡顿,接口请求带
|
||||
```
|
||||
|
||||
## 节点
|
||||
| 名称 | 说明 |
|
||||
|------------------------------|------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||
| LoadImageFromURL | 从网络地址加载图片,一行代表一个图片 |
|
||||
| LoadMaskFromURL | 从网络地址加载遮罩,一行代表一个 |
|
||||
| Base64ToImage | 把图片base64字符串转成图片 |
|
||||
| Base64ToMask | 把遮罩图片base64字符串转成遮罩 |
|
||||
| ImageToBase64Advanced | 把图片转成base64字符串, 可以选择图片类型(image, mask) ,方便接口调用判断 |
|
||||
| ImageToBase64 | 把图片转成base64字符串(imageType=["image"]) |
|
||||
| MaskToBase64Image | 把遮罩转成对应图片的base64字符串(imageType=["mask"]) |
|
||||
| MaskImageToBase64 | 把遮罩图片转成base64字符串(imageType=["mask"]) |
|
||||
| LoadImageToBase64 | 加载本地图片转成base64字符串 |
|
||||
| SamAutoMaskSEGS | 得到图片所有语义分割的coco或uncompress_rle格式。<br/>配合ComfyUI-Impact-Pack的SAMLoader或comfyui_segment_anything的SAMModelLoader。<br/>但是如果使用hq模型,必须使用comfyui_segment_anything |
|
||||
| InsightFaceBBOXDetect | 为图片中的人脸添加序号和区域框 |
|
||||
| ColorPicker | 颜色选择器 |
|
||||
| IntToNumber | 整型转数字 |
|
||||
| StringToList | 字符串转列表 |
|
||||
| IntToList | 整型转列表 |
|
||||
| ListMerge | 列表合并 |
|
||||
| JoinList | 列表根据指定分隔符连接 |
|
||||
| ShowString | 显示字符串(可指定消息中key值) |
|
||||
| ShowInt | 显示整型(可指定消息中key值) |
|
||||
| ShowFloat | 显示浮点型(可指定消息中key值) |
|
||||
| ShowNumber | 显示数字(可指定消息中key值) |
|
||||
| ShowBoolean | 显示布尔值(可指定消息中key值) |
|
||||
| ImageEqual | 图片是否相等(可用于通过判断遮罩图是否全黑来判定是否有遮罩) |
|
||||
| SDBaseVerNumber | 判断SD大模型版本是1.5还是xl |
|
||||
| ListWrapper | 包装成列表(任意类型) |
|
||||
| ListUnWrapper | 转成输出列表,后面连接的节点会把每个元素执行一遍,实现类似遍历效果 |
|
||||
| BboxToCropData | bbox转cropData,方便接入was节点使用 |
|
||||
| BboxToBbox | bbox两种格式(x,y,w,h)和(x1,y1,x2,y2)的相互转换 |
|
||||
| BboxesToBboxes | BboxToBbox节点的列表版本 |
|
||||
| SelectBbox | 从Bbox列表中选择一个 |
|
||||
| SelectBboxes | 从Bbox列表中选择多个 |
|
||||
| CropImageByBbox | 根据Bbox区域裁剪图片 |
|
||||
| MaskByBboxes | 根据Bbox列表画遮罩 |
|
||||
| SplitStringToList | 根据分隔符把字符串拆分为某种数据类型(str/int/float/bool)的列表 |
|
||||
| IndexOfList | 从列表中获取指定位置的元素 |
|
||||
| IndexesOfList | 从列表中筛选出指定位置的元素列表 |
|
||||
| StringArea | 字符串文本框(多行输入区域) |
|
||||
| ForEachOpen | 循环开始节点 |
|
||||
| ForEachClose | 循环结束节点 |
|
||||
| LoadJsonStrToList | json字符串转换为对象列表 |
|
||||
| GetValueFromJsonObj | 从对象中获取指定key的值 |
|
||||
| FilterValueForList | 根据指定值过滤列表中元素 |
|
||||
| SliceList | 列表切片 |
|
||||
| LoadLocalFilePath | 列出给定路径下的文件列表 |
|
||||
| LoadImageFromLocalPath | 根据图片全路径加载图片 |
|
||||
| LoadMaskFromLocalPath | 根据遮罩全路径加载遮罩 |
|
||||
| IsNoneOrEmpty | 判断是否为空或空字符串或空列表或空字典 |
|
||||
| IsNoneOrEmptyOptional | 为空时返回指定值(惰性求值),否则返回原值 |
|
||||
| EmptyOutputNode | 空的输出类型节点 |
|
||||
| SaveTextToFileByImagePath | 保存文本到图片路径,以图片名作为文件名 |
|
||||
| CopyAndRenameFiles | 复制或重命名文件 |
|
||||
| SaveImagesWithoutOutput | 保存图像到指定目录,不是输出类型节点,可用于循环批量跑图和作为惰性求值的前置节点 |
|
||||
| SaveSingleImageWithoutOutput | 保存单个图像到指定目录,不是输出类型节点,可用于循环批量跑图和作为惰性求值的前置节点 |
|
||||
| CropTargetSizeImageByBbox | 以bbox的区域中心裁剪指定大小图片 |
|
||||
| 是否为输出节点 | 名称 | 说明 |
|
||||
|:----------:|------------------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||
| × | LoadImageFromURL | 从网络地址加载图片,一行代表一个图片 |
|
||||
| × | LoadMaskFromURL | 从网络地址加载遮罩,一行代表一个 |
|
||||
| × | Base64ToImage | 把图片base64字符串转成图片 |
|
||||
| × | Base64ToMask | 把遮罩图片base64字符串转成遮罩 |
|
||||
| × | ImageToBase64Advanced | 把图片转成base64字符串, 可以选择图片类型(image, mask) ,方便接口调用判断 |
|
||||
| × | ImageToBase64 | 把图片转成base64字符串(imageType=["image"]) |
|
||||
| √ | MaskToBase64Image | 把遮罩转成对应图片的base64字符串(imageType=["mask"]) |
|
||||
| √ | MaskImageToBase64 | 把遮罩图片转成base64字符串(imageType=["mask"]) |
|
||||
| × | LoadImageToBase64 | 加载本地图片转成base64字符串 |
|
||||
| √ | SamAutoMaskSEGS | 得到图片所有语义分割的coco_rle或uncompress_rle格式。<br/>配合ComfyUI-Impact-Pack的SAMLoader或comfyui_segment_anything的SAMModelLoader。<br/>但是如果使用hq模型,必须使用comfyui_segment_anything |
|
||||
| × | SamAutoMaskSEGSAdvanced | 得到图片所有语义分割的coco_rle或uncompress_rle格式。可以调整sam的参数。 |
|
||||
| × | MaskToRle | 遮罩图转为coco_rle或uncompress_rle格式,rle格式数据只保存二值,所以无法精准还原遮罩 |
|
||||
| × | RleToMask | coco_rle或uncompress_rle格式转为遮罩,rle格式数据只保存二值,所以无法精准还原遮罩 |
|
||||
| × | InsightFaceBBOXDetect | 为图片中的人脸添加序号和区域框 |
|
||||
| × | ColorPicker | 颜色选择器 |
|
||||
| × | IntToNumber | 整型转数字 |
|
||||
| × | StringToList | 字符串转列表 |
|
||||
| × | IntToList | 整型转列表 |
|
||||
| × | ListMerge | 列表合并 |
|
||||
| × | JoinList | 列表根据指定分隔符连接(会先把列表元素转成字符串) |
|
||||
| √ | ShowString | 显示字符串(可指定消息中key值) |
|
||||
| √ | ShowInt | 显示整型(可指定消息中key值) |
|
||||
| √ | ShowFloat | 显示浮点型(可指定消息中key值) |
|
||||
| √ | ShowNumber | 显示数字(可指定消息中key值) |
|
||||
| √ | ShowBoolean | 显示布尔值(可指定消息中key值) |
|
||||
| × | ImageEqual | 图片是否相等(可用于通过判断遮罩图是否全黑来判定是否有遮罩) |
|
||||
| × | SDBaseVerNumber | 判断SD大模型版本是1.5还是xl |
|
||||
| × | ListWrapper | 包装成列表(任意类型) |
|
||||
| × | ListUnWrapper | 转成输出列表,后面连接的节点会把每个元素执行一遍,实现类似遍历效果 |
|
||||
| × | BboxToCropData | bbox转cropData,方便接入was插件节点使用 |
|
||||
| × | BboxToBbox | bbox两种格式(x,y,w,h)和(x1,y1,x2,y2)的相互转换 |
|
||||
| × | BboxesToBboxes | BboxToBbox节点的列表版本 |
|
||||
| × | SelectBbox | 从Bbox列表中选择一个 |
|
||||
| × | SelectBboxes | 从Bbox列表中选择多个 |
|
||||
| × | CropImageByBbox | 根据Bbox区域裁剪图片 |
|
||||
| × | MaskByBboxes | 根据Bbox列表画遮罩 |
|
||||
| × | SplitStringToList | 根据分隔符把字符串拆分为某种数据类型(str/int/float/bool)的列表 |
|
||||
| × | IndexOfList | 从列表中获取指定位置的元素 |
|
||||
| × | IndexesOfList | 从列表中筛选出指定位置的元素列表 |
|
||||
| × | StringArea | 字符串文本框(多行输入区域) |
|
||||
| × | ForEachOpen | 循环开始节点 |
|
||||
| × | ForEachClose | 循环结束节点 |
|
||||
| × | LoadJsonStrToList | json字符串转换为对象列表 |
|
||||
| × | ConvertTypeToAny | 转换数据类型为任意类型,桥接实际具有相同数据类型参数的两节点 |
|
||||
| × | GetValueFromJsonObj | 从对象中获取指定key的值 |
|
||||
| × | FilterValueForList | 根据指定值过滤列表中元素 |
|
||||
| × | SliceList | 列表切片 |
|
||||
| × | LoadLocalFilePath | 列出给定路径下的文件列表 |
|
||||
| × | LoadImageFromLocalPath | 根据图片全路径加载图片 |
|
||||
| × | LoadMaskFromLocalPath | 根据遮罩全路径加载遮罩 |
|
||||
| × | IsNoneOrEmpty | 判断是否为空或空字符串或空列表或空字典 |
|
||||
| × | IsNoneOrEmptyOptional | 为空时返回指定值(惰性求值),否则返回原值。由于ComfyUI本体代码逻辑,非字符串会报错 |
|
||||
| √ | EmptyOutputNode | 空的输出类型节点 |
|
||||
| × | SaveTextToFileByImagePath | 保存文本到图片路径,以图片名作为文件名 |
|
||||
| × | CopyAndRenameFiles | 把某个目录下的文件复制到另一个目录并重命名,若目标目录为空值,则重命名原文件 |
|
||||
| × | SaveImagesWithoutOutput | 保存图像到指定目录,不是输出类型节点,可用于循环批量跑图和作为惰性求值的前置节点 |
|
||||
| × | SaveSingleImageWithoutOutput | 保存单个图像到指定目录,不是输出类型节点,可用于循环批量跑图和作为惰性求值的前置节点 |
|
||||
| × | CropTargetSizeImageByBbox | 以bbox区域中心向外裁剪指定宽高图片 |
|
||||
| × | ConvertToJsonStr | 序列化为json字符串 |
|
||||
|
||||
### 示例
|
||||

|
||||
@@ -86,9 +91,14 @@ Tips: base64格式字符串比较长,会导致界面卡顿,接口请求带
|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||
|
||||
## 更新记录
|
||||
### 2024-11-04 (v1.0.9)
|
||||
- 新增节点:SamAutoMaskSEGSAdvanced、 MaskToRle、 RleToMask、 ConvertToJsonStr
|
||||
|
||||
### 2024-10-24 (v1.0.7)
|
||||
- 新增节点:SaveTextToFileByImagePath、 CopyAndRenameFiles、 SaveImagesWithoutOutput、 SaveSingleImageWithoutOutput、 CropTargetSizeImageByBbox
|
||||
|
||||
@@ -99,7 +109,7 @@ Tips: base64格式字符串比较长,会导致界面卡顿,接口请求带
|
||||
- 新增节点:FilterValueForList
|
||||
|
||||
### 2024-09-26
|
||||
- 新增节点:GetValueFromJsonObj、 LoadJsonStrToList
|
||||
- 新增节点:GetValueFromJsonObj、 LoadJsonStrToList、ConvertTypeToAny
|
||||
|
||||
### 2024-09-25 [示例](example/example_4.png)
|
||||
- 新增节点:ForEachOpen、 ForEachClose
|
||||
|
||||
@@ -461,7 +461,7 @@ class SaveImagesWithoutOutput:
|
||||
"required": {
|
||||
"images": ("IMAGE",),
|
||||
"filename_prefix": ("STRING", {"default": "ComfyUI",
|
||||
"tooltip": "要保存的文件的前缀。可以使用格式化信息,如%date:yyyy-MM-dd%或%Empty Latent Image.width%"}),
|
||||
"tooltip": "要保存的文件的前缀。支持的占位符:%width% %height% %year% %month% %day% %hour% %minute% %second%"}),
|
||||
"output_dir": ("STRING", {"default": "", "tooltip": "若为空,存放到output目录"}),
|
||||
},
|
||||
"optional": {
|
||||
|
||||
+308
-21
@@ -1,21 +1,224 @@
|
||||
import torch
|
||||
from segment_anything import SamAutomaticMaskGenerator
|
||||
import json
|
||||
import numpy as np
|
||||
from segment_anything.utils.amg import area_from_rle, mask_to_rle_pytorch, rle_to_mask, batched_mask_to_box, \
|
||||
box_xyxy_to_xywh, coco_encode_rle
|
||||
from pycocotools import mask as mask_utils
|
||||
|
||||
import nodes
|
||||
from .util import tensor_to_pil
|
||||
|
||||
class SamAutoMaskSEGS:
|
||||
|
||||
class SamAutoMaskSEGSAdvanced:
|
||||
@classmethod
|
||||
def INPUT_TYPES(self):
|
||||
return {"required": {
|
||||
"sam_model": ('SAM_MODEL', {}),
|
||||
"image": ('IMAGE', {}),
|
||||
"output_mode": (['uncompressed_rle', 'coco_rel'], {"default": "uncompressed_rle"}),
|
||||
},
|
||||
return {
|
||||
"required": {
|
||||
"sam_model": ('SAM_MODEL', {}),
|
||||
"image": ('IMAGE', {}),
|
||||
},
|
||||
"optional": {
|
||||
"points_per_side": ("INT",
|
||||
{
|
||||
"default": 32,
|
||||
"min": 1,
|
||||
"max": nodes.MAX_RESOLUTION,
|
||||
"step": 1,
|
||||
"tooltip": "沿图像一侧采样的点数。 总点数为points_per_side的平方。优先级盖玉point_grids, 如果为 None,则 'point_grids'采样点必须传"
|
||||
}),
|
||||
"points_per_batch": ("INT",
|
||||
{
|
||||
"default": 64,
|
||||
"min": 1,
|
||||
"max": nodes.MAX_RESOLUTION,
|
||||
"step": 1,
|
||||
"tooltip": "设置模型同时执行的点数。 数字越大,速度越快,但会占用更多的 GPU 内存"
|
||||
}),
|
||||
"pred_iou_thresh": ("FLOAT",
|
||||
{
|
||||
"default": 0.88,
|
||||
"min": 0,
|
||||
"max": 1.0,
|
||||
"step": 0.01,
|
||||
"tooltip": "置信度阈值。 置信度低于此值的掩码将被忽略"
|
||||
}),
|
||||
"stability_score_thresh": ("FLOAT",
|
||||
{
|
||||
"default": 0.95,
|
||||
"min": 0,
|
||||
"max": 1.0,
|
||||
"step": 0.01,
|
||||
"tooltip": "稳定性得分的过滤阈值,范围[0,1]"
|
||||
}),
|
||||
"stability_score_offset": ("FLOAT",
|
||||
{
|
||||
"default": 1.0,
|
||||
"min": 0,
|
||||
"max": 1.0,
|
||||
"step": 0.01,
|
||||
"tooltip": "计算稳定性得分时thresh偏移量。\n公式简单理解成 score= (mask > stability_score_thresh+stability_score_offset) / (mask > stability_score_thresh-stability_score_offset)"
|
||||
}),
|
||||
"box_nms_thresh": ("FLOAT",
|
||||
{
|
||||
"default": 0.7,
|
||||
"min": 0,
|
||||
"max": 1.0,
|
||||
"step": 0.01,
|
||||
"tooltip": "mask的bbox区域置信度阈值"
|
||||
}),
|
||||
"crop_n_layers": ("INT",
|
||||
{
|
||||
"default": 0,
|
||||
"min": 0,
|
||||
"max": 64,
|
||||
"step": 1,
|
||||
"tooltip": "递归重复检测层数,增大此值可以解决多个物体没拆分开的问题,但是速度会变慢"
|
||||
}),
|
||||
"crop_nms_thresh": ("FLOAT",
|
||||
{
|
||||
"default": 0.7,
|
||||
"min": 0,
|
||||
"max": 1.0,
|
||||
"step": 0.01,
|
||||
"tooltip": "crop_box区域置信度阈值"
|
||||
}),
|
||||
"crop_overlap_ratio": ("FLOAT",
|
||||
{
|
||||
"default": 512 / 1500,
|
||||
"min": 0,
|
||||
"max": 1.0,
|
||||
"step": 0.01,
|
||||
"tooltip": "多层检测时,设置裁剪重叠的程度,第一层使用此值。随着层数增加,重叠程度会减小"
|
||||
}),
|
||||
"crop_n_points_downscale_factor": ("INT",
|
||||
{
|
||||
"default": 1,
|
||||
"min": 1,
|
||||
"max": nodes.MAX_RESOLUTION,
|
||||
"step": 1,
|
||||
"tooltip": "用于计算第n层的points_per_side:int(points_per_side/crop_n_points_downscale_factor**n)"
|
||||
}),
|
||||
"min_mask_region_area": ("INT",
|
||||
{
|
||||
"default": 0,
|
||||
"min": 0,
|
||||
"max": nodes.MAX_RESOLUTION,
|
||||
"step": 1,
|
||||
"tooltip": "最小区域面积。 用于过滤(忽略)小区域"
|
||||
}),
|
||||
"output_mode": (['uncompressed_rle', 'coco_rle'], {"default": "uncompressed_rle"}),
|
||||
},
|
||||
}
|
||||
|
||||
RETURN_TYPES = ("MASK_RLE",)
|
||||
RETURN_NAMES = ("masks_rle",)
|
||||
|
||||
FUNCTION = "generate"
|
||||
|
||||
OUTPUT_NODE = False
|
||||
CATEGORY = "EasyApi/Detect"
|
||||
|
||||
def generate(self,
|
||||
sam_model,
|
||||
image,
|
||||
points_per_side: int = 32,
|
||||
points_per_batch: int = 64,
|
||||
pred_iou_thresh: float = 0.88,
|
||||
stability_score_thresh: float = 0.95,
|
||||
stability_score_offset: float = 1.0,
|
||||
box_nms_thresh: float = 0.7,
|
||||
crop_n_layers: int = 0,
|
||||
crop_nms_thresh: float = 0.7,
|
||||
crop_overlap_ratio: float = 512 / 1500,
|
||||
crop_n_points_downscale_factor: int = 1,
|
||||
min_mask_region_area: int = 0,
|
||||
output_mode: str = "uncompressed_rle",
|
||||
):
|
||||
"""
|
||||
# 沿图像一侧采样的点数。 总点数为 points_per_side**2。优先级盖玉point_grids, 如果为 None,则 'point_grids'采样点必须传。
|
||||
points_per_side = 32
|
||||
# 设置模型同时执行的点数。 数字越大,速度越快,但会占用更多的 GPU 内存。
|
||||
points_per_batch = 64
|
||||
# 置信度阈值。 置信度低于此值的掩码将被忽略。
|
||||
pred_iou_thresh = 0.88
|
||||
# 稳定性得分的过滤阈值,范围[0,1]
|
||||
stability_score_thresh = 0.95
|
||||
# 计算稳定性得分时thresh偏移量
|
||||
# 公式简单理解成 score= (mask > stability_score_thresh+stability_score_offset) / (mask > stability_score_thresh-stability_score_offset)
|
||||
stability_score_offset = 1.0
|
||||
# mask的bbox区域置信度阈值。
|
||||
box_nms_thresh = 0.7
|
||||
# 递归检测次数,增大此值可以解决多个物体没拆分开的问题,但是速度会变慢。
|
||||
crop_n_layers = 0
|
||||
# crop_box区域置信度阈值。
|
||||
crop_nms_thresh = 0.7
|
||||
# 设置裁剪重叠的程度,第一层使用此值。随着层数增加,重叠程度会减小。
|
||||
crop_overlap_ratio = 512 / 1500
|
||||
# 用于计算第n层的points_per_side:按int(points_per_side/crop_n_points_downscale_factor**n)。
|
||||
crop_n_points_downscale_factor = 1
|
||||
# 用于采样的点列表,归一化为[0,1]。列表中的第n个点用于第n个裁剪层。points_per_side不为空时不生效。Optional[List[np.ndarray]]
|
||||
point_grids = None
|
||||
# 最小区域面积。 用于过滤小区域
|
||||
min_mask_region_area = 0
|
||||
"""
|
||||
point_grids = None
|
||||
# 判断是不是HQ
|
||||
encodeClassName = sam_model.image_encoder.__class__.__name__
|
||||
if encodeClassName == "ImageEncoderViTHQ":
|
||||
from custom_nodes.comfyui_segment_anything.sam_hq.automatic import SamAutomaticMaskGeneratorHQ
|
||||
from custom_nodes.comfyui_segment_anything.sam_hq.predictor import SamPredictorHQ
|
||||
samHQ = SamPredictorHQ(sam_model, True)
|
||||
mask_generator = SamAutomaticMaskGeneratorHQ(samHQ,
|
||||
points_per_side,
|
||||
points_per_batch,
|
||||
pred_iou_thresh,
|
||||
stability_score_thresh,
|
||||
stability_score_offset,
|
||||
box_nms_thresh,
|
||||
crop_n_layers,
|
||||
crop_nms_thresh,
|
||||
crop_overlap_ratio,
|
||||
crop_n_points_downscale_factor,
|
||||
point_grids,
|
||||
min_mask_region_area,
|
||||
output_mode=output_mode)
|
||||
else:
|
||||
mask_generator = SamAutomaticMaskGenerator(sam_model,
|
||||
points_per_side,
|
||||
points_per_batch,
|
||||
pred_iou_thresh,
|
||||
stability_score_thresh,
|
||||
stability_score_offset,
|
||||
box_nms_thresh,
|
||||
crop_n_layers,
|
||||
crop_nms_thresh,
|
||||
crop_overlap_ratio,
|
||||
crop_n_points_downscale_factor,
|
||||
point_grids,
|
||||
min_mask_region_area,
|
||||
output_mode=output_mode)
|
||||
image_pil = tensor_to_pil(image)
|
||||
image_np = np.array(image_pil)
|
||||
image_np_rgb = image_np[..., :3]
|
||||
|
||||
masks = mask_generator.generate(image_np_rgb)
|
||||
return (masks,)
|
||||
|
||||
|
||||
class SamAutoMaskSEGS(SamAutoMaskSEGSAdvanced):
|
||||
@classmethod
|
||||
def INPUT_TYPES(self):
|
||||
return {
|
||||
"required": {
|
||||
"sam_model": ('SAM_MODEL', {}),
|
||||
"image": ('IMAGE', {}),
|
||||
"output_mode": (['uncompressed_rle', 'coco_rle'], {"default": "uncompressed_rle"}),
|
||||
},
|
||||
}
|
||||
|
||||
RETURN_TYPES = ("STRING",)
|
||||
RETURN_NAMES = ("RLE_SEGS", )
|
||||
RETURN_NAMES = ("RLE_SEGS",)
|
||||
|
||||
FUNCTION = "generate"
|
||||
|
||||
@@ -26,29 +229,113 @@ class SamAutoMaskSEGS:
|
||||
# OUTPUT_IS_LIST = (False, False)
|
||||
|
||||
def generate(self, sam_model, image, output_mode):
|
||||
# 判断是不是HQ
|
||||
encodeClassName = sam_model.image_encoder.__class__.__name__
|
||||
if encodeClassName == "ImageEncoderViTHQ":
|
||||
from custom_nodes.comfyui_segment_anything.sam_hq.automatic import SamAutomaticMaskGeneratorHQ
|
||||
from custom_nodes.comfyui_segment_anything.sam_hq.predictor import SamPredictorHQ
|
||||
samHQ = SamPredictorHQ(sam_model, True)
|
||||
mask_generator = SamAutomaticMaskGeneratorHQ(samHQ, output_mode=output_mode)
|
||||
else:
|
||||
mask_generator = SamAutomaticMaskGenerator(sam_model, output_mode=output_mode)
|
||||
image_pil = tensor_to_pil(image)
|
||||
image_np = np.array(image_pil)
|
||||
image_np_rgb = image_np[..., :3]
|
||||
|
||||
masks = mask_generator.generate(image_np_rgb)
|
||||
masks = super().generate(sam_model, image, output_mode)
|
||||
masksRle = json.JSONEncoder().encode(masks)
|
||||
return {"ui": {"segsRle": (masksRle,)}, "result": (masksRle,)}
|
||||
|
||||
|
||||
class MaskToRle:
|
||||
@classmethod
|
||||
def INPUT_TYPES(self):
|
||||
return {
|
||||
"required": {
|
||||
"mask": ('MASK', {}),
|
||||
"output_mode": (['uncompressed_rle', 'coco_rle'], {"default": "uncompressed_rle"}),
|
||||
},
|
||||
}
|
||||
|
||||
RETURN_TYPES = ("MASK_RLE",)
|
||||
RETURN_NAMES = ("masks_rle",)
|
||||
|
||||
FUNCTION = "convert"
|
||||
|
||||
OUTPUT_NODE = False
|
||||
CATEGORY = "EasyApi/Detect"
|
||||
|
||||
def convert(self, mask, output_mode):
|
||||
masksRle = []
|
||||
b, h, w = mask.shape
|
||||
rles = mask_to_rle_pytorch((mask > 0.15).bool())
|
||||
for i in range(b):
|
||||
single_rle = rles[i]
|
||||
area = area_from_rle(single_rle)
|
||||
bbox = box_xyxy_to_xywh(batched_mask_to_box(mask.bool())[i]).tolist()
|
||||
# stability_scores = calculate_stability_score(mask[i], mask_threshold, threshold_offset)
|
||||
if output_mode == "coco_rle":
|
||||
single_rle = coco_encode_rle(single_rle)
|
||||
|
||||
masksRle.append(
|
||||
{
|
||||
"segmentation": single_rle,
|
||||
# 遮罩区域面积(像素点数)
|
||||
"area": area,
|
||||
# 蒙版矩形区域XYWH
|
||||
"bbox": bbox,
|
||||
# 用于生成此蒙版的图像的裁剪(XYWH格式)
|
||||
"crop_box": [0, 0, w, h],
|
||||
# "predicted_iou": 0.9494854211807251,
|
||||
# 采样点坐标,自动情况下,蒙版区域内的任意一个点就行
|
||||
# "point_coords": [[54.8475,1075.9375]],
|
||||
# "stability_score": stability_scores.item(),
|
||||
}
|
||||
)
|
||||
return (masksRle,)
|
||||
|
||||
|
||||
class RleToMask:
|
||||
@classmethod
|
||||
def INPUT_TYPES(self):
|
||||
return {
|
||||
"required": {
|
||||
"masks_rle": ('MASK_RLE', {}),
|
||||
"rle_mode": (['uncompressed_rle', 'coco_rle'], {"default": "uncompressed_rle"}),
|
||||
},
|
||||
}
|
||||
|
||||
RETURN_TYPES = ("MASK",)
|
||||
RETURN_NAMES = ("masks",)
|
||||
|
||||
FUNCTION = "convert"
|
||||
|
||||
OUTPUT_NODE = False
|
||||
CATEGORY = "EasyApi/Detect"
|
||||
|
||||
def convert(self, masks_rle, rle_mode='uncompressed_rle'):
|
||||
masks = []
|
||||
if isinstance(masks_rle, dict):
|
||||
list_rle = [masks_rle]
|
||||
else:
|
||||
list_rle = masks_rle
|
||||
for mask_rle in list_rle:
|
||||
if rle_mode == "coco_rle":
|
||||
mask_np = mask_utils.decode(mask_rle["segmentation"])
|
||||
else:
|
||||
mask_np = rle_to_mask(mask_rle["segmentation"])
|
||||
|
||||
mask = torch.from_numpy(mask_np).to(torch.float32)
|
||||
|
||||
masks.append(mask.unsqueeze(0))
|
||||
|
||||
if len(masks) > 1:
|
||||
# 如果有多个图像,则将它们按维度0拼接在一起
|
||||
output_mask = torch.cat(masks, dim=0)
|
||||
else:
|
||||
output_mask = masks[0]
|
||||
|
||||
return (output_mask,)
|
||||
|
||||
|
||||
NODE_CLASS_MAPPINGS = {
|
||||
"SamAutoMaskSEGS": SamAutoMaskSEGS,
|
||||
"SamAutoMaskSEGSAdvanced": SamAutoMaskSEGSAdvanced,
|
||||
"MaskToRle": MaskToRle,
|
||||
"RleToMask": RleToMask,
|
||||
}
|
||||
|
||||
# A dictionary that contains the friendly/humanly readable titles for the nodes
|
||||
NODE_DISPLAY_NAME_MAPPINGS = {
|
||||
"SamAutoMaskSEGS": "SamAutoMaskSEGS",
|
||||
"SamAutoMaskSEGSAdvanced": "SamAutoMaskSEGSAdvanced",
|
||||
"MaskToRle": "MaskToRle",
|
||||
"RleToMask": "RleToMask",
|
||||
}
|
||||
|
||||
+23
-1
@@ -609,6 +609,26 @@ class LoadJsonStrToList:
|
||||
return ([json],)
|
||||
|
||||
|
||||
class ConvertToJsonStr:
|
||||
@classmethod
|
||||
def INPUT_TYPES(s):
|
||||
return {
|
||||
"required": {
|
||||
"obj": (any_type, {"forceInput": True}),
|
||||
}
|
||||
}
|
||||
|
||||
RETURN_TYPES = ("STRING",)
|
||||
RETURN_NAMES = ("json_str",)
|
||||
CATEGORY = "EasyApi/Utils"
|
||||
FUNCTION = "to_json_str"
|
||||
DESCRIPTION = "将任意对象序列化为json字符串"
|
||||
|
||||
def to_json_str(self, obj):
|
||||
json = simplejson.dumps(obj, ignore_nan=True)
|
||||
return (json,)
|
||||
|
||||
|
||||
class GetValueFromJsonObj:
|
||||
@classmethod
|
||||
def INPUT_TYPES(s):
|
||||
@@ -798,7 +818,7 @@ class IsNoneOrEmptyOptional:
|
||||
RETURN_NAMES = ("any",)
|
||||
FUNCTION = "execute"
|
||||
CATEGORY = "EasyApi/Utils"
|
||||
DESCRIPTION = "判断输入any是否为None、空列表、空字符串(trim后判断)、空字典,若为true,返回default的值,否则返回输入值"
|
||||
DESCRIPTION = "判断输入any是否为None、空列表、空字符串(trim后判断)、空字典,若为true,返回default的值,否则返回输入值。受ComfyUI主体代码限制,经测试非字符串会报错"
|
||||
|
||||
def execute(self, any=None, default=None):
|
||||
if any is None:
|
||||
@@ -971,6 +991,7 @@ NODE_CLASS_MAPPINGS = {
|
||||
"ConvertTypeToAny": ConvertTypeToAny,
|
||||
"GetValueFromJsonObj": GetValueFromJsonObj,
|
||||
"LoadJsonStrToList": LoadJsonStrToList,
|
||||
"ConvertToJsonStr": ConvertToJsonStr,
|
||||
"FilterValueForList": FilterValueForList,
|
||||
"LoadLocalFilePath": LoadLocalFilePath,
|
||||
"IsNoneOrEmpty": IsNoneOrEmpty,
|
||||
@@ -1006,6 +1027,7 @@ NODE_DISPLAY_NAME_MAPPINGS = {
|
||||
"ConvertTypeToAny": "ConvertTypeToAny",
|
||||
"GetValueFromJsonObj": "GetValueFromJsonObj",
|
||||
"LoadJsonStrToList": "LoadJsonStrToList",
|
||||
"ConvertToJsonStr": "ConvertToJsonStr",
|
||||
"FilterValueForList": "FilterValueForList",
|
||||
"LoadLocalFilePath": "LoadLocalFilePath",
|
||||
"IsNoneOrEmpty": "IsNoneOrEmpty",
|
||||
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 835 KiB |
+3
-3
@@ -1,9 +1,9 @@
|
||||
[project]
|
||||
name = "comfyui-easyapi-nodes"
|
||||
description = "Provides some features and nodes related to API calls."
|
||||
version = "1.0.8"
|
||||
description = "Provides some features and nodes related to API calls. 开发独立应用调用ComfyUI服务的一些补充节点。"
|
||||
version = "1.0.9"
|
||||
license = { file = "LICENSE" }
|
||||
dependencies = ["segment_anything", "simple_lama_inpainting", "insightface"]
|
||||
dependencies = ["segment_anything", "simple_lama_inpainting", "insightface", "simplejson"]
|
||||
|
||||
[project.urls]
|
||||
Repository = "https://github.com/lldacing/comfyui-easyapi-nodes"
|
||||
|
||||
+2
-1
@@ -1,3 +1,4 @@
|
||||
segment_anything
|
||||
simple_lama_inpainting
|
||||
insightface
|
||||
insightface
|
||||
simplejson
|
||||
|
||||
Reference in New Issue
Block a user