Insight Toolkit for N-dimensional image processing, segmentation, and registration in medical and scientific applications
—
Advanced scenarios, edge cases, and solutions to common problems.
import itk
def robust_image_read(filename, fallback_type=itk.F):
"""
Robustly read image with multiple fallback strategies.
"""
# Try 1: Automatic type detection
try:
return itk.imread(filename)
except RuntimeError as e:
print(f"Auto-detection failed: {e}")
# Try 2: With specified type
try:
return itk.imread(filename, fallback_type)
except RuntimeError as e:
print(f"Failed with {fallback_type}: {e}")
# Try 3: Try different pixel types
for pixel_type in [itk.UC, itk.US, itk.F, itk.D]:
try:
print(f"Trying pixel type: {pixel_type}")
return itk.imread(filename, pixel_type)
except RuntimeError:
continue
# All attempts failed
raise RuntimeError(f"Could not read {filename} with any method")
# Usage
try:
image = robust_image_read('problematic.dcm')
except RuntimeError as e:
print(f"Fatal error: {e}")import itk
def read_dicom_with_gaps(dicom_dir):
"""
Read DICOM series that may have missing slices.
"""
# Get series file names
names_generator = itk.GDCMSeriesFileNames.New()
names_generator.SetDirectory(dicom_dir)
series_uids = names_generator.GetSeriesUIDs()
if len(series_uids) == 0:
raise RuntimeError(f"No DICOM series found in {dicom_dir}")
print(f"Found {len(series_uids)} series")
for i, uid in enumerate(series_uids):
filenames = names_generator.GetFileNames(uid)
print(f"Series {i}: {len(filenames)} files")
try:
# Try to read series
ImageType = itk.Image[itk.SS, 3]
reader = itk.ImageSeriesReader[ImageType].New()
reader.SetFileNames(filenames)
dicom_io = itk.GDCMImageIO.New()
reader.SetImageIO(dicom_io)
reader.Update()
image = reader.GetOutput()
print(f"Successfully read series {i}")
print(f" Size: {itk.size(image)}")
print(f" Spacing: {itk.spacing(image)}")
return image
except RuntimeError as e:
print(f"Failed to read series {i}: {e}")
continue
raise RuntimeError("Could not read any DICOM series")
# Usage
image = read_dicom_with_gaps('/path/to/dicom/')import itk
import numpy as np
def process_huge_image_in_tiles(input_file, output_file, tile_size=512):
"""
Process very large image by processing tiles.
"""
# Read image metadata without loading pixel data
ImageType = itk.Image[itk.F, 2]
reader = itk.ImageFileReader[ImageType].New()
reader.SetFileName(input_file)
reader.UpdateOutputInformation()
full_region = reader.GetOutput().GetLargestPossibleRegion()
full_size = full_region.GetSize()
print(f"Full image size: {full_size[0]} x {full_size[1]}")
# Create output image
output_image = ImageType.New()
output_image.SetRegions(full_region)
output_image.SetSpacing(reader.GetOutput().GetSpacing())
output_image.SetOrigin(reader.GetOutput().GetOrigin())
output_image.Allocate()
# Process in tiles
for y in range(0, full_size[1], tile_size):
for x in range(0, full_size[0], tile_size):
# Define tile region
tile_index = itk.Index[2]()
tile_index[0] = x
tile_index[1] = y
tile_size_actual = itk.Size[2]()
tile_size_actual[0] = min(tile_size, full_size[0] - x)
tile_size_actual[1] = min(tile_size, full_size[1] - y)
tile_region = itk.ImageRegion[2]()
tile_region.SetIndex(tile_index)
tile_region.SetSize(tile_size_actual)
print(f"Processing tile at ({x}, {y})")
# Extract tile
extractor = itk.ExtractImageFilter[ImageType, ImageType].New()
extractor.SetInput(reader.GetOutput())
extractor.SetExtractionRegion(tile_region)
extractor.Update()
tile = extractor.GetOutput()
# Process tile
processed_tile = itk.median_image_filter(tile, radius=2)
# Copy processed tile back to output
tile_array = itk.array_from_image(processed_tile)
output_array = itk.array_view_from_image(output_image)
output_array[y:y+tile_size_actual[1], x:x+tile_size_actual[0]] = tile_array
# Save result
itk.imwrite(output_image, output_file)
print("Processing complete!")
# Usage
process_huge_image_in_tiles('huge_image.tif', 'processed.tif', tile_size=512)import itk
import gc
def process_many_images_safely(image_files):
"""
Process many images without memory leaks.
"""
for i, image_file in enumerate(image_files):
print(f"Processing {i+1}/{len(image_files)}: {image_file}")
try:
# Load image
image = itk.imread(image_file, itk.F)
# Process
result = itk.median_image_filter(image, radius=2)
# Save
output_file = image_file.replace('.png', '_processed.png')
itk.imwrite(result, output_file)
# Explicitly delete to free memory
del image
del result
# Force garbage collection every 10 images
if (i + 1) % 10 == 0:
gc.collect()
print(" Garbage collection performed")
except Exception as e:
print(f" Error: {e}")
continue
# Usage
files = [f'image_{i:04d}.png' for i in range(1000)]
process_many_images_safely(files)import itk
def mixed_type_pipeline(input_file, output_file):
"""
Handle type conversions in a processing pipeline.
"""
# Read as unsigned char
image_uc = itk.imread(input_file, itk.UC)
# Convert to float for processing
InputType = itk.Image[itk.UC, 2]
FloatType = itk.Image[itk.F, 2]
caster_to_float = itk.CastImageFilter[InputType, FloatType].New()
caster_to_float.SetInput(image_uc)
caster_to_float.Update()
image_float = caster_to_float.GetOutput()
# Process as float
smoothed = itk.discrete_gaussian_image_filter(image_float, variance=2.0)
# Normalize to [0, 255]
rescaled = itk.rescale_intensity_image_filter(
smoothed,
output_minimum=0.0,
output_maximum=255.0
)
# Convert back to unsigned char
caster_to_uc = itk.CastImageFilter[FloatType, InputType].New()
caster_to_uc.SetInput(rescaled)
caster_to_uc.Update()
result = caster_to_uc.GetOutput()
# Save
itk.imwrite(result, output_file)
return result
# Usage
result = mixed_type_pipeline('input.png', 'output.png')import itk
def safe_filter_instantiation(image, filter_name='MedianImageFilter', **kwargs):
"""
Safely instantiate filter with automatic type deduction.
"""
# Get image type
ImageType = type(image)
try:
# Try to get filter class
filter_class = getattr(itk, filter_name)
# Try to instantiate with image type
try:
FilterType = filter_class[ImageType, ImageType]
filter_instance = FilterType.New()
filter_instance.SetInput(image)
# Set parameters
for key, value in kwargs.items():
setter = getattr(filter_instance, f'Set{key}')
setter(value)
filter_instance.Update()
return filter_instance.GetOutput()
except KeyError:
print(f"Filter {filter_name} not available for type {ImageType}")
print("Available instantiations:")
print(filter_class)
raise
except AttributeError:
print(f"Filter {filter_name} not found in itk module")
# Search for similar filters
similar = itk.search(filter_name, case_sensitive=False)
if similar:
print(f"Did you mean one of these? {similar[:5]}")
raise
# Usage
image = itk.imread('input.png', itk.F)
try:
result = safe_filter_instantiation(image, 'MedianImageFilter', Radius=2)
except Exception as e:
print(f"Error: {e}")import itk
import numpy as np
def safe_normalization(image, epsilon=1e-10):
"""
Normalize image safely, handling zero variance.
"""
array = itk.array_from_image(image)
mean = np.mean(array)
std = np.std(array)
# Check for zero or near-zero std
if std < epsilon:
print(f"Warning: std={std} is very small, using epsilon={epsilon}")
std = epsilon
# Normalize
normalized = (array - mean) / std
# Convert back
result = itk.image_from_array(normalized.astype(np.float32))
result.SetSpacing(image.GetSpacing())
result.SetOrigin(image.GetOrigin())
result.SetDirection(image.GetDirection())
return result
# Usage
image = itk.imread('constant_image.png', itk.F)
normalized = safe_normalization(image)import itk
import numpy as np
def safe_matrix_inverse(matrix):
"""
Safely compute matrix inverse with singularity check.
"""
try:
# Try direct inversion
inverse = matrix.GetInverse()
return inverse
except RuntimeError as e:
print(f"Matrix is singular: {e}")
# Convert to NumPy for analysis
array = itk.array_from_matrix(matrix)
# Compute condition number
cond = np.linalg.cond(array)
print(f"Condition number: {cond}")
if cond > 1e10:
print("Matrix is ill-conditioned")
# Try pseudo-inverse
print("Computing pseudo-inverse...")
pseudo_inv = np.linalg.pinv(array)
result = itk.matrix_from_array(pseudo_inv)
return result
# Usage
transform = itk.AffineTransform[itk.D, 3].New()
matrix = transform.GetMatrix()
try:
inverse = safe_matrix_inverse(matrix)
except Exception as e:
print(f"Could not compute inverse: {e}")import itk
def robust_registration(fixed, moving, max_attempts=3):
"""
Attempt registration with multiple strategies.
"""
ImageType = itk.Image[itk.F, 3]
strategies = [
{
'name': 'Rigid with MI',
'transform': itk.VersorRigid3DTransform[itk.D],
'metric': itk.MattesMutualInformationImageToImageMetricv4,
'learning_rate': 1.0,
},
{
'name': 'Affine with MI',
'transform': itk.AffineTransform[itk.D, 3],
'metric': itk.MattesMutualInformationImageToImageMetricv4,
'learning_rate': 0.5,
},
{
'name': 'Rigid with MS',
'transform': itk.VersorRigid3DTransform[itk.D],
'metric': itk.MeanSquaresImageToImageMetricv4,
'learning_rate': 0.1,
},
]
for attempt, strategy in enumerate(strategies[:max_attempts], 1):
print(f"Attempt {attempt}: {strategy['name']}")
try:
registration = itk.ImageRegistrationMethodv4[ImageType, ImageType].New()
registration.SetFixedImage(fixed)
registration.SetMovingImage(moving)
# Setup transform
transform = strategy['transform'].New()
registration.SetInitialTransform(transform)
# Setup metric
metric = strategy['metric'][ImageType, ImageType].New()
if 'MutualInformation' in strategy['name']:
metric.SetNumberOfHistogramBins(50)
registration.SetMetric(metric)
# Setup optimizer
optimizer = itk.RegularStepGradientDescentOptimizerv4Template[itk.D].New()
optimizer.SetLearningRate(strategy['learning_rate'])
optimizer.SetMinimumStepLength(0.001)
optimizer.SetNumberOfIterations(100)
registration.SetOptimizer(optimizer)
# Multi-resolution
registration.SetNumberOfLevels(2)
registration.SetSmoothingSigmasPerLevel([1.0, 0.0])
registration.SetShrinkFactorsPerLevel([2, 1])
# Run registration
registration.Update()
final_transform = registration.GetTransform()
final_metric = registration.GetMetricValue()
print(f" Success! Final metric: {final_metric}")
return final_transform, final_metric
except Exception as e:
print(f" Failed: {e}")
if attempt == max_attempts:
raise RuntimeError("All registration attempts failed")
continue
# Usage
fixed = itk.imread('fixed.nii.gz', itk.F)
moving = itk.imread('moving.nii.gz', itk.F)
try:
transform, metric = robust_registration(fixed, moving)
aligned = itk.resample_image_filter(moving, transform=transform)
itk.imwrite(aligned, 'aligned.nii.gz')
except RuntimeError as e:
print(f"Registration failed: {e}")import itk
def segment_with_validation(image, seed, lower, upper):
"""
Perform segmentation with result validation.
"""
# Segment
segmented = itk.connected_threshold_image_filter(
image,
seed=seed,
lower=lower,
upper=upper,
replace_value=255
)
# Check if segmentation is empty
array = itk.array_from_image(segmented)
num_foreground = (array == 255).sum()
total_pixels = array.size
foreground_ratio = num_foreground / total_pixels
print(f"Segmented pixels: {num_foreground}/{total_pixels} ({foreground_ratio*100:.2f}%)")
if num_foreground == 0:
print("Warning: Segmentation is empty!")
print(f" Seed: {seed}")
print(f" Threshold: [{lower}, {upper}]")
# Get intensity at seed
seed_value = image.GetPixel(seed)
print(f" Intensity at seed: {seed_value}")
# Suggest new thresholds
suggested_lower = seed_value * 0.8
suggested_upper = seed_value * 1.2
print(f" Suggested thresholds: [{suggested_lower}, {suggested_upper}]")
return None
if foreground_ratio > 0.9:
print("Warning: Segmentation covers >90% of image!")
print(" Thresholds may be too permissive")
return None
return segmented
# Usage
image = itk.imread('input.png', itk.F)
seed = itk.Index[2]()
seed[0], seed[1] = 100, 100
result = segment_with_validation(image, seed, lower=50.0, upper=150.0)
if result is not None:
itk.imwrite(result, 'segmented.png')import itk
from pathlib import Path
def cross_platform_imread(file_path):
"""
Read image with cross-platform path handling.
"""
# Convert to Path object
path = Path(file_path)
# Resolve to absolute path
abs_path = path.resolve()
# Check existence
if not abs_path.exists():
raise FileNotFoundError(f"File not found: {abs_path}")
# Convert to string with forward slashes (ITK prefers this)
path_str = str(abs_path).replace('\\', '/')
return itk.imread(path_str)
# Usage
image = cross_platform_imread('data/images/scan.nii.gz')import itk
import sys
import traceback
def debug_itk_operation(operation_func, *args, **kwargs):
"""
Execute ITK operation with comprehensive error reporting.
"""
print(f"Executing: {operation_func.__name__}")
print(f"Arguments: {args}")
print(f"Keyword arguments: {kwargs}")
try:
result = operation_func(*args, **kwargs)
print("✓ Operation successful")
return result
except Exception as e:
print(f"✗ Operation failed: {type(e).__name__}")
print(f"Error message: {e}")
print("\nFull traceback:")
traceback.print_exc()
# Additional ITK-specific debugging
print("\nITK Environment:")
print(f" ITK Version: {itk.Version.GetITKVersion()}")
print(f" Python: {sys.version}")
print(f" Threads: {itk.get_nthreads()}")
# If args contain images, print their info
for i, arg in enumerate(args):
if hasattr(arg, 'GetLargestPossibleRegion'):
print(f"\nImage argument {i}:")
print(f" Size: {itk.size(arg)}")
print(f" Spacing: {itk.spacing(arg)}")
print(f" Type: {type(arg)}")
raise
# Usage
try:
image = itk.imread('input.png', itk.F)
result = debug_itk_operation(
itk.median_image_filter,
image,
radius=2
)
except Exception:
print("Operation failed, see details above")Install with Tessl CLI
npx tessl i tessl/pypi-itk@5.4.1docs
guides
reference