9.4 KiB
ComfyUI 批量预览:轻量混淆与性能设计
本文件保留前期研究与基准背景。现有实现已改为独立节点直接使用原生画布预览;当前安装、范围及集成验证结果以 README 为准。原生网格同时显示整批缩略图,节点离开视野后统一释放;Worker 负责还原与校验,浏览器
Image.decode()异步解码一次供原生控件使用。
本稿按用户补充的目标重新设计:用途是减少图片内容检测的误判,重视批量 100 张的处理速度;不包含防盗、密码访问控制或长期密钥管理。用户已确认,服务器上的预览数据可以是浏览器专用二进制,不必保持为能直接打开的“打乱 PNG”。旧项目仅作为需求参考。
推荐方案是“先压缩图片,再对压缩字节做轻量 XOR 混淆;浏览器在 Worker 中还原”。这使处理量取决于压缩后的字节数,不需要重新排列像素、传递块映射或在显示时逐块拼图。XOR 是可逆混淆,不承担密码学保密承诺;协议和还原规则公开即可,界面不增加密码、公私钥或会话解锁流程。
此前建议的 RSA、公私钥会话及 AES 密钥包裹不进入新版基础方案。实际测试表明 AES 运算本身也很快,选择 XOR 的理由是满足当前需求、实现和依赖更少,而不是把标准加密一概当成性能瓶颈。
本机测量支持先优化图片编码和批量显示。
使用 Ryzen 7 2700X、Python 3.12、NumPy/Pillow 和 Chrome 153,对一张公开 Kodak 照片的四个 1024×1024 变体循环处理 100 次,结果如下。它是局部性能测试,不是 100 张真实 ComfyUI 出图或 Kaggle 实测。
| 处理阶段 | 100 次合计 | 平均每次 |
|---|---|---|
| 原尺寸 PNG 编码,压缩级别 1 | 4.313 秒 | 43.13 毫秒 |
| 后端对 PNG 字节做 XOR,包含输出缓冲区分配 | 139.72 毫秒 | 1.40 毫秒 |
| 浏览器 Worker 原地还原 PNG 字节 | 40.30 毫秒 | 0.40 毫秒 |
| 浏览器 XOR 还原及 PNG 解码、Worker 往返 | 1.788 秒 | 17.88 毫秒 |
后端混淆与浏览器还原的计时合计约 0.18 秒 / 100 次;第三行已经包含于第四行,不能重复相加。PNG 编码与解码本来就会产生,不能全算作混淆开销。这些分段时间也不能直接当作实际流水线墙钟时间。
同一组输入改为 384×384、质量 85 的 JPEG 缩略预览后,100 张总载荷从 185.57 MiB 变为 2.58 MiB。缩放、编码、混淆合计约 1.15 秒,浏览器还原和解码等 Worker 往返合计约 0.10 秒。尺寸和有损格式同时变化,这个结果只适用于缩略预览,不代表原图在保持同等画质时也能获得相同压缩率。
完整方法、块重排/AES 对照和测量限制见性能记录。
数据流分为缩略预览和原图读取。
flowchart LR
A[IMAGE 批次] --> B[逐张处理]
B --> C[小尺寸缩略图编码]
B --> D[需要保留的原图编码一次]
C --> E[压缩字节 XOR]
D --> E
E --> F[混淆数据缓存与二进制接口]
F --> G[浏览器 Worker 还原和解码]
G --> H[可见缩略图与当前大图]
缩略预览先以长边 384、JPEG 质量 85 为起点,保留宽高比;需要保留透明度的预览使用支持透明度的格式。这个参数只是默认值,实际图像质量要随实现验收。原图保留原分辨率,要求无损时使用 PNG;本地下载不能把有损缩略图冒充原图。
如果需要保留 100 张原图供查看或下载,每张原图的编码成本仍然存在。可复用的保存和预览路径应共用同一份编码结果,避免同一张图反复转 PNG。按需的是浏览器的原图传输与解码,不能为了省掉编码而长期占住整批 GPU 张量。只需要缩略预览的用法可以省略原图保留。
混淆作用于编码后的字节,保持载荷长度基本不变,不重新压缩已混淆的数据。后端用 NumPy 向量操作,浏览器用 TypedArray;本次后端基准采用独立输出缓冲区,浏览器采用原地还原和可转移 ArrayBuffer。第一版不引入新的密码学依赖。
100 张图片要按有界队列处理,并逐步显示。
- 后端逐张取图、转换、编码和混淆,避免把整个 IMAGE 批次再复制成另一份完整 CPU 图像数组。并发编码从 1~2 个任务起步,以目标环境测试决定,不能给每张图都启动线程。
- 每张可用预览完成后发送小型通知,前端即可显示;全部完成后再发送批次完成状态。结果按任务、节点、图片索引定位,保持生成顺序。用户不需要等待 100 张都处理完成才看到第一张。
- 前端仅加载可见缩略图,打开大图时加载当前原图。请求并发和 Worker 数量有限,不同时启动 100 次下载和解码。
- 图片退出显示范围、切换批次或删除节点时,取消过时请求并释放 ImageBitmap、Blob URL 等资源。迟到的响应不能覆盖新任务的预览。
- 若后续支持采样实时预览,处理来不及时只保留最新帧;最终批次的 100 张图片需要完整的索引和状态,不能使用丢帧策略静默遗漏。
100 张 1024×1024 RGBA 位图仅原始像素就约 400 MiB,尚不含 GPU 纹理、压缩缓存及其他副本。逐步加载和释放的价值不只是减少 CPU 耗时,也能避免浏览器因内存压力卡顿。
缓存和协议保持小而明确。
节点返回与历史记录只保存描述信息和预览 ID。缩略图与原图通过插件自己的 HTTP 二进制接口读取,不将大图转成 Base64 塞进执行消息。通知走现有的自定义文本事件即可,无需改写 ComfyUI 的 WebSocket 二进制解码器。
数据包只保留协议版本、图像类型、长度和还原所需的非秘密参数;确认版本、长度和格式后才处理。需要损坏检测时使用轻量校验码,它不承担身份认证。图像恢复或解码失败必须成为可见错误,不能继续把坏数据当作成功预览。
后端缓存以混淆后的压缩数据为单位,限制总字节、条目数和存活时间。预算必须覆盖声明支持的批次:本次 100 张 1024 原图样本约需 186 MiB 的压缩数据空间,不能默认一个很小的缓存却宣称支持该工作负载。先回收过期和旧批次;不能悄悄删除当前批次中用户尚未查看的图片,容量仍不足时明确报错。更大分辨率和长时间历史保留需要单独确认预算。
批次处理中发生错误时,保留已成功结果及其索引,同时明确标记“批次失败”和失败位置,并向 ComfyUI 传播失败。逐步展示不等于把部分成功伪装成完整成功。缓存过期应显示“预览已过期”,重新执行输出节点时刷新条目并允许上游命中缓存。
插件范围覆盖已接管的图片出口。
新项目使用独立输出节点和前端扩展;Python 可使用当前 V3 节点接口,前端使用 app.registerExtension、公开生命周期钩子和 addDOMWidget。使用实际返回的 widget 对象;字节还原和图像解码放在独立 Worker 文件中,主线程管理界面,不依赖内部 Pinia store 或全局图像对象改写。
当前 ComfyUI 的 PreviewImage 和 V3 ui.PreviewImage 会写临时图片,因此混淆节点直接在内存编码,再提供自己的读取接口。普通 SaveImage 的明文输出不因安装预览插件而改变。
采样实时预览是独立的 WebSocket 图片链路。第一版的配套工作流先关闭这条原生图片输出;支持实时预览时再集中适配,并在后端发送前混淆。核心允许任务覆盖 preview_method,验收必须检查实际执行请求和网络输出。这里也不能只靠浏览器收到原图后再处理。
这是一项降低明文图片预览暴露的工程设计。平台具体扫描哪些出口、是否解析这种格式,以及实际误判是否减少,本次均未在 Kaggle 验证。
后续实现的验收围绕批量正确性、实际响应和内存。
- 用目标机器上的真实图像分别测试 100 张 1024 图和实际常用的更大尺寸;记录首张可见时间、全部完成时间、各阶段耗时、传输字节和峰值内存。
- 全部索引恰好出现一次;点开和下载的是对应原图。覆盖多标签页、取消任务、重跑、翻页、过期和失败中断。
- 混淆还原后编码字节一致;PNG 原图像素一致;缩略图与原图的质量契约分开验证。损坏包、非法尺寸和不支持版本必须可见地失败。
- 检查受控工作流的临时文件、保存输出、HTTP、WebSocket 和历史结果,确认已接管的预览不会绕回明文通道。
- 在声明支持的 ComfyUI 核心/前端组合及节点渲染模式中验证显示、清理和 100 张工作负载,不以本机微基准代替集成测试。
前期研究完成了需求调整、局部性能对照和 Chrome Worker 实验。随后插件已实现,并在隔离的 ComfyUI 0.35.0 / 前端 1.51.10 上通过批量传输及资源清理等集成测试;仍未运行 Kaggle 或 GPU 工作流。详细结果见 README。
核心链路依据:SaveImage / PreviewImage、V3 PreviewImage、WebSocket 图片发送、前端扩展钩子。