StoryDiffusion 更新演进全解读:多角色一致生成、[NC] 标记与漫画工作流实战
StoryDiffusion 更新演进全解读:多角色一致生成、[NC] 标记与漫画工作流实战
StoryDiffusion 是一个面向长序列角色一致图像与视频生成的扩散模型项目(NeurIPS 2024 Spotlight 论文工作),其核心能力包括基于一致自注意力(Consistent Self-Attention)的角色一致性漫画生成,以及基于运动预测器的视频生成。本文以仓库中的 update.md 为骨架,逐条解读该项目的功能演进历史——包括双角色支持、生成结果自动保存、提示词填写方式变更、[NC] 无角色标记、Ref Image 分辨率选型与 Pillow 兼容性修复——并结合仓库源码(Gradio 应用、Pipeline 与工具函数)深入剖析每个更新点背后的实现原理与实操要点。读完本文,你将掌握 StoryDiffusion 完整的提示词语法、多角色/单角色两种模式的适用边界、超参数调优方法,以及如何看懂并扩展其漫画排版与字幕流程。
一、update.md:项目的功能演进日志
update.md 是 StoryDiffusion 仓库中的更新历史文档,README.md 明确指出"访问 update.md 可查看更新历史"(You can visit here to visit update history)。它记录了项目从 2023 年 5 月到 2024 年 5 月的两次重要功能更新,以及一个待办事项清单和社区贡献方向。
与 README 的 Key Features 部分(长序列角色一致生成 + 长视频运动预测两条主线)相呼应,update.md 记录的更新均落在**漫画生成(Comic Generation)**这条主线上,包括:
| 时间 | 更新要点 |
|---|---|
| 2023-05-14 | 支持双角色生成;生成结果自动保存到 results 文件夹;提示词填写方式变更(以示例为准) |
| 2024-05-08 | Ref Image 模式支持 [NC] 标记;PhotoMaker 在 1024×1024 分辨率下效果最佳但显存开销大;合并 Pillow 兼容性修复 |
下面逐节展开,并配合源码给出可验证的实现细节。
二、2023-05-14 更新:双角色支持与结果自动保存
2.1 支持双角色:从单人到双人的角色一致生成
本次更新为故事分镜生成了双角色(Two persons)支持,并预告未来可以扩展更多角色。在当前的 Gradio 应用 中,这一能力以方括号角色标记语法呈现,例如示例中的 [Bob]、[Alice]:
[Bob] A man, wearing a black suit
[Alice] a woman, wearing a white shirt
其底层解析逻辑位于 utils/gradio_utils.py 的 character_to_dict 函数:按行读取"人物文字描述"输入框(general_prompt)中的内容,提取 <a href="https://link.gitcode.com/i/1ee0864fbf609c9323fde2f058aa2e36" target="_blank">角色名] 作为 key、] 之后的文本作为该角色的描述 value,构建 character_dict。随后 process_original_prompt([utils/gradio_utils.py)会把分镜提示词中出现的角色标记替换为对应的人物描述,并从前 id_length 个包含该角色的提示词中抽取"参考帧(ref)"索引,用于一致自注意力模块收集该角色的身份特征。
从源码结构看,多角色能力与以下关键机制相关:
id_length超参数:Gradio 界面中的id_length_滑块(范围 1~4,默认 1)决定"作为身份参考的图像数量",源码中total_length = id_length + 1(见 gradio_app_sdxl_specific_id_low_vram.py)。每个角色至少需要id_length条独立出现(不含其他角色混入)的提示词来充当参考帧,否则会触发"not have enough prompt description"报错。SpatialAttnProcessor2_0的id_bank:在生成参考帧阶段(write=True),处理器把各角色的自注意力中间特征按角色 key、按去噪步数存入id_bank(gradio_app_sdxl_specific_id_low_vram.py);在生成后续场景帧时,再取出对应角色的特征拼接到 encoder 侧参与注意力计算(L185-L190),从而跨帧维持角色外观一致。
2.2 局限说明:PhotoMaker 模式当前仅支持单角色
update.md 同时强调:在 PhotoMaker(Ref Image)模式下,单张图片中目前只能出现一个人物。这一点在 process_generation 中有两处硬校验:
general_prompt超过 3 行角色描述时报错:"Support for more than three characters is temporarily unavailable due to VRAM limitations";- PhotoMaker 模式下若某帧提示词同时命中多个角色,则报错 "Temporarily Not Support Multiple character in Ref Image Mode!"(gradio_app_sdxl_specific_id_low_vram.py)。
也就是说:纯文字描述(Only Using Textual Description)模式支持多角色同框;引用照片(Using Ref Images / PhotoMaker)模式当前只支持单角色。选用哪种模式,取决于你是想"凭空塑造角色"还是"锁定某人的长相"。
2.3 自动保存:生成结果写入 results 文件夹
本次更新加入了生成结果的自动保存。在 gradio_app_sdxl_specific_id_low_vram.py 的 save_results 函数中可以看到具体实现:
def save_results(unet, img_list):
timestamp = datetime.datetime.now().strftime("%Y%m%d-%H%M%S")
folder_name = f"results/{timestamp}"
weight_folder_name = f"{folder_name}/weights"
...
for idx, img in enumerate(img_list):
file_path = os.path.join(folder_name, f"image_{idx}.png")
img.save(file_path)
即每次生成都会在仓库根目录下的 results/{时间戳}/ 中保存 image_0.png、image_1.png…… 顺序编号的图片,并预留 weights 子目录用于(当前以注释形式保留的)角色权重保存。process_generation 在流程末尾调用 save_results(pipe.unet, total_results)(gradio_app_sdxl_specific_id_low_vram.py),因此无论是单帧预览还是最终排版漫画,都会被自动落盘,方便后续整理与二次使用。
2.4 提示词填写方式变更
update.md 提示"提示词填写方式已改变,请参考示例"。结合 Gradio 界面的说明文字(gradio_app_sdxl_specific_id_low_vram.py),当前的标准流程分为四步:
- 角色描述(
(1) Textual Description for Character):输入[角色名] 外观描述,每个角色一行。若使用 Ref Image,需在类词后紧跟触发词img,例如man img、woman img、girl img; - 分镜提示词数组(
(3) Comic Description):每行对应一张生成图像,行首用[角色名]指明该帧主角; - 选择风格模板(Style template);
- 点击 Generate 开始生成。
三、2024-05-08 更新:[NC] 标记、分辨率选型与 Pillow 兼容
3.1 [NC] 标记:在 Ref Image 模式下生成"无角色"场景帧
这是本次更新最重要的功能点:在提示词行首添加 [NC],即可告诉模型"该帧画面中不出现任何角色",用于生成纯场景(如落叶、老虎、藏宝屋)过渡镜头。
以 Gradio 应用内嵌示例 为例:
[Bob] at home, read new paper #at home, The newspaper says there is a treasure house in the forest.
[NC] A tiger appeared in the forest, at night
[NC] The car on the road, near the forest #They drives to the forest in search of treasure.
[Bob] and [Alice] running very fast, in the forest, at night
实现层面分为两段:
- 前端校验与剥离:
process_generation扫描分镜提示词,命中<a href="https://link.gitcode.com/i/1b24b1c03ac432a61160e81392d160c4" target="_blank">NC]的行记录到nc_indexs,并限制"前id_length行是身份参考行,不能使用 [NC]";随后把[NC]前缀从提示词中剔除([gradio_app_sdxl_specific_id_low_vram.py)。 - Pipeline 层跳过身份融合:生成该帧时传入
nc_flag=True。在 utils/pipeline.py 的encode_prompt_with_trigger_word中,nc_flag=True会直接返回None, None, None(不编码触发词、不注入 PhotoMaker ID 图像),后续去噪循环中该帧全程使用纯文本 embedding(utils/pipeline.py),从而保证画面中不出现参考角色。
update.md 中嵌入的 results_examples/image1.png 正是这一能力的效果示例:一个 8 格连续寻宝故事中,既有多角色动作帧(读报、林中奔跑、发现宝库),也有 [NC] 场景帧(森林中的老虎、林中轿车、夜间藏宝屋)。
3.2 Ref Image Model 的分辨率与显存权衡
update.md 明确指出:PhotoMaker 在 1024×1024 分辨率下效果最好,但会消耗大量 GPU 显存,因此建议在显存允许的范围内尽可能使用更高分辨率(use the res. as large as possible)。
这一点在官方示例中也能得到印证:双角色 Ref Image 示例将宽高设为 1024×1024(gradio_app_sdxl_specific_id_low_vram.py),而纯文字示例多为 768×768。同时,为了照顾低显存环境,仓库提供了专门的 gradio_app_sdxl_specific_id_low_vram.py,README 说明它曾在 24GB 显存(Tesla A10)+ 30GB 内存的机器上测试,预期在 >20GB 显存下运行良好;脚本内通过 enable_model_cpu_offload()、enable_vae_slicing() 和高效索引注意力掩码(cal_attn_indice_xl_effcient_memory,见 utils/gradio_utils.py)来压低显存峰值。
实际选型建议(依据源码默认值与界面滑块范围):
| 配置项 | 界面默认值 | 可选范围 | 说明 |
|---|---|---|---|
| height / width | 768 | 256~1024(步长 32) | PhotoMaker 模式建议 1024×1024 以获得最佳效果,显存受限时按需下调 |
| num_steps | 35 | 20~100 | 采样步数,步数越多细节越充分、耗时越久 |
| sa32 / sa64 | 0.5 | 0~1.0(步长 0.1) | 32×32 与 64×64 自注意力层上"配对注意力"作用的程度,值越大跨帧一致性越强 |
| id_length | 1 | 1~4 | 身份参考图像数量,越大身份锁定越稳,但需要的有效提示词行数越多 |
| guidance_scale | 5 | 0.1~10.0 | 提示词引导强度 |
| Seed | 0 | -1~2^31-1 | -1 代表随机,点击 🎲 可随机化 |
其中 sa32/sa64 与 utils/gradio_utils.py 的 cal_attn_mask_xl 直接相关:该函数以概率 sa32/sa64 采样出每帧 token 是否允许"看到"其他帧的 token,并强制每帧与自身 token 保持自注意力,从而构成"选择性跨帧注意力"的一致性机制。
3.3 Pillow 兼容性修复
update.md 记录了一条由社区贡献者 @cryptowooser 合并的 Pull Request,用于支持最新版 Pillow;同时提醒:如果你使用旧版本 Pillow,可能需要自行升级(you may need to update pillow if using the old version)。
从当前 requirements.txt 看,项目通过 pip install -r requirements.txt 安装依赖,其中未对 Pillow 版本做硬性 pin 死(依赖项由 diffusers、gradio 等间接引入),因此在新环境中建议使用较新 Pillow。仓库中与 Pillow 直接相关的排版逻辑(画布拼接、边框、字幕渲染)集中在 utils/utils.py,例如 ImageOps.expand(加白边)、ImageDraw.textbbox(多行文本换行计算)、Image.LANCZOS 缩放等 API,对 Pillow 版本较敏感,升级后请回归验证漫画排版输出是否正常。
四、Todo 与社区贡献:字幕与排版样式的演进方向
4.1 待办:为经典漫画排版风格的全部分镜添加字幕
update.md 将"为所有图像添加字幕,用于经典漫画排版风格(classical Comic Typesetting Style)"列为 Todo。从当前仓库源码看,这一能力在 utils/utils.py 中已有相当完整的实现基础:
get_comic_classical(utils/utils.py)会把生成的帧按[4, 6]分组、随机排布成"一图一格 / 两图竖拼"的经典漫画版式,并通过get_caption_group把字幕列表按组切分,再在get_row_image2中对单图帧调用add_caption渲染字幕;add_caption(utils/utils.py)实现了底部居中/左/右三种字幕位置、自动换行、半透明背景条等功能,支持从fonts/目录选择字体(界面默认 fonts/Inkfree.ttf);- 字幕来源遵循"默认用该行提示词作为字幕,如需自定义则在行尾加
#,#之后的内容作为字幕"的规则(见version说明文本,gradio_app_sdxl_specific_id_low_vram.py),例如:
[Bob] at home, read new paper #at home, The newspaper says there is a treasure house in the forest.
# 之前的部分参与图像生成,# 之后的部分(at home, The newspaper says...)作为该帧的漫画字幕。处理时由 process_generation 用 prompt.rpartition("#")<a href="https://link.gitcode.com/i/69237d4e9a51f590c57ea867da5935d8" target="_blank">0] 分离生成提示词([gradio_app_sdxl_specific_id_low_vram.py),再由 get_comic 分支把 # 后内容作为 captions 传入排版函数。
4.2 欢迎贡献:更多布局样式
update.md 明确向社区开放贡献"各种布局样式(Various layout styles)"。当前代码中布局样式通过 comic_type 下拉框选择,支持三种:
| 选项 | 实现函数(utils/utils.py) | 特点 |
|---|---|---|
| No typesetting (default) | get_comic 直接返回原图 |
不排版,逐帧输出 |
| Four Pannel | get_comic_4panel |
每 4 帧拼成一页,帧底加字幕,不足 4 帧用填充图补齐 |
| Classic Comic Style | get_comic_classical |
按 4/6 分组随机版式,单图帧加字幕、双图帧竖拼缩放 |
若希望贡献新布局,可参照 get_comic_classical / get_comic_4panel 的接口(输入图像列表、字幕列表、字体与填充底图,输出拼好的漫画页),并在 get_comic 的 types 分支中注册新样式。
五、如何亲身体验这些更新能力
根据 README.md,本地体验有两种途径:
方式一:Jupyter Notebook 打开仓库根目录的 Comic_Generation.ipynb 并依次运行单元格。
方式二:本地 Gradio 演示(推荐低显存版)
python gradio_app_sdxl_specific_id_low_vram.py
运行前请确认环境满足:Python ≥ 3.8(推荐 conda 新建 3.10 环境)、PyTorch ≥ 2.0,并安装依赖:
conda create --name storydiffusion python=3.10
conda activate storydiffusion
pip install -U pip
pip install -r requirements.txt
应用启动后会自动加载 SDXL 基础模型。默认模型在 config/models.yaml 中配置,支持四选一:
Juggernaut:
path: "https://huggingface.co/RunDiffusion/Juggernaut-XL-v9/blob/main/Juggernaut-XL_v9_RunDiffusionPhoto_v2.safetensors"
single_files: true # true 表示单文件 safetensors(Civitai 风格)
use_safetensors: true
RealVision:
path: "SG161222/RealVisXL_V4.0"
single_files: false
SDXL:
path: "stabilityai/stable-diffusion-xl-base-1.0"
single_files: false
Unstable:
path: "stablediffusionapi/sdxl-unstable-diffusers-y"
single_files: false
use_safetensors: false
加载逻辑见 utils/load_models_utils.py:single_files 为 true 时走 StableDiffusionXLPipeline.from_single_file,否则走 from_pretrained;PhotoMaker 模式则额外调用 load_photomaker_adapter 注入 photomaker-v1.bin(首次运行自动从 Hugging Face 的 TencentARC/PhotoMaker 仓库下载到本地 data/ 目录,gradio_app_sdxl_specific_id_low_vram.py)。
验证更新能力的建议操作路径:
- 在界面示例区(Examples)选择"双角色寻宝故事"示例,观察自动保存到
results/{时间戳}/的逐帧图片; - 将模型类型切到
Using Ref Images,上传examples/twoperson/中的参考图,把分辨率调到 1024×1024,对比角色锁定效果与显存占用; - 在分镜提示词中新增一行
[NC] ...场景描述,确认该帧不出现角色; - 切换
Four Pannel与Classic Comic Style,用#语法自定义字幕,观察排版与字幕渲染差异。
六、总结
update.md 用两次更新勾勒出 StoryDiffusion 漫画生成能力的三条演进主线:从单人扩展到多角色(伴随 PhotoMaker 单角色边界)、从纯文本约束到 NC] 显式场景控制、从手工整理到自动落盘与生态兼容。结合源码可以看到,这些"更新日志"背后是 character_to_dict/process_original_prompt 的角色解析、SpatialAttnProcessor2_0 的跨帧 id_bank、nc_flag 的跳过身份注入、save_results 的时间戳归档,以及 get_comic_* 系列的排版渲染等一整套可扩展的实现。对想要二次开发或复现实验的研究者而言,[utils/ 目录下的工具函数与 gradio_app_sdxl_specific_id_low_vram.py 的 process_generation 是理解全流程的最佳入口;对希望贡献新布局样式的社区开发者,则可以直接从 utils/utils.py 的排版函数入手。
