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 guide to working with SimpleITK and NumPy together.
SimpleITK provides seamless integration with NumPy for interoperability with the scientific Python ecosystem. Understanding dimension ordering and metadata preservation is crucial for correct implementation.
Critical: SimpleITK and NumPy use opposite dimension conventions.
import SimpleITK as sitk
import numpy as np
# SimpleITK: (X, Y, Z) = (width, height, depth)
image = sitk.Image(256, 256, 128, sitk.sitkFloat32)
print(f"SimpleITK size: {image.GetSize()}") # (256, 256, 128)
# NumPy: (Z, Y, X) = (depth, height, width)
array = sitk.GetArrayFromImage(image)
print(f"NumPy shape: {array.shape}") # (128, 256, 256)
# Accessing corresponding elements:
image.SetPixel(10, 20, 5, 100.0) # (x=10, y=20, z=5)
value = array[5, 20, 10] # [z, y, x]import SimpleITK as sitk
import numpy as np
# Read image
image = sitk.ReadImage('volume.nii.gz')
# Convert to NumPy array (creates copy)
array = sitk.GetArrayFromImage(image)
# Modify array
array = array * 2.0
array = np.clip(array, 0, 1000)
# Image and array are independent
del image # Safe - array is independent copy# Get view (no copy, shares memory)
image = sitk.ReadImage('large_volume.nii.gz')
array_view = sitk.GetArrayViewFromImage(image)
# Read-only operations are efficient
mean = array_view.mean()
std = array_view.std()
max_val = array_view.max()
# WARNING: Image must stay alive
# del image # DON'T DO THIS - array_view becomes invalid
# WARNING: Modifying view modifies image
# array_view *= 2.0 # This modifies the original image!# Create array
array = np.random.randn(100, 256, 256).astype(np.float32)
# Convert to image
image = sitk.GetImageFromArray(array)
# Image has default metadata - set proper values
image.SetSpacing((1.0, 1.0, 2.0))
image.SetOrigin((-127.5, -127.5, -99.0))
image.SetDirection((1, 0, 0, 0, 1, 0, 0, 0, 1))
# Save with metadata
sitk.WriteImage(image, 'output.nii.gz')import SimpleITK as sitk
import numpy as np
def process_with_numpy(input_path, output_path):
"""Complete workflow preserving spatial metadata."""
# 1. Read image (with metadata)
original_image = sitk.ReadImage(input_path)
# 2. Convert to NumPy
array = sitk.GetArrayFromImage(original_image)
# 3. Process with NumPy/SciPy
from scipy.ndimage import gaussian_filter, median_filter
# Apply filters
array = gaussian_filter(array, sigma=2.0)
array = median_filter(array, size=3)
# Normalize
array = (array - array.min()) / (array.max() - array.min())
array = array * 1000
# 4. Convert back to SimpleITK
result_image = sitk.GetImageFromArray(array)
# 5. CRITICAL: Copy spatial metadata from original
result_image.CopyInformation(original_image)
# 6. Save with preserved metadata
sitk.WriteImage(result_image, output_path)
return result_imageimport SimpleITK as sitk
import numpy as np
# Integer types
array_uint8 = np.random.randint(0, 256, (100, 100), dtype=np.uint8)
image_uint8 = sitk.GetImageFromArray(array_uint8)
# Result: sitkUInt8
array_int16 = np.random.randint(-1000, 1000, (100, 100), dtype=np.int16)
image_int16 = sitk.GetImageFromArray(array_int16)
# Result: sitkInt16
# Float types
array_float32 = np.random.randn(100, 100).astype(np.float32)
image_float32 = sitk.GetImageFromArray(array_float32)
# Result: sitkFloat32
# Convert between types
image_converted = sitk.Cast(image_uint8, sitk.sitkFloat32)import SimpleITK as sitk
import numpy as np
# Create RGB array (3 components)
r = np.random.randint(0, 256, (256, 256), dtype=np.uint8)
g = np.random.randint(0, 256, (256, 256), dtype=np.uint8)
b = np.random.randint(0, 256, (256, 256), dtype=np.uint8)
# Stack into (3, 256, 256)
rgb_array = np.stack([r, g, b], axis=0)
# Convert to vector image
rgb_image = sitk.GetImageFromArray(rgb_array, isVector=True)
print(f"Components: {rgb_image.GetNumberOfComponentsPerPixel()}") # 3
# Convert back
array = sitk.GetArrayFromImage(rgb_image)
print(f"Shape: {array.shape}") # (3, 256, 256)
# Extract channels
r_channel = array[0, :, :]
g_channel = array[1, :, :]
b_channel = array[2, :, :]def process_slices(volume_path, output_path):
"""Process 3D volume slice by slice."""
# Read volume
volume = sitk.ReadImage(volume_path)
array = sitk.GetArrayFromImage(volume) # Shape: (depth, height, width)
# Process each slice
processed = np.zeros_like(array)
for z in range(array.shape[0]):
slice_2d = array[z, :, :]
# 2D processing
slice_2d = slice_2d * 2.0
slice_2d = np.clip(slice_2d, 0, 1000)
processed[z, :, :] = slice_2d
# Convert back
result = sitk.GetImageFromArray(processed)
result.CopyInformation(volume)
sitk.WriteImage(result, output_path)
return resultimport SimpleITK as sitk
import numpy as np
from scipy import ndimage
def scipy_processing(image):
"""Use SciPy filters on SimpleITK image."""
# Convert to NumPy
array = sitk.GetArrayFromImage(image)
# SciPy operations
smoothed = ndimage.gaussian_filter(array, sigma=2.0)
edges = ndimage.sobel(smoothed)
labeled, num_features = ndimage.label(edges > 0.5)
# Convert back
result = sitk.GetImageFromArray(labeled.astype(np.uint32))
result.CopyInformation(image)
return result, num_featuresimport SimpleITK as sitk
import numpy as np
from skimage import filters, measure, morphology
def skimage_processing(image):
"""Use scikit-image on SimpleITK image."""
# Convert to NumPy
array = sitk.GetArrayFromImage(image)
# Scikit-image operations
threshold = filters.threshold_otsu(array)
binary = array > threshold
# Remove small objects
cleaned = morphology.remove_small_objects(binary, min_size=64)
# Label and measure
labeled = measure.label(cleaned)
regions = measure.regionprops(labeled)
print(f"Found {len(regions)} objects")
for region in regions:
print(f" Area: {region.area}, Centroid: {region.centroid}")
# Convert back
result = sitk.GetImageFromArray(labeled.astype(np.uint32))
result.CopyInformation(image)
return resultdef hybrid_pipeline(input_path, output_path):
"""Combine SimpleITK and NumPy/SciPy processing."""
# Start with SimpleITK
image = sitk.ReadImage(input_path)
# SimpleITK preprocessing
image = sitk.RescaleIntensity(image, outputMinimum=0, outputMaximum=1000)
image = sitk.SmoothingRecursiveGaussian(image, sigma=[1.0, 1.0, 1.0])
# NumPy/SciPy processing
array = sitk.GetArrayFromImage(image)
from scipy.ndimage import median_filter
array = median_filter(array, size=3)
# Back to SimpleITK
image = sitk.GetImageFromArray(array)
image.CopyInformation(sitk.ReadImage(input_path))
# SimpleITK postprocessing
image = sitk.Cast(image, sitk.sitkUInt16)
sitk.WriteImage(image, output_path)
return image# Use view for read-only operations
image = sitk.ReadImage('huge_volume.nii.gz')
array_view = sitk.GetArrayViewFromImage(image)
# Compute statistics without copying
mean = array_view.mean()
std = array_view.std()
percentiles = np.percentile(array_view, [25, 50, 75])def process_large_volume_chunked(volume_path):
"""Process very large volumes in chunks."""
volume = sitk.ReadImage(volume_path)
array = sitk.GetArrayFromImage(volume)
# Process in chunks to reduce memory
chunk_size = 10
for z_start in range(0, array.shape[0], chunk_size):
z_end = min(z_start + chunk_size, array.shape[0])
# Process chunk
chunk = array[z_start:z_end, :, :]
chunk = chunk * 2.0 # Some operation
array[z_start:z_end, :, :] = chunk
# Convert back
result = sitk.GetImageFromArray(array)
result.CopyInformation(volume)
return result# WRONG: Metadata lost
array = sitk.GetArrayFromImage(image)
result = sitk.GetImageFromArray(array * 2.0)
# result has default metadata (origin=0, spacing=1)
# CORRECT: Metadata preserved
array = sitk.GetArrayFromImage(image)
result = sitk.GetImageFromArray(array * 2.0)
result.CopyInformation(image) # Preserve spatial metadata# WRONG: Dimension mismatch
image_size = image.GetSize() # (256, 256, 128)
array = sitk.GetArrayFromImage(image)
# Trying to use image_size with array indexing fails
# CORRECT: Use array.shape
array_shape = array.shape # (128, 256, 256)
# Use array_shape for array operations# WRONG: Unexpected side effects
array_view = sitk.GetArrayViewFromImage(image)
array_view *= 2.0 # Modifies original image!
# CORRECT: Use copy for modifications
array = sitk.GetArrayFromImage(image) # Deep copy
array *= 2.0 # Safe modification# WRONG: View becomes invalid
image = sitk.ReadImage('data.nii')
array_view = sitk.GetArrayViewFromImage(image)
del image # array_view now points to invalid memory!
# CORRECT: Keep image alive
image = sitk.ReadImage('data.nii')
array_view = sitk.GetArrayViewFromImage(image)
# Use array_view while image is alive
result = array_view.mean()
# Now safe to delete imageInstall with Tessl CLI
npx tessl i tessl/pypi-simpleitk@2.5.1