AI 工具

用 Stable Diffusion 批量生成种子网格:记录提示词、耗时与图像哈希

同一个提示词,换种子差别很大。先批量出小图挑种子,再出正式图,效率最高。 这篇做 seedgrid.py:一次生成“提示词 × 种子”的网格图,并把每张图的参数、耗时和像素哈希写进 runs.csv。

BINBHO · 图文教程

同一个提示词,换种子差别很大。先批量出小图挑种子,再出正式图,效率最高。 这篇做 seed_grid.py:一次生成“提示词 × 种子”的网格图,并把每张图的参数、耗时和像素哈希写进 runs.csv。

先看清成品

  • 2 个提示词 × 4 个种子 = 8 张,每张 4.0~4.8 秒,生成网格图。
  • 实测中文提示词:两个种子都只画出了雾气中的山,没有村庄,也没有日出的暖色;同样种子的英文提示词则符合描述。脚本现在遇到中文提示词会给出提醒。
  • 发现并修复:网格标签中的中文显示为方块(字体顺序问题)。pytest 3 项通过。

合规提醒:sd-turbo 使用 Stability AI Community License:研究、非商业用途,以及年收入低于 100 万美元的个人或组织的商业用途免费;超过的需要企业许可,以模型页面和 stability.ai/license 为准。sd-turbo 仓库不含安全检查器(diffusers 加载时会提示):不要把未经过滤的生成服务公开给他人使用。不生成真实人物的误导性图像,不对他人照片做未经同意的修改;图生图只处理你有权使用的图片。模型从 Hugging Face 官方仓库下载,不使用来路不明的模型文件。

运行说明:本篇在 box 上真实运行了 Stable Diffusion:stabilityai/sd-turbo(revision b261bac6),diffusers 0.41.0,torch 2.14.1(CPU 版),8 核 CPU、无显卡。512×512、1 步生成一张约 4~5 秒。文中的图片都是这次运行生成的。没有测试 GPU,也没有运行 ComfyUI 和 SD 3.5(SD 3.5 需要在 Hugging Face 上同意许可后才能下载,且 CPU 上太慢)。

做完之后你会得到什么?

成果 说明
seed_grid.py 生成网格和 runs.csv
txt2img.py 与 A 篇相同
prompts.txt 示例提示词
examples/ 本文生成的两张网格图
test_grid.py 3 项测试

需要准备哪些环境?

项目 说明
Python 3.10 以上
PyTorch CPU:pip install torch --index-url https://download.pytorch.org/whl/cpu;有 NVIDIA 显卡时按 PyTorch 官网选择 CUDA 版本
diffusers 等 pip install diffusers transformers accelerate safetensors pillow pytest
磁盘 sd-turbo 的 fp16 权重约 2.5 GB
内存 CPU 运行时建议 8 GB 以上(本文机器 15 GB)

关键事实

事实 来源
sd-turbo 使用 CLIP 文本编码器,训练语料以英文为主 sd-turbo 模型页面(基于 SD 2.1);中文效果为本文实测,仅 2 个种子
同一种子在同一环境下结果确定 本文实测:两次运行的像素完全一致

文件结构

75-stable-diffusion-B-src/
├── seed_grid.py
├── txt2img.py
├── prompts.txt
├── test_grid.py
└── examples/
    ├── sheet.jpg
    └── zh_vs_en.jpg

第 1 步:提示词

# 每行一个提示词(英文效果更稳定)
a watercolor illustration of a quiet mountain village at sunrise, soft light, mist
a ceramic teapot on a wooden table, studio product photo, soft shadows

第 2 步:脚本

"""一次生成“提示词 × 种子”的小图网格(contact sheet),并把每张图的参数和耗时写进 runs.csv,方便挑选种子再出正式图。
复用 75A 的 txt2img.py。CPU 上每张约 5 秒,先用少量种子试。"""
import argparse, csv, os, re, sys, time
from PIL import Image, ImageDraw, ImageFont
import txt2img as T

def font(size):
    # 先找中文字体:DejaVu 没有汉字,中文提示词的标签会变成方块(调试时实际出现过)
    for p in ("/usr/share/fonts/opentype/noto/NotoSansCJK-Regular.ttc", "/usr/share/fonts/truetype/wqy/wqy-microhei.ttc",
              "C:/Windows/Fonts/msyh.ttc", "/System/Library/Fonts/PingFang.ttc", "/usr/share/fonts/truetype/dejavu/DejaVuSans.ttf"):
        if os.path.exists(p): return ImageFont.truetype(p, size)
    return ImageFont.load_default()

def sheet(cells, prompts, seeds, thumb=192, pad=24):
    W, H = len(seeds) * thumb, len(prompts) * (thumb + pad)
    img = Image.new("RGB", (W, H + pad), "white"); d = ImageDraw.Draw(img); f = font(14)
    for j, s in enumerate(seeds): d.text((j * thumb + 4, 4), f"seed {s}", fill="black", font=f)
    for i, p in enumerate(prompts):
        y = pad + i * (thumb + pad)
        d.text((4, y + thumb + 4), f"P{i + 1}: {p[:60]}", fill="black", font=f)
        for j, s in enumerate(seeds): img.paste(cells[(i, j)].resize((thumb, thumb)), (j * thumb, y))
    return img

