ComfyScript
一个用于 ComfyUI 的 Python 前端和库。
它有以下用途:
-
作为 ComfyUI 工作流的人类可读格式。
这使得比较和重用工作流的不同部分变得容易。
还可以训练 LLM 生成工作流,因为许多 LLM 能较好地处理 Python 代码。这种方法比仅仅让 LLM 提供一些硬编码参数更强大。
脚本可以从 ComfyUI 的工作流自动转译。详见转译器。
-
直接运行脚本生成图像。
相比使用 Web UI,这样做的主要优势是能够将 Python 代码与 ComfyUI 的节点混合使用,例如进行循环、调用库函数和轻松封装自定义节点。这也使得添加交互变得更容易,因为 UI 和逻辑都可以用 Python 编写。而且,有些人可能觉得简单的 Python 代码比基于图形的 GUI 更舒适。1
详见运行时。脚本可以在本地执行,也可以通过 ComfyUI 服务器远程执行。
-
将 ComfyUI 用作函数库。
通过 ComfyScript,ComfyUI 的节点可以作为函数用于 ML 研究、在其他项目中重用节点、调试自定义节点,以及优化缓存以更快地运行工作流。
详见运行时的实际模式。
-
用脚本生成 ComfyUI 的工作流。
脚本还可以用来生成 ComfyUI 的工作流,然后在 Web UI 或其他地方使用。这样,可以使用循环并生成巨大的工作流,而手动创建这些工作流可能会耗时或不切实际。详见工作流生成。也可以从 ComfyScript 生成的图像中加载工作流。
-
通过运行带有一些存根的脚本来检索所需的任何信息。
详见工作流信息检索。
-
无需 Web UI 即可将工作流从 ComfyUI 的 Web UI 格式转换为 API 格式。
文档
安装
与 ComfyUI 一起安装
首先安装 ComfyUI。然后运行以下命令:
cd ComfyUI/custom_nodes
git clone https://github.com/Chaoses-Ib/ComfyScript.git
cd ComfyScript
python -m pip install -e ".[default]"
(如果看到 ERROR: File "setup.py" or "setup.cfg" not found
,先运行 python -m pip install -U pip
。)
更新:
cd ComfyUI/custom_nodes/ComfyScript
git pull
python -m pip install -e ".[default]"
与 ComfyUI 包一起安装
首先安装 ComfyUI 包:
-
如果未安装 PyTorch:
python -m pip install git+https://github.com/hiddenswitch/ComfyUI.git
-
如果已安装 PyTorch(例如 Google Colab):
python -m pip install wheel python -m pip install --no-build-isolation git+https://github.com/hiddenswitch/ComfyUI.git
安装/更新 ComfyScript:
python -m pip install -U "comfy-script[default]"
[default]
对安装常用依赖项是必要的。其他选项见 pyproject.toml
。如果未指定选项,将安装不含任何依赖项的 comfy-script
。
如果最新的 ComfyUI 包有问题,可以使用最后测试的版本:
python -m pip install --no-build-isolation git+https://github.com/hiddenswitch/ComfyUI.git@e49c662c7f026f05a5e082d48b629e2b977c0441
其他
如果遇到任何问题,请参阅故障排除。要在未安装 ComfyUI 的情况下使用 ComfyScript,请参阅仅 ComfyScript 包。要卸载,请参阅卸载。
转译器
转译器可以将 ComfyUI 的工作流转译为 ComfyScript。
当 ComfyScript 作为自定义节点安装时,SaveImage
和类似节点将被挂钩以自动将脚本保存为图像的元数据。脚本也会打印到终端。
例如,这是 ComfyUI 中的一个工作流:
ComfyScript 从中翻译得到:
model, clip, vae = CheckpointLoaderSimple('v1-5-pruned-emaonly.ckpt')
conditioning = CLIPTextEncode('美丽的自然景观玻璃瓶风景,,紫色星系瓶子,', clip)
conditioning2 = CLIPTextEncode('文本, 水印', clip)
latent = EmptyLatentImage(512, 512, 1)
latent = KSampler(model, 156680208700286, 20, 8, 'euler', 'normal', conditioning, conditioning2, latent, 1)
image = VAEDecode(latent, vae)
SaveImage(image, 'ComfyUI')
如果一个工作流中有两个或更多的SaveImage
节点,只有每个节点必要的输入会被转换为脚本。例如,这里是一个2次传递的txt2img(高分辨率修复)工作流:
为每个保存的图像保存的ComfyScript分别是:
-
model, clip, vae = CheckpointLoaderSimple('v2-1_768-ema-pruned.ckpt') conditioning = CLIPTextEncode('杰作HDR维多利亚时代女性肖像画,金发,山川自然,蓝天', clip) conditioning2 = CLIPTextEncode('糟糕的手,文本,水印', clip) latent = EmptyLatentImage(768, 768, 1) latent = KSampler(model, 89848141647836, 12, 8, 'dpmpp_sde', 'normal', conditioning, conditioning2, latent, 1) image = VAEDecode(latent, vae) SaveImage(image, 'ComfyUI')
-
model, clip, vae = CheckpointLoaderSimple('v2-1_768-ema-pruned.ckpt') conditioning = CLIPTextEncode('杰作HDR维多利亚时代女性肖像画,金发,山川自然,蓝天', clip) conditioning2 = CLIPTextEncode('糟糕的手,文本,水印', clip) latent = EmptyLatentImage(768, 768, 1) latent = KSampler(model, 89848141647836, 12, 8, 'dpmpp_sde', 'normal', conditioning, conditioning2, latent, 1) latent2 = LatentUpscale(latent, 'nearest-exact', 1152, 1152, 'disabled') latent2 = KSampler(model, 469771404043268, 14, 8, 'dpmpp_2m', 'simple', conditioning, conditioning2, latent2, 0.5) image = VAEDecode(latent2, vae) SaveImage(image, 'ComfyUI')
比较脚本:
要控制这些功能,请参见settings.example.toml。
您也可以通过CLI使用转译器。
运行时
使用运行时,可以像这样运行ComfyScript:
from comfy_script.runtime import *
load()
from comfy_script.runtime.nodes import *
with Workflow():
model, clip, vae = CheckpointLoaderSimple('v1-5-pruned-emaonly.ckpt')
conditioning = CLIPTextEncode('美丽的自然景观玻璃瓶风景,,紫色星系瓶子,', clip)
conditioning2 = CLIPTextEncode('文本, 水印', clip)
latent = EmptyLatentImage(512, 512, 1)
latent = KSampler(model, 156680208700286, 20, 8, 'euler', 'normal', conditioning, conditioning2, latent, 1)
image = VAEDecode(latent, vae)
SaveImage(image, 'ComfyUI')
在examples/runtime.ipynb
中提供了一个Jupyter Notebook示例。(examples目录下的文件将被Git忽略,您可以在那里放置个人笔记本。)
-
加载后将在
comfy_script/runtime/nodes.pyi
生成类型存根。主流代码编辑器(如VS Code)可以使用它们来帮助编码:为所有提供值列表的参数生成Python枚举。因此,您可以使用以下方式而不是复制粘贴像
'v1-5-pruned-emaonly.ckpt'
这样的字符串:Checkpoints.v1_5_pruned_emaonly # 或 CheckpointLoaderSimple.ckpt_name.v1_5_pruned_emaonly
嵌入也可以被引用为
Embeddings.my_embedding
,这等同于'embedding:my-embedding'
。详情请参见枚举。如果类型存根对您不起作用(无法获得类似截图的结果),请参见类型存根不起作用。
-
运行时默认是异步的。你可以在不等待第一个任务完成的情况下队列多个任务。守护线程将监视并报告队列中剩余的任务和当前进度,例如:
队列剩余:1 队列剩余:2 100%|██████████████████████████████████████████████████| 20/20 队列剩余:1 100%|██████████████████████████████████████████████████| 20/20 队列剩余:0
还提供了一些控制函数:
# 中断当前任务 queue.cancel_current() # 清空队列 queue.cancel_remaining() # 中断当前任务并清空队列 queue.cancel_all() # 当队列为空时调用回调函数 queue.when_empty(callback) # 使用工作流: Workflow(cancel_remaining=True) Workflow(cancel_all=True)
如果你之前是ComfyUI网页界面的用户,请参阅与ComfyUI网页界面的差异,有关运行时的详细信息,请参阅运行时。
示例
绘图
with Workflow():
seed = 0
pos = '天空,1个女孩,微笑'
neg = 'embedding:easynegative'
model, clip, vae = CheckpointLoaderSimple(Checkpoints.AOM3A1B_orangemixs)
model2, clip2, vae2 = CheckpointLoaderSimple(Checkpoints.CounterfeitV25_25)
model2 = TomePatchModel(model2, 0.5)
for color in '红色', '绿色', '蓝色':
latent = EmptyLatentImage(440, 640)
latent = KSampler(model, seed, steps=15, cfg=6, sampler_name='uni_pc',
positive=CLIPTextEncode(f'{color}, {pos}', clip), negative=CLIPTextEncode(neg, clip),
latent_image=latent)
SaveImage(VAEDecode(latent, vae2), f'{seed} {color}')
latent = LatentUpscaleBy(latent, scale_by=2)
latent = KSampler(model2, seed, steps=15, cfg=6, sampler_name='uni_pc',
positive=CLIPTextEncode(f'{color}, {pos}', clip2), negative=CLIPTextEncode(neg, clip2),
latent_image=latent, denoise=0.6)
SaveImage(VAEDecode(latent, vae2), f'{seed} {color} 高清')
自动队列
当队列变空时自动将新工作流加入队列。
例如,可以使用comfyui-photoshop(目前有些bug)在Photoshop中的图像变化时自动执行img2img:
def f(wf):
seed = 0
pos = '1个女孩,生气,竖中指'
neg = 'embedding:easynegative'
model, clip, vae = CheckpointLoaderSimple(Checkpoints.CounterfeitV25_25)
image, width, height = PhotoshopToComfyUI(wait_for_photoshop_changes=True)
latent = VAEEncode(image, vae)
latent = LatentUpscaleBy(latent, scale_by=1.5)
latent = KSampler(model, seed, steps=15, cfg=6, sampler_name='uni_pc',
positive=CLIPTextEncode(pos, clip), negative=CLIPTextEncode(neg, clip),
latent_image=latent, denoise=0.8)
PreviewImage(VAEDecode(latent, vae))
queue.when_empty(f)
截图:
选择和处理
例如,一次生成3张图像,然后让用户决定他们想要高清修复哪些:
import ipywidgets as widgets
queue.watch_display(False, False)
latents = []
image_batches = []
with Workflow():
seed = 0
pos = '天空,1个女孩,微笑'
neg = 'embedding:easynegative'
model, clip, vae = CheckpointLoaderSimple(Checkpoints.AOM3A1B_orangemixs)
model2, clip2, vae2 = CheckpointLoaderSimple(Checkpoints.CounterfeitV25_25)
for color in '红色', '绿色', '蓝色':
latent = EmptyLatentImage(440, 640)
latent = KSampler(model, seed, steps=15, cfg=6, sampler_name='uni_pc',
positive=CLIPTextEncode(f'{color}, {pos}', clip), negative=CLIPTextEncode(neg, clip),
latent_image=latent)
latents.append(latent)
image_batches.append(SaveImage(VAEDecode(latent, vae), f'{seed} {color}'))
grid = widgets.GridspecLayout(1, len(image_batches))
for i, image_batch in enumerate(image_batches):
image_batch = image_batch.wait()
image = widgets.Image(value=image_batch[0]._repr_png_())
button = widgets.Button(description=f'高清修复 {i}')
def hiresfix(button, i=i):
print(f'选择了图像 {i}')
with Workflow():
latent = LatentUpscaleBy(latents[i], scale_by=2)
latent = KSampler(model2, seed, steps=15, cfg=6, sampler_name='uni_pc',
positive=CLIPTextEncode(pos, clip2), negative=CLIPTextEncode(neg, clip2),
latent_image=latent, denoise=0.6)
image_batch = SaveImage(VAEDecode(latent, vae2), f'{seed} 高清')
display(image_batch.wait())
button.on_click(hiresfix)
grid[0, i] = widgets.VBox(children=(image, button))
display(grid)
这个例子使用ipywidgets作为GUI,但也可以使用其他GUI框架。
截图:
用户界面
ipywidgets 用户界面
图像查看器
一个简单的图像查看器,可以显示多张图片,并可选择添加标题。
Solara 用户界面
这些 Solara 小部件可以在 Jupyter Notebook 和网页中使用。
元数据查看器
一个用于查看由 ComfyScript / ComfyUI / Stable Diffusion 网页界面生成的图像元数据的小部件。同时支持工作流 JSON 文件,包括网页界面格式和 API 格式。