dreamlessx commited on
Commit
1654955
·
verified ·
1 Parent(s): 5c6d1cb

Polish UI: styled header, procedure descriptions, photo tips, cleaner info output

Browse files
Files changed (1) hide show
  1. app.py +96 -26
app.py CHANGED
@@ -552,17 +552,26 @@ def process_image(image_rgb, procedure, intensity):
552
  post_overall, post_regions = compute_symmetry_score(manipulated)
553
  sym_delta = post_overall - pre_overall
554
 
 
555
  info_lines = [
556
- f"Procedure: {procedure.replace('_', ' ').title()}",
557
- f"Intensity: {intensity:.0f}%",
558
- f"Landmarks: {len(face.landmarks)}",
559
- f"Avg displacement: {displacement:.1f} px",
560
- f"Confidence: {face.confidence:.2f}",
561
- f"Time: {elapsed:.2f}s | Mode: TPS (CPU)",
562
- "",
563
- f"Symmetry (before): {pre_overall:.1f}/100",
564
- f"Symmetry (after): {post_overall:.1f}/100",
565
- f"Symmetry change: {sym_delta:+.1f}",
 
 
 
 
 
 
 
 
566
  ]
567
  info = "\n".join(info_lines)
568
  return wireframe_rgb, mask_vis, composited_rgb, image_rgb_512, info
@@ -633,18 +642,50 @@ _proc_table = "\n".join(
633
  f"| {name.replace('_', ' ').title()} | {desc} |" for name, desc in PROCEDURE_INFO.items()
634
  )
635
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
636
  with gr.Blocks(
637
- title="LandmarkDiff",
638
  theme=gr.themes.Soft(),
 
639
  ) as demo:
640
- gr.Markdown(
641
- f"# LandmarkDiff\n\n"
642
- f"Facial surgery outcome prediction from clinical photography. "
643
- f"Upload a face photo, pick a procedure, adjust intensity.\n\n"
644
- f"| Procedure | Effect |\n|---|---|\n{_proc_table}\n\n"
645
- f"[GitHub]({GITHUB_URL}) | "
646
- f"[Docs]({GITHUB_URL}/tree/main/docs) | "
647
- f"[Wiki]({GITHUB_URL}/wiki)"
 
 
 
 
 
 
 
 
 
 
 
648
  )
649
 
650
  # -- Tab 1: Single Procedure --
@@ -656,6 +697,16 @@ with gr.Blocks(
656
  choices=PROCEDURES,
657
  value="rhinoplasty",
658
  label="Procedure",
 
 
 
 
 
 
 
 
 
 
659
  )
660
  intensity = gr.Slider(
661
  0,
@@ -663,10 +714,15 @@ with gr.Blocks(
663
  50,
664
  step=1,
665
  label="Intensity (%)",
666
- info="0 = no change, 100 = maximum",
 
 
 
 
 
 
 
667
  )
668
- run_btn = gr.Button("Generate", variant="primary", size="lg")
669
- info_box = gr.Textbox(label="Info", lines=10, interactive=False)
670
 
671
  with gr.Column(scale=2):
672
  with gr.Row():
@@ -683,6 +739,16 @@ with gr.Blocks(
683
  label="Example faces (click to load)",
684
  )
685
 
 
 
 
 
 
 
 
 
 
 
686
  outputs = [out_wireframe, out_mask, out_result, out_original, info_box]
687
  _inputs = [input_image, procedure, intensity]
688
  run_btn.click(fn=process_image, inputs=_inputs, outputs=outputs)
@@ -841,10 +907,14 @@ with gr.Blocks(
841
  outputs=[sym_pre_overlay, sym_post_overlay, sym_cmp_box],
842
  )
843
 
844
- gr.Markdown(
845
- f"<div style='text-align:center;color:#999;font-size:0.8em;padding:8px'>"
846
- f"LandmarkDiff v0.2.2 | TPS on CPU | MediaPipe 478-point mesh | "
847
- f"<a href='{GITHUB_URL}'>GitHub</a> | MIT License</div>"
 
 
 
 
848
  )
849
 
850
  if __name__ == "__main__":
 
552
  post_overall, post_regions = compute_symmetry_score(manipulated)
553
  sym_delta = post_overall - pre_overall
554
 
555
+ sym_arrow = "+" if sym_delta > 0 else ""
556
  info_lines = [
557
+ f"--- Procedure ---",
558
+ f" Type: {procedure.replace('_', ' ').title()}",
559
+ f" Intensity: {intensity:.0f}%",
560
+ f" Description: {PROCEDURE_INFO.get(procedure, '')}",
561
+ f"",
562
+ f"--- Detection ---",
563
+ f" Landmarks: {len(face.landmarks)} points",
564
+ f" Confidence: {face.confidence:.2f}",
565
+ f" Avg shift: {displacement:.1f} px",
566
+ f"",
567
+ f"--- Symmetry ---",
568
+ f" Before: {pre_overall:.1f} / 100",
569
+ f" After: {post_overall:.1f} / 100",
570
+ f" Change: {sym_arrow}{sym_delta:.1f}",
571
+ f"",
572
+ f"--- Performance ---",
573
+ f" Time: {elapsed:.2f}s",
574
+ f" Mode: TPS (CPU)",
575
  ]
576
  info = "\n".join(info_lines)
577
  return wireframe_rgb, mask_vis, composited_rgb, image_rgb_512, info
 
642
  f"| {name.replace('_', ' ').title()} | {desc} |" for name, desc in PROCEDURE_INFO.items()
643
  )