def main(argv=None):
    ap = argparse.ArgumentParser(); ap.add_argument("prompts", help="每行一个提示词的文本文件"); ap.add_argument("--seeds", default="1,2,3,4")
    ap.add_argument("--steps", type=int, default=1); ap.add_argument("--out", default="grid"); a = ap.parse_args(argv)
    prompts = [l.strip() for l in open(a.prompts, encoding="utf-8") if l.strip() and not l.startswith("#")]
    seeds = [int(s) for s in a.seeds.split(",")]
    for p in prompts:
        if re.search(r"[\u4e00-\u9fff]", p):
            print(f"提醒:“{p[:20]}…”是中文。sd-turbo 的文本编码器(CLIP)主要按英文训练,实测中文提示词的画面与描述明显不符,建议改用英文。")
    os.makedirs(a.out, exist_ok=True); pipe = T.load(); cells = {}
    with open(os.path.join(a.out, "runs.csv"), "w", newline="", encoding="utf-8") as f:
        w = csv.writer(f); w.writerow(["file", "prompt", "seed", "steps", "seconds", "pixel_hash"])
        for i, p in enumerate(prompts):
            for j, s in enumerate(seeds):
                t = time.time(); img = T.generate(pipe, p, s, a.steps); sec = round(time.time() - t, 1)
                fn = f"p{i + 1}_s{s}.png"; h = T.save(img, os.path.join(a.out, fn), {"prompt": p, "seed": s, "steps": a.steps, "model": T.MODEL})
                w.writerow([fn, p, s, a.steps, sec, h]); cells[(i, j)] = img; print(f"{fn}  {sec} 秒")
    sheet(cells, prompts, seeds).save(os.path.join(a.out, "sheet.jpg"), quality=88)
    print(f"{len(cells)} 张 → {a.out}/sheet.jpg,参数见 {a.out}/runs.csv"); return 0

if __name__ == "__main__":
    sys.exit(main())

第 3 步:测试

"""test_sheet_layout 不需要模型;test_small_run 会真实生成 2 张图(约 10 秒)。"""
import csv, os
from PIL import Image
import seed_grid as G

def test_cjk_font_first():
    f = G.font(14)
    assert "CJK" in getattr(f, "path", "") or "msyh" in getattr(f, "path", "") or "PingFang" in getattr(f, "path", "") or "wqy" in getattr(f, "path", "")

def test_sheet_layout():
    cells = {(i, j): Image.new("RGB", (512, 512), (i * 100, j * 100, 0)) for i in range(2) for j in range(3)}
    s = G.sheet(cells, ["水彩山村", "teapot"], [1, 2, 3])
    assert s.size == (3 * 192, 2 * (192 + 24) + 24)

def test_small_run(tmp_path, capsys):
    p = tmp_path / "p.txt"; p.write_text("# 注释\n水彩画:山村\n", encoding="utf-8")
    assert G.main([str(p), "--seeds", "5,6", "--out", str(tmp_path / "g")]) == 0
    rows = list(csv.DictReader(open(tmp_path / "g" / "runs.csv", encoding="utf-8")))
    assert [r["file"] for r in rows] == ["p1_s5.png", "p1_s6.png"] and rows[0]["pixel_hash"] != rows[1]["pixel_hash"]
    assert os.path.exists(tmp_path / "g" / "sheet.jpg") and "建议改用英文" in capsys.readouterr().out

第 4 步:运行

$ python seed_grid.py prompts.txt --seeds 1,2,3,4
p1_s1.png  4.6 秒
p1_s2.png  4.8 秒
p1_s3.png  4.3 秒
p1_s4.png  4.3 秒
p2_s1.png  4.1 秒
p2_s2.png  4.2 秒
p2_s3.png  4.1 秒
p2_s4.png  4.0 秒
8 张 → grid/sheet.jpg,参数见 grid/runs.csv

2 个提示词 × 4 个种子

$ python seed_grid.py zh.txt --seeds 1,2       # 第 1 行中文,第 2 行同义英文
提醒:“水彩画:日出时宁静的山村,柔和的光线,薄…”是中文。sd-turbo 的文本编码器(CLIP)主要按英文训练,实测中文提示词的画面与描述明显不符,建议改用英文。
p1_s1.png  5.5 秒
p1_s2.png  4.8 秒
p2_s1.png  4.4 秒
p2_s2.png  4.4 秒
4 张 → zhgrid/sheet.jpg,参数见 zhgrid/runs.csv

中文与英文提示词对比

$ python -m pytest -q
3 passed

怎么检查结果?

  • runs.csv 中每张图都有种子和像素哈希,挑中后用 A 篇以同样参数重新生成,哈希应一致。
  • 网格标签中的中文应正常显示。
  • 时间:CPU 上每张约 4~5 秒,8 张约 40 秒。

调试示例

1. 中文提示词效果差。 我用同一组种子对比了“水彩画:日出时宁静的山村,柔和的光线,薄雾”和同义的英文提示词。中文版两张都是灰色的雾山,没有村庄,也没有日出的颜色;英文版两张都有村庄和暖色光线(见上图)。只测了 2 个种子,但差别很明显。sd-turbo 的文本编码器以英文为主,所以建议提示词用英文写。

2. 标签变成方块。 第一版字体列表先找 DejaVuSans,它没有汉字,中文提示词的标签显示为一串方块。改为优先使用中文字体(Noto Sans CJK、文泉驿、微软雅黑、苹方),找不到时才用 DejaVu。

常见问题

现象 原因 处理
标签是方块 系统没有中文字体 Linux 安装 fonts-noto-cjk
8 张要几分钟 CPU 太慢或内存不足 先用 2 个种子
想用中文写提示词 文本编码器以英文为主 先翻译成英文

模板规则

  • 先用小图挑种子,再出正式图。
  • 提示词用英文。
  • 记录每张图的参数。

项目资源

用途 地址
sd-turbo 模型页面(官方) https://huggingface.co/stabilityai/sd-turbo
sd-turbo 许可证 https://huggingface.co/stabilityai/sd-turbo/blob/main/LICENSE.md
Stability AI 许可说明 https://stability.ai/license
diffusers 文档 https://huggingface.co/docs/diffusers/index

多看几个种子,再决定用哪一张。