Spaces:
Sleeping
Sleeping
File size: 5,022 Bytes
a71fa9d 0e9675b 0d7b5a8 a71fa9d 0e9675b a71fa9d 0e9675b a71fa9d 0e9675b a71fa9d 0e9675b a71fa9d 0e9675b a71fa9d 0d7b5a8 a71fa9d 0d7b5a8 a71fa9d 0d7b5a8 0e9675b a71fa9d |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 |
import time, base64, io, os, requests
from typing import Optional
from PIL import Image
import gradio as gr
# =========================
# Stable Horde config
# =========================
HORDE_URL = "https://stablehorde.net/api/v2/generate/async"
HORDE_STATUS = "https://stablehorde.net/api/v2/generate/status/{id}"
# Optional: set an API key in your Space secrets for better priority:
# Settings → Repository secrets → add HORDE_API_KEY
HORDE_API_KEY = os.getenv("HORDE_API_KEY", "")
DEFAULT_STEPS = 24
DEFAULT_W = 768
DEFAULT_H = 768
POLL_INTERVAL = 2.5 # seconds between polls
POLL_TIMEOUT = 180 # max seconds to wait for a job
MODEL = None # let Horde choose; or set e.g. "SDXL 1.0"
# =========================
# Horde client
# =========================
def horde_txt2img(prompt: str,
steps: int = DEFAULT_STEPS,
width: int = DEFAULT_W,
height: int = DEFAULT_H,
model: Optional[str] = MODEL) -> Image.Image:
if not prompt or not prompt.strip():
raise gr.Error("Please enter a prompt.")
payload = {
"prompt": prompt.strip(),
"params": {
"steps": int(steps),
"width": int(width),
"height": int(height),
"n": 1
},
"nsfw": False,
"censor_nsfw": True
}
if model:
payload["models"] = [model]
# Submit
headers = {"apikey": HORDE_API_KEY} if HORDE_API_KEY else {}
submit = requests.post(HORDE_URL, json=payload, headers=headers, timeout=30)
submit.raise_for_status()
job_id = submit.json()["id"]
# Poll
start = time.time()
while True:
status = requests.get(HORDE_STATUS.format(id=job_id), timeout=30).json()
if status.get("faulted"):
raise gr.Error(f"Horde job faulted: {status}")
if status.get("done"):
gens = status.get("generations") or []
if not gens:
raise gr.Error("Horde finished but returned no generations.")
b64 = gens[0]["img"]
img_bytes = base64.b64decode(b64)
return Image.open(io.BytesIO(img_bytes)).convert("RGB")
if time.time() - start > POLL_TIMEOUT:
raise gr.Error("Timed out waiting for Horde. Try again or reduce steps/size.")
time.sleep(POLL_INTERVAL)
# Wrapper for Gradio (adds optional negative prompt + long-shot toggle if you want later)
def generate_image(prompt, steps, size):
# size like "768x768"
try:
w, h = [int(x.strip()) for x in size.lower().split("x")]
except Exception:
w, h = DEFAULT_W, DEFAULT_H
img = horde_txt2img(prompt, steps=steps, width=w, height=h)
return img
# =========================
# UI
# =========================
CUSTOM_CSS = """
.gradio-container { padding: 24px; }
/* Rounded prompt boxes */
.prompt-box textarea {
border-radius: 18px !important;
min-height: 90px;
font-size: 16px;
line-height: 1.4;
padding: 14px 16px;
}
/* Pill buttons */
.pill button {
border-radius: 999px !important;
padding: 10px 18px;
font-size: 15px;
}
/* Blue-ish image boxes feel (border radius) */
.image-out .wrap, .image-out .svelte-1ipelgc {
border-radius: 22px !important;
}
"""
with gr.Blocks(css=CUSTOM_CSS, title="Stitch – Image Checkpoints (Stable Horde)") as demo:
gr.Markdown("### Image Checkpoints (Stable Horde) — generate per-prompt frames")
# Global controls (applies to all generators)
with gr.Row():
steps = gr.Slider(8, 50, value=DEFAULT_STEPS, step=1, label="Steps (quality/time)")
size = gr.Dropdown(
choices=["512x512", "768x768", "1024x576", "1024x768"],
value=f"{DEFAULT_W}x{DEFAULT_H}",
label="Resolution"
)
# 4 rows: [Prompt + Button] | [Image Output]
prompt_boxes = []
gen_buttons = []
img_outputs = []
for i in range(1, 5):
with gr.Row():
with gr.Column(scale=1, min_width=320):
p = gr.Textbox(
placeholder=f"Prompt input (Image {i})",
lines=4,
label=None,
elem_classes=["prompt-box"]
)
b = gr.Button(f"Generate image {i}", elem_classes=["pill"])
with gr.Column(scale=2, min_width=380):
img = gr.Image(label=f"Image {i} output", type="pil", elem_classes=["image-out"])
prompt_boxes.append(p)
gen_buttons.append(b)
img_outputs.append(img)
# Wire callbacks
for i in range(4):
gen_buttons[i].click(
fn=generate_image,
inputs=[prompt_boxes[i], steps, size],
outputs=[img_outputs[i]]
)
gr.Markdown(
"> Tip: You can generate images independently per row. Steps ↑ = higher quality but slower. "
"Stable Horde is free (crowd GPUs), so expect occasional queue time."
)
if __name__ == "__main__":
demo.queue().launch()
|