yangyufeng commited on
Commit
5d8652f
·
1 Parent(s): 6a78ea7

update big UI figure

Browse files
Files changed (1) hide show
  1. app.py +58 -25
app.py CHANGED
@@ -48,26 +48,53 @@ TASK_PRESETS = {
48
  }
49
  DEFAULT_PRESET = "Low-light Enhancement"
50
 
51
- # --- UI Header (严格限制两行) ---
52
  TITLE_HTML = """
53
- <div style="text-align: center; margin-bottom: 20px;">
54
- <h1 style="font-size: 2.5rem; margin: 0 0 5px 0; background: linear-gradient(135deg, #4f46e5 0%, #ec4899 100%); -webkit-background-clip: text; -webkit-text-fill-color: transparent;">RealRestorer</h1>
55
- <p style="font-size: 1.05rem; color: var(--body-text-color-subdued); margin: 0; font-weight: 500;">A powerful image restoration model supporting deblurring, denoising, deflaring, low-light enhancement, and more.</p>
56
  </div>
57
  """
58
 
59
- # --- 极简 CSS,不破坏 Gradio 原生布局 ---
60
  CUSTOM_CSS = """
61
- .gradio-container { max-width: 1400px !important; margin: auto !important; padding-top: 20px !important; }
62
- /* 去除一些不必要的内边距让左右更紧凑 */
63
- .gr-box { border-radius: 12px !important; }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
64
  """
65
 
66
  PIPELINE = None
67
  PIPELINE_LOCK = threading.Lock()
68
  INFERENCE_LOCK = threading.Lock()
69
 
70
- @spaces.GPU(duration=1)
71
  def _spaces_gpu_probe(): return None
72
 
73
  def _pick_device():
@@ -98,10 +125,10 @@ def _pil_to_data_url(image: Image.Image) -> str:
98
  return f"data:image/png;base64,{base64.b64encode(buffer.getvalue()).decode('utf-8')}"
99
 
100
  def _build_slider_html(before_image: Image.Image, after_image: Image.Image) -> str:
101
- # 状态:未上传图片,占位符高度固定520px,与左侧对齐
102
  if before_image is None or after_image is None:
103
  return """
104
- <div style='height: 520px; display:flex; align-items:center; justify-content:center; color: var(--body-text-color-subdued); border: 2px dashed var(--border-color-primary); border-radius: 12px; font-size: 1.1rem; background: var(--background-fill-secondary);'>
105
  Upload an image and run to see the comparison here.
106
  </div>
107
  """
@@ -115,24 +142,24 @@ def _build_slider_html(before_image: Image.Image, after_image: Image.Image) -> s
115
 
116
  on_input = f"var p=this.value; document.getElementById('{slider_id}_top').style.clipPath='inset(0 '+(100-p)+'% 0 0)'; document.getElementById('{slider_id}_line').style.left=p+'%';"
117
 
