在过去一年的时光里,ComfyUI 仿若一颗璀璨新星迅速崛起,成功跻身于最受青睐的开源 AI 绘画软件之列。
该软件凭借其独具匠心的灵活工作流形式以及高度可定制化的节点组合功能崭露头角,使用户得以从容不迫地构建起复杂精妙的设计流程。
ComfyUI 所散发的魅力,不仅成功捕获了众多个人艺术家与设计师的目光,还宛如一块强力磁石,吸引着许多商业平台与服务纷纷基于其构建自身的解决方案。
随着 AI 技术持续稳步迈进,应用疆域不断拓展延伸,ComfyUI 有望在未来继续担当这一领域创新发展的领航者角色,引领潮流,奋勇向前。
对于那些热衷于技术开发的同学们而言,若渴望踏入这片充满无限可能的领域,当下无疑是最佳时机,理应即刻行动起来,率先抢占发展先机。
本文将为大家悉心介绍开发 ComfyUI 插件的基本要领与方法,并且具体会以笔者所开发的提示词选择插件作为实例展开详细讲解。需要注意的是,在 ComfyUI 中,插件实质上就是自定义节点(custom nodes)。
需求
我们开发一个东西,不能闭门造车,首先得有需求。
有位设计师同学需要一个ComfyUI工作流提示词预置的功能:他想要在工作流中预置几个常用的提示词,别人使用工作流的时候可以直接切换这几个提示词,而不用手动从其它地方复制,这样比较方便高效。
设计
这个需求很容易理解,我首先想到的就是提供一个下拉列表让用户选择。
那提示词怎么录入呢?再给用户提供一个输入框,可以输入多条提示词。
那用户选择的依据是什么呢?每条提示词可以设置一个简短的短语或者key,一看就明白的那种。
我大概在脑子中形成了一个画面:
1、节点的上方是一个输入框,可以输入键值对形式的提示词,键是短语,值是完整的提示词;
2、节点下方是一个下拉选择框,下拉列表根据上方输入的提示词自动填充,下拉选项是提示词的键;
3、工作流运行时,节点输出选中提示词键对应的完整提示词。
开发
官方有一些介绍文档,大家可以先去了解一些基础知识:
https://docs.comfy.org/essentials/custom_node_overview
如果想直接看这个插件的源码,请访问这个地址:https://github.com/bosima/ComfyUI-PromptSelector
总体的目录结构如下:
后端
包括 nodes.py 和 __init__.py 这两个文件。
节点定义
按照 ComfyUI 节点的开发规范,我们先要定义这样一个Python类型,放到 nodes.py 文件中。
class PromptSelectorNode:
"""提示词选择器节点,用于在ComfyUI中动态选择预定义的提示词"""
@classmethod
def INPUT_TYPES(cls):
return {
"required": {
"prompt_pairs": ("STRING", {
"multiline": True,
"default": '"key1":"value1",\n"key2":"value2",\n"key3":"value3"'
}),
# 使用类变量存储的当前keys
"selected_key": (["key1", "key2", "key3"],),
},
# 这样可以为 FUNCTION 提供 node_id 参数
"hidden": { "node_id": "UNIQUE_ID" }
}
@classmethod
def VALIDATE_INPUTS(cls, selected_key):
return True
RETURN_TYPES = ("STRING",)
RETURN_NAMES = ("selected_value",)
FUNCTION = "process"
CATEGORY = "Prompt Selector"
INPUT_TYPES:输入项定义。它的形式是一个字典,目前可以定义三种类型的输入:required、optional和hidden,required就是必须输入的项,optional是可选填的项,hidden是隐藏的项。
当前插件的输入框和下拉框都是必填的,所以我把它们放到了required中:
第一个输入为 prompt_pairs,是个多行字符串,并通过STRING的default属性给出默认参考值。
第二个输入为 selected_key,也给出了默认提示词键值对对应的下拉选项。
hidden 中只有1个node_id,ComfyUI框架会自动填充这个值,然后我们在服务端处理数据时就可以很容易获取到当前节点的Id。
**RETURN_TYPES:**节点的输出数据类型定义,格式是一个元组,如果没有输出就使用 ()。只有一个输出项时,最后加个英文逗号,让它被识别为元组。
RETURN_NAMES:节点的输出数据名称定义,格式和 RETURN_TYPES 相同。
**FUNCTION:**节点运行时要执行的函数,它会对输入数据进行加工,然后再输出。函数如何定义下边会有具体说明。
CATEGORY:节点在“添加节点”菜单中的分类。
VALIDATE_INPUTS:这个不是必需的,节点运行时会根据INPUT_TYPES中的输入输出定义对数据进行验证,比如必填、是不是在列表中,等等。在这个插件中,因为 selected_key 是前端动态填充的,后端Python执行时获取不到最新的下拉列表项目,就会报错,而这个插件并不需要特殊的验证,所以这里忽略了输入数据验证,总是返回Ture;当然我们也可以把最新的下拉列表项同步到Python端,只是有点麻烦,所以就忽略了。如果你想了解前后端通信的具体实现,可以去Github看看项目源码,里边有一行注释掉的代码。
注意这里 INPUT_TYPES 和 VALIDATE_INPUTS 都使用 @classmethod 注解定义为了静态方法,这是框架要求的。
主处理函数
再看一下节点主函数的代码:
def process(self, prompt_pairs: str, selected_key: str, node_id) -> tuple:
"""处理选择的提示词"""
try:
# 解析提示词对并更新可用的keys
self.parse_prompt_pairs(prompt_pairs)
# 确保选中的key存在,否则使用第一个可用的key
if selected_key not in self.prompt_dict:
selected_key = self.keys_list[0] if self.keys_list else "key1"
return (self.prompt_dict.get(selected_key, ""),)
except Exception as e:
print(f"处理提示词时出错: {str(e)}")
return ("",)
这就是节点运行时要执行的函数,它会接收所有的输入,然后进行处理,输出数据或者不输出。
这里的 prompt_pairs、selected_key、node_id 对应到 INPUT_TYPES 中定义的几个输入项。
这个函数的逻辑很简单,首先解析用户输入的提示词键值对,然后使用用户选择的key获取对应key的提示词内容,最后返回这句提示词。如果出现一场,就返回空字符串。
后端开发的相关文档可以看这里:https://docs.comfy.org/essentials/custom_node_server_overview
前端
前端的逻辑主要是从用户输入的提示词中提取key的列表,然后填充到下拉列表中。不过它也有一些固定的编码规范,主要代码如下图所示:
// prompt_selector/js/prompt_selector.js
import { app } from "../../scripts/app.js";
import { api } from "../../scripts/api.js";
app.registerExtension({
name: "Comfy.PromptSelector",
async beforeRegisterNodeDef(nodeType, nodeData, app) {
if (nodeData.name !== "PromptSelector") {
return;
}
const onNodeCreated = nodeType.prototype.onNodeCreated;
nodeType.prototype.onNodeCreated = function() {
const node = this;
if (onNodeCreated) {
onNodeCreated.apply(this, arguments);
}
const promptWidget = this.widgets.find(w => w.name === "prompt_pairs");
const keyWidget = this.widgets.find(w => w.name === "selected_key");
if (promptWidget && keyWidget) {
promptWidget.callback = (value) => {
const keys = parsePromptPairs(value);
updateKeyWidget(keyWidget, keys);
node.setDirtyCanvas(true, true);
//update_psn_server_keys(node.id, JSON.stringify(keys));
};
// 初始化时触发一次回调
promptWidget.callback(promptWidget.value);
}
};
// 配置加载后的初始化
nodeType.prototype.onConfigure = function() {
const promptWidget = this.widgets.find(w => w.name === "prompt_pairs");
const keyWidget = this.widgets.find(w => w.name === "selected_key");
if (promptWidget?.value && keyWidget) {
const keys = parsePromptPairs(promptWidget.value);
updateKeyWidget(keyWidget, keys);
}
};
}
});
首先我们要引入 app.js 这个Javascript文件,然后通过 app.registerExtension 来注册扩展插件。
name 是插件的注册名字,建议不要太通用,以免冲突。
beforeRegisterNodeDef 是提供插件的一些前端处理逻辑,这里注册之后,ComfyUI会在真正注册插件之前调用这个函数。这里的代码我也简单解释下:
它会被传入节点的类型定义、节点自身数据和整个ComfyUI app。
然后它首先判断当前节点的名字是不是我们的目标节点,这个名字和后端注册的节点名字要保持一致。
然后程序注册了两个事件监听:onNodeCreated 和 onConfigure。
-
onNodeCreated 顾名思义,就是节点注册后触发的事件。这里的主要逻辑就是在提示词变更后将提示词的键提取出来,然后刷新到下拉列表中。
-
onConfigure 是在节点首次加载时,主要逻辑还是将已经填充的提示词的键提取出来,然后刷新到下拉列表中。promptWidget.callback 代表提示词输入框的值发生改变时触发的事件。
这个js文件需要放到插件
前端插件开发的相关文档可以看这里:https://docs.comfy.org/essentials/javascript_overview
注册
然后我们还要将后端和前端的程序注册到ComfyUI中,主要在 __init__.py 文件中,代码如下:
# 节点名字和Python类的映射
NODE_CLASS_MAPPINGS = {
"PromptSelector": PromptSelectorNode
}
# 节点的显示名字
NODE_DISPLAY_NAME_MAPPINGS = {
"PromptSelector": "提示词选择器"
}
WEB_DIRECTORY = "./js"
__all__ = ["NODE_CLASS_MAPPINGS", "NODE_DISPLAY_NAME_MAPPINGS", "WEB_DIRECTORY"]
NODE_CLASS_MAPPINGS 定义了节点与后端类的映射;
NODE_DISPLAY_NAME_MAPPINGS 定义了节点的显示名字。
WEB_DIRECTORY 定义了插件的前端脚本路径,只能处理 .js 文件,不能处理包括 .css等其它类型的文件。
然后这三个常量被放到了 __all__中,__all__中添加的元素会被导出到整个程序中,这是因为包含__init__.py文件的目录会被作为Python模块进行处理。
当前插件只有一个节点,如果有多个节点,可以在 NODE_CLASS_MAPPINGS 和 NODE_DISPLAY_NAME_MAPPINGS 继续添加项目。
运行
现在我们只要把这个插件的文件夹放到 custom_nodes 目录下,重启ComfyUI,就能使用这个插件了。
如何学习Comfyui?
ComfyUI 和大家熟知的 WebUI 一样,都是 Stable Diffusion 的一种用户界面,一句话,一张图,随时随地创作任何内容。
但是具体到个人,只能说是:
“最先掌握AI的人,将会比较晚掌握AI的人有竞争优势”。
这句话,放在计算机、互联网、移动互联网的开局时期,都是一样的道理。
我在一线互联网企业工作十余年里,指导过不少同行后辈。帮助很多人得到了学习和成长。
我意识到有很多经验和知识值得分享给大家,也可以通过我们的能力和经验解答大家在人工智能学习中的很多困惑,所以在工作繁忙的情况下还是坚持各种整理和分享。但苦于知识传播途径有限,很多互联网行业朋友无法获得正确的资料得到学习提升,故此将并将重要的Comfyui资料包括Comfyui入门学习思维导图、商业级Comfyui工作流、视频教程、实战学习等录播视频免费分享出来。
第一阶段(8天)初始入门
该阶段让大家对Comfyui有一个最前沿的认识,规避碎片化学习,对Comfyui的理解将超过 95% 的人。可以在相关讨论发表高级、不跟风、又接地气的见解,成为AI艺术创作领域的佼佼者。
-
初次认识Comfyui
-
comfyui的底层工作原理
-
Comfyui跟webui之前的区别
-
如何快速部署comfyui一键整合包在本地电脑
-
ComfyUI的插件安装与更新详解
-
ComfyUI共用Webui模型,节省硬盘空间
-
ComfyUI的基础操作,工具管理和界面汉化
-
实现局部重绘功能
-
精细控图第一步
-
BrushNet 1. 5/XL图像重绘
-
使用Embedding提升出图质量操作
-
使用二次采样,提升画质操作
-
潜空间模块实现图像放大操作
-
放大模型模块实现图像放大操作
-
…
第二阶段(15天)进阶应用
该阶段我们正式进入Comfyui进阶实战学习,学会构造私有知识库,扩展不同的艺术风格。快速根据甲方的要求改动高效出图。掌握智能绘图最强的AI软件,抓住最新的技术进展,适合所有需出图行业。真·生产力大爆发!!!
-
SD3.5基础原理
-
SD3.5文本与图像的交互
-
SD3.5实际操作指南
-
f1ux新一代最强AI简介
-
f1ux不同模型按需分类
-
f1ux最强模型如何部署
-
f1ux_工作流操作指南
-
Contro1Net预处理节点操作
-
Contro1Net Canny实现精准控图操作
-
Contro1Net Depth实现深度控图操作
-
Contro1Net Openpose实现人物姿势控图操作
-
Contro1Net Dwpose实现精准控图操作
-
…
第三阶段(15天)工作流搭建
恭喜你,如果学到这里,所有设计类岗位你将拥有优先选择权,自己也能搭建comfyui工作流了!快速、流畅的图像生成能力,对低配置设备的友好性,以生成特定的人物、物品或画风,掌握更多技术方案。
到此为止,大概2个月的时间。你已经成为了一名“AI小子”。那么你还想往下探索吗?
-
lMagi cClothing换装
-
AnimateDi ff文生动画
-
Anima teDi ff视频动画
-
视频助手AI视频合成
-
LayerDi ffuse实现透明素材操作指南
-
Instantid换脸高级版
-
重新打光IC-1ight
-
Yo1oWor1d智能识别抠图
-
…
第四阶段(20天):商业闭环
对氛围性场景,关键词技巧,图生图实操流程等方面有一定的认知,教你「精准控制」所有图片细节,可以在云端和本地等多种环境下部署Comfyui,找到适合自己的项目/创业方向,做一名被 AI 武装的社会主义接班人。
-
真人视觉摄影工作流案例
-
电商设觉设计工作流案例
-
建筑设计工作流案例
-
客户产品设计工作流案例
-
室内设计工作流案例
-
自媒体IP搭建
-
…
学习是一个过程,只要学习就会有挑战。天道酬勤,你越努力,就会成为越优秀的自己。
如果你能在15天内完成所有的任务,那你堪称天才。然而,如果你能完成 60-70% 的内容,你就已经开始具备成为一名AI绘图大神的正确特征了。