ComfyUI 图片里藏着的秘密

posts/comfy-ui-png-secret

最近用了用 ComfyUI,发现一张 PNG 图片竟然可以直接拖动到 ComfyUI 里面形成工作流,对此感到好奇,本文将对其原理进行分析。

这和 EXIF 有什么关系

很多人会把图片元数据统称为 “EXIF”,但其实 EXIF(Exchangeable Image File Format) 主要是 JPEG、TIFF 等格式使用的标准,而 PNG 并不原生支持 EXIF。PNG 有自己独立的文本元数据机制,即 tEXt、zTXt 和 iTXt 这些 Chunk。

所以,ComfyUI 原生保存的 PNG 并不是把数据塞进 EXIF 里,而是写进了 PNG 自带的文本 Chunk 中。只有当你通过第三方插件把图片保存为 JPEG、WebP 等格式时,为了兼容性和让 ComfyUI 能继续读取,插件才会把同样的 JSON 数据塞进对应格式的 EXIF 字段(例如 UserComment、Make、ImageDescription)里。

PNG 的 Chunk 机制

PNG 文件并非只是一张图片的裸像素数据,它的结构是由一个固定的文件头(89 50 4E 47 0D 0A 1A 0A)加上一系列数据块(Chunk)组成的。每个 Chunk 的结构如下:

+-------------+--------------+-------------+----------+
|   Length    |     Type     |    Data     |   CRC    |
|  (4 bytes)  |  (4 bytes)   |  (N bytes)  | (4 bytes)|
+-------------+--------------+-------------+----------+

常见的 Chunk 类型有:

  • IHDR:图像头,包含宽高、位深度、颜色类型等基础信息
  • IDAT:经过压缩的图像像素数据
  • IEND:文件结束标记
  • tEXt / zTXt / iTXt:文本元数据块

其中 tEXt、zTXt、iTXt 属于辅助块(ancillary),常规的图片查看器会忽略它们,但这三个 Chunk 正是 ComfyUI 用来存储工作流数据的载体。

ComfyUI 嵌入了什么

ComfyUI 在生成 PNG 时,会把两类数据写入文本 Chunk:

Chunk 关键字内容说明
prompt执行数据。包含节点类型、输入参数、节点之间的连接关系。这是复现图像生成的核心数据。
workflow视觉布局。包含节点在画布上的坐标、大小、分组等 UI 状态。用于在 ComfyUI 界面中还原出原工作流的布局。

对于数据量较大的情况,ComfyUI 通常使用 zTXt Chunk 进行存储。zTXt 与 tEXt 的区别在于它对文本内容做了 zlib/deflate 压缩,可减少大体积 JSON 对文件大小的影响。

手动提取验证

可以用一段 Python 脚本来读取 PNG 里嵌入的 prompt 和 workflow:

import struct
import zlib

def read_png_metadata(path):
    with open(path, "rb") as f:
        header = f.read(8)
        assert header == b'\x89PNG\r\n\x1a\n'

        while True:
            length_bytes = f.read(4)
            if len(length_bytes) < 4:
                break
            length = struct.unpack(">I", length_bytes)[0]
            chunk_type = f.read(4)
            data = f.read(length)
            f.read(4)  # skip CRC

            if chunk_type == b'tEXt':
                keyword, text = data.split(b'\x00', 1)
                print(f"[tEXt] {keyword.decode('latin-1')}: {text[:200]}...")
            elif chunk_type == b'zTXt':
                keyword, rest = data.split(b'\x00', 1)
                compression_method = rest[0]
                compressed_text = rest[1:]
                text = zlib.decompress(compressed_text)
                print(f"[zTXt] {keyword.decode('latin-1')}: {text[:200]}...")

read_png_metadata("your_image.png")

如果你不想写代码,直接用 exiftool 也能快速查看:

exiftool -a -u your_image.png

在输出中查找 Prompt 或 Workflow 关键字可看到 JSON 内容。

注意事项

Photoshop、GIMP、Canva 以及大部分在线图片编辑工具在重新导出 PNG 时,会丢弃掉这些文本 Chunk。如果你后期修过图,再用这张图往 ComfyUI 里拖,就会发现读不出工作流了。需要保留工作流的话,建议把原图和修图后的版本分开存档。

此外,在部分社交平台发图片时会主动剥离 EXIF 和 PNG 文本 Chunk,工作流的原信息会丢失,如需保留可发送原文件。

参考链接