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 reference for SimpleITK's type system, pixel types, and type conversions.
SimpleITK supports 26 distinct pixel types organized into categories:
sitkUInt8 # Unsigned 8-bit: 0 to 255
sitkInt8 # Signed 8-bit: -128 to 127
sitkUInt16 # Unsigned 16-bit: 0 to 65,535
sitkInt16 # Signed 16-bit: -32,768 to 32,767
sitkUInt32 # Unsigned 32-bit: 0 to 4,294,967,295
sitkInt32 # Signed 32-bit: -2,147,483,648 to 2,147,483,647
sitkUInt64 # Unsigned 64-bit
sitkInt64 # Signed 64-bitsitkFloat32 # 32-bit float (single precision): ~7 decimal digits
sitkFloat64 # 64-bit float (double precision): ~15 decimal digitssitkComplexFloat32 # Complex with 32-bit real and imaginary
sitkComplexFloat64 # Complex with 64-bit real and imaginarysitkVectorUInt8 = 13 # Vector of UInt8
sitkVectorInt8 = 14
sitkVectorUInt16 = 15
sitkVectorInt16 = 16
sitkVectorUInt32 = 17
sitkVectorInt32 = 18
sitkVectorUInt64 = 19
sitkVectorInt64 = 20
sitkVectorFloat32 = 21 # RGB images, gradient fields
sitkVectorFloat64 = 22sitkLabelUInt8 = 23 # Up to 255 labels
sitkLabelUInt16 = 24 # Up to 65,535 labels
sitkLabelUInt32 = 25 # Up to 4 billion labels
sitkLabelUInt64 = 26 # Extremely large label setssitkUnknown = -1 # Unspecified or error state| Use Case | Recommended Type | Reason |
|---|---|---|
| Binary masks | sitkUInt8 | 1 byte, values 0-1 |
| CT scans | sitkInt16 | Hounsfield units (-1024 to 3071) |
| MRI | sitkFloat32 | Arbitrary intensity scale |
| PET | sitkFloat32 | Continuous values |
| Segmentation (< 256 labels) | sitkLabelUInt8 | Memory efficient |
| Segmentation (> 256 labels) | sitkLabelUInt16 or sitkLabelUInt32 | More labels |
| RGB images | sitkVectorUInt8 | 3 components per pixel |
| Gradient fields | sitkVectorFloat32 | 3D vectors |
| Distance maps | sitkFloat32 | Signed distances |
| Intermediate processing | sitkFloat32 | Avoid precision loss |
| Type | Bytes/Pixel | 256³ Volume Memory |
|---|---|---|
| UInt8 | 1 | 16 MB |
| UInt16 | 2 | 32 MB |
| Float32 | 4 | 64 MB |
| Float64 | 8 | 128 MB |
| VectorFloat32 (3D) | 12 | 192 MB |
def Cast(image, pixelID: int):
"""
Cast image to specified pixel type.
Args:
image: Input image
pixelID: Target pixel type constant
Returns:
Image with new pixel type
"""
class CastImageFilter:
"""Object-oriented interface for type casting."""
def SetOutputPixelType(self, pixelID: int) -> None:
"""Set target pixel type."""
def Execute(self, image):
"""Returns: Casted image"""import SimpleITK as sitk
# Integer to integer: Direct conversion
uint8_img = sitk.Cast(int16_img, sitk.sitkUInt8)
# Float to integer: Truncation (not rounding)
int_img = sitk.Cast(float_img, sitk.sitkInt16)
# 42.7 becomes 42, not 43
# Integer to float: Exact conversion
float_img = sitk.Cast(int_img, sitk.sitkFloat32)
# Narrowing conversions: May overflow/clamp
uint8_img = sitk.Cast(uint16_img, sitk.sitkUInt8)
# Values > 255 wrap around or clamp (implementation-dependent)def safe_cast_to_uint8(image):
"""Safely cast to UInt8 with proper scaling."""
# Rescale to [0, 255] first
rescaled = sitk.RescaleIntensity(
image,
outputMinimum=0,
outputMaximum=255
)
# Now safe to cast
return sitk.Cast(rescaled, sitk.sitkUInt8)
def safe_cast_with_clamping(image, target_type, min_val, max_val):
"""Cast with explicit clamping."""
# Clamp to valid range
clamped = sitk.Clamp(image, lowerBound=min_val, upperBound=max_val)
# Cast
return sitk.Cast(clamped, target_type)# Integer types
sitkUInt8 <-> numpy.uint8
sitkInt8 <-> numpy.int8
sitkUInt16 <-> numpy.uint16
sitkInt16 <-> numpy.int16
sitkUInt32 <-> numpy.uint32
sitkInt32 <-> numpy.int32
sitkUInt64 <-> numpy.uint64
sitkInt64 <-> numpy.int64
# Float types
sitkFloat32 <-> numpy.float32
sitkFloat64 <-> numpy.float64
# Complex types
sitkComplexFloat32 <-> numpy.complex64
sitkComplexFloat64 <-> numpy.complex128import SimpleITK as sitk
import numpy as np
# Vector types add component dimension
# SimpleITK VectorFloat32 (3 components) <-> NumPy float32 with shape (3, ...)
# Create RGB image
rgb_array = np.random.randint(0, 256, (3, 256, 256), dtype=np.uint8)
rgb_image = sitk.GetImageFromArray(rgb_array, isVector=True)
# Type is VectorUInt8 with 3 components
print(f"Type: {rgb_image.GetPixelIDTypeAsString()}")
print(f"Components: {rgb_image.GetNumberOfComponentsPerPixel()}")# Some filters require specific types
# Morphological filters: Typically require integer types
binary_image = sitk.Cast(image, sitk.sitkUInt8)
eroded = sitk.BinaryErode(binary_image, kernelRadius=[2, 2])
# Statistical filters: Work with any scalar type
stats = sitk.StatisticsImageFilter()
stats.Execute(image) # Any scalar type OK
# FFT filters: Require float types
float_image = sitk.Cast(image, sitk.sitkFloat32)
fft = sitk.ForwardFFT(float_image)# Same type: Result has same type
uint8_1 = sitk.Image(100, 100, sitk.sitkUInt8)
uint8_2 = sitk.Image(100, 100, sitk.sitkUInt8)
result = uint8_1 + uint8_2 # Result is sitkUInt8
# Different types: Requires explicit casting
uint8_img = sitk.Image(100, 100, sitk.sitkUInt8)
float_img = sitk.Image(100, 100, sitk.sitkFloat32)
# result = uint8_img + float_img # ERROR!
# Cast to common type
uint8_as_float = sitk.Cast(uint8_img, sitk.sitkFloat32)
result = uint8_as_float + float_img # OK# Get pixel type ID
pixel_id = image.GetPixelIDValue()
# Get human-readable type name
type_name = image.GetPixelIDTypeAsString()
# Check for specific type
if pixel_id == sitk.sitkFloat32:
print("Image is Float32")
# Check type category
if pixel_id in [sitk.sitkFloat32, sitk.sitkFloat64]:
print("Image is floating point")
# Get component count
num_components = image.GetNumberOfComponentsPerPixel()
if num_components > 1:
print(f"Vector image with {num_components} components")def is_integer_type(image):
"""Check if image has integer pixel type."""
pixel_id = image.GetPixelIDValue()
integer_types = [
sitk.sitkUInt8, sitk.sitkInt8,
sitk.sitkUInt16, sitk.sitkInt16,
sitk.sitkUInt32, sitk.sitkInt32,
sitk.sitkUInt64, sitk.sitkInt64
]
return pixel_id in integer_types
def is_float_type(image):
"""Check if image has floating point type."""
pixel_id = image.GetPixelIDValue()
return pixel_id in [sitk.sitkFloat32, sitk.sitkFloat64]
def is_vector_type(image):
"""Check if image has vector pixel type."""
return image.GetNumberOfComponentsPerPixel() > 1
def is_label_type(image):
"""Check if image has label pixel type."""
pixel_id = image.GetPixelIDValue()
label_types = [
sitk.sitkLabelUInt8, sitk.sitkLabelUInt16,
sitk.sitkLabelUInt32, sitk.sitkLabelUInt64
]
return pixel_id in label_typesdef prepare_for_processing(image):
"""Convert to optimal type for processing."""
# Get current type
current_type = image.GetPixelIDValue()
# Convert to Float32 for processing
if current_type != sitk.sitkFloat32:
print(f"Converting from {image.GetPixelIDTypeAsString()} to Float32")
image = sitk.Cast(image, sitk.sitkFloat32)
return image, current_type
def restore_original_type(image, original_type):
"""Restore original pixel type after processing."""
if image.GetPixelIDValue() != original_type:
# Rescale if going to integer type
if original_type in [sitk.sitkUInt8, sitk.sitkUInt16, sitk.sitkInt16]:
# Rescale to appropriate range
if original_type == sitk.sitkUInt8:
image = sitk.RescaleIntensity(image, outputMinimum=0, outputMaximum=255)
elif original_type == sitk.sitkUInt16:
image = sitk.RescaleIntensity(image, outputMinimum=0, outputMaximum=65535)
image = sitk.Cast(image, original_type)
return imagedef prepare_for_png_export(image):
"""Convert image to PNG-compatible type (UInt8 or UInt16)."""
# Rescale to [0, 255]
rescaled = sitk.RescaleIntensity(image, outputMinimum=0, outputMaximum=255)
# Cast to UInt8
return sitk.Cast(rescaled, sitk.sitkUInt8)
def prepare_for_dicom_export(image):
"""Convert to DICOM-compatible type (typically Int16)."""
# Cast to Int16
return sitk.Cast(image, sitk.sitkInt16)def extract_rgb_channels(rgb_image):
"""Extract R, G, B channels from RGB image."""
# Convert to NumPy
array = sitk.GetArrayFromImage(rgb_image) # Shape: (3, H, W)
# Extract channels
r_array = array[0, :, :]
g_array = array[1, :, :]
b_array = array[2, :, :]
# Convert back to SimpleITK
r_image = sitk.GetImageFromArray(r_array)
g_image = sitk.GetImageFromArray(g_array)
b_image = sitk.GetImageFromArray(b_array)
# Copy metadata
for img in [r_image, g_image, b_image]:
img.CopyInformation(rgb_image)
return r_image, g_image, b_image
def compose_rgb_image(r_image, g_image, b_image):
"""Compose R, G, B channels into RGB image."""
# Use Compose filter
rgb_image = sitk.Compose(r_image, g_image, b_image)
return rgb_image# Extract single component
component_0 = sitk.VectorIndexSelectionCast(vector_image, index=0)
# Compute magnitude
magnitude = sitk.VectorMagnitude(vector_image)
# For single-component vectors
scalar = vector_image.ToScalarImage()def select_label_type(num_labels):
"""Select appropriate label type for number of labels."""
if num_labels <= 255:
return sitk.sitkLabelUInt8
elif num_labels <= 65535:
return sitk.sitkLabelUInt16
elif num_labels <= 4294967295:
return sitk.sitkLabelUInt32
else:
return sitk.sitkLabelUInt64
# Usage
num_objects = 1000
label_type = select_label_type(num_objects)
labeled_image = sitk.Cast(labeled_image, label_type)# Label types are semantically different from regular integers
# Use label types for segmentation masks
# CORRECT: Use label type
segmentation = sitk.ConnectedComponent(binary)
# Returns sitkLabelUInt* type
# For arithmetic: Convert to regular integer
regular_uint = sitk.Cast(segmentation, sitk.sitkUInt32)# WRONG: Precision loss
float64_image = sitk.ReadImage('precise.nii') # Float64
float32_image = sitk.Cast(float64_image, sitk.sitkFloat32) # Loses precision
# CORRECT: Keep Float64 if precision needed
result = sitk.SomeFilter(float64_image) # Process as Float64# WRONG: Overflow risk
uint8_img1 = sitk.Image(100, 100, sitk.sitkUInt8)
uint8_img1.SetPixel(50, 50, 200)
uint8_img2 = sitk.Image(100, 100, sitk.sitkUInt8)
uint8_img2.SetPixel(50, 50, 100)
result = uint8_img1 + uint8_img2 # 200 + 100 = 300, overflows UInt8!
# CORRECT: Cast to larger type first
float1 = sitk.Cast(uint8_img1, sitk.sitkFloat32)
float2 = sitk.Cast(uint8_img2, sitk.sitkFloat32)
result = float1 + float2
result = sitk.Clamp(result, lowerBound=0, upperBound=255)
result = sitk.Cast(result, sitk.sitkUInt8)# Truncation (default)
float_img = sitk.Image(100, 100, sitk.sitkFloat32)
float_img.SetPixel(50, 50, 42.7)
int_img = sitk.Cast(float_img, sitk.sitkInt16)
value = int_img.GetPixel(50, 50) # 42 (truncated)
# Rounding (add 0.5 before casting)
rounded = float_img + 0.5
int_rounded = sitk.Cast(rounded, sitk.sitkInt16)
value = int_rounded.GetPixel(50, 50) # 43 (rounded)# Modulus requires integer types
int_img = sitk.Cast(image, sitk.sitkInt32)
result = int_img % 10
# Bitwise operations require integer types
binary1 = sitk.Cast(binary1, sitk.sitkUInt8)
binary2 = sitk.Cast(binary2, sitk.sitkUInt8)
result = binary1 & binary2# Logarithm requires float types
float_img = sitk.Cast(image, sitk.sitkFloat32)
log_img = sitk.Log(float_img)
# Trigonometric functions require float
sin_img = sitk.Sin(float_img)
cos_img = sitk.Cos(float_img)# FFT produces complex images
float_img = sitk.Cast(image, sitk.sitkFloat32)
fft = sitk.ForwardFFT(float_img) # Returns sitkComplexFloat32
# Extract components
real_part = sitk.ComplexToReal(fft)
imag_part = sitk.ComplexToImaginary(fft)
magnitude = sitk.ComplexToModulus(fft)
phase = sitk.ComplexToPhase(fft)
# Reconstruct
complex_img = sitk.RealAndImaginaryToComplex(real_part, imag_part)def select_processing_type(image):
"""Select appropriate type for processing."""
pixel_id = image.GetPixelIDValue()
# Already float: Keep as is
if pixel_id in [sitk.sitkFloat32, sitk.sitkFloat64]:
return image, pixel_id
# Integer: Convert to Float32
if pixel_id in [sitk.sitkUInt8, sitk.sitkInt8, sitk.sitkUInt16, sitk.sitkInt16]:
return sitk.Cast(image, sitk.sitkFloat32), pixel_id
# Large integers: Convert to Float64
if pixel_id in [sitk.sitkUInt32, sitk.sitkInt32, sitk.sitkUInt64, sitk.sitkInt64]:
return sitk.Cast(image, sitk.sitkFloat64), pixel_id
# Default: Float32
return sitk.Cast(image, sitk.sitkFloat32), pixel_idclass TypeConverter:
"""Utility class for type conversions."""
@staticmethod
def to_float32(image):
"""Convert any type to Float32."""
if image.GetPixelIDValue() != sitk.sitkFloat32:
return sitk.Cast(image, sitk.sitkFloat32)
return image
@staticmethod
def to_uint8_normalized(image):
"""Convert to UInt8 with normalization."""
rescaled = sitk.RescaleIntensity(image, outputMinimum=0, outputMaximum=255)
return sitk.Cast(rescaled, sitk.sitkUInt8)
@staticmethod
def to_label_uint16(image):
"""Convert to label type."""
# Ensure non-negative integers
if image.GetPixelIDValue() not in [sitk.sitkUInt8, sitk.sitkUInt16, sitk.sitkUInt32]:
image = sitk.Cast(image, sitk.sitkUInt16)
return sitk.Cast(image, sitk.sitkLabelUInt16)# Error 1: Type mismatch in binary operation
try:
result = uint8_image + float_image
except RuntimeError as e:
print("Type mismatch - cast to common type")
float_uint8 = sitk.Cast(uint8_image, sitk.sitkFloat32)
result = float_uint8 + float_image
# Error 2: Invalid type for filter
try:
fft = sitk.ForwardFFT(uint8_image)
except RuntimeError as e:
print("FFT requires float type")
float_image = sitk.Cast(uint8_image, sitk.sitkFloat32)
fft = sitk.ForwardFFT(float_image)
# Error 3: Overflow in arithmetic
uint8_img = sitk.Image(100, 100, sitk.sitkUInt8)
uint8_img.SetPixel(50, 50, 250)
# result = uint8_img + 10 # May overflow
float_img = sitk.Cast(uint8_img, sitk.sitkFloat32)
result = float_img + 10 # SafeInstall with Tessl CLI
npx tessl i tessl/pypi-simpleitk