Spaces:
Sleeping
Sleeping
| 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"<b>{label}</b>: {d['description']}<br>" | |
| f"<b>Possible Causes:</b> {d['cause']}<br>" | |
| f"<b>Recommendation:</b> {d['recommendation']}" | |
| ) | |
| return f"<b>{label}</b>: 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 = "<h3>🩺 Top 5 Predictions</h3><table border='1'><tr><th>Condition</th><th>Confidence</th><th>Details</th></tr>" | |
| for idx in topk.indices: | |
| label = LABELS[idx] | |
| html += ( | |
| f"<tr><td>{label}</td>" | |
| f"<td>{probs[idx]:.1f}%</td>" | |
| f"<td>{get_disease_info(label)}</td></tr>" | |
| ) | |
| html += "</table>" | |
| top_label = LABELS[topk.indices[0].item()] | |
| html += f"<br><b>Recommended Action for '{top_label}':</b> {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 = "<h3>📃 Findings Detected in Report:</h3><ul>" | |
| for label in found: | |
| html += f"<li>{get_disease_info(label)}</li>" | |
| html += "</ul>" | |
| else: | |
| html = "<p>No known conditions detected from report text.</p>" | |
| 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" | |
| "<em>This tool provides informative summaries for common radiological findings. Not a substitute for a professional medical opinion.</em>" | |
| ) | |
| 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) | |