""" Nowhere Post ============ Name a place that doesn't exist. The post office of Nowhere issues a stamp for it. Collect them in your album. Track 2 toy for the "Small Models Big Adventures" hackathon. Model: black-forest-labs/FLUX.1-schnell (~12B, Apache-2.0, under the 32B ceiling). Every result is composed into a perforated postage-stamp frame with PIL, so it always looks like a stamp. In model mode FLUX paints the picture; in keeper mode a procedural scene is drawn, so the app boots and demos with no GPU. """ import os import random import inspect import gradio as gr from PIL import Image, ImageDraw, ImageFont _BLOCKS_HAS_CSS = "css" in inspect.signature(gr.Blocks.__init__).parameters _LAUNCH_HAS_SSR = "ssr_mode" in inspect.signature(gr.Blocks.launch).parameters MODEL_ID = os.environ.get("POST_MODEL", "black-forest-labs/FLUX.1-schnell") DEBUG = os.environ.get("POST_DEBUG", "").strip().lower() in {"1", "true", "yes"} ART = 384 # inner picture size (square) STEPS = int(os.environ.get("POST_STEPS", "4")) # schnell is happy at 4 EXAMPLES = ["the Isle of Lost Umbrellas", "Cloudfall", "the Last Lighthouse", "the Market at the Edge of Sleep"] def _noop_gpu(*a, **k): def wrap(fn): return fn return wrap(a[0]) if a and callable(a[0]) else wrap if os.environ.get("SPACES_ZERO_GPU", "").lower() in {"true", "1"}: try: import spaces GPU = spaces.GPU except Exception: # noqa: BLE001 GPU = _noop_gpu else: GPU = _noop_gpu _pipe = None MODE = "keeper" _warmed = False def load_model(): global _pipe, MODE try: import torch from diffusers import FluxPipeline _pipe = FluxPipeline.from_pretrained(MODEL_ID, torch_dtype=torch.bfloat16) MODE = "model" print(f"[Post] Loaded {MODEL_ID} -- model mode.") except Exception as exc: # noqa: BLE001 MODE = "keeper" print(f"[Post] Could not load {MODEL_ID} ({exc}). Keeper mode active.") load_model() def _font(size): for path in ("DejaVuSans-Bold.ttf", "DejaVuSans.ttf", "/usr/share/fonts/truetype/dejavu/DejaVuSans-Bold.ttf"): try: return ImageFont.truetype(path, size) except Exception: # noqa: BLE001 continue return ImageFont.load_default() @GPU(duration=60) def _flux_art(place): import torch if torch.cuda.is_available() and getattr(_pipe, "device", None) is not None \ and _pipe.device.type != "cuda": _pipe.to("cuda") prompt = (f"a vintage postage stamp illustration of {place}, storybook engraving " f"style, soft muted inks, fine linework, whimsical, centered scene") g = torch.Generator(device=_pipe.device).manual_seed(random.randint(0, 2**31 - 1)) img = _pipe(prompt, num_inference_steps=STEPS, guidance_scale=0.0, height=ART, width=ART, max_sequence_length=256, generator=g).images[0] return img def _procedural_art(place): """A seeded little scene for keeper mode, so a stamp always has a picture.""" rng = random.Random(place.strip().lower()) palettes = [((214, 198, 150), (122, 150, 120), (90, 110, 140)), ((232, 210, 170), (180, 140, 110), (120, 90, 120)), ((205, 220, 225), (120, 160, 170), (70, 100, 120)), ((235, 200, 160), (200, 120, 90), (110, 70, 90))] sky, land, accent = rng.choice(palettes) img = Image.new("RGB", (ART, ART), sky) d = ImageDraw.Draw(img) horizon = int(ART * rng.uniform(0.55, 0.72)) d.rectangle([0, horizon, ART, ART], fill=land) # a sun or moon r = rng.randint(28, 46) cxp = rng.randint(r, ART - r) d.ellipse([cxp - r, horizon - 90 - r, cxp + r, horizon - 90 + r], fill=accent) # a few hills / shapes for _ in range(rng.randint(2, 4)): hw = rng.randint(80, 200) hx = rng.randint(-40, ART) hh = rng.randint(30, 80) d.ellipse([hx, horizon - hh, hx + hw, horizon + hh], fill=accent) return img def compose_stamp(inner, place): """Frame a square picture as a perforated postage stamp with a caption.""" pad, cap = 26, 56 W, H = ART + pad * 2, ART + pad * 2 + cap stamp = Image.new("RGB", (W, H), (253, 250, 240)) d = ImageDraw.Draw(stamp) # perforations step = 16 for x in range(0, W, step): d.ellipse([x - 5, -5, x + 5, 5], fill=(243, 234, 215)) d.ellipse([x - 5, H - 5, x + 5, H + 5], fill=(243, 234, 215)) for y in range(0, H, step): d.ellipse([-5, y - 5, 5, y + 5], fill=(243, 234, 215)) d.ellipse([W - 5, y - 5, W + 5, y + 5], fill=(243, 234, 215)) d.rectangle([10, 10, W - 10, H - 10], outline=(170, 100, 28), width=2) stamp.paste(inner.resize((ART, ART)), (pad, pad)) d.rectangle([pad, pad, pad + ART, pad + ART], outline=(170, 100, 28), width=1) # header + caption fhead, fcap = _font(15), _font(20) d.text((pad, 12), "NOWHERE POST", font=fhead, fill=(122, 46, 46)) denom = random.choice(["1", "2", "3", "5", "7"]) d.text((W - pad - 30, 12), f"{denom} \u2606", font=fhead, fill=(122, 46, 46)) name = place if len(place) <= 30 else place[:29] + "\u2026" tw = d.textlength(name, font=fcap) d.text(((W - tw) / 2, pad + ART + 14), name, font=fcap, fill=(58, 47, 37)) return stamp def make_stamp(place): inner = None if MODE == "model": try: inner = _flux_art(place) except Exception as exc: # noqa: BLE001 print(f"[Post] generation error: {exc}") inner = None if inner is None: inner = _procedural_art(place) return compose_stamp(inner, place.strip()) def on_issue(place, album): global _warmed place = (place or "").strip() if not place: yield None, album, album, "" return if MODE == "model" and not _warmed: # show a placeholder frame while FLUX warms up warming = compose_stamp(_procedural_art(place), place) yield warming, album, album, "" stamp = make_stamp(place) if MODE == "model": _warmed = True album = (album or []) + [(stamp, place)] yield stamp, [s for s, _ in album], album, "" def on_reset(): return None, [], [], "" def use_example(text): return text CSS = """ @import url('https://fonts.googleapis.com/css2?family=Fraunces:ital,opsz,wght@0,9..144,400;0,9..144,600;1,9..144,400&family=Spectral:ital,wght@0,400;0,500;1,400&display=swap'); :root{--paper:#f3ead7;--paper-2:#efe3c9;--ink:#2f2a22;--ink-soft:#6b5a47; --post:#7a2e2e;--amber-deep:#aa641c;--line:#c9b48f;} .gradio-container,.gradio-container.dark,.dark{ --body-background-fill:transparent;--background-fill-primary:#fffaf0;--background-fill-secondary:#f6edd8; --block-background-fill:#fffaf0;--block-border-color:var(--line);--border-color-primary:var(--line); --body-text-color:var(--ink);--body-text-color-subdued:var(--ink-soft); --block-label-text-color:var(--post);--block-title-text-color:var(--ink); --block-label-background-fill:#efe3c9;--block-title-background-fill:transparent; --input-background-fill:#fffaf0;--input-border-color:var(--line);--input-placeholder-color:var(--ink-soft); --button-primary-background-fill:var(--post);--button-primary-background-fill-hover:#641f1f; --button-primary-text-color:#fbf7ec;--button-primary-border-color:#641f1f; --button-secondary-background-fill:#efe3c9;--button-secondary-background-fill-hover:#e7d6b3; --button-secondary-text-color:var(--ink);--button-secondary-border-color:var(--line); --color-accent:var(--amber-deep);--color-accent-soft:#f3e3c4;} .gradio-container{background:radial-gradient(120% 80% at 80% -10%,#f7efdd,var(--paper) 55%,var(--paper-2)); font-family:'Spectral',Georgia,serif !important;color:var(--ink) !important;max-width:960px !important;} .gradio-container textarea,.gradio-container input[type="text"],.gradio-container input:not([type]){ background:#fffaf0 !important;color:var(--ink) !important;-webkit-text-fill-color:var(--ink) !important;border-color:var(--line) !important;} .gradio-container textarea::placeholder,.gradio-container input::placeholder{color:var(--ink-soft) !important;-webkit-text-fill-color:var(--ink-soft) !important;opacity:1;} .np-title{font-family:'Fraunces',serif;font-weight:600;font-size:2.5rem;line-height:1;margin:.2rem 0 0;} .np-title em{font-style:italic;color:var(--post);} .np-sub{font-style:italic;color:var(--ink-soft);margin:.35rem 0 1rem;font-size:1.05rem;} .np-mode{display:inline-block;font-size:.72rem;letter-spacing:.12em;text-transform:uppercase;color:var(--post);border:1px solid var(--line);border-radius:999px;padding:.15rem .6rem;} .np-foot{color:var(--ink-soft);font-size:.82rem;font-style:italic;text-align:center;margin-top:10px;} footer{display:none !important;} """ _bk = {"title": "Nowhere Post"} if _BLOCKS_HAS_CSS: _bk["css"] = CSS _bk["theme"] = gr.themes.Soft() with gr.Blocks(**_bk) as demo: album = gr.State([]) public = "FLUX.1-schnell · on-device" if MODE == "model" else "Nowhere Post" mode_label = f"{public} · [{MODE}]" if DEBUG else public gr.HTML(f"""
Nowhere Post
Name a place that doesn't exist. Receive its postage stamp.
{mode_label}
""") with gr.Row(): with gr.Column(scale=3): with gr.Row(): place = gr.Textbox(placeholder="a place that doesn't exist... (the Isle of Lost Umbrellas)", show_label=False, scale=7, autofocus=True) go = gr.Button("Issue stamp", variant="primary", scale=2) with gr.Row(): ex_btns = [gr.Button(e, size="sm") for e in EXAMPLES] stamp = gr.Image(label="Latest stamp", type="pil", height=460, show_label=False) reset = gr.Button("Start a new album", size="sm") with gr.Column(scale=2): gallery = gr.Gallery(label="Your album", columns=2, height=520, show_label=True) gr.HTML('
Every place that isn\'t real still deserves a stamp.
') go.click(on_issue, [place, album], [stamp, gallery, album, place]) place.submit(on_issue, [place, album], [stamp, gallery, album, place]) reset.click(on_reset, None, [stamp, gallery, album, place]) for b, e in zip(ex_btns, EXAMPLES): b.click(use_example, gr.State(e), place) if __name__ == "__main__": _lk = {} if not _BLOCKS_HAS_CSS: _lk["css"] = CSS _lk["theme"] = gr.themes.Soft() if _LAUNCH_HAS_SSR: _lk["ssr_mode"] = False demo.queue(max_size=24).launch(**_lk)