yangyufeng commited on
Commit
9d7213d
·
1 Parent(s): b1b404c

fix texture

Browse files
Files changed (1) hide show
  1. app.py +56 -51
app.py CHANGED
@@ -48,27 +48,27 @@ TASK_PRESETS = {
48
  }
49
  DEFAULT_PRESET = "Low-light Enhancement"
50
 
51
- # --- UI Header (Strictly 2 lines, centered) ---
52
  TITLE_HTML = """
53
  <div style="text-align: center; margin-bottom: 20px;">
54
- <h1 style="font-size: 3.5rem; 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.2rem; 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
- # --- Full-Width Vertical CSS ---
60
  CUSTOM_CSS = """
61
- /* 放宽最大宽度,让网页横向占满 */
62
- .gradio-container { max-width: 1800px !important; margin: auto !important; padding: 40px 20px !important; }
63
 
64
- /* 定义区块的视觉卡片风格 */
65
  .rr-section {
66
- background: #ffffff !important;
67
- border: 1px solid #e2e8f0 !important;
68
  border-radius: 16px !important;
69
- box-shadow: 0 4px 20px rgba(0, 0, 0, 0.04) !important;
70
- padding: 30px !important;
71
- margin-bottom: 30px !important;
72
  }
73
 
74
  /* 巨型行动按钮 */
@@ -76,23 +76,29 @@ CUSTOM_CSS = """
76
  background: linear-gradient(135deg, #4f46e5 0%, #6366f1 100%) !important;
77
  color: white !important;
78
  border: none !important;
79
- font-size: 1.3rem !important;
80
  font-weight: 700 !important;
81
- padding: 18px !important;
82
  border-radius: 12px !important;
83
- box-shadow: 0 6px 20px -4px rgba(79, 70, 229, 0.4) !important;
84
- transition: all 0.2s ease-in-out !important;
85
  margin-top: 10px !important;
86
  }
87
  #run-btn:hover {
88
  transform: translateY(-2px) !important;
89
- box-shadow: 0 10px 25px -4px rgba(79, 70, 229, 0.5) !important;
90
  }
91
 
92
- /* 样式 */
93
- .section-title {
94
- font-size: 1.4rem; font-weight: 700; color: #1e293b;
95
- margin-bottom: 20px; display: flex; align-items: center; gap: 8px;
 
 
 
 
 
 
96
  }
97
  """
98
 
@@ -131,9 +137,10 @@ def _pil_to_data_url(image: Image.Image) -> str:
131
  return f"data:image/png;base64,{base64.b64encode(buffer.getvalue()).decode('utf-8')}"
132
 
133
  def _build_slider_html(before_image: Image.Image, after_image: Image.Image) -> str:
 
134
  if before_image is None or after_image is None:
135
  return """
136
- <div style='height: 500px; width: 100%; 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.3rem; background: var(--background-fill-secondary);'>
137
  Upload an image and run to see the full-width comparison here.
138
  </div>
139
  """
@@ -148,18 +155,17 @@ def _build_slider_html(before_image: Image.Image, after_image: Image.Image) -> s
148
 
149
  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+'%';"
150
 
