Skip to content

Instantly share code, notes, and snippets.

@turtleizzy
Created November 2, 2025 10:16
Show Gist options
  • Select an option

  • Save turtleizzy/9e0e45e447bf1ea6cab123743f90da1e to your computer and use it in GitHub Desktop.

Select an option

Save turtleizzy/9e0e45e447bf1ea6cab123743f90da1e to your computer and use it in GitHub Desktop.
ITK vs SimpleITK - Which should you use if you start a new project in Python?

ITK vs SimpleITK - Which should you use if you start a new project in Python?

Basic information

Both ITK (python package) and SimpleITK (python package) are bindings of ITK C++ library providing a number of verified utilities for medical image processing, especially 3D volumetric image processing.

ITK python package provides both C++ style bindings ( functions and classes in UpperCamelCase ) and pythonic bindings ( in snake_case ). C++ style bindings keeps the original pipeline structure and template programming style in ITK and the pythoic bindings provides somewhat procedural interface. For example, in C++ style bindings, you need to specify the datatype when instantiating filters, an example is as following. Refer to the tutorial for more information.

InputType = itk.Image[itk.F,3]
OutputType = itk.Image[itk.F,3]
median = itk.MedianImageFilter[InputType, OutputType].New()

SimpleITK is a procedural interface for ITK. Everything in SimpleITK is wrapped ITK filters in procedural interfaces so you have a consistent interface for nearly all filters and image datatypes.

Comparison

Env: Linux Mint 22.2, Python 3.11

ITK SimpleITK
Version 5.4.4.post1 2.5.1
Total size 266M 51M
Instantiation time (mean (sd)) 0.36 (0.09)s 0.16 (0.01)s
Resample (100 * 100 * 100 float32) 0.032s 0.021s
FastMarching (50 * 50 * 50 float32) 0.046s 0.044s

Time for package instantiation

for i in {0..10}; do python -c 'import time; st_time=time.time(); import itk; print( time.time() - st_time)'; done

for i in {0..10}; do python -c 'import time; st_time=time.time(); import SimpleITK; print( time.time() - st_time)'; done

Total size of package

pip download itk -d /tmp/itk_download && rm /tmp/itk_download/numpy* && du -sh /tmp/itk_download && rm -rf /tmp/itk_download

Comparison of Resample Image Filter

#!/usr/bin/env python3
"""
Performance comparison between ITK and SimpleITK resample filters.
This script creates a synthetic 3D image and compares the performance
of resampling operations using both libraries.
"""

import time
import numpy as np
import sys

def test_itk_resample():
    """Test ITK resample filter performance"""
    try:
        import itk
        print("Testing ITK resample filter...")

        # Create a synthetic 3D image (100x100x100 voxels)
        image_size = (100, 100, 100)
        image_spacing = (1.0, 1.0, 1.0)
        image_origin = (0.0, 0.0, 0.0)

        # Create synthetic image data
        image_array = np.random.rand(*image_size).astype(np.float32) * 1000

        # Convert to ITK image
        itk_image = itk.GetImageFromArray(image_array)
        itk_image.SetSpacing(image_spacing)
        itk_image.SetOrigin(image_origin)

        # Set up resample filter
        resample_filter = itk.ResampleImageFilter.New(itk_image)

        # Set output parameters (downsample by factor of 2)
        output_spacing = (2.0, 2.0, 2.0)
        output_size = (50, 50, 50)
        output_origin = image_origin

        resample_filter.SetOutputSpacing(output_spacing)
        resample_filter.SetSize(output_size)
        resample_filter.SetOutputOrigin(output_origin)

        # Use linear interpolator
        interpolator = itk.LinearInterpolateImageFunction.New(itk_image)
        resample_filter.SetInterpolator(interpolator)

        # Time the resampling operation
        start_time = time.time()
        resample_filter.Update()
        resampled_image = resample_filter.GetOutput()
        # Force computation by accessing image data
        resampled_array = itk.GetArrayFromImage(resampled_image)
        itk_time = time.time() - start_time

        print(".3f")
        # Get shape from the resampled image directly
        shape = (resampled_image.GetLargestPossibleRegion().GetSize()[0],
                resampled_image.GetLargestPossibleRegion().GetSize()[1],
                resampled_image.GetLargestPossibleRegion().GetSize()[2])
        return itk_time, shape

    except ImportError:
        print("ITK not available")
        return None, None
    except Exception as e:
        print(f"ITK test failed: {e}")
        return None, None

