背景痛点:传统视频处理为何总“卡壳”
过去一年,我在内部做 AIGC 短片项目时,踩过最大的坑就是“视频链路太长”。
- 先写 Python 脚本拆帧 → 2. 手动拖到 Stable Diffusion WebUI 逐张重绘 → 3. 再写脚本合成 mp4 → 4. 发现色调不对,又回到步骤 2 重跑。
只要其中一步参数调了,后面全部重来,脚本之间靠硬盘文件夹“通信”,复现还得翻聊天记录找参数。
更尴尬的是 GPU 利用率:
- 单张图生成时显卡空等硬盘 IO;
- 批量脚本又常因显存爆掉而中断;
- 想并行跑两条分辨率不同的管线,得再复制一份代码,路径写死,维护噩梦。
总结一句话:流程是“线性脚本 + 人肉调度”,既丢不掉重复劳动,也做不到弹性扩缩。
技术对比:为什么最终选了 ComfyUI
| 方案 | 可复现性 | 可视化排错 | 节点复用 | 自动化难度 | 学习曲线 |
|---|---|---|---|---|---|
| 纯脚本(Python+FFmpeg) | 低(路径/参数散落) | 无 | 低 | 中 | 陡 |
| 传统 GUI 工具(WebUI、PR 插件) | 中(需截图参数) | 有 | 低 | 高(GUI 自动化坑多) | 平缓 |
| ComfyUI | 高(JSON 直接存档) | 有(节点逐点预览) | 高(子图/自定义节点) | 低(DAG→API 一键导出) | 中等 |
核心差异:ComfyUI 把“计算图”显式化,节点=算子,边=张量或文件路径,天然符合 DAG 调度。
对开发者来说,Git 里存一份.json工作流,就能在任意机器精确复现,CI/CD 友好。
核心实现:节点化设计原理与视频管线拼装
1. DAG 视角看 ComfyUI
ComfyUI 的后端实际就是一张有向无环图:
- 每个节点运行前,框架会拓扑排序,自动解决依赖;
- 节点输出缓存在内存字典,后续节点若参数未变,直接命中缓存(Memoization);
- 对视频任务,意味着“只要帧率不改,提取节点只跑一次”,极大节省 IO。
2. 视频处理专用节点速览
官方仓库已给出VideoCombine、VHS_LoadVideo、VHS_SplitFrames等节点。
自己写自定义节点时,只要继承comfyui.nodes.NODE_CLASS_MAPPINGS,把FUNCTION指向一个标准(self, **kwargs)接口即可。
下面是我常用的“最小可运行视频重绘单元”:
- LoadVideo→ 拆帧到临时目录;
- BatchStableDiffusion→ 对帧序列做图生图;
- VideoCombine→ 把新帧 + 原音频封装回 mp4。
三步即可串成一条 Pipeline,参数全部外露,调优不用改代码。
代码示例:一份带注释的完整 JSON 工作流
把下面内容直接Ctrl+O导入 ComfyUI,即可跑通“480p→720p 超分 + 色彩修复”示例。
关键字段我都加了//注释,方便二次开发时快速定位。
{ "1": { "inputs": { "video": "/data/input.mp4", "force_rate": 24, // 与输出帧率保持一致,避免音画不同步 "cache_frames": true // 显存够就打开,重复调试时省 IO }, "class_type": "VHS_LoadVideo" }, "2": { "inputs": { "frames_dir": ["1", 0], // 上游节点输出路径 "batch_size": 16, // 一次喂给 SD 的帧数,按 GPU 显存调 "resolution": 1280 // 目标短边长度 }, "class_type": "BatchStableDiffusion", "extra": { "prompt_pos": "masterpiece, 4k, vibrant color", "prompt_neg": "blurry, lowres", "denoise": 0.55 // 太重会丢原图信息,太轻看不出修复效果 } }, "3": { "inputs": { "frames_dir": ["2", 0], "fps": 24, "crf": 18, // 压制质量,越小越清晰,体积越大 "audio_source": ["1", 1] // 把原音频轨道直接混流 }, "class_type": "VideoCombine" } }批量处理脚本(Python 3.9+)
当素材目录有一堆.mp4,可用下面脚本循环提交工作流,并把 GPU 占满。
# submit_all.py import json, subprocess, pathlib, argparse, time TEMPLATE = json.load(open("workflow.json")) COMFY_CLI = "/opt/ComfyUI/venv/bin/python3 main.py" def submit(video_path: pathlib.Path): wf = TEMPLATE.copy() wf["1"]["inputs"]["video"] = str(video_path) tmp_wf = video_path.with_suffix(".json") json.dump(wf, tmp_wf.open("w"), indent=2) # --workflow 参数为社区 PR,未合并前可用重定向 subprocess.Popen([COMFY_CLI, "--workflow", str(tmp_wf)]) time.sleep(5) # 给显存初始化留空档,避免多进程抢显存 if __name__ == "__main__": parser = argparse.ArgumentParser() parser.add_argument("folder", type=pathlib.Path) args = parser.parse_args() for vid in args.folder.glob("*.mp4"): submit(vid)跑法:
- 把
workflow.json放到同目录; python submit_all.py /data/raw_videos;- 脚本会逐个生成对应 JSON 并后台拉起 ComfyUI 进程,实现“离线队列”。
性能考量:让 309 卡全程 95%+
帧缓存策略
- 显存 < 12G 时,把
cache_frames关断,帧拆到 SSD,节点内部用np.memmap只读映射; - 显存 > 24G 可全程放显存,随机访问帧做 augmentation 时提速 3×。
- 显存 < 12G 时,把
BatchSize 与并行度
- SD 1.5 在 24G 卡上,单张 512×512 约 4.2G,留给 CUDA kernel 余量,batch=8 是甜点;
- 想跑两条管线,可在
extra里把device_index指定不同卡,ComfyUI 会自动torch.cuda.set_device。
计算图剪枝
对实验阶段,常把PreviewImage节点挂得到处都是,它们会锁张量。
生产环境用comfyui-cli --prune-optional可自动裁剪无关节点,显存占用降 15% 左右。
避坑指南:来自踩坑一线的 checklist
- 路径含中文 → Linux 下
libx264编码器会异常中断,统一拼音目录。 - 帧率小数 → 23.976 一定写成
24000/1001,否则音画漂移。 - 节点输出重名 → 多进程并发时互相覆盖,建议在
VideoCombine输出文件名加${timestamp}变量。 - 忘记关
PreviewImage→ 显存爆炸,生产环境用--disable-preview启动参数。 - 合成时音频采样率不一致 → 提前用
ffmpeg -ar 48000统一,否则 ComfyUI 会静默失败。
部署建议:
- 容器镜像里把
torch与xformers锁版本,升级 CUDA 驱动即可; - 用 NFS 挂共享模型库,避免每个 Pod 重复拉 4G+ 的
.safetensors; - 对外暴露
/promptREST,前端传 JSON,即可把 ComfyUI 当“视频推理微服务”。
互动环节:动手挑战
任务:给你一段 10 秒 60fps 的竖屏视频(1080×1920),请在 ComfyUI 里搭建工作流,实现“卡通化 + 帧率转 24fps + 体积 < 5MB” 的导出目标。
要求:
- 上传最终
.json工作流与输出视频截图; - 记录 GPU 占用峰值与总耗时;
- 在评论区贴出你最想优化的节点(例如“色彩抖动”),下周我会选 3 份作业直播点评。
把流程图一存,下次需求变更,只需拖拽两下就能交差——再也不用半夜三点改脚本路径了。祝你玩得开心,显存常驻,风扇不飙。