Workflow1111 / make_samples.py
ysharma's picture
ysharma HF Staff
Workflow1111 β€” Automatic1111-style diffusion studio on gr.Workflow
af888c6 verified
Raw
History Blame Contribute Delete
6.34 kB
"""
Generate the sample images shipped with the app as reference-node defaults.
They are produced by the studio's own txt2img pipeline rather than downloaded,
which keeps them licence-clean, on-brand, and β€” for the PNG Info sample β€”
genuinely carrying an embedded A1111 parameter block, so that pipeline
demonstrates a real round trip out of the box.
python apps/05_workflow1111/make_samples.py [--force]
"""
import json
import os
import shutil
import sys
sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
for _s in (sys.stdout, sys.stderr):
try:
_s.reconfigure(encoding="utf-8", errors="replace")
except (AttributeError, ValueError):
pass
from PIL import Image # noqa: E402
import nodes as N # noqa: E402
HERE = os.path.dirname(os.path.abspath(__file__))
SAMPLES = os.path.join(HERE, "samples")
os.makedirs(SAMPLES, exist_ok=True)
# (filename, reference node it seeds, prompt, size, what it has to demonstrate)
SPECS = [
("init_image.jpg", "ref_init_image",
"a quiet wooden lakeside cabin at dawn, still water, pine forest, mist",
(768, 512), "img2img β€” something worth re-lighting or re-seasoning"),
("interrogate.jpg", "ref_interrogate_image",
"a rain-soaked neon street market at night, stalls, paper lanterns, "
"reflections on wet asphalt, crowded",
(768, 512), "interrogate β€” visually dense, lots for a VLM to name"),
("detect.jpg", "ref_detect_image",
"a photograph of a person walking a dog on a city pavement beside a "
"parked bicycle, a car in the background, daylight, documentary photo",
(768, 512), "detect β€” must contain real COCO classes"),
("extras.jpg", "ref_extras_image",
"studio portrait of a golden retriever sitting, plain light grey seamless "
"backdrop, soft key light, sharp focus",
(640, 640), "extras β€” a clean subject for upscaling and cutout"),
("control.jpg", "ref_control_image",
"an ornate victorian building facade, wrought iron balconies, tall "
"windows, intricate stonework, straight-on architectural photograph",
(768, 512), "annotator β€” hard structural edges for Canny"),
]
PNGINFO_PROMPT = ("a red fox curled asleep in deep snow, soft winter light, "
"shallow depth of field")
def shrink(img, box):
"""Fit inside `box` β€” samples ride in the repo, so keep them small."""
img = img.convert("RGB")
img.thumbnail(box, Image.LANCZOS)
return img
def save_jpg(img, path, quality=86):
img.save(path, format="JPEG", quality=quality, optimize=True,
progressive=True, subsampling=1)
return os.path.getsize(path)
def generate(prompt, width, height, seed):
out = N.txt2img(prompt, "blurry, watermark, text, low quality", 4, 1.0,
seed, width, height, "black-forest-labs/FLUX.1-schnell")
return N._load_image(out)
def main():
force = "--force" in sys.argv
manifest = {}
for i, (name, ref_id, prompt, box, why) in enumerate(SPECS):
path = os.path.join(SAMPLES, name)
if os.path.exists(path) and not force:
print(f" skip {name} (exists)")
manifest[ref_id] = name
continue
img = shrink(generate(prompt, box[0], box[1], 2000 + i * 37), box)
kb = save_jpg(img, path) // 1024
print(f" made {name:18} {img.width}Γ—{img.height} {kb} KB β€” {why}")
manifest[ref_id] = name
# The PNG Info sample must carry a real embedded parameter block.
png = os.path.join(SAMPLES, "with_parameters.png")
if not os.path.exists(png) or force:
seed, steps, cfg, w, h = 987654321, 4, 1.0, 768, 512
img = shrink(generate(PNGINFO_PROMPT, w, h, seed), (768, 512))
info = N.generation_info(PNGINFO_PROMPT, "blurry, watermark, text",
steps, cfg, seed, img.width, img.height,
"black-forest-labs/FLUX.1-schnell")
stamped = N.postprocess(N._emit(img), 1, "Lanczos", 0, 1, 1, 1, 0, 0, 0, "", info)
# Quantize to a 256-colour palette before shipping. This sample has to
# stay PNG (a JPEG cannot carry the `parameters` text chunk), and a
# full-colour 768Γ—512 photo PNG is ~471 KB β€” slow enough over the Hub
# (~4.8s) that the reference node looks broken while it loads. The
# palette version is ~161 KB and keeps the text chunk and the
# dimensions, so the embedded `Size:` still matches the image.
from PIL import PngImagePlugin
full = Image.open(stamped["path"])
full.load()
meta = PngImagePlugin.PngInfo()
for key, value in (full.info or {}).items():
if isinstance(value, str):
meta.add_text(key, value)
quantized = full.convert("RGB").quantize(
colors=256, method=Image.MEDIANCUT, dither=Image.FLOYDSTEINBERG)
quantized.save(png, format="PNG", optimize=True, pnginfo=meta)
print(f" made with_parameters.png {os.path.getsize(png)//1024} KB "
"β€” PNG Info, with a real embedded parameter block")
manifest["ref_pnginfo_image"] = "with_parameters.png"
with open(os.path.join(SAMPLES, "manifest.json"), "w", encoding="utf-8") as f:
json.dump(manifest, f, indent=2)
# --- verify the samples actually do their job -------------------------
print("\nverifying:")
det = json.loads(__import__("gradio.workflow", fromlist=["x"]).call_model(
["facebook/detr-resnet-50", "object_detection",
json.dumps({"image": {"path": os.path.join(SAMPLES, "detect.jpg")}}),
__import__("huggingface_hub").get_token(), "auto"]))
labels = sorted({d["label"] for d in det[0]} if isinstance(det[0], list) else set())
print(f" detect.jpg β†’ DETR finds: {labels or 'NOTHING (bad sample)'}")
report, fields_json = N.png_info(png)
fields = json.loads(fields_json)
ok = fields.get("prompt", "").startswith("a red fox curled")
print(f" with_parameters.png β†’ embedded params readable: {ok} "
f"(seed={fields.get('seed')}, size={fields.get('size')})")
total = sum(os.path.getsize(os.path.join(SAMPLES, f))
for f in os.listdir(SAMPLES))
print(f"\n samples/ total: {total // 1024} KB")
if __name__ == "__main__":
main()