def test_simpleitk_resample():
    """Test SimpleITK resample filter performance"""
    try:
        import SimpleITK as sitk
        print("Testing SimpleITK resample filter...")

        # Create a synthetic 3D image (100x100x100 voxels)
        image_size = (100, 100, 100)
        image_spacing = (1.0, 1.0, 1.0)
        image_origin = (0.0, 0.0, 0.0)

        # Create synthetic image data
        image_array = np.random.rand(*image_size).astype(np.float32) * 1000

        # Convert to SimpleITK image
        sitk_image = sitk.GetImageFromArray(image_array)
        sitk_image.SetSpacing(image_spacing)
        sitk_image.SetOrigin(image_origin)

        # Set up resample filter
        resample = sitk.ResampleImageFilter()
        resample.SetReferenceImage(sitk_image)

        # Set output parameters (downsample by factor of 2)
        output_spacing = (2.0, 2.0, 2.0)
        output_size = (50, 50, 50)
        output_origin = image_origin

        resample.SetOutputSpacing(output_spacing)
        resample.SetSize(output_size)
        resample.SetOutputOrigin(output_origin)

        # Use linear interpolator
        resample.SetInterpolator(sitk.sitkLinear)

        # Time the resampling operation
        start_time = time.time()
        resampled_image = resample.Execute(sitk_image)
        # Force computation by accessing image data
        resampled_array = sitk.GetArrayFromImage(resampled_image)
        simpleitk_time = time.time() - start_time

        print(".3f")
        return simpleitk_time, np.array(resampled_array).shape

    except ImportError:
        print("SimpleITK not available")
        return None, None
    except Exception as e:
        print(f"SimpleITK test failed: {e}")
        return None, None

def main():
    print("ITK vs SimpleITK Resample Filter Performance Comparison")
    print("=" * 55)

    # Set random seed for reproducible results
    np.random.seed(42)

    # Test ITK
    itk_time, itk_shape = test_itk_resample()
    print()

    # Test SimpleITK
    simpleitk_time, simpleitk_shape = test_simpleitk_resample()
    print()

    # Compare results
    if itk_time is not None and simpleitk_time is not None:
        print("Performance Comparison:")
        print(".3f")
        print(".3f")

        if itk_time < simpleitk_time:
            speedup = simpleitk_time / itk_time
            print(".2f")
        else:
            speedup = itk_time / simpleitk_time
            print(".2f")

        print(f"Output shapes: ITK={itk_shape}, SimpleITK={simpleitk_shape}")
    else:
        print("Unable to compare both libraries - one or both failed")

if __name__ == "__main__":
    main()

Comparison of Fast Marching Image Filter

#!/usr/bin/env python3
"""
Performance comparison between ITK and SimpleITK FastMarchingImageFilter.
This script creates a synthetic 3D image with seed points and compares
the performance of FastMarching operations using both libraries.
"""

import time
import numpy as np
import sys

