Siyad798 commited on
Commit
a9fb3e7
·
verified ·
1 Parent(s): 4c56761

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +53 -29
app.py CHANGED
@@ -1,13 +1,13 @@
1
  import gradio as gr
2
  import os
3
- import shutil
4
  import zipfile
5
  import torch
6
  import numpy as np
7
  import nibabel as nib
8
  import matplotlib.pyplot as plt
9
- import traceback # <--- Added for debugging
10
- import tempfile # <--- Added for safe temp folders
11
  from monai.networks.nets import SwinUNETR
12
  from monai.inferers import sliding_window_inference
13
  from monai.transforms import (
@@ -17,14 +17,38 @@ from monai.transforms import (
17
  )
18
 
19
  # ==========================================
20
- # 1. CONFIGURATION
21
  # ==========================================
22
- MODEL_FILE = "vision_agent_best.pth"
23
  device = torch.device("cpu")
24
 
25
  print("--- 👁️ Initializing Vision Agent ---")
26
 
27
- # Initialize Model (No img_size, consistent with training)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
28
  model = SwinUNETR(
29
  in_channels=2,
30
  out_channels=2,
@@ -33,17 +57,18 @@ model = SwinUNETR(
33
  spatial_dims=3
34
  ).to(device)
35
 
36
- # Load Weights with Strict Check
37
- if os.path.exists(MODEL_FILE):
38
  try:
 
39
  state_dict = torch.load(MODEL_FILE, map_location=device)
40
  model.load_state_dict(state_dict)
41
  model.eval()
42
  print("✅ Model Loaded Successfully.")
43
  except Exception as e:
44
  print(f"❌ Error loading weights: {e}")
 
45
  else:
46
- print(f"⚠️ WARNING: '{MODEL_FILE}' NOT FOUND. Upload it to the Files tab!")
47
 
48
  # ==========================================
49
  # 2. PREPROCESSING
@@ -59,7 +84,7 @@ preprocess = Compose([
59
  LoadImaged(keys=["image"]),
60
  EnsureChannelFirstd(keys=["image"]),
61
  HandleMissingADCd(keys=["image"]),
62
- # Safety: If input is 4D (Time series), take first volume only
63
  Lambdad(keys=["image"], func=lambda x: x if x.shape[0] == 2 else x[:, :, :, :, 0] if x.ndim==5 else x),
64
  ScaleIntensityd(keys=["image"]),
65
  Orientationd(keys=["image"], axcodes="RAS"),
@@ -90,7 +115,7 @@ def generate_overlay(original_dwi_path, mask_data):
90
  img = nib.load(original_dwi_path)
91
  dwi_vol = img.get_fdata()
92
 
93
- # Orientation check (simple rot90 for display)
94
  dwi_vol = np.rot90(dwi_vol, k=1, axes=(0, 1))
95
  mask_vol = np.rot90(mask_data, k=1, axes=(0, 1))
96
 
@@ -119,35 +144,35 @@ def generate_overlay(original_dwi_path, mask_data):
119
  # 4. INFERENCE ENGINE
120
  # ==========================================
121
  def segment_stroke(zip_file):
122
- # Debug Info
123
  if not zip_file: return {"error": "No file received"}, None
124
 
125
  try:
126
- # 1. Create Unique Temp Dir
127
  with tempfile.TemporaryDirectory() as extract_dir:
128
- # 2. Unzip
129
- with zipfile.ZipFile(zip_file, 'r') as zip_ref:
130
- zip_ref.extractall(extract_dir)
131
-
132
- # 3. Find Data
133
- dwi_path, adc_path = find_sequences(extract_dir)
 
 
134
 
135
  if not dwi_path:
136
  return {"status": "FAILED", "error": "No DWI file found in zip."}, None
137
 
138
- # 4. Prepare
139
  img_list = [dwi_path, adc_path] if adc_path else [dwi_path, dwi_path]
140
  input_data = {"image": img_list, "missing_adc": not bool(adc_path)}
141
 
142
- # 5. Preprocess
143
  tensor_data = preprocess(input_data)["image"].unsqueeze(0).to(device)
144
 
145
- # 6. Inference
146
  with torch.no_grad():
147
  output = model(tensor_data)
148
  pred_mask_small = torch.argmax(output, dim=1).cpu().numpy()[0]
149
 
150
- # 7. Volume Calc (Unsquash)
151
  original_img = nib.load(dwi_path)
152
  resizer = Resize(spatial_size=original_img.shape, mode="nearest")
153
  pred_mask_original = resizer(torch.from_numpy(pred_mask_small).unsqueeze(0))[0].numpy()
@@ -156,7 +181,7 @@ def segment_stroke(zip_file):
156
  voxel_vol_mm3 = np.prod(zooms)
157
  volume_ml = (np.count_nonzero(pred_mask_original) * voxel_vol_mm3) / 1000.0
158
 
159
- # 8. Result
160
  risk = "LOW"
161
  if volume_ml > 70: risk = "HIGH (Futile?)"
162
  elif volume_ml > 50: risk = "MODERATE"
@@ -171,15 +196,14 @@ def segment_stroke(zip_file):
171
  return result, generate_overlay(dwi_path, pred_mask_original)
172
 
173
  except Exception as e:
174
- # CAPTURE THE EXACT ERROR
175
  error_msg = traceback.format_exc()
176
- print(error_msg)
177
- return {"status": "CRASHED", "error": str(e), "traceback": error_msg[-200:]}, None
178
 
179
  # ==========================================
180
- # 5. UI
181
  # ==========================================
182
- with gr.Blocks(theme=gr.themes.Soft()) as demo:
 
183
  gr.Markdown("# 👁️ Neuro-Triage: Vision Agent")
184
 
185
  with gr.Row():
 
1
  import gradio as gr
2
  import os
3
+ import glob
4
  import zipfile
5
  import torch
6
  import numpy as np
7
  import nibabel as nib
8
  import matplotlib.pyplot as plt
9
+ import traceback
10
+ import tempfile
11
  from monai.networks.nets import SwinUNETR
12
  from monai.inferers import sliding_window_inference
13
  from monai.transforms import (
 
17
  )
18
 
19
  # ==========================================
20
+ # 1. MODEL SETUP & WEIGHT EXTRACTION
21
  # ==========================================
 
22
  device = torch.device("cpu")
23
 
24
  print("--- 👁️ Initializing Vision Agent ---")
25
 
26
+ # --- HELPER: Find Model Weights ---
27
+ def locate_model_weights():
28
+ # 1. Look for direct .pth files
29
+ pth_files = glob.glob("*.pth")
30
+ if pth_files: return pth_files[0]
31
+
32
+ # 2. Look inside zips (e.g., if you zipped your model)
33
+ zips = glob.glob("*.zip")
34
+ for z in zips:
35
+ print(f"📦 Scanning zip: {z}")
36
+ try:
37
+ with zipfile.ZipFile(z, 'r') as zf:
38
+ # List files in zip
39
+ for name in zf.namelist():
40
+ if name.endswith(".pth"):
41
+ print(f" -> Found weights in zip: {name}")
42
+ # Extract just the weights
43
+ zf.extract(name, ".")
44
+ return name
45
+ except: pass
46
+
47
+ return None
48
+
49
+ MODEL_FILE = locate_model_weights()
50
+
51
+ # Initialize Model (Swin UNETR - Matches your training)
52
  model = SwinUNETR(
53
  in_channels=2,
54
  out_channels=2,
 
57
  spatial_dims=3
58
  ).to(device)
59
 
60
+ if MODEL_FILE and os.path.exists(MODEL_FILE):
 
61
  try:
62
+ print(f"🔄 Loading weights from: {MODEL_FILE}")
63
  state_dict = torch.load(MODEL_FILE, map_location=device)
64
  model.load_state_dict(state_dict)
65
  model.eval()
66
  print("✅ Model Loaded Successfully.")
67
  except Exception as e:
68
  print(f"❌ Error loading weights: {e}")
69
+ print(" (Are you trying to load nnU-Net/DeepISLES weights? This code expects SwinUNETR weights.)")
70
  else:
71
+ print(f"⚠️ CRITICAL WARNING: No .pth model found. Please upload 'vision_agent_best.pth' to Files.")
72
 
73
  # ==========================================
74
  # 2. PREPROCESSING
 
84
  LoadImaged(keys=["image"]),
85
  EnsureChannelFirstd(keys=["image"]),
86
  HandleMissingADCd(keys=["image"]),
87
+ # Handle 4D inputs if necessary
88
  Lambdad(keys=["image"], func=lambda x: x if x.shape[0] == 2 else x[:, :, :, :, 0] if x.ndim==5 else x),
89
  ScaleIntensityd(keys=["image"]),
90
  Orientationd(keys=["image"], axcodes="RAS"),
 
115
  img = nib.load(original_dwi_path)
116
  dwi_vol = img.get_fdata()
117
 
118
+ # Orientation check
119
  dwi_vol = np.rot90(dwi_vol, k=1, axes=(0, 1))
120
  mask_vol = np.rot90(mask_data, k=1, axes=(0, 1))
121
 
 
144
  # 4. INFERENCE ENGINE
145
  # ==========================================
146
  def segment_stroke(zip_file):
 
147
  if not zip_file: return {"error": "No file received"}, None
148
 
149
  try:
 
150
  with tempfile.TemporaryDirectory() as extract_dir:
151
+ # Handle Zip
152
+ if hasattr(zip_file, 'name') and zip_file.name.endswith('.zip'):
153
+ with zipfile.ZipFile(zip_file, 'r') as zip_ref:
154
+ zip_ref.extractall(extract_dir)
155
+ dwi_path, adc_path = find_sequences(extract_dir)
156
+ # Handle Raw File (if user uploads just one file by mistake)
157
+ else:
158
+ return {"error": "Please upload a .zip file containing the patient scan."}, None
159
 
160
  if not dwi_path:
161
  return {"status": "FAILED", "error": "No DWI file found in zip."}, None
162
 
163
+ # Prepare Input
164
  img_list = [dwi_path, adc_path] if adc_path else [dwi_path, dwi_path]
165
  input_data = {"image": img_list, "missing_adc": not bool(adc_path)}
166
 
167
+ # Preprocess
168
  tensor_data = preprocess(input_data)["image"].unsqueeze(0).to(device)
169
 
170
+ # Inference
171
  with torch.no_grad():
172
  output = model(tensor_data)
173
  pred_mask_small = torch.argmax(output, dim=1).cpu().numpy()[0]
174
 
175
+ # Volume Recalculation (Unsquash)
176
  original_img = nib.load(dwi_path)
177
  resizer = Resize(spatial_size=original_img.shape, mode="nearest")
178
  pred_mask_original = resizer(torch.from_numpy(pred_mask_small).unsqueeze(0))[0].numpy()
 
181
  voxel_vol_mm3 = np.prod(zooms)
182
  volume_ml = (np.count_nonzero(pred_mask_original) * voxel_vol_mm3) / 1000.0
183
 
184
+ # Report
185
  risk = "LOW"
186
  if volume_ml > 70: risk = "HIGH (Futile?)"
187
  elif volume_ml > 50: risk = "MODERATE"
 
196
  return result, generate_overlay(dwi_path, pred_mask_original)
197
 
198
  except Exception as e:
 
199
  error_msg = traceback.format_exc()
200
+ return {"status": "CRASHED", "error": str(e), "details": error_msg[-200:]}, None
 
201
 
202
  # ==========================================
203
+ # 5. GRADIO UI
204
  # ==========================================
205
+ # FIX: Removed 'theme' argument
206
+ with gr.Blocks() as demo:
207
  gr.Markdown("# 👁️ Neuro-Triage: Vision Agent")
208
 
209
  with gr.Row():