seawolf2357 commited on
Commit
d32fd30
·
verified ·
1 Parent(s): 4a310f7

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +559 -877
app.py CHANGED
@@ -1,34 +1,32 @@
1
  import os
2
- import gc
3
- import torch
4
- import gradio as gr
5
- from PIL import Image
6
- from pathlib import Path
7
- import shutil
8
  import json
9
- from huggingface_hub import hf_hub_download, HfApi, create_repo, upload_file
10
- import tempfile
11
- import multiprocessing as mp
12
- import spaces # ZeroGPU support
13
-
14
- # Training imports
15
- from peft import LoraConfig, get_peft_model
16
- from tqdm.auto import tqdm
17
  import numpy as np
 
 
18
 
19
- # Set memory allocation config BEFORE any CUDA operations
20
- os.environ['PYTORCH_CUDA_ALLOC_CONF'] = 'expandable_segments:True,garbage_collection_threshold:0.6'
21
-
22
- # Global state
23
- DEVICE = "cuda" if torch.cuda.is_available() else "cpu"
24
- DTYPE = torch.bfloat16 if torch.cuda.is_available() else torch.float32
25
 
26
- # HF Token from environment
27
- HF_TOKEN = os.environ.get("HF_TOKEN")
 
 
 
28
 
29
- # Model repo
30
- MODEL_REPO = "Tongyi-MAI/Z-Image-Turbo"
 
 
 
31
 
 
 
32
 
33
  # ============================================
34
  # Comic Style CSS
@@ -202,15 +200,6 @@ textarea:focus, input[type="text"]:focus {
202
  color: #1F2937 !important;
203
  }
204
 
