import os os.environ['TORCH_FORCE_NO_WEIGHTS_ONLY_LOAD'] = '1' import numpy as np import torch import gradio as gr from PIL import Image import fitz # PyMuPDF import torchxrayvision as xrv from torchvision import transforms import re # --- Device & Model --- DEVICE = torch.device("cuda" if torch.cuda.is_available() else "cpu") MODEL = xrv.models.get_model("densenet121-res224-all").to(DEVICE).eval() LABELS = MODEL.pathologies # --- Extended Medical Information --- DISEASE_INFO = { "Atelectasis": { "description":"Collapse of part or all of a lung, reducing oxygen exchange.", "cause":"Blocked airway, lung compression, post-surgery.", "recommendation":"Deep breathing exercises, possibly bronchoscopy or physiotherapy.", }, "Cardiomegaly": { "description":"Enlargement of the heart, seen as a broad silhouette.", "cause":"High blood pressure, valve disease, cardiomyopathy.", "recommendation":"Echocardiogram, consult a cardiologist.", }, "Consolidation": { "description":"Lung region filled with liquid instead of air.", "cause":"Often from pneumonia (bacterial or viral).", "recommendation":"Consult a physician; likely antibiotics and follow-up chest X-ray.", }, "Edema": { "description":"Fluid accumulation in the lungs.", "cause":"Heart failure, kidney issues.", "recommendation":"Treat underlying cause, may need diuretics, consult cardiology.", }, "Effusion": { "description":"Fluid buildup between lung and chest wall.", "cause":"Infection, heart failure, cancer.", "recommendation":"May need drainage (thoracentesis), see a pulmonologist.", }, "Emphysema": { "description":"Damage and enlargement of lung air sacs (alveoli).", "cause":"Mainly smoking.", "recommendation":"Quit smoking, pulmonary rehab, inhalers.", }, "Fibrosis": { "description":"Scarring of lung tissue, making breathing difficult.", "cause":"Longstanding inflammation, auto-immune disease, occupational exposure.", "recommendation":"Pulmonologist consult, immunosuppression/antifibrotic therapy.", }, "Fracture": { "description":"Break/crack in a bone (commonly ribs).", "cause":"Trauma, accident, fall.", "recommendation":"Pain management, monitor for organ injury, orthopedic consult if severe.", }, "Infiltration": { "description":"Something abnormal (cells/fluid) in the lungs.", "cause":"Most often infection, sometimes inflammation or cancer.", "recommendation":"See physician, further tests to clarify cause.", }, "Mass": { "description":"Lump or growth seen on X-ray.", "cause":"Could be benign or malignant tumor.", "recommendation":"Consult pulmonologist or oncologist, consider CT/biopsy.", }, "Nodule": { "description":"Small round or oval spot in the lung.", "cause":"Old infection, benign, or early cancer.", "recommendation":"May need CT and follow-up scans, discuss with doctor.", }, "Pleural_Thickening": { "description":"Thickening of chest lining.", "cause":"Old infection, asbestos exposure.", "recommendation":"Pulmonology follow-up; rarely needs intervention.", }, "Pneumonia": { "description":"Infection causing inflammation in the lungs.", "cause":"Bacteria, viruses, or fungus.", "recommendation":"Antibiotics/antivirals if needed. Seek prompt medical attention.", }, "Pneumothorax": { "description":"Collapsed lung (air leaks into chest cavity).", "cause":"Trauma, rupture, sometimes spontaneous.", "recommendation":"May need emergency care to remove air; consult ER.", }, # Add more if you want to extend—see LABELS for all possible findings } # --- Recommendations for top-line advice --- ADVICE = { "Pneumonia": "Possible infection. Recommend antibiotics and pulmonology consult.", "Cardiomegaly": "Enlarged heart. Recommend echocardiography and cardiologist review.", "Effusion": "Fluid in lung space. May need thoracentesis.", "Fracture": "Possible bone break. Requires orthopedic consultation.", "Edema": "Pulmonary fluid overload. Evaluate for heart failure.", } def get_advice(label): return ADVICE.get(label, "Please consult a radiologist for further evaluation.") def get_disease_info(label): d = DISEASE_INFO.get(label) if d: return ( f"{label}: {d['description']}
" f"Possible Causes: {d['cause']}
" f"Recommendation: {d['recommendation']}" ) return f"{label}: No extra info available. Please consult a radiologist." # --- Image Preprocessing --- def preprocess_image(pil_img: Image.Image) -> torch.Tensor: """Convert to grayscale, normalize, and resize for model.""" if pil_img.mode != "L": pil_img = pil_img.convert("L") img_array = np.array(pil_img).astype(np.float32) img_array = xrv.datasets.normalize(img_array, 255) img_array = img_array[None, ...] # [1, H, W] img_array = xrv.datasets.XRayCenterCrop()(img_array) img_array = xrv.datasets.XRayResizer(224)(img_array) tensor = torch.from_numpy(img_array).unsqueeze(0).to(DEVICE) return tensor # --- X-ray Analysis (No CAM) --- def analyse_xray(img: Image.Image): try: if img is None: return "Please upload an X-ray image.", None x = preprocess_image(img) with torch.no_grad(): output = MODEL(x) probs = torch.sigmoid(output)[0] * 100 topk = torch.topk(probs, 5) html = "

🩺 Top 5 Predictions

" for idx in topk.indices: label = LABELS[idx] html += ( f"" f"" f"" ) html += "
ConditionConfidenceDetails
{label}{probs[idx]:.1f}%{get_disease_info(label)}
" top_label = LABELS[topk.indices[0].item()] html += f"
Recommended Action for '{top_label}': {get_advice(top_label)}" return html, img.resize((224, 224)) except Exception as e: return f"Error processing image: {str(e)}", None # --- Report PDF Analysis --- def analyse_report(file): try: if file is None: return "Please upload a PDF report." doc = fitz.open(file.name) text = "\n".join(page.get_text() for page in doc) doc.close() found = [] for label in LABELS: if re.search(rf"\b{label.lower()}\b", text.lower()): found.append(label) if found: html = "

📃 Findings Detected in Report:

" else: html = "

No known conditions detected from report text.

" return html except Exception as e: return f"Error processing PDF: {str(e)}" # --- Gradio UI --- with gr.Blocks(title="🩻 RadiologyScan AI") as demo: gr.Markdown( "## 🩻 RadiologyScan AI\n" "Perform fast AI-based analysis of Chest X-rays and medical reports\n" "This tool provides informative summaries for common radiological findings. Not a substitute for a professional medical opinion." ) with gr.Tabs(): with gr.Tab("🔍 X-ray Analysis"): x_input = gr.Image(label="Upload Chest X-ray", type="pil") x_out_html = gr.HTML() x_out_image = gr.Image(label="Resized X-ray (224x224)") analyze_btn = gr.Button("Analyze X-ray") clear_btn = gr.Button("Clear") analyze_btn.click(analyse_xray, inputs=x_input, outputs=[x_out_html, x_out_image]) clear_btn.click(lambda: (None, "", None), None, [x_input, x_out_html, x_out_image]) with gr.Tab("📄 PDF Report Analysis"): pdf_input = gr.File(file_types=[".pdf"], label="Upload PDF Medical Report") pdf_output = gr.HTML() analyze_pdf_btn = gr.Button("Analyze Report") clear_pdf_btn = gr.Button("Clear") analyze_pdf_btn.click(analyse_report, inputs=pdf_input, outputs=pdf_output) clear_pdf_btn.click(lambda: (None, ""), None, [pdf_input, pdf_output]) if __name__ == "__main__": demo.launch(server_port=int(os.getenv("PORT", 7860)), show_error=True)