CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/pypi-simpleitk

SimpleITK is a simplified interface to the Insight Toolkit (ITK) for image registration and segmentation

Pending

Quality

Pending

Does it follow best practices?

Impact

Pending

No eval scenarios have been run

Overview
Eval results
Files

real-world-scenarios.mddocs/examples/

Real-World Scenarios

Complete examples of SimpleITK usage in real-world medical and scientific imaging applications.

Medical Image Preprocessing Pipeline

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 image

Brain Tumor Segmentation

import 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 labeled

Multi-Modal Registration (CT-MRI)

import 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_transform

Lung Nodule Detection

import 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, labeled

Cardiac Image Analysis

import 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, labels

Bone Segmentation from CT

import 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 labeled

Vessel Enhancement and Segmentation

import 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 skeleton

Multi-Atlas Segmentation

import 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_segmentation

Texture Analysis

import 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 features

Time Series Analysis (4D Images)

import 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_variation

Batch Processing

import 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)

Image Quality Assessment

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_metrics

See Also

  • Quick Start Guide - Getting started
  • Registration Workflow - Registration details
  • Segmentation Workflow - Segmentation techniques
  • Edge Cases - Handling special cases

Install with Tessl CLI

npx tessl i tessl/pypi-simpleitk@2.5.1

docs

examples

edge-cases.md

performance-patterns.md

real-world-scenarios.md

index.md

tile.json