File size: 5,017 Bytes
17ead0c 5b5bcce 17ead0c d0355bf 17ead0c 5b5bcce 17ead0c 2eb41de 5d2b977 588417f 17ead0c 2eb41de 17ead0c d0355bf 17ead0c 2eb41de 17ead0c 5b5bcce 588417f 5b5bcce 6152eb5 5b5bcce 588417f 5d2b977 5b5bcce 17ead0c 5b5bcce | 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 | import io
import random
import threading
import gradio as gr
from fastapi import FastAPI, HTTPException, Query
from fastapi.responses import Response
from PIL import Image
from core import config, service
# ββ Pre-generated image pool ββββββββββββββββββββββββββββββββββββββββββββββββββ
POOL_SIZE = 50
_pool: list[bytes] = []
def _build_pool() -> None:
for _ in range(POOL_SIZE):
img, _ = service.generate(config.DEFAULT_WIDTH, config.DEFAULT_HEIGHT, None, None)
buf = io.BytesIO()
img.save(buf, "PNG", compress_level=9) # max compression β done once, served many times
_pool.append(buf.getvalue())
threading.Thread(target=_build_pool, daemon=True).start()
# ββ FastAPI app + /image route ββββββββββββββββββββββββββββββββββββββββββββββββ
app = FastAPI(title="RIaaS β Random Image as a Service")
_NO_CACHE = {
"Cache-Control": "no-cache, no-store, must-revalidate, max-age=0",
"Pragma": "no-cache",
"Expires": "0",
}
@app.get("/banner.svg")
async def banner_svg():
with open("banner.svg", "rb") as f:
return Response(content=f.read(), media_type="image/svg+xml")
@app.get("/new_banner.svg")
async def new_banner_svg():
with open("new_banner.svg", "rb") as f:
return Response(content=f.read(), media_type="image/svg+xml")
@app.get("/image")
async def image_api(
width: int | None = Query(None, ge=config.MIN_WIDTH, le=config.MAX_WIDTH),
height: int | None = Query(None, ge=config.MIN_HEIGHT, le=config.MAX_HEIGHT),
style: str | None = Query(None),
seed: int | None = Query(None),
):
if _pool and width is None and height is None and style is None and seed is None:
return Response(
content=random.choice(_pool),
media_type="image/png",
headers={**_NO_CACHE, "X-RIaaS-Source": "pool"},
)
try:
img, _ = service.generate(
width or config.DEFAULT_WIDTH,
height or config.DEFAULT_HEIGHT,
style or "random",
seed,
)
except ValueError as exc:
raise HTTPException(status_code=400, detail=str(exc))
buf = io.BytesIO()
img.save(buf, "PNG", compress_level=3)
return Response(
content=buf.getvalue(),
media_type="image/png",
headers={**_NO_CACHE, "X-RIaaS-Source": "live"},
)
# ββ Gradio UI βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
STYLE_CHOICES = ["random"] + config.VALID_STYLES
def generate_image(width: int, height: int, style: str, seed: str) -> tuple[Image.Image, str]:
parsed_seed = int(seed) if seed.strip() else None
try:
image, meta = service.generate(int(width), int(height), style, parsed_seed)
except ValueError as exc:
return None, str(exc)
return image, f"Style: {meta['style']} | Seed: {meta['seed']}"
with gr.Blocks(
title="RIaaS β Random Image as a Service",
theme=gr.themes.Default(),
js="() => { document.body.classList.remove('dark'); }",
) as demo:
gr.Markdown("# Random Image as a Service\nGenerate beautiful gradient images on demand.")
with gr.Row():
with gr.Column(scale=1):
width_slider = gr.Slider(config.MIN_WIDTH, config.MAX_WIDTH, value=config.DEFAULT_WIDTH, step=8, label="Width")
height_slider = gr.Slider(config.MIN_HEIGHT, config.MAX_HEIGHT, value=config.DEFAULT_HEIGHT, step=8, label="Height")
style_dropdown = gr.Dropdown(STYLE_CHOICES, value="random", label="Style")
seed_input = gr.Textbox(value="", placeholder="Leave blank for random", label="Seed")
generate_btn = gr.Button("Generate", variant="primary")
gr.Textbox(
value="https://aakkaasshh-random-image-as-a-service.hf.space/image",
label="Drop this in any <img> tag",
interactive=False,
show_copy_button=True,
)
with gr.Column(scale=2):
output_image = gr.Image(label="Result", type="pil")
output_info = gr.Textbox(label="Info", interactive=False)
gr.HTML('<img src="/new_banner.svg" alt="RIaaS banner" style="width:100%;margin-top:16px;border-radius:4px">')
generate_btn.click(
generate_image,
inputs=[width_slider, height_slider, style_dropdown, seed_input],
outputs=[output_image, output_info],
)
# Mount Gradio at root. /image is registered on `app` above and takes precedence
# over the wildcard Gradio mount for that specific path.
app = gr.mount_gradio_app(app, demo, path="/")
if __name__ == "__main__":
import uvicorn
uvicorn.run(app, host="0.0.0.0", port=7860)
|