whitepeacock commited on
Commit
e5848d7
·
verified ·
1 Parent(s): f96382c

Create sdlxapp.py

Browse files
Files changed (1) hide show
  1. sdlxapp.py +251 -0
sdlxapp.py ADDED
@@ -0,0 +1,251 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import io
2
+ import os
3
+ import base64
4
+ import asyncio
5
+ import random
6
+ from concurrent.futures import ThreadPoolExecutor
7
+
8
+ from fastapi import FastAPI, Request
9
+ from fastapi.middleware.cors import CORSMiddleware
10
+ from fastapi.responses import HTMLResponse, JSONResponse
11
+
12
+ from PIL import Image
13
+ import torch
14
+ from diffusers import DiffusionPipeline
15
+
16
+
17
+ # -------------------------------------------------------------
18
+ # HuggingFace Token
19
+ # -------------------------------------------------------------
20
+ HF_TOKEN = os.getenv("HF_TOKEN")
21
+
22
+
23
+ # -------------------------------------------------------------
24
+ # Model Settings
25
+ # -------------------------------------------------------------
26
+ MODEL_REPO = "stabilityai/sdxl-turbo"
27
+
28
+ device = "cuda" if torch.cuda.is_available() else "cpu"
29
+ dtype = torch.float16 if torch.cuda.is_available() else torch.float32
30
+
31
+ print(f"Loading {MODEL_REPO} on {device}...")
32
+
33
+ pipe = DiffusionPipeline.from_pretrained(
34
+ MODEL_REPO,
35
+ torch_dtype=dtype,
36
+ use_safetensors=True,
37
+ token=HF_TOKEN if HF_TOKEN else None,
38
+ )
39
+
40
+ pipe.to(device)
41
+
42
+ if device == "cpu":
43
+ try:
44
+ pipe.enable_model_cpu_offload()
45
+ except:
46
+ pass
47
+
48
+ print("Model ready.")
49
+
50
+
51
+ # -------------------------------------------------------------
52
+ # Automatic Negative Prompt (backend only)
53
+ # -------------------------------------------------------------
54
+ AUTO_NEGATIVE_PROMPT = (
55
+ "low quality, worst quality, blurry, pixelated, jpeg artifacts, "
56
+ "deformed, distorted, bad anatomy, extra fingers, extra limbs, "
57
+ "missing fingers, watermark, text, logo"
58
+ )
59
+
60
+
61
+ # -------------------------------------------------------------
62
+ # Core Generation Function
63
+ # -------------------------------------------------------------
64
+ def generate_image(prompt, seed, width, height, steps, guidance):
65
+ generator = torch.Generator(device=device).manual_seed(seed)
66
+
67
+ result = pipe(
68
+ prompt=prompt,
69
+ negative_prompt=AUTO_NEGATIVE_PROMPT,
70
+ guidance_scale=guidance,
71
+ num_inference_steps=steps,
72
+ width=width,
73
+ height=height,
74
+ generator=generator,
75
+ )
76
+
77
+ return result.images[0]
78
+
79
+
80
+ # -------------------------------------------------------------
81
+ # Async Queue
82
+ # -------------------------------------------------------------
83
+ executor = ThreadPoolExecutor(max_workers=2)
84
+ semaphore = asyncio.Semaphore(2)
85
+
86
+
87
+ async def run_generate(prompt, seed, width, height, steps, guidance):
88
+ async with semaphore:
89
+ loop = asyncio.get_running_loop()
90
+ return await loop.run_in_executor(
91
+ executor,
92
+ generate_image,
93
+ prompt,
94
+ seed,
95
+ width,
96
+ height,
97
+ steps,
98
+ guidance,
99
+ )
100
+
101
+
102
+ # -------------------------------------------------------------
103
+ # FastAPI App
104
+ # -------------------------------------------------------------
105
+ app = FastAPI(title="SDXL Turbo Generator", version="2.0")
106
+
107
+ app.add_middleware(
108
+ CORSMiddleware,
109
+ allow_origins=["*"],
110
+ allow_credentials=True,
111
+ allow_methods=["*"],
112
+ allow_headers=["*"],
113
+ )
114
+
115
+
116
+ # -------------------------------------------------------------
117
+ # UI
118
+ # -------------------------------------------------------------
119
+ @app.get("/", response_class=HTMLResponse)
120
+ def home():
121
+ return """
122
+ <!doctype html>
123
+ <html>
124
+ <head>
125
+ <meta charset="utf-8"/>
126
+ <title>SDXL Turbo</title>
127
+ <style>
128
+ body {
129
+ font-family: Arial;
130
+ max-width: 900px;
131
+ margin: 30px auto;
132
+ }
133
+ textarea {
134
+ width: 100%;
135
+ padding: 12px;
136
+ margin-bottom: 10px;
137
+ font-size: 15px;
138
+ }
139
+ button {
140
+ padding: 12px 18px;
141
+ background: black;
142
+ color: white;
143
+ border: none;
144
+ cursor: pointer;
145
+ font-size: 15px;
146
+ }
147
+ #status {
148
+ margin-top: 12px;
149
+ }
150
+ #output {
151
+ margin-top: 20px;
152
+ width: 100%;
153
+ height: 432px;
154
+ border: 1px solid #ddd;
155
+ border-radius: 10px;
156
+ display: flex;
157
+ align-items: center;
158
+ justify-content: center;
159
+ background: #fafafa;
160
+ }
161
+ #output img {
162
+ max-width: 100%;
163
+ max-height: 100%;
164
+ border-radius: 8px;
165
+ }
166
+ </style>
167
+ </head>
168
+ <body>
169
+ <h1>SDXL Turbo</h1>
170
+ <textarea id="prompt" placeholder="Enter prompt"></textarea>
171
+ <button onclick="send()">Generate</button>
172
+ <div id="status"></div>
173
+ <div id="output">
174
+ <span id="placeholder">Image will appear here</span>
175
+ <img id="result" style="display:none;" />
176
+ </div>
177
+ <script>
178
+ async function send() {
179
+ const prompt = document.getElementById("prompt").value;
180
+ const status = document.getElementById("status");
181
+ const img = document.getElementById("result");
182
+ const placeholder = document.getElementById("placeholder");
183
+ status.innerText = "Generating...";
184
+ img.style.display = "none";
185
+ placeholder.style.display = "block";
186
+ const res = await fetch("/api/generate", {
187
+ method: "POST",
188
+ headers: {"Content-Type": "application/json"},
189
+ body: JSON.stringify({ prompt })
190
+ });
191
+ const data = await res.json();
192
+ if (data.status !== "success") {
193
+ status.innerText = "Error: " + data.message;
194
+ return;
195
+ }
196
+ img.src = "data:image/png;base64," + data.image_base64;
197
+ img.style.display = "block";
198
+ placeholder.style.display = "none";
199
+ status.innerText = "Done (seed " + data.seed + ")";
200
+ }
201
+ </script>
202
+ </body>
203
+ </html>
204
+ """
205
+
206
+
207
+ # -------------------------------------------------------------
208
+ # API Endpoint
209
+ # -------------------------------------------------------------
210
+ @app.post("/api/generate")
211
+ async def api_generate(request: Request):
212
+ try:
213
+ body = await request.json()
214
+ prompt = body.get("prompt", "").strip()
215
+ except:
216
+ return JSONResponse({"status": "error", "message": "Invalid JSON"}, 400)
217
+
218
+ if not prompt:
219
+ return JSONResponse({"status": "error", "message": "Prompt required"}, 400)
220
+
221
+ width = 768
222
+ height = 432
223
+ steps = 2
224
+ guidance = 0.0
225
+ seed = random.randint(0, 2**31 - 1)
226
+
227
+ try:
228
+ img = await run_generate(prompt, seed, width, height, steps, guidance)
229
+
230
+ buf = io.BytesIO()
231
+ img.save(buf, format="PNG")
232
+ b64 = base64.b64encode(buf.getvalue()).decode()
233
+
234
+ return JSONResponse({
235
+ "status": "success",
236
+ "image_base64": b64,
237
+ "seed": seed,
238
+ "width": width,
239
+ "height": height
240
+ })
241
+
242
+ except Exception as e:
243
+ return JSONResponse({"status": "error", "message": str(e)}, 500)
244
+
245
+
246
+ # -------------------------------------------------------------
247
+ # Local run
248
+ # -------------------------------------------------------------
249
+ if __name__ == "__main__":
250
+ import uvicorn
251
+ uvicorn.run(app, host="0.0.0.0", port=7860)