seawolf2357 commited on
Commit
549121b
ยท
verified ยท
1 Parent(s): 33759f1

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +613 -89
app.py CHANGED
@@ -21,7 +21,6 @@ login(token=os.environ.get('hf'))
21
  dtype = torch.bfloat16
22
  device = "cuda" if torch.cuda.is_available() else "cpu"
23
  pipeline = QwenImageLayeredPipeline.from_pretrained("Qwen/Qwen-Image-Layered", torch_dtype=dtype).to(device)
24
- # pipeline.set_progress_bar_config(disable=None)
25
 
26
  def ensure_dirname(path: str):
27
  if path and not os.path.exists(path):
@@ -54,21 +53,17 @@ def imagelist_to_pptx(img_files):
54
  return tmp.name
55
 
56
  def export_gallery(images):
57
- # images: list of image file paths
58
  images = [e[0] for e in images]
59
  pptx_path = imagelist_to_pptx(images)
60
  return pptx_path
61
 
62
  def export_gallery_zip(images):
63
- # images: list of tuples (file_path, caption)
64
  images = [e[0] for e in images]
65
 
66
  with tempfile.NamedTemporaryFile(suffix=".zip", delete=False) as tmp:
67
  with zipfile.ZipFile(tmp.name, 'w', zipfile.ZIP_DEFLATED) as zipf:
68
  for i, img_path in enumerate(images):
69
- # Get the file extension from original file
70
  ext = os.path.splitext(img_path)[1] or '.png'
71
- # Add each image to the zip with a numbered filename
72
  zipf.write(img_path, f"layer_{i+1}{ext}")
73
  return tmp.name
74
 
@@ -108,8 +103,8 @@ def infer(input_image,
108
  "num_inference_steps": num_inference_steps,
109
  "num_images_per_prompt": 1,
110
  "layers": layer,
111
- "resolution": 640, # Using different bucket (640, 1024) to determine the resolution. For this version, 640 is recommended
112
- "cfg_normalize": cfg_norm, # Whether enable cfg normalization.
113
  "use_en_prompt": use_en_prompt,
114
  }
115
  print(inputs)
