MogensR commited on
Commit
9a9dc8f
·
1 Parent(s): 7930b59

Update ui/ui_components.py

Browse files
Files changed (1) hide show
  1. ui/ui_components.py +0 -343
ui/ui_components.py CHANGED
@@ -1,343 +0,0 @@
1
- #!/usr/bin/env python3
2
- """
3
- UI Components for BackgroundFX Pro (forced Two-Stage)
4
- -----------------------------------------------------
5
- * Pure layout (tiny wrapper to set env for quality)
6
- * All heavy logic stays in ui/callbacks.py
7
- * Two-stage mode is always active (checkbox removed)
8
- """
9
-
10
- from __future__ import annotations
11
- import os
12
- import gradio as gr
13
-
14
- from ui.callbacks import (
15
- cb_load_models,
16
- cb_process_video,
17
- cb_cancel,
18
- cb_status,
19
- cb_clear,
20
- cb_generate_bg,
21
- cb_use_gen_bg,
22
- cb_preset_bg_preview,
23
- )
24
-
25
- # Typography & UI polish: sharper text + cleaner cards
26
- CSS = """
27
- :root {
28
- --radius: 16px;
29
- --font-sans: 'Inter', system-ui, -apple-system, 'Segoe UI', Roboto,
30
- 'Helvetica Neue', Arial, sans-serif;
31
- }
32
-
33
- /* Global crisp text */
34
- html, body, .gradio-container, .gradio-container * {
35
- font-family: var(--font-sans) !important;
36
- -webkit-font-smoothing: antialiased !important;
37
- -moz-osx-font-smoothing: grayscale !important;
38
- text-rendering: optimizeLegibility !important;
39
- font-synthesis-weight: none;
40
- }
41
-
42
- /* Headings tighter & bolder */
43
- .gradio-container h1, .gradio-container h2, .gradio-container h3 {
44
- letter-spacing: -0.01em;
45
- font-weight: 700;
46
- }
47
-
48
- /* Body copy slightly tighter */
49
- #hero .prose, .gr-markdown, .gr-text {
50
- letter-spacing: -0.003em;
51
- }
52
-
53
- /* Card look */
54
- .card {
55
- border-radius: var(--radius);
56
- border: 1px solid rgba(0,0,0,.08);
57
- padding: 16px;
58
- background: linear-gradient(180deg, rgba(255,255,255,.94), rgba(248,250,252,.94));
59
- box-shadow: 0 10px 30px rgba(0,0,0,.06);
60
- }
61
-
62
- .footer-note { opacity: 0.7; font-size: 12px; }
63
- .sm { font-size: 13px; opacity: 0.85; }
64
- #statusbox { min-height: 120px; }
65
- .preview-img { border-radius: var(--radius); border: 1px solid rgba(0,0,0,.08); }
66
-
67
- /* Buttons get a tiny weight bump for clarity */
68
- button, .gr-button {
69
- font-weight: 600;
70
- }
71
- """
72
-
73
- # Keep in sync with utils/cv_processing.PROFESSIONAL_BACKGROUNDS
74
- _BG_CHOICES = [
75
- "minimalist",
76
- "office_modern",
77
- "studio_blue",
78
- "studio_green",
79
- "warm_gradient",
80
- "tech_dark",
81
- ]
82
- PRO_IMAGE_CHOICES = ["minimalist", "office_modern", "studio_blue", "studio_green"]
83
- GRADIENT_COLOR_CHOICES = ["warm_gradient", "tech_dark"]
84
-
85
-
86
- def create_interface() -> gr.Blocks:
87
- with gr.Blocks(
88
- title="🎬 BackgroundFX Pro",
89
- css=CSS,
90
- analytics_enabled=False,
91
- theme=gr.themes.Soft()
92
- ) as demo:
93
-
94
- # ------------------------------------------------------------------
95
- # HERO
96
- # ------------------------------------------------------------------
97
- with gr.Row(elem_id="hero"):
98
- gr.Markdown(
99
- "## 🎬 BackgroundFX Pro (CSP-Safe)\n"
100
- "Replace your video background with cinema-quality AI matting. "
101
- "Built for Hugging Face Spaces CSP.\n\n"
102
- "_Tip: press **Load Models** once after the Space spins up._"
103
- )
104
-
105
- # ------------------------------------------------------------------
106
- # TAB – Quick Start
107
- # ------------------------------------------------------------------
108
- with gr.Tab("🏁 Quick Start"):
109
-
110
- with gr.Row():
111
- # ── Left column ────────────────────────────────────────────
112
- with gr.Column(scale=1):
113
- video = gr.Video(label="Upload Video", interactive=True)
114
-
115
- # Hidden: effective preset key (still used by callbacks / defaults)
116
- bg_style = gr.Dropdown(
117
- label="Background Style (hidden)",
118
- choices=_BG_CHOICES,
119
- value="minimalist",
120
- visible=False,
121
- )
122
-
123
- # =======================
124
- # Background Source block
125
- # =======================
126
- gr.Markdown("### 🖼️ Background Source")
127
-
128
- bg_method = gr.Radio(
129
- label="Choose method",
130
- choices=[
131
- "Upload file",
132
- "Pre-loaded professional images",
133
- "Pre-loaded Gradients / Colors",
134
- "AI generated background",
135
- ],
136
- value="Upload file",
137
- )
138
-
139
- # a) Upload file option
140
- with gr.Group(visible=True) as grp_upload:
141
- custom_bg = gr.Image(
142
- label="Upload Background Image",
143
- interactive=True,
144
- type="filepath", # returns file path
145
- elem_classes=["preview-img"]
146
- )
147
-
148
- # b) Pre-loaded professional images
149
- with gr.Group(visible=False) as grp_pro_images:
150
- pro_image_dd = gr.Dropdown(
151
- label="Professional Images",
152
- choices=PRO_IMAGE_CHOICES,
153
- value=PRO_IMAGE_CHOICES[0],
154
- info="Pre-defined photo-like backgrounds",
155
- )
156
- gr.Markdown(
157
- "<span class='sm'>Selecting a preset updates the preview below.</span>"
158
- )
159
-
160
- # c) Pre-loaded gradients & full colors
161
- with gr.Group(visible=False) as grp_gradients:
162
- gradient_dd = gr.Dropdown(
163
- label="Gradients & Full Colors",
164
- choices=GRADIENT_COLOR_CHOICES,
165
- value=GRADIENT_COLOR_CHOICES[0],
166
- info="Clean gradients or solid color styles",
167
- )
168
- gr.Markdown(
169
- "<span class='sm'>Selecting a preset updates the preview below.</span>"
170
- )
171
-
172
- # d) AI-generated background (inline, lightweight)
173
- with gr.Group(visible=False) as grp_ai:
174
- prompt = gr.Textbox(
175
- label="Describe vibe",
176
- value="modern office",
177
- info="e.g. 'soft sunset studio', 'cool tech dark', 'forest ambience'"
178
- )
179
- with gr.Row():
180
- gen_width = gr.Slider(640, 1920, 1280, step=10, label="Width")
181
- gen_height = gr.Slider(360, 1080, 720, step=10, label="Height")
182
- with gr.Row():
183
- bokeh = gr.Slider(0, 30, 8, step=1, label="Bokeh Blur")
184
- vignette = gr.Slider(0, 0.6, 0.15, step=0.01, label="Vignette")
185
- contrast = gr.Slider(0.8, 1.4, 1.05, step=0.01, label="Contrast")
186
- with gr.Row():
187
- btn_gen_bg_inline = gr.Button("✨ Generate Background", variant="primary")
188
- use_gen_as_custom_inline = gr.Button("📌 Use as Custom Background", variant="secondary")
189
- gen_preview = gr.Image(
190
- label="Generated Background",
191
- interactive=False,
192
- elem_classes=["preview-img"]
193
- )
194
- gen_path = gr.Textbox(label="Saved Path", interactive=False)
195
-
196
- # ── Advanced options accordion ───────────────────────
197
- with gr.Accordion("Advanced", open=False):
198
- chroma_preset = gr.Dropdown(
199
- label="Chroma Preset",
200
- choices=["standard"], # can add 'studio', 'outdoor' later
201
- value="standard"
202
- )
203
- key_color_mode = gr.Dropdown(
204
- label="Key-Colour Mode",
205
- choices=["auto", "green", "blue", "cyan", "magenta"],
206
- value="auto",
207
- info="Auto picks a colour far from your clothes; override if needed."
208
- )
209
- preview_mask = gr.Checkbox(
210
- label="Preview Mask only (mute audio)",
211
- value=False
212
- )
213
- preview_greenscreen = gr.Checkbox(
214
- label="Preview Green-screen only (mute audio)",
215
- value=False
216
- )
217
- # NEW: Quality control (Speed / Balanced / Max)
218
- quality = gr.Dropdown(
219
- label="Quality",
220
- choices=["speed", "balanced", "max"],
221
- value=os.getenv("BFX_QUALITY", "balanced"),
222
- info="Speed = fastest; Max = best edges & spill control."
223
- )
224
-
225
- with gr.Row():
226
- btn_load = gr.Button("🔄 Load Models", variant="secondary")
227
- btn_run = gr.Button("🎬 Process Video", variant="primary")
228
- btn_cancel = gr.Button("⏹️ Cancel", variant="secondary")
229
-
230
- # ── Right column ──────────────────────────────────────────
231
- with gr.Column(scale=1):
232
- out_video = gr.Video(label="Processed Output", interactive=False)
233
- statusbox = gr.Textbox(label="Status", lines=8, elem_id="statusbox")
234
- with gr.Row():
235
- btn_refresh = gr.Button("🔍 Refresh Status", variant="secondary")
236
- btn_clear = gr.Button("🧹 Clear", variant="secondary")
237
-
238
- # ------------------------------------------------------------------
239
- # TAB – Status & settings
240
- # ------------------------------------------------------------------
241
- with gr.Tab("📈 Status & Settings"):
242
- with gr.Row():
243
- with gr.Column(scale=1, elem_classes=["card"]):
244
- model_status = gr.JSON(label="Model Status")
245
- with gr.Column(scale=1, elem_classes=["card"]):
246
- cache_status = gr.JSON(label="Cache / System Status")
247
-
248
- gr.Markdown(
249
- "<div class='footer-note'>If models fail to load, fallbacks keep the UI responsive. "
250
- "Check the runtime log for details.</div>"
251
- )
252
-
253
- # ------------------------------------------------------------------
254
- # Callback wiring
255
- # ------------------------------------------------------------------
256
-
257
- # Toggle which background sub-section is visible
258
- def _toggle_bg_sections(choice: str):
259
- return (
260
- gr.update(visible=(choice == "Upload file")),
261
- gr.update(visible=(choice == "Pre-loaded professional images")),
262
- gr.update(visible=(choice == "Pre-loaded Gradients / Colors")),
263
- gr.update(visible=(choice == "AI generated background")),
264
- )
265
-
266
- bg_method.change(
267
- _toggle_bg_sections,
268
- inputs=[bg_method],
269
- outputs=[grp_upload, grp_pro_images, grp_gradients, grp_ai],
270
- )
271
-
272
- # Load models
273
- btn_load.click(cb_load_models, outputs=statusbox)
274
-
275
- # Tiny wrapper to set env for quality before calling the existing callback
276
- def _run_with_quality(video_pth, bg_style_val, custom_bg_pth,
277
- use_two_stage_state, chroma_p, key_mode,
278
- prev_mask, prev_gs, quality_val):
279
- os.environ["BFX_QUALITY"] = (quality_val or "balanced")
280
- return cb_process_video(
281
- video_pth, bg_style_val, custom_bg_pth,
282
- use_two_stage_state, chroma_p, key_mode, prev_mask, prev_gs
283
- )
284
-
285
- # Always two-stage: pass use_two_stage=True to callback via State
286
- btn_run.click(
287
- _run_with_quality,
288
- inputs=[
289
- video,
290
- bg_style,
291
- custom_bg,
292
- gr.State(value=True), # Always two-stage
293
- chroma_preset,
294
- key_color_mode,
295
- preview_mask,
296
- preview_greenscreen,
297
- quality, # NEW
298
- ],
299
- outputs=[out_video, statusbox],
300
- )
301
-
302
- # Cancel / Status / Clear
303
- btn_cancel.click(cb_cancel, outputs=statusbox)
304
- btn_refresh.click(cb_status, outputs=[model_status, cache_status])
305
-
306
- btn_clear.click(
307
- cb_clear,
308
- outputs=[out_video, statusbox, gen_preview, gen_path, custom_bg]
309
- )
310
-
311
- # Preloaded presets → update preview (write into custom_bg)
312
- pro_image_dd.change(
313
- cb_preset_bg_preview,
314
- inputs=[pro_image_dd],
315
- outputs=[custom_bg],
316
- )
317
- gradient_dd.change(
318
- cb_preset_bg_preview,
319
- inputs=[gradient_dd],
320
- outputs=[custom_bg],
321
- )
322
-
323
- # AI background generation (inline)
324
- btn_gen_bg_inline.click(
325
- cb_generate_bg,
326
- inputs=[prompt, gen_width, gen_height, bokeh, vignette, contrast],
327
- outputs=[gen_preview, gen_path],
328
- )
329
- use_gen_as_custom_inline.click(
330
- cb_use_gen_bg,
331
- inputs=[gen_path],
332
- outputs=[custom_bg],
333
- )
334
-
335
- # Initialize with a default preset preview on load
336
- demo.load(
337
- cb_preset_bg_preview,
338
- inputs=[bg_style],
339
- outputs=[custom_bg]
340
- )
341
-
342
- return demo
343
-