151
- # 动态自适应:外层包裹居中,内层按原��比例撑开,最大高度限制为屏幕高度的 80% 防止竖图过长
152
  return f"""
153
- <div style="display: flex; justify-content: center; width: 100%; background: var(--background-fill-secondary); border-radius: 16px; padding: 20px; border: 1px solid var(--border-color-primary); box-sizing: border-box;">
154
- <div style="position:relative; width:100%; max-height:80vh; aspect-ratio:{width}/{height}; overflow:hidden; border-radius:12px; box-shadow: 0 10px 30px rgba(0,0,0,0.15);">
155
  <!-- 底图: 修复后 -->
156
  <img src="{after_url}" style="position:absolute; top:0; left:0; width:100%; height:100%; object-fit:contain;" draggable="false" />
157
  <!-- 顶图: 原图 -->
158
  <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" />
159
 
160
  <!-- 拖拽线与把手 -->
161
- <div id="{slider_id}_line" style="position:absolute; top:0; left:50%; width:4px; height:100%; background:white; box-shadow:0 0 20px rgba(0,0,0,0.5); transform:translateX(-50%); pointer-events:none; z-index:10;">
162
- <div style="position:absolute; top:50%; left:50%; transform:translate(-50%, -50%); width:56px; height:56px; border-radius:50%; background:white; display:flex; align-items:center; justify-content:center; box-shadow:0 4px 20px rgba(0,0,0,0.4); font-weight:bold; color:#1e293b; font-size:1.5rem;">↔</div>
163
  </div>
164
 
165
  <!-- 透明原生滑动条 -->
@@ -167,7 +173,7 @@ def _build_slider_html(before_image: Image.Image, after_image: Image.Image) -> s
167
  </div>
168
  </div>
169
 
170
- <div style="display:flex; justify-content:space-between; color:var(--body-text-color-subdued); font-size:1.1rem; padding-top:16px; font-weight:600;">
171
  <span>⬅️ Original</span>
172
  <span>Restored ➡️</span>
173
  </div>
@@ -210,30 +216,30 @@ def run_inference(image: Optional[Image.Image], task_name: str, prompt: str, ste
210
  return None, f"❌ Failed: {exc}", _build_slider_html(None, None)
211
 
212
  def build_demo():
213
- # 全局大号字体和间距
214
- atmospheric_theme = gr.themes.Soft(
215
- text_size=gr.themes.sizes.text_lg,
216
- spacing_size=gr.themes.sizes.spacing_lg
217
  )
218
 
219
- with gr.Blocks(css=CUSTOM_CSS, title="RealRestorer", theme=atmospheric_theme) as demo:
220
  gr.HTML(TITLE_HTML)
221
 
222
  # ==========================================
223
- # Section 1 & 2: 图像上传与控制参数 (纵向排列)
224
  # ==========================================
225
  with gr.Column(elem_classes=["rr-section"]):
226
- gr.HTML("<div class='section-title'>📥 1. Upload & Settings</div>")
227
 
228
- # 1. 超的图片上传区域
229
- input_image = gr.Image(label="Upload Image", type="pil", height=450)
230
 
231
- # 2. 控制参数横向铺开,充分利用宽度
232
  with gr.Row():
233
  with gr.Column(scale=1):
234
  task_dropdown = gr.Dropdown(choices=list(TASK_PRESETS.keys()), value=DEFAULT_PRESET, label="Task Preset")
235
  with gr.Column(scale=2):
236
- prompt_box = gr.Textbox(label="Instruction(输入指令)", value=TASK_PRESETS[DEFAULT_PRESET], lines=1)
237
 
238
  with gr.Row():
239
  with gr.Column(scale=1):
@@ -243,20 +249,17 @@ def build_demo():
243
  with gr.Column(scale=1):
244
  seed_box = gr.Number(label="Seed (-1 for random)", value=DEFAULT_SEED, precision=0)
245
 
246
- # 3. 占据一整行的巨型运行按钮
247
  run_button = gr.Button("🚀 Run Restoration", elem_id="run-btn")
248
 
249
  # ==========================================
250
- # Section 3: 结果展示 (横跨整个)
251
  # ==========================================
252
  with gr.Column(elem_classes=["rr-section"]):
253
- gr.HTML("<div class='section-title'>🖼️ 2. Restoration Results</div>")
254
 
255
- status_box = gr.Textbox(
256
- label="Status",
257
- value="💡 Ready. Upload an image, adjust settings, and click Run.",
258
- interactive=False,
259
- lines=1
260
  )
261
 
262
  with gr.Tabs():
@@ -264,8 +267,7 @@ def build_demo():
264
  slider_view = gr.HTML(_build_slider_html(None, None))
265
 
266
  with gr.Tab("Output Image"):
267
- # 单纯展示的输出图也给足高度
268
- output_image = gr.Image(label="Restored Result", type="pil", interactive=False, height=700, show_label=False)
269
 
270
  # ==========================================
271
  # Events
@@ -273,17 +275,20 @@ def build_demo():
273
  task_dropdown.change(fn=_on_preset_change, inputs=[task_dropdown], outputs=[prompt_box])
274
 
275
  run_button.click(
276
- fn=lambda: ("⏳ Processing... Please wait.", _build_slider_html(None, None)),
277
  outputs=[status_box, slider_view]
278
  ).then(
279
  fn=run_inference,
280
  inputs=[input_image, task_dropdown, prompt_box, steps_slider, guidance_slider, seed_box],
281
  outputs=[output_image, status_box, slider_view],
 
 
 
 
282
  )
283
 
284
  return demo
285
 
286
  if __name__ == "__main__":
287
  demo = build_demo()
288
- # default_concurrency_limit=1 prevents multiple heavy runs at once
289
  demo.queue(max_size=8, default_concurrency_limit=1).launch(server_name="0.0.0.0", show_error=True)
 
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: 3rem; 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,不干扰 Gradio 原生颜色 ---
60
  CUSTOM_CSS = """
