Spaces:
Sleeping
Sleeping
File size: 8,640 Bytes
b3a50e3 59a45a0 b3a50e3 59a45a0 b3a50e3 59a45a0 b3a50e3 59a45a0 b3a50e3 59a45a0 b3a50e3 | 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 159 160 161 162 163 164 165 166 167 168 169 170 171 172 | """Personalized text-to-image website. Requires Sumith's hosted model weights."""
import os
import secrets
import threading
from pathlib import Path
import spaces
import gradio as gr
from prompts import make_prompt, STYLES
_pipeline = None
_lock = threading.Lock()
def load_pipeline():
global _pipeline
if _pipeline is not None:
return _pipeline
import torch
from diffusers import StableDiffusionPipeline, DDIMScheduler
from huggingface_hub import hf_hub_download
local_path = os.getenv("MODEL_PATH", "")
repo = os.getenv("MODEL_REPO_ID", "Sumith0102/PersonalisedAiAvatarGenerator")
if local_path:
path = Path(local_path)
elif repo:
path = Path(hf_hub_download(
repo_id=repo,
filename=os.getenv("MODEL_FILENAME", "model.ckpt"),
token=os.getenv("HF_TOKEN") or None,
))
else:
raise gr.Error("The owner is connecting the personalized model. Please try again later.")
if path.suffix == ".ckpt":
from convert_checkpoint import convert
import hashlib
import tempfile
cache_key = hashlib.sha256(str(path.resolve()).encode()).hexdigest()[:20]
safe_path = Path(tempfile.gettempdir()) / f"sumith-{cache_key}.safetensors"
if not safe_path.exists():
convert(path, safe_path)
path = safe_path
elif path.suffix != ".safetensors":
raise gr.Error("The configured model format is not supported.")
pipeline = StableDiffusionPipeline.from_single_file(
str(path),
config=os.getenv("BASE_MODEL_ID", "stable-diffusion-v1-5/stable-diffusion-v1-5"),
torch_dtype=torch.float16,
token=os.getenv("HF_TOKEN") or None,
load_safety_checker=True,
)
pipeline.scheduler = DDIMScheduler.from_config(pipeline.scheduler.config)
pipeline.enable_attention_slicing()
pipeline.enable_vae_slicing()
pipeline.to("cuda")
_pipeline = pipeline
return pipeline
def prepare_prompt(scene, style, enhance=True):
try:
return make_prompt(scene, style, load_pipeline().tokenizer, enhance)
except ValueError as exc:
raise gr.Error(str(exc)) from None
@spaces.GPU(duration=60)
def generate(scene, style, seed, steps, enhance=True, progress=gr.Progress()):
import torch
prompt = prepare_prompt(scene, style, enhance)
if seed is None:
raise gr.Error("Enter a seed, or use -1 for a random image.")
if isinstance(seed, bool) or int(seed) != seed or not -1 <= seed <= 2147483647:
raise gr.Error("Choose a whole-number seed from 0 to 2147483647, or -1 for random.")
seed = secrets.randbelow(2147483648) if seed == -1 else int(seed)
steps = int(steps)
if not 15 <= steps <= 35:
raise gr.Error("Use between 15 and 35 generation steps.")
with _lock:
try:
progress(0.05, desc="Loading the personalized model")
pipe = load_pipeline()
device = "cuda"
rng = torch.Generator(device=device).manual_seed(seed)
def step_callback(pipeline, index, timestep, kwargs):
progress(0.1 + 0.85 * (index + 1) / steps, desc="Creating your portrait")
return kwargs
with torch.inference_mode():
result = pipe(
prompt=prompt,
negative_prompt="blurry, low resolution, distorted face, duplicate person, watermark, text",
height=512, width=512, guidance_scale=7.5,
num_inference_steps=steps, generator=rng,
callback_on_step_end=step_callback,
)
if result.nsfw_content_detected and any(result.nsfw_content_detected):
raise gr.Error("This image could not be displayed. Please try another scene.")
return result.images[0], f"Seed: {seed} 路 512 脳 512 路 AI-generated image"
except gr.Error:
raise
except Exception as exc:
# Do not return upstream paths, tokens, prompts, or credentials to visitors.
print("Generation failed:", type(exc).__name__, flush=True)
raise gr.Error("Generation is unavailable right now. Please try again later.") from None
CSS = """
.gradio-container {max-width: 1240px !important; margin: auto; background: #f5f7fc;}
#studio-header {padding: 28px 0 14px; border-bottom: 1px solid #cdd5e6; margin-bottom: 22px;}
#studio-header h1 {font-size: clamp(30px, 4vw, 48px); letter-spacing: -1.5px; color: #182448;}
#studio-header p {font-size: 16px; color: #475777;}
#generate {background: #304ffe; color: white; border: 0; min-height: 52px; font-size: 17px;}
#portrait {min-height: 460px; background: #e9eef9; border-radius: 18px;}
#footer {font-size: 14px; color: #566581; margin-top: 20px;}
"""
with gr.Blocks(title="Sumith Portrait Studio",
analytics_enabled=False, delete_cache=(3600, 3600)) as demo:
gr.Markdown("# Sumith Portrait Studio\nDescribe a scene. Create an AI portrait of Sumith.", elem_id="studio-header")
with gr.Row():
with gr.Column(scale=5):
scene = gr.Textbox(label="Your scene", lines=5, max_lines=8,
placeholder="Standing beside a lake in the Canadian mountains at sunrise")
style = gr.Radio(list(STYLES), value="Natural", label="Look")
enhance = gr.Checkbox(value=True, label="Add visual detail to my prompt")
gr.Markdown("A lightweight prompt assistant adds lighting and composition cues where there is room. "
"Your scene stays intact. No extra AI service is used.")
preview = gr.Button("Preview generation prompt", variant="secondary")
final_prompt = gr.Textbox(label="Exact generation prompt", lines=4, interactive=False,
placeholder="Preview your prompt, or create a portrait to see it here.")
gr.Markdown("The model has a short text limit. Optional details only fill the remaining space; "
"overlong scenes are rejected, never silently cut off. Turn enhancement off for just your scene and the identity cue.")
with gr.Accordion("Fine-tune the result", open=False):
seed = gr.Number(value=-1, precision=0, label="Seed 路 -1 for a new variation")
steps = gr.Slider(15, 35, value=24, step=1, label="Generation steps")
submit = gr.Button("Create portrait", variant="primary", elem_id="generate")
gr.Examples([
["Walking through Old Montr茅al on a sunny afternoon", "Natural"],
["In a tailored navy suit, soft studio lighting, professional headshot", "Studio"],
["An explorer beside a mountain lake at sunrise", "Cinematic"],
], inputs=[scene, style], label="Try a scene", cache_examples=False)
with gr.Column(scale=6):
portrait = gr.Image(label="Your portrait", type="pil", interactive=False,
buttons=["download"], elem_id="portrait")
details = gr.Markdown("Your generated portrait will appear here.")
gr.Markdown(
"Built by Sumith Reddy 路 DreamBooth / Stable Diffusion 1.5 \n"
"Images are AI-generated and may vary in likeness. Prompts and results are processed by the hosting service. "
"The app keeps temporary output files for up to about two hours. \n"
"[Back to portfolio](https://thanu-sumith.github.io/sumith-portfolio/)", elem_id="footer")
preview.click(prepare_prompt, inputs=[scene, style, enhance], outputs=final_prompt,
api_name="preview_prompt")
# Clear stale previews when the request changes.
for control in [scene, style, enhance]:
control.change(lambda: "", outputs=final_prompt, queue=False, api_name=False)
submit.click(prepare_prompt, inputs=[scene, style, enhance], outputs=final_prompt,
api_name=False).success(
generate, inputs=[scene, style, seed, steps, enhance], outputs=[portrait, details],
concurrency_limit=1, api_name="generate_portrait")
# Prepare the model before ZeroGPU forks a worker. Its CUDA emulation supports
# model placement at startup; real GPU computation happens only in generate().
if __name__ == "__main__":
load_pipeline()
demo.queue(max_size=4, default_concurrency_limit=1).launch(
css=CSS, theme=gr.themes.Soft(primary_hue="indigo", neutral_hue="slate"),
server_name="0.0.0.0", server_port=int(os.getenv("PORT", "7860")),
show_error=False, max_file_size="1mb",
)
|