guohanghui commited on
Commit
f2dfbb5
·
verified ·
1 Parent(s): 3a59483

Update MONAI/mcp_output/mcp_plugin/mcp_service.py

Browse files
MONAI/mcp_output/mcp_plugin/mcp_service.py CHANGED
@@ -1,429 +1,133 @@
1
- import os
2
- import sys
3
- from typing import List, Optional, Dict, Any
4
-
5
- # Add the local source directory to sys.path
6
- source_path = os.path.join(os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))), "source")
7
- if source_path not in sys.path:
8
- sys.path.insert(0, source_path)
9
-
10
  from fastmcp import FastMCP
11
- import numpy as np
12
-
13
- # Import MONAI modules
14
- import monai
15
- from monai.transforms import (
16
- Compose, LoadImage, EnsureChannelFirst, ScaleIntensity,
17
- NormalizeIntensity, Resize, RandRotate, RandFlip, ToTensor
18
- )
19
- from monai.networks.nets import UNet, VNet, AttentionUnet, SegResNet, DenseNet121
20
- from monai.losses import DiceLoss, DiceCELoss, FocalLoss, TverskyLoss
21
- from monai.metrics import DiceMetric, MeanIoU, HausdorffDistanceMetric
22
- from monai.data import decollate_batch
23
- from monai.inferers import sliding_window_inference
24
 
25
  # Create the FastMCP service application
26
  mcp = FastMCP("monai_service")
27
 
28
-
29
- @mcp.tool(name="get_monai_info", description="Get MONAI library information and configuration")
30
- def get_monai_info() -> dict:
31
  """
32
- Get MONAI library version and system configuration.
33
-
34
- Returns:
35
- - dict: MONAI version and configuration info.
36
- """
37
- try:
38
- config = monai.config.get_config_values()
39
- return {
40
- "success": True,
41
- "result": {
42
- "version": monai.__version__,
43
- "config": {k: str(v) for k, v in config.items()}
44
- },
45
- "error": None
46
- }
47
- except Exception as e:
48
- return {"success": False, "result": None, "error": str(e)}
49
-
50
-
51
- @mcp.tool(name="create_unet_model", description="Create and initialize a UNet model")
52
- def create_unet_model(
53
- spatial_dims: int = 3,
54
- in_channels: int = 1,
55
- out_channels: int = 2,
56
- channels: List[int] = None,
57
- strides: List[int] = None
58
- ) -> dict:
59
- """
60
- Create a UNet model for medical image segmentation.
61
 
62
  Parameters:
63
- - spatial_dims: Number of spatial dimensions (2 or 3).
64
- - in_channels: Number of input channels.
65
- - out_channels: Number of output channels/classes.
66
- - channels: Feature channels per layer (default: [16, 32, 64, 128, 256]).
67
- - strides: Strides per layer (default: [2, 2, 2, 2]).
68
 
69
  Returns:
70
- - dict: Model information and parameter count.
71
  """
72
  try:
73
- if channels is None:
74
- channels = [16, 32, 64, 128, 256]
75
- if strides is None:
76
- strides = [2, 2, 2, 2]
77
-
78
- model = UNet(
79
- spatial_dims=spatial_dims,
80
- in_channels=in_channels,
81
- out_channels=out_channels,
82
- channels=channels,
83
- strides=strides,
84
- num_res_units=2
85
- )
86
 
87
- total_params = sum(p.numel() for p in model.parameters())
88
- trainable_params = sum(p.numel() for p in model.parameters() if p.requires_grad)
 
89
 
90
  return {
91
  "success": True,
92
- "result": {
93
- "model_type": "UNet",
94
- "spatial_dims": spatial_dims,
95
- "in_channels": in_channels,
96
- "out_channels": out_channels,
97
- "channels": channels,
98
- "strides": strides,
99
- "total_parameters": total_params,
100
- "trainable_parameters": trainable_params
101
- },
102
- "error": None
103
  }
104
  except Exception as e:
105
- return {"success": False, "result": None, "error": str(e)}
106
 