205
- .result-box textarea {
206
- background: #1F2937 !important;
207
- color: #10B981 !important;
208
- font-family: 'Courier New', monospace !important;
209
- border: 3px solid #10B981 !important;
210
- border-radius: 8px !important;
211
- box-shadow: 4px 4px 0 #10B981 !important;
212
- }
213
-
214
  label, .gr-input-label, .gr-block-label {
215
  color: #1F2937 !important;
216
  font-family: 'Comic Neue', cursive !important;
@@ -224,19 +213,6 @@ label, .gr-input-label, .gr-block-label {
224
  box-shadow: 4px 4px 0 #1F2937 !important;
225
  }
226
 
227
- .tab-nav button {
228
- font-family: 'Comic Neue', cursive !important;
229
- font-weight: 700 !important;
230
- border: 2px solid #1F2937 !important;
231
- margin: 2px !important;
232
- }
233
-
234
- .tab-nav button.selected {
235
- background: #3B82F6 !important;
236
- color: #FFF !important;
237
- box-shadow: 3px 3px 0 #1F2937 !important;
238
- }
239
-
240
  .footer-comic {
241
  text-align: center;
242
  padding: 20px;
@@ -284,11 +260,6 @@ input[type="range"] {
284
  accent-color: #3B82F6;
285
  }
286
 
287
- .gr-slider input[type="range"]::-webkit-slider-thumb {
288
- background: #EF4444 !important;
289
- border: 2px solid #1F2937 !important;
290
- }
291
-
292
  /* Image/Gallery Container */
293
  .gr-image, .gr-gallery {
294
  border: 3px solid #1F2937 !important;
@@ -296,736 +267,495 @@ input[type="range"] {
296
  box-shadow: 4px 4px 0 #1F2937 !important;
297
  }
298
 
299
- /* Quality Badge */
300
- .quality-badge {
301
- display: inline-block;
302
- background: linear-gradient(135deg, #10B981 0%, #059669 100%);
303
- color: white;
304
- padding: 4px 12px;
305
- border-radius: 15px;
306
- font-size: 0.8rem;
307
- font-weight: bold;
308
- border: 2px solid #1F2937;
309
- margin-left: 8px;
310
- }
311
-
312
- #col-container {
313
- max-width: 1200px;
314
- margin: 0 auto;
315
- }
316
-
317
- /* Hide Hugging Face elements */
318
- .huggingface-space-link,
319
- a[href*="huggingface.co/spaces"],
320
- button[class*="share"],
321
- .share-button,
322
- [class*="hf-logo"],
323
- .gr-share-btn,
324
- #hf-logo,
325
- .hf-icon,
326
- svg[class*="hf"],
327
- div[class*="huggingface"],
328
- a[class*="huggingface"],
329
- .svelte-1rjryqp,
330
- header a[href*="huggingface"],
331
- .space-header,
332
- div.absolute.right-0 a[href*="huggingface"],
333
- .gr-group > a[href*="huggingface"],
334
- a[target="_blank"][href*="huggingface.co"] {
335
- display: none !important;
336
- visibility: hidden !important;
337
- opacity: 0 !important;
338
- pointer-events: none !important;
339
- width: 0 !important;
340
- height: 0 !important;
341
- overflow: hidden !important;
342
- }
343
-
344
- /* Training specific styles */
345
- .training-section {
346
- background: linear-gradient(135deg, #E0F2FE 0%, #DBEAFE 100%) !important;
347
- border: 3px solid #1F2937 !important;
348
- border-radius: 12px !important;
349
- padding: 15px !important;
350
- margin: 10px 0 !important;
351
- box-shadow: 4px 4px 0 #1F2937 !important;
352
- }
353
-
354
- .tips-box {
355
- background: linear-gradient(135deg, #D1FAE5 0%, #A7F3D0 100%) !important;
356
- border: 3px solid #1F2937 !important;
357
- border-radius: 8px !important;
358
- padding: 12px 15px !important;
359
- margin: 10px 0 !important;
360
- box-shadow: 4px 4px 0 #1F2937 !important;
361
- font-family: 'Comic Neue', cursive !important;
362
- font-weight: 700 !important;
363
- color: #1F2937 !important;
364
- }
365
  """
366
 
367
-
368
- def aggressive_cleanup():
369
- """Aggressively clean up GPU memory"""
370
- gc.collect()
371
- gc.collect()
372
- if torch.cuda.is_available():
373
- torch.cuda.empty_cache()
374
- torch.cuda.synchronize()
375
- torch.cuda.reset_peak_memory_stats()
376
- torch.cuda.reset_accumulated_memory_stats()
377
-
378
-
379
- def get_gpu_memory_info():
380
- """Get current GPU memory status"""
381
- if torch.cuda.is_available():
382
- allocated = torch.cuda.memory_allocated(0) / 1e9
383
- reserved = torch.cuda.memory_reserved(0) / 1e9
384
- total = torch.cuda.get_device_properties(0).total_memory / 1e9
385
- free = total - allocated
386
- return f"GPU: {allocated:.1f}GB allocated, {reserved:.1f}GB reserved, {free:.1f}GB free of {total:.1f}GB"
387
- return "No GPU"
388
-
389
-
390
- def check_gpu():
391
- """Check GPU availability and memory"""
392
- if torch.cuda.is_available():
393
- gpu_name = torch.cuda.get_device_name(0)
394
- gpu_mem = torch.cuda.get_device_properties(0).total_memory / 1e9
395
- return f"✅ GPU: {gpu_name} ({gpu_mem:.1f}GB total)"
396
- return "❌ No GPU detected"
397
-
398
-
399
- def check_hf_token():
400
- """Check if HF_TOKEN is configured"""
401
- if HF_TOKEN:
402
- try:
403
- api = HfApi(token=HF_TOKEN)
404
- user_info = api.whoami()
405
- return f"✅ Logged in as: {user_info['name']}"
406
- except Exception as e:
407
- return f"⚠️ Token invalid: {str(e)}"
408
- return "❌ HF_TOKEN not set"
409
-
410
-
411
- def get_hf_username():
412
- if HF_TOKEN:
413
- try:
414
- api = HfApi(token=HF_TOKEN)
415
- return api.whoami()['name']
416
- except:
417
- return None
418
- return None
419
-
420
-
421
- # ============================================
422
- # SUBPROCESS-BASED CAPTIONING
423
- # ============================================
424
-
425
- def _caption_worker(image_paths_queue, results_queue, trigger_word, is_person):
426
- """Worker process for Florence-2 captioning - completely isolated GPU context"""
427
- import torch
428
- from PIL import Image
429
-
430
- os.environ['PYTORCH_CUDA_ALLOC_CONF'] = 'expandable_segments:True'
431
- device = "cuda" if torch.cuda.is_available() else "cpu"
432
-
433
- try:
434
- from transformers import AutoProcessor, AutoModelForCausalLM
435
 
436
- print("[Caption Worker] Loading Florence-2-large...")
437
- processor = AutoProcessor.from_pretrained(
438
- "microsoft/Florence-2-large",
439
- trust_remote_code=True
440
- )
441
- # FIX: Add attn_implementation="eager" to avoid _supports_sdpa error
442
- model = AutoModelForCausalLM.from_pretrained(
443
- "microsoft/Florence-2-large",
444
- torch_dtype=torch.float16,
445
- trust_remote_code=True,
446
- attn_implementation="eager" # Disable SDPA to avoid attribute error
447
- ).to(device)
448
- model.eval()
449
- print("[Caption Worker] Florence-2 loaded!")
450
-
451
- image_paths = image_paths_queue.get()
452
- captions = []
453
-
454
- for idx, img_path in enumerate(image_paths):
455
- try:
456
- img = Image.open(img_path).convert("RGB")
457
- task = "<DETAILED_CAPTION>"
458
- inputs = processor(text=task, images=img, return_tensors="pt")
459
- inputs = {k: v.to(device) for k, v in inputs.items()}
460
-
461
- with torch.no_grad():
462
- generated_ids = model.generate(
463
- **inputs, max_new_tokens=256, num_beams=3, do_sample=False
464
- )
465
-
466
- generated_text = processor.batch_decode(generated_ids, skip_special_tokens=False)[0]
467
- parsed = processor.post_process_generation(
468
- generated_text, task=task, image_size=(img.width, img.height)
469
- )
470
-
471
- raw_caption = parsed.get(task, "")
472
-
473
- if raw_caption:
474
- caption = raw_caption.strip()
475
- if is_person:
476
- replacements = [
477
- "a young woman", "a woman", "the woman", "a young man", "a man", "the man",
478
- "a person", "the person", "a young person", "an individual",
479
- "a girl", "the girl", "a boy", "the boy",
480
- "a lady", "the lady", "a gentleman", "the gentleman",
481
- "someone", "a figure", "the figure"
482
- ]
483
- caption_lower = caption.lower()
484
- replaced = False
485
- for ref in replacements:
486
- if ref in caption_lower:
487
- import re
488
- pattern = re.compile(re.escape(ref), re.IGNORECASE)
489
- caption = pattern.sub(trigger_word, caption, count=1)
490
- replaced = True
491
- break
492
- if not replaced:
493
- caption = f"{trigger_word}, {caption}"
494
- else:
495
- caption = f"{trigger_word}, {caption}"
496
- else:
497
- caption = trigger_word
498
-
499
- captions.append(caption)
500
- print(f"[Caption Worker] [{idx+1}/{len(image_paths)}] {caption[:80]}...")
501
-
502
- del inputs, generated_ids, img
503
- torch.cuda.empty_cache()
504
-
505
- except Exception as e:
506
- print(f"[Caption Worker] Error on image {idx}: {e}")
507
- captions.append(trigger_word)
508
-
509
- results_queue.put(captions)
510
- del model, processor
511
- torch.cuda.empty_cache()
512
-
513
- except Exception as e:
514
- print(f"[Caption Worker] Fatal error: {e}")
515
- import traceback
516
- traceback.print_exc()
517
- # Return trigger word as fallback - need to get image_paths first
518
- try:
519
- image_paths = image_paths_queue.get(timeout=1)
520
- results_queue.put([trigger_word] * len(image_paths))
521
- except:
522
- results_queue.put([trigger_word] * 20)
523
-
524
-
525
- def run_captioning_subprocess(image_paths, trigger_word, is_person=True):
526
- """Run captioning in subprocess for complete GPU memory isolation"""
527
- print(f"[Main] Starting captioning subprocess...")
528
- print(f"[Main] Before: {get_gpu_memory_info()}")
529
-
530
- ctx = mp.get_context('spawn')
531
- image_paths_queue = ctx.Queue()
532
- results_queue = ctx.Queue()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
533
 
534
- worker = ctx.Process(
535
- target=_caption_worker,
536
- args=(image_paths_queue, results_queue, trigger_word, is_person)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
537
  )
538
- worker.start()
539
- image_paths_queue.put(image_paths)
540
-
541
- try:
542
- captions = results_queue.get(timeout=900) # 15 min timeout
543
- except Exception as e:
544
- print(f"[Main] Captioning error: {e}")
545
- captions = [trigger_word] * len(image_paths)
546
-
547
- worker.join(timeout=30)
548
- if worker.is_alive():
549
- worker.terminate()
550
- worker.join()
551
-
552
- print(f"[Main] After captioning: {get_gpu_memory_info()}")
553
- return captions
554
-
555
 
556
- def prepare_dataset(images, trigger_word, output_dir, use_auto_caption=True, is_person=True):
557
- """Prepare dataset with subprocess captioning"""
558
- dataset_dir = Path(output_dir) / "dataset"
559
- dataset_dir.mkdir(parents=True, exist_ok=True)
560
-
561
- image_paths = []
562
 
563
- for i, img in enumerate(images):
564
- if img is None:
565
- continue
566
- if isinstance(img, tuple):
567
- img = img[0]
568
- if isinstance(img, str):
569
- img_pil = Image.open(img)
570
- elif isinstance(img, np.ndarray):
571
- img_pil = Image.fromarray(img)
572
- elif hasattr(img, 'mode'):
573
- img_pil = img
 
 
 
 
574
  else:
575
- continue
576
 
577
- if img_pil.mode != "RGB":
578
- img_pil = img_pil.convert("RGB")
579
-
580
- img_path = dataset_dir / f"image_{i:04d}.jpg"
581
- img_pil.save(img_path, quality=95)
582
- image_paths.append(str(img_path))
583
-
584
- if use_auto_caption:
585
- captions = run_captioning_subprocess(image_paths, trigger_word, is_person)
 
 
 
 
 
 
586
  else:
587
- captions = [trigger_word] * len(image_paths)
588
-
589
- for img_path, caption in zip(image_paths, captions):
590
- caption_path = Path(img_path).with_suffix('.txt')
591
- caption_path.write_text(caption)
 
 
592
 
593
- return image_paths, captions, str(dataset_dir)
594
-
595
-
596
- def compute_flow_matching_loss(model_output, target, timesteps):
597
- """Compute Rectified Flow loss"""
598
- loss = torch.nn.functional.mse_loss(model_output, target, reduction="none")
599
- loss = loss.mean(dim=list(range(1, len(loss.shape))))
600
- return loss.mean()
601
 
602
-
603
- def upload_to_hub(lora_path, repo_name, trigger_word, training_info, progress_callback=None):
604
- """Upload to HF Hub"""
605
- if not HF_TOKEN:
606
- return False, "HF_TOKEN not configured"
607
 
608
- try:
609
- api = HfApi(token=HF_TOKEN)
610
- username = get_hf_username()
611
- if not username:
612
- return False, "Could not get username"
613
-
614
- repo_id = f"{username}/{repo_name}"
615
-
616
- try:
617
- create_repo(repo_id=repo_id, token=HF_TOKEN, private=True, repo_type="model", exist_ok=True)
618
- except:
619
- pass
620
 
621
- api.upload_file(
622
- path_or_fileobj=lora_path,
623
- path_in_repo=f"{repo_name}.safetensors",
624
- repo_id=repo_id,
625
- token=HF_TOKEN
626
- )
 
 
627
 
628
- readme = f"""---
629
- license: apache-2.0
630
- base_model: Tongyi-MAI/Z-Image-Turbo
631
- tags: [lora, z-image, text-to-image, diffusers]
632
- ---
633
- # {repo_name}
634
- Trigger: `{trigger_word}`
635
- {training_info}
636
- """
637
- api.upload_file(path_or_fileobj=readme.encode(), path_in_repo="README.md", repo_id=repo_id, token=HF_TOKEN)
638
-
639
- return True, f"https://huggingface.co/{repo_id}"
640
- except Exception as e:
641
- return False, str(e)
642
-
643
-
644
- @spaces.GPU(duration=600) # 10 minutes max for training
645
- def train_lora(
646
- images, trigger_word, output_name, num_steps, learning_rate, lora_rank,
647
- resolution, batch_size, upload_to_hub_flag, hub_repo_name,
648
- use_auto_caption, is_person_training, progress=gr.Progress()
649
- ):
650
- """Train LoRA with CORRECT ZImageTransformer2DModel forward signature"""
651
-
652
- if not torch.cuda.is_available():
653
- return None, "❌ No GPU available"
654
-
655
- if not images or len(images) < 3:
656
- return None, "❌ Please upload at least 3 images"
657
-
658
- if not trigger_word:
659
- return None, "❌ Please specify a trigger word"
660
-
661
- if not output_name:
662
- output_name = "z_image_lora"
663
- output_name = output_name.replace(" ", "_").lower()
664
-
665
- if upload_to_hub_flag and not HF_TOKEN:
666
- return None, "❌ HF_TOKEN not configured"
667
-
668
- progress(0, desc="Initializing...")
669
- print(f"[Train] Start: {get_gpu_memory_info()}")
670
- aggressive_cleanup()
671
-
672
- with tempfile.TemporaryDirectory() as tmpdir:
673
- try:
674
- # ============================================
675
- # PHASE 1: Captioning (Subprocess)
676
- # ============================================
677
- progress(0.02, desc="Running Florence-2 captioning (subprocess)...")
678
-
679
- image_paths, captions, dataset_dir = prepare_dataset(
680
- images, trigger_word, tmpdir, use_auto_caption, is_person_training
681
- )
682
-
683
- if len(image_paths) < 3:
684
- return None, "❌ Not enough valid images"
685
-
686
- progress(0.12, desc=f"Captioning done: {len(image_paths)} images")
687
- aggressive_cleanup()
688
- print(f"[Train] After captioning cleanup: {get_gpu_memory_info()}")
689
-
690
- # ============================================
691
- # PHASE 2: Load Pipeline for Text Encoding
692
- # ============================================
693
- progress(0.15, desc="Loading pipeline for encoding...")
694
- print(f"[Train] Before pipeline: {get_gpu_memory_info()}")
695
-
696
- from diffusers import ZImagePipeline
697
-
698
- # Load pipeline to CPU first
699
- pipe = ZImagePipeline.from_pretrained(
700
- MODEL_REPO,
701
- torch_dtype=DTYPE,
702
- )
703
-
704
- # Get VAE scaling factor
705
- vae_scaling_factor = pipe.vae.config.scaling_factor
706
-
707
- # ============================================
708
- # PHASE 3: Encode Captions with Text Encoder
709
- # ============================================
710
- progress(0.20, desc="Encoding captions...")
711
-
712
- # Move text encoder to GPU
713
- pipe.text_encoder.to(DEVICE)
714
-
715
- cached_text_embeddings = []
716
-
717
- with torch.no_grad():
718
- for idx, caption in enumerate(captions):
719
- text_inputs = pipe.tokenizer(
720
- caption,
721
- padding="max_length",
722
- max_length=256,
723
- truncation=True,
724
- return_tensors="pt"
725
- ).to(DEVICE)
726
-
727
- text_emb = pipe.text_encoder(**text_inputs)[0]
728
- cached_text_embeddings.append(text_emb.cpu())
729
-
730
- del text_inputs, text_emb
731
-
732
- if idx % 2 == 0:
733
- torch.cuda.empty_cache()
734
- progress(0.20 + 0.10 * (idx / len(captions)),
735
- desc=f"Encoding caption {idx+1}/{len(captions)}")
736
-
737
- # Free text encoder
738
- pipe.text_encoder.to("cpu")
739
- del pipe.text_encoder
740
- aggressive_cleanup()
741
- print(f"[Train] After text encoding: {get_gpu_memory_info()}")
742
-
743
- # ============================================
744
- # PHASE 4: Encode Images with VAE
745
- # ============================================
746
- progress(0.32, desc="Encoding images with VAE...")
747
-
748
- pipe.vae.to(DEVICE)
749
-
750
- cached_latents = []
751
-
752
- with torch.no_grad():
753
- for idx, img_path in enumerate(image_paths):
754
- img = Image.open(img_path).convert("RGB")
755
- img = img.resize((int(resolution), int(resolution)), Image.LANCZOS)
756
- img_tensor = torch.from_numpy(np.array(img)).permute(2, 0, 1).float() / 255.0
757
- img_tensor = img_tensor.unsqueeze(0).to(DEVICE, dtype=DTYPE)
758
- img_tensor = 2.0 * img_tensor - 1.0
759
-
760
- latent = pipe.vae.encode(img_tensor).latent_dist.sample()
761
- latent = latent * vae_scaling_factor
762
- cached_latents.append(latent.cpu())
763
-
764
- del img_tensor, latent, img
765
-
766
- if idx % 2 == 0:
767
- torch.cuda.empty_cache()
768
- progress(0.32 + 0.08 * (idx / len(image_paths)),
769
- desc=f"Encoding image {idx+1}/{len(image_paths)}")
770
-
771
- # Free VAE
772
- pipe.vae.to("cpu")
773
- del pipe.vae
774
- aggressive_cleanup()
775
- print(f"[Train] After VAE encoding: {get_gpu_memory_info()}")
776
-
777
- # ============================================
778
- # PHASE 5: Setup Transformer with Training Adapter
779
- # ============================================
780
- progress(0.42, desc="Setting up transformer with training adapter...")
781
-
782
- # Download training adapter
783
- try:
784
- adapter_path = hf_hub_download(
785
- repo_id="ostris/zimage_turbo_training_adapter",
786
- filename="zimage_turbo_training_adapter_v1.safetensors",
787
- local_dir=tmpdir
788
- )
789
- print(f"[Train] Training adapter downloaded: {adapter_path}")
790
- except Exception as e:
791
- return None, f"❌ Could not download training adapter: {e}"
792
-
793
- # Get transformer (still on CPU from pipeline)
794
- transformer = pipe.transformer
795
-
796
- # Load adapter via pipe's load_lora_weights
797
- from safetensors.torch import load_file, save_file
798
-
799
  try:
800
- pipe.load_lora_weights(adapter_path, adapter_name="training_adapter")
801
- print("[Train] Training adapter loaded via load_lora_weights")
 
 
 
 
 
802
  except Exception as e:
803
- print(f"[Train] Warning: Could not load adapter via load_lora_weights: {e}")
804
-
805
- # Configure our training LoRA
806
- progress(0.45, desc="Configuring LoRA...")
807
-
808
- lora_config = LoraConfig(
809
- r=int(lora_rank),
810
- lora_alpha=int(lora_rank),
811
- init_lora_weights="gaussian",
812
- target_modules=[
813
- "to_q", "to_k", "to_v", "to_out.0",
814
- "attn.to_q", "attn.to_k", "attn.to_v", "attn.to_out.0",
815
- "ff.net.0.proj", "ff.net.2",
816
- "proj_in", "proj_out",
817
- ],
818
- lora_dropout=0.0,
819
- )
820
-
821
- transformer = get_peft_model(transformer, lora_config)
822
- trainable = sum(p.numel() for p in transformer.parameters() if p.requires_grad)
823
- total = sum(p.numel() for p in transformer.parameters())
824
- print(f"[Train] Trainable: {trainable:,} / {total:,} ({100*trainable/total:.2f}%)")
825
-
826
- # Move to GPU
827
- transformer.to(DEVICE)
828
- print(f"[Train] Transformer on GPU: {get_gpu_memory_info()}")
829
-
830
- if hasattr(transformer, 'enable_gradient_checkpointing'):
831
- transformer.enable_gradient_checkpointing()
832
-
833
- # Free the rest of pipeline
834
- del pipe
835
- aggressive_cleanup()
836
-
837
- # Optimizer & Scheduler
838
- optimizer = torch.optim.AdamW(
839
- [p for p in transformer.parameters() if p.requires_grad],
840
- lr=learning_rate, weight_decay=0.01, betas=(0.9, 0.999), eps=1e-8
841
- )
842
-
843
- from torch.optim.lr_scheduler import CosineAnnealingWarmRestarts, LinearLR, SequentialLR
844
-
845
- warmup_steps = min(100, int(num_steps * 0.1))
846
- warmup_scheduler = LinearLR(optimizer, start_factor=0.1, total_iters=warmup_steps)
847
- cosine_scheduler = CosineAnnealingWarmRestarts(
848
- optimizer, T_0=max(1, int(num_steps - warmup_steps)), eta_min=learning_rate * 0.01
849
- )
850
- lr_scheduler = SequentialLR(
851
- optimizer, [warmup_scheduler, cosine_scheduler], milestones=[warmup_steps]
852
- )
853
-
854
- progress(0.50, desc=f"Training with {len(cached_latents)} samples...")
855
-
856
- # ============================================
857
- # PHASE 6: Training Loop with CORRECT forward signature
858
- # ZImageTransformer2DModel.forward(x, t, cap_feats, ...)
859
- # x: List[Tensor] where each tensor is [C, F, H, W]
860
- # cap_feats: List[Tensor] where each tensor is [seq_len, dim]
861
- # ============================================
862
- transformer.train()
863
- losses = []
864
- successful_steps = 0
865
-
866
- for step in range(int(num_steps)):
867
- try:
868
- idx = np.random.randint(0, len(cached_latents))
869
-
870
- # latents: [B, C, H, W] -> need [C, F, H, W] where F=1
871
- latents = cached_latents[idx].to(DEVICE, dtype=DTYPE)
872
- # Remove batch dim, add frame dim: [1, C, H, W] -> [C, 1, H, W]
873
- latents = latents.squeeze(0).unsqueeze(1) # [C, 1, H, W]
874
-
875
- # text_embeddings: [B, seq_len, dim] -> [seq_len, dim]
876
- text_embeddings = cached_text_embeddings[idx].to(DEVICE, dtype=DTYPE)
877
- text_embeddings = text_embeddings.squeeze(0) # [seq_len, dim]
878
-
879
- # Timestep for flow matching (0 to 1)
880
- timesteps = torch.rand(1, device=DEVICE, dtype=DTYPE)
881
-
882
- # Create noisy latents using flow matching interpolation
883
- noise = torch.randn_like(latents)
884
- t = timesteps.view(-1, 1, 1, 1)
885
- noisy_latents = (1 - t) * latents + t * noise
886
-
887
- # Target is the velocity: noise - clean
888
- target = noise - latents
889
-
890
- # Scale timestep for model
891
- t_input = timesteps * 1000
892
-
893
- # CORRECT FORWARD CALL:
894
- # x and cap_feats must be Lists!
895
- with torch.amp.autocast('cuda', dtype=DTYPE):
896
- output = transformer(
897
- x=[noisy_latents], # List of [C, F, H, W]
898
- t=t_input, # timestep
899
- cap_feats=[text_embeddings], # List of [seq_len, dim]
900
- return_dict=True
901
- )
902
-
903
- # Get model output - it will also be a list
904
- if hasattr(output, 'sample'):
905
- model_output = output.sample
906
- if isinstance(model_output, list):
907
- model_output = model_output[0]
908
- elif isinstance(output, tuple):
909
- model_output = output[0]
910
- if isinstance(model_output, list):
911
- model_output = model_output[0]
912
- else:
913
- model_output = output
914
- if isinstance(model_output, list):
915
- model_output = model_output[0]
916
-
917
- loss = compute_flow_matching_loss(model_output, target, timesteps)
918
-
919
- optimizer.zero_grad()
920
- loss.backward()
921
- torch.nn.utils.clip_grad_norm_(transformer.parameters(), 1.0)
922
- optimizer.step()
923
- lr_scheduler.step()
924
-
925
- losses.append(loss.item())
926
- successful_steps += 1
927
-
928
- del latents, text_embeddings, noise, noisy_latents, target, model_output, loss, output
929
-
930
- if step % 25 == 0:
931
- avg_loss = np.mean(losses[-50:]) if len(losses) >= 50 else np.mean(losses) if losses else float('nan')
932
- progress(
933
- 0.50 + 0.40 * (step / int(num_steps)),
934
- desc=f"Step {step}/{int(num_steps)} | Loss: {avg_loss:.4f}"
935
- )
936
- print(f"[Train] Step {step}: Loss={avg_loss:.4f}")
937
-
938
- if step % 100 == 0:
939
- gc.collect()
940
- torch.cuda.empty_cache()
941
-
942
- except Exception as e:
943
- if step < 5:
944
- print(f"[Train] Error at step {step}: {e}")
945
- import traceback
946
- traceback.print_exc()
947
- continue
948
-
949
- if successful_steps == 0:
950
- return None, "❌ Training failed - no successful steps. Check model forward signature."
951
-
952
- # ============================================
953
- # PHASE 7: Save LoRA
954
- # ============================================
955
- progress(0.92, desc="Saving LoRA...")
956
-
957
- del cached_latents, cached_text_embeddings
958
- aggressive_cleanup()
959
-
960
- lora_state_dict = {}
961
- for name, param in transformer.named_parameters():
962
- if "lora" in name.lower() and param.requires_grad:
963
- clean_name = name.replace("base_model.model.", "")
964
- lora_state_dict[clean_name] = param.detach().cpu()
965
-
966
- if not lora_state_dict:
967
- return None, "❌ No LoRA weights found"
968
-
969
- final_output = f"/tmp/{output_name}.safetensors"
970
- save_file(lora_state_dict, final_output)
971
-
972
- file_size = os.path.getsize(final_output) / (1024 * 1024)
973
- avg_final_loss = np.mean(losses[-100:]) if len(losses) >= 100 else np.mean(losses) if losses else float('nan')
974
-
975
- training_info = f"""
976
- - Images: {len(image_paths)}
977
- - Steps: {successful_steps}
978
- - Final Loss: {avg_final_loss:.4f}
979
- - LR: {learning_rate}, Rank: {int(lora_rank)}, Resolution: {int(resolution)}
980
- """
981
-
982
- hub_result = ""
983
- if upload_to_hub_flag:
984
- progress(0.94, desc="Uploading to Hub...")
985
- success, result = upload_to_hub(
986
- final_output, hub_repo_name or output_name, trigger_word, training_info
987
- )
988
- hub_result = f"\n\n🚀 Uploaded: {result}" if success else f"\n\n⚠️ Upload failed: {result}"
989
-
990
- del transformer
991
- aggressive_cleanup()
992
- progress(1.0, desc="Complete!")
993
-
994
- sample_captions = "\n".join([f" - {c[:80]}..." for c in captions[:3]])
995
-
996
- return final_output, f"""✅ Training complete!
997
-
998
- 📁 LoRA: {output_name}.safetensors ({file_size:.1f} MB)
999
- 🏷️ Trigger: {trigger_word}
1000
- 📊 Loss: {avg_final_loss:.4f}
1001
- 🖼️ Images: {len(image_paths)}
1002
- ⚙️ Steps: {successful_steps}
1003
-
1004
- **Sample captions:**
1005
- {sample_captions}{hub_result}
1006
-
1007
- **Usage:**
1008
- ```python
1009
- from diffusers import ZImagePipeline
1010
- import torch
1011
-
1012
- pipe = ZImagePipeline.from_pretrained("Tongyi-MAI/Z-Image-Turbo", torch_dtype=torch.bfloat16)
1013
- pipe.load_lora_weights("{output_name}.safetensors")
1014
- pipe.to("cuda")
1015
-
1016
- image = pipe("{trigger_word}, your prompt here", num_inference_steps=8, guidance_scale=0.0).images[0]
1017
- ```"""
1018
-
1019
  except Exception as e:
1020
- aggressive_cleanup()
1021
- import traceback
1022
- return None, f"❌ Error: {str(e)}\n\n{traceback.format_exc()}"
 
1023
 
 
 
1024
 
1025
- # ============================================
1026
- # Gradio UI with Comic Style
1027
- # ============================================
1028
- with gr.Blocks(title="Z-IMAGE GEN/LORA") as demo:
1029
 
1030
  # HOME Button
1031
  gr.HTML("""
@@ -1041,138 +771,90 @@ with gr.Blocks(title="Z-IMAGE GEN/LORA") as demo:
1041
  gr.HTML("""
1042
  <div class="header-container">
1043
  <div class="header-title">🎨 Z-IMAGE GEN/LORA 🎨</div>
1044
- <div class="header-subtitle">Train custom LoRA for Z-Image Turbo with Florence-2 auto-captioning</div>
1045
  <div style="margin-top:12px">
1046
- <span class="stats-badge">🧠 Florence-2 Caption</span>
1047
- <span class="stats-badge">⚡ Memory Optimized</span>
1048
- <span class="stats-badge">🚀 Hub Upload</span>
1049
- <span class="stats-badge">🎯 Person/Style/Object</span>
1050
  </div>
1051
  </div>
1052
  """)
1053
-
1054
- # Status Row
1055
- gr.HTML('<div class="info-box">📊 <b>System Status</b> - Check GPU and HuggingFace connection</div>')
1056
 
 
1057
  with gr.Row():
1058
- with gr.Column(scale=1):
1059
- gpu_status = gr.Textbox(label="🖥️ GPU Status", value=check_gpu(), interactive=False)
1060
- with gr.Column(scale=1):
1061
- hf_status = gr.Textbox(label="🔑 HF Token", value=check_hf_token(), interactive=False)
1062
- refresh_btn = gr.Button("🔄 Refresh", size="sm")
1063
- refresh_btn.click(fn=lambda: (check_gpu(), check_hf_token()), outputs=[gpu_status, hf_status])
1064
-
1065
  with gr.Row():
1066
- # Left Column - Images
1067
- with gr.Column(scale=1):
1068
- gr.HTML('<div class="info-box">📸 <b>Training Images</b> - Upload 6-20 high-quality images</div>')
1069
- images = gr.Gallery(
1070
- label="Upload Images",
1071
- columns=4,
1072
- height=300,
1073
- type="filepath",
1074
- interactive=True
1075
  )
1076
- gr.HTML('<div class="tips-box">💡 <b>Tips:</b> 6-20 images, varied poses/angles, consistent subject, good lighting</div>')
 
 
 
 
 
 
 
1077
 
1078
- # Right Column - Settings
1079
- with gr.Column(scale=1):
1080
- gr.HTML('<div class="info-box">⚙️ <b>Training Settings</b> - Configure your LoRA training</div>')
1081
-
1082
- trigger_word = gr.Textbox(
1083
- label="🏷️ Trigger Word",
1084
- placeholder="ohwx_person",
1085
- info="Use unique token like 'ohwx' to avoid conflicts"
1086
- )
1087
- output_name = gr.Textbox(
1088
- label="📁 Output Name",
1089
- placeholder="my_lora"
1090
- )
1091
-
1092
- with gr.Row():
1093
- use_auto_caption = gr.Checkbox(label="🔤 Auto-Caption (Florence-2)", value=True)
1094
- is_person_training = gr.Checkbox(label="👤 Person/Face Training", value=True)
1095
-
1096
- with gr.Row():
1097
- num_steps = gr.Slider(500, 5000, 1500, step=100, label="🔢 Steps")
1098
- learning_rate = gr.Slider(1e-5, 5e-4, 5e-5, step=1e-5, label="📈 Learning Rate")
1099
-
1100
- with gr.Row():
1101
- lora_rank = gr.Slider(4, 64, 32, step=4, label="🎚️ LoRA Rank")
1102
- resolution = gr.Slider(512, 1024, 1024, step=128, label="📐 Resolution")
1103
-
1104
- batch_size = gr.Slider(1, 4, 1, step=1, visible=False)
1105
-
1106
- gr.HTML('<div class="info-box">🚀 <b>Hub Upload</b> - Upload trained LoRA to HuggingFace</div>')
1107
-
1108
- with gr.Row():
1109
- upload_to_hub_flag = gr.Checkbox(label="📤 Upload to HF Hub (Private)", value=False)
1110
- hub_repo_name = gr.Textbox(label="📦 Repo Name", placeholder="my-zimage-lora")
1111
-
1112
- # Train Button
1113
- with gr.Row():
1114
- train_btn = gr.Button("🚀 START TRAINING!", variant="primary", size="lg")
1115
-
1116
- # Output
1117
  with gr.Row():
1118
- with gr.Column(scale=1):
1119
- output_file = gr.File(label="📥 Download LoRA")
1120
- with gr.Column(scale=1):
1121
- output_log = gr.Textbox(label="📋 Training Log", lines=15)
1122
-
1123
- train_btn.click(
1124
- fn=train_lora,
1125
- inputs=[
1126
- images, trigger_word, output_name, num_steps, learning_rate, lora_rank,
1127
- resolution, batch_size, upload_to_hub_flag, hub_repo_name,
1128
- use_auto_caption, is_person_training
1129
- ],
1130
- outputs=[output_file, output_log]
1131
- )
1132
-
1133
- # Recommended Settings Table
1134
- gr.HTML("""
1135
- <div class="info-box">
1136
- 📋 <b>Recommended Settings by Use Case</b>
1137
- <table style="width:100%; margin-top:10px; border-collapse: collapse;">
1138
- <tr style="background:#3B82F6; color:white;">
1139
- <th style="padding:8px; border:2px solid #1F2937;">Use Case</th>
1140
- <th style="padding:8px; border:2px solid #1F2937;">Steps</th>
1141
- <th style="padding:8px; border:2px solid #1F2937;">LR</th>
1142
- <th style="padding:8px; border:2px solid #1F2937;">Rank</th>
1143
- </tr>
1144
- <tr style="background:#FEF9C3;">
1145
- <td style="padding:8px; border:2px solid #1F2937;">👤 Person</td>
1146
- <td style="padding:8px; border:2px solid #1F2937;">1500</td>
1147
- <td style="padding:8px; border:2px solid #1F2937;">5e-5</td>
1148
- <td style="padding:8px; border:2px solid #1F2937;">32</td>
1149
- </tr>
1150
- <tr style="background:#FFF;">
1151
- <td style="padding:8px; border:2px solid #1F2937;">🎨 Style</td>
1152
- <td style="padding:8px; border:2px solid #1F2937;">2000</td>
1153
- <td style="padding:8px; border:2px solid #1F2937;">1e-4</td>
1154
- <td style="padding:8px; border:2px solid #1F2937;">16</td>
1155
- </tr>
1156
- <tr style="background:#FEF9C3;">
1157
- <td style="padding:8px; border:2px solid #1F2937;">📦 Object</td>
1158
- <td style="padding:8px; border:2px solid #1F2937;">1200</td>
1159
- <td style="padding:8px; border:2px solid #1F2937;">8e-5</td>
1160
- <td style="padding:8px; border:2px solid #1F2937;">24</td>
1161
- </tr>
1162
- </table>
1163
- </div>
1164
- """)
1165
 
1166
  # Footer
1167
  gr.HTML("""
1168
  <div class="footer-comic">
1169
  <p style="font-family:'Bangers',cursive;font-size:1.5rem;letter-spacing:2px">🎨 Z-IMAGE GEN/LORA 🎨</p>
1170
- <p>Powered by Z-Image Turbo + Florence-2 + PEFT</p>
1171
- <p>🧠 Auto Caption Memory Optimized🚀 Fast Training • 📤 Hub Upload</p>
1172
  <p style="margin-top:10px"><a href="https://www.ginigen.com" target="_blank" style="color:#FACC15;text-decoration:none;font-weight:bold;">🏠 www.ginigen.com</a></p>
1173
  </div>
1174
  """)
1175
 
1176
- if __name__ == "__main__":
1177
- mp.set_start_method('spawn', force=True)
1178
- demo.launch(css=COMIC_CSS, theme=gr.themes.Soft(), ssr_mode=False)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
  import os
 
 
 
 
 
 
2
  import json
3
+ import copy
4
+ import time
5
+ import requests
6
+ import random
7
+ import logging
 
 
 
8
  import numpy as np
9
+ import spaces
10
+ from typing import Any, Dict, List, Optional, Union
11
 
12
+ import torch
13
+ from PIL import Image
14
+ import gradio as gr
 
 
 
15
 
16
+ from diffusers import (
17
+ DiffusionPipeline,
18
+ AutoencoderKL,
19
+ ZImagePipeline
20
+ )
21
 
22
+ from huggingface_hub import (
23
+ hf_hub_download,
24
+ HfFileSystem,
25
+ ModelCard,
26
+ snapshot_download)
27
 
28
+ from diffusers.utils import load_image
29
+ from typing import Iterable
30
 
31
  # ============================================
32
  # Comic Style CSS
 
200
  color: #1F2937 !important;
201
  }
202
 
 
 
 
 
 
 
 
 
 
203
  label, .gr-input-label, .gr-block-label {
204
  color: #1F2937 !important;
205
  font-family: 'Comic Neue', cursive !important;
 
213
  box-shadow: 4px 4px 0 #1F2937 !important;
214
  }
215
 
 
 
 
 
 
 
 
 
 
 
 
 
 
216
  .footer-comic {
217
  text-align: center;
218
  padding: 20px;
 
260
  accent-color: #3B82F6;
261
  }
262
 
 
 
 
 
 
263
  /* Image/Gallery Container */
264
  .gr-image, .gr-gallery {
265
  border: 3px solid #1F2937 !important;
 
267
  box-shadow: 4px 4px 0 #1F2937 !important;
268
  }
269
 
270
+ /* Original CSS additions */
271
+ #gen_btn{height: 100%}
272
+ #gen_column{align-self: stretch}
273
+ #title{text-align: center}
274
+ #title h1{font-size: 3em; display:inline-flex; align-items:center}
275
+ #title img{width: 100px; margin-right: 0.5em}
276
+ #gallery .grid-wrap{height: 10vh}
277
+ #lora_list{background: var(--block-background-fill);padding: 0 1em .3em; font-size: 90%}
278
+ .card_internal{display: flex;height: 100px;margin-top: .5em}
279
+ .card_internal img{margin-right: 1em}
280
+ .styler{--form-gap-width: 0px !important}
281
+ #progress{height:30px}
282
+ #progress .generating{display:none}
283
+ .progress-container {width: 100%;height: 30px;background-color: #f0f0f0;border-radius: 15px;overflow: hidden;margin-bottom: 20px}
284
+ .progress-bar {height: 100%;background-color: #4f46e5;width: calc(var(--current) / var(--total) * 100%);transition: width 0.5s ease-in-out}
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
285
  """
286
 
287
+ loras = [
288
+ # 로컬 jimin LoRA (app.py와 같은 디렉토리에 jimin.safetensors 필요)
289
+ {
290
+ "image": "https://i.namu.wiki/i/umL8EZtn0hs-nMRYeFxIrkGrMe-R1u5c9fJE8ufrLjvXz52VcSIbG7TT9QJoL2rR7vsFww1lLrE4bwfn5uOBzfq9a90HGdNdlTLmr_KoqOchTovbVC3RDzhDbp7FI-Wq-esCu7_BYIptqethL4onBg.webp",
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
291
 
292
+ "title": "jimin Style",
293
+ "repo": "./", # 로컬 경로
294
+ "weights": "jimin.safetensors",
295
+ "trigger_word": "jimin"
296
+ },
297
+ {
298
+ "image": "https://huggingface.co/strangerzonehf/Flux-Ultimate-LoRA-Collection/resolve/main/images/1111111111.png",
299
+ "title": "AWPortrait Z",
300
+ "repo": "Shakker-Labs/AWPortrait-Z", #1
301
+ "weights": "AWPortrait-Z.safetensors",
302
+ "trigger_word": "Portrait"
303
+ },
304
+
305
+ {
306
+ "image": "https://cdn-uploads.huggingface.co/production/uploads/653cd3049107029eb004f968/DLCGlF9uUnFo5zxR5uyx6.png",
307
+ "title": "50s Western",
308
+ "repo": "neph1/50s_western_lora_zit",
309
+ "weights": "50s_western_z_100.safetensors",
310
+ "trigger_word": "50s_western"
311
+ },
312
+ {
313
+ "image": "https://huggingface.co/Sumitc13/Z-image-Turbo_LogC4_lora/resolve/main/images/1764464517272__000005000_1.jpg",
314
+ "title": "LogC4",
315
+ "repo": "Sumitc13/Z-image-Turbo_LogC4_lora", #30
316
+ "weights": "z-image-logc4_000005000.safetensors",
317
+ "trigger_word": "LogC4"
318
+ },
319
+ {
320
+ "image": "https://huggingface.co/neph1/80s_scifi_lora_zit/resolve/main/images/ComfyUI_10288_.png",
321
+ "title": "80s Scifi",
322
+ "repo": "neph1/80s_scifi_lora_zit",
323
+ "weights": "80s_scifi_z_80.safetensors",
324
+ "trigger_word": "80s_scifi"
325
+ },
326
+
327
+ # --------------------------------------------------------------------------------------------------------------------------------------
328
+ {
329
+ "image": "https://huggingface.co/Ttio2/Z-Image-Turbo-pencil-sketch/resolve/main/images/z-image_00097_.png",
330
+ "title": "Turbo Pencil",
331
+ "repo": "Ttio2/Z-Image-Turbo-pencil-sketch", #0
332
+ "weights": "Zimage_pencil_sketch.safetensors",
333
+ "trigger_word": "pencil sketch"
334
+ },
335
+ {
336
+ "image": "https://huggingface.co/neph1/50s_scifi_lora_zit/resolve/main/images/ComfyUI_08067_.png",
337
+ "title": "50s Scifi",
338
+ "repo": "neph1/50s_scifi_lora_zit",
339
+ "weights": "50s_scifi_z_80.safetensors",
340
+ "trigger_word": "50s_scifi"
341
+ },
342
+ {
343
+ "image": "https://huggingface.co/strangerzonehf/Flux-Ultimate-LoRA-Collection/resolve/main/images/cookie-mons.png",
344
+ "title": "Yarn Art Style",
345
+ "repo": "linoyts/yarn-art-style", #28
346
+ "weights": "yarn-art-style_000001250.safetensors",
347
+ "trigger_word": "yarn art style"
348
+ },
349
+ {
350
+ "image": "https://huggingface.co/Quorlen/Z-Image-Turbo-Behind-Reeded-Glass-Lora/resolve/main/images/ComfyUI_00391_.png",
351
+ "title": "Behind Reeded Glass",
352
+ "repo": "Quorlen/Z-Image-Turbo-Behind-Reeded-Glass-Lora", #26
353
+ "weights": "Z_Image_Turbo_Behind_Reeded_Glass_Lora_TAV2_000002750.safetensors",
354
+ "trigger_word": "Act1vate!, Behind reeded glass"
355
+ },
356
+ {
357
+ "image": "https://huggingface.co/ostris/z_image_turbo_childrens_drawings/resolve/main/images/1764433619736__000003000_9.jpg",
358
+ "title": "Childrens Drawings",
359
+ "repo": "ostris/z_image_turbo_childrens_drawings", #2
360
+ "weights": "z_image_turbo_childrens_drawings.safetensors",
361
+ "trigger_word": "Children Drawings"
362
+ },
363
+ {
364
+ "image": "https://huggingface.co/strangerzonehf/Flux-Ultimate-LoRA-Collection/resolve/main/images/xcxc.png",
365
+ "title": "Tarot Z",
366
+ "repo": "multimodalart/tarot-z-image-lora", #22
367
+ "weights": "tarot-z-image_000001250.safetensors",
368
+ "trigger_word": "trtcrd"
369
+ },
370
+ {
371
+ "image": "https://huggingface.co/renderartist/Technically-Color-Z-Image-Turbo/resolve/main/images/ComfyUI_00917_.png",
372
+ "title": "Technically Color Z",
373
+ "repo": "renderartist/Technically-Color-Z-Image-Turbo", #3
374
+ "weights": "Technically_Color_Z_Image_Turbo_v1_renderartist_2000.safetensors",
375
+ "trigger_word": "t3chnic4lly"
376
+ },
377
+ {
378
+ "image": "https://huggingface.co/SkyAsl/Tattoo-artist-Z/resolve/main/images/a%20dragon%20with%20flames.png",
379
+ "title": "Tattoo-artist-Z",
380
+ "repo": "SkyAsl/Tattoo-artist-Z", #31
381
+ "weights": "adapter_model.safetensors",
382
+ "trigger_word": "a tattoo design"
383
+ },
384
+ {
385
+ "image": "https://huggingface.co/strangerzonehf/Flux-Ultimate-LoRA-Collection/resolve/main/images/z-image_00147_.png",
386
+ "title": "Turbo Ghibli",
387
+ "repo": "Ttio2/Z-Image-Turbo-Ghibli-Style", #19
388
+ "weights": "ghibli_zimage_finetune.safetensors",
389
+ "trigger_word": "Ghibli Style"
390
+ },
391
+ {
392
+ "image": "https://huggingface.co/tarn59/pixel_art_style_lora_z_image_turbo/resolve/main/images/ComfyUI_00273_.png",
393
+ "title": "Pixel Art",
394
+ "repo": "tarn59/pixel_art_style_lora_z_image_turbo", #4
395
+ "weights": "pixel_art_style_z_image_turbo.safetensors",
396
+ "trigger_word": "Pixel art style."
397
+ },
398
+ {
399
+ "image": "https://huggingface.co/renderartist/Saturday-Morning-Z-Image-Turbo/resolve/main/images/Saturday_Morning_Z_15.png",
400
+ "title": "Saturday Morning",
401
+ "repo": "renderartist/Saturday-Morning-Z-Image-Turbo", #5
402
+ "weights": "Saturday_Morning_Z_Image_Turbo_v1_renderartist_1250.safetensors",
403
+ "trigger_word": "saturd4ym0rning"
404
+ },
405
+ {
406
+ "image": "https://huggingface.co/AIImageStudio/ReversalFilmGravure_z_Image_turbo/resolve/main/images/2025-12-01_173047-z_image_z_image_turbo_bf16-435125750859057-euler_10_hires.png",
407
+ "title": "ReversalFilmGravure",
408
+ "repo": "AIImageStudio/ReversalFilmGravure_z_Image_turbo", #6
409
+ "weights": "z_image_turbo_ReversalFilmGravure_v1.0.safetensors",
410
+ "trigger_word": "Reversal Film Gravure, analog film photography"
411
+ },
412
+ {
413
+ "image": "https://huggingface.co/renderartist/Coloring-Book-Z-Image-Turbo-LoRA/resolve/main/images/CBZ_00274_.png",
414
+ "title": "Coloring Book Z",
415
+ "repo": "renderartist/Coloring-Book-Z-Image-Turbo-LoRA", #7
416
+ "weights": "Coloring_Book_Z_Image_Turbo_v1_renderartist_2000.safetensors",
417
+ "trigger_word": "c0l0ringb00k"
418
+ },
419
+ {
420
+ "image": "https://huggingface.co/damnthatai/1950s_American_Dream/resolve/main/images/ZImage_20251129163459_135x_00001_.jpg",
421
+ "title": "1950s American Dream",
422
+ "repo": "damnthatai/1950s_American_Dream", #8
423
+ "weights": "5os4m3r1c4n4_z.safetensors",
424
+ "trigger_word": "5os4m3r1c4n4, 1950s, painting, a painting of"
425
+ },
426
+ {
427
+ "image": "https://huggingface.co/wcde/Z-Image-Turbo-DeJPEG-Lora/resolve/main/images/01.png",
428
+ "title": "DeJPEG",
429
+ "repo": "wcde/Z-Image-Turbo-DeJPEG-Lora", #9
430
+ "weights": "dejpeg_v3.safetensors",
431
+ "trigger_word": ""
432
+ },
433
+ {
434
+ "image": "https://huggingface.co/suayptalha/Z-Image-Turbo-Realism-LoRA/resolve/main/images/n4aSpqa-YFXYo4dtcIg4W.png",
435
+ "title": "DeJPEG",
436
+ "repo": "suayptalha/Z-Image-Turbo-Realism-LoRA", #10
437
+ "weights": "pytorch_lora_weights.safetensors",
438
+ "trigger_word": "Realism"
439
+ },
440
+ {
441
+ "image": "https://huggingface.co/renderartist/Classic-Painting-Z-Image-Turbo-LoRA/resolve/main/images/Classic_Painting_Z_00247_.png",
442
+ "title": "Classic Painting Z",
443
+ "repo": "renderartist/Classic-Painting-Z-Image-Turbo-LoRA", #11
444
+ "weights": "Classic_Painting_Z_Image_Turbo_v1_renderartist_1750.safetensors",
445
+ "trigger_word": "class1cpa1nt"
446
+ },
447
+ {
448
+ "image": "https://huggingface.co/DK9/3D_MMORPG_style_z-image-turbo_lora/resolve/main/images/10_with_lora.png",
449
+ "title": "3D MMORPG",
450
+ "repo": "DK9/3D_MMORPG_style_z-image-turbo_lora", #12
451
+ "weights": "lostark_v1.safetensors",
452
+ "trigger_word": ""
453
+ },
454
+ {
455
+ "image": "https://huggingface.co/Danrisi/Olympus_UltraReal_ZImage/resolve/main/images/Z-Image_01011_.png",
456
+ "title": "Olympus UltraReal",
457
+ "repo": "Danrisi/Olympus_UltraReal_ZImage", #13
458
+ "weights": "Olympus.safetensors",
459
+ "trigger_word": "digital photography, early 2000s compact camera aesthetic, amateur candid shot, digital photography, early 2000s compact camera aesthetic, amateur candid shot, direct flash lighting, hard flash shadow, specular highlights, overexposed highlights"
460
+ },
461
+ {
462
+ "image": "https://huggingface.co/AiAF/D-ART_Z-Image-Turbo_LoRA/resolve/main/images/example_l3otpwzaz.png",
463
+ "title": "D ART Z Image",
464
+ "repo": "AiAF/D-ART_Z-Image-Turbo_LoRA", #14
465
+ "weights": "D-ART_Z-Image-Turbo.safetensors",
466
+ "trigger_word": "D-ART"
467
+ },
468
+ {
469
+ "image": "https://huggingface.co/AlekseyCalvin/Marionette_Modernism_Z-image-Turbo_LoRA/resolve/main/bluebirdmandoll.webp",
470
+ "title": "Marionette Modernism",
471
+ "repo": "AlekseyCalvin/Marionette_Modernism_Z-image-Turbo_LoRA", #15
472
+ "weights": "ZImageDadadoll_000003600.safetensors",
473
+ "trigger_word": "DADADOLL style"
474
+ },
475
+ {
476
+ "image": "https://huggingface.co/AlekseyCalvin/HistoricColor_Z-image-Turbo-LoRA/resolve/main/HSTZgen2.webp",
477
+ "title": "Historic Color Z",
478
+ "repo": "AlekseyCalvin/HistoricColor_Z-image-Turbo-LoRA", #16
479
+ "weights": "ZImage1HST_000004000.safetensors",
480
+ "trigger_word": "HST style"
481
+ },
482
+ {
483
+ "image": "https://huggingface.co/tarn59/80s_air_brush_style_z_image_turbo/resolve/main/images/ComfyUI_00707_.png",
484
+ "title": "80s Air Brush",
485
+ "repo": "tarn59/80s_air_brush_style_z_image_turbo", #17
486
+ "weights": "80s_air_brush_style_v2_z_image_turbo.safetensors",
487
+ "trigger_word": "80s Air Brush style."
488
+ },
489
+ {
490
+ "image": "https://huggingface.co/CedarC/Z-Image_360/resolve/main/images/1765505225357__000006750_6.jpg",
491
+ "title": "360panorama",
492
+ "repo": "CedarC/Z-Image_360", #18
493
+ "weights": "Z-Image_360.safetensors",
494
+ "trigger_word": "360panorama"
495
+ },
496
+ {
497
+ "image": "https://huggingface.co/HAV0X1014/Z-Image-Turbo-KF-Bat-Eared-Fox-LoRA/resolve/main/images/ComfyUI_00132_.png",
498
+ "title": "KF-Bat-Eared",
499
+ "repo": "HAV0X1014/Z-Image-Turbo-KF-Bat-Eared-Fox-LoRA", #21
500
+ "weights": "z-image-turbo-bat_eared_fox.safetensors",
501
+ "trigger_word": "bat_eared_fox_kemono_friends"
502
+ },
503
+ {
504
+ "image": "https://cdn-uploads.huggingface.co/production/uploads/653cd3049107029eb004f968/IHttgddXu6ZBMo7eyy8p6.png",
505
+ "title": "80s Horror",
506
+ "repo": "neph1/80s_horror_movies_lora_zit", #23
507
+ "weights": "80s_horror_z_80.safetensors",
508
+ "trigger_word": "80s_horror"
509
+ },
510
+ {
511
+ "image": "https://huggingface.co/Quorlen/z_image_turbo_Sunbleached_Protograph_Style_Lora/resolve/main/images/ComfyUI_00024_.png",
512
+ "title": "Sunbleached Protograph",
513
+ "repo": "Quorlen/z_image_turbo_Sunbleached_Protograph_Style_Lora", #24
514
+ "weights": "zimageturbo_Sunbleach_Photograph_Style_Lora_TAV2_000002750.safetensors",
515
+ "trigger_word": "Act1vate!"
516
+ },
517
+ {
518
+ "image": "https://huggingface.co/bunnycore/Z-Art-2.1/resolve/main/images/ComfyUI_00069_.png",
519
+ "title": "Z-Art-2.1",
520
+ "repo": "bunnycore/Z-Art-2.1", #25
521
+ "weights": "Z-Image-Art2.1.safetensors",
522
+ "trigger_word": "anime art"
523
+ },
524
+ {
525
+ "image": "https://huggingface.co/cactusfriend/longfurby-z/resolve/main/images/1764658860954__000003000_1.jpg",
526
+ "title": "Longfurby",
527
+ "repo": "cactusfriend/longfurby-z", #27
528
+ "weights": "longfurbyZ.safetensors",
529
+ "trigger_word": ""
530
+ },
531
+ {
532
+ "image": "https://huggingface.co/SkyAsl/Pixel-artist-Z/resolve/main/pixel-art-result.png",
533
+ "title": "Pixel Art",
534
+ "repo": "SkyAsl/Pixel-artist-Z", #29
535
+ "weights": "adapter_model.safetensors",
536
+ "trigger_word": "a pixel art character"
537
+ },
538
+ ]
539
+
540
+ dtype = torch.bfloat16
541
+ device = "cuda" if torch.cuda.is_available() else "cpu"
542
+ base_model = "Tongyi-MAI/Z-Image-Turbo"
543
+
544
+ print(f"Loading {base_model} pipeline...")
545
+
546
+ # Initialize Pipeline
547
+ pipe = ZImagePipeline.from_pretrained(
548
+ base_model,
549
+ torch_dtype=dtype,
550
+ low_cpu_mem_usage=False,
551
+ ).to(device)
552
+
553
+ # ======== AoTI compilation + FA3 ========
554
+ # As per reference for optimization
555
+ try:
556
+ print("Applying AoTI compilation and FA3...")
557
+ pipe.transformer.layers._repeated_blocks = ["ZImageTransformerBlock"]
558
+ spaces.aoti_blocks_load(pipe.transformer.layers, "zerogpu-aoti/Z-Image", variant="fa3")
559
+ print("Optimization applied successfully.")
560
+ except Exception as e:
561
+ print(f"Optimization warning: {e}. Continuing with standard pipeline.")
562
+
563
+ MAX_SEED = np.iinfo(np.int32).max
564
+
565
+ class calculateDuration:
566
+ def __init__(self, activity_name=""):
567
+ self.activity_name = activity_name
568
+
569
+ def __enter__(self):
570
+ self.start_time = time.time()
571
+ return self
572
 
573
+ def __exit__(self, exc_type, exc_value, traceback):
574
+ self.end_time = time.time()
575
+ self.elapsed_time = self.end_time - self.start_time
576
+ if self.activity_name:
577
+ print(f"Elapsed time for {self.activity_name}: {self.elapsed_time:.6f} seconds")
578
+ else:
579
+ print(f"Elapsed time: {self.elapsed_time:.6f} seconds")
580
+
581
+ def update_selection(evt: gr.SelectData, width, height):
582
+ selected_lora = loras[evt.index]
583
+ new_placeholder = f"Type a prompt for {selected_lora['title']}"
584
+ lora_repo = selected_lora["repo"]
585
+ # 로컬 LoRA 처리
586
+ if lora_repo == "./":
587
+ updated_text = f"### Selected: Local LoRA - {selected_lora['title']} ✅"
588
+ else:
589
+ updated_text = f"### Selected: [{lora_repo}](https://huggingface.co/{lora_repo}) ✅"
590
+ if "aspect" in selected_lora:
591
+ if selected_lora["aspect"] == "portrait":
592
+ width = 768
593
+ height = 1024
594
+ elif selected_lora["aspect"] == "landscape":
595
+ width = 1024
596
+ height = 768
597
+ else:
598
+ width = 1024
599
+ height = 1024
600
+ return (
601
+ gr.update(placeholder=new_placeholder),
602
+ updated_text,
603
+ evt.index,
604
+ width,
605
+ height,
606
  )
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
607
 
608
+ @spaces.GPU
609
+ def run_lora(prompt, image_input, image_strength, cfg_scale, steps, selected_index, randomize_seed, seed, width, height, lora_scale, progress=gr.Progress(track_tqdm=True)):
610
+ # Clean up previous LoRAs in both cases
611
+ with calculateDuration("Unloading LoRA"):
612
+ pipe.unload_lora_weights()
 
613
 
614
+ # Check if a LoRA is selected
615
+ if selected_index is not None and selected_index < len(loras):
616
+ selected_lora = loras[selected_index]
617
+ lora_path = selected_lora["repo"]
618
+ trigger_word = selected_lora["trigger_word"]
619
+
620
+ # Prepare Prompt with Trigger Word
621
+ if(trigger_word):
622
+ if "trigger_position" in selected_lora:
623
+ if selected_lora["trigger_position"] == "prepend":
624
+ prompt_mash = f"{trigger_word} {prompt}"
625
+ else:
626
+ prompt_mash = f"{prompt} {trigger_word}"
627
+ else:
628
+ prompt_mash = f"{trigger_word} {prompt}"
629
  else:
630
+ prompt_mash = prompt
631
 
632
+ # Load LoRA
633
+ with calculateDuration(f"Loading LoRA weights for {selected_lora['title']}"):
634
+ weight_name = selected_lora.get("weights", None)
635
+ try:
636
+ pipe.load_lora_weights(
637
+ lora_path,
638
+ weight_name=weight_name,
639
+ adapter_name="default",
640
+ low_cpu_mem_usage=True
641
+ )
642
+ # Set adapter scale
643
+ pipe.set_adapters(["default"], adapter_weights=[lora_scale])
644
+ except Exception as e:
645
+ print(f"Error loading LoRA: {e}")
646
+ gr.Warning("Failed to load LoRA weights. Generating with base model.")
647
  else:
648
+ # Base Model Case
649
+ print("No LoRA selected. Running with Base Model.")
650
+ prompt_mash = prompt
651
+
652
+ with calculateDuration("Randomizing seed"):
653
+ if randomize_seed:
654
+ seed = random.randint(0, MAX_SEED)
655
 
656
+ generator = torch.Generator(device=device).manual_seed(seed)
 
 
 
 
 
 
 
657
 
658
+ # Note: Z-Image-Turbo is strictly T2I in this reference implementation.
659
+ # Img2Img via image_input is disabled/ignored for this pipeline update.
 
 
 
660
 
661
+ with calculateDuration("Generating image"):
662
+ # For Turbo models, guidance_scale is typically 0.0
663
+ forced_guidance = 0.0 # Turbo mode
 
 
 
 
 
 
 
 
 
664
 
665
+ final_image = pipe(
666
+ prompt=prompt_mash,
667
+ height=int(height),
668
+ width=int(width),
669
+ num_inference_steps=int(steps),
670
+ guidance_scale=forced_guidance,
671
+ generator=generator,
672
+ ).images[0]
673
 
674
+ yield final_image, seed, gr.update(visible=False)
675
+
676
+ def get_huggingface_safetensors(link):
677
+ split_link = link.split("/")
678
+ if(len(split_link) == 2):
679
+ model_card = ModelCard.load(link)
680
+ base_model = model_card.data.get("base_model")
681
+ print(base_model)
682
+
683
+ # Relaxed check to allow Z-Image or Flux or others, assuming user knows what they are doing
684
+ # or specifically check for Z-Image-Turbo
685
+ if base_model not in ["Tongyi-MAI/Z-Image-Turbo", "black-forest-labs/FLUX.1-dev"]:
686
+ # Just a warning instead of error to allow experimentation
687
+ print("Warning: Base model might not match.")
688
+
689
+ image_path = model_card.data.get("widget", [{}])[0].get("output", {}).get("url", None)
690
+ trigger_word = model_card.data.get("instance_prompt", "")
691
+ image_url = f"https://huggingface.co/{link}/resolve/main/{image_path}" if image_path else None
692
+ fs = HfFileSystem()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
693
  try:
694
+ list_of_files = fs.ls(link, detail=False)
695
+ for file in list_of_files:
696
+ if(file.endswith(".safetensors")):
697
+ safetensors_name = file.split("/")[-1]
698
+ if (not image_url and file.lower().endswith((".jpg", ".jpeg", ".png", ".webp"))):
699
+ image_elements = file.split("/")
700
+ image_url = f"https://huggingface.co/{link}/resolve/main/{image_elements[-1]}"
701
  except Exception as e:
702
+ print(e)
703
+ gr.Warning(f"You didn't include a link neither a valid Hugging Face repository with a *.safetensors LoRA")
704
+ raise Exception(f"You didn't include a link neither a valid Hugging Face repository with a *.safetensors LoRA")
705
+ return split_link[1], link, safetensors_name, trigger_word, image_url
706
+
707
+ def check_custom_model(link):
708
+ if(link.startswith("https://")):
709
+ if(link.startswith("https://huggingface.co") or link.startswith("https://www.huggingface.co")):
710
+ link_split = link.split("huggingface.co/")
711
+ return get_huggingface_safetensors(link_split[1])
712
+ else:
713
+ return get_huggingface_safetensors(link)
714
+
715
+ def add_custom_lora(custom_lora):
716
+ global loras
717
+ if(custom_lora):
718
+ try:
719
+ title, repo, path, trigger_word, image = check_custom_model(custom_lora)
720
+ print(f"Loaded custom LoRA: {repo}")
721
+ card = f'''
722
+ <div class="custom_lora_card">
723
+ <span>Loaded custom LoRA:</span>
724
+ <div class="card_internal">
725
+ <img src="{image}" />
726
+ <div>
727
+ <h3>{title}</h3>
728
+ <small>{"Using: <code><b>"+trigger_word+"</code></b> as the trigger word" if trigger_word else "No trigger word found. If there's a trigger word, include it in your prompt"}<br></small>
729
+ </div>
730
+ </div>
731
+ </div>
732
+ '''
733
+ existing_item_index = next((index for (index, item) in enumerate(loras) if item['repo'] == repo), None)
734
+ if(not existing_item_index):
735
+ new_item = {
736
+ "image": image,
737
+ "title": title,
738
+ "repo": repo,
739
+ "weights": path,
740
+ "trigger_word": trigger_word
741
+ }
742
+ print(new_item)
743
+ existing_item_index = len(loras)
744
+ loras.append(new_item)
745
+
746
+ return gr.update(visible=True, value=card), gr.update(visible=True), gr.Gallery(selected_index=None), f"Custom: {path}", existing_item_index, trigger_word
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
747
  except Exception as e:
748
+ gr.Warning(f"Invalid LoRA: either you entered an invalid link, or a non-supported LoRA")
749
+ return gr.update(visible=True, value=f"Invalid LoRA: either you entered an invalid link, a non-supported LoRA"), gr.update(visible=False), gr.update(), "", None, ""
750
+ else:
751
+ return gr.update(visible=False), gr.update(visible=False), gr.update(), "", None, ""
752
 
753
+ def remove_custom_lora():
754
+ return gr.update(visible=False), gr.update(visible=False), gr.update(), "", None, ""
755
 
756
+ run_lora.zerogpu = True
757
+
758
+ with gr.Blocks(title="Z-IMAGE GEN/LORA", delete_cache=(60, 60)) as demo:
 
759
 
760
  # HOME Button
761
  gr.HTML("""
 
771
  gr.HTML("""
772
  <div class="header-container">
773
  <div class="header-title">🎨 Z-IMAGE GEN/LORA 🎨</div>
774
+ <div class="header-subtitle">Generate amazing images with Z-Image Turbo and various LoRA styles!</div>
775
  <div style="margin-top:12px">
776
+ <span class="stats-badge">⚡ Turbo Speed</span>
777
+ <span class="stats-badge">🎭 30+ LoRAs</span>
778
+ <span class="stats-badge">🖼️ High Quality</span>
779
+ <span class="stats-badge">🔧 Custom LoRA</span>
780
  </div>
781
  </div>
782
  """)
 
 
 
783
 
784
+ selected_index = gr.State(None)
785
  with gr.Row():
786
+ with gr.Column(scale=3):
787
+ prompt = gr.Textbox(label="Enter Prompt", lines=1, placeholder="✦︎ Choose the LoRA and type the prompt (LoRA = None → Base Model = Active)")
788
+ with gr.Column(scale=1, elem_id="gen_column"):
789
+ generate_button = gr.Button("🚀 Generate", variant="primary", elem_id="gen_btn")
 
 
 
790
  with gr.Row():
791
+ with gr.Column():
792
+ selected_info = gr.Markdown("### No LoRA Selected (Base Model)")
793
+ gallery = gr.Gallery(
794
+ [(item["image"], item["title"]) for item in loras],
795
+ label="Z-Image LoRAs",
796
+ allow_preview=False,
797
+ columns=3,
798
+ elem_id="gallery",
 
799
  )
800
+ with gr.Group():
801
+ custom_lora = gr.Textbox(label="Enter Custom LoRA", placeholder="Paste the LoRA path and press Enter (e.g., Shakker-Labs/AWPortrait-Z).")
802
+ gr.Markdown("[Check the list of Z-Image LoRA's](https://huggingface.co/models?other=base_model:adapter:Tongyi-MAI/Z-Image-Turbo)", elem_id="lora_list")
803
+ custom_lora_info = gr.HTML(visible=False)
804
+ custom_lora_button = gr.Button("Remove custom LoRA", visible=False)
805
+ with gr.Column():
806
+ progress_bar = gr.Markdown(elem_id="progress",visible=False)
807
+ result = gr.Image(label="Generated Image", format="png", height=630)
808
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
809
  with gr.Row():
810
+ with gr.Accordion("Advanced Settings", open=False):
811
+ with gr.Row():
812
+ input_image = gr.Image(label="Input image (Ignored for Z-Image-Turbo)", type="filepath", visible=False)
813
+ image_strength = gr.Slider(label="Denoise Strength", info="Ignored for Z-Image-Turbo", minimum=0.1, maximum=1.0, step=0.01, value=0.75, visible=False)
814
+ with gr.Column():
815
+ with gr.Row():
816
+ cfg_scale = gr.Slider(label="CFG Scale", info="Forced to 0.0 for Turbo", minimum=0, maximum=20, step=0.5, value=0.0, interactive=False)
817
+ steps = gr.Slider(label="Steps", minimum=1, maximum=50, step=1, value=25)
818
+
819
+ with gr.Row():
820
+ width = gr.Slider(label="Width", minimum=256, maximum=1536, step=64, value=1536)
821
+ height = gr.Slider(label="Height", minimum=256, maximum=1536, step=64, value=1536)
822
+
823
+ with gr.Row():
824
+ randomize_seed = gr.Checkbox(True, label="Randomize seed")
825
+ seed = gr.Slider(label="Seed", minimum=0, maximum=MAX_SEED, step=1, value=0, randomize=True)
826
+ lora_scale = gr.Slider(label="LoRA Scale", minimum=0, maximum=3, step=0.01, value=0.95)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
827
 
828
  # Footer
829
  gr.HTML("""
830
  <div class="footer-comic">
831
  <p style="font-family:'Bangers',cursive;font-size:1.5rem;letter-spacing:2px">🎨 Z-IMAGE GEN/LORA 🎨</p>
832
+ <p>Powered by Z-Image Turbo + LoRA Adapters</p>
833
+ <p>⚡ Fast Generation🎭 Multiple Styles🖼️ High Quality</p>
834
  <p style="margin-top:10px"><a href="https://www.ginigen.com" target="_blank" style="color:#FACC15;text-decoration:none;font-weight:bold;">🏠 www.ginigen.com</a></p>
835
  </div>
836
  """)
837
 
838
+ gallery.select(
839
+ update_selection,
840
+ inputs=[width, height],
841
+ outputs=[prompt, selected_info, selected_index, width, height]
842
+ )
843
+ custom_lora.input(
844
+ add_custom_lora,
845
+ inputs=[custom_lora],
846
+ outputs=[custom_lora_info, custom_lora_button, gallery, selected_info, selected_index, prompt]
847
+ )
848
+ custom_lora_button.click(
849
+ remove_custom_lora,
850
+ outputs=[custom_lora_info, custom_lora_button, gallery, selected_info, selected_index, custom_lora]
851
+ )
852
+ gr.on(
853
+ triggers=[generate_button.click, prompt.submit],
854
+ fn=run_lora,
855
+ inputs=[prompt, input_image, image_strength, cfg_scale, steps, selected_index, randomize_seed, seed, width, height, lora_scale],
856
+ outputs=[result, seed, progress_bar]
857
+ )
858
+
859
+ demo.queue()
860
+ demo.launch(css=COMIC_CSS, ssr_mode=False, show_error=True)