Introduce DynamicImageCombiner class and update init.py for node integration.

This commit adds the DynamicImageCombiner class, enabling dynamic addition of multiple image inputs and outputting a list of connected images. The init.py file now includes this new node in NODE_CLASS_MAPPINGS and NODE_DISPLAY_NAME_MAPPINGS. A new JavaScript file has also been created to manage the node's behavior, enhancing image processing functionality.
引入DynamicImageCombiner类并更新__init__.py以实现节点集成。
此提交添加了DynamicImageCombiner类,可以动态地添加多个图像输入,并输出所有连接图像的列表。init.py文件现在在NODE_CLASS_MAPPINGS和NODE_DISPLAY_NAME_MAPPINGS中包含了这个新节点。此外,还创建了一个新的JavaScript文件来管理该节点的行为,增强了图像处理功能。
This commit is contained in:
jammyfu
2024-12-25 22:23:01 +08:00
parent 18a2e0f24f
commit 1610fce3f4
6 changed files with 118 additions and 43 deletions
+4 -1
View File
@@ -19,6 +19,7 @@ try:
from .text_combiner import TextCombiner
from .multiline_text_input import MultilineTextInput
from .click_popup import ClickPopup
from .dynamic_image_input import DynamicImageCombiner
# 节点映射
@@ -29,6 +30,7 @@ try:
"TextCombiner": TextCombiner,
"MultilineTextInput": MultilineTextInput,
"ClickPopup": ClickPopup,
"DynamicImageCombiner": DynamicImageCombiner,
}
NODE_DISPLAY_NAME_MAPPINGS = {
@@ -37,7 +39,8 @@ try:
"RemoveEmptyLinesAndLeadingSpaces": "Remove Empty Lines And Leading Spaces",
"TextCombiner": "Text Combiner",
"MultilineTextInput": "Multiline Text Input",
"ClickPopup": "Click Popup"
"ClickPopup": "Click Popup",
"DynamicImageCombiner": "Dynamic Image Combiner"
}
Binary file not shown.
Binary file not shown.
Binary file not shown.
+50 -42
View File
@@ -1,45 +1,53 @@
class DynamicImageInputs:
"""动态图片输入节点
这个节点允许动态添加多个图片输入端口,并将所有输入的图片合并成一个列表输出。
特点:
1. 支持动态增减图片输入端口
2. 自动忽略未连接的输入端口
3. 输出一个包含所有输入图片的列表
使用方法:
1. 将节点添加到工作流中
2. 连接图片到输入端口
3. 使用 get_input 方法动态增加更多输入端口
"""
# -*- coding: utf-8 -*-
# Filename: dynamic_image_input.py
# Developer: jammyfu
# Category: 🎨Painting👓Coder/🖼️Image
import torch
class DynamicImageCombiner:
@classmethod
def INPUT_TYPES(cls):
"""定义节点的输入类型
Returns:
dict: 包含默认的单个图片输入端口
"""
return {"required": {"image_1": ("IMAGE",)}}
def INPUT_TYPES(s):
return {
"required": {}, # 不需要默认输入
"optional": {}
}
RETURN_TYPES = ("IMAGE",)
RETURN_NAMES = ("images",)
OUTPUT_IS_LIST = (True,) # 标记输出为列表
FUNCTION = "combine_images"
CATEGORY = "🎨Painting👓Coder/🖼️Image"
RETURN_TYPES = ("IMAGE",) # 返回图片类型
RETURN_NAMES = ("images",) # 输出端口名称
OUTPUT_IS_LIST = (True,) # 标记输出为列表类型
FUNCTION = "process_images" # 处理函数名称
CATEGORY = "🎨Painting👓Coder/🖼️Image" # 节点分类
def combine_images(self, **image_inputs):
try:
# 收集所有非空图像
images = []
for i in range(1, len(image_inputs) + 1):
key = f"image_{i}"
if key in image_inputs and image_inputs[key] is not None:
if isinstance(image_inputs[key], torch.Tensor):
images.append(image_inputs[key])
def process_images(self, **kwargs):
"""处理所有输入的图片
Args:
**kwargs: 包含所有输入图片的字典,键名格式为 "image_1", "image_2" 等
Returns:
tuple: 包含所有输入图片的列表
"""
images = []
for k, v in kwargs.items():
if k.startswith("image_") and v is not None:
images.append(v)
return (images,)
# 如果没有图像,返回一个包含空白图像的列表
if not images:
empty_image = torch.zeros((1, 512, 512, 3))
return ([empty_image],)
# 直接返回图像列表
return (images,)
except Exception as e:
print(f"Error in DynamicImageCombiner: {str(e)}")
# 发生错误时返回包含空白图像的列表
empty_image = torch.zeros((1, 512, 512, 3))
return ([empty_image],)
# 添加到 ComfyUI 节点注册
NODE_CLASS_MAPPINGS = {
"DynamicImageCombiner": DynamicImageCombiner,
}
NODE_DISPLAY_NAME_MAPPINGS = {
"DynamicImageCombiner": "Dynamic Image Input 🖼️",
}
+64
View File
@@ -0,0 +1,64 @@
import { app } from "/scripts/app.js";
app.registerExtension({
name: "Comfy.PaintingCoder.DynamicImageCombiner",
async beforeRegisterNodeDef(nodeType, nodeData, app) {
if (nodeData.name === "DynamicImageCombiner") {
// 重写 onNodeCreated
const onNodeCreated = nodeType.prototype.onNodeCreated;
nodeType.prototype.onNodeCreated = function() {
const result = onNodeCreated ? onNodeCreated.apply(this, arguments) : undefined;
// 初始化状态
this.lastConnectedCount = 0;
// 添加第一个输入点
console.log("初始化:添加第一个图像输入点");
this.addInput("image_1", "IMAGE");
return result;
};
// 添加连接监听
nodeType.prototype.onConnectionsChange = function(connectionType, slot, isConnected) {
if (connectionType === 1) { // 输入连接
console.log(`连接变化 - 槽位: ${slot}, 已连接: ${isConnected}`);
// 计算已连接的输入点数量
const connectedCount = this.inputs.reduce((count, input) =>
count + (input.link ? 1 : 0), 0);
console.log(`当前连接数量: ${connectedCount}, 上次连接数量: ${this.lastConnectedCount}`);
// 只在连接数量发生变化时更新
if (connectedCount !== this.lastConnectedCount) {
this.lastConnectedCount = connectedCount;
const targetCount = connectedCount + 1;
console.log(`目标输入点数量: ${targetCount}`);
// 添加新的输入点
while (this.inputs.length < targetCount) {
const newIndex = this.inputs.length + 1;
console.log(`添加新输入点: image_${newIndex}`);
this.addInput(`image_${newIndex}`, "IMAGE");
}
// 移除多余的输入点,但至少保留一个
while (this.inputs.length > Math.max(targetCount, 1)) {
const lastInput = this.inputs[this.inputs.length - 1];
if (!lastInput.link) {
console.log(`移除输入点: ${this.inputs.length}`);
this.removeInput(this.inputs.length - 1);
}
}
// 更新节点大小
console.log(`更新节点大小,当前输入点数量: ${this.inputs.length}`);
this.setSize(this.computeSize());
app.graph.setDirtyCanvas(true);
}
}
};
}
}
});