Spaces:
Sleeping
Sleeping
Polish UI: styled header, procedure descriptions, photo tips, cleaner info output
Browse files
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
|
| 557 |
-
f"
|
| 558 |
-
f"
|
| 559 |
-
f"
|
| 560 |
-
f"
|
| 561 |
-
f"
|
| 562 |
-
"",
|
| 563 |
-
f"
|
| 564 |
-
f"
|
| 565 |
-
f"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 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.
|
| 641 |
-
f"
|
| 642 |
-
|
| 643 |
-
|
| 644 |
-
|
| 645 |
-
|
| 646 |
-
|
| 647 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 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.
|
| 845 |
-
f"<div style='text-align:center;color:#
|
| 846 |
-
f"
|
| 847 |
-
f"
|
|
|
|
|
|
|
|
|
|
|
|
|
| 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 · TPS on CPU · "
|
| 914 |
+
f"MediaPipe 478-point mesh · "
|
| 915 |
+
f"<a href='{GITHUB_URL}' style='color:#7eb8f7;'>GitHub</a> · "
|
| 916 |
+
f"MIT License · For research and educational purposes only"
|
| 917 |
+
f"</div>"
|
| 918 |
)
|
| 919 |
|
| 920 |
if __name__ == "__main__":
|