def test_itk_fastmarching():
    """Test ITK FastMarchingImageFilter performance"""
    try:
        import itk
        print("Testing ITK FastMarchingImageFilter...")

        # Create a 3D speed image (50x50x50 voxels)
        image_size = (50, 50, 50)
        image_spacing = (1.0, 1.0, 1.0)
        image_origin = (0.0, 0.0, 0.0)

        # Create speed image: uniform speed of 1.0 everywhere
        speed_array = np.ones(image_size, dtype=np.float32)

        # Convert to ITK image
        speed_image = itk.GetImageFromArray(speed_array)
        speed_image.SetSpacing(image_spacing)
        speed_image.SetOrigin(image_origin)

        # Try using the specific 3D float filter as suggested
        fastmarching_filter = itk.FastMarchingImageFilter.IF3IF3.New(speed_image)

        # Create seed points using NodeContainer
        # Use the available D3 node type
        nodes = itk.VectorContainer[itk.UI, itk.LevelSetNode.F3].New()

        # Add seed point at center
        node = itk.LevelSetNode.F3()
        node.SetValue(0.0)  # Initial distance
        node.SetIndex([25, 25, 25])  # Center point
        nodes.InsertElement(0, node)

        fastmarching_filter.SetTrialPoints(nodes)
        fastmarching_filter.SetStoppingValue(25.0)

        # Time the FastMarching operation
        start_time = time.time()
        fastmarching_filter.Update()
        result_image = fastmarching_filter.GetOutput()
        # Force computation
        result_array = itk.GetArrayFromImage(result_image)
        itk_time = time.time() - start_time

        print(f"{itk_time:.3f}")
        # Get shape from the result image directly
        shape = (result_image.GetLargestPossibleRegion().GetSize()[0],
                result_image.GetLargestPossibleRegion().GetSize()[1],
                result_image.GetLargestPossibleRegion().GetSize()[2])
        return itk_time, shape

    except ImportError:
        print("ITK not available")
        return None, None
    except Exception as e:
        print(f"ITK test failed: {e}")
        return None, None

def test_simpleitk_fastmarching():
    """Test SimpleITK FastMarchingImageFilter performance"""
    try:
        import SimpleITK as sitk
        print("Testing SimpleITK FastMarchingImageFilter...")

        # Create a 3D speed image (50x50x50 voxels)
        image_size = (50, 50, 50)
        image_spacing = (1.0, 1.0, 1.0)
        image_origin = (0.0, 0.0, 0.0)

        # Create speed image: uniform speed of 1.0 everywhere
        speed_array = np.ones(image_size, dtype=np.float32)

        # Convert to SimpleITK image
        speed_image = sitk.GetImageFromArray(speed_array)
        speed_image.SetSpacing(image_spacing)
        speed_image.SetOrigin(image_origin)

        # Set up FastMarchingImageFilter
        fastmarching_filter = sitk.FastMarchingImageFilter()

        # Set seed point
        fastmarching_filter.AddTrialPoint([25, 25, 25])  # Center point (integers)

        # Set stopping value (maximum distance to compute)
        fastmarching_filter.SetStoppingValue(25.0)

        # Time the FastMarching operation
        start_time = time.time()
        result_image = fastmarching_filter.Execute(speed_image)
        # Force computation
        result_array = sitk.GetArrayFromImage(result_image)
        simpleitk_time = time.time() - start_time

        print(f"{simpleitk_time:.3f}")
        return simpleitk_time, np.array(result_array).shape

    except ImportError:
        print("SimpleITK not available")
        return None, None
    except Exception as e:
        print(f"SimpleITK test failed: {e}")
        return None, None

def main():
    print("ITK vs SimpleITK FastMarchingImageFilter Performance Comparison (3D)")
    print("=" * 68)

    # Set random seed for reproducible results
    np.random.seed(42)

    # Test ITK
    itk_time, itk_shape = test_itk_fastmarching()
    print()

    # Test SimpleITK
    simpleitk_time, simpleitk_shape = test_simpleitk_fastmarching()
    print()

    # Compare results
    if itk_time is not None and simpleitk_time is not None:
        print("Performance Comparison:")
        print(f"ITK time: {itk_time:.3f} seconds")
        print(f"SimpleITK time: {simpleitk_time:.3f} seconds")

        if itk_time < simpleitk_time:
            speedup = simpleitk_time / itk_time
            print(f"ITK was {speedup:.2f}x faster than SimpleITK")
        else:
            speedup = itk_time / simpleitk_time
            print(f"SimpleITK was {speedup:.2f}x faster than ITK")

        print(f"Output shapes: ITK={itk_shape}, SimpleITK={simpleitk_shape}")
    elif simpleitk_time is not None:
        print("Only SimpleITK test completed successfully:")
        print(f"SimpleITK time: {simpleitk_time:.3f} seconds")
        print(f"Output shape: {simpleitk_shape}")
        print("Note: ITK FastMarching requires specific template instantiations not available in this Python build")
    else:
        print("Unable to run tests - both libraries failed")

if __name__ == "__main__":
    main()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment