import os import sys import cv2 import numpy as np import gradio as gr from PIL import Image # Ensure backend directory and root directory are in sys.path base_dir = os.path.dirname(os.path.abspath(__file__)) backend_dir = os.path.join(base_dir, "backend") if os.path.exists(backend_dir) and backend_dir not in sys.path: sys.path.insert(0, backend_dir) if base_dir not in sys.path: sys.path.insert(0, base_dir) try: import spaces except ImportError: class spaces: @staticmethod def GPU(func): return func try: from app.services.ai_pipeline import ai_pipeline_service from app.kb import kb_service except ModuleNotFoundError: from backend.app.services.ai_pipeline import ai_pipeline_service from backend.app.kb import kb_service # Custom Dark Theme CSS for Gradio custom_css = """ body, .gradio-container { background-color: #0b0f19 !important; color: #e2e8f0 !important; font-family: 'Inter', system-ui, sans-serif !important; } .header-box { text-align: center; padding: 24px; background: linear-gradient(135deg, rgba(30, 41, 59, 0.8), rgba(15, 23, 42, 0.9)); border: 1px solid rgba(255, 255, 255, 0.1); border-radius: 16px; margin-bottom: 24px; box-shadow: 0 10px 30px rgba(0,0,0,0.5); } .header-box h1 { font-size: 2.2rem; font-weight: 800; background: linear-gradient(90deg, #38bdf8, #818cf8, #c084fc); -webkit-background-clip: text; -webkit-text-fill-color: transparent; margin-bottom: 8px; } .badge-normal { background-color: #10b981; color: white; padding: 4px 12px; border-radius: 9999px; font-weight: bold; } .badge-severe { background-color: #ef4444; color: white; padding: 4px 12px; border-radius: 9999px; font-weight: bold; } .badge-moderate { background-color: #f59e0b; color: white; padding: 4px 12px; border-radius: 9999px; font-weight: bold; } """ @spaces.GPU def analyze_scan(image_input): if image_input is None: return None, None, "
{res.get('error', 'Unknown error')}
" return None, None, error_msg, res modality = res.get("image_type", "Unknown") disease = res.get("disease", "Normal") confidence = res.get("confidence", 0.0) severity = res.get("severity", "Normal") findings = res.get("findings", {}) evidence = res.get("evidence", {}) heatmap_path = res.get("heatmap_filepath") mask_path = os.path.join(os.path.dirname(heatmap_path), f"mask_{res.get('report_id')}.png") if heatmap_path else None heatmap_img = Image.open(heatmap_path) if heatmap_path and os.path.exists(heatmap_path) else None mask_img = Image.open(mask_path) if mask_path and os.path.exists(mask_path) else None # Format HTML / Markdown Diagnostic Report conf_pct = f"{confidence * 100:.1f}%" severity_badge = f"{severity}" report_md = f""" ## đŠē Clinical Diagnostic Report `#${res.get('report_id')}` | Metric | Value | |---|---| | **Detected Modality** | **`{modality}`** *(Auto-Detected)* | | **Primary Diagnosis** | **`{disease}`** | | **Certainty Confidence** | **`{conf_pct}`** | | **Severity Index** | {severity_badge} | --- ### đŦ **Clinical Findings** > **Summary**: {findings.get('summary', 'No findings summary available.')} **Observation Details**: """ for detail in findings.get("details", []): report_md += f"- {detail}\n" if evidence and disease != "Normal": report_md += f""" --- ### đ **Evidence-Based Guidelines & Recommendations** - **Overview**: {evidence.get('overview', 'N/A')} - **Key Manifestations**: {", ".join(evidence.get('clinical_manifestations', []))} - **Recommended Action**: """ for rec in evidence.get("recommendations", []): report_md += f" 1. {rec}\n" report_md += "\n\n*Disclaimer: MedVision AI output is intended for clinical decision support and researcher validation.*" return heatmap_img, mask_img, report_md, res def get_kb_info(disease_name): if not disease_name: return "Select a disease to view clinical evidence guidelines." info = kb_service.retrieve_info(disease_name) md = f"## đ Medical Knowledge Base: **{disease_name}**\n\n" md += f"**Overview**: {info.get('overview', 'N/A')}\n\n" md += f"**ICD-10 Code**: `{info.get('icd_10', 'N/A')}` | **Modality**: `{info.get('modality', 'N/A')}`\n\n" md += "### Clinical Manifestations:\n" for item in info.get("clinical_manifestations", []): md += f"- {item}\n" md += "\n### Recommended Clinical Workup:\n" for item in info.get("recommendations", []): md += f"1. {item}\n" return md # Build Gradio UI Blocks with gr.Blocks(theme=gr.themes.Soft(primary_hue="indigo"), css=custom_css, title="MedVision AI Diagnostic Engine") as demo: gr.HTML("""Instant Auto-Modality Routing (Chest X-Ray âĸ Brain MRI âĸ Eye Fundus) with GradCAM & Contour Explainability