118
- # 状态:渲染完成高度同样固定520px
119
  return f"""
120
- <div style="position:relative; width:100%; height:520px; overflow:hidden; border-radius:12px; background:var(--background-fill-secondary); border: 1px solid var(--border-color-primary); margin: 0 auto;">
121
  <!-- 底图: 修复后 -->
122
  <img src="{after_url}" style="position:absolute; top:0; left:0; width:100%; height:100%; object-fit:contain;" draggable="false" />
123
  <!-- 顶图: 原图 -->
124
  <img id="{slider_id}_top" src="{before_url}" style="position:absolute; top:0; left:0; width:100%; height:100%; object-fit:contain; clip-path:inset(0 50% 0 0);" draggable="false" />
125
 
126
  <!-- 拖拽线与把手 -->
127
- <div id="{slider_id}_line" style="position:absolute; top:0; left:50%; width:4px; height:100%; background:white; box-shadow:0 0 10px rgba(0,0,0,0.3); transform:translateX(-50%); pointer-events:none; z-index:10;">
128
- <div style="position:absolute; top:50%; left:50%; transform:translate(-50%, -50%); width:40px; height:40px; border-radius:50%; background:white; display:flex; align-items:center; justify-content:center; box-shadow:0 2px 10px rgba(0,0,0,0.4); font-weight:bold; color:#1e293b; font-size:1.1rem;">↔</div>
129
  </div>
130
 
131
  <!-- 透明原生滑动条 -->
132
  <input type="range" min="0" max="100" value="50" oninput="{on_input}" style="position:absolute; top:0; left:0; width:100%; height:100%; opacity:0; cursor:ew-resize; z-index:20; margin:0; appearance:auto;" />
133
  </div>
134
 
135
- <div style="display:flex; justify-content:space-between; color:var(--body-text-color-subdued); font-size:0.95rem; padding-top:12px; font-weight:600;">
136
  <span>⬅️ Original</span>
137
  <span>Restored ➡️</span>
138
  </div>
@@ -176,8 +203,13 @@ def run_inference(image: Optional[Image.Image], task_name: str, prompt: str, ste
176
 
177
 
178
  def build_demo():
179
- # 使Gradio 原生 Soft 主题确保颜色一致且在各浏览器下不渲染崩溃
180
- with gr.Blocks(css=CUSTOM_CSS, title="RealRestorer", theme=gr.themes.Soft()) as demo:
 
 
 
 
 
181
  gr.HTML(TITLE_HTML)
182
 
183
  # 完美 1:1 对齐布局
@@ -187,8 +219,8 @@ def build_demo():
187
  with gr.Column(scale=1):
188
  gr.Markdown("### ⚙️ 1. Setup & Control")
189
 
190
- # 高度设定为 320px
191
- input_image = gr.Image(label="Upload Image", type="pil", height=320)
192
 
193
  task_dropdown = gr.Dropdown(choices=list(TASK_PRESETS.keys()), value=DEFAULT_PRESET, label="Task Preset")
194
  prompt_box = gr.Textbox(label="Instruction", value=TASK_PRESETS[DEFAULT_PRESET], lines=2)
@@ -200,7 +232,7 @@ def build_demo():
200
  with gr.Accordion("Advanced Settings", open=False):
201
  seed_box = gr.Number(label="Seed (-1 for random)", value=DEFAULT_SEED, precision=0)
202
 
203
- run_button = gr.Button("🚀 Run Restoration", variant="primary", size="lg")
204
 
205
 
206
  # ===== 右侧:展示面板 =====
@@ -211,17 +243,18 @@ def build_demo():
211
  label="Status",
212
  value="💡 Ready. Upload an image and click Run.",
213
  interactive=False,
214
- lines=1
 
215
  )
216
 
217
  with gr.Tabs():
218
  with gr.Tab("Compare View"):
219
- # 高度在 HTML 中已锁定为 520px
220
  slider_view = gr.HTML(_build_slider_html(None, None))
221
 
222
  with gr.Tab("Output Image"):
223
- # 高度设定为 520px,与 Compare View 严格一致
224
- output_image = gr.Image(label="Restored Result", type="pil", interactive=False, height=520, show_label=False)
225
 
226
  # ===== 事件绑定 =====
227
  task_dropdown.change(fn=_on_preset_change, inputs=[task_dropdown], outputs=[prompt_box])
 
48
  }
49
  DEFAULT_PRESET = "Low-light Enhancement"
50
 
51
+ # --- UI Header (大气版:字号放大,严格限制两行) ---
52
  TITLE_HTML = """
53
+ <div style="text-align: center; margin-bottom: 30px;">
54
+ <h1 style="font-size: 3.2rem; margin: 0 0 10px 0; background: linear-gradient(135deg, #4f46e5 0%, #ec4899 100%); -webkit-background-clip: text; -webkit-text-fill-color: transparent; letter-spacing: -1px;">RealRestorer</h1>
55
+ <p style="font-size: 1.15rem; color: var(--body-text-color-subdued); margin: 0; font-weight: 500;">A powerful image restoration model supporting deblurring, denoising, deflaring, low-light enhancement, and more.</p>
56
  </div>
57
  """
58
 
59
+ # --- 宽屏大气 CSS ---
60
  CUSTOM_CSS = """
61
+ /* 扩大最大宽度,增加顶部留白,营造呼吸感 */
62
+ .gradio-container { max-width: 1600px !important; margin: auto !important; padding-top: 40px !important; }
63
+
64
+ /* 调整面板圆角与阴影使其更大气 */
65
+ .gr-box { border-radius: 16px !important; }
66
+
67
+ /* 强化主按钮质感与视觉吸引力 */
68
+ #run-btn {
69
+ background: linear-gradient(135deg, #4f46e5 0%, #6366f1 100%) !important;
70
+ color: white !important;
71
+ border: none !important;
72
+ font-size: 1.25rem !important;
73
+ font-weight: 700 !important;
74
+ padding: 16px !important;
75
+ border-radius: 12px !important;
76
+ box-shadow: 0 6px 15px -3px rgba(79, 70, 229, 0.4) !important;
77
+ transition: all 0.2s ease-in-out !important;
78
+ }
79
+ #run-btn:hover {
80
+ transform: translateY(-2px) !important;
81
+ box-shadow: 0 10px 20px -3px rgba(79, 70, 229, 0.5) !important;
82
+ }
83
+
84
+ /* 优化状态栏的视觉 */
85
+ .rr-status textarea {
86
+ font-size: 1.05rem !important;
87
+ color: #334155 !important;
88
+ background: #f8fafc !important;
89
+ font-family: ui-monospace, monospace !important;
90
+ }
91
  """
92
 
93
  PIPELINE = None
94
  PIPELINE_LOCK = threading.Lock()
95
  INFERENCE_LOCK = threading.Lock()
96
 
97
+ @spaces.GPU(duration=180)
98
  def _spaces_gpu_probe(): return None
99
 
100
  def _pick_device():
 
125
  return f"data:image/png;base64,{base64.b64encode(buffer.getvalue()).decode('utf-8')}"
126
 
127
  def _build_slider_html(before_image: Image.Image, after_image: Image.Image) -> str:
128
+ # 状态:未上传图片高度放大650px,与左侧对齐
129
  if before_image is None or after_image is None:
130
  return """