61
+ /* 放宽最大宽度,横向占满 */
62
+ .gradio-container { max-width: 1400px !important; margin: auto !important; padding-top: 30px !important; }
63
 
64
+ /* 卡片容器:使用 Gradio 自带的 CSS 变量,完美适应深浅模式 */
65
  .rr-section {
66
+ background: var(--background-fill-primary) !important;
67
+ border: 1px solid var(--border-color-primary) !important;
68
  border-radius: 16px !important;
69
+ box-shadow: 0 4px 15px rgba(0, 0, 0, 0.05) !important;
70
+ padding: 24px !important;
71
+ margin-bottom: 24px !important;
72
  }
73
 
74
  /* 巨型行动按钮 */
 
76
  background: linear-gradient(135deg, #4f46e5 0%, #6366f1 100%) !important;
77
  color: white !important;
78
  border: none !important;
79
+ font-size: 1.25rem !important;
80
  font-weight: 700 !important;
81
+ padding: 16px !important;
82
  border-radius: 12px !important;
83
+ box-shadow: 0 4px 15px rgba(79, 70, 229, 0.4) !important;
84
+ transition: transform 0.2s, box-shadow 0.2s !important;
85
  margin-top: 10px !important;
86
  }
87
  #run-btn:hover {
88
  transform: translateY(-2px) !important;
89
+ box-shadow: 0 6px 20px rgba(79, 70, 229, 0.5) !important;
90
  }
91
 
92
+ /* 状态栏:使用主变量 */
93
+ .rr-status {
94
+ background: var(--background-fill-secondary) !important;
95
+ border: 1px solid var(--border-color-primary) !important;
96
+ border-radius: 8px !important;
97
+ padding: 12px 16px !important;
98
+ font-size: 1rem !important;
99
+ color: var(--body-text-color) !important;
100
+ margin-bottom: 20px !important;
101
+ font-family: ui-monospace, monospace !important;
102
  }
103
  """
104
 
 
137
  return f"data:image/png;base64,{base64.b64encode(buffer.getvalue()).decode('utf-8')}"
138
 
139
  def _build_slider_html(before_image: Image.Image, after_image: Image.Image) -> str:
140
+ # 巧妙利用 var(--body-text-color) 等变量,适配深色模式
141
  if before_image is None or after_image is None:
142
  return """
143
+ <div style='height: 500px; width: 100%; 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.2rem; background: var(--background-fill-secondary);'>
144
  Upload an image and run to see the full-width comparison here.
145
  </div>
146
  """
 
155
 
156
  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+'%';"
157
 
 
158
  return f"""
159
+ <div style="display: flex; justify-content: center; width: 100%; background: var(--background-fill-secondary); border-radius: 12px; padding: 20px; border: 1px solid var(--border-color-primary); box-sizing: border-box;">
160
+ <div style="position:relative; width:100%; max-height:80vh; aspect-ratio:{width}/{height}; overflow:hidden; border-radius:8px; box-shadow: 0 4px 15px rgba(0,0,0,0.2);">
161
  <!-- 底图: 修复后 -->
162
  <img src="{after_url}" style="position:absolute; top:0; left:0; width:100%; height:100%; object-fit:contain;" draggable="false" />
163
  <!-- 顶图: 原图 -->
164
  <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" />
165
 
166
  <!-- 拖拽线与把手 -->
