介绍
ComfyUI 是一个基于 Node 搭建 workflow 实现具体功能的软件。常见于 stable diffusion 扩散模型生图领域。同时其提供了自定义 Node 能力的机制,支持扩展生图之外的功能。比如生成视频,生成音频,大语言模型使用等等。
python main.py --listen
整体架构
ComfyUI 是 C/S 架构,分为 Server 和 Client 两部分。 Server 部分使用 Python 语言编码,负责数据处理,模型加载,具体的功能逻辑。Client 部分使用 JS 语言编写,负责 UI 展示和用户交互逻辑。ComfyUI 完美的将 View 和 Controller 进行了解耦,所以 ComfyUI 同时支持 API 模式,可以另外实现 UI 交互或者纯命令交互,以及编写脚本自动执行任务。
- 独立的 UI 交互,无 Server: github.com/Comfy-Org/C…
- 纯命令交互: github.com/Comfy-Org/c…
erDiagram
ComfyUI-Server ||--|{ ComfyUI-Client : API
ComfyUI-Server ||..|{ comfy-cli : API
ComfyUI-Server }|..|{ ComfyUI_frontend : API
Node
Node 是由输入,输出,以及处理逻辑组成,处理逻辑可以是简单任务,也可以是较复杂的任务。
基于 ComfyUI 的架构,Node 可以分为三类: docs.comfy.org/essentials/…
- Server Side Only
The majority of Nodes run purely on the server side, by defining a Python class that specifies the input and output types, and provides a function that can be called to process inputs and produce an output.
- Client Side Only
A few Nodes provide a modification to the client UI, but do not add core functionality. Despite the name, they may not even add new nodes to the system.
- Independent Client and Server
Nodes may provide additional server features, and additional (related) UI features (such as a new widget to deal with a new data type). In most cases, communication between the client and server can be handled by the Comfy data flow control.
Server Side
下面是一个自定义 Node 官方示例: docs.comfy.org/custom-node…
class InvertImageNode:
@classmethod
def INPUT_TYPES(cls):
return {
"required": { "image_in" : ("IMAGE", {}) },
}
RETURN_TYPES = ("IMAGE",)
RETURN_NAMES = ("image_out",)
CATEGORY = "examples"
FUNCTION = "invert"
def invert(self, image_in):
image_out = 1 - image_in
return (image_out,)
其中有一下元素:
- INPUT_TYPES: 定义 Node 的输入,支持必要,可选,隐藏三类输入。每一个输入由 name 和 类型,以及一组 key-value 的参数。且 INPUT_TYPES 是一个类方法,因为 ComfyUI 加载时,调此方法或者输入定义,生成对应 UI 。
- RETURN_TYPES: 定义 Node 的输出, 类型为元组,可以定义多个输出。
RETURN_TYPES = ("IMAGE",)表示无输出。 - RETURN_NAMES: 给输出命名,可选,默认使用类型小写。
- CATEGORY: 在菜单中分类项入口名
- FUNCTION: Python 方法名,此 Node 执行时被调用的方法。参数为
INPUT_TYPES中定义的输入;返回则是RETURN_TYPES中定义的输出。
Client Node
ComfyUI 的前端页面基于 LiteGraph , ComfyNode 则是 LGraphNode 的子类, 对 LGraphNode 进行了功能扩展,对应一个 Node 在 UI 上的表示。
ComfyNode 中包含很多 properties 和 functions, 这些支持了对 Node 进行 hijack to modify 默认行为
async nodeCreated(node) {
console.log("nodeCreated")
}
| Widgets | |
|---|---|
addWidget | Add a standard Comfy widget |
addCustomWidget | Add a custom widget (defined in the getComfyWidgets hook) |
addDOMWidget | Add a widget defined by a DOM element |
convertWidgetToInput | Convert a widget to an input if allowed by isConvertableWidget (in widgetInputs.js) |
如何注入 Custom Node
How Comfy loads custom nodes
Custom Node 需要放置在 custom_nodes 目录中, ComfyUI 启动时,会遍历此目录中的 python moduels,导入其中的 NODE_CLASS_MAPPINGS
python module 一个包含
__init__.py文件的目录. module 导出的符合都定义在__init__.py文件中的变量__all__
一个简单 __init__.py 文件示例:
from .python_file import MyCustomNode
NODE_CLASS_MAPPINGS = { "My Custom Node" : MyCustomNode }
__all__ = ["NODE_CLASS_MAPPINGS"]
NODE_CLASS_MAPPINGS
NODE_CLASS_MAPPINGS must be a dict mapping custom node names (unique across the Comfy install) to the corresponding node class.
NODE_DISPLAY_NAME_MAPPINGS
__init__.py may also export NODE_DISPLAY_NAME_MAPPINGS, which maps the same unique name to a display name for the node. If NODE_DISPLAY_NAME_MAPPINGS is not provided, Comfy will use the unique name as the display name.
WEB_DIRECTORY
docs.comfy.org/custom-node…
Client side Node 的代码通过 WEB_DIRECTORY 变量进行导出,
WEB_DIRECTORY = "./js"
__all__ = ["NODE_CLASS_MAPPINGS", "NODE_DISPLAY_NAME_MAPPINGS", "WEB_DIRECTORY"]
- 在你的 Python module 中定义
WEB_DIRECTORY变量,指定目录相对 path - 你将需要的
.js文件放入此目录中,ComfyUI 启动时将加载此目录中的.js文件。 - 你的代码中在
app.registerExtension方法中注册自己的 Custom Node 。
import { app } from "../../scripts/app.js";
app.registerExtension({
name: "a.unique.name.for.a.useless.extension",
async setup() {
alert("Setup complete!")
},
})
链接
comfy: docs.comfy.org/custom-node…
litegraph: gausszhou.github.io/litegraph/d…
三方 guide: github.com/Suzie1/Comf…