lulavc commited on
Commit
3f2902c
·
verified ·
1 Parent(s): 0125529

Upload app.py with huggingface_hub

Browse files
Files changed (1) hide show
  1. app.py +218 -21
app.py CHANGED
@@ -9,11 +9,73 @@ import requests
9
  import io
10
  import base64
11
  import tempfile
 
12
  from typing import Tuple, Optional, Dict
13
  from PIL import Image
14
  from diffusers import DiffusionPipeline, ZImageImg2ImgPipeline
15
  from openai import OpenAI
16
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
17
  # Configure logging (replaces debug print statements)
18
  logging.basicConfig(level=logging.INFO, format='[%(levelname)s] %(message)s')
19
  logger = logging.getLogger(__name__)
@@ -861,15 +923,19 @@ def do_polish_transform_prompt(prompt: str, style: str, do_polish: bool) -> Tupl
861
 
862
  def generate_with_polish(prompt: str, style: str, do_polish: bool, ratio: str, steps: int, seed: int, randomize: bool):
863
  """Unified generate with progress feedback using generator.
864
- Yields intermediate status updates so user knows what's happening.
865
  """
866
  logger.info(f"generate_with_polish: do_polish={do_polish}, style={style}, prompt_len={len(prompt) if prompt else 0}")
867
 
868
- # Always yield initial status
 
 
 
 
869
  if do_polish:
870
- yield None, "Enhancing prompt with DeepSeek Reasoner...", seed
871
  else:
872
- yield None, "🎨 Preparing generation...", seed
873
 
874
  full_prompt, polished_display = do_polish_prompt(prompt, style, do_polish, mode="generate")
875
 