107
-
108
- @mcp.tool(name="create_segresnet_model", description="Create a SegResNet model")
109
- def create_segresnet_model(
110
- spatial_dims: int = 3,
111
- in_channels: int = 1,
112
- out_channels: int = 2,
113
- init_filters: int = 16
114
- ) -> dict:
115
  """
116
- Create a SegResNet model for medical image segmentation.
117
 
118
  Parameters:
119
- - spatial_dims: Number of spatial dimensions (2 or 3).
120
- - in_channels: Number of input channels.
121
- - out_channels: Number of output channels/classes.
122
- - init_filters: Initial number of filters.
123
 
124
  Returns:
125
- - dict: Model information and parameter count.
126
  """
127
  try:
128
- model = SegResNet(
129
- spatial_dims=spatial_dims,
130
- in_channels=in_channels,
131
- out_channels=out_channels,
132
- init_filters=init_filters
133
- )
134
 
135
- total_params = sum(p.numel() for p in model.parameters())
 
 
136
 
137
  return {
138
  "success": True,
139
- "result": {
140
- "model_type": "SegResNet",
141
- "spatial_dims": spatial_dims,
142
- "in_channels": in_channels,
143
- "out_channels": out_channels,
144
- "init_filters": init_filters,
145
- "total_parameters": total_params
146
- },
147
- "error": None
148
  }
149
  except Exception as e:
150
- return {"success": False, "result": None, "error": str(e)}
151
-
152
 
153
- @mcp.tool(name="compute_dice_score", description="Compute Dice score between prediction and ground truth")
154
- def compute_dice_score(
155
- prediction: List[List[List[float]]],
156
- ground_truth: List[List[List[float]]],
157
- include_background: bool = False
158
- ) -> dict:
159
  """
160
- Compute Dice similarity coefficient between prediction and ground truth.
161
 
162
  Parameters:
163
- - prediction: Predicted segmentation as nested list (2D or 3D).
164
- - ground_truth: Ground truth segmentation as nested list.
165
- - include_background: Whether to include background class.
166
 
167
  Returns:
168
- - dict: Dice score for each class.
169
  """
170
  try:
171
- import torch
172
 
173
- pred_tensor = torch.tensor(prediction).unsqueeze(0).unsqueeze(0).float()
174
- gt_tensor = torch.tensor(ground_truth).unsqueeze(0).unsqueeze(0).float()
175
-
176
- dice_metric = DiceMetric(include_background=include_background, reduction="mean")
177
- dice_metric(y_pred=pred_tensor, y=gt_tensor)
178
- dice_score = dice_metric.aggregate().item()
179
- dice_metric.reset()
180
 
181
  return {
182
  "success": True,
183
- "result": {
184
- "dice_score": dice_score,
185
- "include_background": include_background
186
- },
187
- "error": None
188
  }
189
  except Exception as e:
190
- return {"success": False, "result": None, "error": str(e)}
191
-
192
 
193
- @mcp.tool(name="compute_loss", description="Compute segmentation loss")
194
- def compute_loss(
195
- prediction: List[List[List[float]]],
196
- ground_truth: List[List[List[float]]],
197
- loss_type: str = "dice"
198
- ) -> dict:
199
  """
200
- Compute segmentation loss between prediction and ground truth.
201
 
202
  Parameters:
203
- - prediction: Predicted logits as nested list.
204
- - ground_truth: Ground truth labels as nested list.
205
- - loss_type: Type of loss ('dice', 'dice_ce', 'focal', 'tversky').
206
 
207
  Returns:
208
- - dict: Loss value.
209
  """
210
  try:
211
- import torch
212
-
213
- pred_tensor = torch.tensor(prediction).unsqueeze(0).unsqueeze(0).float()
214
- gt_tensor = torch.tensor(ground_truth).unsqueeze(0).unsqueeze(0).float()
215
 
