Spaces:
Sleeping
Sleeping
| # Repo layout (create these files in your Space) | |
| # ββ app.py β paste this whole file | |
| # ββ requirements.txt β at end of this file | |
| # ββ assets/msk/Gleamer bone view.png, assets/msk/overlay.png β optional demo images | |
| import math | |
| from pathlib import Path | |
| import gradio as gr | |
| import matplotlib.pyplot as plt | |
| # ------------------------------ | |
| # Config: Gleamer Bone View scenario | |
| # ------------------------------ | |
| ASSETS = Path("assets") | |
| MSK_CFG = { | |
| "description": ( | |
| "Gleamer Bone View AI assists in detecting fractures on X-rays, speeding reporting, " | |
| "reducing missed diagnoses, and improving workflow efficiency." | |
| ), | |
| "sample_images": ["msk/Gleamer Bone View.png", "msk/overlay.png"], | |
| "roi_inputs": { | |
| "baseline_volume": 15000, | |
| "avg_rev_per_study": 6000, | |
| "mins_saved_per_study": 5, | |
| "cost_per_rad_hour": 5200, | |
| "baseline_repeats": 750, | |
| "cost_per_repeat": 6000, | |
| "repeat_reduction_pct": 10, | |
| "program_cost_annual": 1200000 | |
| }, | |
| "evidence": [ | |
| "Time savings in fracture detection reported in multi-center settings.", | |
| "Reduced missed fractures through AI-assisted reads.", | |
| "High agreement with subspecialty musculoskeletal radiologists in fracture detection." | |
| ], | |
| "methodology": ( | |
| "Gross benefit = Efficiency savings + Savings from reduced repeats.\n" | |
| "Efficiency savings = (Minutes saved per study / 60) Γ Volume Γ Cost/hour.\n" | |
| "Repeat savings = (Baseline repeats Γ Reduction% Γ Cost per repeat).\n" | |
| "ROI = (Gross benefit β Program cost) / Program cost.\n" | |
| "Payback (months) = Program cost / (Gross benefit / 12), if benefit > 0." | |
| ), | |
| } | |
| # ------------------------------ | |
| # ROI math | |
| # ------------------------------ | |
| def compute_roi(period, baseline_volume, avg_rev_per_study, mins_saved_per_study, | |
| cost_per_rad_hour, baseline_repeats, cost_per_repeat, | |
| repeat_reduction_pct, program_cost_annual): | |
| eff_savings_annual = (mins_saved_per_study / 60.0) * baseline_volume * cost_per_rad_hour | |
| repeats_avoided_annual = baseline_repeats * (repeat_reduction_pct / 100.0) | |
| repeat_savings_annual = repeats_avoided_annual * cost_per_repeat | |
| gross_benefit_annual = eff_savings_annual + repeat_savings_annual | |
| program_cost = program_cost_annual | |
| net_benefit_annual = gross_benefit_annual - program_cost | |
| roi_pct = (net_benefit_annual / program_cost * 100.0) if program_cost else 0.0 | |
| payback_months = (program_cost / (gross_benefit_annual / 12.0)) if gross_benefit_annual > 0 else math.inf | |
| hours_saved_annual = (mins_saved_per_study / 60.0) * baseline_volume | |
| fte_saved_eq = hours_saved_annual / 1920.0 | |
| factor = 1 if period == "Annual" else 1/12 | |
| metrics = { | |
| "Program cost": round(program_cost * factor, 2), | |
| "Efficiency savings": round(eff_savings_annual * factor, 2), | |
| "Repeat savings": round(repeat_savings_annual * factor, 2), | |
| "Gross benefit": round(gross_benefit_annual * factor, 2), | |
| "Net benefit": round(net_benefit_annual * factor, 2), | |
| "ROI % (annualized)": round(roi_pct, 1), | |
| "Payback (months)": (round(payback_months, 1) if payback_months != math.inf else float("inf")), | |
| "Hours saved / year": round(hours_saved_annual, 1), | |
| "FTE saved (β1920h/yr)": round(fte_saved_eq, 2), | |
| "Avg revenue per study (input)": avg_rev_per_study, | |
| } | |
| return metrics | |
| def waterfall_plot(metrics: dict): | |
| fig = plt.figure() | |
| components = ["Efficiency savings", "Repeat savings", "Program cost"] | |
| deltas = [metrics[c] for c in components] | |
| deltas[2] = -abs(deltas[2]) | |
| running = 0 | |
| cumulative = [0] | |
| for d in deltas: | |
| running += d | |
| cumulative.append(running) | |
| for i in range(len(deltas)): | |
| y0, y1 = cumulative[i], cumulative[i+1] | |
| plt.plot([i, i], [y0, y1], linewidth=14) | |
| plt.axhline(0, linewidth=1) | |
| plt.title("Benefit/Cost Waterfall (selected period)") | |
| plt.xlabel("Components") | |
| plt.ylabel("Value") | |
| plt.xticks(range(len(components)), components, rotation=15) | |
| return fig | |
| def init(period): | |
| cfg = MSK_CFG | |
| desc = cfg["description"] | |
| gallery = [str(ASSETS / p) for p in cfg["sample_images"] if (ASSETS / p).exists()] | |
| inputs = cfg["roi_inputs"] | |
| metrics = compute_roi(period, **inputs) | |
| fig = waterfall_plot(metrics) | |
| readout = ( | |
| f"**Gleamer Bone View snapshot** β Net benefit: {metrics['Net benefit']:.0f}; " | |
| f"ROI (annualized): {metrics['ROI % (annualized)']}%; " | |
| f"Payback: {metrics['Payback (months)']} months.\n\n" | |
| f"Operational: {metrics['Hours saved / year']:.0f} hours saved (~{metrics['FTE saved (β1920h/yr)']:.2f} FTE)." | |
| ) | |
| ev_md = "\n".join([f"- {e}" for e in cfg.get('evidence', [])]) or "_Add citations_" | |
| meth_md = cfg.get("methodology", "") | |
| defaults = list(inputs.values()) | |
| return desc, gallery, *defaults, metrics, fig, readout, ev_md, meth_md | |
| def recalc(period, baseline_volume, avg_rev_per_study, mins_saved_per_study, | |
| cost_per_rad_hour, baseline_repeats, cost_per_repeat, | |
| repeat_reduction_pct, program_cost_annual): | |
| metrics = compute_roi(period, baseline_volume, avg_rev_per_study, mins_saved_per_study, | |
| cost_per_rad_hour, baseline_repeats, cost_per_repeat, | |
| repeat_reduction_pct, program_cost_annual) | |
| fig = waterfall_plot(metrics) | |
| readout = ( | |
| f"**Gleamer Bone View snapshot** β Net benefit: {metrics['Net benefit']:.0f}; " | |
| f"ROI (annualized): {metrics['ROI % (annualized)']}%; " | |
| f"Payback: {metrics['Payback (months)']} months.\n\n" | |
| f"Operational: {metrics['Hours saved / year']:.0f} hours saved (~{metrics['FTE saved (β1920h/yr)']:.2f} FTE)." | |
| ) | |
| return metrics, fig, readout | |
| with gr.Blocks(title="Gleamer Bone View ROI β Interactive", fill_height=True) as demo: | |
| gr.Markdown(""" | |
| # Gleamer Bone View ROI β Interactive | |
| **Clinician-first sandbox**: explore a sample fracture detection case, tweak workflow assumptions, and see financial, operational, and clinical impact instantly. | |
| """) | |
| period = gr.Radio(["Annual", "Monthly"], value="Annual", label="Time basis") | |
| with gr.Tabs(): | |
| with gr.Tab("Explore"): | |
| desc = gr.Markdown() | |
| gallery = gr.Gallery(label="Sample case", columns=3, height=320, show_label=True) | |
| with gr.Tab("ROI Simulator"): | |
| with gr.Row(): | |
| with gr.Column(scale=1, min_width=360): | |
| baseline_volume = gr.Slider(0, 100000, value=15000, step=50, label="Annual volume") | |
| avg_rev_per_study = gr.Slider(0, 50000, value=6000, step=50, label="Avg revenue per study (info)") | |
| mins_saved_per_study = gr.Slider(0, 60, value=5, step=1, label="Minutes saved per study") | |
| cost_per_rad_hour = gr.Slider(0, 20000, value=5200, step=50, label="Radiologist cost/hour") | |
| baseline_repeats = gr.Slider(0, 100000, value=750, step=10, label="Baseline repeats/year") | |
| cost_per_repeat = gr.Slider(0, 50000, value=6000, step=50, label="Cost per repeat") | |
| repeat_reduction_pct = gr.Slider(0, 100, value=10, step=1, label="Repeat reduction with AI (%)") | |
| program_cost_annual = gr.Slider(0, 10000000, value=1200000, step=10000, label="Program cost (annual)") | |
| with gr.Column(scale=1): | |
| metrics = gr.JSON(label="Outputs") | |
| chart = gr.Plot(label="Waterfall") | |
| readout = gr.Markdown(label="Executive readout") | |
| with gr.Tab("Evidence"): | |
| evidence_md = gr.Markdown() | |
| with gr.Tab("Methodology"): | |
| methodology_md = gr.Markdown() | |
| period.change(init, [period], [desc, gallery, baseline_volume, avg_rev_per_study, mins_saved_per_study, | |
| cost_per_rad_hour, baseline_repeats, cost_per_repeat, repeat_reduction_pct, program_cost_annual, | |
| metrics, chart, readout, evidence_md, methodology_md]) | |
| for comp in [baseline_volume, avg_rev_per_study, mins_saved_per_study, cost_per_rad_hour, | |
| baseline_repeats, cost_per_repeat, repeat_reduction_pct, program_cost_annual]: | |
| comp.change(recalc, [period, baseline_volume, avg_rev_per_study, mins_saved_per_study, | |
| cost_per_rad_hour, baseline_repeats, cost_per_repeat, repeat_reduction_pct, program_cost_annual], | |
| [metrics, chart, readout]) | |
| demo.load(init, [period], [desc, gallery, baseline_volume, avg_rev_per_study, mins_saved_per_study, | |
| cost_per_rad_hour, baseline_repeats, cost_per_repeat, repeat_reduction_pct, program_cost_annual, | |
| metrics, chart, readout, evidence_md, methodology_md]) | |
| if __name__ == "__main__": | |
| demo.launch() | |
| # ------------------------------ | |
| # requirements.txt | |
| # ------------------------------ | |
| # gradio>=4.44.0 | |
| # matplotlib |