@@ -121,22 +116,39 @@ def infer(input_image,
121
  temp_files = []
122
  for i, image in enumerate(output_images):
123
  output.append(image)
124
- # Save to temp file for export
125
  tmp = tempfile.NamedTemporaryFile(suffix=".png", delete=False)
126
  image.save(tmp.name)
127
  temp_files.append(tmp.name)
128
 
129
- # Generate PPTX
130
  pptx_path = imagelist_to_pptx(temp_files)
131
 
132
- # Generate ZIP
133
  with tempfile.NamedTemporaryFile(suffix=".zip", delete=False) as tmp:
134
  with zipfile.ZipFile(tmp.name, 'w', zipfile.ZIP_DEFLATED) as zipf:
135
  for i, img_path in enumerate(temp_files):
136
  zipf.write(img_path, f"layer_{i+1}.png")
137
  zip_path = tmp.name
138
 
139
- return output, pptx_path, zip_path
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
140
 
141
  ensure_dirname(LOG_DIR)
142
  examples = [
@@ -153,87 +165,599 @@ examples = [
153
  "assets/test_images/11.png",
154
  "assets/test_images/12.png",
155
  "assets/test_images/13.png",
156
- ]
157
-
158
-
159
- with gr.Blocks() as demo:
160
- with gr.Column(elem_id="col-container"):
161
- gr.HTML('<img src="https://qianwen-res.oss-cn-beijing.aliyuncs.com/Qwen-Image/layered/qwen-image-layered-logo.png" alt="Qwen-Image-Layered Logo" width="600" style="display: block; margin: 0 auto;">')
162
- gr.Markdown("""
163
- The text prompt is intended to describe the overall content of the input imageโ€”including elements that may be partially occluded (e.g., you may specify the text hidden behind a foreground object). It is not designed to control the semantic content of individual layers explicitly.
164
- """)
165
- with gr.Row():
166
- with gr.Column(scale=1):
167
- input_image = gr.Image(label="Input Image", image_mode="RGBA")
168
-
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
169
 
170
- with gr.Accordion("Advanced Settings", open=False):
171
- prompt = gr.Textbox(
172
- label="Prompt (Optional)",
173
- placeholder="Please enter the prompt to descibe the image. ๏ผˆOptional๏ผ‰",
174
- value="",
175
- lines=2,
176
- )
177
- neg_prompt = gr.Textbox(
178
- label="Negative Prompt (Optional)",
179
- placeholder="Please enter the negative prompt",
180
- value=" ",
181
- lines=2,
182
- )
183
-
184
- seed = gr.Slider(
185
- label="Seed",
186
- minimum=0,
187
- maximum=MAX_SEED,
188
- step=1,
189
- value=0,
190
- )
191
- randomize_seed = gr.Checkbox(label="Randomize seed", value=True)
192
-
193
- true_guidance_scale = gr.Slider(
194
- label="True guidance scale",
195
- minimum=1.0,
196
- maximum=10.0,
197
- step=0.1,
198
- value=4.0
199
- )
200
-
201
- num_inference_steps = gr.Slider(
202
- label="Number of inference steps",
203
- minimum=1,
204
- maximum=50,
205
- step=1,
206
- value=50,
207
- )
208
-
209
- layer = gr.Slider(
210
- label="Layers",
211
- minimum=2,
212
- maximum=10,
213
- step=1,
214
- value=4,
215
- )
216
-
217
- cfg_norm = gr.Checkbox(label="Whether enable CFG normalization", value=True)
218
- use_en_prompt = gr.Checkbox(label="Automatic caption language if no prompt provided, True for EN, False for ZH", value=True)
219
 
220
- run_button = gr.Button("Decompose!", variant="primary")
221
-
222
- with gr.Column(scale=2):
223
- gallery = gr.Gallery(label="Layers", columns=4, rows=1, format="png")
224
- with gr.Row():
225
- export_file = gr.File(label="Download PPTX")
226
- export_zip_file = gr.File(label="Download ZIP")
227
-
228
- gr.Examples(examples=examples,
229
- inputs=[input_image],
230
- outputs=[gallery, export_file, export_zip_file],
231
- fn=infer,
232
- examples_per_page=14,
233
- cache_examples=False,
234
- run_on_click=True
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
235
  )
236
 
 
237
  run_button.click(
238
  fn=infer,
239
  inputs=[
@@ -248,7 +772,7 @@ with gr.Blocks() as demo:
248
  cfg_norm,
249
  use_en_prompt,
250
  ],
251
- outputs=[gallery, export_file, export_zip_file],
252
  )
253
 
254
  if __name__ == "__main__":
 
21
  dtype = torch.bfloat16
22
  device = "cuda" if torch.cuda.is_available() else "cpu"
23
  pipeline = QwenImageLayeredPipeline.from_pretrained("Qwen/Qwen-Image-Layered", torch_dtype=dtype).to(device)
 
24
 
25
  def ensure_dirname(path: str):
26
  if path and not os.path.exists(path):
 
53
  return tmp.name
54
 
55
  def export_gallery(images):
 
56
  images = [e[0] for e in images]
57
  pptx_path = imagelist_to_pptx(images)
58
  return pptx_path
59
 
60
  def export_gallery_zip(images):
 
61
  images = [e[0] for e in images]
62
 
63
  with tempfile.NamedTemporaryFile(suffix=".zip", delete=False) as tmp:
64
  with zipfile.ZipFile(tmp.name, 'w', zipfile.ZIP_DEFLATED) as zipf:
65
  for i, img_path in enumerate(images):
 
66
  ext = os.path.splitext(img_path)[1] or '.png'
 
67
  zipf.write(img_path, f"layer_{i+1}{ext}")
68
  return tmp.name
69
 
 
103
  "num_inference_steps": num_inference_steps,
104
  "num_images_per_prompt": 1,
105
  "layers": layer,
106
+ "resolution": 640,
107
+ "cfg_normalize": cfg_norm,
108
  "use_en_prompt": use_en_prompt,
109
  }
110
  print(inputs)
 
116
  temp_files = []
117
  for i, image in enumerate(output_images):
118
  output.append(image)
 
119
  tmp = tempfile.NamedTemporaryFile(suffix=".png", delete=False)
120
  image.save(tmp.name)
121
  temp_files.append(tmp.name)
122
 
 
123
  pptx_path = imagelist_to_pptx(temp_files)
124
 
 
125
  with tempfile.NamedTemporaryFile(suffix=".zip", delete=False) as tmp:
126
  with zipfile.ZipFile(tmp.name, 'w', zipfile.ZIP_DEFLATED) as zipf:
127
  for i, img_path in enumerate(temp_files):
128
  zipf.write(img_path, f"layer_{i+1}.png")
129
  zip_path = tmp.name
130
 
131
+ # ์ •๋ณด ๋กœ๊ทธ ์ƒ์„ฑ
132
+ info_log = f"""โœ… DECOMPOSITION COMPLETE!
133
+ {'=' * 50}
134
+ ๐Ÿ–ผ๏ธ Input Image Info:
135
+ โ€ข Size: {pil_image.size[0]} x {pil_image.size[1]}
136
+ โ€ข Mode: {pil_image.mode}
137
+ {'=' * 50}
138
+ โš™๏ธ Generation Settings:
139
+ โ€ข Seed: {seed}
140
+ โ€ข Layers: {layer}
141
+ โ€ข Steps: {num_inference_steps}
142
+ โ€ข CFG Scale: {true_guidance_scale}
143
+ {'=' * 50}
144
+ ๐Ÿ“ฆ Output:
145
+ โ€ข Generated Layers: {len(output_images)}
146
+ โ€ข PPTX: Ready to download!
147
+ โ€ข ZIP: Ready to download!
148
+ {'=' * 50}
149
+ ๐Ÿ’พ All files ready for download!"""
150
+
151
+ return output, pptx_path, zip_path, info_log
152
 
153
  ensure_dirname(LOG_DIR)
154
  examples = [
 
165
  "assets/test_images/11.png",
166
  "assets/test_images/12.png",
167
  "assets/test_images/13.png",
168
+ ]
169
+
170
+
171
+ # ============================================
172
+ # ๐ŸŽจ Comic Classic Theme - Toon Playground
173
+ # ============================================
174
+
175
+ css = """
176
+ /* ===== ๐ŸŽจ Google Fonts Import ===== */
177
+ @import url('https://fonts.googleapis.com/css2?family=Bangers&family=Comic+Neue:wght@400;700&display=swap');
178
+
179
+ /* ===== ๐ŸŽจ Comic Classic ๋ฐฐ๊ฒฝ - ๋นˆํ‹ฐ์ง€ ํŽ˜์ดํผ + ๋„ํŠธ ํŒจํ„ด ===== */
180
+ .gradio-container {
181
+ background-color: #FEF9C3 !important;
182
+ background-image:
183
+ radial-gradient(#1F2937 1px, transparent 1px) !important;
184
+ background-size: 20px 20px !important;
185
+ min-height: 100vh !important;
186
+ font-family: 'Comic Neue', cursive, sans-serif !important;
187
+ }
188
+
189
+ /* ===== ํ—ˆ๊น…ํŽ˜์ด์Šค ์ƒ๋‹จ ์š”์†Œ ์ˆจ๊น€ ===== */
190
+ .huggingface-space-header,
191
+ #space-header,
192
+ .space-header,
193
+ [class*="space-header"],
194
+ .svelte-1ed2p3z,
195
+ .space-header-badge,
196
+ .header-badge,
197
+ [data-testid="space-header"],
198
+ .svelte-kqij2n,
199
+ .svelte-1ax1toq,
200
+ .embed-container > div:first-child {
201
+ display: none !important;
202
+ visibility: hidden !important;
203
+ height: 0 !important;
204
+ width: 0 !important;
205
+ overflow: hidden !important;
206
+ opacity: 0 !important;
207
+ pointer-events: none !important;
208
+ }
209
+
210
+ /* ===== Footer ์™„์ „ ์ˆจ๊น€ ===== */
211
+ footer,
212
+ .footer,
213
+ .gradio-container footer,
214
+ .built-with,
215
+ [class*="footer"],
216
+ .gradio-footer,
217
+ .main-footer,
218
+ div[class*="footer"],
219
+ .show-api,
220
+ .built-with-gradio,
221
+ a[href*="gradio.app"],
222
+ a[href*="huggingface.co/spaces"] {
223
+ display: none !important;
224
+ visibility: hidden !important;
225
+ height: 0 !important;
226
+ padding: 0 !important;
227
+ margin: 0 !important;
228
+ }
229
+
230
+ /* ===== ๋ฉ”์ธ ์ปจํ…Œ์ด๋„ˆ ===== */
231
+ #col-container {
232
+ max-width: 1200px;
233
+ margin: 0 auto;
234
+ }
235
+
236
+ /* ===== ๐ŸŽจ ํ—ค๋” ํƒ€์ดํ‹€ - ์ฝ”๋ฏน ์Šคํƒ€์ผ ===== */
237
+ .header-text h1 {
238
+ font-family: 'Bangers', cursive !important;
239
+ color: #1F2937 !important;
240
+ font-size: 3.5rem !important;
241
+ font-weight: 400 !important;
242
+ text-align: center !important;
243
+ margin-bottom: 0.5rem !important;
244
+ text-shadow:
245
+ 4px 4px 0px #FACC15,
246
+ 6px 6px 0px #1F2937 !important;
247
+ letter-spacing: 3px !important;
248
+ -webkit-text-stroke: 2px #1F2937 !important;
249
+ }
250
+
251
+ /* ===== ๐ŸŽจ ์„œ๋ธŒํƒ€์ดํ‹€ ===== */
252
+ .subtitle {
253
+ text-align: center !important;
254
+ font-family: 'Comic Neue', cursive !important;
255
+ font-size: 1.2rem !important;
256
+ color: #1F2937 !important;
257
+ margin-bottom: 1.5rem !important;
258
+ font-weight: 700 !important;
259
+ }
260
+
261
+ /* ===== ๐ŸŽจ ์นด๋“œ/ํŒจ๋„ - ๋งŒํ™” ํ”„๋ ˆ์ž„ ์Šคํƒ€์ผ ===== */
262
+ .gr-panel,
263
+ .gr-box,
264
+ .gr-form,
265
+ .block,
266
+ .gr-group {
267
+ background: #FFFFFF !important;
268
+ border: 3px solid #1F2937 !important;
269
+ border-radius: 8px !important;
270
+ box-shadow: 6px 6px 0px #1F2937 !important;
271
+ transition: all 0.2s ease !important;
272
+ }
273
+
274
+ .gr-panel:hover,
275
+ .block:hover {
276
+ transform: translate(-2px, -2px) !important;
277
+ box-shadow: 8px 8px 0px #1F2937 !important;
278
+ }
279
+
280
+ /* ===== ๐ŸŽจ ์ž…๋ ฅ ํ•„๋“œ (Textbox) ===== */
281
+ textarea,
282
+ input[type="text"],
283
+ input[type="number"] {
284
+ background: #FFFFFF !important;
285
+ border: 3px solid #1F2937 !important;
286
+ border-radius: 8px !important;
287
+ color: #1F2937 !important;
288
+ font-family: 'Comic Neue', cursive !important;
289
+ font-size: 1rem !important;
290
+ font-weight: 700 !important;
291
+ transition: all 0.2s ease !important;
292
+ }
293
+
294
+ textarea:focus,
295
+ input[type="text"]:focus,
296
+ input[type="number"]:focus {
297
+ border-color: #3B82F6 !important;
298
+ box-shadow: 4px 4px 0px #3B82F6 !important;
299
+ outline: none !important;
300
+ }
301
+
302
+ textarea::placeholder {
303
+ color: #9CA3AF !important;
304
+ font-weight: 400 !important;
305
+ }
306
+
307
+ /* ===== ๐ŸŽจ Primary ๋ฒ„ํŠผ - ์ฝ”๋ฏน ๋ธ”๋ฃจ ===== */
308
+ .gr-button-primary,
309
+ button.primary,
310
+ .gr-button.primary {
311
+ background: #3B82F6 !important;
312
+ border: 3px solid #1F2937 !important;
313
+ border-radius: 8px !important;
314
+ color: #FFFFFF !important;
315
+ font-family: 'Bangers', cursive !important;
316
+ font-weight: 400 !important;
317
+ font-size: 1.3rem !important;
318
+ letter-spacing: 2px !important;
319
+ padding: 14px 28px !important;
320
+ box-shadow: 5px 5px 0px #1F2937 !important;
321
+ transition: all 0.1s ease !important;
322
+ text-shadow: 1px 1px 0px #1F2937 !important;
323
+ }
324
+
325
+ .gr-button-primary:hover,
326
+ button.primary:hover,
327
+ .gr-button.primary:hover {
328
+ background: #2563EB !important;
329
+ transform: translate(-2px, -2px) !important;
330
+ box-shadow: 7px 7px 0px #1F2937 !important;
331
+ }
332
+
333
+ .gr-button-primary:active,
334
+ button.primary:active,
335
+ .gr-button.primary:active {
336
+ transform: translate(3px, 3px) !important;
337
+ box-shadow: 2px 2px 0px #1F2937 !important;
338
+ }
339
+
340
+ /* ===== ๐ŸŽจ Secondary ๋ฒ„ํŠผ - ์ฝ”๋ฏน ๋ ˆ๋“œ ===== */
341
+ .gr-button-secondary,
342
+ button.secondary,
343
+ .decompose-btn {
344
+ background: #EF4444 !important;
345
+ border: 3px solid #1F2937 !important;
346
+ border-radius: 8px !important;
347
+ color: #FFFFFF !important;
348
+ font-family: 'Bangers', cursive !important;
349
+ font-weight: 400 !important;
350
+ font-size: 1.1rem !important;
351
+ letter-spacing: 1px !important;
352
+ box-shadow: 4px 4px 0px #1F2937 !important;
353
+ transition: all 0.1s ease !important;
354
+ text-shadow: 1px 1px 0px #1F2937 !important;
355
+ }
356
+
357
+ .gr-button-secondary:hover,
358
+ button.secondary:hover,
359
+ .decompose-btn:hover {
360
+ background: #DC2626 !important;
361
+ transform: translate(-2px, -2px) !important;
362
+ box-shadow: 6px 6px 0px #1F2937 !important;
363
+ }
364
+
365
+ .gr-button-secondary:active,
366
+ button.secondary:active,
367
+ .decompose-btn:active {
368
+ transform: translate(2px, 2px) !important;
369
+ box-shadow: 2px 2px 0px #1F2937 !important;
370
+ }
371
+
372
+ /* ===== ๐ŸŽจ ๋กœ๊ทธ ์ถœ๋ ฅ ์˜์—ญ ===== */
373
+ .info-log textarea {
374
+ background: #1F2937 !important;
375
+ color: #10B981 !important;
376
+ font-family: 'Courier New', monospace !important;
377
+ font-size: 0.9rem !important;
378
+ font-weight: 400 !important;
379
+ border: 3px solid #10B981 !important;
380
+ border-radius: 8px !important;
381
+ box-shadow: 4px 4px 0px #10B981 !important;
382
+ }
383
+
384
+ /* ===== ๐ŸŽจ ์ด๋ฏธ์ง€ ๏ฟฝ๏ฟฝ๏ฟฝ๋กœ๋“œ ์˜์—ญ ===== */
385
+ .image-upload {
386
+ border: 4px dashed #3B82F6 !important;
387
+ border-radius: 12px !important;
388
+ background: #EFF6FF !important;
389
+ transition: all 0.2s ease !important;
390
+ }
391
+
392
+ .image-upload:hover {
393
+ border-color: #EF4444 !important;
394
+ background: #FEF2F2 !important;
395
+ }
396
+
397
+ /* ===== ๐ŸŽจ ์•„์ฝ”๋””์–ธ - ๋งํ’์„  ์Šคํƒ€์ผ ===== */
398
+ .gr-accordion {
399
+ background: #FACC15 !important;
400
+ border: 3px solid #1F2937 !important;
401
+ border-radius: 8px !important;
402
+ box-shadow: 4px 4px 0px #1F2937 !important;
403
+ }
404
+
405
+ .gr-accordion-header {
406
+ color: #1F2937 !important;
407
+ font-family: 'Comic Neue', cursive !important;
408
+ font-weight: 700 !important;
409
+ font-size: 1.1rem !important;
410
+ }
411
+
412
+ /* ===== ๐ŸŽจ ๊ฐค๋Ÿฌ๋ฆฌ ์Šคํƒ€์ผ ===== */
413
+ .gr-gallery,
414
+ .gallery-container {
415
+ border: 4px solid #1F2937 !important;
416
+ border-radius: 8px !important;
417
+ box-shadow: 8px 8px 0px #1F2937 !important;
418
+ overflow: hidden !important;
419
+ background: #FFFFFF !important;
420
+ }
421
+
422
+ .gr-gallery .thumbnail-item {
423
+ border: 3px solid #1F2937 !important;
424
+ border-radius: 6px !important;
425
+ transition: all 0.2s ease !important;
426
+ }
427
+
428
+ .gr-gallery .thumbnail-item:hover {
429
+ transform: scale(1.05) !important;
430
+ box-shadow: 4px 4px 0px #3B82F6 !important;
431
+ }
432
+
433
+ /* ===== ๐ŸŽจ ์ด๋ฏธ์ง€ ์ถœ๋ ฅ ์˜์—ญ ===== */
434
+ .gr-image,
435
+ .image-container {
436
+ border: 4px solid #1F2937 !important;
437
+ border-radius: 8px !important;
438
+ box-shadow: 8px 8px 0px #1F2937 !important;
439
+ overflow: hidden !important;
440
+ background: #FFFFFF !important;
441
+ }
442
+
443
+ /* ===== ๐ŸŽจ ํŒŒ์ผ ๋‹ค์šด๋กœ๋“œ ์˜์—ญ ===== */
444
+ .gr-file,
445
+ .file-container {
446
+ background: #FFFFFF !important;
447
+ border: 3px solid #1F2937 !important;
448
+ border-radius: 8px !important;
449
+ box-shadow: 4px 4px 0px #1F2937 !important;
450
+ }
451
+
452
+ .gr-file:hover {
453
+ transform: translate(-2px, -2px) !important;
454
+ box-shadow: 6px 6px 0px #1F2937 !important;
455
+ }
456
+
457
+ /* ===== ๐ŸŽจ ์Šฌ๋ผ์ด๋” ์Šคํƒ€์ผ ===== */
458
+ input[type="range"] {
459
+ accent-color: #3B82F6 !important;
460
+ }
461
+
462
+ .gr-slider {
463
+ background: #FFFFFF !important;
464
+ }
465
+
466
+ /* ===== ๐ŸŽจ ์ฒดํฌ๋ฐ•์Šค ์Šคํƒ€์ผ ===== */
467
+ input[type="checkbox"] {
468
+ accent-color: #3B82F6 !important;
469
+ width: 20px !important;
470
+ height: 20px !important;
471
+ border: 2px solid #1F2937 !important;
472
+ }
473
+
474
+ /* ===== ๐ŸŽจ ๋ผ๋ฒจ ์Šคํƒ€์ผ ===== */
475
+ label,
476
+ .gr-input-label,
477
+ .gr-block-label {
478
+ color: #1F2937 !important;
479
+ font-family: 'Comic Neue', cursive !important;
480
+ font-weight: 700 !important;
481
+ font-size: 1rem !important;
482
+ }
483
+
484
+ span.gr-label {
485
+ color: #1F2937 !important;
486
+ }
487
+
488
+ /* ===== ๐ŸŽจ ์ •๋ณด ํ…์ŠคํŠธ ===== */
489
+ .gr-info,
490
+ .info {
491
+ color: #6B7280 !important;
492
+ font-family: 'Comic Neue', cursive !important;
493
+ font-size: 0.9rem !important;
494
+ }
495
+
496
+ /* ===== ๐ŸŽจ ํ”„๋กœ๊ทธ๋ ˆ์Šค ๋ฐ” ===== */
497
+ .progress-bar,
498
+ .gr-progress-bar {
499
+ background: #3B82F6 !important;
500
+ border: 2px solid #1F2937 !important;
501
+ border-radius: 4px !important;
502
+ }
503
+
504
+ /* ===== ๐ŸŽจ Examples ์„น์…˜ ===== */
505
+ .gr-examples {
506
+ background: #FFFFFF !important;
507
+ border: 3px solid #1F2937 !important;
508
+ border-radius: 8px !important;
509
+ box-shadow: 6px 6px 0px #1F2937 !important;
510
+ padding: 1rem !important;
511
+ }
512
+
513
+ .gr-examples .gr-sample {
514
+ border: 2px solid #1F2937 !important;
515
+ border-radius: 6px !important;
516
+ transition: all 0.2s ease !important;
517
+ }
518
+
519
+ .gr-examples .gr-sample:hover {
520
+ transform: translate(-2px, -2px) !important;
521
+ box-shadow: 4px 4px 0px #3B82F6 !important;
522
+ }
523
+
524
+ /* ===== ๐ŸŽจ ์Šคํฌ๋กค๋ฐ” - ์ฝ”๋ฏน ์Šคํƒ€์ผ ===== */
525
+ ::-webkit-scrollbar {
526
+ width: 12px;
527
+ height: 12px;
528
+ }
529
+
530
+ ::-webkit-scrollbar-track {
531
+ background: #FEF9C3;
532
+ border: 2px solid #1F2937;
533
+ }
534
+
535
+ ::-webkit-scrollbar-thumb {
536
+ background: #3B82F6;
537
+ border: 2px solid #1F2937;
538
+ border-radius: 0px;
539
+ }
540
+
541
+ ::-webkit-scrollbar-thumb:hover {
542
+ background: #EF4444;
543
+ }
544
+
545
+ /* ===== ๐ŸŽจ ์„ ํƒ ํ•˜์ด๋ผ์ดํŠธ ===== */
546
+ ::selection {
547
+ background: #FACC15;
548
+ color: #1F2937;
549
+ }
550
+
551
+ /* ===== ๐ŸŽจ ๋งํฌ ์Šคํƒ€์ผ ===== */
552
+ a {
553
+ color: #3B82F6 !important;
554
+ text-decoration: none !important;
555
+ font-weight: 700 !important;
556
+ }
557
+
558
+ a:hover {
559
+ color: #EF4444 !important;
560
+ }
561
+
562
+ /* ===== ๐ŸŽจ Row/Column ๊ฐ„๊ฒฉ ===== */
563
+ .gr-row {
564
+ gap: 1.5rem !important;
565
+ }
566
+
567
+ .gr-column {
568
+ gap: 1rem !important;
569
+ }
570
+
571
+ /* ===== ๐ŸŽจ ๋‹ค์šด๋กœ๋“œ ๋ฒ„ํŠผ ๊ฐ•์กฐ ===== */
572
+ .download-section {
573
+ background: linear-gradient(135deg, #FACC15 0%, #FEF9C3 100%) !important;
574
+ border: 3px solid #1F2937 !important;
575
+ border-radius: 8px !important;
576
+ padding: 1rem !important;
577
+ box-shadow: 4px 4px 0px #1F2937 !important;
578
+ }
579
+
580
+ /* ===== ๋ฐ˜์‘ํ˜• ์กฐ์ • ===== */
581
+ @media (max-width: 768px) {
582
+ .header-text h1 {
583
+ font-size: 2.2rem !important;
584
+ text-shadow:
585
+ 3px 3px 0px #FACC15,
586
+ 4px 4px 0px #1F2937 !important;
587
+ }
588
+
589
+ .gr-button-primary,
590
+ button.primary {
591
+ padding: 12px 20px !important;
592
+ font-size: 1.1rem !important;
593
+ }
594
+
595
+ .gr-panel,
596
+ .block {
597
+ box-shadow: 4px 4px 0px #1F2937 !important;
598
+ }
599
+ }
600
+
601
+ /* ===== ๐ŸŽจ ๋‹คํฌ๋ชจ๋“œ ๋น„ํ™œ์„ฑํ™” (์ฝ”๋ฏน์€ ๋ฐ์•„์•ผ ํ•จ) ===== */
602
+ @media (prefers-color-scheme: dark) {
603
+ .gradio-container {
604
+ background-color: #FEF9C3 !important;
605
+ }
606
+ }
607
+ """
608
+
609
+ # Build the Gradio interface
610
+ with gr.Blocks(fill_height=True, css=css) as demo:
611
+
612
+ # HOME Badge
613
+ gr.HTML("""
614
+ <div style="text-align: center; margin: 20px 0 10px 0;">
615
+ <a href="https://www.humangen.ai" target="_blank" style="text-decoration: none;">
616
+ <img src="https://img.shields.io/static/v1?label=๐Ÿ  HOME&message=HUMANGEN.AI&color=0000ff&labelColor=ffcc00&style=for-the-badge" alt="HOME">
617
+ </a>
618
+ </div>
619
+ """)
620
+
621
+ # Header Title
622
+ gr.Markdown(
623
+ """
624
+ # ๐ŸŽจ QWEN IMAGE LAYERED DECOMPOSER ๐Ÿ–ผ๏ธ
625
+ """,
626
+ elem_classes="header-text"
627
+ )
628
+
629
+ gr.Markdown(
630
+ """
631
+ <p class="subtitle">๐Ÿ”ฎ Upload an image and decompose it into magical layers! โœจ PPTX & ZIP export ready! ๐Ÿ“ฆ</p>
632
+ """,
633
+ )
634
+
635
+ with gr.Row(equal_height=False):
636
+ # Left column - Input
637
+ with gr.Column(scale=1, min_width=350):
638
+ input_image = gr.Image(
639
+ label="๐Ÿ–ผ๏ธ Upload Your Image",
640
+ image_mode="RGBA",
641
+ elem_classes="image-upload"
642
+ )
643
+
644
+ run_button = gr.Button(
645
+ "๐ŸŽจ DECOMPOSE INTO LAYERS! ๐Ÿ”ฎ",
646
+ variant="primary",
647
+ size="lg",
648
+ elem_classes="decompose-btn"
649
+ )
650
+
651
+ with gr.Accordion("โš™๏ธ Advanced Settings", open=False):
652
+ prompt = gr.Textbox(
653
+ label="โœ๏ธ Prompt (Optional)",
654
+ placeholder="Describe the image content including hidden elements...",
655
+ value="",
656
+ lines=2,
657
+ )
658
+ neg_prompt = gr.Textbox(
659
+ label="๐Ÿšซ Negative Prompt (Optional)",
660
+ placeholder="What to avoid in generation...",
661
+ value=" ",
662
+ lines=2,
663
+ )
664
 
665
+ seed = gr.Slider(
666
+ label="๐ŸŽฒ Seed",
667
+ minimum=0,
668
+ maximum=MAX_SEED,
669
+ step=1,
670
+ value=0,
671
+ )
672
+ randomize_seed = gr.Checkbox(label="๐Ÿ”€ Randomize seed", value=True)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
673
 
674
+ true_guidance_scale = gr.Slider(
675
+ label="๐ŸŽฏ True Guidance Scale",
676
+ minimum=1.0,
677
+ maximum=10.0,
678
+ step=0.1,
679
+ value=4.0
680
+ )
681
+
682
+ num_inference_steps = gr.Slider(
683
+ label="๐Ÿ”„ Inference Steps",
684
+ minimum=1,
685
+ maximum=50,
686
+ step=1,
687
+ value=50,
688
+ )
689
+
690
+ layer = gr.Slider(
691
+ label="๐Ÿ“š Number of Layers",
692
+ minimum=2,
693
+ maximum=10,
694
+ step=1,
695
+ value=4,
696
+ )
697
+
698
+ cfg_norm = gr.Checkbox(label="โœ… Enable CFG Normalization", value=True)
699
+ use_en_prompt = gr.Checkbox(label="๐ŸŒ Auto Caption (EN=True, ZH=False)", value=True)
700
+
701
+ with gr.Accordion("๐Ÿ“œ Processing Log", open=True):
702
+ info_log = gr.Textbox(
703
+ label="",
704
+ placeholder="Upload an image and click decompose to see info...",
705
+ lines=14,
706
+ max_lines=20,
707
+ interactive=False,
708
+ elem_classes="info-log"
709
+ )
710
+
711
+ # Right column - Output
712
+ with gr.Column(scale=2, min_width=500):
713
+ gallery = gr.Gallery(
714
+ label="๐ŸŽญ Decomposed Layers",
715
+ columns=4,
716
+ rows=2,
717
+ format="png",
718
+ height=400
719
+ )
720
+
721
+ gr.Markdown(
722
+ """
723
+ <p style="text-align: center; margin: 15px 0; font-weight: 700; color: #1F2937; font-size: 1.1rem;">
724
+ ๐Ÿ’พ Download Your Layers Below! ๐Ÿ‘‡
725
+ </p>
726
+ """
727
+ )
728
+
729
+ with gr.Row(elem_classes="download-section"):
730
+ export_file = gr.File(label="๐Ÿ“Š Download PPTX")
731
+ export_zip_file = gr.File(label="๐Ÿ“ฆ Download ZIP")
732
+
733
+ gr.Markdown(
734
+ """
735
+ <p style="text-align: center; margin-top: 15px; font-weight: 700; color: #6B7280;">
736
+ ๐Ÿ’ก PPTX preserves layers for editing โ€ข ZIP contains all PNG files
737
+ </p>
738
+ """
739
+ )
740
+
741
+ # Examples Section
742
+ gr.Markdown(
743
+ """
744
+ <p style="text-align: center; margin: 20px 0 10px 0; font-family: 'Bangers', cursive; font-size: 1.5rem; color: #1F2937;">
745
+ ๐ŸŒŸ TRY THESE EXAMPLES! ๐ŸŒŸ
746
+ </p>
747
+ """
748
+ )
749
+
750
+ gr.Examples(
751
+ examples=examples,
752
+ inputs=[input_image],
753
+ outputs=[gallery, export_file, export_zip_file, info_log],
754
+ fn=infer,
755
+ examples_per_page=14,
756
+ cache_examples=False,
757
+ run_on_click=True
758
  )
759
 
760
+ # Connect the button
761
  run_button.click(
762
  fn=infer,
763
  inputs=[
 
772
  cfg_norm,
773
  use_en_prompt,
774
  ],
775
+ outputs=[gallery, export_file, export_zip_file, info_log],
776
  )
777
 
778
  if __name__ == "__main__":