216
- if loss_type == "dice":
217
- loss_fn = DiceLoss(sigmoid=True)
218
- elif loss_type == "dice_ce":
219
- loss_fn = DiceCELoss(sigmoid=True)
220
- elif loss_type == "focal":
221
- loss_fn = FocalLoss()
222
- elif loss_type == "tversky":
223
- loss_fn = TverskyLoss(sigmoid=True)
224
- else:
225
- return {"success": False, "result": None, "error": f"Unknown loss type: {loss_type}"}
226
-
227
- loss_value = loss_fn(pred_tensor, gt_tensor).item()
228
 
229
  return {
230
  "success": True,
231
- "result": {
232
- "loss_type": loss_type,
233
- "loss_value": loss_value
234
- },
235
- "error": None
236
  }
237
  except Exception as e:
238
- return {"success": False, "result": None, "error": str(e)}
239
-
240
 
241
- @mcp.tool(name="apply_intensity_transforms", description="Apply intensity transforms to an image")
242
- def apply_intensity_transforms(
243
- image: List[List[List[float]]],
244
- normalize: bool = True,
245
- scale_intensity: bool = False,
246
- target_min: float = 0.0,
247
- target_max: float = 1.0
248
- ) -> dict:
249
  """
250
- Apply intensity transforms to a medical image.
251
 
252
  Parameters:
253
- - image: Input image as nested list (2D or 3D).
254
- - normalize: Whether to normalize intensity (zero mean, unit std).
255
- - scale_intensity: Whether to scale intensity to target range.
256
- - target_min: Minimum value for scaling.
257
- - target_max: Maximum value for scaling.
258
 
259
  Returns:
260
- - dict: Transformed image statistics.
261
  """
262
  try:
263
- img_array = np.array(image, dtype=np.float32)
264
 
265
- transforms_list = []
266
- applied_transforms = []
267
-
268
- if normalize:
269
- transforms_list.append(NormalizeIntensity())
270
- applied_transforms.append("NormalizeIntensity")
271
-
272
- if scale_intensity:
273
- transforms_list.append(ScaleIntensity(minv=target_min, maxv=target_max))
274
- applied_transforms.append(f"ScaleIntensity({target_min}, {target_max})")
275
-
276
- if transforms_list:
277
- transform = Compose(transforms_list)
278
- result = transform(img_array)
279
- else:
280
- result = img_array
281
 
282
  return {
283
  "success": True,
284
- "result": {
285
- "applied_transforms": applied_transforms,
286
- "output_shape": list(result.shape),
287
- "output_min": float(result.min()),
288
- "output_max": float(result.max()),
289
- "output_mean": float(result.mean()),
290
- "output_std": float(result.std())
291
- },
292
- "error": None
293
  }
294
  except Exception as e:
