ImageGen / app.py
tomiconic's picture
Update app.py
e389efe verified
raw
history blame
9.42 kB
import gradio as gr
import torch
from diffusers import StableDiffusionXLPipeline, DPMSolverMultistepScheduler
from huggingface_hub import hf_hub_download
import random
# ── Device ────────────────────────────────────────────────────────────────────
DEVICE = "cuda" if torch.cuda.is_available() else "cpu"
DTYPE = torch.float16 if DEVICE == "cuda" else torch.float32
print(f"Running on: {DEVICE.upper()}")
# ── Model ─────────────────────────────────────────────────────────────────────
MODEL_REPO = "cyberdelia/CyberRealisticPony"
MODEL_FILE = "CyberRealisticPony_V16.0_FP16.safetensors"
PONY_POS = "score_9, score_8_up, score_7_up, "
PONY_NEG = "score_6, score_5, score_4, "
print("Downloading model...")
local_path = hf_hub_download(repo_id=MODEL_REPO, filename=MODEL_FILE)
print(f"Loading from {local_path}...")
pipe = StableDiffusionXLPipeline.from_single_file(local_path, torch_dtype=DTYPE)
pipe.scheduler = DPMSolverMultistepScheduler.from_config(
pipe.scheduler.config, use_karras_sigmas=True
)
pipe.enable_attention_slicing()
if DEVICE == "cuda":
pipe.enable_xformers_memory_efficient_attention()
pipe = pipe.to(DEVICE)
print("Ready.")
# ── Generation ────────────────────────────────────────────────────────────────
def generate(prompt, negative_prompt, width, height, steps, guidance, seed, randomize):
if not prompt.strip():
raise gr.Error("Please enter a prompt.")
if randomize:
seed = random.randint(0, 2**32 - 1)
seed = int(seed)
generator = torch.Generator(device=DEVICE).manual_seed(seed)
result = pipe(
prompt=PONY_POS + prompt.strip(),
negative_prompt=PONY_NEG + negative_prompt.strip(),
width=int(width),
height=int(height),
num_inference_steps=int(steps),
guidance_scale=float(guidance),
generator=generator,
clip_skip=2,
)
return result.images[0], seed
# ── CSS ───────────────────────────────────────────────────────────────────────
css = """
/* ── Base ── */
body, .gradio-container {
background: #0f0f13 !important;
font-family: 'Inter', system-ui, sans-serif !important;
}
/* ── Header ── */
.header-box {
background: linear-gradient(135deg, #1a1a2e 0%, #16213e 50%, #0f3460 100%);
border: 1px solid #e94560;
border-radius: 16px;
padding: 20px;
text-align: center;
margin-bottom: 8px;
}
.header-box h1 {
color: #ffffff;
font-size: 1.8em;
font-weight: 800;
margin: 0 0 6px 0;
letter-spacing: -0.5px;
}
.header-box p {
color: #a0a0b8;
font-size: 0.85em;
margin: 0;
}
.header-box .badge {
display: inline-block;
background: #e94560;
color: white;
font-size: 0.7em;
font-weight: 700;
padding: 2px 10px;
border-radius: 20px;
margin-top: 8px;
text-transform: uppercase;
letter-spacing: 1px;
}
/* ── Output image ── */
.output-box {
background: #1a1a2e;
border: 2px dashed #2a2a4a;
border-radius: 16px;
overflow: hidden;
min-height: 300px;
}
.output-box img {
border-radius: 14px;
width: 100%;
}
/* ── Panel cards ── */
.panel-card {
background: #1a1a2e;
border: 1px solid #2a2a4a;
border-radius: 14px;
padding: 16px;
margin-bottom: 10px;
}
.section-label {
color: #e94560 !important;
font-size: 0.75em !important;
font-weight: 700 !important;
text-transform: uppercase !important;
letter-spacing: 1.5px !important;
margin-bottom: 10px !important;
}
/* ── Inputs ── */
textarea, input[type="number"] {
background: #0f0f13 !important;
border: 1px solid #2a2a4a !important;
border-radius: 10px !important;
color: #e0e0f0 !important;
font-size: 15px !important;
}
textarea:focus, input[type="number"]:focus {
border-color: #e94560 !important;
box-shadow: 0 0 0 2px rgba(233, 69, 96, 0.2) !important;
}
/* ── Sliders ── */
.gradio-slider input[type=range] {
accent-color: #e94560;
}
/* ── Generate button ── */
.generate-btn {
background: linear-gradient(135deg, #e94560, #c23152) !important;
border: none !important;
border-radius: 14px !important;
color: white !important;
font-size: 1.1em !important;
font-weight: 700 !important;
padding: 16px !important;
width: 100% !important;
letter-spacing: 0.5px !important;
box-shadow: 0 4px 20px rgba(233, 69, 96, 0.4) !important;
transition: all 0.2s ease !important;
}
.generate-btn:active {
transform: scale(0.98) !important;
box-shadow: 0 2px 10px rgba(233, 69, 96, 0.3) !important;
}
/* ── Seed row ── */
.seed-row {
display: flex;
align-items: center;
gap: 10px;
}
/* ── Used seed display ── */
.used-seed input {
background: #0f0f13 !important;
border: 1px solid #2a2a4a !important;
color: #606080 !important;
font-size: 0.85em !important;
text-align: center !important;
}
/* ── Labels ── */
label span, .gradio-label {
color: #a0a0c0 !important;
font-size: 0.82em !important;
font-weight: 600 !important;
text-transform: uppercase !important;
letter-spacing: 0.8px !important;
}
/* ── Mobile: stack columns ── */
@media (max-width: 768px) {
.gr-row { flex-direction: column !important; }
.gr-col { width: 100% !important; min-width: 100% !important; }
}
"""
# ── UI ────────────────────────────────────────────────────────────────────────
with gr.Blocks(css=css, title="CyberRealistic Pony") as demo:
# Header
gr.HTML("""
<div class="header-box">
<h1>🐴 CyberRealistic Pony</h1>
<p>SDXL Β· Pony Architecture Β· v16.0</p>
<span class="badge">⚠ CPU Mode β€” Slow</span>
</div>
""")
# Output image β€” top on mobile so you see result first
with gr.Group(elem_classes="output-box"):
output_image = gr.Image(
label="",
type="pil",
show_label=False,
height=420,
)
used_seed = gr.Number(
label="Seed used β€” save to reproduce",
interactive=False,
elem_classes="used-seed",
)
# ── Prompt card ──
gr.HTML('<div class="section-label" style="margin-top:14px;color:#e94560;font-size:0.75em;font-weight:700;text-transform:uppercase;letter-spacing:1.5px;">✏️ Prompt</div>')
prompt = gr.Textbox(
label="",
placeholder="a woman in a futuristic city, cinematic lighting, highly detailed, photorealistic",
lines=3,
show_label=False,
)
gr.HTML('<div class="section-label" style="margin-top:10px;color:#e94560;font-size:0.75em;font-weight:700;text-transform:uppercase;letter-spacing:1.5px;">🚫 Negative Prompt</div>')
negative_prompt = gr.Textbox(
label="",
value=(
"(worst quality:1.2), (low quality:1.2), (normal quality:1.2), "
"lowres, bad anatomy, bad hands, signature, watermarks, "
"ugly, imperfect eyes, skewed eyes, unnatural face, "
"unnatural body, error, extra limb, missing limbs"
),
lines=2,
show_label=False,
)
# ── Size card ──
gr.HTML('<div class="section-label" style="margin-top:14px;color:#e94560;font-size:0.75em;font-weight:700;text-transform:uppercase;letter-spacing:1.5px;">πŸ“ Size</div>')
with gr.Row():
width = gr.Slider(512, 896, value=768, step=64, label="Width")
height = gr.Slider(512, 1152, value=896, step=64, label="Height")
# ── Sampling card ──
gr.HTML('<div class="section-label" style="margin-top:14px;color:#e94560;font-size:0.75em;font-weight:700;text-transform:uppercase;letter-spacing:1.5px;">βš™οΈ Sampling</div>')
steps = gr.Slider(10, 30, value=20, step=1, label="Steps")
guidance = gr.Slider(1.0, 12.0, value=5.0, step=0.5, label="CFG Scale")
# ── Seed card ──
gr.HTML('<div class="section-label" style="margin-top:14px;color:#e94560;font-size:0.75em;font-weight:700;text-transform:uppercase;letter-spacing:1.5px;">🎲 Seed</div>')
with gr.Row():
seed = gr.Number(label="Seed", value=42, precision=0, minimum=0, maximum=2**32-1, scale=2)
randomize_seed = gr.Checkbox(label="Random", value=True, scale=1)
# ── Generate button ──
generate_btn = gr.Button(
"✦ Generate Image",
variant="primary",
size="lg",
elem_classes="generate-btn",
)
# ── Wire up ──
generate_btn.click(
fn=generate,
inputs=[prompt, negative_prompt, width, height, steps, guidance, seed, randomize_seed],
outputs=[output_image, used_seed],
)
demo.launch()