mmrech commited on
Commit
b4dcf94
·
1 Parent(s): 206cb4c

Complete NeuroSAM 3 app - use HF_TOKEN environment variable

Browse files
Files changed (3) hide show
  1. README.md +54 -5
  2. app.py +217 -170
  3. requirements.txt +10 -0
README.md CHANGED
@@ -1,7 +1,56 @@
1
  ---
2
- title: NeuroSAM3
3
- app_file: app.py
 
 
4
  sdk: gradio
5
- sdk_version: 6.0.2
6
- pinned: true
7
- ---
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
  ---
2
+ title: NeuroSAM 3
3
+ emoji: 🏥
4
+ colorFrom: blue
5
+ colorTo: purple
6
  sdk: gradio
7
+ sdk_version: 4.0.0
8
+ app_file: app.py
9
+ pinned: false
10
+ license: apache-2.0
11
+ ---
12
+
13
+ # NeuroSAM 3: Medical Image Segmentation
14
+
15
+ A medical image segmentation application using SAM 3 (Segment Anything Model 3) for DICOM file analysis.
16
+
17
+ ## Features
18
+
19
+ - 🧠 **SAM 3 Integration**: Uses the latest Segment Anything Model 3 for medical image segmentation
20
+ - 📁 **DICOM Support**: Process CT and MRI DICOM files
21
+ - 🎯 **Text Prompts**: Describe what you want to segment (e.g., "brain", "tumor", "skull")
22
+ - ⚙️ **Windowing Strategies**: Optimized windowing presets for CT images
23
+ - 🎨 **Visualization**: Overlay segmentation masks on medical images
24
+
25
+ ## Usage
26
+
27
+ 1. Upload a DICOM (.dcm) file
28
+ 2. Enter a text prompt describing what to segment
29
+ 3. Select the imaging modality (CT or MRI)
30
+ 4. Choose the windowing strategy (for CT images)
31
+ 5. Click "Segment Structure" to process
32
+
33
+ ## Requirements
34
+
35
+ - Python 3.8+
36
+ - PyTorch
37
+ - Gradio
38
+ - PyDICOM
39
+ - Transformers (with SAM 3 support)
40
+
41
+ ## Model
42
+
43
+ This app uses the SAM 3 model from Facebook/Meta. You need:
44
+ - A Hugging Face account
45
+ - Access token with read permissions for the SAM 3 model
46
+ - Set `HF_TOKEN` environment variable in Space settings
47
+
48
+ ## Public Datasets
49
+
50
+ - UniqueData/dicom-brain-dataset (Hugging Face) - MRI Brain scans
51
+ - The Cancer Imaging Archive (TCIA) - Various medical imaging
52
+ - Imaging Data Commons (IDC) - Large collection of DICOM files
53
+
54
+ ## License
55
+
56
+ Apache 2.0
app.py CHANGED
@@ -1,3 +1,8 @@
 
 
 
 
 
1
  import os
2
  import tempfile
3
  import gradio as gr
@@ -8,97 +13,152 @@ from PIL import Image
8
  from transformers import Sam3Processor, Sam3Model
9
  import matplotlib.pyplot as plt
10
 
11
- def process_medical_image(dicom_file, prompt_text, modality, window_type, model, processor, device):
12
- """
13
- Process a DICOM medical image and perform segmentation using SAM 3.
14
-
15
- Args:
16
- dicom_file: Path to DICOM file (from Gradio File component)
17
- prompt_text: Text prompt describing what to segment
18
- modality: "CT" or "MRI"
19
- window_type: Windowing strategy for CT images
20
- model: The loaded SAM 3 model
21
- processor: The loaded SAM 3 processor
22
- device: The device (cpu/cuda) the model is on
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
23
 
24
- Returns:
25
- Path to output image with segmentation overlay, or None on error
26
- """
27
- # Check if model is loaded
28
  if model is None or processor is None:
29
- print("❌ Error: Model not loaded. Please check the model loading section.")
30
  return None
31
-
32
- # Validate inputs
33
  if dicom_file is None:
34
  return None
35
-
36
  if not prompt_text or not prompt_text.strip():
37
- print("⚠️ Warning: Empty prompt text. Using default 'brain'.")
38
  prompt_text = "brain"
39
-
40
  try:
41
- # --- A. Read DICOM ---
42
- # Gradio File component returns a file path string
43
  dicom_path = dicom_file if isinstance(dicom_file, str) else str(dicom_file)