@@ -880,35 +946,40 @@ def generate_with_polish(prompt: str, style: str, do_polish: bool, ratio: str, s
880
  logger.warning(f"generate_with_polish: Prompt+ was enabled but enhancement unchanged")
881
 
882
  if not full_prompt.strip():
883
- yield None, "Empty prompt - please enter a description", seed
884
  return
885
 
886
  # Show status before GPU generation with the prompt that will be used
887
- status_prompt = polished_display if polished_display else full_prompt
888
- yield None, f"🎨 Generating image...\n\n{status_prompt}", seed
889
 
890
  # GPU generation
891
  image, used_seed = generate(full_prompt, polished_display, ratio, steps, seed, randomize)
892
 
893
- # Final result
 
894
  final_display = polished_display if polished_display else full_prompt
895
- yield image, final_display, used_seed
 
896
 
897
  def transform_with_polish(input_image: Optional[Image.Image], prompt: str, style: str, do_polish: bool, strength: float, steps: int, seed: int, randomize: bool):
898
  """Unified transform with progress feedback using generator.
899
- Yields intermediate status updates so user knows what's happening.
900
  """
901
  logger.info(f"transform_with_polish: do_polish={do_polish}, style={style}, prompt_len={len(prompt) if prompt else 0}")
902
 
 
 
 
 
903
  if input_image is None:
904
- yield None, "Please upload an image first", 0
905
  return
906
 
907
- # Always yield initial status
908
  if do_polish:
909
- yield None, "Enhancing prompt with DeepSeek Reasoner...", 0
910
  else:
911
- yield None, "🎨 Preparing transformation...", 0
912
 
913
  full_prompt, polished_display = do_polish_transform_prompt(prompt, style, do_polish)
914
 
@@ -918,16 +989,17 @@ def transform_with_polish(input_image: Optional[Image.Image], prompt: str, style
918
  elif do_polish:
919
  logger.warning(f"transform_with_polish: Prompt+ was enabled but enhancement unchanged")
920
 
921
- # Show status before GPU transform with the prompt that will be used
922
- status_prompt = polished_display if polished_display else full_prompt
923
- yield None, f"🎨 Transforming image...\n\n{status_prompt}", 0
924
 
925
  # GPU transform
926
  image, used_seed = transform(input_image, full_prompt, polished_display, strength, steps, seed, randomize)
927
 
928
- # Final result
 
929
  final_display = polished_display if polished_display else full_prompt
930
- yield image, final_display, used_seed
 
931
 
932
  @spaces.GPU(duration=120)
933
  def generate(full_prompt: str, polished_display: str, ratio: str, steps: int, seed: int, randomize: bool, progress=gr.Progress(track_tqdm=True)) -> Tuple[Optional[Image.Image], int]:
@@ -1435,8 +1507,133 @@ h1 { font-size: clamp(1.5rem, 4vw, 2.2rem); font-weight: 700; }
1435
  .progress-bar { background: var(--bg-tertiary) !important; border-radius: 4px; }
1436
  .progress-bar > div { background: var(--accent-gradient) !important; border-radius: 4px; }
1437
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1438
  @media (prefers-reduced-motion: reduce) {
1439
  *, *::before, *::after { animation-duration: 0.01ms !important; transition-duration: 0.01ms !important; }
 
1440
  }
1441
 
1442
  @media (max-width: 768px) {
@@ -1678,7 +1875,7 @@ with gr.Blocks(title="Z Image Turbo", css=css, theme=dark_theme) as demo:
1678
 
1679
  with gr.Column(scale=3):
1680
  gen_output = gr.Image(label="Generated Image", type="pil", interactive=False, height=512)
1681
- gen_polished_prompt = gr.Textbox(label="Enhanced Prompt", interactive=False, visible=True, lines=4)
1682
  gen_seed_out = gr.Number(label="Seed Used", interactive=False)
1683
  with gr.Row():
1684
  gen_share_btn = gr.Button("Share", variant="secondary")
@@ -1755,7 +1952,7 @@ with gr.Blocks(title="Z Image Turbo", css=css, theme=dark_theme) as demo:
1755
 
1756
  with gr.Column(scale=3):
1757
  trans_output = gr.Image(label="Transformed Image", type="pil", interactive=False, height=512)
1758
- trans_polished_prompt = gr.Textbox(label="Enhanced Prompt", interactive=False, visible=True, lines=4)
1759
  trans_seed_out = gr.Number(label="Seed Used", interactive=False)
1760
  with gr.Row():
1761
  trans_share_btn = gr.Button("Share", variant="secondary")
 
9
  import io
10
  import base64
11
  import tempfile
12
+ import time
13
  from typing import Tuple, Optional, Dict
14
  from PIL import Image
15
  from diffusers import DiffusionPipeline, ZImageImg2ImgPipeline
16
  from openai import OpenAI
17
 
18
+
19
+ # =============================================================================
20
+ # GENERATION TIMER CLASS
21
+ # =============================================================================
22
+ class GenerationTimer:
23
+ """Timer for tracking image generation elapsed time."""
24
+
25
+ def __init__(self):
26
+ self.start_time: Optional[float] = None
27
+ self.end_time: Optional[float] = None
28
+
29
+ def start(self):
30
+ """Start the timer."""
31
+ self.start_time = time.time()
32
+ self.end_time = None
33
+
34
+ def stop(self):
35
+ """Stop the timer."""
36
+ self.end_time = time.time()
37
+
38
+ def elapsed(self) -> float:
39
+ """Get elapsed time in seconds."""
40
+ if self.start_time is None:
41
+ return 0.0
42
+ end = self.end_time if self.end_time else time.time()
43
+ return end - self.start_time
44
+
45
+ def format(self) -> str:
46
+ """Format elapsed time as string."""
47
+ elapsed = self.elapsed()
48
+ if elapsed < 60:
49
+ return f"{elapsed:.1f}s"
50
+ minutes = int(elapsed // 60)
51
+ seconds = elapsed % 60
52
+ return f"{minutes}m {seconds:.1f}s"
53
+
54
+
55
+ def create_status_html(message: str, elapsed: str, is_generating: bool = True) -> str:
56
+ """Create HTML status display with animation and timer."""
57
+ if is_generating:
58
+ return f'''
59
+ <div class="generation-status generating">
60
+ <div class="status-content">
61
+ <div class="generating-spinner-dual"></div>
62
+ <div class="status-text-container">
63
+ <span class="status-text">{message}</span>
64
+ <span class="status-timer">⏱️ {elapsed}</span>
65
+ </div>
66
+ </div>
67
+ </div>
68
+ '''
69
+ else:
70
+ return f'''
71
+ <div class="generation-status complete">
72
+ <div class="status-content">
73
+ <span class="status-complete">✅ {message}</span>
74
+ <span class="status-timer-final">⏱️ {elapsed}</span>
75
+ </div>
76
+ </div>
77
+ '''
78
+
79
  # Configure logging (replaces debug print statements)
80
  logging.basicConfig(level=logging.INFO, format='[%(levelname)s] %(message)s')
81
  logger = logging.getLogger(__name__)
 
923
 
924
  def generate_with_polish(prompt: str, style: str, do_polish: bool, ratio: str, steps: int, seed: int, randomize: bool):
925
  """Unified generate with progress feedback using generator.
926
+ Yields intermediate status updates with timer so user knows what's happening.
927
  """
928
  logger.info(f"generate_with_polish: do_polish={do_polish}, style={style}, prompt_len={len(prompt) if prompt else 0}")
929
 
930
+ # Start timer
931
+ timer = GenerationTimer()
932
+ timer.start()
933
+
934
+ # Always yield initial status with animation
935
  if do_polish:
936
+ yield None, create_status_html("Enhancing prompt with DeepSeek Reasoner", timer.format()), seed
937
  else:
938
+ yield None, create_status_html("Preparing generation", timer.format()), seed
939
 
940
  full_prompt, polished_display = do_polish_prompt(prompt, style, do_polish, mode="generate")
941
 
 
946
  logger.warning(f"generate_with_polish: Prompt+ was enabled but enhancement unchanged")
947
 
948
  if not full_prompt.strip():
949
+ yield None, create_status_html("Empty prompt - please enter a description", timer.format(), is_generating=False).replace("✅", "❌"), seed
950
  return
951
 
952
  # Show status before GPU generation with the prompt that will be used
953
+ yield None, create_status_html("Generating image", timer.format()), seed
 
954
 
955
  # GPU generation
956
  image, used_seed = generate(full_prompt, polished_display, ratio, steps, seed, randomize)
957
 
958
+ # Stop timer and show final result
959
+ timer.stop()
960
  final_display = polished_display if polished_display else full_prompt
961
+ final_status = create_status_html(f"Generated in {timer.format()}", timer.format(), is_generating=False)
962
+ yield image, final_status + f"\n\n{final_display}", used_seed
963
 
964
  def transform_with_polish(input_image: Optional[Image.Image], prompt: str, style: str, do_polish: bool, strength: float, steps: int, seed: int, randomize: bool):
965
  """Unified transform with progress feedback using generator.
966
+ Yields intermediate status updates with timer so user knows what's happening.
967
  """
968
  logger.info(f"transform_with_polish: do_polish={do_polish}, style={style}, prompt_len={len(prompt) if prompt else 0}")
969
 
970
+ # Start timer
971
+ timer = GenerationTimer()
972
+ timer.start()
973
+
974
  if input_image is None:
975
+ yield None, create_status_html("Please upload an image first", timer.format(), is_generating=False).replace("✅", "❌"), 0
976
  return
977
 
978
+ # Always yield initial status with animation
979
  if do_polish:
980
+ yield None, create_status_html("Enhancing prompt with DeepSeek Reasoner", timer.format()), 0
981
  else:
982
+ yield None, create_status_html("Preparing transformation", timer.format()), 0
983
 
984
  full_prompt, polished_display = do_polish_transform_prompt(prompt, style, do_polish)
985
 
 
989
  elif do_polish:
990
  logger.warning(f"transform_with_polish: Prompt+ was enabled but enhancement unchanged")
991
 
992
+ # Show status before GPU transform
993
+ yield None, create_status_html("Transforming image", timer.format()), 0
 
994
 
995
  # GPU transform
996
  image, used_seed = transform(input_image, full_prompt, polished_display, strength, steps, seed, randomize)
997
 
998
+ # Stop timer and show final result
999
+ timer.stop()
1000
  final_display = polished_display if polished_display else full_prompt
1001
+ final_status = create_status_html(f"Transformed in {timer.format()}", timer.format(), is_generating=False)
1002
+ yield image, final_status + f"\n\n{final_display}", used_seed
1003
 
1004
  @spaces.GPU(duration=120)
1005
  def generate(full_prompt: str, polished_display: str, ratio: str, steps: int, seed: int, randomize: bool, progress=gr.Progress(track_tqdm=True)) -> Tuple[Optional[Image.Image], int]:
 
1507
  .progress-bar { background: var(--bg-tertiary) !important; border-radius: 4px; }
1508
  .progress-bar > div { background: var(--accent-gradient) !important; border-radius: 4px; }
1509
 
1510
+ /* ============================================
1511
+ GENERATING IMAGE LOADING ANIMATIONS
1512
+ ============================================ */
1513
+
1514
+ @keyframes status-pulse {
1515
+ 0%, 100% {
1516
+ opacity: 1;
1517
+ text-shadow: 0 0 4px rgba(129, 140, 248, 0.4), 0 0 8px rgba(129, 140, 248, 0.2);
1518
+ }
1519
+ 50% {
1520
+ opacity: 0.7;
1521
+ text-shadow: 0 0 8px rgba(129, 140, 248, 0.6), 0 0 20px rgba(167, 139, 250, 0.4);
1522
+ }
1523
+ }
1524
+
1525
+ @keyframes spinner-rotate {
1526
+ 0% { transform: rotate(0deg); }
1527
+ 100% { transform: rotate(360deg); }
1528
+ }
1529
+
1530
+ @keyframes glow-pulse {
1531
+ 0%, 100% { opacity: 0.5; transform: scale(1); }
1532
+ 50% { opacity: 0.8; transform: scale(1.02); }
1533
+ }
1534
+
1535
+ /* Generation status container */
1536
+ .generation-status {
1537
+ padding: 16px 20px;
1538
+ border-radius: var(--radius-md);
1539
+ margin: 8px 0;
1540
+ transition: all 0.3s ease;
1541
+ }
1542
+
1543
+ .generation-status.generating {
1544
+ background: linear-gradient(135deg, rgba(99, 102, 241, 0.15) 0%, rgba(139, 92, 246, 0.1) 100%);
1545
+ border: 1px solid rgba(129, 140, 248, 0.3);
1546
+ box-shadow: 0 0 20px rgba(129, 140, 248, 0.2);
1547
+ }
1548
+
1549
+ .generation-status.complete {
1550
+ background: linear-gradient(135deg, rgba(16, 185, 129, 0.15) 0%, rgba(52, 211, 153, 0.1) 100%);
1551
+ border: 1px solid rgba(16, 185, 129, 0.3);
1552
+ }
1553
+
1554
+ .generation-status .status-content {
1555
+ display: flex;
1556
+ align-items: center;
1557
+ gap: 14px;
1558
+ }
1559
+
1560
+ .generation-status .status-text-container {
1561
+ display: flex;
1562
+ flex-direction: column;
1563
+ gap: 4px;
1564
+ }
1565
+
1566
+ .generation-status .status-text {
1567
+ color: var(--accent-primary);
1568
+ font-weight: 600;
1569
+ font-size: 1rem;
1570
+ animation: status-pulse 2s ease-in-out infinite;
1571
+ }
1572
+
1573
+ .generation-status .status-timer {
1574
+ color: var(--text-muted);
1575
+ font-size: 0.85rem;
1576
+ font-family: monospace;
1577
+ }
1578
+
1579
+ .generation-status .status-complete {
1580
+ color: var(--success);
1581
+ font-weight: 600;
1582
+ font-size: 1rem;
1583
+ }
1584
+
1585
+ .generation-status .status-timer-final {
1586
+ color: var(--text-secondary);
1587
+ font-size: 0.9rem;
1588
+ font-family: monospace;
1589
+ margin-left: auto;
1590
+ }
1591
+
1592
+ /* Dual-ring spinner */
1593
+ .generating-spinner-dual {
1594
+ display: inline-block;
1595
+ position: relative;
1596
+ width: 28px;
1597
+ height: 28px;
1598
+ flex-shrink: 0;
1599
+ }
1600
+
1601
+ .generating-spinner-dual::before,
1602
+ .generating-spinner-dual::after {
1603
+ content: '';
1604
+ position: absolute;
1605
+ inset: 0;
1606
+ border-radius: 50%;
1607
+ border: 3px solid transparent;
1608
+ }
1609
+
1610
+ .generating-spinner-dual::before {
1611
+ border-top-color: var(--accent-primary);
1612
+ animation: spinner-rotate 1.2s linear infinite;
1613
+ }
1614
+
1615
+ .generating-spinner-dual::after {
1616
+ border-bottom-color: var(--accent-secondary);
1617
+ animation: spinner-rotate 0.9s linear reverse infinite;
1618
+ }
1619
+
1620
+ /* Image container glow while generating */
1621
+ .generating .gr-image::after {
1622
+ content: '';
1623
+ position: absolute;
1624
+ inset: -8px;
1625
+ border-radius: inherit;
1626
+ background: var(--accent-gradient);
1627
+ filter: blur(20px);
1628
+ opacity: 0.3;
1629
+ animation: glow-pulse 2s ease-in-out infinite;
1630
+ z-index: -1;
1631
+ pointer-events: none;
1632
+ }
1633
+
1634
  @media (prefers-reduced-motion: reduce) {
1635
  *, *::before, *::after { animation-duration: 0.01ms !important; transition-duration: 0.01ms !important; }
1636
+ .generation-status .status-text { animation: none; text-shadow: 0 0 8px rgba(129, 140, 248, 0.5); }
1637
  }
1638
 
1639
  @media (max-width: 768px) {
 
1875
 
1876
  with gr.Column(scale=3):
1877
  gen_output = gr.Image(label="Generated Image", type="pil", interactive=False, height=512)
1878
+ gen_polished_prompt = gr.HTML(label="Status", value="")
1879
  gen_seed_out = gr.Number(label="Seed Used", interactive=False)
1880
  with gr.Row():
1881
  gen_share_btn = gr.Button("Share", variant="secondary")
 
1952
 
1953
  with gr.Column(scale=3):
1954
  trans_output = gr.Image(label="Transformed Image", type="pil", interactive=False, height=512)
1955
+ trans_polished_prompt = gr.HTML(label="Status", value="")
1956
  trans_seed_out = gr.Number(label="Seed Used", interactive=False)
1957
  with gr.Row():
1958
  trans_share_btn = gr.Button("Share", variant="secondary")