Nior18867 commited on
Commit
3aef124
·
verified ·
1 Parent(s): 9dab7b8

Upload folder using huggingface_hub

Browse files
Files changed (3) hide show
  1. Dockerfile +14 -0
  2. README.md +6 -6
  3. app.py +458 -0
Dockerfile ADDED
@@ -0,0 +1,14 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ FROM python:3.10-slim
2
+
3
+ WORKDIR /app
4
+
5
+ RUN pip install --no-cache-dir gradio==3.50.2 requests>=2.28.0
6
+
7
+ COPY app.py .
8
+
9
+
10
+ EXPOSE 7860
11
+ ENV GRADIO_SERVER_NAME="0.0.0.0"
12
+ ENV GRADIO_SERVER_PORT="7860"
13
+
14
+ CMD ["python", "app.py"]
README.md CHANGED
@@ -1,10 +1,10 @@
1
  ---
2
- title: Upscaler Comparison
3
- emoji:
4
- colorFrom: pink
5
- colorTo: green
6
  sdk: docker
7
  pinned: false
 
 
8
  ---
9
-
10
- Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
 
1
  ---
2
+ title: Image Upscaler Comparison
3
+ emoji: 🖼️
4
+ colorFrom: blue
5
+ colorTo: purple
6
  sdk: docker
7
  pinned: false
8
+ license: mit
9
+ short_description: Compare Image Upscaler vs Ultimate Upscaler side by sid...
10
  ---
 
 
