Spaces:
Build error
Build error
File size: 8,651 Bytes
a756962 |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 |
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)
|