167
+ <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.5); transform:translateX(-50%); pointer-events:none; z-index:10;">
168
+ <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 2px 10px rgba(0,0,0,0.4); font-weight:bold; color:#1e293b; font-size:1.3rem;">↔</div>
169
  </div>
170
 
171
  <!-- 透明原生滑动条 -->
 
173
  </div>
174
  </div>
175
 
176
+ <div style="display:flex; justify-content:space-between; color:var(--body-text-color-subdued); font-size:1.05rem; padding-top:12px; font-weight:600;">
177
  <span>⬅️ Original</span>
178
  <span>Restored ➡️</span>
179
  </div>
 
216
  return None, f"❌ Failed: {exc}", _build_slider_html(None, None)
217
 
218
  def build_demo():
219
+ # 使纯净的 Soft 主题,交由 Gradio 原生处理深浅色模式的颜色变化
220
+ theme = gr.themes.Soft(
221
+ primary_hue="indigo",
222
+ text_size=gr.themes.sizes.text_lg
223
  )
224
 
225
+ with gr.Blocks(css=CUSTOM_CSS, title="RealRestorer", theme=theme) as demo:
226
  gr.HTML(TITLE_HTML)
227
 
228
  # ==========================================
229
+ # Section 1: 图像上传与控制参数 (纵向排列)
230
  # ==========================================
231
  with gr.Column(elem_classes=["rr-section"]):
232
+ gr.Markdown("### 📥 1. Upload & Settings")
233
 
234
+ # 横向上传
235
+ input_image = gr.Image(label="Upload Image", type="pil", height=420)
236
 
237
+ # 控制参数横向铺开
238
  with gr.Row():
239
  with gr.Column(scale=1):
240
  task_dropdown = gr.Dropdown(choices=list(TASK_PRESETS.keys()), value=DEFAULT_PRESET, label="Task Preset")
241
  with gr.Column(scale=2):
242
+ prompt_box = gr.Textbox(label="Instruction (输入指令)", value=TASK_PRESETS[DEFAULT_PRESET], lines=1)
243
 
244
  with gr.Row():
245
  with gr.Column(scale=1):
 
249
  with gr.Column(scale=1):
250
  seed_box = gr.Number(label="Seed (-1 for random)", value=DEFAULT_SEED, precision=0)
251
 
252
+ # 巨型运行按钮
253
  run_button = gr.Button("🚀 Run Restoration", elem_id="run-btn")
254
 
255
  # ==========================================
256
+ # Section 2: 结果展示 (横跨屏)
257
  # ==========================================
258
  with gr.Column(elem_classes=["rr-section"]):
259
+ gr.Markdown("### 🖼️ 2. Restoration Results")
260
 
261
+ status_box = gr.HTML(
262
+ value="<div class='rr-status'>💡 Status: Ready. Upload an image, adjust settings, and click Run.</div>"
 
 
 
263
  )
264
 
265
  with gr.Tabs():
 
267
  slider_view = gr.HTML(_build_slider_html(None, None))
268
 
269
  with gr.Tab("Output Image"):
270
+ output_image = gr.Image(label="Restored Result", type="pil", interactive=False, height=600, show_label=False)
 
271
 
272
  # ==========================================
273
  # Events
 
275
  task_dropdown.change(fn=_on_preset_change, inputs=[task_dropdown], outputs=[prompt_box])
276
 
277
  run_button.click(
278
+ fn=lambda: ("<div class='rr-status'>Status: Processing... Please wait.</div>", _build_slider_html(None, None)),
279
  outputs=[status_box, slider_view]
280
  ).then(
281
  fn=run_inference,
282
  inputs=[input_image, task_dropdown, prompt_box, steps_slider, guidance_slider, seed_box],
283
  outputs=[output_image, status_box, slider_view],
284
+ ).then(
285
+ fn=lambda status: f"<div class='rr-status'>{status}</div>",
286
+ inputs=[status_box],
287
+ outputs=[status_box]
288
  )
289
 
290
  return demo
291
 
292
  if __name__ == "__main__":
293
  demo = build_demo()
 
294
  demo.queue(max_size=8, default_concurrency_limit=1).launch(server_name="0.0.0.0", show_error=True)