app.py ADDED
@@ -0,0 +1,458 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ Image Upscaler Comparison - Model Comparison
3
+ Powered by WaveSpeed AI - Auto-generated by Space Generator
4
+ """
5
+
6
+ import gradio as gr
7
+ import requests
8
+ import time
9
+ from typing import Dict, Any
10
+
11
+ # ============ API Configuration ============
12
+ WAVESPEED_API_BASE = "https://api.wavespeed.ai/api/v3"
13
+ UPLOAD_ENDPOINT = "https://api.wavespeed.ai/api/v3/media/upload/binary"
14
+
15
+ MODEL_A_ENDPOINT = "wavespeed-ai/image-upscaler"
16
+ MODEL_A_NAME = "Image Upscaler"
17
+
18
+ MODEL_B_ENDPOINT = "wavespeed-ai/ultimate-image-upscaler"
19
+ MODEL_B_NAME = "Ultimate Upscaler"
20
+
21
+ POLL_INTERVAL = 1.5
22
+ POLL_MAX_SECONDS = 120
23
+
24
+
25
+
26
+ # ============ CSS ============
27
+ CUSTOM_CSS = """
28
+ /* ===== Base Styles ===== */
29
+ html, body, .gradio-container {
30
+ background: linear-gradient(145deg, #f5f3ff 0%, #ede9fe 50%, #e0e7ff 100%) !important;
31
+ font-family: 'Inter', -apple-system, BlinkMacSystemFont, sans-serif !important;
32
+ }
33
+
34
+ .gradio-container {
35
+ max-width: 1200px !important;
36
+ margin: 0 auto !important;
37
+ padding: 24px !important;
38
+ }
39
+
40
+ /* ===== Hero Section ===== */
41
+ .hero-container {
42
+ text-align: center;
43
+ padding: 40px 20px 30px;
44
+ }
45
+
46
+ .hero-badge {
47
+ display: inline-block;
48
+ background: linear-gradient(135deg, #06b6d4, #0891b2);
49
+ padding: 10px 24px;
50
+ border-radius: 50px;
51
+ font-size: 0.7rem;
52
+ color: #fff;
53
+ font-weight: 700;
54
+ letter-spacing: 1.5px;
55
+ margin-bottom: 16px;
56
+ box-shadow: 0 4px 20px rgba(139, 92, 246, 0.35);
57
+ }
58
+
59
+ .hero-title {
60
+ font-size: 2.5rem;
61
+ font-weight: 800;
62
+ margin: 0 0 12px 0;
63
+ background: linear-gradient(135deg, #6d28d9 0%, #06b6d4 50%, #a78bfa 100%);
64
+ -webkit-background-clip: text;
65
+ -webkit-text-fill-color: transparent;
66
+ background-clip: text;
67
+ }
68
+
69
+ .hero-desc {
70
+ font-size: 1rem;
71
+ color: #64748b;
72
+ max-width: 100%;
73
+ margin: 0 auto 16px;
74
+ line-height: 1.6;
75
+ }
76
+
77
+ /* ===== Main Card ===== */
78
+ .main-card {
79
+ background: #ffffff;
80
+ border: 1px solid rgba(139, 92, 246, 0.1);
81
+ border-radius: 20px;
82
+ padding: 24px;
83
+ margin-bottom: 16px;
84
+ box-shadow: 0 4px 24px rgba(139, 92, 246, 0.08);
85
+ }
86
+
87
+ /* ===== Model Labels ===== */
88
+ .model-label {
89
+ display: inline-block;
90
+ padding: 6px 14px;
91
+ border-radius: 8px;
92
+ font-weight: 700;
93
+ font-size: 0.85rem;
94
+ margin-bottom: 12px;
95
+ }
96
+
97
+ .model-a-label {
98
+ background: linear-gradient(135deg, #3b82f6, #2563eb);
99
+ color: #fff;
100
+ }
101
+
102
+ .model-b-label {
103
+ background: linear-gradient(135deg, #10b981, #059669);
104
+ color: #fff;
105
+ }
106
+
107
+ /* ===== VS Divider ===== */
108
+ .vs-divider {
109
+ display: flex;
110
+ align-items: center;
111
+ justify-content: center;
112
+ padding: 8px 0;
113
+ }
114
+
115
+ .vs-badge {
116
+ background: linear-gradient(135deg, #06b6d4, #0891b2);
117
+ color: #fff;
118
+ padding: 8px 16px;
119
+ border-radius: 20px;
120
+ font-weight: 800;
121
+ font-size: 0.9rem;
122
+ box-shadow: 0 4px 12px rgba(139, 92, 246, 0.3);
123
+ }
124
+
125
+ /* ===== API Key Section ===== */
126
+ .api-key-row {
127
+ display: flex;
128
+ align-items: center;
129
+ justify-content: space-between;
130
+ margin-bottom: 12px;
131
+ }
132
+
133
+ .api-key-label {
134
+ display: flex;
135
+ align-items: center;
136
+ gap: 10px;
137
+ color: #1e293b;
138
+ font-weight: 700;
139
+ font-size: 1rem;
140
+ }
141
+
142
+ .get-key-btn {
143
+ padding: 10px 20px;
144
+ background: linear-gradient(135deg, #06b6d4, #0891b2);
145
+ border: none;
146
+ border-radius: 10px;
147
+ color: #fff !important;
148
+ text-decoration: none;
149
+ font-weight: 600;
150
+ font-size: 0.85rem;
151
+ transition: all 0.25s ease;
152
+ box-shadow: 0 4px 12px rgba(139, 92, 246, 0.3);
153
+ }
154
+
155
+ .get-key-btn:hover {
156
+ transform: translateY(-2px);
157
+ box-shadow: 0 6px 20px rgba(139, 92, 246, 0.4);
158
+ }
159
+
160
+ /* ===== Section Title ===== */
161
+ .section-title {
162
+ color: #1e293b;
163
+ font-weight: 700;
164
+ font-size: 1rem;
165
+ display: flex;
166
+ align-items: center;
167
+ gap: 10px;
168
+ margin-bottom: 12px;
169
+ }
170
+
171
+ /* ===== Upload Area ===== */
172
+ .upload-area {
173
+ border: 2px dashed rgba(139, 92, 246, 0.3) !important;
174
+ border-radius: 16px !important;
175
+ background: linear-gradient(145deg, #faf5ff 0%, #f5f3ff 100%) !important;
176
+ min-height: 180px !important;
177
+ }
178
+
179
+ /* ===== Result Areas ===== */
180
+ .result-area-a {
181
+ border: 2px solid rgba(59, 130, 246, 0.3) !important;
182
+ border-radius: 16px !important;
183
+ background: rgba(239, 246, 255, 0.5) !important;
184
+ min-height: 200px !important;
185
+ }
186
+
187
+ .result-area-b {
188
+ border: 2px solid rgba(16, 185, 129, 0.3) !important;
189
+ border-radius: 16px !important;
190
+ background: rgba(236, 253, 245, 0.5) !important;
191
+ min-height: 200px !important;
192
+ }
193
+
194
+ /* ===== Button Styling ===== */
195
+ .compare-btn {
196
+ width: 100%;
197
+ margin-top: 16px !important;
198
+ background: linear-gradient(135deg, #06b6d4, #0891b2) !important;
199
+ border: none !important;
200
+ color: #fff !important;
201
+ font-weight: 700 !important;
202
+ font-size: 1.1rem !important;
203
+ padding: 16px 28px !important;
204
+ border-radius: 12px !important;
205
+ box-shadow: 0 4px 16px rgba(139, 92, 246, 0.35) !important;
206
+ }
207
+
208
+ .compare-btn:hover {
209
+ transform: translateY(-2px) !important;
210
+ box-shadow: 0 8px 24px rgba(139, 92, 246, 0.45) !important;
211
+ }
212
+
213
+ /* ===== CTA Section ===== */
214
+ .cta-container {
215
+ text-align: center;
216
+ padding: 36px 28px;
217
+ background: linear-gradient(135deg, #06b6d4 0%, #0891b2 50%, #6d28d9 100%);
218
+ border-radius: 20px;
219
+ margin-top: 8px;
220
+ box-shadow: 0 8px 32px rgba(139, 92, 246, 0.35);
221
+ }
222
+
223
+ .cta-title {
224
+ color: #fff;
225
+ font-size: 1.4rem;
226
+ font-weight: 800;
227
+ margin: 0 0 8px 0;
228
+ }
229
+
230
+ .cta-desc {
231
+ color: rgba(255, 255, 255, 0.9);
232
+ font-size: 0.95rem;
233
+ margin: 0 0 20px 0;
234
+ }
235
+
236
+ .cta-btn {
237
+ display: inline-block;
238
+ padding: 12px 32px;
239
+ background: #fff;
240
+ border-radius: 12px;
241
+ color: #0891b2 !important;
242
+ text-decoration: none;
243
+ font-weight: 700;
244
+ font-size: 0.95rem;
245
+ box-shadow: 0 4px 16px rgba(0, 0, 0, 0.1);
246
+ }
247
+
248
+ .cta-btn:hover {
249
+ transform: translateY(-3px);
250
+ box-shadow: 0 8px 24px rgba(0, 0, 0, 0.15);
251
+ }
252
+
253
+ /* ===== Hide Elements ===== */
254
+ footer { display: none !important; }
255
+
256
+ /* ===== Input Styling ===== */
257
+ .gradio-container input[type="password"],
258
+ .gradio-container input[type="text"] {
259
+ border: 2px solid #e2e8f0 !important;
260
+ border-radius: 12px !important;
261
+ padding: 12px 14px !important;
262
+ font-size: 0.95rem !important;
263
+ }
264
+
265
+ .gradio-container input:focus {
266
+ border-color: #06b6d4 !important;
267
+ box-shadow: 0 0 0 3px rgba(139, 92, 246, 0.15) !important;
268
+ }
269
+ """
270
+
271
+
272
+ # ============ API Functions ============
273
+ def upload_image(api_key: str, file_path: str) -> str:
274
+ headers = {"Authorization": f"Bearer {api_key.strip()}"}
275
+ with open(file_path, "rb") as f:
276
+ resp = requests.post(UPLOAD_ENDPOINT, headers=headers, files={"file": f}, timeout=60)
277
+ if resp.status_code == 401:
278
+ raise Exception("Invalid API Key")
279
+ elif resp.status_code >= 400:
280
+ raise Exception(f"Upload failed: {resp.status_code}")
281
+ data = resp.json()
282
+ if data.get("code") != 200:
283
+ raise Exception(data.get("message", "Upload failed"))
284
+ return data.get("data", {}).get("download_url")
285
+
286
+
287
+ def call_api(api_key: str, endpoint: str, payload: Dict[str, Any]) -> Dict[str, Any]:
288
+ headers = {"Authorization": f"Bearer {api_key.strip()}", "Content-Type": "application/json"}
289
+ resp = requests.post(f"{WAVESPEED_API_BASE}/{endpoint}", json=payload, headers=headers, timeout=30)
290
+ if resp.status_code == 401:
291
+ raise Exception("Invalid API Key")
292
+ elif resp.status_code == 429:
293
+ raise Exception("Quota exceeded")
294
+ elif resp.status_code >= 400:
295
+ raise Exception(f"API error: {resp.status_code}")
296
+ data = resp.json()
297
+ if data.get("code") != 200:
298
+ raise Exception(data.get("message", "Unknown error"))
299
+ return data.get("data", {})
300
+
301
+
302
+ def poll_result(api_key: str, request_id: str) -> Dict[str, Any]:
303
+ headers = {"Authorization": f"Bearer {api_key.strip()}"}
304
+ url = f"{WAVESPEED_API_BASE}/predictions/{request_id}/result"
305
+ start_time = time.time()
306
+ while time.time() - start_time < POLL_MAX_SECONDS:
307
+ resp = requests.get(url, headers=headers, timeout=30)
308
+ if resp.status_code >= 400:
309
+ raise Exception("Failed to get result")
310
+ result = resp.json().get("data", {})
311
+ status = result.get("status", "")
312
+ if status == "completed":
313
+ return result
314
+ elif status == "failed":
315
+ raise Exception("Generation failed")
316
+ time.sleep(POLL_INTERVAL)
317
+ raise Exception("Timeout")
318
+
319
+
320
+ def run_model(api_key: str, endpoint: str, payload: Dict[str, Any]):
321
+ """运行单个模型"""
322
+ try:
323
+ result = call_api(api_key, endpoint, payload)
324
+ request_id = result.get("id")
325
+ if not request_id:
326
+ return None
327
+ final_result = poll_result(api_key, request_id)
328
+ outputs = final_result.get("outputs", [])
329
+ if outputs:
330
+ return outputs[0]
331
+ return None
332
+ except Exception as e:
333
+ return None
334
+
335
+
336
+ def compare_models(api_key: str, image_path: str):
337
+ """同时运行两个模型进行对比"""
338
+ if not api_key or not api_key.strip():
339
+ gr.Warning("Please enter your API Key")
340
+ return None, None
341
+ if not image_path:
342
+ gr.Warning("Please upload an image")
343
+ return None, None
344
+
345
+ try:
346
+ gr.Info("Uploading image...")
347
+ image_url = upload_image(api_key, image_path)
348
+ except Exception as e:
349
+ gr.Warning(f"Upload failed: {e}")
350
+ return None, None
351
+
352
+ gr.Info(f"Running {MODEL_A_NAME}...")
353
+ payload_a = {
354
+ "image": image_url,
355
+ "enable_base64_output": False,
356
+ "enable_sync_mode": False
357
+ }
358
+ result_a = run_model(api_key, MODEL_A_ENDPOINT, payload_a)
359
+
360
+ gr.Info(f"Running {MODEL_B_NAME}...")
361
+ payload_b = {
362
+ "image": image_url,
363
+ "enable_base64_output": False,
364
+ "enable_sync_mode": False
365
+ }
366
+ result_b = run_model(api_key, MODEL_B_ENDPOINT, payload_b)
367
+
368
+ if result_a and result_b:
369
+ gr.Info("Comparison complete!")
370
+ elif result_a:
371
+ gr.Warning(f"{MODEL_B_NAME} failed")
372
+ elif result_b:
373
+ gr.Warning(f"{MODEL_A_NAME} failed")
374
+ else:
375
+ gr.Warning("Both models failed")
376
+
377
+ return result_a, result_b
378
+
379
+
380
+ # ============ Gradio UI ============
381
+ with gr.Blocks(css=CUSTOM_CSS, title="Image Upscaler Comparison") as demo:
382
+
383
+ # Hero Section
384
+ gr.HTML("""
385
+ <div class="hero-container">
386
+ <div class="hero-badge">WAVESPEED AI</div>
387
+ <h1 class="hero-title">Image Upscaler Comparison</h1>
388
+ <p class="hero-desc">Compare Image Upscaler vs Ultimate Upscaler side by side.</p>
389
+ </div>
390
+ """)
391
+
392
+
393
+
394
+ # API Key Card
395
+ with gr.Group(elem_classes="main-card"):
396
+ gr.HTML("""
397
+ <div class="api-key-row">
398
+ <span class="api-key-label">
399
+ <svg width="20" height="20" fill="#06b6d4" viewBox="0 0 20 20"><path fill-rule="evenodd" d="M18 8a6 6 0 01-7.743 5.743L10 14l-1 1-1 1H6v2H2v-4l4.257-4.257A6 6 0 1118 8zm-6-4a1 1 0 100 2 2 2 0 012 2 1 1 0 102 0 4 4 0 00-4-4z" clip-rule="evenodd"/></svg>
400
+ API Key
401
+ </span>
402
+ <a href="https://wavespeed.ai/accesskey" target="_blank" class="get-key-btn">Get API Key</a>
403
+ </div>
404
+ """)
405
+ api_key_input = gr.Textbox(
406
+ placeholder="Enter your WaveSpeed API key",
407
+ type="password",
408
+ show_label=False
409
+ )
410
+
411
+ # Input Section
412
+ with gr.Group(elem_classes="main-card"):
413
+ gr.HTML("""
414
+ <div class="section-title">
415
+ <svg width="20" height="20" fill="#06b6d4" viewBox="0 0 20 20"><path fill-rule="evenodd" d="M4 3a2 2 0 00-2 2v10a2 2 0 002 2h12a2 2 0 002-2V5a2 2 0 00-2-2H4zm12 12H4l4-8 3 6 2-4 3 6z" clip-rule="evenodd"/></svg>
416
+ Input
417
+ </div>
418
+ """)
419
+ image_input = gr.Image(
420
+ label="Upload Image",
421
+ type="filepath",
422
+ source="upload",
423
+ elem_classes="upload-area"
424
+ )
425
+ compare_btn = gr.Button("🔄 Compare Models", variant="primary", elem_classes="compare-btn")
426
+
427
+ # Results Section
428
+ with gr.Group(elem_classes="main-card"):
429
+ with gr.Row():
430
+ with gr.Column(scale=1):
431
+ gr.HTML(f'<span class="model-label model-a-label">{MODEL_A_NAME}</span>')
432
+ output_a = gr.Image(label="", interactive=False, elem_classes="result-area-a")
433
+
434
+ with gr.Column(scale=1):
435
+ gr.HTML(f'<span class="model-label model-b-label">{MODEL_B_NAME}</span>')
436
+ output_b = gr.Image(label="", interactive=False, elem_classes="result-area-b")
437
+
438
+ # CTA Section
439
+ gr.HTML("""
440
+ <div class="cta-container">
441
+ <h3 class="cta-title">Want More Features?</h3>
442
+ <p class="cta-desc">Higher resolutions, batch processing, and 700+ AI models</p>
443
+ <a href="https://wavespeed.ai/models" target="_blank" class="cta-btn">
444
+ Explore WaveSpeed.ai
445
+ </a>
446
+ </div>
447
+ """)
448
+
449
+ # Event binding
450
+ compare_btn.click(
451
+ fn=compare_models,
452
+ inputs=[api_key_input, image_input],
453
+ outputs=[output_a, output_b],
454
+ )
455
+
456
+
457
+ if __name__ == "__main__":
458
+ demo.launch(server_name="0.0.0.0", server_port=7860)