295
- return {"success": False, "result": None, "error": str(e)}
296
-
297
-
298
- @mcp.tool(name="resize_image", description="Resize a medical image using MONAI")
299
- def resize_image(
300
- image: List[List[List[float]]],
301
- spatial_size: List[int],
302
- mode: str = "trilinear"
303
- ) -> dict:
304
- """
305
- Resize a medical image to target spatial size.
306
-
307
- Parameters:
308
- - image: Input image as nested list (2D or 3D).
309
- - spatial_size: Target spatial size [H, W] or [D, H, W].
310
- - mode: Interpolation mode ('nearest', 'bilinear', 'trilinear').
311
-
312
- Returns:
313
- - dict: Resized image info.
314
- """
315
- try:
316
- img_array = np.array(image, dtype=np.float32)
317
- original_shape = img_array.shape
318
-
319
- # Add channel dimension if needed
320
- if len(img_array.shape) == len(spatial_size):
321
- img_array = img_array[np.newaxis, ...]
322
-
323
- resize_transform = Resize(spatial_size=spatial_size, mode=mode)
324
- resized = resize_transform(img_array)
325
-
326
- return {
327
- "success": True,
328
- "result": {
329
- "original_shape": list(original_shape),
330
- "target_size": spatial_size,
331
- "output_shape": list(resized.shape),
332
- "interpolation_mode": mode
333
- },
334
- "error": None
335
- }
336
- except Exception as e:
337
- return {"success": False, "result": None, "error": str(e)}
338
-
339
-
340
- @mcp.tool(name="get_network_architectures", description="Get details about available network architectures")
341
- def get_network_architectures() -> dict:
342
- """
343
- Get detailed information about available MONAI network architectures.
344
-
345
- Returns:
346
- - dict: Network architecture details.
347
- """
348
- try:
349
- architectures = {
350
- "UNet": {
351
- "description": "U-Net architecture for semantic segmentation",
352
- "use_case": "General medical image segmentation",
353
- "parameters": ["spatial_dims", "in_channels", "out_channels", "channels", "strides"]
354
- },
355
- "VNet": {
356
- "description": "V-Net for volumetric medical image segmentation",
357
- "use_case": "3D medical image segmentation",
358
- "parameters": ["spatial_dims", "in_channels", "out_channels"]
359
- },
360
- "AttentionUnet": {
361
- "description": "Attention U-Net with attention gates",
362
- "use_case": "Segmentation with attention mechanism",
363
- "parameters": ["spatial_dims", "in_channels", "out_channels", "channels", "strides"]
364
- },
365
- "SegResNet": {
366
- "description": "ResNet-based encoder-decoder for segmentation",
367
- "use_case": "Medical image segmentation with residual connections",
368
- "parameters": ["spatial_dims", "in_channels", "out_channels", "init_filters"]
369
- },
370
- "SwinUNETR": {
371
- "description": "Swin Transformer based U-Net",
372
- "use_case": "State-of-the-art medical image segmentation",
373
- "parameters": ["img_size", "in_channels", "out_channels", "feature_size"]
374
- },
375
- "DenseNet121": {
376
- "description": "DenseNet for classification",
377
- "use_case": "Medical image classification",
378
- "parameters": ["spatial_dims", "in_channels", "out_channels"]
379
- }
380
- }
381
-
382
- return {"success": True, "result": architectures, "error": None}
383
- except Exception as e:
384
- return {"success": False, "result": None, "error": str(e)}
385
-
386
-
387
- @mcp.tool(name="create_densenet_classifier", description="Create a DenseNet classifier model")
388
- def create_densenet_classifier(
389
- spatial_dims: int = 3,
390
- in_channels: int = 1,
391
- out_channels: int = 2
392
- ) -> dict:
393
- """
394
- Create a DenseNet121 classifier for medical image classification.
395
-
396
- Parameters:
397
- - spatial_dims: Number of spatial dimensions (2 or 3).
398
- - in_channels: Number of input channels.
399
- - out_channels: Number of output classes.
400
-
401
- Returns:
402
- - dict: Model information.
403
- """
404
- try:
405
- model = DenseNet121(
406
- spatial_dims=spatial_dims,
407
- in_channels=in_channels,
408
- out_channels=out_channels
409
- )
410
-
411
- total_params = sum(p.numel() for p in model.parameters())
412
-
413
- return {
414
- "success": True,
415
- "result": {
416
- "model_type": "DenseNet121",
417
- "spatial_dims": spatial_dims,
418
- "in_channels": in_channels,
419
- "out_channels": out_channels,
420
- "total_parameters": total_params
421
- },
422
- "error": None
423
- }
424
- except Exception as e:
425
- return {"success": False, "result": None, "error": str(e)}
426
-
427
 
428
  def create_app() -> FastMCP:
429
  """
 
 
 
 
 
 
 
 
 
 
1
  from fastmcp import FastMCP
 
 
 
 
 
 
 
 
 
 
 
 
 
2
 
3
  # Create the FastMCP service application
4
  mcp = FastMCP("monai_service")
5
 
6
+ @mcp.tool(name="load_medical_dataset", description="Load a medical imaging dataset using MONAI")
7
+ def load_medical_dataset(dataset_path: str) -> dict:
 
8
  """
9
+ Load a medical imaging dataset using MONAI.
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
10
 
11
  Parameters:
12
+ - dataset_path: Path to the dataset.
 
 
 
 
13
 
14
  Returns:
15
+ - dict: Information about the loaded dataset.
16
  """
17
  try:
18
+ from monai.data import Dataset
19
+ from monai.transforms import LoadImaged
 
 
 
 
 
 
 
 
 
 
 
20
 
21
+ data = [{"image": dataset_path}]
22
+ transforms = LoadImaged(keys=["image"])
23
+ dataset = Dataset(data, transforms)
24
 
25
  return {
26
  "success": True,
27
+ "dataset": str(dataset)
 
 
 
 
 
 
 
 
 
 
28
  }
29
  except Exception as e:
30
+ return {"success": False, "error": str(e)}
31
 
32
+ @mcp.tool(name="train_segmentation_model", description="Train a segmentation model using MONAI")
33
+ def train_segmentation_model(config: dict) -> dict:
 
 
 
 
 
 
34
  """
35
+ Train a segmentation model using MONAI.
36
 
37
  Parameters:
38
+ - config: Configuration dictionary for training.
 
 
 
39
 
40
  Returns:
41
+ - dict: Training results.
42
  """
43
  try:
44
+ from monai.engines import SupervisedTrainer
45
+ from monai.transforms import Compose
 
 
 
 
46
 
47
+ # Example: Initialize trainer with config
48
+ trainer = SupervisedTrainer(**config)
49
+ trainer.run()
50
 
51
  return {
52
  "success": True,
53
+ "message": "Training completed successfully."
 
 
 
 
 
 
 
 
54
  }
55
  except Exception as e:
56
+ return {"success": False, "error": str(e)}
 
57
 
58
+ @mcp.tool(name="evaluate_model", description="Evaluate a trained model using MONAI")
59
+ def evaluate_model(model_path: str, test_data: list) -> dict:
 
 
 
 
60
  """
61
+ Evaluate a trained model using MONAI.
62
 
63
  Parameters:
64
+ - model_path: Path to the trained model.
65
+ - test_data: Test dataset.
 
66
 
67
  Returns:
68
+ - dict: Evaluation metrics.
69
  """
70
  try:
71
+ from monai.engines import SupervisedEvaluator
72
 
73
+ evaluator = SupervisedEvaluator(model_path=model_path, data=test_data)
74
+ metrics = evaluator.run()
 
 
 
 
 
75
 
76
  return {
77
  "success": True,
78
+ "metrics": metrics
 
 
 
 
79
  }
80
  except Exception as e:
81
+ return {"success": False, "error": str(e)}
 
82
 
83
+ @mcp.tool(name="apply_transforms", description="Apply MONAI transforms to medical images")
84
+ def apply_transforms(image_path: str, transforms: list) -> dict:
 
 
 
 
85
  """
86
+ Apply MONAI transforms to medical images.
87
 
88
  Parameters:
89
+ - image_path: Path to the image.
90
+ - transforms: List of transforms to apply.
 
91
 
92
  Returns:
93
+ - dict: Transformed image data.
94
  """
95
  try:
96
+ from monai.transforms import Compose
 
 
 
97
 
98
+ composed_transforms = Compose(transforms)
99
+ transformed_image = composed_transforms(image_path)
 
 
 
 
 
 
 
 
 
 
100
 
101
  return {
102
  "success": True,
103
+ "transformed_image": transformed_image
 
 
 
 
104
  }
105
  except Exception as e:
106
+ return {"success": False, "error": str(e)}
 
107
 
108
+ @mcp.tool(name="visualize_segmentation", description="Visualize segmentation results using MONAI")
109
+ def visualize_segmentation(image_path: str, segmentation_path: str) -> dict:
 
 
 
 
 
 
110
  """
111
+ Visualize segmentation results using MONAI.
112
 
113
  Parameters:
114
+ - image_path: Path to the original image.
115
+ - segmentation_path: Path to the segmentation result.
 
 
 
116
 
117
  Returns:
118
+ - dict: Visualization status.
119
  """
120
  try:
121
+ from monai.visualize import blend_images
122
 
123
+ blended_image = blend_images(image_path, segmentation_path)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
124
 
125
  return {
126
  "success": True,
127
+ "blended_image": blended_image
 
 
 
 
 
 
 
 
128
  }
129
  except Exception as e:
130
+ return {"success": False, "error": str(e)}
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
131
 
132
  def create_app() -> FastMCP:
133
  """