644
 
645
+ _CSS = """
646
+ .header-banner {
647
+ background: linear-gradient(135deg, #1a1a2e 0%, #16213e 50%, #0f3460 100%);
648
+ border-radius: 12px;
649
+ padding: 24px 32px;
650
+ margin-bottom: 8px;
651
+ color: white;
652
+ }
653
+ .header-banner h1 { color: white !important; margin-bottom: 4px; font-size: 2em; }
654
+ .header-banner p { color: #ccd; margin: 4px 0; font-size: 0.95em; }
655
+ .header-banner a { color: #7eb8f7; text-decoration: none; }
656
+ .header-banner a:hover { text-decoration: underline; }
657
+ .link-bar { display: flex; gap: 16px; margin-top: 10px; flex-wrap: wrap; }
658
+ .info-output textarea {
659
+ font-family: 'SF Mono', 'Fira Code', 'Consolas', monospace !important;
660
+ font-size: 0.88em !important;
661
+ line-height: 1.6 !important;
662
+ }
663
+ """
664
+
665
  with gr.Blocks(
666
+ title="LandmarkDiff -- Facial Surgery Prediction",
667
  theme=gr.themes.Soft(),
668
+ css=_CSS,
669
  ) as demo:
670
+ gr.HTML(
671
+ f"""<div class="header-banner">
672
+ <h1>LandmarkDiff</h1>
673
+ <p>
674
+ Anatomically-conditioned facial surgery outcome prediction from standard clinical
675
+ photography. Upload a face photo, select a procedure, adjust intensity, and see
676
+ the predicted result in real time.
677
+ </p>
678
+ <p style="font-size:0.85em; color:#aab;">
679
+ Powered by MediaPipe 478-point face mesh, thin-plate spline warping, and
680
+ procedure-specific anatomical displacement models. Runs entirely on CPU.
681
+ </p>
682
+ <div class="link-bar">
683
+ <a href="{GITHUB_URL}">GitHub</a>
684
+ <a href="{GITHUB_URL}/tree/main/docs">Documentation</a>
685
+ <a href="{GITHUB_URL}/wiki">Wiki</a>
686
+ <a href="{GITHUB_URL}/discussions">Discussions</a>
687
+ </div>
688
+ </div>"""
689
  )
690
 
691
  # -- Tab 1: Single Procedure --
 
697
  choices=PROCEDURES,
698
  value="rhinoplasty",
699
  label="Procedure",
700
+ info="Select a surgical procedure to simulate",
701
+ )
702
+ # Show a brief description for each procedure
703
+ _proc_desc_md = " | ".join(
704
+ f"**{k.replace('_', ' ').title()}**: {v}"
705
+ for k, v in PROCEDURE_INFO.items()
706
+ )
707
+ gr.Markdown(
708
+ f"<div style='font-size:0.82em;color:#666;line-height:1.5;'>"
709
+ f"{_proc_desc_md}</div>"
710
  )
711
  intensity = gr.Slider(
712
  0,
 
714
  50,
715
  step=1,
716
  label="Intensity (%)",
717
+ info="0 = no change, 100 = maximum effect",
718
+ )
719
+ run_btn = gr.Button("Generate Prediction", variant="primary", size="lg")
720
+ info_box = gr.Textbox(
721
+ label="Results",
722
+ lines=10,
723
+ interactive=False,
724
+ elem_classes=["info-output"],
725
  )
 
 
726
 
727
  with gr.Column(scale=2):
728
  with gr.Row():
 
739
  label="Example faces (click to load)",
740
  )
741
 
742
+ with gr.Accordion("Photo Tips for Best Results", open=False):
743
+ gr.Markdown(
744
+ "- **Front-facing**: Use a straight-on frontal photo, not a side profile\n"
745
+ "- **Good lighting**: Even, natural lighting works best. Avoid harsh shadows\n"
746
+ "- **Neutral expression**: Keep a relaxed, neutral face for accurate landmark detection\n"
747
+ "- **No obstructions**: Remove glasses, hats, or anything covering the face\n"
748
+ "- **Resolution**: At least 256x256 pixels. The image will be resized to 512x512 internally\n"
749
+ "- **Single face**: Make sure only one face is clearly visible in the frame"
750
+ )
751
+
752
  outputs = [out_wireframe, out_mask, out_result, out_original, info_box]
753
  _inputs = [input_image, procedure, intensity]
754
  run_btn.click(fn=process_image, inputs=_inputs, outputs=outputs)
 
907
  outputs=[sym_pre_overlay, sym_post_overlay, sym_cmp_box],
908
  )
909
 
910
+ gr.HTML(
911
+ f"<div style='text-align:center;color:#888;font-size:0.78em;padding:12px 8px;"
912
+ f"border-top:1px solid #eee;margin-top:12px;'>"
913
+ f"LandmarkDiff v0.2.2 &middot; TPS on CPU &middot; "
914
+ f"MediaPipe 478-point mesh &middot; "
915
+ f"<a href='{GITHUB_URL}' style='color:#7eb8f7;'>GitHub</a> &middot; "
916
+ f"MIT License &middot; For research and educational purposes only"
917
+ f"</div>"
918
  )
919
 
920
  if __name__ == "__main__":