dennny123 commited on
Commit
1f3d381
·
verified ·
1 Parent(s): 3e5df04

Redesign Gradio UI: dark indigo theme, cleaner input/output layout

Browse files
Files changed (1) hide show
  1. app.py +144 -51
app.py CHANGED
@@ -465,31 +465,98 @@ def reconstruct_scene(
465
 
466
  def _build_startup_markdown() -> str:
467
  if not STARTUP_NOTES:
468
- return (
469
- "Build a 3D scene from a short video. "
470
- "This app uses the upstream LingBot-Map checkpoint and exports a navigable GLB scene plus a downloadable results bundle."
471
- )
472
  return "\n".join([f"- {note}" for note in STARTUP_NOTES])
473
 
474
 
475
  CSS = """
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
476
  .shell {
477
- max-width: 1180px;
478
  margin: 0 auto;
 
479
  }
480
- .headline {
481
- background: linear-gradient(135deg, #f3ead7 0%, #d6e6d4 100%);
482
- border: 1px solid #d9ccb3;
483
- border-radius: 20px;
484
- padding: 20px 24px;
 
 
485
  }
486
- .headline h1 {
487
- margin: 0 0 8px 0;
488
- color: #14231a;
 
 
 
489
  }
490
- .headline p {
491
  margin: 0;
492
- color: #304437;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
493
  }
494
  """
495
 
@@ -497,48 +564,74 @@ CSS = """
497
  _eager_load_default_model()
498
 
499
 
500
- with gr.Blocks(css=CSS, theme=gr.themes.Soft(primary_hue="green", secondary_hue="amber"), title="LingBot 3D") as demo:
501
- gr.Markdown("<div class='shell'>")
502
- with gr.Row():
503
- gr.Image(value=str(ROOT / "assets" / "teaser.png"), show_label=False, interactive=False, min_width=320)
504
- gr.Markdown(
505
- """
506
- <div class="headline">
 
 
 
 
 
 
 
 
507
  <h1>LingBot 3D</h1>
508
- <p>Upload one short video, sample a compact clip, and export a 3D scene you can inspect or download.</p>
509
  </div>
510
- """
511
- )
512
-
513
- gr.Markdown(_build_startup_markdown())
514
 
515
- with gr.Row():
516
- with gr.Column(scale=1):
517
- video_file = gr.File(
518
- label="Upload one short video",
519
- file_types=["video"],
520
- type="filepath",
521
- )
522
- fps = gr.Slider(minimum=1, maximum=12, step=1, value=DEFAULT_FPS, label="Video sampling FPS")
523
- max_frames = gr.Slider(minimum=2, maximum=MAX_FRAMES_HARD_LIMIT, step=1, value=DEFAULT_MAX_FRAMES, label="Max frames")
524
- num_scale_frames = gr.Slider(minimum=1, maximum=8, step=1, value=DEFAULT_SCALE_FRAMES, label="Scale frames")
525
- keyframe_interval = gr.Slider(minimum=1, maximum=8, step=1, value=DEFAULT_KEYFRAME_INTERVAL, label="Keyframe interval")
526
- conf_percentile = gr.Slider(
527
- minimum=0,
528
- maximum=90,
529
- step=5,
530
- value=DEFAULT_CONF_PERCENTILE,
531
- label="GLB confidence percentile",
532
  )
533
- run_button = gr.Button("Build 3D Scene", variant="primary")
534
 
535
- with gr.Column(scale=1):
536
- model_preview = gr.Model3D(label="3D preview", clear_color=[0.97, 0.94, 0.88, 1.0])
537
- preview_image = gr.Image(label="Preview strip", interactive=False)
538
- artifact_file = gr.File(label="Download bundle")
539
- summary_json = gr.JSON(label="Run summary")
 
 
 
 
 
 
 
 
 
 
 
 
540
  status_markdown = gr.Markdown()
541
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
542
  run_button.click(
543
  fn=reconstruct_scene,
544
  inputs=[
@@ -558,7 +651,7 @@ with gr.Blocks(css=CSS, theme=gr.themes.Soft(primary_hue="green", secondary_hue=
558
  ],
559
  show_progress="full",
560
  )
561
- gr.Markdown("</div>")
562
 
563
  demo.queue(default_concurrency_limit=1)
564
 
 
465
 
466
  def _build_startup_markdown() -> str:
467
  if not STARTUP_NOTES:
468
+ return ""
 
 
 
469
  return "\n".join([f"- {note}" for note in STARTUP_NOTES])
470
 
471
 
472
  CSS = """
473
+ :root {
474
+ --lb-bg: #0b0d12;
475
+ --lb-panel: #12151c;
476
+ --lb-panel-2: #161a23;
477
+ --lb-border: #232836;
478
+ --lb-text: #e6e8ee;
479
+ --lb-muted: #8a93a6;
480
+ --lb-accent: #6366f1;
481
+ --lb-accent-2: #22d3ee;
482
+ }
483
+ .gradio-container {
484
+ background: var(--lb-bg) !important;
485
+ color: var(--lb-text) !important;
486
+ font-family: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
487
+ }
488
  .shell {
489
+ max-width: 1200px;
490
  margin: 0 auto;
491
+ padding: 8px 4px 32px;
492
  }
493
+ .hero {
494
+ display: flex;
495
+ flex-direction: column;
496
+ gap: 6px;
497
+ padding: 28px 4px 20px;
498
+ border-bottom: 1px solid var(--lb-border);
499
+ margin-bottom: 20px;
500
  }
501
+ .hero .eyebrow {
502
+ font-size: 11px;
503
+ letter-spacing: 0.14em;
504
+ text-transform: uppercase;
505
+ color: var(--lb-accent-2);
506
+ font-weight: 600;
507
  }
508
+ .hero h1 {
509
  margin: 0;
510
+ font-size: 34px;
511
+ font-weight: 700;
512
+ letter-spacing: -0.02em;
513
+ background: linear-gradient(90deg, #ffffff 0%, #a5b4fc 100%);
514
+ -webkit-background-clip: text;
515
+ -webkit-text-fill-color: transparent;
516
+ background-clip: text;
517
+ }
518
+ .hero p {
519
+ margin: 4px 0 0;
520
+ color: var(--lb-muted);
521
+ font-size: 15px;
522
+ max-width: 640px;
523
+ line-height: 1.5;
524
+ }
525
+ .panel, .gr-panel, .gradio-container .gr-box {
526
+ background: var(--lb-panel) !important;
527
+ border: 1px solid var(--lb-border) !important;
528
+ border-radius: 14px !important;
529
+ }
530
+ .section-title {
531
+ font-size: 12px;
532
+ letter-spacing: 0.1em;
533
+ text-transform: uppercase;
534
+ color: var(--lb-muted);
535
+ font-weight: 600;
536
+ margin: 4px 0 8px 2px;
537
+ }
538
+ button.primary, .gradio-container button.primary {
539
+ background: linear-gradient(135deg, var(--lb-accent) 0%, #8b5cf6 100%) !important;
540
+ border: none !important;
541
+ color: #fff !important;
542
+ font-weight: 600 !important;
543
+ font-size: 15px !important;
544
+ padding: 12px 20px !important;
545
+ border-radius: 10px !important;
546
+ box-shadow: 0 4px 14px rgba(99, 102, 241, 0.35) !important;
547
+ transition: transform 0.15s ease, box-shadow 0.15s ease !important;
548
+ }
549
+ button.primary:hover {
550
+ transform: translateY(-1px);
551
+ box-shadow: 0 6px 20px rgba(99, 102, 241, 0.5) !important;
552
+ }
553
+ .startup-notes {
554
+ font-size: 12px;
555
+ color: var(--lb-muted);
556
+ padding: 8px 12px;
557
+ background: var(--lb-panel-2);
558
+ border-radius: 8px;
559
+ border: 1px solid var(--lb-border);
560
  }
561
  """
562
 
 
564
  _eager_load_default_model()
565
 
566
 
567
+ with gr.Blocks(
568
+ css=CSS,
569
+ theme=gr.themes.Base(
570
+ primary_hue="indigo",
571
+ secondary_hue="cyan",
572
+ neutral_hue="slate",
573
+ font=[gr.themes.GoogleFont("Inter"), "system-ui", "sans-serif"],
574
+ ),
575
+ title="LingBot 3D",
576
+ ) as demo:
577
+ gr.HTML("<div class='shell'>")
578
+ gr.HTML(
579
+ """
580
+ <div class="hero">
581
+ <span class="eyebrow">Video → 3D Reconstruction</span>
582
  <h1>LingBot 3D</h1>
583
+ <p>Upload a short clip and get back a navigable 3D scene. Powered by the LingBot-Map checkpoint, exported as GLB plus a downloadable results bundle.</p>
584
  </div>
585
+ """
586
+ )
 
 
587
 
588
+ startup_md = _build_startup_markdown()
589
+ if startup_md:
590
+ gr.Markdown(startup_md, elem_classes=["startup-notes"])
591
+
592
+ with gr.Row(equal_height=False):
593
+ with gr.Column(scale=5, min_width=360):
594
+ gr.HTML("<div class='section-title'>1. Input</div>")
595
+ video_file = gr.Video(
596
+ label="Short video clip",
597
+ sources=["upload"],
598
+ format="mp4",
599
+ height=260,
 
 
 
 
 
600
  )
 
601
 
602
+ with gr.Accordion("Sampling & reconstruction settings", open=False):
603
+ with gr.Row():
604
+ fps = gr.Slider(minimum=1, maximum=12, step=1, value=DEFAULT_FPS, label="Sampling FPS")
605
+ max_frames = gr.Slider(minimum=2, maximum=MAX_FRAMES_HARD_LIMIT, step=1, value=DEFAULT_MAX_FRAMES, label="Max frames")
606
+ with gr.Row():
607
+ num_scale_frames = gr.Slider(minimum=1, maximum=8, step=1, value=DEFAULT_SCALE_FRAMES, label="Scale frames")
608
+ keyframe_interval = gr.Slider(minimum=1, maximum=8, step=1, value=DEFAULT_KEYFRAME_INTERVAL, label="Keyframe interval")
609
+ conf_percentile = gr.Slider(
610
+ minimum=0,
611
+ maximum=90,
612
+ step=5,
613
+ value=DEFAULT_CONF_PERCENTILE,
614
+ label="GLB confidence percentile",
615
+ info="Higher = fewer, more confident points",
616
+ )
617
+
618
+ run_button = gr.Button("Build 3D Scene", variant="primary", size="lg")
619
  status_markdown = gr.Markdown()
620
 
621
+ with gr.Column(scale=7, min_width=420):
622
+ gr.HTML("<div class='section-title'>2. Output</div>")
623
+ model_preview = gr.Model3D(
624
+ label="3D preview",
625
+ display_mode="point_cloud",
626
+ clear_color=[0.04, 0.05, 0.07, 1.0],
627
+ height=420,
628
+ )
629
+ with gr.Row():
630
+ preview_image = gr.Image(label="Frame preview", interactive=False, height=180)
631
+ artifact_file = gr.File(label="Download results bundle")
632
+ with gr.Accordion("Run summary", open=False):
633
+ summary_json = gr.JSON(label=None)
634
+
635
  run_button.click(
636
  fn=reconstruct_scene,
637
  inputs=[
 
651
  ],
652
  show_progress="full",
653
  )
654
+ gr.HTML("</div>")
655
 
656
  demo.queue(default_concurrency_limit=1)
657