import os import gradio as gr from models.inference import ( run_inference, get_available_model_choices, get_default_model_key, get_model_label, ) from models.metadata_builder import build_metadata_csv GROUP_CHOICES = [ ("Demographics", "demographics"), ("Clinical History", "history"), ("Symptoms", "symptoms"), ("Lesion Geometry", "lesion_geometry"), ] DEFAULT_GROUPS = ["demographics", "symptoms", "lesion_geometry"] REGION_CHOICES = ["HEAD", "NECK", "BACK", "ARM", "LEG", "TORSO"] GENDER_CHOICES = ["MALE", "FEMALE"] MODEL_CHOICES = get_available_model_choices() DEFAULT_MODEL_KEY = get_default_model_key() PAPER_URL = os.environ.get("PAPER_URL", "").strip() PAPER_TITLE = os.environ.get( "PAPER_TITLE", "RG-DermNet: Multimodal Skin Lesion Explainability" ).strip() PAPER_DESCRIPTION = os.environ.get( "PAPER_DESCRIPTION", "This Space accompanies the proposed multimodal framework and allows real-time " "inspection of how clinical metadata affects prediction behavior and GradCAM++ attention maps." ).strip() PAPER_FILE = os.environ.get("PAPER_FILE", "paper.pdf").strip() PAPER_EXISTS = os.path.exists(PAPER_FILE) custom_css = """ .gradio-container { background: linear-gradient(180deg, #08111f 0%, #0b0f19 100%) !important; color: #e8eef8 !important; } .main-shell { max-width: 1280px; margin: 0 auto; } .hero { padding: 28px 24px 18px 24px; border: 1px solid #24364d; border-radius: 18px; background: linear-gradient(135deg, rgba(17, 28, 46, 0.95), rgba(8, 13, 24, 0.95)); margin-bottom: 18px; } .hero h1 { margin: 0 0 10px 0; font-size: 2.1rem; line-height: 1.2; } .hero p { margin: 0; color: #d2dceb; line-height: 1.65; font-size: 1rem; } .badge-row { display: flex; flex-wrap: wrap; gap: 8px; margin-top: 14px; } .badge { padding: 6px 10px; border-radius: 999px; background: #12253f; border: 1px solid #2f527c; color: #beddff; font-size: 0.9rem; } .section-card { border: 1px solid #24364d; border-radius: 18px; background: rgba(11, 18, 31, 0.92); padding: 18px 20px; margin-bottom: 18px; } .section-card h2, .section-card h3 { margin-top: 0; } .paper-card { padding: 16px 18px; border: 1px solid #2d4f7c; border-radius: 14px; background: linear-gradient(135deg, rgba(18, 34, 58, 0.95), rgba(10, 16, 28, 0.95)); } .paper-card h3 { margin: 0 0 8px 0; font-size: 1.05rem; } .paper-card p { margin: 0 0 8px 0; color: #d3deee; line-height: 1.55; } .paper-card a { color: #8bc4ff; text-decoration: none; font-weight: 600; } .paper-card a:hover { text-decoration: underline; } .pipeline-box { padding: 14px; border-radius: 14px; border: 1px solid #2b405e; background: #0d1727; text-align: center; min-height: 120px; display: flex; flex-direction: column; justify-content: center; } .pipeline-box h3 { margin-bottom: 8px; } .pipeline-box p { margin: 0; color: #ced9ea; line-height: 1.5; } .pipeline-arrow { text-align: center; font-size: 1.6rem; color: #8bc4ff; padding-top: 38px; font-weight: 700; } .demo-panel { border: 1px solid #2d3748; padding: 16px; border-radius: 16px; background: #111a29; } .predict-btn { background: #3182ce !important; color: white !important; font-weight: bold !important; border: none !important; } .predict-btn:hover { background: #4299e1 !important; } .soft-text { color: #b9c7da; line-height: 1.6; } .footer-note { font-size: 0.95rem; color: #b7c6db; line-height: 1.6; } .paper-frame-wrap { border: 1px solid #2d4f7c; border-radius: 14px; overflow: hidden; background: #0b1321; } .paper-frame { width: 100%; height: 900px; border: none; background: white; } .muted-divider { opacity: 0.25; margin: 10px 0 14px 0; } """ def build_paper_card(): link_html = "" if PAPER_URL: link_html += ( f'

' f'Open paper link

' ) if PAPER_EXISTS: link_html += '

Open embedded PDF in new tab

' if not link_html: link_html = "

No external paper link configured yet.

" return f"""

๐Ÿ“„ Associated Paper

{PAPER_TITLE}

{PAPER_DESCRIPTION}

{link_html}
""" def build_hero(): return """

๐Ÿ”ฌ RG-DermNet: A Multimodal Attention-Based Model with Residual Block Usage for Skin Lesion Classification

This interactive scientific demo presents a multimodal skin lesion analysis system that combines clinical images and patient metadata to generate predictions and GradCAM++ explanations. The interface allows real-time inspection of how metadata influences model attention and diagnostic behavior.

Clinical Image + Metadata Multimodal Attention GradCAM++ Explainability Interactive Paper Demo
""" def build_paper_embed(): if not PAPER_EXISTS: return """

Paper preview unavailable

The file paper.pdf was not found in the repository root. Add it to enable in-Space preview.

""" return """
""" def format_groups(enabled_groups): if not enabled_groups: return "No metadata group selected." label_map = dict(GROUP_CHOICES) return " | ".join([label_map.get(g, g) for g in enabled_groups]) def safe_bool(value): return bool(value) def build_values_dict(age, gender, region, diameter1, diameter2, itch, grew, hurt, changed, bleed, elevation): return { "age": age, "gender": gender, "region": region, "diameter_1": diameter1, "diameter_2": diameter2, "itch": safe_bool(itch), "grew": safe_bool(grew), "hurt": safe_bool(hurt), "changed": safe_bool(changed), "bleed": safe_bool(bleed), "elevation": safe_bool(elevation), } def build_metadata_preview(enabled_groups, age, gender, region, diameter1, diameter2, itch, grew, hurt, changed, bleed, elevation): values = build_values_dict(age, gender, region, diameter1, diameter2, itch, grew, hurt, changed, bleed, elevation) metadata_csv = build_metadata_csv(values, enabled_groups) groups_text = format_groups(enabled_groups) return metadata_csv, groups_text def validate_inputs(image, enabled_groups, age, diameter1, diameter2): if image is None: raise gr.Error("Please upload a dermoscopic image first.") if not enabled_groups: raise gr.Error("Please select at least one metadata group.") if age is None or age < 0: raise gr.Error("Age must be a valid non-negative number.") if diameter1 is None or diameter1 < 0: raise gr.Error("Diameter 1 must be a valid non-negative number.") if diameter2 is None or diameter2 < 0: raise gr.Error("Diameter 2 must be a valid non-negative number.") def gradio_predict(image, selected_model_key, enabled_groups, age, gender, region, diameter1, diameter2, itch, grew, hurt, changed, bleed, elevation): validate_inputs(image, enabled_groups, age, diameter1, diameter2) values = build_values_dict(age, gender, region, diameter1, diameter2, itch, grew, hurt, changed, bleed, elevation) metadata_csv = build_metadata_csv(values, enabled_groups) try: heatmap_img, prediction_text = run_inference(image, metadata_csv, selected_model_key) except RuntimeError as exc: raise gr.Error(str(exc)) from exc groups_text = format_groups(enabled_groups) model_text = get_model_label(selected_model_key) pretty_prediction = ( f"### ๐Ÿฉบ Prediction Result\n\n" f"**Selected model:** {model_text}\n\n" f"**Active groups:** {groups_text}\n\n" f"**Model output:**\n{prediction_text}" ) return image, heatmap_img, pretty_prediction, metadata_csv, groups_text def clear_all(): default_model = DEFAULT_MODEL_KEY if default_model is None and MODEL_CHOICES: default_model = MODEL_CHOICES[0][1] return ( None, DEFAULT_GROUPS, default_model, 55.0, "FEMALE", "NECK", 6.0, 5.0, False, False, False, False, False, False, None, None, "### Prediction Result\n\nRun the model to see the output here.", "", format_groups(DEFAULT_GROUPS) ) with gr.Blocks( title="Skin Lesion Explainability", theme=gr.themes.Default(primary_hue="blue"), css=custom_css, ) as demo: with gr.Column(elem_classes="main-shell"): gr.HTML(build_hero()) with gr.Group(elem_classes="section-card"): gr.Markdown("## ๐Ÿงช Interactive Demonstration") gr.Markdown( "Modify the metadata, choose a model variant, and inspect how the attention map changes.", elem_classes="soft-text", ) with gr.Row(): with gr.Column(scale=1, elem_classes="demo-panel"): gr.Markdown("### ๐Ÿ“ฅ Input Data") image_input = gr.Image(type="pil", label="Dermoscopic Image", height=320) group_selector = gr.CheckboxGroup( choices=GROUP_CHOICES, value=DEFAULT_GROUPS, label="Enable Metadata Groups" ) model_selector = gr.Dropdown( choices=MODEL_CHOICES, value=DEFAULT_MODEL_KEY if DEFAULT_MODEL_KEY is not None else None, label="Attention Mechanism Model", info="Choose which pretrained attention mechanism/model to run.", ) with gr.Accordion("๐Ÿ‘ค Demographics", open=True): age = gr.Number(label="Age", value=55, precision=0) with gr.Row(): gender = gr.Dropdown(GENDER_CHOICES, value="FEMALE", label="Gender") region = gr.Dropdown(REGION_CHOICES, value="NECK", label="Region") with gr.Accordion("๐Ÿ“ Lesion Geometry", open=False): with gr.Row(): diameter1 = gr.Number(label="Diameter 1", value=6) diameter2 = gr.Number(label="Diameter 2", value=5) with gr.Accordion("๐Ÿšฉ Symptoms", open=False): with gr.Row(): itch = gr.Checkbox(label="Itch") grew = gr.Checkbox(label="Grew") hurt = gr.Checkbox(label="Hurt") with gr.Row(): changed = gr.Checkbox(label="Changed") bleed = gr.Checkbox(label="Bleed") elevation = gr.Checkbox(label="Elevation") with gr.Row(): clear_btn = gr.Button("Clear", variant="secondary") run_btn = gr.Button("Generate GradCAM++", variant="primary", elem_classes="predict-btn") with gr.Column(scale=2, elem_classes="demo-panel"): gr.Markdown("### ๐Ÿ“Š Analysis Dashboard") with gr.Row(): original_img_out = gr.Image(label="Original Lesion", interactive=False) heatmap_out = gr.Image(label="Attention Map (GradCAM++)", interactive=False) with gr.Group(): prediction_out = gr.Markdown( value="### Prediction Result\n\nRun the model to see the output here." ) with gr.Accordion("๐Ÿ“‹ System Metadata Details", open=False): active_groups_text = gr.Textbox(label="Active Groups", interactive=False) metadata_preview = gr.Textbox(label="Final CSV Input", lines=6, interactive=False) with gr.Group(elem_classes="section-card"): gr.Markdown("## ๐Ÿ“š Notes for Readers") gr.Markdown( """ - This demo is intended as a qualitative companion to the paper. - Users can inspect how metadata groups influence model behavior and attention maps. - The available models correspond to pretrained multimodal attention-based variants. - For best scientific use, this Space should be interpreted together with the associated manuscript. """, elem_classes="footer-note", ) gr.Markdown("## ๐Ÿ“Ž Citation and Reproducibility") gr.Markdown( """ If you reference this demo in a paper, thesis, or presentation, cite the associated manuscript and include the Hugging Face Space as supplementary interactive material. BibTeX: @inproceedings{rocha2026rgdermnet, title = {RG-DermNet: A Multimodal Attention-Based Model with Residual Block Usage for Skin Lesion Classification}, author = {Rocha, Wyctor F. and Bouzon, Pedro H. G. and Ramos, Lucas A. and Pacheco, Andre G. C. and Souza Jr., Luis A.}, booktitle = {International Joint Conference on Neural Networks (IJCNN)}, year = {2026}, note = {Accepted} } """, elem_classes="footer-note", ) preview_inputs = [ group_selector, age, gender, region, diameter1, diameter2, itch, grew, hurt, changed, bleed, elevation ] for component in preview_inputs: component.change( fn=build_metadata_preview, inputs=preview_inputs, outputs=[metadata_preview, active_groups_text] ) run_btn.click( fn=gradio_predict, inputs=[image_input, model_selector] + preview_inputs, outputs=[original_img_out, heatmap_out, prediction_out, metadata_preview, active_groups_text] ) clear_btn.click( fn=clear_all, inputs=[], outputs=[ image_input, group_selector, model_selector, age, gender, region, diameter1, diameter2, itch, grew, hurt, changed, bleed, elevation, original_img_out, heatmap_out, prediction_out, metadata_preview, active_groups_text ] )