131
+ <div style='height: 650px; display:flex; align-items:center; justify-content:center; color: var(--body-text-color-subdued); border: 2px dashed var(--border-color-primary); border-radius: 16px; font-size: 1.25rem; background: var(--background-fill-secondary);'>
132
  Upload an image and run to see the comparison here.
133
  </div>
134
  """
 
142
 
143
  on_input = f"var p=this.value; document.getElementById('{slider_id}_top').style.clipPath='inset(0 '+(100-p)+'% 0 0)'; document.getElementById('{slider_id}_line').style.left=p+'%';"
144
 
145
+ # 状态:渲染完成高度同样放大650px,手柄也相应变大
146
  return f"""
147
+ <div style="position:relative; width:100%; height:650px; overflow:hidden; border-radius:16px; background:var(--background-fill-secondary); border: 1px solid var(--border-color-primary); margin: 0 auto;">
148
  <!-- 底图: 修复后 -->
149
  <img src="{after_url}" style="position:absolute; top:0; left:0; width:100%; height:100%; object-fit:contain;" draggable="false" />
150
  <!-- 顶图: 原图 -->
151
  <img id="{slider_id}_top" src="{before_url}" style="position:absolute; top:0; left:0; width:100%; height:100%; object-fit:contain; clip-path:inset(0 50% 0 0);" draggable="false" />
152
 
153
  <!-- 拖拽线与把手 -->
154
+ <div id="{slider_id}_line" style="position:absolute; top:0; left:50%; width:4px; height:100%; background:white; box-shadow:0 0 15px rgba(0,0,0,0.4); transform:translateX(-50%); pointer-events:none; z-index:10;">
155
+ <div style="position:absolute; top:50%; left:50%; transform:translate(-50%, -50%); width:48px; height:48px; border-radius:50%; background:white; display:flex; align-items:center; justify-content:center; box-shadow:0 4px 15px rgba(0,0,0,0.4); font-weight:bold; color:#1e293b; font-size:1.3rem;">↔</div>
156
  </div>
157
 
158
  <!-- 透明原生滑动条 -->
159
  <input type="range" min="0" max="100" value="50" oninput="{on_input}" style="position:absolute; top:0; left:0; width:100%; height:100%; opacity:0; cursor:ew-resize; z-index:20; margin:0; appearance:auto;" />
160
  </div>
161
 
162
+ <div style="display:flex; justify-content:space-between; color:var(--body-text-color-subdued); font-size:1.05rem; padding-top:16px; font-weight:600;">
163
  <span>⬅️ Original</span>
164
  <span>Restored ➡️</span>
165
  </div>
 
203
 
204
 
205
  def build_demo():
206
+ # text_lg spacing_lg让界面自带大气的留白和清晰的大字体
207
+ atmospheric_theme = gr.themes.Soft(
208
+ text_size=gr.themes.sizes.text_lg,
209
+ spacing_size=gr.themes.sizes.spacing_lg
210
+ )
211
+
212
+ with gr.Blocks(css=CUSTOM_CSS, title="RealRestorer", theme=atmospheric_theme) as demo:
213
  gr.HTML(TITLE_HTML)
214
 
215
  # 完美 1:1 对齐布局
 
219
  with gr.Column(scale=1):
220
  gr.Markdown("### ⚙️ 1. Setup & Control")
221
 
222
+ # 上传区域放大至 420px,提供更宽裕的拖拽空间
223
+ input_image = gr.Image(label="Upload Image", type="pil", height=420)
224
 
225
  task_dropdown = gr.Dropdown(choices=list(TASK_PRESETS.keys()), value=DEFAULT_PRESET, label="Task Preset")
226
  prompt_box = gr.Textbox(label="Instruction", value=TASK_PRESETS[DEFAULT_PRESET], lines=2)
 
232
  with gr.Accordion("Advanced Settings", open=False):
233
  seed_box = gr.Number(label="Seed (-1 for random)", value=DEFAULT_SEED, precision=0)
234
 
235
+ run_button = gr.Button("🚀 Run Restoration", elem_id="run-btn")
236
 
237
 
238
  # ===== 右侧:展示面板 =====
 
243
  label="Status",
244
  value="💡 Ready. Upload an image and click Run.",
245
  interactive=False,
246
+ lines=1,
247
+ elem_classes=["rr-status"]
248
  )
249
 
250
  with gr.Tabs():
251
  with gr.Tab("Compare View"):
252
+ # 高度在 HTML 中已锁定为巨大的 650px
253
  slider_view = gr.HTML(_build_slider_html(None, None))
254
 
255
  with gr.Tab("Output Image"):
256
+ # 高度同步设定为 650px,与 Compare View 严格一致
257
+ output_image = gr.Image(label="Restored Result", type="pil", interactive=False, height=650, show_label=False)
258
 
259
  # ===== 事件绑定 =====
260
  task_dropdown.change(fn=_on_preset_change, inputs=[task_dropdown], outputs=[prompt_box])