SimpleITK is a simplified interface to the Insight Toolkit (ITK) for image registration and segmentation
—
Quality
Pending
Does it follow best practices?
Impact
Pending
No eval scenarios have been run
Complete examples of SimpleITK usage in real-world medical and scientific imaging applications.
import SimpleITK as sitk
import numpy as np
def medical_image_preprocessing(input_path, output_path):
"""
Complete preprocessing pipeline for medical images.
Steps:
1. Read DICOM or NIfTI
2. Denoise with anisotropic diffusion
3. Normalize intensity
4. Resample to isotropic spacing
5. Bias field correction
6. Save preprocessed image
"""
# Read image
image = sitk.ReadImage(input_path)
print(f"Original size: {image.GetSize()}")
print(f"Original spacing: {image.GetSpacing()}")
# Cast to float for processing
image = sitk.Cast(image, sitk.sitkFloat32)
# Denoise with anisotropic diffusion
image = sitk.CurvatureAnisotropicDiffusion(
image,
timeStep=0.0625,
conductanceParameter=3.0,
numberOfIterations=5
)
# Normalize intensity to [0, 1000]
image = sitk.RescaleIntensity(image, outputMinimum=0, outputMaximum=1000)
# Resample to isotropic spacing
original_spacing = image.GetSpacing()
original_size = image.GetSize()
# Use minimum spacing for isotropic
iso_spacing = [min(original_spacing)] * 3
# Compute new size
new_size = [
int(original_size[i] * original_spacing[i] / iso_spacing[i])
for i in range(3)
]
image = sitk.Resample(
image,
size=new_size,
transform=sitk.Transform(),
interpolator=sitk.sitkLinear,
outputOrigin=image.GetOrigin(),
outputSpacing=iso_spacing,
outputDirection=image.GetDirection(),
defaultPixelValue=0
)
print(f"Resampled size: {image.GetSize()}")
print(f"Resampled spacing: {image.GetSpacing()}")
# Bias field correction (N4)
image = sitk.N4BiasFieldCorrection(image, image > 0)
# Save
sitk.WriteImage(image, output_path)
return imageimport SimpleITK as sitk
def segment_brain_tumor(mri_path, seed_points, output_path):
"""
Segment brain tumor from MRI using region growing and morphology.
Args:
mri_path: Path to MRI image
seed_points: List of seed coordinates [(x,y,z), ...]
output_path: Path to save segmentation
"""
# Read and preprocess
image = sitk.ReadImage(mri_path)
image = sitk.Cast(image, sitk.sitkFloat32)
# Denoise
image = sitk.CurvatureAnisotropicDiffusion(
image,
timeStep=0.0625,
conductanceParameter=3.0,
numberOfIterations=5
)
# Initial segmentation with confidence connected
initial_seg = sitk.ConfidenceConnected(
image,
seedList=seed_points,
numberOfIterations=5,
multiplier=2.5,
initialNeighborhoodRadius=1
)
# Morphological cleanup
# Remove small noise
cleaned = sitk.BinaryMorphologicalOpening(
initial_seg,
kernelRadius=[2, 2, 2],
kernelType=sitk.sitkBall
)
# Fill holes
filled = sitk.BinaryFillhole(cleaned)
# Smooth boundaries
smoothed = sitk.BinaryMorphologicalClosing(
filled,
kernelRadius=[1, 1, 1],
kernelType=sitk.sitkBall
)
# Label connected components
labeled = sitk.ConnectedComponent(smoothed)
# Keep only largest component
labeled = sitk.RelabelComponent(labeled, minimumObjectSize=500)
# Get statistics
shape_stats = sitk.LabelShapeStatisticsImageFilter()
shape_stats.Execute(labeled)
if shape_stats.GetNumberOfLabels() > 0:
label = shape_stats.GetLabels()[0]
volume_mm3 = shape_stats.GetPhysicalSize(label)
centroid = shape_stats.GetCentroid(label)
print(f"Tumor volume: {volume_mm3:.2f} mm³")
print(f"Tumor centroid: {centroid}")
# Save segmentation
sitk.WriteImage(labeled, output_path)
return labeledimport SimpleITK as sitk
def ct_mri_registration(ct_path, mri_path, output_path):
"""
Register CT to MRI using mutual information.
Multi-modal registration requires mutual information metric.
"""
# Read images
fixed_ct = sitk.ReadImage(ct_path, sitk.sitkFloat32)
moving_mri = sitk.ReadImage(mri_path, sitk.sitkFloat32)
# Initialize rigid transform
initial_transform = sitk.CenteredTransformInitializer(
fixed_ct,
moving_mri,
sitk.Euler3DTransform(),
sitk.CenteredTransformInitializerFilter.GEOMETRY
)
# Registration method
registration = sitk.ImageRegistrationMethod()
# Mutual information for multi-modal
registration.SetMetricAsMattesMutualInformation(numberOfHistogramBins=50)
registration.SetMetricSamplingStrategy(registration.RANDOM)
registration.SetMetricSamplingPercentage(0.01)
# Optimizer
registration.SetOptimizerAsGradientDescent(
learningRate=1.0,
numberOfIterations=100,
convergenceMinimumValue=1e-6,
convergenceWindowSize=10
)
registration.SetOptimizerScalesFromPhysicalShift()
# Multi-resolution
registration.SetShrinkFactorsPerLevel([4, 2, 1])
registration.SetSmoothingSigmasPerLevel([2.0, 1.0, 0.0])
registration.SetSmoothingSigmasAreSpecifiedInPhysicalUnits(True)
registration.SetInterpolator(sitk.sitkLinear)
registration.SetInitialTransform(initial_transform, inPlace=False)
# Monitor progress
def iteration_callback():
if registration.GetOptimizerIteration() % 10 == 0:
print(f"Iteration {registration.GetOptimizerIteration()}: "
f"Metric = {registration.GetMetricValue():.4f}")
registration.AddCommand(sitk.sitkIterationEvent, iteration_callback)
# Execute
final_transform = registration.Execute(fixed_ct, moving_mri)
# Apply transform
registered_mri = sitk.Resample(
moving_mri,
fixed_ct,
final_transform,
sitk.sitkLinear,
0.0,
moving_mri.GetPixelID()
)
# Save
sitk.WriteImage(registered_mri, output_path)
# Save transform
sitk.WriteTransform(final_transform, output_path.replace('.nii', '_transform.tfm'))
return registered_mri, final_transformimport SimpleITK as sitk
import numpy as np
def detect_lung_nodules(ct_path):
"""
Detect lung nodules in CT scan.
Uses thresholding, morphology, and shape analysis.
"""
# Read CT
ct = sitk.ReadImage(ct_path)
ct = sitk.Cast(ct, sitk.sitkFloat32)
# Threshold for lung tissue (-1000 to -400 HU)
lung_mask = sitk.BinaryThreshold(
ct,
lowerThreshold=-1000,
upperThreshold=-400,
insideValue=1,
outsideValue=0
)
# Fill lung regions
lung_mask = sitk.BinaryFillhole(lung_mask)
# Threshold for potential nodules (-400 to 100 HU)
nodule_candidates = sitk.BinaryThreshold(
ct,
lowerThreshold=-400,
upperThreshold=100,
insideValue=1,
outsideValue=0
)
# Mask to lung region only
nodule_candidates = sitk.And(nodule_candidates, lung_mask)
# Label candidates
labeled = sitk.ConnectedComponent(nodule_candidates)
# Filter by shape
shape_stats = sitk.LabelShapeStatisticsImageFilter()
shape_stats.Execute(labeled)
nodules = []
for label in shape_stats.GetLabels():
size = shape_stats.GetNumberOfPixels(label)
roundness = shape_stats.GetRoundness(label)
physical_size = shape_stats.GetPhysicalSize(label)
# Filter criteria: size 50-5000 mm³, roundness > 0.5
if 50 < physical_size < 5000 and roundness > 0.5:
centroid = shape_stats.GetCentroid(label)
nodules.append({
'label': label,
'volume_mm3': physical_size,
'centroid': centroid,
'roundness': roundness
})
print(f"Detected {len(nodules)} potential nodules")
for i, nodule in enumerate(nodules):
print(f" Nodule {i+1}: {nodule['volume_mm3']:.1f} mm³ at {nodule['centroid']}")
return nodules, labeledimport SimpleITK as sitk
def segment_cardiac_chambers(cardiac_mri_path):
"""
Segment cardiac chambers from MRI using watershed.
"""
# Read cardiac MRI
image = sitk.ReadImage(cardiac_mri_path)
image = sitk.Cast(image, sitk.sitkFloat32)
# Smooth
smoothed = sitk.SmoothingRecursiveGaussian(image, sigma=[1.0, 1.0, 1.0])
# Compute gradient magnitude
gradient = sitk.GradientMagnitudeRecursiveGaussian(smoothed, sigma=1.0)
# Watershed segmentation
labels = sitk.MorphologicalWatershed(
gradient,
level=0.1,
markWatershedLine=False
)
# Analyze chambers
shape_stats = sitk.LabelShapeStatisticsImageFilter()
shape_stats.Execute(labels)
intensity_stats = sitk.LabelIntensityStatisticsImageFilter()
intensity_stats.Execute(image, labels)
chambers = []
for label in shape_stats.GetLabels():
volume = shape_stats.GetPhysicalSize(label)
mean_intensity = intensity_stats.GetMean(label)
if volume > 1000: # Filter small regions
chambers.append({
'label': label,
'volume_mm3': volume,
'mean_intensity': mean_intensity,
'centroid': shape_stats.GetCentroid(label)
})
print(f"Identified {len(chambers)} cardiac chambers")
return chambers, labelsimport SimpleITK as sitk
def segment_bone_from_ct(ct_path, output_path):
"""
Segment bone from CT using Hounsfield unit thresholding.
Bone typically has HU > 200.
"""
# Read CT
ct = sitk.ReadImage(ct_path)
# Threshold for bone (HU > 200)
bone_mask = sitk.BinaryThreshold(
ct,
lowerThreshold=200,
upperThreshold=3000,
insideValue=1,
outsideValue=0
)
# Morphological cleanup
# Remove small fragments
bone_mask = sitk.BinaryMorphologicalOpening(
bone_mask,
kernelRadius=[2, 2, 2],
kernelType=sitk.sitkBall
)
# Fill small holes
bone_mask = sitk.BinaryMorphologicalClosing(
bone_mask,
kernelRadius=[3, 3, 3],
kernelType=sitk.sitkBall
)
# Label bones
labeled = sitk.ConnectedComponent(bone_mask)
labeled = sitk.RelabelComponent(labeled, minimumObjectSize=1000)
# Analyze each bone
shape_stats = sitk.LabelShapeStatisticsImageFilter()
shape_stats.Execute(labeled)
print(f"Segmented {shape_stats.GetNumberOfLabels()} bones")
for label in shape_stats.GetLabels():
volume = shape_stats.GetPhysicalSize(label)
centroid = shape_stats.GetCentroid(label)
print(f" Bone {label}: {volume:.2f} mm³")
# Save
sitk.WriteImage(labeled, output_path)
return labeledimport SimpleITK as sitk
def enhance_and_segment_vessels(angiography_path, output_path):
"""
Enhance and segment blood vessels from angiography.
Uses Hessian-based vessel enhancement.
"""
# Read image
image = sitk.ReadImage(angiography_path)
image = sitk.Cast(image, sitk.sitkFloat32)
# Smooth
smoothed = sitk.SmoothingRecursiveGaussian(image, sigma=[0.5, 0.5, 0.5])
# Hessian-based vessel enhancement
# Note: This is a simplified approach
# For production, use dedicated vessel enhancement filters
# Multi-scale enhancement
enhanced = None
for sigma in [1.0, 2.0, 3.0]:
hessian = sitk.HessianRecursiveGaussian(smoothed, sigma=sigma)
# Process Hessian eigenvalues for vesselness
# (simplified - actual implementation more complex)
# Threshold enhanced image
vessels = sitk.BinaryThreshold(
smoothed, # Using smoothed as proxy
lowerThreshold=100,
upperThreshold=1000
)
# Morphological cleanup
vessels = sitk.BinaryMorphologicalOpening(
vessels,
kernelRadius=[1, 1, 1],
kernelType=sitk.sitkBall
)
# Skeletonize vessels
skeleton = sitk.BinaryThinning(vessels)
# Save
sitk.WriteImage(skeleton, output_path)
return skeletonimport SimpleITK as sitk
def multi_atlas_segmentation(target_path, atlas_paths, atlas_label_paths, output_path):
"""
Multi-atlas segmentation using label fusion.
Args:
target_path: Target image to segment
atlas_paths: List of atlas image paths
atlas_label_paths: List of atlas label paths
output_path: Output segmentation path
"""
# Read target
target = sitk.ReadImage(target_path, sitk.sitkFloat32)
registered_labels = []
# Register each atlas to target
for atlas_path, label_path in zip(atlas_paths, atlas_label_paths):
print(f"Processing atlas: {atlas_path}")
# Read atlas and labels
atlas = sitk.ReadImage(atlas_path, sitk.sitkFloat32)
labels = sitk.ReadImage(label_path)
# Register atlas to target
initial_transform = sitk.CenteredTransformInitializer(
target,
atlas,
sitk.AffineTransform(3),
sitk.CenteredTransformInitializerFilter.GEOMETRY
)
registration = sitk.ImageRegistrationMethod()
registration.SetMetricAsMattesMutualInformation(50)
registration.SetOptimizerAsGradientDescent(
learningRate=1.0,
numberOfIterations=100
)
registration.SetInterpolator(sitk.sitkLinear)
registration.SetInitialTransform(initial_transform)
final_transform = registration.Execute(target, atlas)
# Warp labels to target space
warped_labels = sitk.Resample(
labels,
target,
final_transform,
sitk.sitkNearestNeighbor, # Use nearest neighbor for labels
0,
labels.GetPixelID()
)
registered_labels.append(warped_labels)
# Label fusion using STAPLE
consensus = sitk.STAPLEImageFilter()
consensus.SetForegroundValue(1)
fused = consensus.Execute(*registered_labels)
# Threshold consensus
final_segmentation = sitk.BinaryThreshold(
fused,
lowerThreshold=0.5,
upperThreshold=1.0,
insideValue=1,
outsideValue=0
)
# Save
sitk.WriteImage(final_segmentation, output_path)
return final_segmentationimport SimpleITK as sitk
import numpy as np
def texture_based_classification(image_path, label_path):
"""
Classify regions based on texture features.
Uses local statistics and filtering.
"""
# Read image and labels
image = sitk.ReadImage(image_path)
labels = sitk.ReadImage(label_path)
# Compute texture features for each region
intensity_stats = sitk.LabelIntensityStatisticsImageFilter()
intensity_stats.Execute(image, labels)
# Local gradient magnitude
gradient = sitk.GradientMagnitude(image)
gradient_stats = sitk.LabelIntensityStatisticsImageFilter()
gradient_stats.Execute(gradient, labels)
# Analyze each region
features = []
for label in intensity_stats.GetLabels():
feature_dict = {
'label': label,
'mean_intensity': intensity_stats.GetMean(label),
'std_intensity': intensity_stats.GetStandardDeviation(label),
'mean_gradient': gradient_stats.GetMean(label),
'std_gradient': gradient_stats.GetStandardDeviation(label)
}
features.append(feature_dict)
return featuresimport SimpleITK as sitk
import numpy as np
def analyze_4d_timeseries(timeseries_path):
"""
Analyze 4D time series (3D + time).
Computes temporal statistics and motion.
"""
# Read 4D image
image_4d = sitk.ReadImage(timeseries_path)
# Convert to NumPy for temporal analysis
array_4d = sitk.GetArrayFromImage(image_4d)
# Shape: (time, depth, height, width)
num_timepoints = array_4d.shape[0]
print(f"Time points: {num_timepoints}")
# Compute temporal mean
mean_over_time = np.mean(array_4d, axis=0)
mean_image = sitk.GetImageFromArray(mean_over_time)
# Compute temporal standard deviation
std_over_time = np.std(array_4d, axis=0)
std_image = sitk.GetImageFromArray(std_over_time)
# Copy spatial metadata from first timepoint
# Extract first timepoint
first_timepoint = sitk.Extract(
image_4d,
size=list(image_4d.GetSize()[:3]) + [0],
index=[0, 0, 0, 0]
)
mean_image.CopyInformation(first_timepoint)
std_image.CopyInformation(first_timepoint)
# Identify regions with high temporal variation
high_variation = sitk.BinaryThreshold(
std_image,
lowerThreshold=np.percentile(std_over_time, 90),
upperThreshold=std_over_time.max()
)
return mean_image, std_image, high_variationimport SimpleITK as sitk
import os
from pathlib import Path
def batch_process_images(input_dir, output_dir, processing_func):
"""
Batch process all images in directory.
Args:
input_dir: Input directory path
output_dir: Output directory path
processing_func: Function to apply to each image
"""
# Create output directory
Path(output_dir).mkdir(parents=True, exist_ok=True)
# Process each image
for filename in os.listdir(input_dir):
if filename.endswith(('.nii', '.nii.gz', '.mha', '.dcm')):
input_path = os.path.join(input_dir, filename)
output_path = os.path.join(output_dir, filename)
try:
print(f"Processing {filename}...")
image = sitk.ReadImage(input_path)
result = processing_func(image)
sitk.WriteImage(result, output_path)
print(f" ✓ Saved to {output_path}")
except Exception as e:
print(f" ✗ Error processing {filename}: {e}")
# Example usage
def my_processing(image):
"""Custom processing function."""
image = sitk.SmoothingRecursiveGaussian(image, sigma=[1.0, 1.0, 1.0])
image = sitk.RescaleIntensity(image, outputMinimum=0, outputMaximum=255)
return sitk.Cast(image, sitk.sitkUInt8)
batch_process_images('input_scans/', 'processed_scans/', my_processing)import SimpleITK as sitk
import numpy as np
def assess_image_quality(image_path):
"""
Assess image quality metrics.
Computes SNR, CNR, and other quality measures.
"""
image = sitk.ReadImage(image_path)
array = sitk.GetArrayFromImage(image)
# Signal-to-Noise Ratio (simplified)
signal = array[array > np.percentile(array, 50)].mean()
noise = array[array < np.percentile(array, 10)].std()
snr = signal / noise if noise > 0 else float('inf')
# Contrast-to-Noise Ratio
foreground = array[array > np.percentile(array, 75)].mean()
background = array[array < np.percentile(array, 25)].mean()
cnr = (foreground - background) / noise if noise > 0 else float('inf')
# Compute statistics
stats = sitk.StatisticsImageFilter()
stats.Execute(image)
quality_metrics = {
'snr': snr,
'cnr': cnr,
'mean': stats.GetMean(),
'std': stats.GetSigma(),
'min': stats.GetMinimum(),
'max': stats.GetMaximum()
}
print("Image Quality Metrics:")
for key, value in quality_metrics.items():
print(f" {key.upper()}: {value:.2f}")
return quality_metricsInstall with Tessl CLI
npx tessl i tessl/pypi-simpleitk