44
-
45
  if not os.path.exists(dicom_path):
46
  print(f"❌ Error: DICOM file not found at {dicom_path}")
47
  return None
48
-
49
  ds = pydicom.dcmread(dicom_path)
50
-
51
- # Check if pixel data exists
52
  if not hasattr(ds, 'pixel_array'):
53
  print("❌ Error: DICOM file does not contain pixel data.")
54
  return None
55
-
56
  raw = ds.pixel_array.astype(np.float32)
57
  slope = getattr(ds, 'RescaleSlope', 1)
58
  intercept = getattr(ds, 'RescaleIntercept', 0)
59
  img_hu = raw * slope + intercept
60
 
61
- # --- B. Apply Windowing ---
62
  if modality == "CT":
63
  if window_type == "Brain (Grey Matter)":
64
  level, width = 40, 80
65
  elif window_type == "Bone (Skull)":
66
  level, width = 500, 2000
67
- else: # Soft Tissue / Face
68
  level, width = 40, 400
69
  img_min = level - (width / 2)
70
  img_max = level + (width / 2)
71
- else: # MRI (Percentile based)
72
  img_min = np.percentile(img_hu, 1)
73
  img_max = np.percentile(img_hu, 99)
74
 
75
- # Handle division by zero
76
  img_range = img_max - img_min
77
  if img_range <= 0:
78
- print("⚠️ Warning: Invalid image range. Using full range.")
79
  img_min = np.min(img_hu)
80
  img_max = np.max(img_hu)
81
  img_range = img_max - img_min
82
  if img_range <= 0:
83
- print("❌ Error: Cannot process image with zero range.")
84
  return None
85
 
86
  img_windowed = (img_hu - img_min) / img_range
87
  img_windowed = np.clip(img_windowed, 0, 1)
88
-
89
- # Convert to RGB for SAM 3
90
  img_uint8 = (img_windowed * 255).astype(np.uint8)
91
-
92
- # Handle grayscale to RGB conversion
93
  if len(img_uint8.shape) == 2:
94
  pil_image = Image.fromarray(img_uint8).convert('RGB')
95
  else:
96
  pil_image = Image.fromarray(img_uint8)
97
 
98
- # --- C. Run SAM 3 Inference ---
99
  try:
100
  inputs = processor(images=pil_image, text=prompt_text, return_tensors="pt").to(device)
101
-
102
  with torch.no_grad():
103
  outputs = model(**inputs)
104
 
@@ -108,159 +168,146 @@ def process_medical_image(dicom_file, prompt_text, modality, window_type, model,
108
  except Exception as e:
109
  print(f"❌ Error during model inference: {e}")
110
  return None
111
-
112
- # --- D. Draw Masks on Image ---
113
- # Create a figure to plot the result
114
  plt.figure(figsize=(10, 10))
115
  plt.imshow(pil_image)
116
-
117
- # Check if masks exist in results
118
  if 'masks' in results and results['masks'] is not None:
119
  masks = results['masks'].cpu().numpy()
120
  if len(masks) > 0:
121
  final_mask = np.any(masks, axis=0)
122
- plt.imshow(final_mask, alpha=0.5, cmap='spring') # Overlay mask
123
  else:
124
  print("⚠️ Warning: No masks found in segmentation results.")
125
  else:
126
- print("⚠️ Warning: No masks in results. Showing original image.")
127
-
128
  plt.axis('off')
129
  plt.title(f"Segmentation: {prompt_text}", fontsize=12, pad=10)
130
-
131
- # Save plot to a temporary file with unique name
132
  output_file = tempfile.NamedTemporaryFile(delete=False, suffix='.png')
133
  output_path = output_file.name
134
  output_file.close()
135
-
136
  plt.savefig(output_path, bbox_inches='tight', pad_inches=0, dpi=100)
137
  plt.close()
138
-
139
  return output_path
140
-
141
- except pydicom.errors.InvalidDicomError as e:
142
- print(f"❌ Error: Invalid DICOM file format. {e}")
143
- return None
144
  except Exception as e:
145
  print(f"❌ Error processing image: {e}")
146
  import traceback
147
  traceback.print_exc()
148
  return None
149
 
150
- def create_gradio_interface(model_instance, processor_instance, device_instance, demo_file_path=None):
151
- demo_file_available = False
152
- if demo_file_path and os.path.exists(demo_file_path):
153
- demo_file_available = True
154
 
155
- with gr.Blocks(theme=gr.themes.Soft()) as demo:
156
- gr.Markdown("# 🏥 NeuroSAM 3: Medical Image Segmentation")
157
-
158
- demo_info = ""
159
- if demo_file_available and demo_file_path:
160
- demo_info = f"\n\n**📁 Demo File Available:** A sample DICOM file is ready: `{demo_file_path}`\nClick 'Load Demo File' button below to use it!"
161
-
162
- gr.Markdown(f"""
163
- Upload a DICOM file (CT or MRI) and type what you want to find (e.g., 'brain', 'skull', 'eyes').
164
- {demo_info}
165
-
166
- **Instructions:**
167
- 1. Upload a DICOM (.dcm) file (or click 'Load Demo File' if available)
168
- 2. Enter a text prompt describing what to segment
169
- 3. Select the imaging modality (CT or MRI)
170
- 4. Choose the windowing strategy (for CT images)
171
- 5. Click "Segment Structure" to process
172
-
173
- **Note:** Make sure GPU is enabled in Colab (Runtime → Change runtime type → GPU) for best performance.
174
-
175
- **Public Datasets:**
176
- - UniqueData/dicom-brain-dataset (Hugging Face) - MRI Brain scans
177
- - The Cancer Imaging Archive (TCIA) - Various medical imaging
178
- - Imaging Data Commons (IDC) - Large collection of DICOM files
179
- """)
180
-
181
- with gr.Row():
182
- with gr.Column():
183
- file_input = gr.File(
184
- label="Upload DICOM (.dcm)",
185
- file_types=[".dcm"],
186
- type="filepath",
187
- value=demo_file_path if demo_file_available else None
188
- )
189
-
190
- load_demo_btn = gr.Button(
191
- "📁 Load Demo File",
192
- variant="secondary",
193
- size="sm",
194
- visible=demo_file_available
195
- )
196
-
197
- text_input = gr.Textbox(
198
- label="Text Prompt",
199
- value="brain",
200
- placeholder="e.g. brain, tumor, skull, eyes",
201
- info="Describe what anatomical structure or region you want to segment"
202
- )
203
-
204
- with gr.Row():
205
- modality_dropdown = gr.Dropdown(
206
- ["CT", "MRI"],
207
- label="Modality",
208
- value="MRI",
209
- info="Select the imaging modality"
210
- )
211
- window_dropdown = gr.Dropdown(
212
- ["Brain (Grey Matter)", "Bone (Skull)", "Soft Tissue (Face)"],
213
- label="Windowing Strategy (CT only)",
214
- value="Brain (Grey Matter)",
215
- info="CT windowing preset (ignored for MRI)"
216
- )
217
-
218
- submit_btn = gr.Button("Segment Structure", variant="primary", size="lg")
219
 
220
- with gr.Column():
221
- image_output = gr.Image(
222
- label="Segmentation Result",
223
- type="filepath"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
224
  )
225
-
226
- gr.Markdown("### Status")
227
- status_text = gr.Textbox(
228
- label="Processing Status",
229
- value="Ready. Upload a DICOM file to begin.",
230
- interactive=False
231
  )
232
-
233
- def load_demo_file_fn():
234
- """Load the demo DICOM file."""
235
- if demo_file_available and demo_file_path and os.path.exists(demo_file_path):
236
- return demo_file_path, f"✅ Demo file loaded: {demo_file_path}\nReady to segment!"
237
- else:
238
- return None, "⚠️ Demo file not found. Please upload a DICOM file."
239
-
240
- def process_with_status_fn(dicom_file, prompt_text, modality, window_type):
241
- """Wrapper function to update status during processing."""
242
- if model_instance is None or processor_instance is None:
243
- return None, " Error: Model not loaded. Please check the model loading section above."
244
-
245
- if dicom_file is None:
246
- return None, "⚠️ Please upload a DICOM file or load the demo file."
247
-
248
- result = process_medical_image(dicom_file, prompt_text, modality, window_type, model_instance, processor_instance, device_instance)
249
-
250
- if result is None:
251
- return None, "❌ Processing failed. Check console for error details."
252
- else:
253
- return result, "✅ Segmentation complete!"
254
-
255
- load_demo_btn.click(
256
- fn=load_demo_file_fn,
257
- inputs=[],
258
- outputs=[file_input, status_text]
259
- )
260
-
261
- submit_btn.click(
262
- fn=process_with_status_fn,
263
- inputs=[file_input, text_input, modality_dropdown, window_dropdown],
264
- outputs=[image_output, status_text]
265
- )
266
- return demo
 
1
+ """
2
+ NeuroSAM 3: Medical Image Segmentation App
3
+ A Gradio app for segmenting medical images (CT/MRI) using SAM 3
4
+ """
5
+
6
  import os
7
  import tempfile
8
  import gradio as gr
 
13
  from transformers import Sam3Processor, Sam3Model
14
  import matplotlib.pyplot as plt
15
 
16
+ # Hugging Face Token (must be set as HF_TOKEN environment variable in Space settings)
17
+ hf_token = os.getenv("HF_TOKEN")
18
+ if not hf_token:
19
+ raise ValueError("HF_TOKEN environment variable is required. Please set it in Space settings.")
20
+
21
+ # Login to Hugging Face Hub
22
+ try:
23
+ from huggingface_hub import login
24
+ login(token=hf_token, add_to_git_credential=False)
25
+ except Exception as e:
26
+ print(f"⚠️ Could not login to HF Hub (non-critical): {e}")
27
+
28
+ # Load SAM 3 Model
29
+ print("🧠 Loading SAM 3 Model...")
30
+ device = "cuda" if torch.cuda.is_available() else "cpu"
31
+ model = None
32
+ processor = None
33
+
34
+ try:
35
+ model = Sam3Model.from_pretrained("facebook/sam3", token=hf_token).to(device)
36
+ processor = Sam3Processor.from_pretrained("facebook/sam3", token=hf_token)
37
+ model.eval()
38
+ print("✅ Model Loaded Successfully!")
39
+ except Exception as e:
40
+ print(f"⚠️ Model Load Warning: {e}")
41
+ print("Ensure you have the correct HuggingFace model name/identifier and access permissions.")
42
+
43
+ # Create Sample DICOM File for Demo
44
+ demo_dicom_path = "demo_brain_mri.dcm"
45
+ demo_file_available = False
46
+
47
+ try:
48
+ from pydicom.data import get_testdata_file
49
+ test_file = get_testdata_file("MR_small.dcm")
50
+ if test_file and os.path.exists(test_file):
51
+ import shutil
52
+ shutil.copy(test_file, demo_dicom_path)
53
+ demo_file_available = True
54
+ print(f"✅ Demo file ready: {demo_dicom_path}")
55
+ except:
56
+ try:
57
+ # Create synthetic DICOM file
58
+ from pydicom.dataset import FileDataset, FileMetaDataset
59
+ from pydicom.uid import generate_uid
60
+ from datetime import datetime
61
+
62
+ synthetic_image = np.random.randint(0, 255, (256, 256), dtype=np.uint16)
63
+ center_x, center_y = 128, 128
64
+ y, x = np.ogrid[:256, :256]
65
+ mask = (x - center_x)**2 + (y - center_y)**2 <= 100**2
66
+ synthetic_image[mask] = np.clip(synthetic_image[mask] + 50, 0, 255)
67
+
68
+ file_meta = FileMetaDataset()
69
+ file_meta.MediaStorageSOPClassUID = '1.2.840.10008.5.1.4.1.1.4'
70
+ file_meta.MediaStorageSOPInstanceUID = generate_uid()
71
+ file_meta.TransferSyntaxUID = '1.2.840.10008.1.2.1'
72
+
73
+ ds = FileDataset(demo_dicom_path, {}, file_meta=file_meta, preamble=b"\x00" * 128)
74
+ ds.PatientName = "Demo^Patient"
75
+ ds.PatientID = "DEMO001"
76
+ ds.Modality = "MR"
77
+ ds.Rows = 256
78
+ ds.Columns = 256
79
+ ds.BitsAllocated = 16
80
+ ds.BitsStored = 16
81
+ ds.HighBit = 15
82
+ ds.SamplesPerPixel = 1
83
+ ds.PixelRepresentation = 0
84
+ ds.PhotometricInterpretation = "MONOCHROME2"
85
+ ds.PixelSpacing = [1.0, 1.0]
86
+ ds.RescaleIntercept = "0"
87
+ ds.RescaleSlope = "1"
88
+ ds.PixelData = synthetic_image.tobytes()
89
+
90
+ ds.save_as(demo_dicom_path, write_like_original=False)
91
+ demo_file_available = True
92
+ print(f"✅ Synthetic demo file created: {demo_dicom_path}")
93
+ except Exception as e:
94
+ print(f"⚠️ Could not create demo file: {e}")
95
 
96
+ def process_medical_image(dicom_file, prompt_text, modality, window_type):
97
+ """Process a DICOM medical image and perform segmentation using SAM 3."""
 
 
98
  if model is None or processor is None:
99
+ print("❌ Error: Model not loaded.")
100
  return None
101
+
 
102
  if dicom_file is None:
103
  return None
104
+
105
  if not prompt_text or not prompt_text.strip():
 
106
  prompt_text = "brain"
107
+
108
  try:
 
 
109
  dicom_path = dicom_file if isinstance(dicom_file, str) else str(dicom_file)
110
+
111
  if not os.path.exists(dicom_path):
112
  print(f"❌ Error: DICOM file not found at {dicom_path}")
113
  return None
114
+
115
  ds = pydicom.dcmread(dicom_path)
116
+
 
117
  if not hasattr(ds, 'pixel_array'):
118
  print("❌ Error: DICOM file does not contain pixel data.")
119
  return None
120
+
121
  raw = ds.pixel_array.astype(np.float32)
122
  slope = getattr(ds, 'RescaleSlope', 1)
123
  intercept = getattr(ds, 'RescaleIntercept', 0)
124
  img_hu = raw * slope + intercept
125
 
126
+ # Apply Windowing
127
  if modality == "CT":
128
  if window_type == "Brain (Grey Matter)":
129
  level, width = 40, 80
130
  elif window_type == "Bone (Skull)":
131
  level, width = 500, 2000
132
+ else:
133
  level, width = 40, 400
134
  img_min = level - (width / 2)
135
  img_max = level + (width / 2)
136
+ else: # MRI
137
  img_min = np.percentile(img_hu, 1)
138
  img_max = np.percentile(img_hu, 99)
139
 
 
140
  img_range = img_max - img_min
141
  if img_range <= 0:
 
142
  img_min = np.min(img_hu)
143
  img_max = np.max(img_hu)
144
  img_range = img_max - img_min
145
  if img_range <= 0:
 
146
  return None
147
 
148
  img_windowed = (img_hu - img_min) / img_range
149
  img_windowed = np.clip(img_windowed, 0, 1)
150
+
 
151
  img_uint8 = (img_windowed * 255).astype(np.uint8)
152
+
 
153
  if len(img_uint8.shape) == 2:
154
  pil_image = Image.fromarray(img_uint8).convert('RGB')
155
  else:
156
  pil_image = Image.fromarray(img_uint8)
157
 
158
+ # Run SAM 3 Inference
159
  try:
160
  inputs = processor(images=pil_image, text=prompt_text, return_tensors="pt").to(device)
161
+
162
  with torch.no_grad():
163
  outputs = model(**inputs)
164
 
 
168
  except Exception as e:
169
  print(f"❌ Error during model inference: {e}")
170
  return None
171
+
172
+ # Draw Masks on Image
 
173
  plt.figure(figsize=(10, 10))
174
  plt.imshow(pil_image)
175
+
 
176
  if 'masks' in results and results['masks'] is not None:
177
  masks = results['masks'].cpu().numpy()
178
  if len(masks) > 0:
179
  final_mask = np.any(masks, axis=0)
180
+ plt.imshow(final_mask, alpha=0.5, cmap='spring')
181
  else:
182
  print("⚠️ Warning: No masks found in segmentation results.")
183
  else:
184
+ print("⚠️ Warning: No masks in results.")
185
+
186
  plt.axis('off')
187
  plt.title(f"Segmentation: {prompt_text}", fontsize=12, pad=10)
188
+
 
189
  output_file = tempfile.NamedTemporaryFile(delete=False, suffix='.png')
190
  output_path = output_file.name
191
  output_file.close()
192
+
193
  plt.savefig(output_path, bbox_inches='tight', pad_inches=0, dpi=100)
194
  plt.close()
195
+
196
  return output_path
197
+
 
 
 
198
  except Exception as e:
199
  print(f"❌ Error processing image: {e}")
200
  import traceback
201
  traceback.print_exc()
202
  return None
203
 
204
+ # Create Gradio Interface
205
+ demo_file_path = demo_dicom_path if demo_file_available and os.path.exists(demo_dicom_path) else None
 
 
206
 
207
+ def load_demo_file():
208
+ """Load the demo DICOM file."""
209
+ if demo_file_path and os.path.exists(demo_file_path):
210
+ return demo_file_path, f"✅ Demo file loaded: {demo_file_path}\nReady to segment!"
211
+ else:
212
+ return None, "⚠️ Demo file not found. Please upload a DICOM file."
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
213
 
214
+ def process_with_status(dicom_file, prompt_text, modality, window_type):
215
+ """Wrapper function to update status during processing."""
216
+ if model is None or processor is None:
217
+ return None, "❌ Error: Model not loaded."
218
+
219
+ if dicom_file is None:
220
+ return None, "⚠️ Please upload a DICOM file or load the demo file."
221
+
222
+ result = process_medical_image(dicom_file, prompt_text, modality, window_type)
223
+
224
+ if result is None:
225
+ return None, "❌ Processing failed. Check console for error details."
226
+ else:
227
+ return result, "✅ Segmentation complete!"
228
+
229
+ with gr.Blocks() as demo:
230
+ gr.Markdown("# 🏥 NeuroSAM 3: Medical Image Segmentation")
231
+
232
+ demo_info = ""
233
+ if demo_file_path:
234
+ demo_info = f"\n\n**📁 Demo File Available:** A sample DICOM file is ready: `{demo_file_path}`\nClick 'Load Demo File' button below to use it!"
235
+
236
+ gr.Markdown(f"""
237
+ Upload a DICOM file (CT or MRI) and type what you want to find (e.g., 'brain', 'skull', 'eyes').
238
+ {demo_info}
239
+
240
+ **Instructions:**
241
+ 1. Upload a DICOM (.dcm) file (or click 'Load Demo File' if available)
242
+ 2. Enter a text prompt describing what to segment
243
+ 3. Select the imaging modality (CT or MRI)
244
+ 4. Choose the windowing strategy (for CT images)
245
+ 5. Click "Segment Structure" to process
246
+ """)
247
+
248
+ with gr.Row():
249
+ with gr.Column():
250
+ file_input = gr.File(
251
+ label="Upload DICOM (.dcm)",
252
+ file_types=[".dcm"],
253
+ type="filepath",
254
+ value=demo_file_path
255
+ )
256
+
257
+ load_demo_btn = gr.Button(
258
+ "📁 Load Demo File",
259
+ variant="secondary",
260
+ size="sm",
261
+ visible=bool(demo_file_path)
262
+ )
263
+
264
+ text_input = gr.Textbox(
265
+ label="Text Prompt",
266
+ value="brain",
267
+ placeholder="e.g. brain, tumor, skull, eyes",
268
+ info="Describe what anatomical structure or region you want to segment"
269
+ )
270
+
271
+ with gr.Row():
272
+ modality_dropdown = gr.Dropdown(
273
+ ["CT", "MRI"],
274
+ label="Modality",
275
+ value="MRI",
276
+ info="Select the imaging modality"
277
  )
278
+ window_dropdown = gr.Dropdown(
279
+ ["Brain (Grey Matter)", "Bone (Skull)", "Soft Tissue (Face)"],
280
+ label="Windowing Strategy (CT only)",
281
+ value="Brain (Grey Matter)",
282
+ info="CT windowing preset (ignored for MRI)"
 
283
  )
284
+
285
+ submit_btn = gr.Button("Segment Structure", variant="primary", size="lg")
286
+
287
+ with gr.Column():
288
+ image_output = gr.Image(
289
+ label="Segmentation Result",
290
+ type="filepath"
291
+ )
292
+
293
+ gr.Markdown("### Status")
294
+ status_text = gr.Textbox(
295
+ label="Processing Status",
296
+ value="Ready. Upload a DICOM file to begin.",
297
+ interactive=False
298
+ )
299
+
300
+ load_demo_btn.click(
301
+ fn=load_demo_file,
302
+ inputs=[],
303
+ outputs=[file_input, status_text]
304
+ )
305
+
306
+ submit_btn.click(
307
+ fn=process_with_status,
308
+ inputs=[file_input, text_input, modality_dropdown, window_dropdown],
309
+ outputs=[image_output, status_text]
310
+ )
311
+
312
+ if __name__ == "__main__":
313
+ demo.launch()
 
 
 
 
 
requirements.txt ADDED
@@ -0,0 +1,10 @@
 
 
 
 
 
 
 
 
 
 
 
1
+ gradio>=4.0.0
2
+ pydicom>=2.4.0
3
+ numpy>=1.24.0
4
+ pillow>=10.0.0
5
+ matplotlib>=3.7.0
6
+ torch>=2.0.0
7
+ torchvision>=0.15.0
8
+ transformers>=4.41.0
9
+ huggingface-hub>=0.20.0
10
+