Spaces:
Sleeping
Sleeping
Update medpy/mcp_output/mcp_plugin/mcp_service.py
Browse files
medpy/mcp_output/mcp_plugin/mcp_service.py
CHANGED
|
@@ -3,6 +3,11 @@ import sys
|
|
| 3 |
import numpy as np
|
| 4 |
import json
|
| 5 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 6 |
source_path = os.path.join(os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))), "source")
|
| 7 |
sys.path.insert(0, source_path)
|
| 8 |
|
|
@@ -386,6 +391,126 @@ def otsu_threshold_tool(image_data: list) -> dict:
|
|
| 386 |
except Exception as e:
|
| 387 |
return {"success": False, "result": None, "error": str(e)}
|
| 388 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 389 |
@mcp.tool(name="gaussian_gradient_magnitude", description="Compute Gaussian gradient magnitude of image.")
|
| 390 |
def gaussian_gradient_magnitude_tool(image_data: list, sigma: float = 1.0) -> dict:
|
| 391 |
"""
|
|
|
|
| 3 |
import numpy as np
|
| 4 |
import json
|
| 5 |
|
| 6 |
+
try:
|
| 7 |
+
from scipy import ndimage
|
| 8 |
+
except ImportError:
|
| 9 |
+
ndimage = None
|
| 10 |
+
|
| 11 |
source_path = os.path.join(os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))), "source")
|
| 12 |
sys.path.insert(0, source_path)
|
| 13 |
|
|
|
|
| 391 |
except Exception as e:
|
| 392 |
return {"success": False, "result": None, "error": str(e)}
|
| 393 |
|
| 394 |
+
@mcp.tool(name="detect_lesions", description="Detect lesion regions in medical images and return detailed location information.")
|
| 395 |
+
def detect_lesions_tool(image_data: list, method: str = "otsu", min_size: int = 10) -> dict:
|
| 396 |
+
"""
|
| 397 |
+
Detects lesion regions in medical images and provides detailed location information.
|
| 398 |
+
|
| 399 |
+
This tool automatically segments lesion regions and returns:
|
| 400 |
+
- Lesion locations (coordinates)
|
| 401 |
+
- Lesion sizes (pixel counts)
|
| 402 |
+
- Lesion area percentage
|
| 403 |
+
- Visual description of lesion distribution
|
| 404 |
+
|
| 405 |
+
Parameters:
|
| 406 |
+
- image_data: Input medical image as 2D array (list of lists).
|
| 407 |
+
- method: Segmentation method - "otsu" for automatic thresholding (default) or "manual" for manual threshold.
|
| 408 |
+
- min_size: Minimum lesion size in pixels to filter noise (default: 10).
|
| 409 |
+
|
| 410 |
+
Returns:
|
| 411 |
+
- dict: Contains detailed lesion information including locations, sizes, and distribution.
|
| 412 |
+
"""
|
| 413 |
+
try:
|
| 414 |
+
image_array = np.asarray(image_data)
|
| 415 |
+
|
| 416 |
+
# Step 1: Apply Otsu thresholding to detect lesion regions
|
| 417 |
+
threshold = otsu(image_array)
|
| 418 |
+
binary_result = (image_array >= threshold).astype(int)
|
| 419 |
+
|
| 420 |
+
# Step 2: Find lesion regions (connected components)
|
| 421 |
+
lesion_mask = binary_result.astype(bool)
|
| 422 |
+
|
| 423 |
+
# Step 3: Calculate lesion statistics
|
| 424 |
+
total_pixels = lesion_mask.size
|
| 425 |
+
lesion_pixels = np.sum(lesion_mask)
|
| 426 |
+
background_pixels = total_pixels - lesion_pixels
|
| 427 |
+
lesion_percentage = (lesion_pixels / total_pixels * 100) if total_pixels > 0 else 0
|
| 428 |
+
|
| 429 |
+
# Step 4: Find lesion boundaries and bounding boxes
|
| 430 |
+
lesion_coords = []
|
| 431 |
+
lesion_info = []
|
| 432 |
+
|
| 433 |
+
if np.any(lesion_mask):
|
| 434 |
+
# Find all connected components
|
| 435 |
+
if ndimage is not None:
|
| 436 |
+
labeled, num_features = ndimage.label(lesion_mask)
|
| 437 |
+
else:
|
| 438 |
+
# Fallback: use simple detection without connected components
|
| 439 |
+
num_features = 1 if np.any(lesion_mask) else 0
|
| 440 |
+
labeled = lesion_mask.astype(int)
|
| 441 |
+
|
| 442 |
+
for label in range(1, num_features + 1):
|
| 443 |
+
# Get coordinates of this lesion
|
| 444 |
+
lesion_indices = np.where(labeled == label)
|
| 445 |
+
coords = list(zip(lesion_indices[0], lesion_indices[1]))
|
| 446 |
+
lesion_coords.append(coords)
|
| 447 |
+
|
| 448 |
+
# Calculate bounding box
|
| 449 |
+
if coords:
|
| 450 |
+
rows = [c[0] for c in coords]
|
| 451 |
+
cols = [c[1] for c in coords]
|
| 452 |
+
min_row, max_row = min(rows), max(rows)
|
| 453 |
+
min_col, max_col = min(cols), max(cols)
|
| 454 |
+
|
| 455 |
+
lesion_info.append({
|
| 456 |
+
"id": label,
|
| 457 |
+
"pixel_count": len(coords),
|
| 458 |
+
"top_left": [int(min_row), int(min_col)],
|
| 459 |
+
"bottom_right": [int(max_row), int(max_col)],
|
| 460 |
+
"width": int(max_col - min_col + 1),
|
| 461 |
+
"height": int(max_row - min_row + 1),
|
| 462 |
+
"area_percentage": (len(coords) / total_pixels * 100)
|
| 463 |
+
})
|
| 464 |
+
|
| 465 |
+
# Filter by min_size
|
| 466 |
+
lesion_info = [lesion for lesion in lesion_info if lesion["pixel_count"] >= min_size]
|
| 467 |
+
|
| 468 |
+
# Find largest lesion
|
| 469 |
+
if lesion_info:
|
| 470 |
+
largest_lesion = max(lesion_info, key=lambda x: x["pixel_count"])
|
| 471 |
+
lesion_description = f"检测到 {len(lesion_info)} 个病变区域。最大病变位于: 行{largest_lesion['top_left'][0]}-{largest_lesion['bottom_right'][0]}, 列{largest_lesion['top_left'][1]}-{largest_lesion['bottom_right'][1]}, 面积为 {largest_lesion['area_percentage']:.2f}%"
|
| 472 |
+
else:
|
| 473 |
+
lesion_description = "未检测到符合尺寸要求的病变区域"
|
| 474 |
+
else:
|
| 475 |
+
lesion_description = "未检测到病变区域"
|
| 476 |
+
|
| 477 |
+
# Step 5: Calculate intensity statistics for lesion vs background
|
| 478 |
+
lesion_intensities = image_array[lesion_mask]
|
| 479 |
+
background_intensities = image_array[~lesion_mask]
|
| 480 |
+
|
| 481 |
+
result = {
|
| 482 |
+
"success": True,
|
| 483 |
+
"result": {
|
| 484 |
+
"detection_summary": lesion_description,
|
| 485 |
+
"total_lesions": len(lesion_info),
|
| 486 |
+
"lesion_details": lesion_info,
|
| 487 |
+
"statistics": {
|
| 488 |
+
"total_pixels": int(total_pixels),
|
| 489 |
+
"lesion_pixels": int(lesion_pixels),
|
| 490 |
+
"background_pixels": int(background_pixels),
|
| 491 |
+
"lesion_percentage": round(lesion_percentage, 2),
|
| 492 |
+
"threshold_used": round(float(threshold), 4),
|
| 493 |
+
"lesion_intensity_range": {
|
| 494 |
+
"min": float(np.min(lesion_intensities)) if len(lesion_intensities) > 0 else 0,
|
| 495 |
+
"max": float(np.max(lesion_intensities)) if len(lesion_intensities) > 0 else 0,
|
| 496 |
+
"mean": float(np.mean(lesion_intensities)) if len(lesion_intensities) > 0 else 0
|
| 497 |
+
},
|
| 498 |
+
"background_intensity_range": {
|
| 499 |
+
"min": float(np.min(background_intensities)) if len(background_intensities) > 0 else 0,
|
| 500 |
+
"max": float(np.max(background_intensities)) if len(background_intensities) > 0 else 0,
|
| 501 |
+
"mean": float(np.mean(background_intensities)) if len(background_intensities) > 0 else 0
|
| 502 |
+
}
|
| 503 |
+
},
|
| 504 |
+
"binary_mask": binary_result.tolist()
|
| 505 |
+
},
|
| 506 |
+
"error": None
|
| 507 |
+
}
|
| 508 |
+
|
| 509 |
+
return result
|
| 510 |
+
|
| 511 |
+
except Exception as e:
|
| 512 |
+
return {"success": False, "result": None, "error": str(e)}
|
| 513 |
+
|
| 514 |
@mcp.tool(name="gaussian_gradient_magnitude", description="Compute Gaussian gradient magnitude of image.")
|
| 515 |
def gaussian_gradient_magnitude_tool(image_data: list, sigma: float = 1.0) -> dict:
|
| 516 |
"""
|